Compare commits
	
		
			341 Commits
		
	
	
		
			v0.14.1
			...
			v0.12.5-qe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 69903f803a | ||
|  | 174f225e9d | ||
|  | e916448940 | ||
|  | bb44e0bbce | ||
|  | 191d44fc43 | ||
|  | a2f0cbaa58 | ||
|  | a9d9a66f13 | ||
|  | 37060c28e5 | ||
|  | 7205c21e76 | ||
|  | ceef722d01 | ||
|  | dfe0bb55ee | ||
|  | 6fd82592ce | ||
|  | 39187b5192 | ||
|  | 729862401d | ||
|  | 34d0d68bdf | ||
|  | 82e9cbeb0d | ||
|  | 2020dd5535 | ||
|  | 0c0f53e25c | ||
|  | 3dbe0714dd | ||
|  | 9067bac11d | ||
|  | 74471f3742 | ||
|  | 370f80376a | ||
|  | ed3aac289a | ||
|  | 11b52a6536 | ||
|  | b6185fc79c | ||
|  | 8fd7d5438e | ||
|  | a513171f80 | ||
|  | ff9e177617 | ||
|  | db3519a9ec | ||
|  | 258e351d12 | ||
|  | cd14f4d346 | ||
|  | df631629b1 | ||
|  | af0269b036 | ||
|  | d37dbf988d | ||
|  | cc7ed88f28 | ||
|  | 07442ab4a1 | ||
|  | dbe6a18d82 | ||
|  | 7dd007c2ed | ||
|  | 9c6a8f503d | ||
|  | 0c459361a1 | ||
|  | 72d3457e8d | ||
|  | e1f0c1d05d | ||
|  | 74bcc51b99 | ||
|  | 7e4f956056 | ||
|  | 1fb9798b69 | ||
|  | 9f6a84bc43 | ||
|  | 8cef921d18 | ||
|  | b04c3db504 | ||
|  | d04d7cf158 | ||
|  | 2b8bdd5c7f | ||
|  | 2a44494726 | ||
|  | 8f30db54d9 | ||
|  | b09ac1abe7 | ||
|  | 012d4869c1 | ||
|  | 3597c9c1d5 | ||
|  | 3b4bef0696 | ||
|  | d899303743 | ||
|  | 5773685183 | ||
|  | d40ba77ebf | ||
|  | a8c46d182c | ||
|  | d80e20a1c3 | ||
|  | 1ce4fad939 | ||
|  | 9167a242db | ||
|  | 09e96924ec | ||
|  | 69ff4e9dbd | ||
|  | 0434349d6a | ||
|  | e007221223 | ||
|  | 4622317288 | ||
|  | ffac613ff9 | ||
|  | aba5288247 | ||
|  | 4f7cb96931 | ||
|  | fafc2e4b33 | ||
|  | 83ef70f24a | ||
|  | de17c16e1f | ||
|  | 9462695b64 | ||
|  | 5eb089588e | ||
|  | 2039f70c23 | ||
|  | 082a9fc256 | ||
|  | 36a013c956 | ||
|  | c4c4b32b81 | ||
|  | 804b6ab08d | ||
|  | 81b168a702 | ||
|  | 5c6892078a | ||
|  | 18a21890ff | ||
|  | 6629fa6473 | ||
|  | 2a7996ce0e | ||
|  | 8ec131fb59 | ||
|  | 30d061750d | ||
|  | c5f5dc5bad | ||
|  | d2df336c58 | ||
|  | b299b12b17 | ||
|  | c248df6161 | ||
|  | 7d5625d5f7 | ||
|  | cc21d131e3 | ||
|  | 41a5bda61f | ||
|  | 5163f6e864 | ||
|  | 6173d56bdc | ||
|  | f39942d217 | ||
|  | 5dde87088f | ||
|  | 3fa017e24b | ||
|  | 35924dbe8c | ||
|  | 88aa905668 | ||
|  | b93c5c84c8 | ||
|  | f203baee5b | ||
|  | 5e3be62385 | ||
|  | b391493bc6 | ||
|  | 57f9f4c9f5 | ||
|  | 7ebc79037c | ||
|  | ea299062eb | ||
|  | e03dd1a6c2 | ||
|  | 535d2eb34a | ||
|  | beb8eab90c | ||
|  | 8d67694fbf | ||
|  | 02510b2436 | ||
|  | b57a2297f2 | ||
|  | 43fab08210 | ||
|  | 915080e6b1 | ||
|  | 9f59ddcc4f | ||
|  | 999ceb2c1d | ||
|  | 307331a42a | ||
|  | 6728dd464b | ||
|  | bb45bcc8de | ||
|  | 096109c804 | ||
|  | 7ae1fcc88c | ||
|  | 299e0bc52a | ||
|  | 74f0529e24 | ||
|  | 614971158c | ||
|  | afa328b1b2 | ||
|  | 868dab5dc2 | ||
|  | 29bb3bf350 | ||
|  | dbf45b44b7 | ||
|  | d0d888bc6d | ||
|  | 19abbad0da | ||
|  | f48aba6de7 | ||
|  | cb2ae96bf6 | ||
|  | 848f874ca1 | ||
|  | a1a86bf902 | ||
|  | c727a05459 | ||
|  | eb05143e24 | ||
|  | 0c709e6195 | ||
|  | dc88aa49b4 | ||
|  | dc2ffbf6d8 | ||
|  | d3bf9367f2 | ||
|  | c502715a74 | ||
|  | b9a61d2154 | ||
|  | 9525204c5d | ||
|  | f79d556b4f | ||
|  | 41ae9ece21 | ||
|  | 40480d2bf4 | ||
|  | e389e937a7 | ||
|  | 73b48d914f | ||
|  | 3999bf3244 | ||
|  | a3441a43a6 | ||
|  | 49a3aaac4a | ||
|  | 027866ce23 | ||
|  | 04babf6c6f | ||
|  | d2b8117310 | ||
|  | 0c4b9aef7b | ||
|  | 431c829f33 | ||
|  | be7398ec06 | ||
|  | be59ce1f48 | ||
|  | eacad66dbe | ||
|  | 66dbb62824 | ||
|  | d47d251286 | ||
|  | 348af56fae | ||
|  | 09866b9baa | ||
|  | e1daf40e3e | ||
|  | de3ea06d59 | ||
|  | fe46a160ce | ||
|  | 8033c42abd | ||
|  | 4713c69fa2 | ||
|  | d68bf60838 | ||
|  | 57fa5ca551 | ||
|  | 8610774f79 | ||
|  | 76ba04832b | ||
|  | 644f5de21b | ||
|  | dcc0da8297 | ||
|  | 41193c50fa | ||
|  | da0266005a | ||
|  | eacdccbb3e | ||
|  | 65e8c51928 | ||
|  | e470436f19 | ||
|  | b60c2c74f3 | ||
|  | fe1b69708c | ||
|  | a1678e85db | ||
|  | 8212d18cf5 | ||
|  | 6c412ddf1c | ||
|  | 862ad4be53 | ||
|  | aac2ad563a | ||
|  | eb41f58a4e | ||
|  | 5543b41167 | ||
|  | 31d85f6a6b | ||
|  | 9c49a2533c | ||
|  | c6faf5fd73 | ||
|  | 069def25cb | ||
|  | 3733a1e804 | ||
|  | 5b06a3f785 | ||
|  | baaf73aaac | ||
|  | 345c22aa80 | ||
|  | 26bb2a0865 | ||
|  | e6ea832410 | ||
|  | 22d0cc8d38 | ||
|  | 898829d5c7 | ||
|  | 72bb3c7571 | ||
|  | 48c437f0ab | ||
|  | 07d00c2174 | ||
|  | 3243a06f51 | ||
|  | 1c3f96be38 | ||
|  | df9e7219db | ||
|  | e83421f511 | ||
|  | 2b311b3cce | ||
|  | 4b5db3749c | ||
|  | a1497a782c | ||
|  | 3c547d7bb7 | ||
|  | 3b43502e3a | ||
|  | 078517421f | ||
|  | afc7055619 | ||
|  | 53425683d4 | ||
|  | ef5a63186a | ||
|  | 4a0e0accd7 | ||
|  | 73e47683de | ||
|  | 115e94a31e | ||
|  | 5fd5f6999d | ||
|  | 602e97b725 | ||
|  | 97b766dfcd | ||
|  | fb8cf78db6 | ||
|  | c5238ac21b | ||
|  | 99917a99cd | ||
|  | 55ed56908f | ||
|  | 139e310025 | ||
|  | bed93b1dcb | ||
|  | 73b4ac5cd8 | ||
|  | 00e8277b83 | ||
|  | a8ea3a357b | ||
|  | f8051485c1 | ||
|  | 807c80b259 | ||
|  | 686a3c3dc2 | ||
|  | a381d8277c | ||
|  | 8647b09bfd | ||
|  | 9153014fa0 | ||
|  | f6d4446ea8 | ||
|  | f1e247ee6b | ||
|  | a49668769d | ||
|  | 97d949d9da | ||
|  | 040093b1a5 | ||
|  | 5d4e53dc81 | ||
|  | 3ebee80226 | ||
|  | c56651312b | ||
|  | 869ca150e7 | ||
|  | 910628f396 | ||
|  | 251241dc90 | ||
|  | 03a23e5c6e | ||
|  | a68fc29ceb | ||
|  | 0014803d23 | ||
|  | 5118f7b47c | ||
|  | 1c1d7bda2c | ||
|  | bdae662c94 | ||
|  | 0108d4e323 | ||
|  | 4305793bad | ||
|  | d2d51eeff0 | ||
|  | 3be42b28c1 | ||
|  | ee70ef8771 | ||
|  | 5f9fe0f8d0 | ||
|  | 7589acc9e8 | ||
|  | 94f539bdac | ||
|  | e637fd2386 | ||
|  | 6e785bee32 | ||
|  | f883e4f7b8 | ||
|  | 5daa7bb7a4 | ||
|  | b0a84d0525 | ||
|  | f1f84ba223 | ||
|  | db830f26cb | ||
|  | 61a606dade | ||
|  | 2d95575edb | ||
|  | d707483ce3 | ||
|  | e2deb622c2 | ||
|  | 6e792a557e | ||
|  | ea2138cf90 | ||
|  | 992f3cb78e | ||
|  | 828b2ff676 | ||
|  | a231a8272c | ||
|  | f2604b35dc | ||
|  | fc05630f1f | ||
|  | ad960ddbce | ||
|  | 239a69680c | ||
|  | f4f1df70f2 | ||
|  | 782e9e6554 | ||
|  | 64de0113f1 | ||
|  | 84db615abc | ||
|  | 7c6a56cc63 | ||
|  | a20600b917 | ||
|  | 4986fd4111 | ||
|  | 96639424e2 | ||
|  | 6ac733bf09 | ||
|  | 25d82d3311 | ||
|  | f9800fe5a0 | ||
|  | 542d991b4c | ||
|  | d1d6963eba | ||
|  | 7058b807cd | ||
|  | f49d2561cb | ||
|  | a63e5f1971 | ||
|  | ebbc8a3d8e | ||
|  | 08b2d3ba9a | ||
|  | 72fbd9f97c | ||
|  | 5b6d0419d9 | ||
|  | 9df9eeeb18 | ||
|  | 5b6321a237 | ||
|  | 5e0c455842 | ||
|  | 4d687b13cf | ||
|  | d7b8193716 | ||
|  | 2e51813417 | ||
|  | 90f445e1c9 | ||
|  | 143d288cba | ||
|  | 13a2ccc46f | ||
|  | ea2b7d7079 | ||
|  | 0b52786ce1 | ||
|  | e36469149a | ||
|  | e5fc266be5 | ||
|  | 3e4cd634cc | ||
|  | 06976f82e7 | ||
|  | fe7c6c90a8 | ||
|  | 960a4b537a | ||
|  | c756b1e762 | ||
|  | 06921ec84f | ||
|  | 8cb1cec656 | ||
|  | a46657d185 | ||
|  | 28acf422cb | ||
|  | a7d5da8857 | ||
|  | 931a548be3 | ||
|  | bcddbd0f6a | ||
|  | b3dfdb5a3b | ||
|  | 6ccc51fd20 | ||
|  | 0ea5709a32 | ||
|  | 67a2698dac | ||
|  | eea4acfa5c | ||
|  | c99d32efe6 | ||
|  | 9fa7591beb | ||
|  | 066263f377 | ||
|  | 20c1a35211 | ||
|  | ea6112b165 | ||
|  | e222100afe | 
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,16 +2,11 @@ config-devices.* | |||||||
| config-all-devices.* | config-all-devices.* | ||||||
| config-host.* | config-host.* | ||||||
| config-target.* | config-target.* | ||||||
| trace.h | i386 | ||||||
| trace.c |  | ||||||
| trace-dtrace.h |  | ||||||
| trace-dtrace.dtrace |  | ||||||
| *-timestamp |  | ||||||
| *-softmmu | *-softmmu | ||||||
| *-darwin-user | *-darwin-user | ||||||
| *-linux-user | *-linux-user | ||||||
| *-bsd-user | *-bsd-user | ||||||
| libdis* |  | ||||||
| libhw32 | libhw32 | ||||||
| libhw64 | libhw64 | ||||||
| libuser | libuser | ||||||
| @@ -27,13 +22,11 @@ qemu-img | |||||||
| qemu-nbd | qemu-nbd | ||||||
| qemu-nbd.8 | qemu-nbd.8 | ||||||
| qemu-nbd.pod | qemu-nbd.pod | ||||||
| qemu-options.def |  | ||||||
| qemu-options.texi | qemu-options.texi | ||||||
| qemu-img-cmds.texi | qemu-img-cmds.texi | ||||||
| qemu-img-cmds.h | qemu-img-cmds.h | ||||||
| qemu-io | qemu-io | ||||||
| qemu-monitor.texi | qemu-monitor.texi | ||||||
| QMP/qmp-commands.txt |  | ||||||
| .gdbinit | .gdbinit | ||||||
| *.a | *.a | ||||||
| *.aux | *.aux | ||||||
| @@ -43,9 +36,7 @@ QMP/qmp-commands.txt | |||||||
| *.fn | *.fn | ||||||
| *.ky | *.ky | ||||||
| *.log | *.log | ||||||
| *.pdf |  | ||||||
| *.pg | *.pg | ||||||
| *.pyc |  | ||||||
| *.toc | *.toc | ||||||
| *.tp | *.tp | ||||||
| *.vr | *.vr | ||||||
| @@ -55,8 +46,7 @@ QMP/qmp-commands.txt | |||||||
| patches | patches | ||||||
| pc-bios/bios-pq/status | pc-bios/bios-pq/status | ||||||
| pc-bios/vgabios-pq/status | pc-bios/vgabios-pq/status | ||||||
| pc-bios/optionrom/linuxboot.bin |  | ||||||
| pc-bios/optionrom/multiboot.bin | pc-bios/optionrom/multiboot.bin | ||||||
| pc-bios/optionrom/multiboot.raw | pc-bios/optionrom/multiboot.raw | ||||||
|  | pc-bios/optionrom/extboot.bin | ||||||
| .stgit-* | .stgit-* | ||||||
| cscope.* |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| [submodule "roms/vgabios"] | [submodule "roms/vgabios"] | ||||||
| 	path = roms/vgabios | 	path = roms/vgabios | ||||||
| 	url = git://git.qemu.org/vgabios.git/ | 	url = ../vgabios.git | ||||||
| [submodule "roms/seabios"] | [submodule "roms/seabios"] | ||||||
| 	path = roms/seabios | 	path = roms/seabios | ||||||
| 	url = git://git.qemu.org/seabios.git/ | 	url = ../seabios.git | ||||||
|   | |||||||
| @@ -1,9 +1,6 @@ | |||||||
| Qemu Coding Style | Qemu Coding Style | ||||||
| ================= | ================= | ||||||
|  |  | ||||||
| Please use the script checkpatch.pl in the scripts directory to check |  | ||||||
| patches before submitting. |  | ||||||
|  |  | ||||||
| 1. Whitespace | 1. Whitespace | ||||||
|  |  | ||||||
| Of course, the most important aspect in any coding style is whitespace. | Of course, the most important aspect in any coding style is whitespace. | ||||||
| @@ -49,8 +46,8 @@ names are lower_case_with_underscores_ending_with_a_t, like the POSIX | |||||||
| uint64_t and family.  Note that this last convention contradicts POSIX | uint64_t and family.  Note that this last convention contradicts POSIX | ||||||
| and is therefore likely to be changed. | and is therefore likely to be changed. | ||||||
|  |  | ||||||
| When wrapping standard library functions, use the prefix qemu_ to alert | Typedefs are used to eliminate the redundant 'struct' keyword.  It is the | ||||||
| readers that they are seeing a wrapped version; otherwise avoid this prefix. | QEMU coding style. | ||||||
|  |  | ||||||
| 4. Block structure | 4. Block structure | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										187
									
								
								Changelog
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								Changelog
									
									
									
									
									
								
							| @@ -1,3 +1,190 @@ | |||||||
|  | version 0.12.5 | ||||||
|  |  - audio/alsa: Handle SND_PCM_STATE_SETUP in alsa_poll_handler | ||||||
|  |  - block: Handle multiwrite errors only when all requests have completed | ||||||
|  |  - block: Fix early failure in multiwrite | ||||||
|  |  - vpc: Use bdrv_(p)write_sync for metadata writes | ||||||
|  |  - vmdk: Use bdrv_(p)write_sync for metadata writes | ||||||
|  |  - qcow2: Use bdrv_(p)write_sync for metadata writes | ||||||
|  |  - qcow: Use bdrv_(p)write_sync for metadata writes | ||||||
|  |  - block: Add bdrv_(p)write_sync | ||||||
|  |  - qcow2: Restore L1 entry on l2_allocate failure | ||||||
|  |  - block/vdi: Fix image opening and creation for odd disk sizes | ||||||
|  |  - block/vpc: Fix conversion from size to disk geometry | ||||||
|  |  - qcow2: Remove abort on free_clusters failure | ||||||
|  |  - vmdk: Fix COW | ||||||
|  |  - qcow2: Fix creation of large images | ||||||
|  |  - vmdk: fix double free | ||||||
|  |  - qemu-options: add documentation for stdio signal=on|off | ||||||
|  |  - target-arm : fix parallel saturated subtraction implementation | ||||||
|  |  - target-arm : fix thumb2 parallel add/sub opcode decoding | ||||||
|  |  - target-arm: fix addsub/subadd implementation | ||||||
|  |  - target-i386: fix xchg rax,r8 | ||||||
|  |  - block/vvfat.c: fix warnings with _FORTIFY_SOURCE | ||||||
|  |  - audio/alsa: Spelling typo (paramters) | ||||||
|  |  - target-mips: fix DINSU instruction | ||||||
|  |  - Correct definitions for FD_CMD_SAVE and FD_CMD_RESTORE | ||||||
|  |  - qcow2: Fix corruption after error in update_refcount | ||||||
|  |  - qcow2: Fix corruption after refblock allocation | ||||||
|  |  - block: Fix multiwrite with overlapping requests | ||||||
|  |  - qcow2: Fix error handling in l2_allocate | ||||||
|  |  - qcow2: Clear L2 table cache after write error | ||||||
|  |  - ide: Fix ide_dma_cancel | ||||||
|  |  - usb-bus: fix no params | ||||||
|  |  - Avoid crash on '-usbdevice <device>' without parameters | ||||||
|  |  - Fix -usbdevice crash | ||||||
|  |  - Fix multiboot compilation | ||||||
|  |  - Fix missing symbols in .rel/.rela.plt sections | ||||||
|  |  - target-ppc: fix RFI by clearing some bits of MSR | ||||||
|  |  - Fix typo in balloon help | ||||||
|  |  - arm_timer: fix oneshot mode | ||||||
|  |  - arm_timer: reload timer when enabled | ||||||
|  |  - qemu-sockets: avoid strlen of NULL pointer | ||||||
|  |  - block: fix aio_flush segfaults for read-only protocols (e.g. curl) | ||||||
|  |  - virtio-blk: fix barrier support | ||||||
|  |  - block: fix sector comparism in multiwrite_req_compare | ||||||
|  |  - pci: irq_state vmstate breakage | ||||||
|  |  - qemu-img: use the heap instead of the huge stack array for win32 | ||||||
|  |  | ||||||
|  | version 0.12.4 | ||||||
|  |  - Workaround for broken OSS_GETVERSION on FreeBSD, part two (Juergen Lock) | ||||||
|  |  - oss: fix fragment setting (malc) | ||||||
|  |  - oss: issue OSS_GETVERSION ioctl only when needed (malc) | ||||||
|  |  - oss: refactor code around policy setting (malc) | ||||||
|  |  - oss: workaround for cases when OSS_GETVERSION is not defined (malc) | ||||||
|  |  - block: Free iovec arrays allocated by multiwrite_merge() (Stefan Hajnoczi) | ||||||
|  |  - lsi: fix segfault in lsi_command_complete (Gerd Hoffmann) | ||||||
|  |  - lsi: pass lsi_request to lsi_reselect (Gerd Hoffmann) | ||||||
|  |  - lsi: move dma_len+dma_buf into lsi_request (Gerd Hoffmann) | ||||||
|  |  - lsi: move current_dev into lsi_request (Gerd Hoffmann) | ||||||
|  |  - lsi: have lsi_request for the whole life time of the request. (Gerd Hoffmann) | ||||||
|  |  - lsi: use QTAILQ for lsi_queue (Gerd Hoffmann) | ||||||
|  |  - tcp/mips: Change TCG_AREG0 (fp -> s0) (Stefan Weil) | ||||||
|  |  - sh_pci: fix memory and I/O access (Aurelien Jarno) | ||||||
|  |  - Fix incoming migration with iothread (Marcelo Tosatti) | ||||||
|  |  - Fix SIGFPE for vnc display of width/height = 1 (Chris Webb) | ||||||
|  |  - net: remove broken net_set_boot_mask() boot device validation (Eduardo Habkost) | ||||||
|  |  - qcow2: Remove request from in-flight list after error (Kevin Wolf) | ||||||
|  |  - qcow2: Don't ignore immediate read/write failures (Kevin Wolf) | ||||||
|  |  - block: Fix multiwrite memory leak in error case (Kevin Wolf) | ||||||
|  |  - block: Fix error code in multiwrite for immediate failures (Kevin Wolf) | ||||||
|  |  - block: Fix multiwrite error handling (Kevin Wolf) | ||||||
|  |  - scsi-disk: fix buffer overflow (Gerd Hoffmann) | ||||||
|  |  - qcow2: Rewrite alloc_refcount_block/grow_refcount_table (Kevin Wolf) | ||||||
|  |  - qcow2: Factor next_refcount_table_size out (Kevin Wolf) | ||||||
|  |  - block: avoid creating too large iovecs in multiwrite_merge (Christoph Hellwig) | ||||||
|  |  - json-parser: Fix segfault on malformed input (Kevin Wolf) | ||||||
|  |  - linux-user: switch default ppc64 CPU to 970fx from 970 (Aurelien Jarno) | ||||||
|  |  - target-sh4: MMU: fix store queue addresses (Aurelien Jarno) | ||||||
|  |  - target-sh4: MMU: fix ITLB priviledge check (Aurelien Jarno) | ||||||
|  |  - target-sh4: MMU: fix mem_idx computation (Aurelien Jarno) | ||||||
|  |  - sh7750: handle MMUCR TI bit (Aurelien Jarno) | ||||||
|  |  - UHCI spurious interrut fix (Paul Brook) | ||||||
|  |  - tcg/mips: fix branch offset during retranslation (Aurelien Jarno) | ||||||
|  |  - tcg/arm: correctly save/restore registers in prologue/epilogue (Aurelien Jarno) | ||||||
|  |  - workaround for cmd646 bmdma register access while no dma is active (Igor V. Kovalenko) | ||||||
|  |  - Fix corner case in chardev udp: parameter (Jan Kiszka) | ||||||
|  |  - Don't set default monitor when there is a mux'ed one (Jan Kiszka) | ||||||
|  |  - spelling typo (compatibilty) in hw/fw_cfg.c (Vagrant Cascadian) | ||||||
|  |  - fdc: fix drive property handling. (Gerd Hoffmann) | ||||||
|  |  - target-i386: fix commit c22549204a6edc431e8e4358e61bd56386ff6957 (TeLeMan) | ||||||
|  |  - target-i386: fix SIB decoding with index = 4 (Aurelien Jarno) | ||||||
|  |  - Fix segfault with ram_size > 4095M without kvm (Ryan Harper) | ||||||
|  |  - target-i386: Fix long jumps/calls in long mode with REX.W set (malc) | ||||||
|  |  - target-i386: fix lddqu SSE instruction (Aurelien Jarno) | ||||||
|  |  - qemu-char.c: drop debug printfs from qemu_chr_parse_compat (Jan Kiszka) | ||||||
|  |  - fix undefined shifts by >32 (Paolo Bonzini) | ||||||
|  |  - Fix qemu -net user,hostfwd= example (Aurelien Jarno) | ||||||
|  |  | ||||||
|  | version 0.12.3 | ||||||
|  |   - kvm: Fix eflags corruption in kvm mode (Jan Kiszka) | ||||||
|  |   - qcow2: Fix access after end of array (Kevin Wolf) | ||||||
|  |   - ide save/restore pio/atapi cmd transfer fields and io buffer (Marcelo Tosatti) | ||||||
|  |   - net: Monitor command set_link finds only VLAN clients, fix (Markus Armbruster) | ||||||
|  |   - net: info network shows only VLAN clients, fix (Markus Armbruster) | ||||||
|  |   - net: net_check_clients() checks only VLAN clients, fix (Markus Armbruster) | ||||||
|  |   - net: Fix bogus "Warning: vlan 0 with no nics" with -device (Markus Armbruster) | ||||||
|  |   - net: net_check_clients() runs too early to see -device, fix (Markus Armbruster) | ||||||
|  |   - net: Remove unused net_client_uninit() (Markus Armbruster) | ||||||
|  |   - don't dereference NULL after failed strdup (Jim Meyering) | ||||||
|  |   - virtio-net: fix network stall under load (Tom Lendacky) | ||||||
|  |   - json: fix PRId64 on Win32 (Roy Tam) | ||||||
|  |   - fix inet_parse typo (Marcelo Tosatti) | ||||||
|  |   - iothread: fix vcpu stop with smp tcg (Marcelo Tosatti) | ||||||
|  |   - segfault due to buffer overrun in usb-serial (David S. Ahern) | ||||||
|  |   - qcow2: Fix signedness bugs (Kevin Wolf) | ||||||
|  |   - Do not ignore error, if open file failed (-serial /dev/tty) (Evgeniy Dushistov) | ||||||
|  |   - pc-bios: update to newer version of (stable) seabios (Anthony Liguori) | ||||||
|  |   - target-mips: fix ROTR and DROTR by zero (Aurelien Jarno) | ||||||
|  |   - target-mips: fix CpU exception for coprocessor 0 (Nathan Froyd) | ||||||
|  |   - tcg/mips: fix crash in tcg_out_qemu_ld() (Aurelien Jarno) | ||||||
|  |   - target-mips: don't call cpu_loop_exit() from helper.c (Aurelien Jarno) | ||||||
|  |   - virtio-blk: Fix error cases which ignored rerror/werror (Kevin Wolf) | ||||||
|  |   - virtio-blk: Fix restart after read error (Kevin Wolf) | ||||||
|  |   - virtio_blk: Factor virtio_blk_handle_request out (Kevin Wolf) | ||||||
|  |   - cirrus: Properly re-register cirrus_linear_io_addr on vram unmap (Jan Kiszka) | ||||||
|  |   - qcow2: Don't ignore qcow2_alloc_clusters return value (Kevin Wolf) | ||||||
|  |   - qcow2: Don't ignore update_refcount return value (Kevin Wolf) | ||||||
|  |   - qcow2: Allow updating no refcounts (Kevin Wolf) | ||||||
|  |   - qcow2: Improve error handling in update_refcount (Kevin Wolf) | ||||||
|  |   - qcow2: Fix error handling in grow_refcount_table (Kevin Wolf) | ||||||
|  |   - block: Return original error codes in bdrv_pread/write (Kevin Wolf) | ||||||
|  |   - qcow2: Return 0/-errno in qcow2_alloc_cluster_offset (Kevin Wolf) | ||||||
|  |   - qcow2: Return 0/-errno in get_cluster_table (Kevin Wolf) | ||||||
|  |   - qcow2: Fix error handling in qcow_save_vmstate (Kevin Wolf) | ||||||
|  |   - qcow2: Fix error handling in qcow2_grow_l1_table (Kevin Wolf) | ||||||
|  |   - win32/sdl: Fix toggle full screen (Herve Poussineau) | ||||||
|  |   - win32: pair qemu_memalign() with qemu_vfree() (Herve Poussineau) | ||||||
|  |   - vnc_refresh: calling vnc_update_client might free vs (Stefano Stabellini) | ||||||
|  |   - Musicpal: Fix descriptor walk in eth_send (Jan Kiszka) | ||||||
|  |   - Musicpal: Fix wm8750 I2C address (Jan Kiszka) | ||||||
|  |   - fix savevm command without id or tag (Marcelo Tosatti) | ||||||
|  |   - reduce number of reinjects on ACK (Gleb Natapov) | ||||||
|  |   - QMP: Fix asynchronous events delivery (Luiz Capitulino) | ||||||
|  |   - Documentation: Add missing documentation for qdev related command line options (Stefan Weil) | ||||||
|  |   - pc: add driver version compat properties (Gerd Hoffmann) | ||||||
|  |   - scsi: device version property (Gerd Hoffmann) | ||||||
|  |   - ide: device version property (Gerd Hoffmann) | ||||||
|  |   - QMP: Emit asynchronous events on all QMP monitors (Adam Litke) | ||||||
|  |   - Fix QEMU_WARN_UNUSED_RESULT (Kevin Wolf) | ||||||
|  |  | ||||||
|  | version 0.12.2: | ||||||
|  |   - Qemu's internal TFTP server breaks lock-step-iness of TFTP (Milan Plzik) | ||||||
|  |   - osdep.c: Fix accept4 fallback (Kevin Wolf) | ||||||
|  |   - pc: add rombar to compat properties for pc-0.10 and pc-0.11 (Gerd Hoffmann) | ||||||
|  |   - pci: allow loading roms via fw_cfg. (Gerd Hoffmann) | ||||||
|  |   - roms: rework rom loading via fw (Gerd Hoffmann) | ||||||
|  |   - fw_cfg: rom loader tweaks. (Gerd Hoffmann) | ||||||
|  |   - roms: minor fixes and cleanups. (Gerd Hoffmann) | ||||||
|  |   - pc: add machine type for 0.12 (Gerd Hoffmann) | ||||||
|  |   - loader: more ignores for rom intended to be loaded by the bios (Aurelien Jarno) | ||||||
|  |   - vnc_refresh: return if vd->timer is NULL (Stefano Stabellini) | ||||||
|  |   - QMP: Don't free async event's 'data' (Luiz Capitulino) | ||||||
|  |   - Handle TFTP ERROR from client (Thomas Horsten) | ||||||
|  |   - dmg: fix ->open failure (Christoph Hellwig) | ||||||
|  |   - virtio-pci: thinko fix (Michael S. Tsirkin) | ||||||
|  |   - pc-bios: Update README (SeaBIOS) (Stefan Weil) | ||||||
|  |   - vmware_vga: Check cursor dimensions passed from guest to avoid buffer overflow (Roland Dreier) | ||||||
|  |   - remove pending exception on vcpu reset. (Gleb Natapov) | ||||||
|  |   - Fix CPU topology initialization (Jiri Denemark) | ||||||
|  |   - MCE: Fix bug of IA32_MCG_STATUS after system reset (Huang Ying) | ||||||
|  |   - linuxboot: fix gdt address calculation (Avi Kivity) | ||||||
|  |   - QMP: Drop wrong assert() (Luiz Capitulino) | ||||||
|  |   - vnc: Fix artifacts in hextile decoding (Anthony Liguori) | ||||||
|  |   - target-i386: Fix "call im" on x86_64 when executing 32-bit code (Aurelien Jarno) | ||||||
|  |   - Add missing newline at the end of options list (Michael Tokarev) | ||||||
|  |   - Don't load options roms intended to be loaded by the bios in qemu (Avi Kivity) | ||||||
|  |   - USB: Improve usbdevice error messages (Scott Tsai) | ||||||
|  |   - cpu-all.h: fix cpu_get_real_ticks() #ifdef (Aurelien Jarno) | ||||||
|  |   - alpha: fix compile (Blue Swirl) | ||||||
|  |   - user_only: compile everything with -fpie (Kirill A. Shutemov) | ||||||
|  |   - fdc/sparc32: don't hang on detection under OBP (Artyom Tarasenko) | ||||||
|  |   - scsi-disk: Inquiry with allocation length of CDB < 36 (v4) (Artyom Tarasenko) | ||||||
|  |   - e1000: fix init values for command register (Michael S. Tsirkin) | ||||||
|  |  | ||||||
|  | version 0.12.1: | ||||||
|  |   - loader: fix rom loading at address 0 (fixes target-arm) (Aurelien Jarno) | ||||||
|  |   - loader: fix rom_copy (fixes multiboot) (Kevin Wolf) | ||||||
|  |  | ||||||
| version 0.12.0: | version 0.12.0: | ||||||
|  |  | ||||||
|   - Update to SeaBIOS 0.5.0 |   - Update to SeaBIOS 0.5.0 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								EXTERNAL_DEPENDENCIES
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								EXTERNAL_DEPENDENCIES
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | seabios 9fb3f4d950744e97cc655b7d7b523d8bf101e4a0 | ||||||
|  | vgabios 6e62666cfc19e7fd45dd0d7c3ad62fd8d0b5f67a | ||||||
							
								
								
									
										125
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								HACKING
									
									
									
									
									
								
							| @@ -1,125 +0,0 @@ | |||||||
| 1. Preprocessor |  | ||||||
|  |  | ||||||
| For variadic macros, stick with this C99-like syntax: |  | ||||||
|  |  | ||||||
| #define DPRINTF(fmt, ...)                                       \ |  | ||||||
|     do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0) |  | ||||||
|  |  | ||||||
| 2. C types |  | ||||||
|  |  | ||||||
| It should be common sense to use the right type, but we have collected |  | ||||||
| a few useful guidelines here. |  | ||||||
|  |  | ||||||
| 2.1. Scalars |  | ||||||
|  |  | ||||||
| If you're using "int" or "long", odds are good that there's a better type. |  | ||||||
| If a variable is counting something, it should be declared with an |  | ||||||
| unsigned type. |  | ||||||
|  |  | ||||||
| If it's host memory-size related, size_t should be a good choice (use |  | ||||||
| ssize_t only if required). Guest RAM memory offsets must use ram_addr_t, |  | ||||||
| but only for RAM, it may not cover whole guest address space. |  | ||||||
|  |  | ||||||
| If it's file-size related, use off_t. |  | ||||||
| If it's file-offset related (i.e., signed), use off_t. |  | ||||||
| If it's just counting small numbers use "unsigned int"; |  | ||||||
| (on all but oddball embedded systems, you can assume that that |  | ||||||
| type is at least four bytes wide). |  | ||||||
|  |  | ||||||
| In the event that you require a specific width, use a standard type |  | ||||||
| like int32_t, uint32_t, uint64_t, etc.  The specific types are |  | ||||||
| mandatory for VMState fields. |  | ||||||
|  |  | ||||||
| Don't use Linux kernel internal types like u32, __u32 or __le32. |  | ||||||
|  |  | ||||||
| Use target_phys_addr_t for guest physical addresses except pcibus_t |  | ||||||
| for PCI addresses.  In addition, ram_addr_t is a QEMU internal address |  | ||||||
| space that maps guest RAM physical addresses into an intermediate |  | ||||||
| address space that can map to host virtual address spaces.  Generally |  | ||||||
| speaking, the size of guest memory can always fit into ram_addr_t but |  | ||||||
| it would not be correct to store an actual guest physical address in a |  | ||||||
| ram_addr_t. |  | ||||||
|  |  | ||||||
| Use target_ulong (or abi_ulong) for CPU virtual addresses, however |  | ||||||
| devices should not need to use target_ulong. |  | ||||||
|  |  | ||||||
| Of course, take all of the above with a grain of salt.  If you're about |  | ||||||
| to use some system interface that requires a type like size_t, pid_t or |  | ||||||
| off_t, use matching types for any corresponding variables. |  | ||||||
|  |  | ||||||
| Also, if you try to use e.g., "unsigned int" as a type, and that |  | ||||||
| conflicts with the signedness of a related variable, sometimes |  | ||||||
| it's best just to use the *wrong* type, if "pulling the thread" |  | ||||||
| and fixing all related variables would be too invasive. |  | ||||||
|  |  | ||||||
| Finally, while using descriptive types is important, be careful not to |  | ||||||
| go overboard.  If whatever you're doing causes warnings, or requires |  | ||||||
| casts, then reconsider or ask for help. |  | ||||||
|  |  | ||||||
| 2.2. Pointers |  | ||||||
|  |  | ||||||
| Ensure that all of your pointers are "const-correct". |  | ||||||
| Unless a pointer is used to modify the pointed-to storage, |  | ||||||
| give it the "const" attribute.  That way, the reader knows |  | ||||||
| up-front that this is a read-only pointer.  Perhaps more |  | ||||||
| importantly, if we're diligent about this, when you see a non-const |  | ||||||
| pointer, you're guaranteed that it is used to modify the storage |  | ||||||
| it points to, or it is aliased to another pointer that is. |  | ||||||
|  |  | ||||||
| 2.3. Typedefs |  | ||||||
| Typedefs are used to eliminate the redundant 'struct' keyword. |  | ||||||
|  |  | ||||||
| 2.4. Reserved namespaces in C and POSIX |  | ||||||
| Underscore capital, double underscore, and underscore 't' suffixes should be |  | ||||||
| avoided. |  | ||||||
|  |  | ||||||
| 3. Low level memory management |  | ||||||
|  |  | ||||||
| Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign |  | ||||||
| APIs is not allowed in the QEMU codebase. Instead of these routines, |  | ||||||
| use the replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or |  | ||||||
| qemu_vmalloc/qemu_memalign/qemu_vfree APIs. |  | ||||||
|  |  | ||||||
| Please note that NULL check for the qemu_malloc result is redundant and |  | ||||||
| that qemu_malloc() call with zero size is not allowed. |  | ||||||
|  |  | ||||||
| Memory allocated by qemu_vmalloc or qemu_memalign must be freed with |  | ||||||
| qemu_vfree, since breaking this will cause problems on Win32 and user |  | ||||||
| emulators. |  | ||||||
|  |  | ||||||
| 4. String manipulation |  | ||||||
|  |  | ||||||
| Do not use the strncpy function.  According to the man page, it does |  | ||||||
| *not* guarantee a NULL-terminated buffer, which makes it extremely dangerous |  | ||||||
| to use.  Instead, use functionally equivalent function: |  | ||||||
| void pstrcpy(char *buf, int buf_size, const char *str) |  | ||||||
|  |  | ||||||
| Don't use strcat because it can't check for buffer overflows, but: |  | ||||||
| char *pstrcat(char *buf, int buf_size, const char *s) |  | ||||||
|  |  | ||||||
| The same limitation exists with sprintf and vsprintf, so use snprintf and |  | ||||||
| vsnprintf. |  | ||||||
|  |  | ||||||
| QEMU provides other useful string functions: |  | ||||||
| int strstart(const char *str, const char *val, const char **ptr) |  | ||||||
| int stristart(const char *str, const char *val, const char **ptr) |  | ||||||
| int qemu_strnlen(const char *s, int max_len) |  | ||||||
|  |  | ||||||
| There are also replacement character processing macros for isxyz and toxyz, |  | ||||||
| so instead of e.g. isalnum you should use qemu_isalnum. |  | ||||||
|  |  | ||||||
| Because of the memory management rules, you must use qemu_strdup/qemu_strndup |  | ||||||
| instead of plain strdup/strndup. |  | ||||||
|  |  | ||||||
| 5. Printf-style functions |  | ||||||
|  |  | ||||||
| Whenever you add a new printf-style function, i.e., one with a format |  | ||||||
| string argument and following "..." in its prototype, be sure to use |  | ||||||
| gcc's printf attribute directive in the prototype. |  | ||||||
|  |  | ||||||
| This makes it so gcc's -Wformat and -Wformat-security options can do |  | ||||||
| their jobs and cross-check format strings with the number and types |  | ||||||
| of arguments. |  | ||||||
|  |  | ||||||
| Currently many functions in QEMU are not following this rule but |  | ||||||
| patches to add the attribute would be very much appreciated. |  | ||||||
							
								
								
									
										1
									
								
								KVM_VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								KVM_VERSION
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | qemu-kvm-0.12.5 | ||||||
							
								
								
									
										551
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										551
									
								
								MAINTAINERS
									
									
									
									
									
								
							| @@ -1,489 +1,88 @@ | |||||||
| QEMU Maintainers | QEMU Maintainers | ||||||
| ================ | ================ | ||||||
|  |  | ||||||
| The intention of this file is not to establish who owns what portions of the | Project leaders: | ||||||
| code base, but to provide a set of names that developers can consult when they |  | ||||||
| have a question about a particular subset and also to provide a set of names |  | ||||||
| to be CC'd when submitting a patch to obtain appropriate review. |  | ||||||
|  |  | ||||||
| In general, if you have a question about inclusion of a patch, you should |  | ||||||
| consult qemu-devel and not any specific individual privately. |  | ||||||
|  |  | ||||||
| Descriptions of section entries: |  | ||||||
|  |  | ||||||
| 	M: Mail patches to: FullName <address@domain> |  | ||||||
| 	L: Mailing list that is relevant to this area |  | ||||||
| 	W: Web-page with status/info |  | ||||||
| 	Q: Patchwork web based patch tracking system site |  | ||||||
| 	T: SCM tree type and location.  Type is one of: git, hg, quilt, stgit. |  | ||||||
| 	S: Status, one of the following: |  | ||||||
| 	   Supported:	Someone is actually paid to look after this. |  | ||||||
| 	   Maintained:	Someone actually looks after it. |  | ||||||
| 	   Odd Fixes:	It has a maintainer but they don't have time to do |  | ||||||
| 			much other than throw the odd patch in. See below.. |  | ||||||
| 	   Orphan:	No current maintainer [but maybe you could take the |  | ||||||
| 			role as you write your new code]. |  | ||||||
| 	   Obsolete:	Old code. Something tagged obsolete generally means |  | ||||||
| 			it has been replaced by a better system and you |  | ||||||
| 			should be using that. |  | ||||||
| 	F: Files and directories with wildcard patterns. |  | ||||||
| 	   A trailing slash includes all files and subdirectory files. |  | ||||||
| 	   F:	drivers/net/	all files in and below drivers/net |  | ||||||
| 	   F:	drivers/net/*	all files in drivers/net, but not below |  | ||||||
| 	   F:	*/net/*		all files in "any top level directory"/net |  | ||||||
| 	   One pattern per line.  Multiple F: lines acceptable. |  | ||||||
| 	X: Files and directories that are NOT maintained, same rules as F: |  | ||||||
| 	   Files exclusions are tested before file matches. |  | ||||||
| 	   Can be useful for excluding a specific subdirectory, for instance: |  | ||||||
| 	   F:	net/ |  | ||||||
| 	   X:	net/ipv6/ |  | ||||||
| 	   matches all files in and below net excluding net/ipv6/ |  | ||||||
| 	K: Keyword perl extended regex pattern to match content in a |  | ||||||
| 	   patch or file.  For instance: |  | ||||||
| 	   K: of_get_profile |  | ||||||
| 	      matches patches or files that contain "of_get_profile" |  | ||||||
| 	   K: \b(printk|pr_(info|err))\b |  | ||||||
| 	      matches patches or files that contain one or more of the words |  | ||||||
| 	      printk, pr_info or pr_err |  | ||||||
| 	   One regex pattern per line.  Multiple K: lines acceptable. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| General Project Administration |  | ||||||
| ------------------------------ |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
|  |  | ||||||
| Guest CPU cores (TCG): |  | ||||||
| ---------------------- |  | ||||||
| Alpha |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: target-alpha/ |  | ||||||
|  |  | ||||||
| ARM |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: target-arm/ |  | ||||||
|  |  | ||||||
| CRIS |  | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: target-cris/ |  | ||||||
|  |  | ||||||
| M68K |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: target-m68k/ |  | ||||||
|  |  | ||||||
| MicroBlaze |  | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: target-microblaze/ |  | ||||||
|  |  | ||||||
| MIPS |  | ||||||
| M: Aurelien Jarno <aurelien@aurel32.net> |  | ||||||
| S: Maintained |  | ||||||
| F: target-mips/ |  | ||||||
|  |  | ||||||
| PowerPC |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: target-ppc/ |  | ||||||
|  |  | ||||||
| S390 |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: target-s390x/ |  | ||||||
|  |  | ||||||
| SH4 |  | ||||||
| M: Aurelien Jarno <aurelien@aurel32.net> |  | ||||||
| S: Maintained |  | ||||||
| F: target-sh4/ |  | ||||||
|  |  | ||||||
| SPARC |  | ||||||
| M: Blue Swirl <blauwirbel@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: target-sparc/ |  | ||||||
|  |  | ||||||
| X86 |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Odd Fixes |  | ||||||
| F: target-i386/ |  | ||||||
|  |  | ||||||
| Guest CPU Cores (KVM): |  | ||||||
| ---------------------- |  | ||||||
|  |  | ||||||
| Overall |  | ||||||
| M: Avi Kivity <avi@redhat.com> |  | ||||||
| M: Marcelo Tosatti <mtosatti@redhat.com> |  | ||||||
| L: kvm@vger.kernel.org |  | ||||||
| S: Supported |  | ||||||
| F: kvm-* |  | ||||||
| F: */kvm.* |  | ||||||
|  |  | ||||||
| PPC |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: target-ppc/kvm.c |  | ||||||
|  |  | ||||||
| S390 |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: target-s390x/kvm.c |  | ||||||
|  |  | ||||||
| X86 |  | ||||||
| M: Avi Kivity <avi@redhat.com> |  | ||||||
| M: Marcelo Tosatti <mtosatti@redhat.com> |  | ||||||
| L: kvm@vger.kernel.org |  | ||||||
| S: Supported |  | ||||||
| F: target-i386/kvm.c |  | ||||||
|  |  | ||||||
| ARM Machines |  | ||||||
| ------------ |  | ||||||
| Gumstix |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: hw/gumstix.c |  | ||||||
|  |  | ||||||
| Integrator CP |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/integratorcp.c |  | ||||||
|  |  | ||||||
| Mainstone |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: hw/mainstone.c |  | ||||||
|  |  | ||||||
| Musicpal |  | ||||||
| M: Jan Kiszka <jan.kiszka@web.de> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/musicpal.c |  | ||||||
|  |  | ||||||
| nSeries |  | ||||||
| M: Andrzej Zaborowski <balrogg@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/nseries.c |  | ||||||
|  |  | ||||||
| Palm |  | ||||||
| M: Andrzej Zaborowski <balrogg@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/palm.c |  | ||||||
|  |  | ||||||
| Real View |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/realview* |  | ||||||
|  |  | ||||||
| Spitz |  | ||||||
| M: Andrzej Zaborowski <balrogg@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/spitz.c |  | ||||||
|  |  | ||||||
| Stellaris |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/stellaris.c |  | ||||||
|  |  | ||||||
| Versatile PB |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/versatilepb.c |  | ||||||
|  |  | ||||||
| CRIS Machines |  | ||||||
| ------------- |  | ||||||
| Axis Dev88 |  | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/axis_dev88.c |  | ||||||
|  |  | ||||||
| etraxfs |  | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/etraxfs.c |  | ||||||
|  |  | ||||||
| M68K Machines |  | ||||||
| ------------- |  | ||||||
| an5206 |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/an5206.c |  | ||||||
|  |  | ||||||
| dummy_m68k |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/dummy_m68k.c |  | ||||||
|  |  | ||||||
| mcf5208 |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/mcf5208.c |  | ||||||
|  |  | ||||||
| MicroBlaze Machines |  | ||||||
| ------------------- |  | ||||||
| petalogix_s3adsp1800 |  | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/petalogix_s3adsp1800.c |  | ||||||
|  |  | ||||||
| MIPS Machines |  | ||||||
| ------------- |  | ||||||
| Jazz |  | ||||||
| M: Hervé Poussineau <hpoussin@reactos.org> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/mips_jazz.c |  | ||||||
|  |  | ||||||
| Malta |  | ||||||
| M: Aurelien Jarno <aurelien@aurel32.net> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/mips_malta.c |  | ||||||
|  |  | ||||||
| Mipssim |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: hw/mips_mipssim.c |  | ||||||
|  |  | ||||||
| R4000 |  | ||||||
| M: Aurelien Jarno <aurelien@aurel32.net> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/mips_r4k.c |  | ||||||
|  |  | ||||||
| PowerPC Machines |  | ||||||
| ---------------- | ---------------- | ||||||
| 405 |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/ppc405_boards.c |  | ||||||
|  |  | ||||||
| New World | Fabrice Bellard | ||||||
| M: Alexander Graf <agraf@suse.de> | Paul Brook | ||||||
| S: Maintained |  | ||||||
| F: hw/ppc_newworld.c |  | ||||||
|  |  | ||||||
| Old World | CPU cores: | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/ppc_oldworld.c |  | ||||||
|  |  | ||||||
| Prep |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: hw/ppc_prep.c |  | ||||||
|  |  | ||||||
| SH4 Machines |  | ||||||
| ------------ |  | ||||||
| R2D |  | ||||||
| M: Magnus Damm <magnus.damm@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/r2d.c |  | ||||||
|  |  | ||||||
| Shix |  | ||||||
| M: Magnus Damm <magnus.damm@gmail.com> |  | ||||||
| S: Orphan |  | ||||||
| F: hw/shix.c |  | ||||||
|  |  | ||||||
| SPARC Machines |  | ||||||
| -------------- |  | ||||||
| Sun4m |  | ||||||
| M: Blue Swirl <blauwirbel@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/sun4m.c |  | ||||||
|  |  | ||||||
| Sun4u |  | ||||||
| M: Blue Swirl <blauwirbel@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/sun4u.c |  | ||||||
|  |  | ||||||
| S390 Machines |  | ||||||
| ------------- |  | ||||||
| S390 Virtio |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/s390-*.c |  | ||||||
|  |  | ||||||
| X86 Machines |  | ||||||
| ------------ |  | ||||||
| PC |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/pc.[ch] hw/pc_piix.c |  | ||||||
|  |  | ||||||
| Devices |  | ||||||
| ------- |  | ||||||
| IDE |  | ||||||
| M: Kevin Wolf <kwolf@redhat.com> |  | ||||||
| S: Odd Fixes |  | ||||||
| F: hw/ide/ |  | ||||||
|  |  | ||||||
| PCI |  | ||||||
| M: Michael S. Tsirkin <mst@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/pci* |  | ||||||
| F: hw/piix* |  | ||||||
|  |  | ||||||
| SCSI |  | ||||||
| M: Paul Brook <paul@codesourcery.com> |  | ||||||
| M: Kevin Wolf <kwolf@redhat.com> |  | ||||||
| S: Odd Fixes |  | ||||||
| F: hw/lsi53c895a.c |  | ||||||
| F: hw/scsi* |  | ||||||
|  |  | ||||||
| USB |  | ||||||
| M: Gerd Hoffmann <kraxel@redhat.com> |  | ||||||
| S: Maintained |  | ||||||
| F: hw/usb* |  | ||||||
|  |  | ||||||
| vhost |  | ||||||
| M: Michael S. Tsirkin <mst@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/vhost* |  | ||||||
|  |  | ||||||
| virtio |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/virtio* |  | ||||||
|  |  | ||||||
| virtio-9p |  | ||||||
| M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/virtio-9p* |  | ||||||
|  |  | ||||||
| virtio-blk |  | ||||||
| M: Kevin Wolf <kwolf@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/virtio-blk* |  | ||||||
|  |  | ||||||
| virtio-serial |  | ||||||
| M: Amit Shah <amit.shah@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: hw/virtio-serial* |  | ||||||
| F: hw/virtio-console* |  | ||||||
|  |  | ||||||
| Subsystems |  | ||||||
| ---------- | ---------- | ||||||
| Audio |  | ||||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> |  | ||||||
| S: Maintained |  | ||||||
| F: audio/ |  | ||||||
|  |  | ||||||
| Block | x86                Fabrice Bellard | ||||||
| M: Kevin Wolf <kwolf@redhat.com> | ARM                Paul Brook | ||||||
| S: Supported | SPARC              Blue Swirl | ||||||
| F: block* | MIPS               Thiemo Seufer | ||||||
| F: block/ | PowerPC            ? | ||||||
|  | M68K               Paul Brook | ||||||
|  | SH4                ? | ||||||
|  | CRIS               Edgar E. Iglesias | ||||||
|  | Alpha              ? | ||||||
|  | MicroBlaze         Edgar E. Iglesias | ||||||
|  | S390               ? | ||||||
|  |  | ||||||
| Character Devices | Machines (sorted by CPU): | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| S: Maintained |  | ||||||
| F: qemu-char.c |  | ||||||
|  |  | ||||||
| GDB stub |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Odd Fixes |  | ||||||
| F: gdbstub* |  | ||||||
| F: gdb-xml/ |  | ||||||
|  |  | ||||||
| SPICE |  | ||||||
| M: Gerd Hoffmann <kraxel@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: ui/qemu-spice.h |  | ||||||
| F: ui/spice-*.c |  | ||||||
| F: audio/spiceaudio.c |  | ||||||
| F: hw/qxl* |  | ||||||
|  |  | ||||||
| Graphics |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| S: Maintained |  | ||||||
| F: ui/ |  | ||||||
|  |  | ||||||
| Main loop |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| S: Supported |  | ||||||
| F: vl.c |  | ||||||
|  |  | ||||||
| Monitor (QMP/HMP) |  | ||||||
| M: Luiz Capitulino <lcapitulino@redhat.com> |  | ||||||
| M: Markus Armbruster <armbru@redhat.com> |  | ||||||
| S: Supported |  | ||||||
| F: monitor.c |  | ||||||
|  |  | ||||||
| Network device layer |  | ||||||
| M: Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| M: Mark McLoughlin <markmc@redhat.com> |  | ||||||
| S: Maintained |  | ||||||
| F: net/ |  | ||||||
|  |  | ||||||
| SLIRP |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: slirp/ |  | ||||||
|  |  | ||||||
| Usermode Emulation |  | ||||||
| ------------------ |  | ||||||
| BSD user |  | ||||||
| M: Blue Swirl <blauwirbel@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: bsd-user/ |  | ||||||
|  |  | ||||||
| Darwin user |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Orphan |  | ||||||
| F: darwin-user/ |  | ||||||
|  |  | ||||||
| Linux user |  | ||||||
| M: Riku Voipio <riku.voipio@iki.fi> |  | ||||||
| S: Maintained |  | ||||||
| F: linux-user/ |  | ||||||
|  |  | ||||||
| Tiny Code Generator (TCG) |  | ||||||
| ------------------------- | ------------------------- | ||||||
| Common code |  | ||||||
| M: qemu-devel@nongnu.org |  | ||||||
| S: Maintained |  | ||||||
| F: tcg/ |  | ||||||
|  |  | ||||||
| ARM target | x86 | ||||||
| M: Andrzej Zaborowski <balrogg@gmail.com> |   pc.c                    Fabrice Bellard (new maintainer needed) | ||||||
| S: Maintained | ARM | ||||||
| F: tcg/arm/ |   integratorcp.c          Paul Brook | ||||||
|  |   versatilepb.c           Paul Brook | ||||||
|  |   Real View               Paul Brook | ||||||
|  |   spitz.c                 Andrzej Zaborowski | ||||||
|  |   palm.c                  Andrzej Zaborowski | ||||||
|  |   nseries.c               Andrzej Zaborowski | ||||||
|  |   stellaris.c             Paul Brook | ||||||
|  |   gumstix.c               Thorsten Zitterell | ||||||
|  |   mainstone.c             Armin Kuster | ||||||
|  |   musicpal.c              Jan Kiszka | ||||||
|  | SPARC | ||||||
|  |   sun4u.c                 Blue Swirl | ||||||
|  |   sun4m.c                 Blue Swirl | ||||||
|  | MIPS | ||||||
|  |   mips_r4k.c              Aurelien Jarno | ||||||
|  |   mips_malta.c            Aurelien Jarno | ||||||
|  |   mips_jazz.c             Hervé Poussineau | ||||||
|  |   mips_mipssim.c          Thiemo Seufer | ||||||
|  | PowerPC | ||||||
|  |   ppc_prep.c              ? | ||||||
|  |   ppc_oldworld.c          Fabrice Bellard | ||||||
|  |   ppc_chrp.c              Fabrice Bellard | ||||||
|  |   ppc405_boards.c         ? | ||||||
|  | M86K | ||||||
|  |   mcf5208.c               Paul Brook | ||||||
|  |   an5206.c                Paul Brook | ||||||
|  |   dummy_m68k.c            Paul Brook | ||||||
|  | SH4 | ||||||
|  |   shix.c                  ? | ||||||
|  |   r2d.c                   Magnus Damm | ||||||
|  | CRIS | ||||||
|  |   etraxfs.c               Edgar E. Iglesias | ||||||
|  |   axis_dev88.c            Edgar E. Iglesias | ||||||
|  | Alpha | ||||||
|  | MicroBlaze | ||||||
|  |   petalogix_s3adsp1800.c  Edgar E. Iglesias | ||||||
|  | S390 | ||||||
|  |   s390-*.c                Alexander Graf | ||||||
|  |  | ||||||
| HPPA target | Generic Subsystems: | ||||||
| M: Richard Henderson <rth@twiddle.net> | -------------------   | ||||||
| S: Maintained |  | ||||||
| F: tcg/hppa/ |  | ||||||
|  |  | ||||||
| i386 target | Dynamic translator        Fabrice Bellard | ||||||
| M: qemu-devel@nongnu.org | Main loop                 Fabrice Bellard (new maintainer needed) | ||||||
| S: Maintained | TCG                       Fabrice Bellard | ||||||
| F: tcg/i386/ | IDE device                ? | ||||||
|  | SCSI device               Paul Brook | ||||||
| IA64 target | PCI layer                 ? | ||||||
| M: Aurelien Jarno <aurelien@aurel32.net> | USB layer                 ? | ||||||
| S: Maintained | Block layer               ? | ||||||
| F: tcg/ia64/ | Graphic layer             ? | ||||||
|  | Audio device layer        Vassili Karpov (malc) | ||||||
| MIPS target | Character device layer    ? | ||||||
| M: Aurelien Jarno <aurelien@aurel32.ne> | Network device layer      ? | ||||||
| S: Maintained | GDB stub                  ? | ||||||
| F: tcg/mips/ | Linux user                ? | ||||||
|  | Darwin user               ? | ||||||
| PPC | SLIRP                     ? | ||||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> |  | ||||||
| S: Maintained |  | ||||||
| F: tcg/ppc/ |  | ||||||
|  |  | ||||||
| PPC64 target |  | ||||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> |  | ||||||
| S: Maintained |  | ||||||
| F: tcg/ppc64/ |  | ||||||
|  |  | ||||||
| S390 target |  | ||||||
| M: Alexander Graf <agraf@suse.de> |  | ||||||
| M: Richard Henderson <rth@twiddle.net> |  | ||||||
| S: Maintained |  | ||||||
| F: tcg/s390/ |  | ||||||
|  |  | ||||||
| SPARC target |  | ||||||
| M: Blue Swirl <blauwirbel@gmail.com> |  | ||||||
| S: Maintained |  | ||||||
| F: tcg/sparc/ |  | ||||||
|   | |||||||
							
								
								
									
										402
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										402
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,16 +1,14 @@ | |||||||
| # Makefile for QEMU. | # Makefile for QEMU. | ||||||
|  |  | ||||||
| GENERATED_HEADERS = config-host.h trace.h qemu-options.def | # This needs to be defined before rules.mak | ||||||
| ifeq ($(TRACE_BACKEND),dtrace) | GENERATED_HEADERS = config-host.h | ||||||
| GENERATED_HEADERS += trace-dtrace.h |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| ifneq ($(wildcard config-host.mak),) | ifneq ($(wildcard config-host.mak),) | ||||||
| # Put the all: rule here so that config-host.mak can contain dependencies. | # Put the all: rule here so that config-host.mak can contain dependencies. | ||||||
| all: build-all | all: build-all | ||||||
| include config-host.mak | include config-host.mak | ||||||
| include $(SRC_PATH)/rules.mak | include $(SRC_PATH)/rules.mak | ||||||
| config-host.mak: $(SRC_PATH)/configure | config-host.mak: configure | ||||||
| 	@echo $@ is out-of-date, running configure | 	@echo $@ is out-of-date, running configure | ||||||
| 	@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh | 	@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh | ||||||
| else | else | ||||||
| @@ -25,45 +23,32 @@ Makefile: ; | |||||||
| configure: ; | configure: ; | ||||||
|  |  | ||||||
| .PHONY: all clean cscope distclean dvi html info install install-doc \ | .PHONY: all clean cscope distclean dvi html info install install-doc \ | ||||||
| 	pdf recurse-all speed tar tarbin test build-all | 	recurse-all speed tar tarbin test build-all | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) | VPATH=$(SRC_PATH):$(SRC_PATH)/hw | ||||||
|  |  | ||||||
| LIBS+=-lz $(LIBS_TOOLS) | LIBS+=-lz $(LIBS_TOOLS) | ||||||
|  |  | ||||||
| ifdef BUILD_DOCS | ifdef BUILD_DOCS | ||||||
| DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt | DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 | ||||||
| else | else | ||||||
| DOCS= | DOCS= | ||||||
| endif | endif | ||||||
|  |  | ||||||
| SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) | SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) | ||||||
| SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) | SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) | ||||||
| SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS)) |  | ||||||
|  |  | ||||||
| config-all-devices.mak: $(SUBDIR_DEVICES_MAK) | config-all-devices.mak: $(SUBDIR_DEVICES_MAK) | ||||||
| 	$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@,"  GEN   $@") | 	$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@,"  GEN   $@") | ||||||
|  |  | ||||||
| -include $(SUBDIR_DEVICES_MAK_DEP) |  | ||||||
|  |  | ||||||
| %/config-devices.mak: default-configs/%.mak | %/config-devices.mak: default-configs/%.mak | ||||||
| 	$(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, "  GEN   $@") | 	$(call quiet-command,cat $< > $@.tmp, "  GEN   $@") | ||||||
| 	@if test -f $@; then \ | 	@if test -f $@ ; then \ | ||||||
| 	  if cmp -s $@.old $@; then \ | 	  echo "WARNING: $@ out of date." ;\ | ||||||
| 	    mv $@.tmp $@; \ | 	  echo "Run \"make defconfig\" to regenerate." ; \ | ||||||
| 	    cp -p $@ $@.old; \ | 	  rm $@.tmp ; \ | ||||||
| 	  else \ |  | ||||||
| 	    if test -f $@.old; then \ |  | ||||||
| 	      echo "WARNING: $@ (user modified) out of date.";\ |  | ||||||
| 	    else \ |  | ||||||
| 	      echo "WARNING: $@ out of date.";\ |  | ||||||
| 	    fi; \ |  | ||||||
| 	    echo "Run \"make defconfig\" to regenerate."; \ |  | ||||||
| 	    rm $@.tmp; \ |  | ||||||
| 	  fi; \ |  | ||||||
| 	 else \ | 	 else \ | ||||||
| 	  mv $@.tmp $@; \ | 	  mv $@.tmp $@ ; \ | ||||||
| 	  cp -p $@ $@.old; \ |  | ||||||
| 	 fi | 	 fi | ||||||
|  |  | ||||||
| defconfig: | defconfig: | ||||||
| @@ -75,22 +60,30 @@ build-all: $(DOCS) $(TOOLS) recurse-all | |||||||
|  |  | ||||||
| config-host.h: config-host.h-timestamp | config-host.h: config-host.h-timestamp | ||||||
| config-host.h-timestamp: config-host.mak | config-host.h-timestamp: config-host.mak | ||||||
| qemu-options.def: $(SRC_PATH)/qemu-options.hx |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $@") |  | ||||||
|  |  | ||||||
| SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | ||||||
|  |  | ||||||
|  | ifeq ($(KVM_KMOD),yes) | ||||||
|  |  | ||||||
|  | .PHONEY: kvm-kmod | ||||||
|  |  | ||||||
|  | all: kvm-kmod | ||||||
|  |  | ||||||
|  | kvm-kmod: | ||||||
|  | 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C kvm/kernel V="$(V)" ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | endif | ||||||
|  |  | ||||||
| subdir-%: $(GENERATED_HEADERS) | subdir-%: $(GENERATED_HEADERS) | ||||||
| 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) | 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) | ||||||
|  |  | ||||||
| ifneq ($(wildcard config-host.mak),) | $(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a | ||||||
| include $(SRC_PATH)/Makefile.objs |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| $(common-obj-y): $(GENERATED_HEADERS) | $(filter %-user,$(SUBDIR_RULES)): libuser.a | ||||||
| $(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis |  | ||||||
|  |  | ||||||
| $(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser | libuser.a: $(GENERATED_HEADERS) | ||||||
|  | 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libuser V="$(V)" TARGET_DIR="libuser/" all,) | ||||||
|  |  | ||||||
| ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ||||||
| romsubdir-%: | romsubdir-%: | ||||||
| @@ -100,102 +93,194 @@ ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) | |||||||
|  |  | ||||||
| recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) | recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) | ||||||
|  |  | ||||||
|  | ####################################################################### | ||||||
|  | # QObject | ||||||
|  | qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o | ||||||
|  | qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o | ||||||
|  | qobject-obj-y += qerror.o | ||||||
|  |  | ||||||
|  | ####################################################################### | ||||||
|  | # block-obj-y is code used by both qemu system emulation and qemu-img | ||||||
|  |  | ||||||
|  | block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o | ||||||
|  | block-obj-y += nbd.o block.o aio.o aes.o osdep.o | ||||||
|  | block-obj-$(CONFIG_POSIX) += posix-aio-compat.o | ||||||
|  | block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | ||||||
|  | block-obj-$(CONFIG_POSIX) += compatfd.o | ||||||
|  |  | ||||||
|  | block-nested-y += cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o | ||||||
|  | block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o | ||||||
|  | block-nested-y += parallels.o nbd.o | ||||||
|  | block-nested-$(CONFIG_WIN32) += raw-win32.o | ||||||
|  | block-nested-$(CONFIG_POSIX) += raw-posix.o | ||||||
|  | block-nested-$(CONFIG_CURL) += curl.o | ||||||
|  |  | ||||||
|  | block-obj-y +=  $(addprefix block/, $(block-nested-y)) | ||||||
|  |  | ||||||
|  | net-obj-y = net.o | ||||||
|  | net-nested-y = queue.o checksum.o util.o | ||||||
|  | net-nested-y += socket.o | ||||||
|  | net-nested-y += dump.o | ||||||
|  | net-nested-$(CONFIG_POSIX) += tap.o | ||||||
|  | net-nested-$(CONFIG_LINUX) += tap-linux.o | ||||||
|  | net-nested-$(CONFIG_WIN32) += tap-win32.o | ||||||
|  | net-nested-$(CONFIG_BSD) += tap-bsd.o | ||||||
|  | net-nested-$(CONFIG_SOLARIS) += tap-solaris.o | ||||||
|  | net-nested-$(CONFIG_AIX) += tap-aix.o | ||||||
|  | net-nested-$(CONFIG_SLIRP) += slirp.o | ||||||
|  | net-nested-$(CONFIG_VDE) += vde.o | ||||||
|  | net-obj-y += $(addprefix net/, $(net-nested-y)) | ||||||
|  |  | ||||||
|  | ###################################################################### | ||||||
|  | # libqemu_common.a: Target independent part of system emulation. The | ||||||
|  | # long term path is to suppress *all* target specific code in case of | ||||||
|  | # system emulation, i.e. a single QEMU executable should support all | ||||||
|  | # CPUs and machines. | ||||||
|  |  | ||||||
|  | obj-y = $(block-obj-y) | ||||||
|  | obj-y += $(net-obj-y) | ||||||
|  | obj-y += $(qobject-obj-y) | ||||||
|  | obj-y += readline.o console.o | ||||||
|  |  | ||||||
|  | obj-y += tcg-runtime.o host-utils.o | ||||||
|  | obj-y += irq.o ioport.o | ||||||
|  | obj-$(CONFIG_PTIMER) += ptimer.o | ||||||
|  | obj-$(CONFIG_MAX7310) += max7310.o | ||||||
|  | obj-$(CONFIG_WM8750) += wm8750.o | ||||||
|  | obj-$(CONFIG_TWL92230) += twl92230.o | ||||||
|  | obj-$(CONFIG_TSC2005) += tsc2005.o | ||||||
|  | obj-$(CONFIG_LM832X) += lm832x.o | ||||||
|  | obj-$(CONFIG_TMP105) += tmp105.o | ||||||
|  | obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o | ||||||
|  | obj-$(CONFIG_SSD0303) += ssd0303.o | ||||||
|  | obj-$(CONFIG_SSD0323) += ssd0323.o | ||||||
|  | obj-$(CONFIG_ADS7846) += ads7846.o | ||||||
|  | obj-$(CONFIG_MAX111X) += max111x.o | ||||||
|  | obj-$(CONFIG_DS1338) += ds1338.o | ||||||
|  | obj-y += i2c.o smbus.o smbus_eeprom.o | ||||||
|  | obj-y += eeprom93xx.o | ||||||
|  | obj-y += scsi-disk.o cdrom.o | ||||||
|  | obj-y += scsi-generic.o scsi-bus.o | ||||||
|  | obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o | ||||||
|  | obj-y += usb-serial.o usb-net.o usb-bus.o | ||||||
|  | obj-$(CONFIG_SSI) += ssi.o | ||||||
|  | obj-$(CONFIG_SSI_SD) += ssi-sd.o | ||||||
|  | obj-$(CONFIG_SD) += sd.o | ||||||
|  | obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o | ||||||
|  | obj-y += bt-hci-csr.o | ||||||
|  | obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o | ||||||
|  | obj-y += qemu-char.o aio.o savevm.o | ||||||
|  | obj-y += msmouse.o ps2.o | ||||||
|  | obj-y += qdev.o qdev-properties.o | ||||||
|  | obj-y += qemu-config.o block-migration.o | ||||||
|  |  | ||||||
|  | obj-$(CONFIG_BRLAPI) += baum.o | ||||||
|  | obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o | ||||||
|  |  | ||||||
| audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) | audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) | ||||||
|  |  | ||||||
|  | audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o | ||||||
|  | audio-obj-$(CONFIG_SDL) += sdlaudio.o | ||||||
|  | audio-obj-$(CONFIG_OSS) += ossaudio.o | ||||||
|  | audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o | ||||||
|  | audio-obj-$(CONFIG_ALSA) += alsaaudio.o | ||||||
|  | audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o | ||||||
|  | audio-obj-$(CONFIG_FMOD) += fmodaudio.o | ||||||
|  | audio-obj-$(CONFIG_ESD) += esdaudio.o | ||||||
|  | audio-obj-$(CONFIG_PA) += paaudio.o | ||||||
|  | audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o | ||||||
|  | audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o | ||||||
|  | audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | ||||||
|  | audio-obj-y += wavcapture.o | ||||||
|  | obj-y += $(addprefix audio/, $(audio-obj-y)) | ||||||
|  |  | ||||||
|  | obj-y += keymaps.o | ||||||
|  | obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o | ||||||
|  | obj-$(CONFIG_CURSES) += curses.o | ||||||
|  | obj-y += vnc.o acl.o d3des.o | ||||||
|  | obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o | ||||||
|  | obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o | ||||||
|  | obj-$(CONFIG_COCOA) += cocoa.o | ||||||
|  | obj-$(CONFIG_IOTHREAD) += qemu-thread.o | ||||||
|  |  | ||||||
|  | slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o | ||||||
|  | slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o | ||||||
|  | slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o | ||||||
|  | obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) | ||||||
|  |  | ||||||
|  | # xen backend driver support | ||||||
|  | obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o | ||||||
|  | obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=$(CURL_CFLAGS) | QEMU_CFLAGS+=$(CURL_CFLAGS) | ||||||
|  |  | ||||||
| ui/cocoa.o: ui/cocoa.m | cocoa.o: cocoa.m | ||||||
|  |  | ||||||
| ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) | keymaps.o: keymaps.c keymaps.h | ||||||
|  |  | ||||||
| ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h | ||||||
|  |  | ||||||
|  | sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h | ||||||
|  |  | ||||||
|  | sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) | ||||||
|  |  | ||||||
|  | acl.o: acl.h acl.c | ||||||
|  |  | ||||||
|  | vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h | ||||||
|  |  | ||||||
|  | vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h | ||||||
|  |  | ||||||
|  | vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | ||||||
|  |  | ||||||
|  | vnc-tls.o: vnc-tls.c vnc.h | ||||||
|  |  | ||||||
|  | vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h | ||||||
|  |  | ||||||
|  | vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h | ||||||
|  |  | ||||||
|  | curses.o: curses.c keymaps.h curses_keys.h | ||||||
|  |  | ||||||
| bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) | bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) | ||||||
|  |  | ||||||
| ifeq ($(TRACE_BACKEND),dtrace) | libqemu_common.a: $(obj-y) | ||||||
| trace.h: trace.h-timestamp trace-dtrace.h |  | ||||||
| else |  | ||||||
| trace.h: trace.h-timestamp |  | ||||||
| endif |  | ||||||
| trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@,"  GEN   trace.h") |  | ||||||
| 	@cmp -s $@ trace.h || cp $@ trace.h |  | ||||||
|  |  | ||||||
| trace.c: trace.c-timestamp |  | ||||||
| trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@,"  GEN   trace.c") |  | ||||||
| 	@cmp -s $@ trace.c || cp $@ trace.c |  | ||||||
|  |  | ||||||
| trace.o: trace.c $(GENERATED_HEADERS) |  | ||||||
|  |  | ||||||
| trace-dtrace.h: trace-dtrace.dtrace |  | ||||||
| 	$(call quiet-command,dtrace -o $@ -h -s $<, "  GEN   trace-dtrace.h") |  | ||||||
|  |  | ||||||
| # Normal practice is to name DTrace probe file with a '.d' extension |  | ||||||
| # but that gets picked up by QEMU's Makefile as an external dependancy |  | ||||||
| # rule file. So we use '.dtrace' instead |  | ||||||
| trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp |  | ||||||
| trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@,"  GEN   trace-dtrace.dtrace") |  | ||||||
| 	@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace |  | ||||||
|  |  | ||||||
| trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) |  | ||||||
| 	$(call quiet-command,dtrace -o $@ -G -s $<, "  GEN trace-dtrace.o") |  | ||||||
|  |  | ||||||
| simpletrace.o: simpletrace.c $(GENERATED_HEADERS) |  | ||||||
|  |  | ||||||
| version.o: $(SRC_PATH)/version.rc config-host.mak |  | ||||||
| 	$(call quiet-command,$(WINDRES) -I. -o $@ $<,"  RC    $(TARGET_DIR)$@") |  | ||||||
|  |  | ||||||
| version-obj-$(CONFIG_WIN32) += version.o |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
|  |  | ||||||
| qemu-img.o: qemu-img-cmds.h | qemu-img.o: qemu-img-cmds.h | ||||||
| qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS) |  | ||||||
|  |  | ||||||
| qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | qemu-img$(EXESUF): qemu-img.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) | ||||||
|  |  | ||||||
| qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | qemu-nbd$(EXESUF):  qemu-nbd.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) | ||||||
|  |  | ||||||
| qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | qemu-io$(EXESUF):  qemu-io.o qemu-tool.o cmd.o $(block-obj-y) $(qobject-obj-y) | ||||||
|  |  | ||||||
| qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx | qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $@") | ||||||
|  |  | ||||||
| check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) | check-qint: check-qint.o qint.o qemu-malloc.o | ||||||
|  | check-qstring: check-qstring.o qstring.o qemu-malloc.o | ||||||
| CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) | check-qdict: check-qdict.o qdict.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o | ||||||
|  | check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o | ||||||
| check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS) | check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o | ||||||
| check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS) | check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o | ||||||
| check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS) |  | ||||||
| check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) |  | ||||||
| check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) |  | ||||||
| check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS) |  | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| # avoid old build problems by removing potentially incorrect old files | # avoid old build problems by removing potentially incorrect old files | ||||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||||
| 	rm -f qemu-options.def |  | ||||||
| 	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~ | 	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~ | ||||||
| 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d | 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d | ||||||
| 	rm -f qemu-img-cmds.h | 	rm -f qemu-img-cmds.h | ||||||
| 	rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp |  | ||||||
| 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp |  | ||||||
| 	rm -f trace-dtrace.h trace-dtrace.h-timestamp |  | ||||||
| 	$(MAKE) -C tests clean | 	$(MAKE) -C tests clean | ||||||
| 	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \ | 	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser; do \ | ||||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||||
| 	rm -f $$d/qemu-options.def; \ |  | ||||||
|         done |         done | ||||||
|  |  | ||||||
| distclean: clean | distclean: clean | ||||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi | 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi | ||||||
| 	rm -f config-all-devices.mak | 	rm -f config-all-devices.mak | ||||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr | 	rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr} | ||||||
| 	rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr | 	for d in $(TARGET_DIRS) libhw32 libhw64 libuser; do \ | ||||||
| 	for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \ |  | ||||||
| 	rm -rf $$d || exit 1 ; \ | 	rm -rf $$d || exit 1 ; \ | ||||||
|         done |         done | ||||||
|  |  | ||||||
| @@ -204,16 +289,15 @@ ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | |||||||
| common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr | common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr | ||||||
|  |  | ||||||
| ifdef INSTALL_BLOBS | ifdef INSTALL_BLOBS | ||||||
| BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \ | BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ | ||||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ | video.x openbios-sparc32 openbios-sparc64 openbios-ppc \ | ||||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \ | pxe-e1000.bin pxe-i82559er.bin \ | ||||||
| gpxe-eepro100-80861209.rom \ |  | ||||||
| pxe-e1000.bin \ |  | ||||||
| pxe-ne2k_pci.bin pxe-pcnet.bin \ | pxe-ne2k_pci.bin pxe-pcnet.bin \ | ||||||
| pxe-rtl8139.bin pxe-virtio.bin \ | pxe-rtl8139.bin pxe-virtio.bin \ | ||||||
| bamboo.dtb petalogix-s3adsp1800.dtb \ | bamboo.dtb petalogix-s3adsp1800.dtb \ | ||||||
| multiboot.bin linuxboot.bin \ | multiboot.bin linuxboot.bin | ||||||
| s390-zipl.rom | BLOBS += extboot.bin | ||||||
|  | BLOBS += vapic.bin | ||||||
| else | else | ||||||
| BLOBS= | BLOBS= | ||||||
| endif | endif | ||||||
| @@ -228,11 +312,7 @@ ifdef CONFIG_POSIX | |||||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||||
| endif | endif | ||||||
|  |  | ||||||
| install-sysconfig: | install: all $(if $(BUILD_DOCS),install-doc) | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu" |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu" |  | ||||||
|  |  | ||||||
| install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig |  | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(bindir)" | 	$(INSTALL_DIR) "$(DESTDIR)$(bindir)" | ||||||
| ifneq ($(TOOLS),) | ifneq ($(TOOLS),) | ||||||
| 	$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" | 	$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" | ||||||
| @@ -240,7 +320,12 @@ endif | |||||||
| ifneq ($(BLOBS),) | ifneq ($(BLOBS),) | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)" | 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)" | ||||||
| 	set -e; for x in $(BLOBS); do \ | 	set -e; for x in $(BLOBS); do \ | ||||||
|  | 	    if [ -f $(SRC_PATH)/pc-bios/$$x ];then \ | ||||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ | 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ | ||||||
|  | 	    fi \ | ||||||
|  | 	    ; if [ -f pc-bios/optionrom/$$x ];then \ | ||||||
|  | 		$(INSTALL_DATA) pc-bios/optionrom/$$x "$(DESTDIR)$(datadir)"; \ | ||||||
|  | 	    fi \ | ||||||
| 	done | 	done | ||||||
| endif | endif | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" | 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" | ||||||
| @@ -250,6 +335,9 @@ endif | |||||||
| 	for d in $(TARGET_DIRS); do \ | 	for d in $(TARGET_DIRS); do \ | ||||||
| 	$(MAKE) -C $$d $@ || exit 1 ; \ | 	$(MAKE) -C $$d $@ || exit 1 ; \ | ||||||
|         done |         done | ||||||
|  | ifeq ($(KVM_KMOD),yes) | ||||||
|  | 	$(MAKE) -C kvm/kernel $@ | ||||||
|  | endif | ||||||
|  |  | ||||||
| # various test targets | # various test targets | ||||||
| test speed: all | test speed: all | ||||||
| @@ -265,60 +353,49 @@ cscope: | |||||||
| 	cscope -b | 	cscope -b | ||||||
|  |  | ||||||
| # documentation | # documentation | ||||||
| MAKEINFO=makeinfo |  | ||||||
| MAKEINFOFLAGS=--no-headers --no-split --number-sections |  | ||||||
| TEXIFLAG=$(if $(V),,--quiet) |  | ||||||
| %.dvi: %.texi |  | ||||||
| 	$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"  GEN   $@") |  | ||||||
|  |  | ||||||
| %.html: %.texi | %.html: %.texi | ||||||
| 	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \ | 	$(call quiet-command,texi2html -I=. -monolithic -number $<,"  GEN   $@") | ||||||
| 	"  GEN   $@") |  | ||||||
|  |  | ||||||
| %.info: %.texi | %.info: %.texi | ||||||
| 	$(call quiet-command,$(MAKEINFO) $< -o $@,"  GEN   $@") | 	$(call quiet-command,makeinfo -I . $< -o $@,"  GEN   $@") | ||||||
|  |  | ||||||
| %.pdf: %.texi | %.dvi: %.texi | ||||||
| 	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"  GEN   $@") | 	$(call quiet-command,texi2dvi -I . $<,"  GEN   $@") | ||||||
|  |  | ||||||
| qemu-options.texi: $(SRC_PATH)/qemu-options.hx | qemu-options.texi: $(SRC_PATH)/qemu-options.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||||
|  |  | ||||||
| qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx | qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||||
|  |  | ||||||
| QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@,"  GEN   $@") |  | ||||||
|  |  | ||||||
| qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx | qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||||
|  |  | ||||||
| qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi | qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi | ||||||
| 	$(call quiet-command, \ | 	$(call quiet-command, \ | ||||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ | 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu.pod && \ | ||||||
| 	  pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ | 	  pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ | ||||||
| 	  "  GEN   $@") | 	  "  GEN   $@") | ||||||
|  |  | ||||||
| qemu-img.1: qemu-img.texi qemu-img-cmds.texi | qemu-img.1: qemu-img.texi qemu-img-cmds.texi | ||||||
| 	$(call quiet-command, \ | 	$(call quiet-command, \ | ||||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ | 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod && \ | ||||||
| 	  pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ | 	  pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ | ||||||
| 	  "  GEN   $@") | 	  "  GEN   $@") | ||||||
|  |  | ||||||
| qemu-nbd.8: qemu-nbd.texi | qemu-nbd.8: qemu-nbd.texi | ||||||
| 	$(call quiet-command, \ | 	$(call quiet-command, \ | ||||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ | 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \ | ||||||
| 	  pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ | 	  pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ | ||||||
| 	  "  GEN   $@") | 	  "  GEN   $@") | ||||||
|  |  | ||||||
| dvi: qemu-doc.dvi qemu-tech.dvi |  | ||||||
| html: qemu-doc.html qemu-tech.html |  | ||||||
| info: qemu-doc.info qemu-tech.info | info: qemu-doc.info qemu-tech.info | ||||||
| pdf: qemu-doc.pdf qemu-tech.pdf |  | ||||||
|  |  | ||||||
| qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ | dvi: qemu-doc.dvi qemu-tech.dvi | ||||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi \ |  | ||||||
| 	qemu-monitor.texi qemu-img-cmds.texi | html: qemu-doc.html qemu-tech.html | ||||||
|  |  | ||||||
|  | qemu-doc.dvi qemu-doc.html qemu-doc.info: qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-monitor.texi qemu-img-cmds.texi | ||||||
|  |  | ||||||
| VERSION ?= $(shell cat VERSION) | VERSION ?= $(shell cat VERSION) | ||||||
| FILE = qemu-$(VERSION) | FILE = qemu-$(VERSION) | ||||||
| @@ -330,28 +407,50 @@ tar: | |||||||
| 	cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn | 	cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn | ||||||
| 	rm -rf /tmp/$(FILE) | 	rm -rf /tmp/$(FILE) | ||||||
|  |  | ||||||
| SYSTEM_TARGETS=$(filter %-softmmu,$(TARGET_DIRS)) |  | ||||||
| SYSTEM_PROGS=$(patsubst qemu-system-i386,qemu, \ |  | ||||||
|              $(patsubst %-softmmu,qemu-system-%, \ |  | ||||||
|              $(SYSTEM_TARGETS))) |  | ||||||
|  |  | ||||||
| USER_TARGETS=$(filter %-user,$(TARGET_DIRS)) |  | ||||||
| USER_PROGS=$(patsubst %-bsd-user,qemu-%, \ |  | ||||||
|            $(patsubst %-darwin-user,qemu-%, \ |  | ||||||
|            $(patsubst %-linux-user,qemu-%, \ |  | ||||||
|            $(USER_TARGETS)))) |  | ||||||
|  |  | ||||||
| # generate a binary distribution | # generate a binary distribution | ||||||
| tarbin: | tarbin: | ||||||
| 	cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \ | 	cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \ | ||||||
| 	$(patsubst %,$(bindir)/%, $(SYSTEM_PROGS)) \ | 	$(bindir)/qemu \ | ||||||
| 	$(patsubst %,$(bindir)/%, $(USER_PROGS)) \ | 	$(bindir)/qemu-system-x86_64 \ | ||||||
|  | 	$(bindir)/qemu-system-arm \ | ||||||
|  | 	$(bindir)/qemu-system-cris \ | ||||||
|  | 	$(bindir)/qemu-system-m68k \ | ||||||
|  | 	$(bindir)/qemu-system-microblaze \ | ||||||
|  | 	$(bindir)/qemu-system-mips \ | ||||||
|  | 	$(bindir)/qemu-system-mipsel \ | ||||||
|  | 	$(bindir)/qemu-system-mips64 \ | ||||||
|  | 	$(bindir)/qemu-system-mips64el \ | ||||||
|  | 	$(bindir)/qemu-system-ppc \ | ||||||
|  | 	$(bindir)/qemu-system-ppcemb \ | ||||||
|  | 	$(bindir)/qemu-system-ppc64 \ | ||||||
|  | 	$(bindir)/qemu-system-sh4 \ | ||||||
|  | 	$(bindir)/qemu-system-sh4eb \ | ||||||
|  | 	$(bindir)/qemu-system-sparc \ | ||||||
|  | 	$(bindir)/qemu-i386 \ | ||||||
|  | 	$(bindir)/qemu-x86_64 \ | ||||||
|  | 	$(bindir)/qemu-alpha \ | ||||||
|  | 	$(bindir)/qemu-arm \ | ||||||
|  | 	$(bindir)/qemu-armeb \ | ||||||
|  | 	$(bindir)/qemu-cris \ | ||||||
|  | 	$(bindir)/qemu-m68k \ | ||||||
|  | 	$(bindir)/qemu-microblaze \ | ||||||
|  | 	$(bindir)/qemu-mips \ | ||||||
|  | 	$(bindir)/qemu-mipsel \ | ||||||
|  | 	$(bindir)/qemu-ppc \ | ||||||
|  | 	$(bindir)/qemu-ppc64 \ | ||||||
|  | 	$(bindir)/qemu-ppc64abi32 \ | ||||||
|  | 	$(bindir)/qemu-sh4 \ | ||||||
|  | 	$(bindir)/qemu-sh4eb \ | ||||||
|  | 	$(bindir)/qemu-sparc \ | ||||||
|  | 	$(bindir)/qemu-sparc64 \ | ||||||
|  | 	$(bindir)/qemu-sparc32plus \ | ||||||
| 	$(bindir)/qemu-img \ | 	$(bindir)/qemu-img \ | ||||||
| 	$(bindir)/qemu-nbd \ | 	$(bindir)/qemu-nbd \ | ||||||
| 	$(datadir)/bios.bin \ | 	$(datadir)/bios.bin \ | ||||||
| 	$(datadir)/vgabios.bin \ | 	$(datadir)/vgabios.bin \ | ||||||
| 	$(datadir)/vgabios-cirrus.bin \ | 	$(datadir)/vgabios-cirrus.bin \ | ||||||
| 	$(datadir)/ppc_rom.bin \ | 	$(datadir)/ppc_rom.bin \ | ||||||
|  | 	$(datadir)/video.x \ | ||||||
| 	$(datadir)/openbios-sparc32 \ | 	$(datadir)/openbios-sparc32 \ | ||||||
| 	$(datadir)/openbios-sparc64 \ | 	$(datadir)/openbios-sparc64 \ | ||||||
| 	$(datadir)/openbios-ppc \ | 	$(datadir)/openbios-ppc \ | ||||||
| @@ -359,6 +458,7 @@ tarbin: | |||||||
| 	$(datadir)/pxe-rtl8139.bin \ | 	$(datadir)/pxe-rtl8139.bin \ | ||||||
| 	$(datadir)/pxe-pcnet.bin \ | 	$(datadir)/pxe-pcnet.bin \ | ||||||
| 	$(datadir)/pxe-e1000.bin \ | 	$(datadir)/pxe-e1000.bin \ | ||||||
|  | 	$(datadir)/extboot.bin \ | ||||||
| 	$(docdir)/qemu-doc.html \ | 	$(docdir)/qemu-doc.html \ | ||||||
| 	$(docdir)/qemu-tech.html \ | 	$(docdir)/qemu-tech.html \ | ||||||
| 	$(mandir)/man1/qemu.1 \ | 	$(mandir)/man1/qemu.1 \ | ||||||
| @@ -366,4 +466,4 @@ tarbin: | |||||||
| 	$(mandir)/man8/qemu-nbd.8 | 	$(mandir)/man8/qemu-nbd.8 | ||||||
|  |  | ||||||
| # Include automatically generated dependency files | # Include automatically generated dependency files | ||||||
| -include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d) | -include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d) | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							| @@ -1,23 +0,0 @@ | |||||||
| # Makefile for disassemblers. |  | ||||||
|  |  | ||||||
| include ../config-host.mak |  | ||||||
| include config.mak |  | ||||||
| include $(SRC_PATH)/rules.mak |  | ||||||
|  |  | ||||||
| .PHONY: all |  | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH)) |  | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I.. |  | ||||||
|  |  | ||||||
| include $(SRC_PATH)/Makefile.objs |  | ||||||
|  |  | ||||||
| all: $(libdis-y) |  | ||||||
| # Dummy command so that make thinks it has done something |  | ||||||
| 	@true |  | ||||||
|  |  | ||||||
| clean: |  | ||||||
| 	rm -f *.o *.d *.a *~ |  | ||||||
|  |  | ||||||
| # Include automatically generated dependency files |  | ||||||
| -include $(wildcard *.d */*.d) |  | ||||||
							
								
								
									
										39
									
								
								Makefile.hw
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								Makefile.hw
									
									
									
									
									
								
							| @@ -7,18 +7,49 @@ include $(SRC_PATH)/rules.mak | |||||||
|  |  | ||||||
| .PHONY: all | .PHONY: all | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) | VPATH=$(SRC_PATH):$(SRC_PATH)/hw | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu | QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu | ||||||
|  |  | ||||||
| include $(SRC_PATH)/Makefile.objs | obj-y = | ||||||
|  | obj-y += loader.o | ||||||
|  | obj-y += virtio.o | ||||||
|  | obj-y += fw_cfg.o | ||||||
|  | obj-y += watchdog.o | ||||||
|  | obj-$(CONFIG_ECC) += ecc.o | ||||||
|  | obj-$(CONFIG_NAND) += nand.o | ||||||
|  |  | ||||||
| all: $(hw-obj-y) | obj-$(CONFIG_M48T59) += m48t59.o | ||||||
|  | obj-$(CONFIG_ESCC) += escc.o | ||||||
|  |  | ||||||
|  | # PCI watchdog devices | ||||||
|  | obj-y += wdt_i6300esb.o | ||||||
|  |  | ||||||
|  | # MSI-X depends on kvm for interrupt injection, | ||||||
|  | # so moved it from Makefile.hw to Makefile.target for now | ||||||
|  | # obj-y += msix.o | ||||||
|  |  | ||||||
|  | # PCI network cards | ||||||
|  | obj-y += ne2000.o | ||||||
|  |  | ||||||
|  | obj-$(CONFIG_SMC91C111) += smc91c111.o | ||||||
|  | obj-$(CONFIG_LAN9118) += lan9118.o | ||||||
|  |  | ||||||
|  | # SCSI layer | ||||||
|  | obj-y += lsi53c895a.o | ||||||
|  | obj-$(CONFIG_ESP) += esp.o | ||||||
|  |  | ||||||
|  | obj-y += dma-helpers.o sysbus.o isa-bus.o | ||||||
|  | obj-$(CONFIG_QDEV_ADDR) += qdev-addr.o | ||||||
|  |  | ||||||
|  | all: $(HWLIB) | ||||||
| # Dummy command so that make thinks it has done something | # Dummy command so that make thinks it has done something | ||||||
| 	@true | 	@true | ||||||
|  |  | ||||||
|  | $(HWLIB): $(obj-y) | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| 	rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ | 	rm -f *.o *.d *.a *~ | ||||||
|  |  | ||||||
| # Include automatically generated dependency files | # Include automatically generated dependency files | ||||||
| -include $(wildcard *.d */*.d) | -include $(wildcard *.d */*.d) | ||||||
|   | |||||||
							
								
								
									
										319
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										319
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,319 +0,0 @@ | |||||||
| ####################################################################### |  | ||||||
| # QObject |  | ||||||
| qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o |  | ||||||
| qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o |  | ||||||
| qobject-obj-y += qerror.o |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # oslib-obj-y is code depending on the OS (win32 vs posix) |  | ||||||
| oslib-obj-y = osdep.o |  | ||||||
| oslib-obj-$(CONFIG_WIN32) += oslib-win32.o |  | ||||||
| oslib-obj-$(CONFIG_POSIX) += oslib-posix.o |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # block-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o |  | ||||||
| block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o |  | ||||||
| block-obj-$(CONFIG_POSIX) += posix-aio-compat.o |  | ||||||
| block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o |  | ||||||
|  |  | ||||||
| block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o |  | ||||||
| block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o |  | ||||||
| block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o |  | ||||||
| block-nested-y += qed-check.o |  | ||||||
| block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o |  | ||||||
| block-nested-$(CONFIG_WIN32) += raw-win32.o |  | ||||||
| block-nested-$(CONFIG_POSIX) += raw-posix.o |  | ||||||
| block-nested-$(CONFIG_CURL) += curl.o |  | ||||||
| block-nested-$(CONFIG_RBD) += rbd.o |  | ||||||
|  |  | ||||||
| block-obj-y +=  $(addprefix block/, $(block-nested-y)) |  | ||||||
|  |  | ||||||
| net-obj-y = net.o |  | ||||||
| net-nested-y = queue.o checksum.o util.o |  | ||||||
| net-nested-y += socket.o |  | ||||||
| net-nested-y += dump.o |  | ||||||
| net-nested-$(CONFIG_POSIX) += tap.o |  | ||||||
| net-nested-$(CONFIG_LINUX) += tap-linux.o |  | ||||||
| net-nested-$(CONFIG_WIN32) += tap-win32.o |  | ||||||
| net-nested-$(CONFIG_BSD) += tap-bsd.o |  | ||||||
| net-nested-$(CONFIG_SOLARIS) += tap-solaris.o |  | ||||||
| net-nested-$(CONFIG_AIX) += tap-aix.o |  | ||||||
| net-nested-$(CONFIG_HAIKU) += tap-haiku.o |  | ||||||
| net-nested-$(CONFIG_SLIRP) += slirp.o |  | ||||||
| net-nested-$(CONFIG_VDE) += vde.o |  | ||||||
| net-obj-y += $(addprefix net/, $(net-nested-y)) |  | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy) |  | ||||||
| # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. |  | ||||||
| # only pull in the actual virtio-9p device if we also enabled virtio. |  | ||||||
| CONFIG_REALLY_VIRTFS=y |  | ||||||
| endif |  | ||||||
| fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o |  | ||||||
| fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y)) |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # libqemu_common.a: Target independent part of system emulation. The |  | ||||||
| # long term path is to suppress *all* target specific code in case of |  | ||||||
| # system emulation, i.e. a single QEMU executable should support all |  | ||||||
| # CPUs and machines. |  | ||||||
|  |  | ||||||
| common-obj-y = $(block-obj-y) blockdev.o |  | ||||||
| common-obj-y += $(net-obj-y) |  | ||||||
| common-obj-y += $(qobject-obj-y) |  | ||||||
| common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX)) |  | ||||||
| common-obj-y += readline.o console.o cursor.o async.o qemu-error.o |  | ||||||
| common-obj-y += $(oslib-obj-y) |  | ||||||
| common-obj-$(CONFIG_WIN32) += os-win32.o |  | ||||||
| common-obj-$(CONFIG_POSIX) += os-posix.o |  | ||||||
|  |  | ||||||
| common-obj-y += tcg-runtime.o host-utils.o |  | ||||||
| common-obj-y += irq.o ioport.o input.o |  | ||||||
| common-obj-$(CONFIG_PTIMER) += ptimer.o |  | ||||||
| common-obj-$(CONFIG_MAX7310) += max7310.o |  | ||||||
| common-obj-$(CONFIG_WM8750) += wm8750.o |  | ||||||
| common-obj-$(CONFIG_TWL92230) += twl92230.o |  | ||||||
| common-obj-$(CONFIG_TSC2005) += tsc2005.o |  | ||||||
| common-obj-$(CONFIG_LM832X) += lm832x.o |  | ||||||
| common-obj-$(CONFIG_TMP105) += tmp105.o |  | ||||||
| common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o |  | ||||||
| common-obj-$(CONFIG_SSD0303) += ssd0303.o |  | ||||||
| common-obj-$(CONFIG_SSD0323) += ssd0323.o |  | ||||||
| common-obj-$(CONFIG_ADS7846) += ads7846.o |  | ||||||
| common-obj-$(CONFIG_MAX111X) += max111x.o |  | ||||||
| common-obj-$(CONFIG_DS1338) += ds1338.o |  | ||||||
| common-obj-y += i2c.o smbus.o smbus_eeprom.o |  | ||||||
| common-obj-y += eeprom93xx.o |  | ||||||
| common-obj-y += scsi-disk.o cdrom.o |  | ||||||
| common-obj-y += scsi-generic.o scsi-bus.o |  | ||||||
| common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o |  | ||||||
| common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o |  | ||||||
| common-obj-$(CONFIG_SSI) += ssi.o |  | ||||||
| common-obj-$(CONFIG_SSI_SD) += ssi-sd.o |  | ||||||
| common-obj-$(CONFIG_SD) += sd.o |  | ||||||
| common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o |  | ||||||
| common-obj-y += bt-hci-csr.o |  | ||||||
| common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o |  | ||||||
| common-obj-y += qemu-char.o savevm.o #aio.o |  | ||||||
| common-obj-y += msmouse.o ps2.o |  | ||||||
| common-obj-y += qdev.o qdev-properties.o |  | ||||||
| common-obj-y += block-migration.o |  | ||||||
| common-obj-y += pflib.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_BRLAPI) += baum.o |  | ||||||
| common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o |  | ||||||
| common-obj-$(CONFIG_WIN32) += version.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o |  | ||||||
|  |  | ||||||
| audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o |  | ||||||
| audio-obj-$(CONFIG_SDL) += sdlaudio.o |  | ||||||
| audio-obj-$(CONFIG_OSS) += ossaudio.o |  | ||||||
| audio-obj-$(CONFIG_SPICE) += spiceaudio.o |  | ||||||
| audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o |  | ||||||
| audio-obj-$(CONFIG_ALSA) += alsaaudio.o |  | ||||||
| audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o |  | ||||||
| audio-obj-$(CONFIG_FMOD) += fmodaudio.o |  | ||||||
| audio-obj-$(CONFIG_ESD) += esdaudio.o |  | ||||||
| audio-obj-$(CONFIG_PA) += paaudio.o |  | ||||||
| audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o |  | ||||||
| audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o |  | ||||||
| audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o |  | ||||||
| audio-obj-y += wavcapture.o |  | ||||||
| common-obj-y += $(addprefix audio/, $(audio-obj-y)) |  | ||||||
|  |  | ||||||
| ui-obj-y += keymaps.o |  | ||||||
| ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o |  | ||||||
| ui-obj-$(CONFIG_CURSES) += curses.o |  | ||||||
| ui-obj-y += vnc.o d3des.o |  | ||||||
| ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o |  | ||||||
| ui-obj-y += vnc-enc-tight.o vnc-palette.o |  | ||||||
| ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o |  | ||||||
| ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o |  | ||||||
| ui-obj-$(CONFIG_COCOA) += cocoa.o |  | ||||||
| ifdef CONFIG_VNC_THREAD |  | ||||||
| ui-obj-y += vnc-jobs-async.o |  | ||||||
| else |  | ||||||
| ui-obj-y += vnc-jobs-sync.o |  | ||||||
| endif |  | ||||||
| common-obj-y += $(addprefix ui/, $(ui-obj-y)) |  | ||||||
|  |  | ||||||
| common-obj-y += iov.o acl.o |  | ||||||
| common-obj-$(CONFIG_THREAD) += qemu-thread.o |  | ||||||
| common-obj-$(CONFIG_IOTHREAD) += compatfd.o |  | ||||||
| common-obj-y += notify.o event_notifier.o |  | ||||||
| common-obj-y += qemu-timer.o qemu-timer-common.o |  | ||||||
|  |  | ||||||
| slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o |  | ||||||
| slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o |  | ||||||
| slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o |  | ||||||
| common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) |  | ||||||
|  |  | ||||||
| # xen backend driver support |  | ||||||
| common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o |  | ||||||
| common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # libuser |  | ||||||
|  |  | ||||||
| user-obj-y = |  | ||||||
| user-obj-y += envlist.o path.o |  | ||||||
| user-obj-y += tcg-runtime.o host-utils.o |  | ||||||
| user-obj-y += cutils.o cache-utils.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # libhw |  | ||||||
|  |  | ||||||
| hw-obj-y = |  | ||||||
| hw-obj-y += vl.o loader.o |  | ||||||
| hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o |  | ||||||
| hw-obj-y += fw_cfg.o |  | ||||||
| hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o |  | ||||||
| hw-obj-$(CONFIG_PCI) += msix.o msi.o |  | ||||||
| hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o |  | ||||||
| hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o |  | ||||||
| hw-obj-y += watchdog.o |  | ||||||
| hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o |  | ||||||
| hw-obj-$(CONFIG_ECC) += ecc.o |  | ||||||
| hw-obj-$(CONFIG_NAND) += nand.o |  | ||||||
| hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o |  | ||||||
| hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_M48T59) += m48t59.o |  | ||||||
| hw-obj-$(CONFIG_ESCC) += escc.o |  | ||||||
| hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_SERIAL) += serial.o |  | ||||||
| hw-obj-$(CONFIG_PARALLEL) += parallel.o |  | ||||||
| hw-obj-$(CONFIG_I8254) += i8254.o |  | ||||||
| hw-obj-$(CONFIG_PCSPK) += pcspk.o |  | ||||||
| hw-obj-$(CONFIG_PCKBD) += pckbd.o |  | ||||||
| hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o |  | ||||||
| hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o |  | ||||||
| hw-obj-$(CONFIG_FDC) += fdc.o |  | ||||||
| hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o |  | ||||||
| hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o |  | ||||||
| hw-obj-$(CONFIG_DMA) += dma.o |  | ||||||
|  |  | ||||||
| # PPC devices |  | ||||||
| hw-obj-$(CONFIG_OPENPIC) += openpic.o |  | ||||||
| hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o |  | ||||||
| # Mac shared devices |  | ||||||
| hw-obj-$(CONFIG_MACIO) += macio.o |  | ||||||
| hw-obj-$(CONFIG_CUDA) += cuda.o |  | ||||||
| hw-obj-$(CONFIG_ADB) += adb.o |  | ||||||
| hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o |  | ||||||
| hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o |  | ||||||
| # OldWorld PowerMac |  | ||||||
| hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o |  | ||||||
| hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o |  | ||||||
| # NewWorld PowerMac |  | ||||||
| hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o |  | ||||||
| hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o |  | ||||||
| # PowerPC E500 boards |  | ||||||
| hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o |  | ||||||
|  |  | ||||||
| # MIPS devices |  | ||||||
| hw-obj-$(CONFIG_PIIX4) += piix4.o |  | ||||||
|  |  | ||||||
| # PCI watchdog devices |  | ||||||
| hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o |  | ||||||
|  |  | ||||||
| # PCI network cards |  | ||||||
| hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o |  | ||||||
| hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o |  | ||||||
| hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o |  | ||||||
| hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o |  | ||||||
| hw-obj-$(CONFIG_E1000_PCI) += e1000.o |  | ||||||
| hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_SMC91C111) += smc91c111.o |  | ||||||
| hw-obj-$(CONFIG_LAN9118) += lan9118.o |  | ||||||
| hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o |  | ||||||
|  |  | ||||||
| # IDE |  | ||||||
| hw-obj-$(CONFIG_IDE_CORE) += ide/core.o |  | ||||||
| hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o |  | ||||||
| hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o |  | ||||||
| hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o |  | ||||||
| hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o |  | ||||||
| hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o |  | ||||||
| hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o |  | ||||||
| hw-obj-$(CONFIG_IDE_VIA) += ide/via.o |  | ||||||
| hw-obj-$(CONFIG_AHCI) += ide/ahci.o |  | ||||||
| hw-obj-$(CONFIG_AHCI) += ide/ich.o |  | ||||||
|  |  | ||||||
| # SCSI layer |  | ||||||
| hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o |  | ||||||
| hw-obj-$(CONFIG_ESP) += esp.o |  | ||||||
|  |  | ||||||
| hw-obj-y += dma-helpers.o sysbus.o isa-bus.o |  | ||||||
| hw-obj-y += qdev-addr.o |  | ||||||
|  |  | ||||||
| # VGA |  | ||||||
| hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o |  | ||||||
| hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o |  | ||||||
| hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o |  | ||||||
| hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_RC4030) += rc4030.o |  | ||||||
| hw-obj-$(CONFIG_DP8393X) += dp8393x.o |  | ||||||
| hw-obj-$(CONFIG_DS1225Y) += ds1225y.o |  | ||||||
| hw-obj-$(CONFIG_MIPSNET) += mipsnet.o |  | ||||||
|  |  | ||||||
| # Sound |  | ||||||
| sound-obj-y = |  | ||||||
| sound-obj-$(CONFIG_SB16) += sb16.o |  | ||||||
| sound-obj-$(CONFIG_ES1370) += es1370.o |  | ||||||
| sound-obj-$(CONFIG_AC97) += ac97.o |  | ||||||
| sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o |  | ||||||
| sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o |  | ||||||
| sound-obj-$(CONFIG_CS4231A) += cs4231a.o |  | ||||||
| sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o |  | ||||||
|  |  | ||||||
| adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 |  | ||||||
| hw-obj-$(CONFIG_SOUND) += $(sound-obj-y) |  | ||||||
|  |  | ||||||
| hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o |  | ||||||
| hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o |  | ||||||
| hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # libdis |  | ||||||
| # NOTE: the disassembler code is only needed for debugging |  | ||||||
|  |  | ||||||
| libdis-y = |  | ||||||
| libdis-$(CONFIG_ALPHA_DIS) += alpha-dis.o |  | ||||||
| libdis-$(CONFIG_ARM_DIS) += arm-dis.o |  | ||||||
| libdis-$(CONFIG_CRIS_DIS) += cris-dis.o |  | ||||||
| libdis-$(CONFIG_HPPA_DIS) += hppa-dis.o |  | ||||||
| libdis-$(CONFIG_I386_DIS) += i386-dis.o |  | ||||||
| libdis-$(CONFIG_IA64_DIS) += ia64-dis.o |  | ||||||
| libdis-$(CONFIG_M68K_DIS) += m68k-dis.o |  | ||||||
| libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o |  | ||||||
| libdis-$(CONFIG_MIPS_DIS) += mips-dis.o |  | ||||||
| libdis-$(CONFIG_PPC_DIS) += ppc-dis.o |  | ||||||
| libdis-$(CONFIG_S390_DIS) += s390-dis.o |  | ||||||
| libdis-$(CONFIG_SH4_DIS) += sh4-dis.o |  | ||||||
| libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # trace |  | ||||||
|  |  | ||||||
| ifeq ($(TRACE_BACKEND),dtrace) |  | ||||||
| trace-obj-y = trace-dtrace.o |  | ||||||
| else |  | ||||||
| trace-obj-y = trace.o |  | ||||||
| ifeq ($(TRACE_BACKEND),simple) |  | ||||||
| trace-obj-y += simpletrace.o |  | ||||||
| user-obj-y += qemu-timer-common.o |  | ||||||
| endif |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) |  | ||||||
|  |  | ||||||
| vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) |  | ||||||
|  |  | ||||||
							
								
								
									
										302
									
								
								Makefile.target
									
									
									
									
									
								
							
							
						
						
									
										302
									
								
								Makefile.target
									
									
									
									
									
								
							| @@ -1,23 +1,17 @@ | |||||||
| # -*- Mode: makefile -*- | # -*- Mode: makefile -*- | ||||||
|  |  | ||||||
|  | # This needs to be defined before rules.mak | ||||||
| GENERATED_HEADERS = config-target.h | GENERATED_HEADERS = config-target.h | ||||||
| CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y) |  | ||||||
| CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y) |  | ||||||
|  |  | ||||||
| include ../config-host.mak | include ../config-host.mak | ||||||
| include config-devices.mak | include config-devices.mak | ||||||
| include config-target.mak | include config-target.mak | ||||||
| include $(SRC_PATH)/rules.mak | include $(SRC_PATH)/rules.mak | ||||||
| ifneq ($(HWDIR),) |  | ||||||
| include $(HWDIR)/config.mak |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) | ||||||
| $(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw) | VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | ||||||
| QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H | QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H | ||||||
|  |  | ||||||
| include $(SRC_PATH)/Makefile.objs |  | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| # user emulator name | # user emulator name | ||||||
| QEMU_PROG=qemu-$(TARGET_ARCH2) | QEMU_PROG=qemu-$(TARGET_ARCH2) | ||||||
| @@ -31,61 +25,57 @@ endif | |||||||
| endif | endif | ||||||
|  |  | ||||||
| PROGS=$(QEMU_PROG) | PROGS=$(QEMU_PROG) | ||||||
| STPFILES= |  | ||||||
|  |  | ||||||
| ifndef CONFIG_HAIKU |  | ||||||
| LIBS+=-lm | LIBS+=-lm | ||||||
| endif |  | ||||||
|  |  | ||||||
| kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS) | kvm.o kvm-all.o: QEMU_CFLAGS+=$(KVM_CFLAGS) | ||||||
|  |  | ||||||
|  | CFLAGS += $(KVM_CFLAGS) | ||||||
|  |  | ||||||
| config-target.h: config-target.h-timestamp | config-target.h: config-target.h-timestamp | ||||||
| config-target.h-timestamp: config-target.mak | config-target.h-timestamp: config-target.mak | ||||||
|  |  | ||||||
| ifdef CONFIG_SYSTEMTAP_TRACE | all: $(PROGS) | ||||||
| stap: $(QEMU_PROG).stp |  | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY |  | ||||||
| TARGET_TYPE=user |  | ||||||
| else |  | ||||||
| TARGET_TYPE=system |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| $(QEMU_PROG).stp: |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ |  | ||||||
| 		--$(TRACE_BACKEND) \ |  | ||||||
| 		--binary $(bindir)/$(QEMU_PROG) \ |  | ||||||
| 		--target-arch $(TARGET_ARCH) \ |  | ||||||
| 		--target-type $(TARGET_TYPE) \ |  | ||||||
| 		--stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp,"  GEN   $(QEMU_PROG).stp") |  | ||||||
| else |  | ||||||
| stap: |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| all: $(PROGS) stap |  | ||||||
|  |  | ||||||
| # Dummy command so that make thinks it has done something | # Dummy command so that make thinks it has done something | ||||||
| 	@true | 	@true | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # cpu emulator library | # cpu emulator library | ||||||
| libobj-y = exec.o translate-all.o cpu-exec.o translate.o | libobj-y = exec.o cpu-exec.o | ||||||
| libobj-y += tcg/tcg.o | libobj-$(CONFIG_NO_CPU_EMULATION) += fake-exec.o | ||||||
|  | libobj-$(CONFIG_CPU_EMULATION) += translate-all.o translate.o | ||||||
|  | libobj-$(CONFIG_CPU_EMULATION) += tcg/tcg.o | ||||||
| libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o | libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o | ||||||
| libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o | libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o | ||||||
| libobj-y += op_helper.o helper.o | libobj-y += op_helper.o helper.o | ||||||
| ifeq ($(TARGET_BASE_ARCH), i386) |  | ||||||
| libobj-y += cpuid.o |  | ||||||
| endif |  | ||||||
| libobj-$(CONFIG_NEED_MMU) += mmu.o | libobj-$(CONFIG_NEED_MMU) += mmu.o | ||||||
|  |  | ||||||
|  | libobj-$(CONFIG_KVM) += kvm-tpr-opt.o | ||||||
|  | libobj-$(CONFIG_KVM) += qemu-kvm-helper.o | ||||||
|  |  | ||||||
| libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o | libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o | ||||||
|  | libobj-$(TARGET_ALPHA) += alpha_palcode.o | ||||||
|  |  | ||||||
|  | # NOTE: the disassembler code is only needed for debugging | ||||||
| libobj-y += disas.o | libobj-y += disas.o | ||||||
|  | libobj-$(CONFIG_ALPHA_DIS) += alpha-dis.o | ||||||
| $(libobj-y): $(GENERATED_HEADERS) | libobj-$(CONFIG_ARM_DIS) += arm-dis.o | ||||||
|  | libobj-$(CONFIG_CRIS_DIS) += cris-dis.o | ||||||
|  | libobj-$(CONFIG_HPPA_DIS) += hppa-dis.o | ||||||
|  | libobj-$(CONFIG_I386_DIS) += i386-dis.o | ||||||
|  | libobj-$(CONFIG_M68K_DIS) += m68k-dis.o | ||||||
|  | libobj-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o | ||||||
|  | libobj-$(CONFIG_MIPS_DIS) += mips-dis.o | ||||||
|  | libobj-$(CONFIG_PPC_DIS) += ppc-dis.o | ||||||
|  | libobj-$(CONFIG_S390_DIS) += s390-dis.o | ||||||
|  | libobj-$(CONFIG_SH4_DIS) += sh4-dis.o | ||||||
|  | libobj-$(CONFIG_SPARC_DIS) += sparc-dis.o | ||||||
|  |  | ||||||
| # libqemu | # libqemu | ||||||
|  |  | ||||||
|  | libqemu.a: $(libobj-y) | ||||||
|  |  | ||||||
| translate.o: translate.c cpu.h | translate.o: translate.c cpu.h | ||||||
|  |  | ||||||
| translate-all.o: translate-all.c cpu.h | translate-all.o: translate-all.c cpu.h | ||||||
| @@ -100,19 +90,20 @@ op_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | |||||||
| # cpu_signal_handler() in cpu-exec.c. | # cpu_signal_handler() in cpu-exec.c. | ||||||
| signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | ||||||
|  |  | ||||||
|  | qemu-kvm-helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # Linux user emulator target | # Linux user emulator target | ||||||
|  |  | ||||||
| ifdef CONFIG_LINUX_USER | ifdef CONFIG_LINUX_USER | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)) | VPATH+=:$(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | ||||||
| obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ | obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ | ||||||
|       elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \ |       elfload.o linuxload.o uaccess.o gdbstub.o | ||||||
|       qemu-malloc.o $(oslib-obj-y) |  | ||||||
|  |  | ||||||
| obj-$(TARGET_HAS_BFLT) += flatload.o | obj-$(TARGET_HAS_BFLT) += flatload.o | ||||||
|  | obj-$(TARGET_HAS_ELFLOAD32) += elfload32.o | ||||||
|  |  | ||||||
| obj-$(TARGET_I386) += vm86.o | obj-$(TARGET_I386) += vm86.o | ||||||
|  |  | ||||||
| @@ -125,11 +116,7 @@ obj-arm-y += arm-semi.o | |||||||
|  |  | ||||||
| obj-m68k-y += m68k-sim.o m68k-semi.o | obj-m68k-y += m68k-sim.o m68k-semi.o | ||||||
|  |  | ||||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ARLIBS=../libuser/libuser.a libqemu.a | ||||||
|  |  | ||||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) |  | ||||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) |  | ||||||
| obj-y += $(libobj-y) |  | ||||||
|  |  | ||||||
| endif #CONFIG_LINUX_USER | endif #CONFIG_LINUX_USER | ||||||
|  |  | ||||||
| @@ -138,8 +125,7 @@ endif #CONFIG_LINUX_USER | |||||||
|  |  | ||||||
| ifdef CONFIG_DARWIN_USER | ifdef CONFIG_DARWIN_USER | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH)/darwin-user) | VPATH+=:$(SRC_PATH)/darwin-user | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) | QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) | ||||||
|  |  | ||||||
| # Leave some space for the regular program loading zone | # Leave some space for the regular program loading zone | ||||||
| @@ -152,11 +138,7 @@ obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \ | |||||||
|  |  | ||||||
| obj-i386-y += ioport-user.o | obj-i386-y += ioport-user.o | ||||||
|  |  | ||||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ARLIBS=../libuser/libuser.a libqemu.a | ||||||
|  |  | ||||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) |  | ||||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) |  | ||||||
| obj-y += $(libobj-y) |  | ||||||
|  |  | ||||||
| endif #CONFIG_DARWIN_USER | endif #CONFIG_DARWIN_USER | ||||||
|  |  | ||||||
| @@ -165,8 +147,7 @@ endif #CONFIG_DARWIN_USER | |||||||
|  |  | ||||||
| ifdef CONFIG_BSD_USER | ifdef CONFIG_BSD_USER | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH)/bsd-user) | VPATH+=:$(SRC_PATH)/bsd-user | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) | QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) | ||||||
|  |  | ||||||
| obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ | obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ | ||||||
| @@ -174,11 +155,7 @@ obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ | |||||||
|  |  | ||||||
| obj-i386-y += ioport-user.o | obj-i386-y += ioport-user.o | ||||||
|  |  | ||||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ARLIBS=../libuser/libuser.a libqemu.a | ||||||
|  |  | ||||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) |  | ||||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) |  | ||||||
| obj-y += $(libobj-y) |  | ||||||
|  |  | ||||||
| endif #CONFIG_BSD_USER | endif #CONFIG_BSD_USER | ||||||
|  |  | ||||||
| @@ -186,74 +163,97 @@ endif #CONFIG_BSD_USER | |||||||
| # System emulator target | # System emulator target | ||||||
| ifdef CONFIG_SOFTMMU | ifdef CONFIG_SOFTMMU | ||||||
|  |  | ||||||
| obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o | obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o | ||||||
| # virtio has to be here due to weird dependency between PCI and virtio-net. | # virtio has to be here due to weird dependency between PCI and virtio-net. | ||||||
| # need to fix this properly | # need to fix this properly | ||||||
| obj-$(CONFIG_NO_PCI) += pci-stub.o | obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o | ||||||
| obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o |  | ||||||
| obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o |  | ||||||
| obj-y += vhost_net.o |  | ||||||
| obj-$(CONFIG_VHOST_NET) += vhost.o |  | ||||||
| obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o |  | ||||||
| obj-y += rwhandler.o |  | ||||||
| obj-$(CONFIG_KVM) += kvm.o kvm-all.o | obj-$(CONFIG_KVM) += kvm.o kvm-all.o | ||||||
| obj-$(CONFIG_NO_KVM) += kvm-stub.o | # MSI-X depends on kvm for interrupt injection, | ||||||
|  | # so moved it from Makefile.hw to Makefile.target for now | ||||||
|  | obj-y += msix.o | ||||||
|  |  | ||||||
|  | obj-$(CONFIG_ISA_MMIO) += isa_mmio.o | ||||||
| LIBS+=-lz | LIBS+=-lz | ||||||
|  |  | ||||||
|  | sound-obj-y = | ||||||
|  | sound-obj-$(CONFIG_SB16) += sb16.o | ||||||
|  | sound-obj-$(CONFIG_ES1370) += es1370.o | ||||||
|  | sound-obj-$(CONFIG_AC97) += ac97.o | ||||||
|  | sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o | ||||||
|  | sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o | ||||||
|  | sound-obj-$(CONFIG_CS4231A) += cs4231a.o | ||||||
|  |  | ||||||
|  | adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 | ||||||
|  |  | ||||||
| QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | ||||||
| QEMU_CFLAGS += $(VNC_SASL_CFLAGS) | QEMU_CFLAGS += $(VNC_SASL_CFLAGS) | ||||||
| QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) |  | ||||||
| QEMU_CFLAGS += $(VNC_PNG_CFLAGS) |  | ||||||
|  |  | ||||||
| # xen backend driver support | # xen backend driver support | ||||||
| obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o | obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o | ||||||
|  |  | ||||||
| # Inter-VM PCI shared memory | # USB layer | ||||||
| obj-$(CONFIG_KVM) += ivshmem.o | obj-$(CONFIG_USB_OHCI) += usb-ohci.o | ||||||
|  |  | ||||||
|  | # PCI network cards | ||||||
|  | obj-y += eepro100.o | ||||||
|  | obj-y += pcnet.o | ||||||
|  | obj-y += rtl8139.o | ||||||
|  | obj-y += e1000.o | ||||||
|  |  | ||||||
| # Hardware support | # Hardware support | ||||||
| obj-i386-y += vga.o | obj-i386-y = ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o | ||||||
| obj-i386-y += mc146818rtc.o i8259.o pc.o | obj-i386-y += pckbd.o $(sound-obj-y) dma.o | ||||||
| obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o | obj-i386-y += vga.o vga-pci.o vga-isa.o | ||||||
| obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o | obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o | ||||||
|  | obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o | ||||||
|  | obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o | ||||||
| obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o | obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o | ||||||
| obj-i386-y += debugcon.o multiboot.o | obj-i386-y += extboot.o | ||||||
| obj-i386-y += pc_piix.o | obj-i386-y += ne2000-isa.o | ||||||
| obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o | obj-i386-y += testdev.o | ||||||
|  |  | ||||||
|  | obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o | ||||||
|  | obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o | ||||||
|  |  | ||||||
|  | # Hardware support | ||||||
|  | obj-ia64-y += ide.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | ||||||
|  | obj-ia64-y += fdc.o mc146818rtc.o serial.o i8259.o ipf.o | ||||||
|  | obj-ia64-y += cirrus_vga.o parallel.o acpi.o piix_pci.o | ||||||
|  | obj-ia64-y += usb-uhci.o | ||||||
|  | obj-ia64-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o | ||||||
|  |  | ||||||
| # shared objects | # shared objects | ||||||
| obj-ppc-y = ppc.o | obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o | ||||||
| obj-ppc-y += vga.o | obj-ppc-y += ide/cmd646.o | ||||||
|  | obj-ppc-y += vga.o vga-pci.o $(sound-obj-y) dma.o openpic.o | ||||||
|  | obj-ppc-y += cirrus_vga.o | ||||||
| # PREP target | # PREP target | ||||||
| obj-ppc-y += i8259.o mc146818rtc.o | obj-ppc-y += pckbd.o serial.o i8259.o i8254.o fdc.o mc146818rtc.o | ||||||
| obj-ppc-y += ppc_prep.o | obj-ppc-y += prep_pci.o ppc_prep.o ne2000-isa.o | ||||||
|  | # Mac shared devices | ||||||
|  | obj-ppc-y += macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o | ||||||
| # OldWorld PowerMac | # OldWorld PowerMac | ||||||
| obj-ppc-y += ppc_oldworld.o | obj-ppc-y += heathrow_pic.o grackle_pci.o ppc_oldworld.o | ||||||
| # NewWorld PowerMac | # NewWorld PowerMac | ||||||
| obj-ppc-y += ppc_newworld.o | obj-ppc-y += unin_pci.o ppc_newworld.o | ||||||
| # PowerPC 4xx boards | # PowerPC 4xx boards | ||||||
| obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o | obj-ppc-y += pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o | ||||||
| obj-ppc-y += ppc440.o ppc440_bamboo.o | obj-ppc-y += ppc440.o ppc440_bamboo.o | ||||||
| # PowerPC E500 boards | # PowerPC E500 boards | ||||||
| obj-ppc-y += ppce500_mpc8544ds.o | obj-ppc-y += ppce500_pci.o ppce500_mpc8544ds.o | ||||||
| # PowerPC 440 Xilinx ML507 reference board. |  | ||||||
| obj-ppc-y += virtex_ml507.o |  | ||||||
| obj-ppc-$(CONFIG_KVM) += kvm_ppc.o | obj-ppc-$(CONFIG_KVM) += kvm_ppc.o | ||||||
| obj-ppc-$(CONFIG_FDT) += device_tree.o | obj-ppc-$(CONFIG_FDT) += device_tree.o | ||||||
|  |  | ||||||
| # Xilinx PPC peripherals |  | ||||||
| obj-ppc-y += xilinx_intc.o |  | ||||||
| obj-ppc-y += xilinx_timer.o |  | ||||||
| obj-ppc-y += xilinx_uartlite.o |  | ||||||
| obj-ppc-y += xilinx_ethlite.o |  | ||||||
|  |  | ||||||
| obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o | obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o | ||||||
| obj-mips-y += mips_addr.o mips_timer.o mips_int.o | obj-mips-y += mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o rc4030.o | ||||||
| obj-mips-y += vga.o i8259.o | obj-mips-y += vga-pci.o vga-isa.o vga-isa-mm.o | ||||||
| obj-mips-y += g364fb.o jazz_led.o | obj-mips-y += g364fb.o jazz_led.o dp8393x.o | ||||||
| obj-mips-y += gt64xxx.o mc146818rtc.o | obj-mips-y += ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o | ||||||
| obj-mips-y += cirrus_vga.o | obj-mips-y += gt64xxx.o pckbd.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o | ||||||
| obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o | obj-mips-y += piix4.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y) | ||||||
|  | obj-mips-y += mipsnet.o ne2000-isa.o | ||||||
|  | obj-mips-y += pflash_cfi01.o | ||||||
|  | obj-mips-y += vmware_vga.o | ||||||
|  |  | ||||||
| obj-microblaze-y = petalogix_s3adsp1800_mmu.o | obj-microblaze-y = petalogix_s3adsp1800_mmu.o | ||||||
|  |  | ||||||
| @@ -263,13 +263,12 @@ obj-microblaze-y += xilinx_timer.o | |||||||
| obj-microblaze-y += xilinx_uartlite.o | obj-microblaze-y += xilinx_uartlite.o | ||||||
| obj-microblaze-y += xilinx_ethlite.o | obj-microblaze-y += xilinx_ethlite.o | ||||||
|  |  | ||||||
|  | obj-microblaze-y += pflash_cfi02.o | ||||||
|  |  | ||||||
| obj-microblaze-$(CONFIG_FDT) += device_tree.o | obj-microblaze-$(CONFIG_FDT) += device_tree.o | ||||||
|  |  | ||||||
| # Boards | # Boards | ||||||
| obj-cris-y = cris_pic_cpu.o | obj-cris-y = cris_pic_cpu.o etraxfs.o axis_dev88.o | ||||||
| obj-cris-y += cris-boot.o |  | ||||||
| obj-cris-y += etraxfs.o axis_dev88.o |  | ||||||
| obj-cris-y += axis_dev88.o |  | ||||||
|  |  | ||||||
| # IO blocks | # IO blocks | ||||||
| obj-cris-y += etraxfs_dma.o | obj-cris-y += etraxfs_dma.o | ||||||
| @@ -278,18 +277,18 @@ obj-cris-y += etraxfs_eth.o | |||||||
| obj-cris-y += etraxfs_timer.o | obj-cris-y += etraxfs_timer.o | ||||||
| obj-cris-y += etraxfs_ser.o | obj-cris-y += etraxfs_ser.o | ||||||
|  |  | ||||||
| ifeq ($(TARGET_ARCH), sparc64) | obj-cris-y += pflash_cfi02.o | ||||||
| obj-sparc-y = sun4u.o apb_pci.o |  | ||||||
| obj-sparc-y += vga.o |  | ||||||
| obj-sparc-y += mc146818rtc.o |  | ||||||
| obj-sparc-y += cirrus_vga.o |  | ||||||
| else |  | ||||||
| obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o |  | ||||||
| obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o |  | ||||||
| obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o |  | ||||||
|  |  | ||||||
| # GRLIB | ifeq ($(TARGET_ARCH), sparc64) | ||||||
| obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o | obj-sparc-y = sun4u.o pckbd.o apb_pci.o | ||||||
|  | obj-sparc-y += ide/core.o ide/qdev.o ide/pci.o ide/cmd646.o | ||||||
|  | obj-sparc-y += vga.o vga-pci.o | ||||||
|  | obj-sparc-y += fdc.o mc146818rtc.o serial.o | ||||||
|  | obj-sparc-y += cirrus_vga.o parallel.o | ||||||
|  | else | ||||||
|  | obj-sparc-y = sun4m.o lance.o tcx.o iommu.o slavio_intctl.o | ||||||
|  | obj-sparc-y += slavio_timer.o slavio_misc.o fdc.o sparc32_dma.o | ||||||
|  | obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o | ||||||
| endif | endif | ||||||
|  |  | ||||||
| obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o | obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o | ||||||
| @@ -301,79 +300,68 @@ obj-arm-y += pl061.o | |||||||
| obj-arm-y += arm-semi.o | obj-arm-y += arm-semi.o | ||||||
| obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o | obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o | ||||||
| obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | ||||||
| obj-arm-y += gumstix.o | obj-arm-y += pflash_cfi01.o gumstix.o | ||||||
| obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o | obj-arm-y += zaurus.o ide/core.o ide/microdrive.o serial.o spitz.o tosa.o tc6393xb.o | ||||||
| obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ | obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o | ||||||
| 		omap_gpio.o omap_intc.o omap_uart.o | obj-arm-y += omap2.o omap_dss.o soc_dma.o | ||||||
| obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ |  | ||||||
| 		omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o |  | ||||||
| obj-arm-y += omap_sx1.o palm.o tsc210x.o | obj-arm-y += omap_sx1.o palm.o tsc210x.o | ||||||
| obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o | obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o | ||||||
| obj-arm-y += mst_fpga.o mainstone.o | obj-arm-y += mst_fpga.o mainstone.o | ||||||
| obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o | obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o marvell_88w8618_audio.o | ||||||
| obj-arm-y += framebuffer.o | obj-arm-y += framebuffer.o | ||||||
| obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o | obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o | ||||||
| obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o | obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o | ||||||
| obj-arm-y += syborg_virtio.o | obj-arm-y += syborg_virtio.o | ||||||
|  |  | ||||||
| obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | ||||||
| obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o | obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o serial.o | ||||||
| obj-sh4-y += ide/mmio.o | obj-sh4-y += ide/core.o ide/mmio.o | ||||||
|  |  | ||||||
| obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | ||||||
| obj-m68k-y += m68k-semi.o dummy_m68k.o | obj-m68k-y += m68k-semi.o dummy_m68k.o | ||||||
|  |  | ||||||
| obj-s390x-y = s390-virtio-bus.o s390-virtio.o | obj-s390x-y = s390-virtio-bus.o s390-virtio.o | ||||||
|  |  | ||||||
| obj-alpha-y = alpha_palcode.o | ifeq ($(TARGET_ARCH), ia64) | ||||||
|  | firmware.o: firmware.c | ||||||
|  | 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  | endif | ||||||
|  |  | ||||||
| main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) | main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) | ||||||
|  |  | ||||||
| monitor.o: hmp-commands.h qmp-commands.h | vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) | ||||||
|  |  | ||||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | vl.o: qemu-options.h | ||||||
|  |  | ||||||
| obj-y += $(addprefix ../, $(common-obj-y)) | monitor.o: qemu-monitor.h | ||||||
| obj-y += $(addprefix ../libdis/, $(libdis-y)) |  | ||||||
| obj-y += $(libobj-y) | ARLIBS=../libqemu_common.a libqemu.a $(HWLIB) | ||||||
| obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) |  | ||||||
|  |  | ||||||
| endif # CONFIG_SOFTMMU | endif # CONFIG_SOFTMMU | ||||||
|  |  | ||||||
| obj-y += $(addprefix ../, $(trace-obj-y)) |  | ||||||
| obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o | obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o | ||||||
|  |  | ||||||
| $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) | $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS) | ||||||
| 	$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)) | 	$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)) | ||||||
|  |  | ||||||
|  |  | ||||||
| gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh | gdbstub-xml.c: $(TARGET_XML_FILES) feature_to_c.sh | ||||||
| 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@") | 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@") | ||||||
|  |  | ||||||
| hmp-commands.h: $(SRC_PATH)/hmp-commands.hx | qemu-options.h: $(SRC_PATH)/qemu-options.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||||
|  |  | ||||||
| qmp-commands.h: $(SRC_PATH)/qmp-commands.hx | qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| 	rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o | 	rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o | ||||||
| 	rm -f *.d */*.d tcg/*.o ide/*.o | 	rm -f *.d */*.d tcg/*.o ide/*.o | ||||||
| 	rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c | 	rm -f qemu-options.h qemu-monitor.h gdbstub-xml.c | ||||||
| ifdef CONFIG_SYSTEMTAP_TRACE |  | ||||||
| 	rm -f *.stp |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| install: all | install: all | ||||||
| ifneq ($(PROGS),) | ifneq ($(PROGS),) | ||||||
| 	$(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)" | 	$(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)" | ||||||
| ifneq ($(STRIP),) |  | ||||||
| 	$(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS)) |  | ||||||
| endif |  | ||||||
| endif |  | ||||||
| ifdef CONFIG_SYSTEMTAP_TRACE |  | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset" |  | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset" |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Include automatically generated dependency files | # Include automatically generated dependency files | ||||||
|   | |||||||
| @@ -6,16 +6,25 @@ include $(SRC_PATH)/rules.mak | |||||||
|  |  | ||||||
| .PHONY: all | .PHONY: all | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH)) | # Do not take %.o from $(SRC_PATH), only %.c and %.h | ||||||
|  | # All %.o for user targets should be built with -fpie, when | ||||||
|  | # configured with --enable-user-pie, so we don't want to | ||||||
|  | # take %.o from $(SRC_PATH), since they built without -fpie | ||||||
|  | vpath %.c %.h $(SRC_PATH) | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I.. | QEMU_CFLAGS+=-I.. | ||||||
|  |  | ||||||
| include $(SRC_PATH)/Makefile.objs | obj-y = | ||||||
|  | obj-y += envlist.o path.o | ||||||
|  | obj-y += tcg-runtime.o host-utils.o | ||||||
|  | obj-y += cutils.o cache-utils.o | ||||||
|  |  | ||||||
| all: $(user-obj-y) | all: libuser.a | ||||||
| # Dummy command so that make thinks it has done something | # Dummy command so that make thinks it has done something | ||||||
| 	@true | 	@true | ||||||
|  |  | ||||||
|  | libuser.a: $(obj-y) | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| 	rm -f *.o *.d *.a *~ | 	rm -f *.o *.d *.a *~ | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								QMP/README
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								QMP/README
									
									
									
									
									
								
							| @@ -7,82 +7,57 @@ Introduction | |||||||
| The QEMU Monitor Protocol (QMP) allows applications to communicate with | The QEMU Monitor Protocol (QMP) allows applications to communicate with | ||||||
| QEMU's Monitor. | QEMU's Monitor. | ||||||
|  |  | ||||||
| QMP is JSON[1] based and currently has the following features: | QMP is JSON[1] based and has the following features: | ||||||
|  |  | ||||||
| - Lightweight, text-based, easy to parse data format | - Lightweight, text-based, easy to parse data format | ||||||
| - Asynchronous messages support (ie. events) | - Asynchronous events support  | ||||||
| - Capabilities Negotiation | - Stability | ||||||
|  |  | ||||||
| For detailed information on QMP's usage, please, refer to the following files: | For more information, please, refer to the following files: | ||||||
|  |  | ||||||
| o qmp-spec.txt      QEMU Monitor Protocol current specification | o qmp-spec.txt    QEMU Monitor Protocol current specification | ||||||
| o qmp-commands.txt  QMP supported commands (auto-generated at build-time) | o qmp-events.txt  List of available asynchronous events | ||||||
| o qmp-events.txt    List of available asynchronous events |  | ||||||
|  |  | ||||||
| There is also a simple Python script called 'qmp-shell' available. | There are also two simple Python scripts available: | ||||||
|  |  | ||||||
| IMPORTANT: It's strongly recommended to read the 'Stability Considerations' |  | ||||||
| section in the qmp-commands.txt file before making any serious use of QMP. |  | ||||||
|  |  | ||||||
|  | o qmp-shell       A shell | ||||||
|  | o vm-info         Show some information about the Virtual Machine | ||||||
|  |  | ||||||
| [1] http://www.json.org | [1] http://www.json.org | ||||||
|  |  | ||||||
| Usage | Usage | ||||||
| ----- | ----- | ||||||
|  |  | ||||||
| To enable QMP, you need a QEMU monitor instance in "control mode". There are | To enable QMP, QEMU has to be started in "control mode". There are | ||||||
| two ways of doing this. | two ways of doing this, the simplest one is using the the '-qmp' | ||||||
|  | command-line option. | ||||||
|  |  | ||||||
| The simplest one is using the '-qmp' command-line option. The following | For example: | ||||||
| example makes QMP available on localhost port 4444: |  | ||||||
|  |  | ||||||
|   $ qemu [...] -qmp tcp:localhost:4444,server | $ qemu [...] -qmp tcp:localhost:4444,server | ||||||
|  |  | ||||||
| However, in order to have more complex combinations, like multiple monitors, | Will start QEMU in control mode, waiting for a client TCP connection | ||||||
| the '-mon' command-line option should be used along with the '-chardev' one. | on localhost port 4444. | ||||||
| For instance, the following example creates one user monitor on stdio and one |  | ||||||
| QMP monitor on localhost port 4444. |  | ||||||
|  |  | ||||||
|    $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ | It is also possible to use the '-mon' command-line option to have | ||||||
|                 -chardev socket,id=mon1,host=localhost,port=4444,server \ | more complex combinations. Please, refer to the QEMU's manpage for | ||||||
|                 -mon chardev=mon1,mode=control | more information. | ||||||
|  |  | ||||||
| Please, refer to QEMU's manpage for more information. |  | ||||||
|  |  | ||||||
| Simple Testing | Simple Testing | ||||||
| -------------- | -------------- | ||||||
|  |  | ||||||
| To manually test QMP one can connect with telnet and issue commands by hand: | To manually test QMP one can connect with telnet and issue commands: | ||||||
|  |  | ||||||
| $ telnet localhost 4444 | $ telnet localhost 4444 | ||||||
| Trying 127.0.0.1... | Trying 127.0.0.1... | ||||||
| Connected to localhost. | Connected to localhost. | ||||||
| Escape character is '^]'. | Escape character is '^]'. | ||||||
| {"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}} | {"QMP": {"capabilities": []}} | ||||||
| { "execute": "qmp_capabilities" } |  | ||||||
| {"return": {}} |  | ||||||
| { "execute": "query-version" } | { "execute": "query-version" } | ||||||
| {"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}} | {"return": {"qemu": "0.11.50", "package": ""}} | ||||||
|  |  | ||||||
| Development Process | Contact | ||||||
| ------------------- | ------- | ||||||
|  |  | ||||||
| When changing QMP's interface (by adding new commands, events or modifying | http://www.linux-kvm.org/page/MonitorProtocol | ||||||
| existing ones) it's mandatory to update the relevant documentation, which is | Luiz Fernando N. Capitulino <lcapitulino@redhat.com> | ||||||
| one (or more) of the files listed in the 'Introduction' section*. |  | ||||||
|  |  | ||||||
| Also, it's strongly recommended to send the documentation patch first, before |  | ||||||
| doing any code change. This is so because: |  | ||||||
|  |  | ||||||
|   1. Avoids the code dictating the interface |  | ||||||
|  |  | ||||||
|   2. Review can improve your interface.  Letting that happen before |  | ||||||
|      you implement it can save you work. |  | ||||||
|  |  | ||||||
| * The qmp-commands.txt file is generated from the qmp-commands.hx one, which |  | ||||||
|   is the file that should be edited. |  | ||||||
|  |  | ||||||
| Homepage |  | ||||||
| -------- |  | ||||||
|  |  | ||||||
| http://wiki.qemu.org/QMP |  | ||||||
|   | |||||||
| @@ -1,266 +1,26 @@ | |||||||
|                    QEMU Monitor Protocol Events |                    QEMU Monitor Protocol: Events | ||||||
|                    ============================ |                    ============================= | ||||||
|  |  | ||||||
| BLOCK_IO_ERROR | 1 SHUTDOWN | ||||||
| -------------- | ----------- | ||||||
|  |  | ||||||
| Emitted when a disk I/O error occurs. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "device": device name (json-string) |  | ||||||
| - "operation": I/O operation (json-string, "read" or "write") |  | ||||||
| - "action": action that has been taken, it's one of the following (json-string): |  | ||||||
|     "ignore": error has been ignored |  | ||||||
|     "report": error has been reported to the device |  | ||||||
|     "stop": error caused VM to be stopped |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "BLOCK_IO_ERROR", |  | ||||||
|     "data": { "device": "ide0-hd1", |  | ||||||
|               "operation": "write", |  | ||||||
|               "action": "stop" }, |  | ||||||
|     "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } |  | ||||||
|  |  | ||||||
| Note: If action is "stop", a STOP event will eventually follow the |  | ||||||
| BLOCK_IO_ERROR event. |  | ||||||
|  |  | ||||||
| RESET |  | ||||||
| ----- |  | ||||||
|  |  | ||||||
| Emitted when the Virtual Machine is reseted. |  | ||||||
|  |  | ||||||
|  | Description: Issued when the Virtual Machine is powered down. | ||||||
| Data: None. | Data: None. | ||||||
|  |  | ||||||
| Example: | 2 RESET | ||||||
|  | ------- | ||||||
|  |  | ||||||
| { "event": "RESET", | Description: Issued when the Virtual Machine is reseted. | ||||||
|     "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } | Data: None. | ||||||
|  |  | ||||||
| RESUME | 3 STOP | ||||||
| ------ | ------ | ||||||
|  |  | ||||||
| Emitted when the Virtual Machine resumes execution. | Description: Issued when the Virtual Machine is stopped. | ||||||
|  |  | ||||||
| Data: None. | Data: None. | ||||||
|  |  | ||||||
| Example: | 4 DEBUG | ||||||
|  | ------- | ||||||
| { "event": "RESUME", |  | ||||||
|     "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } |  | ||||||
|  |  | ||||||
| RTC_CHANGE |  | ||||||
| ---------- |  | ||||||
|  |  | ||||||
| Emitted when the guest changes the RTC time. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "offset": delta against the host UTC in seconds (json-number) |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "RTC_CHANGE", |  | ||||||
|     "data": { "offset": 78 }, |  | ||||||
|     "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } |  | ||||||
|  |  | ||||||
| SHUTDOWN |  | ||||||
| -------- |  | ||||||
|  |  | ||||||
| Emitted when the Virtual Machine is powered down. |  | ||||||
|  |  | ||||||
|  | Description: Issued when the Virtual Machine enters debug mode. | ||||||
| Data: None. | Data: None. | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "SHUTDOWN", |  | ||||||
|     "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } |  | ||||||
|  |  | ||||||
| Note: If the command-line option "-no-shutdown" has been specified, a STOP |  | ||||||
| event will eventually follow the SHUTDOWN event. |  | ||||||
|  |  | ||||||
| STOP |  | ||||||
| ---- |  | ||||||
|  |  | ||||||
| Emitted when the Virtual Machine is stopped. |  | ||||||
|  |  | ||||||
| Data: None. |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "STOP", |  | ||||||
|     "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } |  | ||||||
|  |  | ||||||
| VNC_CONNECTED |  | ||||||
| ------------- |  | ||||||
|  |  | ||||||
| Emitted when a VNC client establishes a connection. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "server": Server information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "auth": authentication method (json-string, optional) |  | ||||||
| - "client": Client information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "VNC_CONNECTED", |  | ||||||
|     "data": { |  | ||||||
|         "server": { "auth": "sasl", "family": "ipv4", |  | ||||||
|                     "service": "5901", "host": "0.0.0.0" }, |  | ||||||
|         "client": { "family": "ipv4", "service": "58425", |  | ||||||
|                     "host": "127.0.0.1" } }, |  | ||||||
|     "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Note: This event is emitted before any authentication takes place, thus |  | ||||||
| the authentication ID is not provided. |  | ||||||
|  |  | ||||||
| VNC_DISCONNECTED |  | ||||||
| ---------------- |  | ||||||
|  |  | ||||||
| Emitted when the conection is closed. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "server": Server information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "auth": authentication method (json-string, optional) |  | ||||||
| - "client": Client information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "x509_dname": TLS dname (json-string, optional) |  | ||||||
|   - "sasl_username": SASL username (json-string, optional) |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "VNC_DISCONNECTED", |  | ||||||
|     "data": { |  | ||||||
|         "server": { "auth": "sasl", "family": "ipv4", |  | ||||||
|                     "service": "5901", "host": "0.0.0.0" }, |  | ||||||
|         "client": { "family": "ipv4", "service": "58425", |  | ||||||
|                     "host": "127.0.0.1", "sasl_username": "luiz" } }, |  | ||||||
|     "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } |  | ||||||
|  |  | ||||||
| VNC_INITIALIZED |  | ||||||
| --------------- |  | ||||||
|  |  | ||||||
| Emitted after authentication takes place (if any) and the VNC session is |  | ||||||
| made active. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "server": Server information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "auth": authentication method (json-string, optional) |  | ||||||
| - "client": Client information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "service": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "x509_dname": TLS dname (json-string, optional) |  | ||||||
|   - "sasl_username": SASL username (json-string, optional) |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "VNC_INITIALIZED", |  | ||||||
|     "data": { |  | ||||||
|         "server": { "auth": "sasl", "family": "ipv4", |  | ||||||
|                     "service": "5901", "host": "0.0.0.0"}, |  | ||||||
|         "client": { "family": "ipv4", "service": "46089", |  | ||||||
|                     "host": "127.0.0.1", "sasl_username": "luiz" } }, |  | ||||||
|         "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } |  | ||||||
|  |  | ||||||
| SPICE_CONNECTED, SPICE_DISCONNECTED |  | ||||||
| ----------------------------------- |  | ||||||
|  |  | ||||||
| Emitted when a SPICE client connects or disconnects. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "server": Server information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "port": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
| - "client": Client information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "port": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, |  | ||||||
|   "event": "SPICE_CONNECTED", |  | ||||||
|   "data": { |  | ||||||
|     "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, |  | ||||||
|     "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} |  | ||||||
| }} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| SPICE_INITIALIZED |  | ||||||
| ----------------- |  | ||||||
|  |  | ||||||
| Emitted after initial handshake and authentication takes place (if any) |  | ||||||
| and the SPICE channel is up'n'running |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "server": Server information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "port": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "auth": authentication method (json-string, optional) |  | ||||||
| - "client": Client information (json-object) |  | ||||||
|   - "host": IP address (json-string) |  | ||||||
|   - "port": port number (json-string) |  | ||||||
|   - "family": address family (json-string, "ipv4" or "ipv6") |  | ||||||
|   - "connection-id": spice connection id.  All channels with the same id |  | ||||||
|                      belong to the same spice session (json-int) |  | ||||||
|   - "channel-type": channel type.  "1" is the main control channel, filter for |  | ||||||
|                     this one if you want track spice sessions only (json-int) |  | ||||||
|   - "channel-id": channel id.  Usually "0", might be different needed when |  | ||||||
|                   multiple channels of the same type exist, such as multiple |  | ||||||
|                   display channels in a multihead setup (json-int) |  | ||||||
|   - "tls": whevener the channel is encrypted (json-bool) |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, |  | ||||||
|   "event": "SPICE_INITIALIZED", |  | ||||||
|   "data": {"server": {"auth": "spice", "port": "5921", |  | ||||||
|                       "family": "ipv4", "host": "127.0.0.1"}, |  | ||||||
|            "client": {"port": "49004", "family": "ipv4", "channel-type": 3, |  | ||||||
|                       "connection-id": 1804289383, "host": "127.0.0.1", |  | ||||||
|                       "channel-id": 0, "tls": true} |  | ||||||
| }} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| WATCHDOG |  | ||||||
| -------- |  | ||||||
|  |  | ||||||
| Emitted when the watchdog device's timer is expired. |  | ||||||
|  |  | ||||||
| Data: |  | ||||||
|  |  | ||||||
| - "action": Action that has been taken, it's one of the following (json-string): |  | ||||||
|             "reset", "shutdown", "poweroff", "pause", "debug", or "none" |  | ||||||
|  |  | ||||||
| Example: |  | ||||||
|  |  | ||||||
| { "event": "WATCHDOG", |  | ||||||
|      "data": { "action": "reset" }, |  | ||||||
|      "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } |  | ||||||
|  |  | ||||||
| Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is |  | ||||||
| followed respectively by the RESET, SHUTDOWN, or STOP events. |  | ||||||
|   | |||||||
							
								
								
									
										265
									
								
								QMP/qmp-shell
									
									
									
									
									
								
							
							
						
						
									
										265
									
								
								QMP/qmp-shell
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| #!/usr/bin/python | #!/usr/bin/python | ||||||
| # | # | ||||||
| # Low-level QEMU shell on top of QMP. | # Simple QEMU shell on top of QMP | ||||||
| # | # | ||||||
| # Copyright (C) 2009, 2010 Red Hat Inc. | # Copyright (C) 2009 Red Hat Inc. | ||||||
| # | # | ||||||
| # Authors: | # Authors: | ||||||
| #  Luiz Capitulino <lcapitulino@redhat.com> | #  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
| @@ -14,246 +14,59 @@ | |||||||
| # | # | ||||||
| # Start QEMU with: | # Start QEMU with: | ||||||
| # | # | ||||||
| # # qemu [...] -qmp unix:./qmp-sock,server | # $ qemu [...] -monitor control,unix:./qmp,server | ||||||
| # | # | ||||||
| # Run the shell: | # Run the shell: | ||||||
| # | # | ||||||
| # $ qmp-shell ./qmp-sock | # $ qmp-shell ./qmp | ||||||
| # | # | ||||||
| # Commands have the following format: | # Commands have the following format: | ||||||
| # | # | ||||||
| #    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] | # < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] | ||||||
| # | # | ||||||
| # For example: | # For example: | ||||||
| # | # | ||||||
| # (QEMU) device_add driver=e1000 id=net1 | # (QEMU) info item=network | ||||||
| # {u'return': {}} |  | ||||||
| # (QEMU) |  | ||||||
|  |  | ||||||
| import qmp | import qmp | ||||||
| import readline | import readline | ||||||
| import sys | from sys import argv,exit | ||||||
|  |  | ||||||
| class QMPCompleter(list): | def shell_help(): | ||||||
|     def complete(self, text, state): |     print 'bye  exit from the shell' | ||||||
|         for cmd in self: |  | ||||||
|             if cmd.startswith(text): |  | ||||||
|                 if not state: |  | ||||||
|                     return cmd |  | ||||||
|                 else: |  | ||||||
|                     state -= 1 |  | ||||||
|  |  | ||||||
| class QMPShellError(Exception): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| class QMPShellBadPort(QMPShellError): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and |  | ||||||
| #       _execute_cmd()). Let's design a better one. |  | ||||||
| class QMPShell(qmp.QEMUMonitorProtocol): |  | ||||||
|     def __init__(self, address): |  | ||||||
|         qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) |  | ||||||
|         self._greeting = None |  | ||||||
|         self._completer = None |  | ||||||
|  |  | ||||||
|     def __get_address(self, arg): |  | ||||||
|         """ |  | ||||||
|         Figure out if the argument is in the port:host form, if it's not it's |  | ||||||
|         probably a file path. |  | ||||||
|         """ |  | ||||||
|         addr = arg.split(':') |  | ||||||
|         if len(addr) == 2: |  | ||||||
|             try: |  | ||||||
|                 port = int(addr[1]) |  | ||||||
|             except ValueError: |  | ||||||
|                 raise QMPShellBadPort |  | ||||||
|             return ( addr[0], port ) |  | ||||||
|         # socket path |  | ||||||
|         return arg |  | ||||||
|  |  | ||||||
|     def _fill_completion(self): |  | ||||||
|         for cmd in self.cmd('query-commands')['return']: |  | ||||||
|             self._completer.append(cmd['name']) |  | ||||||
|  |  | ||||||
|     def __completer_setup(self): |  | ||||||
|         self._completer = QMPCompleter() |  | ||||||
|         self._fill_completion() |  | ||||||
|         readline.set_completer(self._completer.complete) |  | ||||||
|         readline.parse_and_bind("tab: complete") |  | ||||||
|         # XXX: default delimiters conflict with some command names (eg. query-), |  | ||||||
|         # clearing everything as it doesn't seem to matter |  | ||||||
|         readline.set_completer_delims('') |  | ||||||
|  |  | ||||||
|     def __build_cmd(self, cmdline): |  | ||||||
|         """ |  | ||||||
|         Build a QMP input object from a user provided command-line in the |  | ||||||
|         following format: |  | ||||||
|      |  | ||||||
|             < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] |  | ||||||
|         """ |  | ||||||
|         cmdargs = cmdline.split() |  | ||||||
|         qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } |  | ||||||
|         for arg in cmdargs[1:]: |  | ||||||
|             opt = arg.split('=') |  | ||||||
|             try: |  | ||||||
|                 value = int(opt[1]) |  | ||||||
|             except ValueError: |  | ||||||
|                 value = opt[1] |  | ||||||
|             qmpcmd['arguments'][opt[0]] = value |  | ||||||
|         return qmpcmd |  | ||||||
|  |  | ||||||
|     def _execute_cmd(self, cmdline): |  | ||||||
|         try: |  | ||||||
|             qmpcmd = self.__build_cmd(cmdline) |  | ||||||
|         except: |  | ||||||
|             print 'command format: <command-name> ', |  | ||||||
|             print '[arg-name1=arg1] ... [arg-nameN=argN]' |  | ||||||
|             return True |  | ||||||
|         resp = self.cmd_obj(qmpcmd) |  | ||||||
|         if resp is None: |  | ||||||
|             print 'Disconnected' |  | ||||||
|             return False |  | ||||||
|         print resp |  | ||||||
|         return True |  | ||||||
|  |  | ||||||
|     def connect(self): |  | ||||||
|         self._greeting = qmp.QEMUMonitorProtocol.connect(self) |  | ||||||
|         self.__completer_setup() |  | ||||||
|  |  | ||||||
|     def show_banner(self, msg='Welcome to the QMP low-level shell!'): |  | ||||||
|         print msg |  | ||||||
|         version = self._greeting['QMP']['version']['qemu'] |  | ||||||
|         print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) |  | ||||||
|  |  | ||||||
|     def read_exec_command(self, prompt): |  | ||||||
|         """ |  | ||||||
|         Read and execute a command. |  | ||||||
|  |  | ||||||
|         @return True if execution was ok, return False if disconnected. |  | ||||||
|         """ |  | ||||||
|         try: |  | ||||||
|             cmdline = raw_input(prompt) |  | ||||||
|         except EOFError: |  | ||||||
|             print |  | ||||||
|             return False |  | ||||||
|         if cmdline == '': |  | ||||||
|             for ev in self.get_events(): |  | ||||||
|                 print ev |  | ||||||
|             self.clear_events() |  | ||||||
|             return True |  | ||||||
|         else: |  | ||||||
|             return self._execute_cmd(cmdline) |  | ||||||
|  |  | ||||||
| class HMPShell(QMPShell): |  | ||||||
|     def __init__(self, address): |  | ||||||
|         QMPShell.__init__(self, address) |  | ||||||
|         self.__cpu_index = 0 |  | ||||||
|  |  | ||||||
|     def __cmd_completion(self): |  | ||||||
|         for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'): |  | ||||||
|             if cmd and cmd[0] != '[' and cmd[0] != '\t': |  | ||||||
|                 name = cmd.split()[0] # drop help text |  | ||||||
|                 if name == 'info': |  | ||||||
|                     continue |  | ||||||
|                 if name.find('|') != -1: |  | ||||||
|                     # Command in the form 'foobar|f' or 'f|foobar', take the |  | ||||||
|                     # full name |  | ||||||
|                     opt = name.split('|') |  | ||||||
|                     if len(opt[0]) == 1: |  | ||||||
|                         name = opt[1] |  | ||||||
|                     else: |  | ||||||
|                         name = opt[0] |  | ||||||
|                 self._completer.append(name) |  | ||||||
|                 self._completer.append('help ' + name) # help completion |  | ||||||
|  |  | ||||||
|     def __info_completion(self): |  | ||||||
|         for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'): |  | ||||||
|             if cmd: |  | ||||||
|                 self._completer.append('info ' + cmd.split()[1]) |  | ||||||
|  |  | ||||||
|     def __other_completion(self): |  | ||||||
|         # special cases |  | ||||||
|         self._completer.append('help info') |  | ||||||
|  |  | ||||||
|     def _fill_completion(self): |  | ||||||
|         self.__cmd_completion() |  | ||||||
|         self.__info_completion() |  | ||||||
|         self.__other_completion() |  | ||||||
|  |  | ||||||
|     def __cmd_passthrough(self, cmdline, cpu_index = 0): |  | ||||||
|         return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments': |  | ||||||
|                               { 'command-line': cmdline, |  | ||||||
|                                 'cpu-index': cpu_index } }) |  | ||||||
|  |  | ||||||
|     def _execute_cmd(self, cmdline): |  | ||||||
|         if cmdline.split()[0] == "cpu": |  | ||||||
|             # trap the cpu command, it requires special setting |  | ||||||
|             try: |  | ||||||
|                 idx = int(cmdline.split()[1]) |  | ||||||
|                 if not 'return' in self.__cmd_passthrough('info version', idx): |  | ||||||
|                     print 'bad CPU index' |  | ||||||
|                     return True |  | ||||||
|                 self.__cpu_index = idx |  | ||||||
|             except ValueError: |  | ||||||
|                 print 'cpu command takes an integer argument' |  | ||||||
|                 return True |  | ||||||
|         resp = self.__cmd_passthrough(cmdline, self.__cpu_index) |  | ||||||
|         if resp is None: |  | ||||||
|             print 'Disconnected' |  | ||||||
|             return False |  | ||||||
|         assert 'return' in resp or 'error' in resp |  | ||||||
|         if 'return' in resp: |  | ||||||
|             # Success |  | ||||||
|             if len(resp['return']) > 0: |  | ||||||
|                 print resp['return'], |  | ||||||
|         else: |  | ||||||
|             # Error |  | ||||||
|             print '%s: %s' % (resp['error']['class'], resp['error']['desc']) |  | ||||||
|         return True |  | ||||||
|  |  | ||||||
|     def show_banner(self): |  | ||||||
|         QMPShell.show_banner(self, msg='Welcome to the HMP shell!') |  | ||||||
|  |  | ||||||
| def die(msg): |  | ||||||
|     sys.stderr.write('ERROR: %s\n' % msg) |  | ||||||
|     sys.exit(1) |  | ||||||
|  |  | ||||||
| def fail_cmdline(option=None): |  | ||||||
|     if option: |  | ||||||
|         sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) |  | ||||||
|     sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n') |  | ||||||
|     sys.exit(1) |  | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|     addr = '' |     if len(argv) != 2: | ||||||
|     try: |         print 'qemu-shell <unix-socket>' | ||||||
|         if len(sys.argv) == 2: |         exit(1) | ||||||
|             qemu = QMPShell(sys.argv[1]) |  | ||||||
|             addr = sys.argv[1] |     qemu = qmp.QEMUMonitorProtocol(argv[1]) | ||||||
|         elif len(sys.argv) == 3: |     qemu.connect() | ||||||
|             if sys.argv[1] != '-H': |  | ||||||
|                 fail_cmdline(sys.argv[1]) |     print 'Connected!' | ||||||
|             qemu = HMPShell(sys.argv[2]) |  | ||||||
|             addr = sys.argv[2] |     while True: | ||||||
|  |         try: | ||||||
|  |             cmd = raw_input('(QEMU) ') | ||||||
|  |         except EOFError: | ||||||
|  |             print | ||||||
|  |             break | ||||||
|  |         if cmd == '': | ||||||
|  |             continue | ||||||
|  |         elif cmd == 'bye': | ||||||
|  |             break | ||||||
|  |         elif cmd == 'help': | ||||||
|  |             shell_help() | ||||||
|         else: |         else: | ||||||
|                 fail_cmdline() |             try: | ||||||
|     except QMPShellBadPort: |                 resp = qemu.send(cmd) | ||||||
|         die('bad port number in command-line') |                 if resp == None: | ||||||
|  |                     print 'Disconnected' | ||||||
|     try: |                     break | ||||||
|         qemu.connect() |                 print resp | ||||||
|     except qmp.QMPConnectError: |             except IndexError: | ||||||
|         die('Didn\'t get QMP greeting message') |                 print '-> command format: <command-name> ', | ||||||
|     except qmp.QMPCapabilitiesError: |                 print '[arg-name1=arg1] ... [arg-nameN=argN]' | ||||||
|         die('Could not negotiate capabilities') |  | ||||||
|     except qemu.error: |  | ||||||
|         die('Could not connect to %s' % addr) |  | ||||||
|  |  | ||||||
|     qemu.show_banner() |  | ||||||
|     while qemu.read_exec_command('(QEMU) '): |  | ||||||
|         pass |  | ||||||
|     qemu.close() |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     main() |     main() | ||||||
|   | |||||||
| @@ -44,17 +44,14 @@ they can be in ANY order, thus no particular order should be assumed. | |||||||
|  |  | ||||||
| Right when connected the Server will issue a greeting message, which signals | Right when connected the Server will issue a greeting message, which signals | ||||||
| that the connection has been successfully established and that the Server is | that the connection has been successfully established and that the Server is | ||||||
| ready for capabilities negotiation (for more information refer to section | waiting for commands. | ||||||
| '4. Capabilities Negotiation'). |  | ||||||
|  |  | ||||||
| The format is: | The format is: | ||||||
|  |  | ||||||
| { "QMP": { "version": json-object, "capabilities": json-array } } | { "QMP": { "capabilities": json-array } } | ||||||
|  |  | ||||||
|  Where, |  Where, | ||||||
|  |  | ||||||
| - The "version" member contains the Server's version information (the format |  | ||||||
|   is the same of the 'query-version' command) |  | ||||||
| - The "capabilities" member specify the availability of features beyond the | - The "capabilities" member specify the availability of features beyond the | ||||||
|   baseline specification |   baseline specification | ||||||
|  |  | ||||||
| @@ -155,7 +152,7 @@ This section provides some examples of real QMP usage, in all of them | |||||||
| 3.1 Server greeting | 3.1 Server greeting | ||||||
| ------------------- | ------------------- | ||||||
|  |  | ||||||
| S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}} | S: {"QMP": {"capabilities": []}} | ||||||
|  |  | ||||||
| 3.2 Simple 'stop' execution | 3.2 Simple 'stop' execution | ||||||
| --------------------------- | --------------------------- | ||||||
| @@ -182,91 +179,25 @@ S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data": | |||||||
| S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": | S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": | ||||||
| "POWERDOWN"} | "POWERDOWN"} | ||||||
|  |  | ||||||
| 4. Capabilities Negotiation | 4. Compatibility Considerations | ||||||
| ---------------------------- | -------------------------------- | ||||||
|  |  | ||||||
| When a Client successfully establishes a connection, the Server is in | In order to achieve maximum compatibility between versions, Clients must not  | ||||||
| Capabilities Negotiation mode. | assume any particular: | ||||||
|  |  | ||||||
| In this mode only the 'qmp_capabilities' command is allowed to run, all |  | ||||||
| other commands will return the CommandNotFound error. Asynchronous messages |  | ||||||
| are not delivered either. |  | ||||||
|  |  | ||||||
| Clients should use the 'qmp_capabilities' command to enable capabilities |  | ||||||
| advertised in the Server's greeting (section '2.2 Server Greeting') they |  | ||||||
| support. |  | ||||||
|  |  | ||||||
| When the 'qmp_capabilities' command is issued, and if it does not return an |  | ||||||
| error, the Server enters in Command mode where capabilities changes take |  | ||||||
| effect, all commands (except 'qmp_capabilities') are allowed and asynchronous |  | ||||||
| messages are delivered. |  | ||||||
|  |  | ||||||
| 5 Compatibility Considerations |  | ||||||
| ------------------------------ |  | ||||||
|  |  | ||||||
| All protocol changes or new features which modify the protocol format in an |  | ||||||
| incompatible way are disabled by default and will be advertised by the |  | ||||||
| capabilities array (section '2.2 Server Greeting'). Thus, Clients can check |  | ||||||
| that array and enable the capabilities they support. |  | ||||||
|  |  | ||||||
| Additionally, Clients must not assume any particular: |  | ||||||
|  |  | ||||||
| - Size of json-objects or length of json-arrays | - Size of json-objects or length of json-arrays | ||||||
| - Order of json-object members or json-array elements | - Order of json-object members or json-array elements | ||||||
| - Amount of errors generated by a command, that is, new errors can be added | - Amount of errors generated by a command, that is, new errors can be added | ||||||
|   to any existing command in newer versions of the Server |   to any existing command in newer versions of the Server | ||||||
|  |  | ||||||
| 6. Downstream extension of QMP | Additionally, Clients should always: | ||||||
| ------------------------------ |  | ||||||
|  |  | ||||||
| We recommend that downstream consumers of QEMU do *not* modify QMP. | - Check the capabilities json-array at connection time | ||||||
| Management tools should be able to support both upstream and downstream | - Check the availability of commands with 'query-commands' before issuing them | ||||||
| versions of QMP without special logic, and downstream extensions are |  | ||||||
| inherently at odds with that. |  | ||||||
|  |  | ||||||
| However, we recognize that it is sometimes impossible for downstreams to | 5. Recommendations to Client implementors | ||||||
| avoid modifying QMP.  Both upstream and downstream need to take care to | ----------------------------------------- | ||||||
| preserve long-term compatibility and interoperability. |  | ||||||
|  |  | ||||||
| To help with that, QMP reserves JSON object member names beginning with | 5.1 The Server should be always started in pause mode, thus the Client is | ||||||
| '__' (double underscore) for downstream use ("downstream names").  This |     able to perform any setup procedure without the risk of race conditions | ||||||
| means upstream will never use any downstream names for its commands, |     and related problems | ||||||
| arguments, errors, asynchronous events, and so forth. |  | ||||||
|  |  | ||||||
| Any new names downstream wishes to add must begin with '__'.  To |  | ||||||
| ensure compatibility with other downstreams, it is strongly |  | ||||||
| recommended that you prefix your downstram names with '__RFQDN_' where |  | ||||||
| RFQDN is a valid, reverse fully qualified domain name which you |  | ||||||
| control.  For example, a qemu-kvm specific monitor command would be: |  | ||||||
|  |  | ||||||
|     (qemu) __org.linux-kvm_enable_irqchip |  | ||||||
|  |  | ||||||
| Downstream must not change the server greeting (section 2.2) other than |  | ||||||
| to offer additional capabilities.  But see below for why even that is |  | ||||||
| discouraged. |  | ||||||
|  |  | ||||||
| Section '5 Compatibility Considerations' applies to downstream as well |  | ||||||
| as to upstream, obviously.  It follows that downstream must behave |  | ||||||
| exactly like upstream for any input not containing members with |  | ||||||
| downstream names ("downstream members"), except it may add members |  | ||||||
| with downstream names to its output. |  | ||||||
|  |  | ||||||
| Thus, a client should not be able to distinguish downstream from |  | ||||||
| upstream as long as it doesn't send input with downstream members, and |  | ||||||
| properly ignores any downstream members in the output it receives. |  | ||||||
|  |  | ||||||
| Advice on downstream modifications: |  | ||||||
|  |  | ||||||
| 1. Introducing new commands is okay.  If you want to extend an existing |  | ||||||
|    command, consider introducing a new one with the new behaviour |  | ||||||
|    instead. |  | ||||||
|  |  | ||||||
| 2. Introducing new asynchronous messages is okay.  If you want to extend |  | ||||||
|    an existing message, consider adding a new one instead. |  | ||||||
|  |  | ||||||
| 3. Introducing new errors for use in new commands is okay.  Adding new |  | ||||||
|    errors to existing commands counts as extension, so 1. applies. |  | ||||||
|  |  | ||||||
| 4. New capabilities are strongly discouraged.  Capabilities are for |  | ||||||
|    evolving the basic protocol, and multiple diverging basic protocol |  | ||||||
|    dialects are most undesirable. |  | ||||||
|   | |||||||
							
								
								
									
										161
									
								
								QMP/qmp.py
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								QMP/qmp.py
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| # QEMU Monitor Protocol Python class | # QEMU Monitor Protocol Python class | ||||||
| #  | #  | ||||||
| # Copyright (C) 2009, 2010 Red Hat Inc. | # Copyright (C) 2009 Red Hat Inc. | ||||||
| # | # | ||||||
| # Authors: | # Authors: | ||||||
| #  Luiz Capitulino <lcapitulino@redhat.com> | #  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
| @@ -8,9 +8,7 @@ | |||||||
| # This work is licensed under the terms of the GNU GPL, version 2.  See | # This work is licensed under the terms of the GNU GPL, version 2.  See | ||||||
| # the COPYING file in the top-level directory. | # the COPYING file in the top-level directory. | ||||||
|  |  | ||||||
| import json | import socket, json | ||||||
| import errno |  | ||||||
| import socket |  | ||||||
|  |  | ||||||
| class QMPError(Exception): | class QMPError(Exception): | ||||||
|     pass |     pass | ||||||
| @@ -18,114 +16,57 @@ class QMPError(Exception): | |||||||
| class QMPConnectError(QMPError): | class QMPConnectError(QMPError): | ||||||
|     pass |     pass | ||||||
|  |  | ||||||
| class QMPCapabilitiesError(QMPError): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| class QEMUMonitorProtocol: | class QEMUMonitorProtocol: | ||||||
|     def __init__(self, address): |  | ||||||
|         """ |  | ||||||
|         Create a QEMUMonitorProtocol class. |  | ||||||
|  |  | ||||||
|         @param address: QEMU address, can be either a unix socket path (string) |  | ||||||
|                         or a tuple in the form ( address, port ) for a TCP |  | ||||||
|                         connection |  | ||||||
|         @note No connection is established, this is done by the connect() method |  | ||||||
|         """ |  | ||||||
|         self.__events = [] |  | ||||||
|         self.__address = address |  | ||||||
|         self.__sock = self.__get_sock() |  | ||||||
|         self.__sockfile = self.__sock.makefile() |  | ||||||
|  |  | ||||||
|     def __get_sock(self): |  | ||||||
|         if isinstance(self.__address, tuple): |  | ||||||
|             family = socket.AF_INET |  | ||||||
|         else: |  | ||||||
|             family = socket.AF_UNIX |  | ||||||
|         return socket.socket(family, socket.SOCK_STREAM) |  | ||||||
|  |  | ||||||
|     def __json_read(self): |  | ||||||
|         while True: |  | ||||||
|             data = self.__sockfile.readline() |  | ||||||
|             if not data: |  | ||||||
|                 return |  | ||||||
|             resp = json.loads(data) |  | ||||||
|             if 'event' in resp: |  | ||||||
|                 self.__events.append(resp) |  | ||||||
|                 continue |  | ||||||
|             return resp |  | ||||||
|  |  | ||||||
|     error = socket.error |  | ||||||
|  |  | ||||||
|     def connect(self): |     def connect(self): | ||||||
|         """ |         self.sock.connect(self.filename) | ||||||
|         Connect to the QMP Monitor and perform capabilities negotiation. |         data = self.__json_read() | ||||||
|  |         if data == None: | ||||||
|         @return QMP greeting dict |  | ||||||
|         @raise socket.error on socket connection errors |  | ||||||
|         @raise QMPConnectError if the greeting is not received |  | ||||||
|         @raise QMPCapabilitiesError if fails to negotiate capabilities |  | ||||||
|         """ |  | ||||||
|         self.__sock.connect(self.__address) |  | ||||||
|         greeting = self.__json_read() |  | ||||||
|         if greeting is None or not greeting.has_key('QMP'): |  | ||||||
|             raise QMPConnectError |             raise QMPConnectError | ||||||
|         # Greeting seems ok, negotiate capabilities |         if not data.has_key('QMP'): | ||||||
|         resp = self.cmd('qmp_capabilities') |             raise QMPConnectError | ||||||
|         if "return" in resp: |         return data['QMP']['capabilities'] | ||||||
|             return greeting |  | ||||||
|         raise QMPCapabilitiesError |  | ||||||
|  |  | ||||||
|     def cmd_obj(self, qmp_cmd): |  | ||||||
|         """ |  | ||||||
|         Send a QMP command to the QMP Monitor. |  | ||||||
|  |  | ||||||
|         @param qmp_cmd: QMP command to be sent as a Python dict |  | ||||||
|         @return QMP response as a Python dict or None if the connection has |  | ||||||
|                 been closed |  | ||||||
|         """ |  | ||||||
|         try: |  | ||||||
|             self.__sock.sendall(json.dumps(qmp_cmd)) |  | ||||||
|         except socket.error, err: |  | ||||||
|             if err[0] == errno.EPIPE: |  | ||||||
|                 return |  | ||||||
|             raise socket.error(err) |  | ||||||
|         return self.__json_read() |  | ||||||
|  |  | ||||||
|     def cmd(self, name, args=None, id=None): |  | ||||||
|         """ |  | ||||||
|         Build a QMP command and send it to the QMP Monitor. |  | ||||||
|  |  | ||||||
|         @param name: command name (string) |  | ||||||
|         @param args: command arguments (dict) |  | ||||||
|         @param id: command id (dict, list, string or int) |  | ||||||
|         """ |  | ||||||
|         qmp_cmd = { 'execute': name } |  | ||||||
|         if args: |  | ||||||
|             qmp_cmd['arguments'] = args |  | ||||||
|         if id: |  | ||||||
|             qmp_cmd['id'] = id |  | ||||||
|         return self.cmd_obj(qmp_cmd) |  | ||||||
|  |  | ||||||
|     def get_events(self): |  | ||||||
|         """ |  | ||||||
|         Get a list of available QMP events. |  | ||||||
|         """ |  | ||||||
|         self.__sock.setblocking(0) |  | ||||||
|         try: |  | ||||||
|             self.__json_read() |  | ||||||
|         except socket.error, err: |  | ||||||
|             if err[0] == errno.EAGAIN: |  | ||||||
|                 # No data available |  | ||||||
|                 pass |  | ||||||
|         self.__sock.setblocking(1) |  | ||||||
|         return self.__events |  | ||||||
|  |  | ||||||
|     def clear_events(self): |  | ||||||
|         """ |  | ||||||
|         Clear current list of pending events. |  | ||||||
|         """ |  | ||||||
|         self.__events = [] |  | ||||||
|  |  | ||||||
|     def close(self): |     def close(self): | ||||||
|         self.__sock.close() |         self.sock.close() | ||||||
|         self.__sockfile.close() |  | ||||||
|  |     def send_raw(self, line): | ||||||
|  |         self.sock.send(str(line)) | ||||||
|  |         return self.__json_read() | ||||||
|  |  | ||||||
|  |     def send(self, cmdline): | ||||||
|  |         cmd = self.__build_cmd(cmdline) | ||||||
|  |         self.__json_send(cmd) | ||||||
|  |         resp = self.__json_read() | ||||||
|  |         if resp == None: | ||||||
|  |             return | ||||||
|  |         elif resp.has_key('error'): | ||||||
|  |             return resp['error'] | ||||||
|  |         else: | ||||||
|  |             return resp['return'] | ||||||
|  |  | ||||||
|  |     def __build_cmd(self, cmdline): | ||||||
|  |         cmdargs = cmdline.split() | ||||||
|  |         qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } | ||||||
|  |         for arg in cmdargs[1:]: | ||||||
|  |             opt = arg.split('=') | ||||||
|  |             try: | ||||||
|  |                 value = int(opt[1]) | ||||||
|  |             except ValueError: | ||||||
|  |                 value = opt[1] | ||||||
|  |             qmpcmd['arguments'][opt[0]] = value | ||||||
|  |         return qmpcmd | ||||||
|  |  | ||||||
|  |     def __json_send(self, cmd): | ||||||
|  |         # XXX: We have to send any additional char, otherwise | ||||||
|  |         # the Server won't read our input | ||||||
|  |         self.sock.send(json.dumps(cmd) + ' ') | ||||||
|  |  | ||||||
|  |     def __json_read(self): | ||||||
|  |         try: | ||||||
|  |             return json.loads(self.sock.recv(1024)) | ||||||
|  |         except ValueError: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |     def __init__(self, filename): | ||||||
|  |         self.filename = filename | ||||||
|  |         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								QMP/vm-info
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								QMP/vm-info
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | #!/usr/bin/python | ||||||
|  | # | ||||||
|  | # Print Virtual Machine information | ||||||
|  | # | ||||||
|  | # Usage: | ||||||
|  | # | ||||||
|  | # Start QEMU with: | ||||||
|  | # | ||||||
|  | # $ qemu [...] -monitor control,unix:./qmp,server | ||||||
|  | # | ||||||
|  | # Run vm-info: | ||||||
|  | # | ||||||
|  | # $ vm-info ./qmp | ||||||
|  | # | ||||||
|  | # Luiz Capitulino <lcapitulino@redhat.com> | ||||||
|  |  | ||||||
|  | import qmp | ||||||
|  | from sys import argv,exit | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  |     if len(argv) != 2: | ||||||
|  |         print 'vm-info <unix-socket>' | ||||||
|  |         exit(1) | ||||||
|  |  | ||||||
|  |     qemu = qmp.QEMUMonitorProtocol(argv[1]) | ||||||
|  |     qemu.connect() | ||||||
|  |  | ||||||
|  |     for cmd in [ 'version', 'hpet', 'kvm', 'status', 'uuid', 'balloon' ]: | ||||||
|  |         print cmd + ': ' + str(qemu.send('query-' + cmd)) | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
| @@ -22,9 +22,6 @@ along with this file; see the file COPYING.  If not, see | |||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include "dis-asm.h" | #include "dis-asm.h" | ||||||
|  |  | ||||||
| /* MAX is redefined below, so remove any previous definition. */ |  | ||||||
| #undef MAX |  | ||||||
|  |  | ||||||
| /* The opcode table is an array of struct alpha_opcode.  */ | /* The opcode table is an array of struct alpha_opcode.  */ | ||||||
|  |  | ||||||
| struct alpha_opcode | struct alpha_opcode | ||||||
|   | |||||||
							
								
								
									
										726
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										726
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -1,726 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU System Emulator |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #ifndef _WIN32 |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/mman.h> |  | ||||||
| #endif |  | ||||||
| #include "config.h" |  | ||||||
| #include "monitor.h" |  | ||||||
| #include "sysemu.h" |  | ||||||
| #include "arch_init.h" |  | ||||||
| #include "audio/audio.h" |  | ||||||
| #include "hw/pc.h" |  | ||||||
| #include "hw/pci.h" |  | ||||||
| #include "hw/audiodev.h" |  | ||||||
| #include "kvm.h" |  | ||||||
| #include "migration.h" |  | ||||||
| #include "net.h" |  | ||||||
| #include "gdbstub.h" |  | ||||||
| #include "hw/smbios.h" |  | ||||||
|  |  | ||||||
| #ifdef TARGET_SPARC |  | ||||||
| int graphic_width = 1024; |  | ||||||
| int graphic_height = 768; |  | ||||||
| int graphic_depth = 8; |  | ||||||
| #else |  | ||||||
| int graphic_width = 800; |  | ||||||
| int graphic_height = 600; |  | ||||||
| int graphic_depth = 15; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf"; |  | ||||||
|  |  | ||||||
| #if defined(TARGET_ALPHA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_ALPHA |  | ||||||
| #elif defined(TARGET_ARM) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_ARM |  | ||||||
| #elif defined(TARGET_CRIS) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_CRIS |  | ||||||
| #elif defined(TARGET_I386) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_I386 |  | ||||||
| #elif defined(TARGET_M68K) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_M68K |  | ||||||
| #elif defined(TARGET_MICROBLAZE) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE |  | ||||||
| #elif defined(TARGET_MIPS) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_MIPS |  | ||||||
| #elif defined(TARGET_PPC) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_PPC |  | ||||||
| #elif defined(TARGET_S390X) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_S390X |  | ||||||
| #elif defined(TARGET_SH4) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_SH4 |  | ||||||
| #elif defined(TARGET_SPARC) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_SPARC |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| const uint32_t arch_type = QEMU_ARCH; |  | ||||||
|  |  | ||||||
| /***********************************************************/ |  | ||||||
| /* ram save/restore */ |  | ||||||
|  |  | ||||||
| #define RAM_SAVE_FLAG_FULL     0x01 /* Obsolete, not used anymore */ |  | ||||||
| #define RAM_SAVE_FLAG_COMPRESS 0x02 |  | ||||||
| #define RAM_SAVE_FLAG_MEM_SIZE 0x04 |  | ||||||
| #define RAM_SAVE_FLAG_PAGE     0x08 |  | ||||||
| #define RAM_SAVE_FLAG_EOS      0x10 |  | ||||||
| #define RAM_SAVE_FLAG_CONTINUE 0x20 |  | ||||||
|  |  | ||||||
| static int is_dup_page(uint8_t *page, uint8_t ch) |  | ||||||
| { |  | ||||||
|     uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; |  | ||||||
|     uint32_t *array = (uint32_t *)page; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { |  | ||||||
|         if (array[i] != val) { |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static RAMBlock *last_block; |  | ||||||
| static ram_addr_t last_offset; |  | ||||||
|  |  | ||||||
| static int ram_save_block(QEMUFile *f) |  | ||||||
| { |  | ||||||
|     RAMBlock *block = last_block; |  | ||||||
|     ram_addr_t offset = last_offset; |  | ||||||
|     ram_addr_t current_addr; |  | ||||||
|     int bytes_sent = 0; |  | ||||||
|  |  | ||||||
|     if (!block) |  | ||||||
|         block = QLIST_FIRST(&ram_list.blocks); |  | ||||||
|  |  | ||||||
|     current_addr = block->offset + offset; |  | ||||||
|  |  | ||||||
|     do { |  | ||||||
|         if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { |  | ||||||
|             uint8_t *p; |  | ||||||
|             int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0; |  | ||||||
|  |  | ||||||
|             cpu_physical_memory_reset_dirty(current_addr, |  | ||||||
|                                             current_addr + TARGET_PAGE_SIZE, |  | ||||||
|                                             MIGRATION_DIRTY_FLAG); |  | ||||||
|  |  | ||||||
|             p = block->host + offset; |  | ||||||
|  |  | ||||||
|             if (is_dup_page(p, *p)) { |  | ||||||
|                 qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS); |  | ||||||
|                 if (!cont) { |  | ||||||
|                     qemu_put_byte(f, strlen(block->idstr)); |  | ||||||
|                     qemu_put_buffer(f, (uint8_t *)block->idstr, |  | ||||||
|                                     strlen(block->idstr)); |  | ||||||
|                 } |  | ||||||
|                 qemu_put_byte(f, *p); |  | ||||||
|                 bytes_sent = 1; |  | ||||||
|             } else { |  | ||||||
|                 qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE); |  | ||||||
|                 if (!cont) { |  | ||||||
|                     qemu_put_byte(f, strlen(block->idstr)); |  | ||||||
|                     qemu_put_buffer(f, (uint8_t *)block->idstr, |  | ||||||
|                                     strlen(block->idstr)); |  | ||||||
|                 } |  | ||||||
|                 qemu_put_buffer(f, p, TARGET_PAGE_SIZE); |  | ||||||
|                 bytes_sent = TARGET_PAGE_SIZE; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         offset += TARGET_PAGE_SIZE; |  | ||||||
|         if (offset >= block->length) { |  | ||||||
|             offset = 0; |  | ||||||
|             block = QLIST_NEXT(block, next); |  | ||||||
|             if (!block) |  | ||||||
|                 block = QLIST_FIRST(&ram_list.blocks); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         current_addr = block->offset + offset; |  | ||||||
|  |  | ||||||
|     } while (current_addr != last_block->offset + last_offset); |  | ||||||
|  |  | ||||||
|     last_block = block; |  | ||||||
|     last_offset = offset; |  | ||||||
|  |  | ||||||
|     return bytes_sent; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uint64_t bytes_transferred; |  | ||||||
|  |  | ||||||
| static ram_addr_t ram_save_remaining(void) |  | ||||||
| { |  | ||||||
|     RAMBlock *block; |  | ||||||
|     ram_addr_t count = 0; |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|         ram_addr_t addr; |  | ||||||
|         for (addr = block->offset; addr < block->offset + block->length; |  | ||||||
|              addr += TARGET_PAGE_SIZE) { |  | ||||||
|             if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { |  | ||||||
|                 count++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return count; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t ram_bytes_remaining(void) |  | ||||||
| { |  | ||||||
|     return ram_save_remaining() * TARGET_PAGE_SIZE; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t ram_bytes_transferred(void) |  | ||||||
| { |  | ||||||
|     return bytes_transferred; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t ram_bytes_total(void) |  | ||||||
| { |  | ||||||
|     RAMBlock *block; |  | ||||||
|     uint64_t total = 0; |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) |  | ||||||
|         total += block->length; |  | ||||||
|  |  | ||||||
|     return total; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int block_compar(const void *a, const void *b) |  | ||||||
| { |  | ||||||
|     RAMBlock * const *ablock = a; |  | ||||||
|     RAMBlock * const *bblock = b; |  | ||||||
|     if ((*ablock)->offset < (*bblock)->offset) { |  | ||||||
|         return -1; |  | ||||||
|     } else if ((*ablock)->offset > (*bblock)->offset) { |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void sort_ram_list(void) |  | ||||||
| { |  | ||||||
|     RAMBlock *block, *nblock, **blocks; |  | ||||||
|     int n; |  | ||||||
|     n = 0; |  | ||||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|         ++n; |  | ||||||
|     } |  | ||||||
|     blocks = qemu_malloc(n * sizeof *blocks); |  | ||||||
|     n = 0; |  | ||||||
|     QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) { |  | ||||||
|         blocks[n++] = block; |  | ||||||
|         QLIST_REMOVE(block, next); |  | ||||||
|     } |  | ||||||
|     qsort(blocks, n, sizeof *blocks, block_compar); |  | ||||||
|     while (--n >= 0) { |  | ||||||
|         QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next); |  | ||||||
|     } |  | ||||||
|     qemu_free(blocks); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) |  | ||||||
| { |  | ||||||
|     ram_addr_t addr; |  | ||||||
|     uint64_t bytes_transferred_last; |  | ||||||
|     double bwidth = 0; |  | ||||||
|     uint64_t expected_time = 0; |  | ||||||
|  |  | ||||||
|     if (stage < 0) { |  | ||||||
|         cpu_physical_memory_set_dirty_tracking(0); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { |  | ||||||
|         qemu_file_set_error(f); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (stage == 1) { |  | ||||||
|         RAMBlock *block; |  | ||||||
|         bytes_transferred = 0; |  | ||||||
|         last_block = NULL; |  | ||||||
|         last_offset = 0; |  | ||||||
|         sort_ram_list(); |  | ||||||
|  |  | ||||||
|         /* Make sure all dirty bits are set */ |  | ||||||
|         QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|             for (addr = block->offset; addr < block->offset + block->length; |  | ||||||
|                  addr += TARGET_PAGE_SIZE) { |  | ||||||
|                 if (!cpu_physical_memory_get_dirty(addr, |  | ||||||
|                                                    MIGRATION_DIRTY_FLAG)) { |  | ||||||
|                     cpu_physical_memory_set_dirty(addr); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Enable dirty memory tracking */ |  | ||||||
|         cpu_physical_memory_set_dirty_tracking(1); |  | ||||||
|  |  | ||||||
|         qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); |  | ||||||
|  |  | ||||||
|         QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|             qemu_put_byte(f, strlen(block->idstr)); |  | ||||||
|             qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); |  | ||||||
|             qemu_put_be64(f, block->length); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bytes_transferred_last = bytes_transferred; |  | ||||||
|     bwidth = qemu_get_clock_ns(rt_clock); |  | ||||||
|  |  | ||||||
|     while (!qemu_file_rate_limit(f)) { |  | ||||||
|         int bytes_sent; |  | ||||||
|  |  | ||||||
|         bytes_sent = ram_save_block(f); |  | ||||||
|         bytes_transferred += bytes_sent; |  | ||||||
|         if (bytes_sent == 0) { /* no more blocks */ |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bwidth = qemu_get_clock_ns(rt_clock) - bwidth; |  | ||||||
|     bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; |  | ||||||
|  |  | ||||||
|     /* if we haven't transferred anything this round, force expected_time to a |  | ||||||
|      * a very high value, but without crashing */ |  | ||||||
|     if (bwidth == 0) { |  | ||||||
|         bwidth = 0.000001; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* try transferring iterative blocks of memory */ |  | ||||||
|     if (stage == 3) { |  | ||||||
|         int bytes_sent; |  | ||||||
|  |  | ||||||
|         /* flush all remaining blocks regardless of rate limiting */ |  | ||||||
|         while ((bytes_sent = ram_save_block(f)) != 0) { |  | ||||||
|             bytes_transferred += bytes_sent; |  | ||||||
|         } |  | ||||||
|         cpu_physical_memory_set_dirty_tracking(0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_put_be64(f, RAM_SAVE_FLAG_EOS); |  | ||||||
|  |  | ||||||
|     expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; |  | ||||||
|  |  | ||||||
|     return (stage == 2) && (expected_time <= migrate_max_downtime()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void *host_from_stream_offset(QEMUFile *f, |  | ||||||
|                                             ram_addr_t offset, |  | ||||||
|                                             int flags) |  | ||||||
| { |  | ||||||
|     static RAMBlock *block = NULL; |  | ||||||
|     char id[256]; |  | ||||||
|     uint8_t len; |  | ||||||
|  |  | ||||||
|     if (flags & RAM_SAVE_FLAG_CONTINUE) { |  | ||||||
|         if (!block) { |  | ||||||
|             fprintf(stderr, "Ack, bad migration stream!\n"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return block->host + offset; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     len = qemu_get_byte(f); |  | ||||||
|     qemu_get_buffer(f, (uint8_t *)id, len); |  | ||||||
|     id[len] = 0; |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|         if (!strncmp(id, block->idstr, sizeof(id))) |  | ||||||
|             return block->host + offset; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fprintf(stderr, "Can't find block %s!\n", id); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int ram_load(QEMUFile *f, void *opaque, int version_id) |  | ||||||
| { |  | ||||||
|     ram_addr_t addr; |  | ||||||
|     int flags; |  | ||||||
|  |  | ||||||
|     if (version_id < 3 || version_id > 4) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     do { |  | ||||||
|         addr = qemu_get_be64(f); |  | ||||||
|  |  | ||||||
|         flags = addr & ~TARGET_PAGE_MASK; |  | ||||||
|         addr &= TARGET_PAGE_MASK; |  | ||||||
|  |  | ||||||
|         if (flags & RAM_SAVE_FLAG_MEM_SIZE) { |  | ||||||
|             if (version_id == 3) { |  | ||||||
|                 if (addr != ram_bytes_total()) { |  | ||||||
|                     return -EINVAL; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 /* Synchronize RAM block list */ |  | ||||||
|                 char id[256]; |  | ||||||
|                 ram_addr_t length; |  | ||||||
|                 ram_addr_t total_ram_bytes = addr; |  | ||||||
|  |  | ||||||
|                 while (total_ram_bytes) { |  | ||||||
|                     RAMBlock *block; |  | ||||||
|                     uint8_t len; |  | ||||||
|  |  | ||||||
|                     len = qemu_get_byte(f); |  | ||||||
|                     qemu_get_buffer(f, (uint8_t *)id, len); |  | ||||||
|                     id[len] = 0; |  | ||||||
|                     length = qemu_get_be64(f); |  | ||||||
|  |  | ||||||
|                     QLIST_FOREACH(block, &ram_list.blocks, next) { |  | ||||||
|                         if (!strncmp(id, block->idstr, sizeof(id))) { |  | ||||||
|                             if (block->length != length) |  | ||||||
|                                 return -EINVAL; |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if (!block) { |  | ||||||
|                         fprintf(stderr, "Unknown ramblock \"%s\", cannot " |  | ||||||
|                                 "accept migration\n", id); |  | ||||||
|                         return -EINVAL; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     total_ram_bytes -= length; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (flags & RAM_SAVE_FLAG_COMPRESS) { |  | ||||||
|             void *host; |  | ||||||
|             uint8_t ch; |  | ||||||
|  |  | ||||||
|             if (version_id == 3) |  | ||||||
|                 host = qemu_get_ram_ptr(addr); |  | ||||||
|             else |  | ||||||
|                 host = host_from_stream_offset(f, addr, flags); |  | ||||||
|             if (!host) { |  | ||||||
|                 return -EINVAL; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ch = qemu_get_byte(f); |  | ||||||
|             memset(host, ch, TARGET_PAGE_SIZE); |  | ||||||
| #ifndef _WIN32 |  | ||||||
|             if (ch == 0 && |  | ||||||
|                 (!kvm_enabled() || kvm_has_sync_mmu())) { |  | ||||||
|                 qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); |  | ||||||
|             } |  | ||||||
| #endif |  | ||||||
|         } else if (flags & RAM_SAVE_FLAG_PAGE) { |  | ||||||
|             void *host; |  | ||||||
|  |  | ||||||
|             if (version_id == 3) |  | ||||||
|                 host = qemu_get_ram_ptr(addr); |  | ||||||
|             else |  | ||||||
|                 host = host_from_stream_offset(f, addr, flags); |  | ||||||
|  |  | ||||||
|             qemu_get_buffer(f, host, TARGET_PAGE_SIZE); |  | ||||||
|         } |  | ||||||
|         if (qemu_file_has_error(f)) { |  | ||||||
|             return -EIO; |  | ||||||
|         } |  | ||||||
|     } while (!(flags & RAM_SAVE_FLAG_EOS)); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qemu_service_io(void) |  | ||||||
| { |  | ||||||
|     qemu_notify_event(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifdef HAS_AUDIO |  | ||||||
| struct soundhw { |  | ||||||
|     const char *name; |  | ||||||
|     const char *descr; |  | ||||||
|     int enabled; |  | ||||||
|     int isa; |  | ||||||
|     union { |  | ||||||
|         int (*init_isa) (qemu_irq *pic); |  | ||||||
|         int (*init_pci) (PCIBus *bus); |  | ||||||
|     } init; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct soundhw soundhw[] = { |  | ||||||
| #ifdef HAS_AUDIO_CHOICE |  | ||||||
| #if defined(TARGET_I386) || defined(TARGET_MIPS) |  | ||||||
|     { |  | ||||||
|         "pcspk", |  | ||||||
|         "PC speaker", |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         { .init_isa = pcspk_audio_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SB16 |  | ||||||
|     { |  | ||||||
|         "sb16", |  | ||||||
|         "Creative Sound Blaster 16", |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         { .init_isa = SB16_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_CS4231A |  | ||||||
|     { |  | ||||||
|         "cs4231a", |  | ||||||
|         "CS4231A", |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         { .init_isa = cs4231a_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ADLIB |  | ||||||
|     { |  | ||||||
|         "adlib", |  | ||||||
| #ifdef HAS_YMF262 |  | ||||||
|         "Yamaha YMF262 (OPL3)", |  | ||||||
| #else |  | ||||||
|         "Yamaha YM3812 (OPL2)", |  | ||||||
| #endif |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         { .init_isa = Adlib_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_GUS |  | ||||||
|     { |  | ||||||
|         "gus", |  | ||||||
|         "Gravis Ultrasound GF1", |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         { .init_isa = GUS_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_AC97 |  | ||||||
|     { |  | ||||||
|         "ac97", |  | ||||||
|         "Intel 82801AA AC97 Audio", |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         { .init_pci = ac97_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ES1370 |  | ||||||
|     { |  | ||||||
|         "es1370", |  | ||||||
|         "ENSONIQ AudioPCI ES1370", |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         { .init_pci = es1370_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_HDA |  | ||||||
|     { |  | ||||||
|         "hda", |  | ||||||
|         "Intel HD Audio", |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         { .init_pci = intel_hda_and_codec_init } |  | ||||||
|     }, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif /* HAS_AUDIO_CHOICE */ |  | ||||||
|  |  | ||||||
|     { NULL, NULL, 0, 0, { NULL } } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void select_soundhw(const char *optarg) |  | ||||||
| { |  | ||||||
|     struct soundhw *c; |  | ||||||
|  |  | ||||||
|     if (*optarg == '?') { |  | ||||||
|     show_valid_cards: |  | ||||||
|  |  | ||||||
|         printf("Valid sound card names (comma separated):\n"); |  | ||||||
|         for (c = soundhw; c->name; ++c) { |  | ||||||
|             printf ("%-11s %s\n", c->name, c->descr); |  | ||||||
|         } |  | ||||||
|         printf("\n-soundhw all will enable all of the above\n"); |  | ||||||
|         exit(*optarg != '?'); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         size_t l; |  | ||||||
|         const char *p; |  | ||||||
|         char *e; |  | ||||||
|         int bad_card = 0; |  | ||||||
|  |  | ||||||
|         if (!strcmp(optarg, "all")) { |  | ||||||
|             for (c = soundhw; c->name; ++c) { |  | ||||||
|                 c->enabled = 1; |  | ||||||
|             } |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         p = optarg; |  | ||||||
|         while (*p) { |  | ||||||
|             e = strchr(p, ','); |  | ||||||
|             l = !e ? strlen(p) : (size_t) (e - p); |  | ||||||
|  |  | ||||||
|             for (c = soundhw; c->name; ++c) { |  | ||||||
|                 if (!strncmp(c->name, p, l) && !c->name[l]) { |  | ||||||
|                     c->enabled = 1; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (!c->name) { |  | ||||||
|                 if (l > 80) { |  | ||||||
|                     fprintf(stderr, |  | ||||||
|                             "Unknown sound card name (too big to show)\n"); |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     fprintf(stderr, "Unknown sound card name `%.*s'\n", |  | ||||||
|                             (int) l, p); |  | ||||||
|                 } |  | ||||||
|                 bad_card = 1; |  | ||||||
|             } |  | ||||||
|             p += l + (e != NULL); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (bad_card) { |  | ||||||
|             goto show_valid_cards; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) |  | ||||||
| { |  | ||||||
|     struct soundhw *c; |  | ||||||
|  |  | ||||||
|     for (c = soundhw; c->name; ++c) { |  | ||||||
|         if (c->enabled) { |  | ||||||
|             if (c->isa) { |  | ||||||
|                 if (isa_pic) { |  | ||||||
|                     c->init.init_isa(isa_pic); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 if (pci_bus) { |  | ||||||
|                     c->init.init_pci(pci_bus); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| void select_soundhw(const char *optarg) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| int qemu_uuid_parse(const char *str, uint8_t *uuid) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     if (strlen(str) != 36) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], |  | ||||||
|                  &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], |  | ||||||
|                  &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], |  | ||||||
|                  &uuid[15]); |  | ||||||
|  |  | ||||||
|     if (ret != 16) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| #ifdef TARGET_I386 |  | ||||||
|     smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); |  | ||||||
| #endif |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void do_acpitable_option(const char *optarg) |  | ||||||
| { |  | ||||||
| #ifdef TARGET_I386 |  | ||||||
|     if (acpi_table_add(optarg) < 0) { |  | ||||||
|         fprintf(stderr, "Wrong acpi table provided\n"); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void do_smbios_option(const char *optarg) |  | ||||||
| { |  | ||||||
| #ifdef TARGET_I386 |  | ||||||
|     if (smbios_entry_add(optarg) < 0) { |  | ||||||
|         fprintf(stderr, "Wrong smbios provided\n"); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cpudef_init(void) |  | ||||||
| { |  | ||||||
| #if defined(cpudef_setup) |  | ||||||
|     cpudef_setup(); /* parse cpu definitions in target config file */ |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int audio_available(void) |  | ||||||
| { |  | ||||||
| #ifdef HAS_AUDIO |  | ||||||
|     return 1; |  | ||||||
| #else |  | ||||||
|     return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_available(void) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_KVM |  | ||||||
|     return 1; |  | ||||||
| #else |  | ||||||
|     return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int xen_available(void) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_XEN |  | ||||||
|     return 1; |  | ||||||
| #else |  | ||||||
|     return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
							
								
								
									
										34
									
								
								arch_init.h
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								arch_init.h
									
									
									
									
									
								
							| @@ -1,34 +0,0 @@ | |||||||
| #ifndef QEMU_ARCH_INIT_H |  | ||||||
| #define QEMU_ARCH_INIT_H |  | ||||||
|  |  | ||||||
| extern const char arch_config_name[]; |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     QEMU_ARCH_ALL = -1, |  | ||||||
|     QEMU_ARCH_ALPHA = 1, |  | ||||||
|     QEMU_ARCH_ARM = 2, |  | ||||||
|     QEMU_ARCH_CRIS = 4, |  | ||||||
|     QEMU_ARCH_I386 = 8, |  | ||||||
|     QEMU_ARCH_M68K = 16, |  | ||||||
|     QEMU_ARCH_MICROBLAZE = 32, |  | ||||||
|     QEMU_ARCH_MIPS = 64, |  | ||||||
|     QEMU_ARCH_PPC = 128, |  | ||||||
|     QEMU_ARCH_S390X = 256, |  | ||||||
|     QEMU_ARCH_SH4 = 512, |  | ||||||
|     QEMU_ARCH_SPARC = 1024, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| extern const uint32_t arch_type; |  | ||||||
|  |  | ||||||
| void select_soundhw(const char *optarg); |  | ||||||
| int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque); |  | ||||||
| int ram_load(QEMUFile *f, void *opaque, int version_id); |  | ||||||
| void do_acpitable_option(const char *optarg); |  | ||||||
| void do_smbios_option(const char *optarg); |  | ||||||
| void cpudef_init(void); |  | ||||||
| int audio_available(void); |  | ||||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus); |  | ||||||
| int kvm_available(void); |  | ||||||
| int xen_available(void); |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
							
								
								
									
										55
									
								
								arm-dis.c
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								arm-dis.c
									
									
									
									
									
								
							| @@ -60,8 +60,10 @@ | |||||||
| #define FPU_VFP_EXT_V3	 0 | #define FPU_VFP_EXT_V3	 0 | ||||||
| #define FPU_NEON_EXT_V1	 0 | #define FPU_NEON_EXT_V1	 0 | ||||||
|  |  | ||||||
|  | int floatformat_ieee_single_little; | ||||||
| /* Assume host uses ieee float.  */ | /* Assume host uses ieee float.  */ | ||||||
| static void floatformat_to_double (unsigned char *data, double *dest) | static void floatformat_to_double (int *ignored, unsigned char *data, | ||||||
|  |                                    double *dest) | ||||||
| { | { | ||||||
|     union { |     union { | ||||||
|         uint32_t i; |         uint32_t i; | ||||||
| @@ -1587,7 +1589,7 @@ arm_decode_bitfield (const char *ptr, unsigned long insn, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| arm_decode_shift (long given, fprintf_function func, void *stream, | arm_decode_shift (long given, fprintf_ftype func, void *stream, | ||||||
| 		  int print_shift) | 		  int print_shift) | ||||||
| { | { | ||||||
|   func (stream, "%s", arm_regnames[given & 0xf]); |   func (stream, "%s", arm_regnames[given & 0xf]); | ||||||
| @@ -1633,7 +1635,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given, | |||||||
| { | { | ||||||
|   const struct opcode32 *insn; |   const struct opcode32 *insn; | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|   unsigned long mask; |   unsigned long mask; | ||||||
|   unsigned long value; |   unsigned long value; | ||||||
|   int cond; |   int cond; | ||||||
| @@ -2127,7 +2129,7 @@ static void | |||||||
| print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) | print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) | ||||||
| { | { | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|  |  | ||||||
|   if (((given & 0x000f0000) == 0x000f0000) |   if (((given & 0x000f0000) == 0x000f0000) | ||||||
|       && ((given & 0x02000000) == 0)) |       && ((given & 0x02000000) == 0)) | ||||||
| @@ -2222,7 +2224,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | |||||||
| { | { | ||||||
|   const struct opcode32 *insn; |   const struct opcode32 *insn; | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|  |  | ||||||
|   if (thumb) |   if (thumb) | ||||||
|     { |     { | ||||||
| @@ -2515,6 +2517,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | |||||||
| 			  { | 			  { | ||||||
| 			    func (stream, "<illegal constant %.8x:%x:%x>", | 			    func (stream, "<illegal constant %.8x:%x:%x>", | ||||||
|                                   bits, cmode, op); |                                   bits, cmode, op); | ||||||
|  |                             size = 32; | ||||||
| 			    break; | 			    break; | ||||||
| 			  } | 			  } | ||||||
|                         switch (size) |                         switch (size) | ||||||
| @@ -2540,7 +2543,9 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | |||||||
|                                 valbytes[2] = (value >> 16) & 0xff; |                                 valbytes[2] = (value >> 16) & 0xff; | ||||||
|                                 valbytes[3] = (value >> 24) & 0xff; |                                 valbytes[3] = (value >> 24) & 0xff; | ||||||
|  |  | ||||||
|                                 floatformat_to_double (valbytes, &fvalue); |                                 floatformat_to_double | ||||||
|  |                                   (&floatformat_ieee_single_little, valbytes, | ||||||
|  |                                   &fvalue); | ||||||
|  |  | ||||||
|                                 func (stream, "#%.7g\t; 0x%.8lx", fvalue, |                                 func (stream, "#%.7g\t; 0x%.8lx", fvalue, | ||||||
|                                       value); |                                       value); | ||||||
| @@ -2676,7 +2681,7 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| { | { | ||||||
|   const struct opcode32 *insn; |   const struct opcode32 *insn; | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|  |  | ||||||
|   if (print_insn_coprocessor (pc, info, given, false)) |   if (print_insn_coprocessor (pc, info, given, false)) | ||||||
|     return; |     return; | ||||||
| @@ -3036,7 +3041,7 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| { | { | ||||||
|   const struct opcode16 *insn; |   const struct opcode16 *insn; | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|  |  | ||||||
|   for (insn = thumb_opcodes; insn->assembler; insn++) |   for (insn = thumb_opcodes; insn->assembler; insn++) | ||||||
|     if ((given & insn->mask) == insn->value) |     if ((given & insn->mask) == insn->value) | ||||||
| @@ -3148,14 +3153,14 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| 		      if (started) | 		      if (started) | ||||||
| 			func (stream, ", "); | 			func (stream, ", "); | ||||||
| 		      started = 1; | 		      started = 1; | ||||||
| 		      func (stream, "%s", arm_regnames[14] /* "lr" */); | 		      func (stream, arm_regnames[14] /* "lr" */); | ||||||
| 		    } | 		    } | ||||||
|  |  | ||||||
| 		  if (domaskpc) | 		  if (domaskpc) | ||||||
| 		    { | 		    { | ||||||
| 		      if (started) | 		      if (started) | ||||||
| 			func (stream, ", "); | 			func (stream, ", "); | ||||||
| 		      func (stream, "%s", arm_regnames[15] /* "pc" */); | 		      func (stream, arm_regnames[15] /* "pc" */); | ||||||
| 		    } | 		    } | ||||||
|  |  | ||||||
| 		  func (stream, "}"); | 		  func (stream, "}"); | ||||||
| @@ -3312,7 +3317,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| { | { | ||||||
|   const struct opcode32 *insn; |   const struct opcode32 *insn; | ||||||
|   void *stream = info->stream; |   void *stream = info->stream; | ||||||
|   fprintf_function func = info->fprintf_func; |   fprintf_ftype func = info->fprintf_func; | ||||||
|  |  | ||||||
|   if (print_insn_coprocessor (pc, info, given, true)) |   if (print_insn_coprocessor (pc, info, given, true)) | ||||||
|     return; |     return; | ||||||
| @@ -3698,7 +3703,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| 		  } | 		  } | ||||||
| 		else | 		else | ||||||
| 		  { | 		  { | ||||||
| 		    func (stream, "%s", psr_name (given & 0xff)); | 		    func (stream, psr_name (given & 0xff)); | ||||||
| 		  } | 		  } | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
| @@ -3706,7 +3711,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | |||||||
| 		if ((given & 0xff) == 0) | 		if ((given & 0xff) == 0) | ||||||
| 		  func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C'); | 		  func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C'); | ||||||
| 		else | 		else | ||||||
| 		  func (stream, "%s", psr_name (given & 0xff)); | 		  func (stream, psr_name (given & 0xff)); | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
| 	      case '0': case '1': case '2': case '3': case '4': | 	      case '0': case '1': case '2': case '3': case '4': | ||||||
| @@ -4101,30 +4106,6 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) | |||||||
|        addresses, since the addend is not currently pc-relative.  */ |        addresses, since the addend is not currently pc-relative.  */ | ||||||
|     pc = 0; |     pc = 0; | ||||||
|  |  | ||||||
|   /* We include the hexdump of the instruction. The format here |  | ||||||
|      matches that used by objdump and the ARM ARM (in particular, |  | ||||||
|      32 bit Thumb instructions are displayed as pairs of halfwords, |  | ||||||
|      not as a single word.)  */ |  | ||||||
|   if (is_thumb) |  | ||||||
|     { |  | ||||||
|       if (size == 2) |  | ||||||
| 	{ |  | ||||||
| 	  info->fprintf_func(info->stream, "%04lx       ", |  | ||||||
| 			     ((unsigned long)given) & 0xffff); |  | ||||||
| 	} |  | ||||||
|       else |  | ||||||
| 	{ |  | ||||||
| 	  info->fprintf_func(info->stream, "%04lx %04lx  ", |  | ||||||
| 			     (((unsigned long)given) >> 16) & 0xffff, |  | ||||||
| 			     ((unsigned long)given) & 0xffff); |  | ||||||
| 	} |  | ||||||
|     } |  | ||||||
|   else |  | ||||||
|     { |  | ||||||
|       info->fprintf_func(info->stream, "%08lx      ", |  | ||||||
| 			 ((unsigned long)given) & 0xffffffff); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|   printer (pc, info, given); |   printer (pc, info, given); | ||||||
|  |  | ||||||
|   if (is_thumb) |   if (is_thumb) | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								arm-semi.c
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								arm-semi.c
									
									
									
									
									
								
							| @@ -373,64 +373,45 @@ uint32_t do_arm_semihosting(CPUState *env) | |||||||
| #ifdef CONFIG_USER_ONLY | #ifdef CONFIG_USER_ONLY | ||||||
|         /* Build a commandline from the original argv.  */ |         /* Build a commandline from the original argv.  */ | ||||||
|         { |         { | ||||||
|             char *arm_cmdline_buffer; |             char **arg = ts->info->host_argv; | ||||||
|             const char *host_cmdline_buffer; |             int len = ARG(1); | ||||||
|  |             /* lock the buffer on the ARM side */ | ||||||
|  |             char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0); | ||||||
|  |  | ||||||
|             unsigned int i; |             if (!cmdline_buffer) | ||||||
|             unsigned int arm_cmdline_len = ARG(1); |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|             unsigned int host_cmdline_len = |                 return (uint32_t)-1; | ||||||
|                 ts->info->arg_end-ts->info->arg_start; |  | ||||||
|  |  | ||||||
|             if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { |             s = cmdline_buffer; | ||||||
|                 return -1; /* not enough space to store command line */ |             while (*arg && len > 2) { | ||||||
|             } |                 int n = strlen(*arg); | ||||||
|  |  | ||||||
|             if (!host_cmdline_len) { |                 if (s != cmdline_buffer) { | ||||||
|                 /* We special-case the "empty command line" case (argc==0). |                     *(s++) = ' '; | ||||||
|                    Just provide the terminating 0. */ |                     len--; | ||||||
|                 arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0); |  | ||||||
|                 arm_cmdline_buffer[0] = 0; |  | ||||||
|                 unlock_user(arm_cmdline_buffer, ARG(0), 1); |  | ||||||
|  |  | ||||||
|                 /* Adjust the commandline length argument. */ |  | ||||||
|                 SET_ARG(1, 0); |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             /* lock the buffers on the ARM side */ |  | ||||||
|             arm_cmdline_buffer = |  | ||||||
|                 lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0); |  | ||||||
|             host_cmdline_buffer = |  | ||||||
|                 lock_user(VERIFY_READ, ts->info->arg_start, |  | ||||||
|                                        host_cmdline_len, 1); |  | ||||||
|  |  | ||||||
|             if (arm_cmdline_buffer && host_cmdline_buffer) |  | ||||||
|             { |  | ||||||
|                 /* the last argument is zero-terminated; |  | ||||||
|                    no need for additional termination */ |  | ||||||
|                 memcpy(arm_cmdline_buffer, host_cmdline_buffer, |  | ||||||
|                        host_cmdline_len); |  | ||||||
|  |  | ||||||
|                 /* separate arguments by white spaces */ |  | ||||||
|                 for (i = 0; i < host_cmdline_len-1; i++) { |  | ||||||
|                     if (arm_cmdline_buffer[i] == 0) { |  | ||||||
|                         arm_cmdline_buffer[i] = ' '; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|  |                 if (n >= len) | ||||||
|                 /* Adjust the commandline length argument. */ |                     n = len - 1; | ||||||
|                 SET_ARG(1, host_cmdline_len-1); |                 memcpy(s, *arg, n); | ||||||
|  |                 s += n; | ||||||
|  |                 len -= n; | ||||||
|  |                 arg++; | ||||||
|             } |             } | ||||||
|  |             /* Null terminate the string.  */ | ||||||
|  |             *s = 0; | ||||||
|  |             len = s - cmdline_buffer; | ||||||
|  |  | ||||||
|             /* Unlock the buffers on the ARM side.  */ |             /* Unlock the buffer on the ARM side.  */ | ||||||
|             unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len); |             unlock_user(cmdline_buffer, ARG(0), len); | ||||||
|             unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0); |  | ||||||
|  |  | ||||||
|             /* Return success if we could return a commandline.  */ |             /* Adjust the commandline length argument.  */ | ||||||
|             return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; |             SET_ARG(1, len); | ||||||
|  |  | ||||||
|  |             /* Return success if commandline fit into buffer.  */ | ||||||
|  |             return *arg ? -1 : 0; | ||||||
|         } |         } | ||||||
| #else | #else | ||||||
|         return -1; |       return -1; | ||||||
| #endif | #endif | ||||||
|     case SYS_HEAPINFO: |     case SYS_HEAPINFO: | ||||||
|         { |         { | ||||||
| @@ -478,7 +459,6 @@ uint32_t do_arm_semihosting(CPUState *env) | |||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|     case SYS_EXIT: |     case SYS_EXIT: | ||||||
|         gdb_exit(env, 0); |  | ||||||
|         exit(0); |         exit(0); | ||||||
|     default: |     default: | ||||||
|         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); |         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); | ||||||
|   | |||||||
| @@ -318,7 +318,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) | static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUD_FMT_S8: |     case AUD_FMT_S8: | ||||||
| @@ -328,36 +328,16 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) | |||||||
|         return SND_PCM_FORMAT_U8; |         return SND_PCM_FORMAT_U8; | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |         return SND_PCM_FORMAT_S16_LE; | ||||||
|             return SND_PCM_FORMAT_S16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return SND_PCM_FORMAT_S16_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |         return SND_PCM_FORMAT_U16_LE; | ||||||
|             return SND_PCM_FORMAT_U16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return SND_PCM_FORMAT_U16_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S32: |     case AUD_FMT_S32: | ||||||
|         if (endianness) { |         return SND_PCM_FORMAT_S32_LE; | ||||||
|             return SND_PCM_FORMAT_S32_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return SND_PCM_FORMAT_S32_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U32: |     case AUD_FMT_U32: | ||||||
|         if (endianness) { |         return SND_PCM_FORMAT_U32_LE; | ||||||
|             return SND_PCM_FORMAT_U32_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return SND_PCM_FORMAT_U32_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); |         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||||
| @@ -431,11 +411,10 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void alsa_dump_info (struct alsa_params_req *req, | static void alsa_dump_info (struct alsa_params_req *req, | ||||||
|                             struct alsa_params_obt *obt, |                             struct alsa_params_obt *obt) | ||||||
|                             snd_pcm_format_t obtfmt) |  | ||||||
| { | { | ||||||
|     dolog ("parameter | requested value | obtained value\n"); |     dolog ("parameter | requested value | obtained value\n"); | ||||||
|     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt); |     dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt); | ||||||
|     dolog ("channels  |      %10d |     %10d\n", |     dolog ("channels  |      %10d |     %10d\n", | ||||||
|            req->nchannels, obt->nchannels); |            req->nchannels, obt->nchannels); | ||||||
|     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq); |     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq); | ||||||
| @@ -687,15 +666,15 @@ static int alsa_open (int in, struct alsa_params_req *req, | |||||||
|     *handlep = handle; |     *handlep = handle; | ||||||
|  |  | ||||||
|     if (conf.verbose && |     if (conf.verbose && | ||||||
|         (obtfmt != req->fmt || |         (obt->fmt != req->fmt || | ||||||
|          obt->nchannels != req->nchannels || |          obt->nchannels != req->nchannels || | ||||||
|          obt->freq != req->freq)) { |          obt->freq != req->freq)) { | ||||||
|         dolog ("Audio parameters for %s\n", typ); |         dolog ("Audio parameters for %s\n", typ); | ||||||
|         alsa_dump_info (req, obt, obtfmt); |         alsa_dump_info (req, obt); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
|     alsa_dump_info (req, obt, obtfmt); |     alsa_dump_info (req, obt); | ||||||
| #endif | #endif | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -829,7 +808,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |     req.fmt = aud_to_alsafmt (as->fmt); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.period_size = conf.period_size_out; |     req.period_size = conf.period_size_out; | ||||||
| @@ -863,15 +842,11 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define VOICE_CTL_PAUSE 0 | static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) | ||||||
| #define VOICE_CTL_PREPARE 1 |  | ||||||
| #define VOICE_CTL_START 2 |  | ||||||
|  |  | ||||||
| static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) |  | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|  |  | ||||||
|     if (ctl == VOICE_CTL_PAUSE) { |     if (pause) { | ||||||
|         err = snd_pcm_drop (handle); |         err = snd_pcm_drop (handle); | ||||||
|         if (err < 0) { |         if (err < 0) { | ||||||
|             alsa_logerr (err, "Could not stop %s\n", typ); |             alsa_logerr (err, "Could not stop %s\n", typ); | ||||||
| @@ -884,13 +859,6 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) | |||||||
|             alsa_logerr (err, "Could not prepare handle for %s\n", typ); |             alsa_logerr (err, "Could not prepare handle for %s\n", typ); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|         if (ctl == VOICE_CTL_START) { |  | ||||||
|             err = snd_pcm_start(handle); |  | ||||||
|             if (err < 0) { |  | ||||||
|                 alsa_logerr (err, "Could not start handle for %s\n", typ); |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -915,16 +883,12 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|                 poll_mode = 0; |                 poll_mode = 0; | ||||||
|             } |             } | ||||||
|             hw->poll_mode = poll_mode; |             hw->poll_mode = poll_mode; | ||||||
|             return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); |             return alsa_voice_ctl (alsa->handle, "playback", 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case VOICE_DISABLE: |     case VOICE_DISABLE: | ||||||
|         ldebug ("disabling voice\n"); |         ldebug ("disabling voice\n"); | ||||||
|         if (hw->poll_mode) { |         return alsa_voice_ctl (alsa->handle, "playback", 1); | ||||||
|             hw->poll_mode = 0; |  | ||||||
|             alsa_fini_poll (&alsa->pollhlp); |  | ||||||
|         } |  | ||||||
|         return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| @@ -938,7 +902,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) | |||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |     req.fmt = aud_to_alsafmt (as->fmt); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.period_size = conf.period_size_in; |     req.period_size = conf.period_size_in; | ||||||
| @@ -1097,7 +1061,7 @@ static int alsa_run_in (HWVoiceIn *hw) | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             hw->conv (dst, src, nread); |             hw->conv (dst, src, nread, &nominal_volume); | ||||||
|  |  | ||||||
|             src = advance (src, nread << hwshift); |             src = advance (src, nread << hwshift); | ||||||
|             dst += nread; |             dst += nread; | ||||||
| @@ -1137,7 +1101,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|             } |             } | ||||||
|             hw->poll_mode = poll_mode; |             hw->poll_mode = poll_mode; | ||||||
|  |  | ||||||
|             return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); |             return alsa_voice_ctl (alsa->handle, "capture", 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case VOICE_DISABLE: |     case VOICE_DISABLE: | ||||||
| @@ -1146,7 +1110,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|             hw->poll_mode = 0; |             hw->poll_mode = 0; | ||||||
|             alsa_fini_poll (&alsa->pollhlp); |             alsa_fini_poll (&alsa->pollhlp); | ||||||
|         } |         } | ||||||
|         return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); |         return alsa_voice_ctl (alsa->handle, "capture", 1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
|   | |||||||
| @@ -44,9 +44,6 @@ | |||||||
|     that we generate the list. |     that we generate the list. | ||||||
| */ | */ | ||||||
| static struct audio_driver *drvtab[] = { | static struct audio_driver *drvtab[] = { | ||||||
| #ifdef CONFIG_SPICE |  | ||||||
|     &spice_audio_driver, |  | ||||||
| #endif |  | ||||||
|     CONFIG_AUDIO_DRIVERS |     CONFIG_AUDIO_DRIVERS | ||||||
|     &no_audio_driver, |     &no_audio_driver, | ||||||
|     &wav_audio_driver |     &wav_audio_driver | ||||||
| @@ -104,7 +101,7 @@ static struct { | |||||||
|  |  | ||||||
| static AudioState glob_audio_state; | static AudioState glob_audio_state; | ||||||
|  |  | ||||||
| const struct mixeng_volume nominal_volume = { | struct mixeng_volume nominal_volume = { | ||||||
|     .mute = 0, |     .mute = 0, | ||||||
| #ifdef FLOAT_MIXENG | #ifdef FLOAT_MIXENG | ||||||
|     .r = 1.0, |     .r = 1.0, | ||||||
| @@ -118,9 +115,6 @@ const struct mixeng_volume nominal_volume = { | |||||||
| #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED | #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED | ||||||
| #error No its not | #error No its not | ||||||
| #else | #else | ||||||
| static void audio_print_options (const char *prefix, |  | ||||||
|                                  struct audio_option *opt); |  | ||||||
|  |  | ||||||
| int audio_bug (const char *funcname, int cond) | int audio_bug (const char *funcname, int cond) | ||||||
| { | { | ||||||
|     if (cond) { |     if (cond) { | ||||||
| @@ -128,16 +122,10 @@ int audio_bug (const char *funcname, int cond) | |||||||
|  |  | ||||||
|         AUD_log (NULL, "A bug was just triggered in %s\n", funcname); |         AUD_log (NULL, "A bug was just triggered in %s\n", funcname); | ||||||
|         if (!shown) { |         if (!shown) { | ||||||
|             struct audio_driver *d; |  | ||||||
|  |  | ||||||
|             shown = 1; |             shown = 1; | ||||||
|             AUD_log (NULL, "Save all your work and restart without audio\n"); |             AUD_log (NULL, "Save all your work and restart without audio\n"); | ||||||
|             AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); |             AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); | ||||||
|             AUD_log (NULL, "I am sorry\n"); |             AUD_log (NULL, "I am sorry\n"); | ||||||
|             d = glob_audio_state.drv; |  | ||||||
|             if (d) { |  | ||||||
|                 audio_print_options (d->name, d->options); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         AUD_log (NULL, "Context:\n"); |         AUD_log (NULL, "Context:\n"); | ||||||
|  |  | ||||||
| @@ -333,10 +321,10 @@ void AUD_vlog (const char *cap, const char *fmt, va_list ap) | |||||||
| { | { | ||||||
|     if (conf.log_to_monitor) { |     if (conf.log_to_monitor) { | ||||||
|         if (cap) { |         if (cap) { | ||||||
|             monitor_printf(default_mon, "%s: ", cap); |             monitor_printf(cur_mon, "%s: ", cap); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         monitor_vprintf(default_mon, fmt, ap); |         monitor_vprintf(cur_mon, fmt, ap); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         if (cap) { |         if (cap) { | ||||||
| @@ -702,11 +690,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | |||||||
| /* | /* | ||||||
|  * Capture |  * Capture | ||||||
|  */ |  */ | ||||||
| static void noop_conv (struct st_sample *dst, const void *src, int samples) | static void noop_conv (struct st_sample *dst, const void *src, | ||||||
|  |                        int samples, struct mixeng_volume *vol) | ||||||
| { | { | ||||||
|     (void) src; |     (void) src; | ||||||
|     (void) dst; |     (void) dst; | ||||||
|     (void) samples; |     (void) samples; | ||||||
|  |     (void) vol; | ||||||
| } | } | ||||||
|  |  | ||||||
| static CaptureVoiceOut *audio_pcm_capture_find_specific ( | static CaptureVoiceOut *audio_pcm_capture_find_specific ( | ||||||
| @@ -954,8 +944,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) | |||||||
|         total += isamp; |         total += isamp; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     mixeng_volume (sw->buf, ret, &sw->vol); |  | ||||||
|  |  | ||||||
|     sw->clip (buf, sw->buf, ret); |     sw->clip (buf, sw->buf, ret); | ||||||
|     sw->total_hw_samples_acquired += total; |     sw->total_hw_samples_acquired += total; | ||||||
|     return ret << sw->info.shift; |     return ret << sw->info.shift; | ||||||
| @@ -1037,8 +1025,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | |||||||
|     swlim = ((int64_t) dead << 32) / sw->ratio; |     swlim = ((int64_t) dead << 32) / sw->ratio; | ||||||
|     swlim = audio_MIN (swlim, samples); |     swlim = audio_MIN (swlim, samples); | ||||||
|     if (swlim) { |     if (swlim) { | ||||||
|         sw->conv (sw->buf, buf, swlim); |         sw->conv (sw->buf, buf, swlim, &sw->vol); | ||||||
|         mixeng_volume (sw->buf, swlim, &sw->vol); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (swlim) { |     while (swlim) { | ||||||
| @@ -1097,6 +1084,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | |||||||
| /* | /* | ||||||
|  * Timer |  * Timer | ||||||
|  */ |  */ | ||||||
|  | static void audio_timer (void *opaque) | ||||||
|  | { | ||||||
|  |     AudioState *s = opaque; | ||||||
|  |  | ||||||
|  |     audio_run ("timer"); | ||||||
|  |     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static int audio_is_timer_needed (void) | static int audio_is_timer_needed (void) | ||||||
| { | { | ||||||
|     HWVoiceIn *hwi = NULL; |     HWVoiceIn *hwi = NULL; | ||||||
| @@ -1111,8 +1107,10 @@ static int audio_is_timer_needed (void) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void audio_reset_timer (AudioState *s) | static void audio_reset_timer (void) | ||||||
| { | { | ||||||
|  |     AudioState *s = &glob_audio_state; | ||||||
|  |  | ||||||
|     if (audio_is_timer_needed ()) { |     if (audio_is_timer_needed ()) { | ||||||
|         qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); |         qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | ||||||
|     } |     } | ||||||
| @@ -1121,12 +1119,6 @@ static void audio_reset_timer (AudioState *s) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void audio_timer (void *opaque) |  | ||||||
| { |  | ||||||
|     audio_run ("timer"); |  | ||||||
|     audio_reset_timer (opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Public API |  * Public API | ||||||
|  */ |  */ | ||||||
| @@ -1191,7 +1183,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | |||||||
|                 hw->enabled = 1; |                 hw->enabled = 1; | ||||||
|                 if (s->vm_running) { |                 if (s->vm_running) { | ||||||
|                     hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); |                     hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); | ||||||
|                     audio_reset_timer (s); |                     audio_reset_timer (); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1236,7 +1228,6 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) | |||||||
|                 hw->enabled = 1; |                 hw->enabled = 1; | ||||||
|                 if (s->vm_running) { |                 if (s->vm_running) { | ||||||
|                     hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); |                     hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); | ||||||
|                     audio_reset_timer (s); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             sw->total_hw_samples_acquired = hw->total_samples_captured; |             sw->total_hw_samples_acquired = hw->total_samples_captured; | ||||||
| @@ -1758,7 +1749,7 @@ static void audio_vm_change_state_handler (void *opaque, int running, | |||||||
|     while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { |     while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { | ||||||
|         hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); |         hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); | ||||||
|     } |     } | ||||||
|     audio_reset_timer (s); |     audio_reset_timer (); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void audio_atexit (void) | static void audio_atexit (void) | ||||||
| @@ -1901,7 +1892,7 @@ static void audio_init (void) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     QLIST_INIT (&s->card_head); |     QLIST_INIT (&s->card_head); | ||||||
|     vmstate_register (NULL, 0, &vmstate_audio, s); |     vmstate_register (0, &vmstate_audio, s); | ||||||
| } | } | ||||||
|  |  | ||||||
| void AUD_register_card (const char *name, QEMUSoundCard *card) | void AUD_register_card (const char *name, QEMUSoundCard *card) | ||||||
|   | |||||||
| @@ -86,8 +86,12 @@ typedef struct QEMUAudioTimeStamp { | |||||||
|     uint64_t old_ts; |     uint64_t old_ts; | ||||||
| } QEMUAudioTimeStamp; | } QEMUAudioTimeStamp; | ||||||
|  |  | ||||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); | void AUD_vlog (const char *cap, const char *fmt, va_list ap); | ||||||
| void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | void AUD_log (const char *cap, const char *fmt, ...) | ||||||
|  | #ifdef __GNUC__ | ||||||
|  |     __attribute__ ((__format__ (__printf__, 2, 3))) | ||||||
|  | #endif | ||||||
|  |     ; | ||||||
|  |  | ||||||
| void AUD_help (void); | void AUD_help (void); | ||||||
| void AUD_register_card (const char *name, QEMUSoundCard *card); | void AUD_register_card (const char *name, QEMUSoundCard *card); | ||||||
|   | |||||||
| @@ -209,9 +209,8 @@ extern struct audio_driver coreaudio_audio_driver; | |||||||
| extern struct audio_driver dsound_audio_driver; | extern struct audio_driver dsound_audio_driver; | ||||||
| extern struct audio_driver esd_audio_driver; | extern struct audio_driver esd_audio_driver; | ||||||
| extern struct audio_driver pa_audio_driver; | extern struct audio_driver pa_audio_driver; | ||||||
| extern struct audio_driver spice_audio_driver; |  | ||||||
| extern struct audio_driver winwave_audio_driver; | extern struct audio_driver winwave_audio_driver; | ||||||
| extern const struct mixeng_volume nominal_volume; | extern struct mixeng_volume nominal_volume; | ||||||
|  |  | ||||||
| void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | ||||||
| void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | ||||||
| @@ -237,6 +236,14 @@ static inline int audio_ring_dist (int dst, int src, int len) | |||||||
|     return (dst >= src) ? (dst - src) : (len - src + dst); |     return (dst >= src) ? (dst - src) : (len - src + dst); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #if defined __GNUC__ | ||||||
|  | #define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) | ||||||
|  | #define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) | ||||||
|  | #else | ||||||
|  | #define GCC_ATTR /**/ | ||||||
|  | #define GCC_FMT_ATTR(n, m) | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static void GCC_ATTR dolog (const char *fmt, ...) | static void GCC_ATTR dolog (const char *fmt, ...) | ||||||
| { | { | ||||||
|     va_list ap; |     va_list ap; | ||||||
|   | |||||||
| @@ -6,10 +6,7 @@ | |||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| #include "audio_pt_int.h" | #include "audio_pt_int.h" | ||||||
|  |  | ||||||
| #include <signal.h> | static void logerr (struct audio_pt *pt, int err, const char *fmt, ...) | ||||||
|  |  | ||||||
| static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err, |  | ||||||
|                                        const char *fmt, ...) |  | ||||||
| { | { | ||||||
|     va_list ap; |     va_list ap; | ||||||
|  |  | ||||||
| @@ -26,16 +23,9 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), | |||||||
| { | { | ||||||
|     int err, err2; |     int err, err2; | ||||||
|     const char *efunc; |     const char *efunc; | ||||||
|     sigset_t set, old_set; |  | ||||||
|  |  | ||||||
|     p->drv = drv; |     p->drv = drv; | ||||||
|  |  | ||||||
|     err = sigfillset (&set); |  | ||||||
|     if (err) { |  | ||||||
|         logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = pthread_mutex_init (&p->mutex, NULL); |     err = pthread_mutex_init (&p->mutex, NULL); | ||||||
|     if (err) { |     if (err) { | ||||||
|         efunc = "pthread_mutex_init"; |         efunc = "pthread_mutex_init"; | ||||||
| @@ -48,23 +38,7 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), | |||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); |  | ||||||
|     if (err) { |  | ||||||
|         efunc = "pthread_sigmask"; |  | ||||||
|         goto err2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = pthread_create (&p->thread, NULL, func, opaque); |     err = pthread_create (&p->thread, NULL, func, opaque); | ||||||
|  |  | ||||||
|     err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL); |  | ||||||
|     if (err2) { |  | ||||||
|         logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed", |  | ||||||
|                 cap, AUDIO_FUNC); |  | ||||||
|         /* We have failed to restore original signal mask, all bets are off, |  | ||||||
|            so terminate the process */ |  | ||||||
|         exit (EXIT_FAILURE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (err) { |     if (err) { | ||||||
|         efunc = "pthread_create"; |         efunc = "pthread_create"; | ||||||
|         goto err2; |         goto err2; | ||||||
|   | |||||||
| @@ -108,7 +108,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) | |||||||
| { | { | ||||||
|     int samples; |     int samples; | ||||||
|  |  | ||||||
|  | #ifdef DAC | ||||||
|  |     samples = sw->hw->samples; | ||||||
|  | #else | ||||||
|     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; |     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample)); |     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample)); | ||||||
|     if (!sw->buf) { |     if (!sw->buf) { | ||||||
| @@ -537,7 +541,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | |||||||
|  |  | ||||||
|     cur_ts = sw->hw->ts_helper; |     cur_ts = sw->hw->ts_helper; | ||||||
|     old_ts = ts->old_ts; |     old_ts = ts->old_ts; | ||||||
|     /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ |     /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ | ||||||
|  |  | ||||||
|     if (cur_ts >= old_ts) { |     if (cur_ts >= old_ts) { | ||||||
|         delta = cur_ts - old_ts; |         delta = cur_ts - old_ts; | ||||||
|   | |||||||
| @@ -831,11 +831,11 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|     decr = len1 + len2; |     decr = len1 + len2; | ||||||
|  |  | ||||||
|     if (p1 && len1) { |     if (p1 && len1) { | ||||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); |         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (p2 && len2) { |     if (p2 && len2) { | ||||||
|         hw->conv (hw->conv_buf, p2, len2); |         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     dsound_unlock_in (dscb, p1, p2, blen1, blen2); |     dsound_unlock_in (dscb, p1, p2, blen1, blen2); | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ | |||||||
| #include <esd.h> | #include <esd.h> | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  | #include <signal.h> | ||||||
|  |  | ||||||
| #define AUDIO_CAP "esd" | #define AUDIO_CAP "esd" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| @@ -189,6 +190,10 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     ESDVoiceOut *esd = (ESDVoiceOut *) hw; |     ESDVoiceOut *esd = (ESDVoiceOut *) hw; | ||||||
|     struct audsettings obt_as = *as; |     struct audsettings obt_as = *as; | ||||||
|     int esdfmt = ESD_STREAM | ESD_PLAY; |     int esdfmt = ESD_STREAM | ESD_PLAY; | ||||||
|  |     int err; | ||||||
|  |     sigset_t set, old_set; | ||||||
|  |  | ||||||
|  |     sigfillset (&set); | ||||||
|  |  | ||||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; |     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||||
|     switch (as->fmt) { |     switch (as->fmt) { | ||||||
| @@ -226,25 +231,43 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); |     esd->fd = -1; | ||||||
|     if (esd->fd < 0) { |     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); | ||||||
|         qesd_logerr (errno, "esd_play_stream failed\n"); |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask failed\n"); | ||||||
|         goto fail1; |         goto fail1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { |     esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); | ||||||
|  |     if (esd->fd < 0) { | ||||||
|  |         qesd_logerr (errno, "esd_play_stream failed\n"); | ||||||
|         goto fail2; |         goto fail2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||||
|  |         goto fail3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||||
|  |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
|  fail2: |  fail3: | ||||||
|     if (close (esd->fd)) { |     if (close (esd->fd)) { | ||||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", |         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||||
|                      AUDIO_FUNC, esd->fd); |                      AUDIO_FUNC, esd->fd); | ||||||
|     } |     } | ||||||
|     esd->fd = -1; |     esd->fd = -1; | ||||||
|  |  | ||||||
|  |  fail2: | ||||||
|  |     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||||
|  |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  fail1: |  fail1: | ||||||
|     qemu_free (esd->pcm_buf); |     qemu_free (esd->pcm_buf); | ||||||
|     esd->pcm_buf = NULL; |     esd->pcm_buf = NULL; | ||||||
| @@ -346,7 +369,8 @@ static void *qesd_thread_in (void *arg) | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); |             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, | ||||||
|  |                       &nominal_volume); | ||||||
|             wpos = (wpos + chunk) % hw->samples; |             wpos = (wpos + chunk) % hw->samples; | ||||||
|             to_grab -= chunk; |             to_grab -= chunk; | ||||||
|         } |         } | ||||||
| @@ -399,6 +423,10 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) | |||||||
|     ESDVoiceIn *esd = (ESDVoiceIn *) hw; |     ESDVoiceIn *esd = (ESDVoiceIn *) hw; | ||||||
|     struct audsettings obt_as = *as; |     struct audsettings obt_as = *as; | ||||||
|     int esdfmt = ESD_STREAM | ESD_RECORD; |     int esdfmt = ESD_STREAM | ESD_RECORD; | ||||||
|  |     int err; | ||||||
|  |     sigset_t set, old_set; | ||||||
|  |  | ||||||
|  |     sigfillset (&set); | ||||||
|  |  | ||||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; |     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||||
|     switch (as->fmt) { |     switch (as->fmt) { | ||||||
| @@ -433,25 +461,44 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); |     esd->fd = -1; | ||||||
|     if (esd->fd < 0) { |  | ||||||
|         qesd_logerr (errno, "esd_record_stream failed\n"); |     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); | ||||||
|  |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask failed\n"); | ||||||
|         goto fail1; |         goto fail1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { |     esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); | ||||||
|  |     if (esd->fd < 0) { | ||||||
|  |         qesd_logerr (errno, "esd_record_stream failed\n"); | ||||||
|         goto fail2; |         goto fail2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||||
|  |         goto fail3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||||
|  |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
|  fail2: |  fail3: | ||||||
|     if (close (esd->fd)) { |     if (close (esd->fd)) { | ||||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", |         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||||
|                      AUDIO_FUNC, esd->fd); |                      AUDIO_FUNC, esd->fd); | ||||||
|     } |     } | ||||||
|     esd->fd = -1; |     esd->fd = -1; | ||||||
|  |  | ||||||
|  |  fail2: | ||||||
|  |     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||||
|  |     if (err) { | ||||||
|  |         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  fail1: |  fail1: | ||||||
|     qemu_free (esd->pcm_buf); |     qemu_free (esd->pcm_buf); | ||||||
|     esd->pcm_buf = NULL; |     esd->pcm_buf = NULL; | ||||||
|   | |||||||
| @@ -488,10 +488,10 @@ static int fmod_run_in (HWVoiceIn *hw) | |||||||
|     decr = len1 + len2; |     decr = len1 + len2; | ||||||
|  |  | ||||||
|     if (p1 && blen1) { |     if (p1 && blen1) { | ||||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); |         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||||
|     } |     } | ||||||
|     if (p2 && len2) { |     if (p2 && len2) { | ||||||
|         hw->conv (hw->conv_buf, p2, len2); |         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||||
|   | |||||||
| @@ -123,7 +123,7 @@ | |||||||
| #undef IN_T | #undef IN_T | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| /* Unsigned 32 bit */ | /* Unsigned 16 bit */ | ||||||
| #define IN_T uint32_t | #define IN_T uint32_t | ||||||
| #define IN_MIN 0 | #define IN_MIN 0 | ||||||
| #define IN_MAX UINT32_MAX | #define IN_MAX UINT32_MAX | ||||||
| @@ -333,28 +333,3 @@ void mixeng_clear (struct st_sample *buf, int len) | |||||||
| { | { | ||||||
|     memset (buf, 0, len * sizeof (struct st_sample)); |     memset (buf, 0, len * sizeof (struct st_sample)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_MIXEMU |  | ||||||
|     if (vol->mute) { |  | ||||||
|         mixeng_clear (buf, len); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (len--) { |  | ||||||
| #ifdef FLOAT_MIXENG |  | ||||||
|         buf->l = buf->l * vol->l; |  | ||||||
|         buf->r = buf->r * vol->r; |  | ||||||
| #else |  | ||||||
|         buf->l = (buf->l * vol->l) >> 32; |  | ||||||
|         buf->r = (buf->r * vol->r) >> 32; |  | ||||||
| #endif |  | ||||||
|         buf += 1; |  | ||||||
|     } |  | ||||||
| #else |  | ||||||
|     (void) buf; |  | ||||||
|     (void) len; |  | ||||||
|     (void) vol; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -33,7 +33,8 @@ struct mixeng_volume { int mute; int64_t r; int64_t l; }; | |||||||
| struct st_sample { int64_t l; int64_t r; }; | struct st_sample { int64_t l; int64_t r; }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); | typedef void (t_sample) (struct st_sample *dst, const void *src, | ||||||
|  |                          int samples, struct mixeng_volume *vol); | ||||||
| typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); | typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); | ||||||
|  |  | ||||||
| extern t_sample *mixeng_conv[2][2][2][3]; | extern t_sample *mixeng_conv[2][2][2][3]; | ||||||
| @@ -46,6 +47,5 @@ void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *o | |||||||
|                        int *isamp, int *osamp); |                        int *isamp, int *osamp); | ||||||
| void st_rate_stop (void *opaque); | void st_rate_stop (void *opaque); | ||||||
| void mixeng_clear (struct st_sample *buf, int len); | void mixeng_clear (struct st_sample *buf, int len); | ||||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); |  | ||||||
|  |  | ||||||
| #endif  /* mixeng.h */ | #endif  /* mixeng.h */ | ||||||
|   | |||||||
| @@ -31,6 +31,16 @@ | |||||||
| #define HALF (IN_MAX >> 1) | #define HALF (IN_MAX >> 1) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef CONFIG_MIXEMU | ||||||
|  | #ifdef FLOAT_MIXENG | ||||||
|  | #define VOL(a, b) ((a) * (b)) | ||||||
|  | #else | ||||||
|  | #define VOL(a, b) ((a) * (b)) >> 32 | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | #define VOL(a, b) a | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) | #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) | ||||||
|  |  | ||||||
| #ifdef FLOAT_MIXENG | #ifdef FLOAT_MIXENG | ||||||
| @@ -99,26 +109,40 @@ static inline IN_T glue (clip_, ET) (int64_t v) | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static void glue (glue (conv_, ET), _to_stereo) | static void glue (glue (conv_, ET), _to_stereo) | ||||||
|     (struct st_sample *dst, const void *src, int samples) |     (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) | ||||||
| { | { | ||||||
|     struct st_sample *out = dst; |     struct st_sample *out = dst; | ||||||
|     IN_T *in = (IN_T *) src; |     IN_T *in = (IN_T *) src; | ||||||
|  | #ifdef CONFIG_MIXEMU | ||||||
|  |     if (vol->mute) { | ||||||
|  |         mixeng_clear (dst, samples); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     (void) vol; | ||||||
|  | #endif | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         out->l = glue (conv_, ET) (*in++); |         out->l = VOL (glue (conv_, ET) (*in++), vol->l); | ||||||
|         out->r = glue (conv_, ET) (*in++); |         out->r = VOL (glue (conv_, ET) (*in++), vol->r); | ||||||
|         out += 1; |         out += 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (glue (conv_, ET), _to_mono) | static void glue (glue (conv_, ET), _to_mono) | ||||||
|     (struct st_sample *dst, const void *src, int samples) |     (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) | ||||||
| { | { | ||||||
|     struct st_sample *out = dst; |     struct st_sample *out = dst; | ||||||
|     IN_T *in = (IN_T *) src; |     IN_T *in = (IN_T *) src; | ||||||
|  | #ifdef CONFIG_MIXEMU | ||||||
|  |     if (vol->mute) { | ||||||
|  |         mixeng_clear (dst, samples); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     (void) vol; | ||||||
|  | #endif | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         out->l = glue (conv_, ET) (in[0]); |         out->l = VOL (glue (conv_, ET) (in[0]), vol->l); | ||||||
|         out->r = out->l; |         out->r = out->l; | ||||||
|         out += 1; |         out += 1; | ||||||
|         in += 1; |         in += 1; | ||||||
| @@ -150,3 +174,4 @@ static void glue (glue (clip_, ET), _from_mono) | |||||||
|  |  | ||||||
| #undef ET | #undef ET | ||||||
| #undef HALF | #undef HALF | ||||||
|  | #undef VOL | ||||||
|   | |||||||
| @@ -117,14 +117,11 @@ static int no_run_in (HWVoiceIn *hw) | |||||||
|  |  | ||||||
| static int no_read (SWVoiceIn *sw, void *buf, int size) | static int no_read (SWVoiceIn *sw, void *buf, int size) | ||||||
| { | { | ||||||
|     /* use custom code here instead of audio_pcm_sw_read() to avoid |  | ||||||
|      * useless resampling/mixing */ |  | ||||||
|     int samples = size >> sw->info.shift; |     int samples = size >> sw->info.shift; | ||||||
|     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; |     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | ||||||
|     int to_clear = audio_MIN (samples, total); |     int to_clear = audio_MIN (samples, total); | ||||||
|     sw->total_hw_samples_acquired += total; |  | ||||||
|     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); |     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); | ||||||
|     return to_clear << sw->info.shift; |     return to_clear; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
|   | |||||||
| @@ -161,7 +161,7 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_ossfmt (audfmt_e fmt, int endianness) | static int aud_to_ossfmt (audfmt_e fmt) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUD_FMT_S8: |     case AUD_FMT_S8: | ||||||
| @@ -171,20 +171,10 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) | |||||||
|         return AFMT_U8; |         return AFMT_U8; | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |         return AFMT_S16_LE; | ||||||
|             return AFMT_S16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return AFMT_S16_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |         return AFMT_U16_LE; | ||||||
|             return AFMT_U16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             return AFMT_U16_LE; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); |         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||||
| @@ -526,7 +516,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.fragsize = conf.fragsize; |     req.fragsize = conf.fragsize; | ||||||
| @@ -692,7 +682,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) | |||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.fragsize = conf.fragsize; |     req.fragsize = conf.fragsize; | ||||||
| @@ -788,7 +778,8 @@ static int oss_run_in (HWVoiceIn *hw) | |||||||
|                            hw->info.align + 1); |                            hw->info.align + 1); | ||||||
|                 } |                 } | ||||||
|                 read_samples += nread >> hwshift; |                 read_samples += nread >> hwshift; | ||||||
|                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); |                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, | ||||||
|  |                           &nominal_volume); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (bufs[i].len - nread) { |             if (bufs[i].len - nread) { | ||||||
|   | |||||||
| @@ -33,11 +33,13 @@ typedef struct { | |||||||
|  |  | ||||||
| static struct { | static struct { | ||||||
|     int samples; |     int samples; | ||||||
|  |     int divisor; | ||||||
|     char *server; |     char *server; | ||||||
|     char *sink; |     char *sink; | ||||||
|     char *source; |     char *source; | ||||||
| } conf = { | } conf = { | ||||||
|     .samples = 4096, |     .samples = 1024, | ||||||
|  |     .divisor = 2, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) | static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) | ||||||
| @@ -55,6 +57,9 @@ static void *qpa_thread_out (void *arg) | |||||||
| { | { | ||||||
|     PAVoiceOut *pa = arg; |     PAVoiceOut *pa = arg; | ||||||
|     HWVoiceOut *hw = &pa->hw; |     HWVoiceOut *hw = &pa->hw; | ||||||
|  |     int threshold; | ||||||
|  |  | ||||||
|  |     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||||
|  |  | ||||||
|     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { |     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -68,7 +73,7 @@ static void *qpa_thread_out (void *arg) | |||||||
|                 goto exit; |                 goto exit; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (pa->live > 0) { |             if (pa->live > threshold) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -77,8 +82,8 @@ static void *qpa_thread_out (void *arg) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         decr = to_mix = audio_MIN (pa->live, conf.samples >> 2); |         decr = to_mix = pa->live; | ||||||
|         rpos = pa->rpos; |         rpos = hw->rpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { |         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||||
|             return NULL; |             return NULL; | ||||||
| @@ -147,6 +152,9 @@ static void *qpa_thread_in (void *arg) | |||||||
| { | { | ||||||
|     PAVoiceIn *pa = arg; |     PAVoiceIn *pa = arg; | ||||||
|     HWVoiceIn *hw = &pa->hw; |     HWVoiceIn *hw = &pa->hw; | ||||||
|  |     int threshold; | ||||||
|  |  | ||||||
|  |     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||||
|  |  | ||||||
|     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { |     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -160,7 +168,7 @@ static void *qpa_thread_in (void *arg) | |||||||
|                 goto exit; |                 goto exit; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (pa->dead > 0) { |             if (pa->dead > threshold) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -169,8 +177,8 @@ static void *qpa_thread_in (void *arg) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2); |         incr = to_grab = pa->dead; | ||||||
|         wpos = pa->wpos; |         wpos = hw->wpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { |         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||||
|             return NULL; |             return NULL; | ||||||
| @@ -187,7 +195,7 @@ static void *qpa_thread_in (void *arg) | |||||||
|                 return NULL; |                 return NULL; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             hw->conv (hw->conv_buf + wpos, buf, chunk); |             hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); | ||||||
|             wpos = (wpos + chunk) % hw->samples; |             wpos = (wpos + chunk) % hw->samples; | ||||||
|             to_grab -= chunk; |             to_grab -= chunk; | ||||||
|         } |         } | ||||||
| @@ -287,7 +295,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
| { | { | ||||||
|     int error; |     int error; | ||||||
|     static pa_sample_spec ss; |     static pa_sample_spec ss; | ||||||
|     static pa_buffer_attr ba; |  | ||||||
|     struct audsettings obt_as = *as; |     struct audsettings obt_as = *as; | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||||
|  |  | ||||||
| @@ -295,15 +302,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     ss.channels = as->nchannels; |     ss.channels = as->nchannels; | ||||||
|     ss.rate = as->freq; |     ss.rate = as->freq; | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * qemu audio tick runs at 250 Hz (by default), so processing |  | ||||||
|      * data chunks worth 4 ms of sound should be a good fit. |  | ||||||
|      */ |  | ||||||
|     ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); |  | ||||||
|     ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); |  | ||||||
|     ba.maxlength = -1; |  | ||||||
|     ba.prebuf = -1; |  | ||||||
|  |  | ||||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||||
|  |  | ||||||
|     pa->s = pa_simple_new ( |     pa->s = pa_simple_new ( | ||||||
| @@ -314,7 +312,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|         "pcm.playback", |         "pcm.playback", | ||||||
|         &ss, |         &ss, | ||||||
|         NULL,                   /* channel map */ |         NULL,                   /* channel map */ | ||||||
|         &ba,                    /* buffering attributes */ |         NULL,                   /* buffering attributes */ | ||||||
|         &error |         &error | ||||||
|         ); |         ); | ||||||
|     if (!pa->s) { |     if (!pa->s) { | ||||||
| @@ -325,7 +323,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|     hw->samples = conf.samples; |     hw->samples = conf.samples; | ||||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); |     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||||
|     pa->rpos = hw->rpos; |  | ||||||
|     if (!pa->pcm_buf) { |     if (!pa->pcm_buf) { | ||||||
|         dolog ("Could not allocate buffer (%d bytes)\n", |         dolog ("Could not allocate buffer (%d bytes)\n", | ||||||
|                hw->samples << hw->info.shift); |                hw->samples << hw->info.shift); | ||||||
| @@ -380,7 +377,6 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) | |||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|     hw->samples = conf.samples; |     hw->samples = conf.samples; | ||||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); |     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||||
|     pa->wpos = hw->wpos; |  | ||||||
|     if (!pa->pcm_buf) { |     if (!pa->pcm_buf) { | ||||||
|         dolog ("Could not allocate buffer (%d bytes)\n", |         dolog ("Could not allocate buffer (%d bytes)\n", | ||||||
|                hw->samples << hw->info.shift); |                hw->samples << hw->info.shift); | ||||||
| @@ -475,6 +471,12 @@ struct audio_option qpa_options[] = { | |||||||
|         .valp  = &conf.samples, |         .valp  = &conf.samples, | ||||||
|         .descr = "buffer size in samples" |         .descr = "buffer size in samples" | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "DIVISOR", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &conf.divisor, | ||||||
|  |         .descr = "threshold divisor" | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|         .name  = "SERVER", |         .name  = "SERVER", | ||||||
|         .tag   = AUD_OPT_STR, |         .tag   = AUD_OPT_STR, | ||||||
|   | |||||||
							
								
								
									
										115
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -41,8 +41,8 @@ | |||||||
| typedef struct SDLVoiceOut { | typedef struct SDLVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     int live; |     int live; | ||||||
|     int rpos; |  | ||||||
|     int decr; |     int decr; | ||||||
|  |     int pending; | ||||||
| } SDLVoiceOut; | } SDLVoiceOut; | ||||||
|  |  | ||||||
| static struct { | static struct { | ||||||
| @@ -115,19 +115,23 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | |||||||
|     return sdl_post (s, forfn); |     return sdl_post (s, forfn); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_sdlfmt (audfmt_e fmt) | static int aud_to_sdlfmt (audfmt_e fmt, int *shift) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUD_FMT_S8: |     case AUD_FMT_S8: | ||||||
|  |         *shift = 0; | ||||||
|         return AUDIO_S8; |         return AUDIO_S8; | ||||||
|  |  | ||||||
|     case AUD_FMT_U8: |     case AUD_FMT_U8: | ||||||
|  |         *shift = 0; | ||||||
|         return AUDIO_U8; |         return AUDIO_U8; | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |     case AUD_FMT_S16: | ||||||
|  |         *shift = 1; | ||||||
|         return AUDIO_S16LSB; |         return AUDIO_S16LSB; | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |     case AUD_FMT_U16: | ||||||
|  |         *shift = 1; | ||||||
|         return AUDIO_U16LSB; |         return AUDIO_U16LSB; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -184,20 +188,11 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
| { | { | ||||||
|     int status; |     int status; | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     int err; |  | ||||||
|     sigset_t new, old; |     sigset_t new, old; | ||||||
|  |  | ||||||
|     /* Make sure potential threads created by SDL don't hog signals.  */ |     /* Make sure potential threads created by SDL don't hog signals.  */ | ||||||
|     err = sigfillset (&new); |     sigfillset (&new); | ||||||
|     if (err) { |     pthread_sigmask (SIG_BLOCK, &new, &old); | ||||||
|         dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     err = pthread_sigmask (SIG_BLOCK, &new, &old); |  | ||||||
|     if (err) { |  | ||||||
|         dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     status = SDL_OpenAudio (req, obt); |     status = SDL_OpenAudio (req, obt); | ||||||
| @@ -206,14 +201,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     err = pthread_sigmask (SIG_SETMASK, &old, NULL); |     pthread_sigmask (SIG_SETMASK, &old, NULL); | ||||||
|     if (err) { |  | ||||||
|         dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", |  | ||||||
|                strerror (errno)); |  | ||||||
|         /* We have failed to restore original signal mask, all bets are off, |  | ||||||
|            so exit the process */ |  | ||||||
|         exit (EXIT_FAILURE); |  | ||||||
|     } |  | ||||||
| #endif | #endif | ||||||
|     return status; |     return status; | ||||||
| } | } | ||||||
| @@ -237,6 +225,10 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|     HWVoiceOut *hw = &sdl->hw; |     HWVoiceOut *hw = &sdl->hw; | ||||||
|     int samples = len >> hw->info.shift; |     int samples = len >> hw->info.shift; | ||||||
|  |  | ||||||
|  |     if (sdl_lock (s, "sdl_callback")) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (s->exit) { |     if (s->exit) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -244,49 +236,34 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|     while (samples) { |     while (samples) { | ||||||
|         int to_mix, decr; |         int to_mix, decr; | ||||||
|  |  | ||||||
|         /* dolog ("in callback samples=%d\n", samples); */ |         while (!sdl->pending) { | ||||||
|         sdl_wait (s, "sdl_callback"); |             if (sdl_unlock (s, "sdl_callback")) { | ||||||
|         if (s->exit) { |                 return; | ||||||
|             return; |             } | ||||||
|  |  | ||||||
|  |             sdl_wait (s, "sdl_callback"); | ||||||
|  |             if (s->exit) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (sdl_lock (s, "sdl_callback")) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             sdl->pending += sdl->live; | ||||||
|  |             sdl->live = 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (sdl_lock (s, "sdl_callback")) { |         to_mix = audio_MIN (samples, sdl->pending); | ||||||
|             return; |         decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0); | ||||||
|         } |         buf += decr << hw->info.shift; | ||||||
|  |  | ||||||
|         if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { |  | ||||||
|             dolog ("sdl->live=%d hw->samples=%d\n", |  | ||||||
|                    sdl->live, hw->samples); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!sdl->live) { |  | ||||||
|             goto again; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* dolog ("in callback live=%d\n", live); */ |  | ||||||
|         to_mix = audio_MIN (samples, sdl->live); |  | ||||||
|         decr = to_mix; |  | ||||||
|         while (to_mix) { |  | ||||||
|             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); |  | ||||||
|             struct st_sample *src = hw->mix_buf + hw->rpos; |  | ||||||
|  |  | ||||||
|             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ |  | ||||||
|             hw->clip (buf, src, chunk); |  | ||||||
|             sdl->rpos = (sdl->rpos + chunk) % hw->samples; |  | ||||||
|             to_mix -= chunk; |  | ||||||
|             buf += chunk << hw->info.shift; |  | ||||||
|         } |  | ||||||
|         samples -= decr; |         samples -= decr; | ||||||
|         sdl->live -= decr; |  | ||||||
|         sdl->decr += decr; |         sdl->decr += decr; | ||||||
|  |         sdl->pending -= decr; | ||||||
|     again: |     } | ||||||
|         if (sdl_unlock (s, "sdl_callback")) { |  | ||||||
|             return; |     if (sdl_unlock (s, "sdl_callback")) { | ||||||
|         } |         return; | ||||||
|     } |     } | ||||||
|     /* dolog ("done len=%d\n", len); */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | ||||||
| @@ -304,18 +281,9 @@ static int sdl_run_out (HWVoiceOut *hw, int live) | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (sdl->decr > live) { |     sdl->live = live; | ||||||
|         ldebug ("sdl->decr %d live %d sdl->live %d\n", |     decr = sdl->decr; | ||||||
|                 sdl->decr, |     sdl->decr = 0; | ||||||
|                 live, |  | ||||||
|                 sdl->live); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     decr = audio_MIN (sdl->decr, live); |  | ||||||
|     sdl->decr -= decr; |  | ||||||
|  |  | ||||||
|     sdl->live = live - decr; |  | ||||||
|     hw->rpos = sdl->rpos; |  | ||||||
|  |  | ||||||
|     if (sdl->live > 0) { |     if (sdl->live > 0) { | ||||||
|         sdl_unlock_and_post (s, "sdl_run_out"); |         sdl_unlock_and_post (s, "sdl_run_out"); | ||||||
| @@ -338,13 +306,16 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) | |||||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; |     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     SDL_AudioSpec req, obt; |     SDL_AudioSpec req, obt; | ||||||
|  |     int shift; | ||||||
|     int endianess; |     int endianess; | ||||||
|     int err; |     int err; | ||||||
|     audfmt_e effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |  | ||||||
|  |     shift <<= as->nchannels == 2; | ||||||
|  |  | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.format = aud_to_sdlfmt (as->fmt); |     req.format = aud_to_sdlfmt (as->fmt, &shift); | ||||||
|     req.channels = as->nchannels; |     req.channels = as->nchannels; | ||||||
|     req.samples = conf.nb_samples; |     req.samples = conf.nb_samples; | ||||||
|     req.callback = sdl_callback; |     req.callback = sdl_callback; | ||||||
|   | |||||||
| @@ -1,345 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (C) 2010 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * maintained by Gerd Hoffmann <kraxel@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or |  | ||||||
|  * modify it under the terms of the GNU General Public License as |  | ||||||
|  * published by the Free Software Foundation; either version 2 or |  | ||||||
|  * (at your option) version 3 of the License. |  | ||||||
|  * |  | ||||||
|  * This program is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "hw/hw.h" |  | ||||||
| #include "qemu-timer.h" |  | ||||||
| #include "ui/qemu-spice.h" |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "spice" |  | ||||||
| #include "audio.h" |  | ||||||
| #include "audio_int.h" |  | ||||||
|  |  | ||||||
| #define LINE_IN_SAMPLES 1024 |  | ||||||
| #define LINE_OUT_SAMPLES 1024 |  | ||||||
|  |  | ||||||
| typedef struct SpiceRateCtl { |  | ||||||
|     int64_t               start_ticks; |  | ||||||
|     int64_t               bytes_sent; |  | ||||||
| } SpiceRateCtl; |  | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceOut { |  | ||||||
|     HWVoiceOut            hw; |  | ||||||
|     SpicePlaybackInstance sin; |  | ||||||
|     SpiceRateCtl          rate; |  | ||||||
|     int                   active; |  | ||||||
|     uint32_t              *frame; |  | ||||||
|     uint32_t              *fpos; |  | ||||||
|     uint32_t              fsize; |  | ||||||
| } SpiceVoiceOut; |  | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceIn { |  | ||||||
|     HWVoiceIn             hw; |  | ||||||
|     SpiceRecordInstance   sin; |  | ||||||
|     SpiceRateCtl          rate; |  | ||||||
|     int                   active; |  | ||||||
|     uint32_t              samples[LINE_IN_SAMPLES]; |  | ||||||
| } SpiceVoiceIn; |  | ||||||
|  |  | ||||||
| static const SpicePlaybackInterface playback_sif = { |  | ||||||
|     .base.type          = SPICE_INTERFACE_PLAYBACK, |  | ||||||
|     .base.description   = "playback", |  | ||||||
|     .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, |  | ||||||
|     .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const SpiceRecordInterface record_sif = { |  | ||||||
|     .base.type          = SPICE_INTERFACE_RECORD, |  | ||||||
|     .base.description   = "record", |  | ||||||
|     .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, |  | ||||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void *spice_audio_init (void) |  | ||||||
| { |  | ||||||
|     if (!using_spice) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     return &spice_audio_init; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void spice_audio_fini (void *opaque) |  | ||||||
| { |  | ||||||
|     /* nothing */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rate_start (SpiceRateCtl *rate) |  | ||||||
| { |  | ||||||
|     memset (rate, 0, sizeof (*rate)); |  | ||||||
|     rate->start_ticks = qemu_get_clock (vm_clock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) |  | ||||||
| { |  | ||||||
|     int64_t now; |  | ||||||
|     int64_t ticks; |  | ||||||
|     int64_t bytes; |  | ||||||
|     int64_t samples; |  | ||||||
|  |  | ||||||
|     now = qemu_get_clock (vm_clock); |  | ||||||
|     ticks = now - rate->start_ticks; |  | ||||||
|     bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ()); |  | ||||||
|     samples = (bytes - rate->bytes_sent) >> info->shift; |  | ||||||
|     if (samples < 0 || samples > 65536) { |  | ||||||
|         fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples); |  | ||||||
|         rate_start (rate); |  | ||||||
|         samples = 0; |  | ||||||
|     } |  | ||||||
|     rate->bytes_sent += samples << info->shift; |  | ||||||
|     return samples; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* playback */ |  | ||||||
|  |  | ||||||
| static int line_out_init (HWVoiceOut *hw, struct audsettings *as) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|     struct audsettings settings; |  | ||||||
|  |  | ||||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; |  | ||||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; |  | ||||||
|     settings.fmt        = AUD_FMT_S16; |  | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |  | ||||||
|     hw->samples = LINE_OUT_SAMPLES; |  | ||||||
|     out->active = 0; |  | ||||||
|  |  | ||||||
|     out->sin.base.sif = &playback_sif.base; |  | ||||||
|     qemu_spice_add_interface (&out->sin.base); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void line_out_fini (HWVoiceOut *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|  |  | ||||||
|     spice_server_remove_interface (&out->sin.base); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_run (HWVoiceOut *hw, int live) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|     int rpos, decr; |  | ||||||
|     int samples; |  | ||||||
|  |  | ||||||
|     if (!live) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     decr = rate_get_samples (&hw->info, &out->rate); |  | ||||||
|     decr = audio_MIN (live, decr); |  | ||||||
|  |  | ||||||
|     samples = decr; |  | ||||||
|     rpos = hw->rpos; |  | ||||||
|     while (samples) { |  | ||||||
|         int left_till_end_samples = hw->samples - rpos; |  | ||||||
|         int len = audio_MIN (samples, left_till_end_samples); |  | ||||||
|  |  | ||||||
|         if (!out->frame) { |  | ||||||
|             spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); |  | ||||||
|             out->fpos = out->frame; |  | ||||||
|         } |  | ||||||
|         if (out->frame) { |  | ||||||
|             len = audio_MIN (len, out->fsize); |  | ||||||
|             hw->clip (out->fpos, hw->mix_buf + rpos, len); |  | ||||||
|             out->fsize -= len; |  | ||||||
|             out->fpos  += len; |  | ||||||
|             if (out->fsize == 0) { |  | ||||||
|                 spice_server_playback_put_samples (&out->sin, out->frame); |  | ||||||
|                 out->frame = out->fpos = NULL; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         rpos = (rpos + len) % hw->samples; |  | ||||||
|         samples -= len; |  | ||||||
|     } |  | ||||||
|     hw->rpos = rpos; |  | ||||||
|     return decr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_write (SWVoiceOut *sw, void *buf, int len) |  | ||||||
| { |  | ||||||
|     return audio_pcm_sw_write (sw, buf, len); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         if (out->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         out->active = 1; |  | ||||||
|         rate_start (&out->rate); |  | ||||||
|         spice_server_playback_start (&out->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_DISABLE: |  | ||||||
|         if (!out->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         out->active = 0; |  | ||||||
|         if (out->frame) { |  | ||||||
|             memset (out->fpos, 0, out->fsize << 2); |  | ||||||
|             spice_server_playback_put_samples (&out->sin, out->frame); |  | ||||||
|             out->frame = out->fpos = NULL; |  | ||||||
|         } |  | ||||||
|         spice_server_playback_stop (&out->sin); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* record */ |  | ||||||
|  |  | ||||||
| static int line_in_init (HWVoiceIn *hw, struct audsettings *as) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|     struct audsettings settings; |  | ||||||
|  |  | ||||||
|     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; |  | ||||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; |  | ||||||
|     settings.fmt        = AUD_FMT_S16; |  | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |  | ||||||
|     hw->samples = LINE_IN_SAMPLES; |  | ||||||
|     in->active = 0; |  | ||||||
|  |  | ||||||
|     in->sin.base.sif = &record_sif.base; |  | ||||||
|     qemu_spice_add_interface (&in->sin.base); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void line_in_fini (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|  |  | ||||||
|     spice_server_remove_interface (&in->sin.base); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_in_run (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|     int num_samples; |  | ||||||
|     int ready; |  | ||||||
|     int len[2]; |  | ||||||
|     uint64_t delta_samp; |  | ||||||
|     const uint32_t *samples; |  | ||||||
|  |  | ||||||
|     if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     delta_samp = rate_get_samples (&hw->info, &in->rate); |  | ||||||
|     num_samples = audio_MIN (num_samples, delta_samp); |  | ||||||
|  |  | ||||||
|     ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); |  | ||||||
|     samples = in->samples; |  | ||||||
|     if (ready == 0) { |  | ||||||
|         static const uint32_t silence[LINE_IN_SAMPLES]; |  | ||||||
|         samples = silence; |  | ||||||
|         ready = LINE_IN_SAMPLES; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     num_samples = audio_MIN (ready, num_samples); |  | ||||||
|  |  | ||||||
|     if (hw->wpos + num_samples > hw->samples) { |  | ||||||
|         len[0] = hw->samples - hw->wpos; |  | ||||||
|         len[1] = num_samples - len[0]; |  | ||||||
|     } else { |  | ||||||
|         len[0] = num_samples; |  | ||||||
|         len[1] = 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); |  | ||||||
|  |  | ||||||
|     if (len[1]) { |  | ||||||
|         hw->conv (hw->conv_buf, samples + len[0], len[1]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     hw->wpos = (hw->wpos + num_samples) % hw->samples; |  | ||||||
|  |  | ||||||
|     return num_samples; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_in_read (SWVoiceIn *sw, void *buf, int size) |  | ||||||
| { |  | ||||||
|     return audio_pcm_sw_read (sw, buf, size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         if (in->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         in->active = 1; |  | ||||||
|         rate_start (&in->rate); |  | ||||||
|         spice_server_record_start (&in->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_DISABLE: |  | ||||||
|         if (!in->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         in->active = 0; |  | ||||||
|         spice_server_record_stop (&in->sin); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct audio_option audio_options[] = { |  | ||||||
|     { /* end of list */ }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct audio_pcm_ops audio_callbacks = { |  | ||||||
|     .init_out = line_out_init, |  | ||||||
|     .fini_out = line_out_fini, |  | ||||||
|     .run_out  = line_out_run, |  | ||||||
|     .write    = line_out_write, |  | ||||||
|     .ctl_out  = line_out_ctl, |  | ||||||
|  |  | ||||||
|     .init_in  = line_in_init, |  | ||||||
|     .fini_in  = line_in_fini, |  | ||||||
|     .run_in   = line_in_run, |  | ||||||
|     .read     = line_in_read, |  | ||||||
|     .ctl_in   = line_in_ctl, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct audio_driver spice_audio_driver = { |  | ||||||
|     .name           = "spice", |  | ||||||
|     .descr          = "spice audio driver", |  | ||||||
|     .options        = audio_options, |  | ||||||
|     .init           = spice_audio_init, |  | ||||||
|     .fini           = spice_audio_fini, |  | ||||||
|     .pcm_ops        = &audio_callbacks, |  | ||||||
|     .max_voices_out = 1, |  | ||||||
|     .max_voices_in  = 1, |  | ||||||
|     .voice_size_out = sizeof (SpiceVoiceOut), |  | ||||||
|     .voice_size_in  = sizeof (SpiceVoiceIn), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void qemu_spice_audio_init (void) |  | ||||||
| { |  | ||||||
|     spice_audio_driver.can_be_default = 1; |  | ||||||
| } |  | ||||||
| @@ -581,7 +581,8 @@ static int winwave_run_in (HWVoiceIn *hw) | |||||||
|         int conv = audio_MIN (left, decr); |         int conv = audio_MIN (left, decr); | ||||||
|         hw->conv (hw->conv_buf + hw->wpos, |         hw->conv (hw->conv_buf + hw->wpos, | ||||||
|                   advance (wave->pcm_buf, wave->rpos << hw->info.shift), |                   advance (wave->pcm_buf, wave->rpos << hw->info.shift), | ||||||
|                   conv); |                   conv, | ||||||
|  |                   &nominal_volume); | ||||||
|  |  | ||||||
|         wave->rpos = (wave->rpos + conv) % hw->samples; |         wave->rpos = (wave->rpos + conv) % hw->samples; | ||||||
|         hw->wpos = (hw->wpos + conv) % hw->samples; |         hw->wpos = (hw->wpos + conv) % hw->samples; | ||||||
|   | |||||||
							
								
								
									
										148
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -1,148 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU System Emulator |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "sysemu.h" |  | ||||||
| #include "monitor.h" |  | ||||||
| #include "qjson.h" |  | ||||||
| #include "qint.h" |  | ||||||
| #include "cpu-common.h" |  | ||||||
| #include "kvm.h" |  | ||||||
| #include "balloon.h" |  | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static QEMUBalloonEvent *qemu_balloon_event; |  | ||||||
| void *qemu_balloon_event_opaque; |  | ||||||
|  |  | ||||||
| void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) |  | ||||||
| { |  | ||||||
|     qemu_balloon_event = func; |  | ||||||
|     qemu_balloon_event_opaque = opaque; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) |  | ||||||
| { |  | ||||||
|     if (qemu_balloon_event) { |  | ||||||
|         trace_balloon_event(qemu_balloon_event_opaque, target); |  | ||||||
|         qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque); |  | ||||||
|         return 1; |  | ||||||
|     } else { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qemu_balloon_status(MonitorCompletion cb, void *opaque) |  | ||||||
| { |  | ||||||
|     if (qemu_balloon_event) { |  | ||||||
|         qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque); |  | ||||||
|         return 1; |  | ||||||
|     } else { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void print_balloon_stat(const char *key, QObject *obj, void *opaque) |  | ||||||
| { |  | ||||||
|     Monitor *mon = opaque; |  | ||||||
|  |  | ||||||
|     if (strcmp(key, "actual")) |  | ||||||
|         monitor_printf(mon, ",%s=%" PRId64, key, |  | ||||||
|                        qint_get_int(qobject_to_qint(obj))); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void monitor_print_balloon(Monitor *mon, const QObject *data) |  | ||||||
| { |  | ||||||
|     QDict *qdict; |  | ||||||
|  |  | ||||||
|     qdict = qobject_to_qdict(data); |  | ||||||
|     if (!qdict_haskey(qdict, "actual")) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     monitor_printf(mon, "balloon: actual=%" PRId64, |  | ||||||
|                    qdict_get_int(qdict, "actual") >> 20); |  | ||||||
|     qdict_iter(qdict, print_balloon_stat, mon); |  | ||||||
|     monitor_printf(mon, "\n"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * do_info_balloon(): Balloon information |  | ||||||
|  * |  | ||||||
|  * Make an asynchronous request for balloon info.  When the request completes |  | ||||||
|  * a QDict will be returned according to the following specification: |  | ||||||
|  * |  | ||||||
|  * - "actual": current balloon value in bytes |  | ||||||
|  * The following fields may or may not be present: |  | ||||||
|  * - "mem_swapped_in": Amount of memory swapped in (bytes) |  | ||||||
|  * - "mem_swapped_out": Amount of memory swapped out (bytes) |  | ||||||
|  * - "major_page_faults": Number of major faults |  | ||||||
|  * - "minor_page_faults": Number of minor faults |  | ||||||
|  * - "free_mem": Total amount of free and unused memory (bytes) |  | ||||||
|  * - "total_mem": Total amount of available memory (bytes) |  | ||||||
|  * |  | ||||||
|  * Example: |  | ||||||
|  * |  | ||||||
|  * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0, |  | ||||||
|  *   "major_page_faults": 142, "minor_page_faults": 239245, |  | ||||||
|  *   "free_mem": 1014185984, "total_mem": 1044668416 } |  | ||||||
|  */ |  | ||||||
| int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     if (kvm_enabled() && !kvm_has_sync_mmu()) { |  | ||||||
|         qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qemu_balloon_status(cb, opaque); |  | ||||||
|     if (!ret) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * do_balloon(): Request VM to change its memory allocation |  | ||||||
|  */ |  | ||||||
| int do_balloon(Monitor *mon, const QDict *params, |  | ||||||
| 	       MonitorCompletion cb, void *opaque) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     if (kvm_enabled() && !kvm_has_sync_mmu()) { |  | ||||||
|         qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque); |  | ||||||
|     if (ret == 0) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cb(opaque, NULL); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
							
								
								
									
										14
									
								
								balloon.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								balloon.h
									
									
									
									
									
								
							| @@ -14,20 +14,14 @@ | |||||||
| #ifndef _QEMU_BALLOON_H | #ifndef _QEMU_BALLOON_H | ||||||
| #define _QEMU_BALLOON_H | #define _QEMU_BALLOON_H | ||||||
|  |  | ||||||
| #include "monitor.h" | #include "cpu-defs.h" | ||||||
|  |  | ||||||
| typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, | typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target); | ||||||
|                                 MonitorCompletion cb, void *cb_data); |  | ||||||
|  |  | ||||||
| void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); | void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); | ||||||
|  |  | ||||||
| int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque); | void qemu_balloon(ram_addr_t target); | ||||||
|  |  | ||||||
| int qemu_balloon_status(MonitorCompletion cb, void *opaque); | ram_addr_t qemu_balloon_status(void); | ||||||
|  |  | ||||||
| void monitor_print_balloon(Monitor *mon, const QObject *data); |  | ||||||
| int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); |  | ||||||
| int do_balloon(Monitor *mon, const QDict *params, |  | ||||||
|                MonitorCompletion cb, void *opaque); |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -15,11 +15,8 @@ | |||||||
| #include "block_int.h" | #include "block_int.h" | ||||||
| #include "hw/hw.h" | #include "hw/hw.h" | ||||||
| #include "qemu-queue.h" | #include "qemu-queue.h" | ||||||
| #include "qemu-timer.h" |  | ||||||
| #include "monitor.h" | #include "monitor.h" | ||||||
| #include "block-migration.h" | #include "block-migration.h" | ||||||
| #include "migration.h" |  | ||||||
| #include "blockdev.h" |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  |  | ||||||
| #define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) | #define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) | ||||||
| @@ -29,14 +26,17 @@ | |||||||
| #define BLK_MIG_FLAG_PROGRESS           0x04 | #define BLK_MIG_FLAG_PROGRESS           0x04 | ||||||
|  |  | ||||||
| #define MAX_IS_ALLOCATED_SEARCH 65536 | #define MAX_IS_ALLOCATED_SEARCH 65536 | ||||||
|  | #define MAX_BLOCKS_READ 10000 | ||||||
|  | #define BLOCKS_READ_CHANGE 100 | ||||||
|  | #define INITIAL_BLOCKS_READ 100 | ||||||
|  |  | ||||||
| //#define DEBUG_BLK_MIGRATION | //#define DEBUG_BLK_MIGRATION | ||||||
|  |  | ||||||
| #ifdef DEBUG_BLK_MIGRATION | #ifdef DEBUG_BLK_MIGRATION | ||||||
| #define DPRINTF(fmt, ...) \ | #define dprintf(fmt, ...) \ | ||||||
|     do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) |     do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) | ||||||
| #else | #else | ||||||
| #define DPRINTF(fmt, ...) \ | #define dprintf(fmt, ...) \ | ||||||
|     do { } while (0) |     do { } while (0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -45,24 +45,20 @@ typedef struct BlkMigDevState { | |||||||
|     int bulk_completed; |     int bulk_completed; | ||||||
|     int shared_base; |     int shared_base; | ||||||
|     int64_t cur_sector; |     int64_t cur_sector; | ||||||
|     int64_t cur_dirty; |  | ||||||
|     int64_t completed_sectors; |     int64_t completed_sectors; | ||||||
|     int64_t total_sectors; |     int64_t total_sectors; | ||||||
|     int64_t dirty; |     int64_t dirty; | ||||||
|     QSIMPLEQ_ENTRY(BlkMigDevState) entry; |     QSIMPLEQ_ENTRY(BlkMigDevState) entry; | ||||||
|     unsigned long *aio_bitmap; |  | ||||||
| } BlkMigDevState; | } BlkMigDevState; | ||||||
|  |  | ||||||
| typedef struct BlkMigBlock { | typedef struct BlkMigBlock { | ||||||
|     uint8_t *buf; |     uint8_t *buf; | ||||||
|     BlkMigDevState *bmds; |     BlkMigDevState *bmds; | ||||||
|     int64_t sector; |     int64_t sector; | ||||||
|     int nr_sectors; |  | ||||||
|     struct iovec iov; |     struct iovec iov; | ||||||
|     QEMUIOVector qiov; |     QEMUIOVector qiov; | ||||||
|     BlockDriverAIOCB *aiocb; |     BlockDriverAIOCB *aiocb; | ||||||
|     int ret; |     int ret; | ||||||
|     int64_t time; |  | ||||||
|     QSIMPLEQ_ENTRY(BlkMigBlock) entry; |     QSIMPLEQ_ENTRY(BlkMigBlock) entry; | ||||||
| } BlkMigBlock; | } BlkMigBlock; | ||||||
|  |  | ||||||
| @@ -76,9 +72,6 @@ typedef struct BlkMigState { | |||||||
|     int transferred; |     int transferred; | ||||||
|     int64_t total_sector_sum; |     int64_t total_sector_sum; | ||||||
|     int prev_progress; |     int prev_progress; | ||||||
|     int bulk_completed; |  | ||||||
|     long double total_time; |  | ||||||
|     int reads; |  | ||||||
| } BlkMigState; | } BlkMigState; | ||||||
|  |  | ||||||
| static BlkMigState block_mig_state; | static BlkMigState block_mig_state; | ||||||
| @@ -131,76 +124,13 @@ uint64_t blk_mig_bytes_total(void) | |||||||
|     return sum << BDRV_SECTOR_BITS; |     return sum << BDRV_SECTOR_BITS; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline void add_avg_read_time(int64_t time) |  | ||||||
| { |  | ||||||
|     block_mig_state.reads++; |  | ||||||
|     block_mig_state.total_time += time; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline long double compute_read_bwidth(void) |  | ||||||
| { |  | ||||||
|     assert(block_mig_state.total_time != 0); |  | ||||||
|     return  (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) |  | ||||||
| { |  | ||||||
|     int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|  |  | ||||||
|     if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) { |  | ||||||
|         return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & |  | ||||||
|             (1UL << (chunk % (sizeof(unsigned long) * 8)))); |  | ||||||
|     } else { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, |  | ||||||
|                              int nb_sectors, int set) |  | ||||||
| { |  | ||||||
|     int64_t start, end; |  | ||||||
|     unsigned long val, idx, bit; |  | ||||||
|  |  | ||||||
|     start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|     end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|  |  | ||||||
|     for (; start <= end; start++) { |  | ||||||
|         idx = start / (sizeof(unsigned long) * 8); |  | ||||||
|         bit = start % (sizeof(unsigned long) * 8); |  | ||||||
|         val = bmds->aio_bitmap[idx]; |  | ||||||
|         if (set) { |  | ||||||
|             val |= 1UL << bit; |  | ||||||
|         } else { |  | ||||||
|             val &= ~(1UL << bit); |  | ||||||
|         } |  | ||||||
|         bmds->aio_bitmap[idx] = val; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void alloc_aio_bitmap(BlkMigDevState *bmds) |  | ||||||
| { |  | ||||||
|     BlockDriverState *bs = bmds->bs; |  | ||||||
|     int64_t bitmap_size; |  | ||||||
|  |  | ||||||
|     bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + |  | ||||||
|             BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; |  | ||||||
|     bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; |  | ||||||
|  |  | ||||||
|     bmds->aio_bitmap = qemu_mallocz(bitmap_size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_mig_read_cb(void *opaque, int ret) | static void blk_mig_read_cb(void *opaque, int ret) | ||||||
| { | { | ||||||
|     BlkMigBlock *blk = opaque; |     BlkMigBlock *blk = opaque; | ||||||
|  |  | ||||||
|     blk->ret = ret; |     blk->ret = ret; | ||||||
|  |  | ||||||
|     blk->time = qemu_get_clock_ns(rt_clock) - blk->time; |  | ||||||
|  |  | ||||||
|     add_avg_read_time(blk->time); |  | ||||||
|  |  | ||||||
|     QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); |     QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); | ||||||
|     bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); |  | ||||||
|  |  | ||||||
|     block_mig_state.submitted--; |     block_mig_state.submitted--; | ||||||
|     block_mig_state.read_done++; |     block_mig_state.read_done++; | ||||||
| @@ -208,7 +138,7 @@ static void blk_mig_read_cb(void *opaque, int ret) | |||||||
| } | } | ||||||
|  |  | ||||||
| static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, | static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, | ||||||
|                                 BlkMigDevState *bmds) |                                 BlkMigDevState *bmds, int is_async) | ||||||
| { | { | ||||||
|     int64_t total_sectors = bmds->total_sectors; |     int64_t total_sectors = bmds->total_sectors; | ||||||
|     int64_t cur_sector = bmds->cur_sector; |     int64_t cur_sector = bmds->cur_sector; | ||||||
| @@ -244,20 +174,27 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, | |||||||
|     blk->buf = qemu_malloc(BLOCK_SIZE); |     blk->buf = qemu_malloc(BLOCK_SIZE); | ||||||
|     blk->bmds = bmds; |     blk->bmds = bmds; | ||||||
|     blk->sector = cur_sector; |     blk->sector = cur_sector; | ||||||
|     blk->nr_sectors = nr_sectors; |  | ||||||
|  |  | ||||||
|     blk->iov.iov_base = blk->buf; |     if (is_async) { | ||||||
|     blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; |         blk->iov.iov_base = blk->buf; | ||||||
|     qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); |         blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; | ||||||
|  |         qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); | ||||||
|  |  | ||||||
|     blk->time = qemu_get_clock_ns(rt_clock); |         blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, | ||||||
|  |                                     nr_sectors, blk_mig_read_cb, blk); | ||||||
|  |         if (!blk->aiocb) { | ||||||
|  |             goto error; | ||||||
|  |         } | ||||||
|  |         block_mig_state.submitted++; | ||||||
|  |     } else { | ||||||
|  |         if (bdrv_read(bs, cur_sector, blk->buf, nr_sectors) < 0) { | ||||||
|  |             goto error; | ||||||
|  |         } | ||||||
|  |         blk_send(f, blk); | ||||||
|  |  | ||||||
|     blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, |         qemu_free(blk->buf); | ||||||
|                                 nr_sectors, blk_mig_read_cb, blk); |         qemu_free(blk); | ||||||
|     if (!blk->aiocb) { |  | ||||||
|         goto error; |  | ||||||
|     } |     } | ||||||
|     block_mig_state.submitted++; |  | ||||||
|  |  | ||||||
|     bdrv_reset_dirty(bs, cur_sector, nr_sectors); |     bdrv_reset_dirty(bs, cur_sector, nr_sectors); | ||||||
|     bmds->cur_sector = cur_sector + nr_sectors; |     bmds->cur_sector = cur_sector + nr_sectors; | ||||||
| @@ -281,58 +218,49 @@ static void set_dirty_tracking(int enable) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void init_blk_migration_it(void *opaque, BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     Monitor *mon = opaque; |  | ||||||
|     BlkMigDevState *bmds; |  | ||||||
|     int64_t sectors; |  | ||||||
|  |  | ||||||
|     if (!bdrv_is_read_only(bs)) { |  | ||||||
|         sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; |  | ||||||
|         if (sectors <= 0) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bmds = qemu_mallocz(sizeof(BlkMigDevState)); |  | ||||||
|         bmds->bs = bs; |  | ||||||
|         bmds->bulk_completed = 0; |  | ||||||
|         bmds->total_sectors = sectors; |  | ||||||
|         bmds->completed_sectors = 0; |  | ||||||
|         bmds->shared_base = block_mig_state.shared_base; |  | ||||||
|         alloc_aio_bitmap(bmds); |  | ||||||
|         drive_get_ref(drive_get_by_blockdev(bs)); |  | ||||||
|         bdrv_set_in_use(bs, 1); |  | ||||||
|  |  | ||||||
|         block_mig_state.total_sector_sum += sectors; |  | ||||||
|  |  | ||||||
|         if (bmds->shared_base) { |  | ||||||
|             monitor_printf(mon, "Start migration for %s with shared base " |  | ||||||
|                                 "image\n", |  | ||||||
|                            bs->device_name); |  | ||||||
|         } else { |  | ||||||
|             monitor_printf(mon, "Start full migration for %s\n", |  | ||||||
|                            bs->device_name); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void init_blk_migration(Monitor *mon, QEMUFile *f) | static void init_blk_migration(Monitor *mon, QEMUFile *f) | ||||||
| { | { | ||||||
|  |     BlkMigDevState *bmds; | ||||||
|  |     BlockDriverState *bs; | ||||||
|  |     int64_t sectors; | ||||||
|  |  | ||||||
|     block_mig_state.submitted = 0; |     block_mig_state.submitted = 0; | ||||||
|     block_mig_state.read_done = 0; |     block_mig_state.read_done = 0; | ||||||
|     block_mig_state.transferred = 0; |     block_mig_state.transferred = 0; | ||||||
|     block_mig_state.total_sector_sum = 0; |     block_mig_state.total_sector_sum = 0; | ||||||
|     block_mig_state.prev_progress = -1; |     block_mig_state.prev_progress = -1; | ||||||
|     block_mig_state.bulk_completed = 0; |  | ||||||
|     block_mig_state.total_time = 0; |  | ||||||
|     block_mig_state.reads = 0; |  | ||||||
|  |  | ||||||
|     bdrv_iterate(init_blk_migration_it, mon); |     for (bs = bdrv_first; bs != NULL; bs = bs->next) { | ||||||
|  |         if (bs->type == BDRV_TYPE_HD) { | ||||||
|  |             sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; | ||||||
|  |             if (sectors == 0) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             bmds = qemu_mallocz(sizeof(BlkMigDevState)); | ||||||
|  |             bmds->bs = bs; | ||||||
|  |             bmds->bulk_completed = 0; | ||||||
|  |             bmds->total_sectors = sectors; | ||||||
|  |             bmds->completed_sectors = 0; | ||||||
|  |             bmds->shared_base = block_mig_state.shared_base; | ||||||
|  |  | ||||||
|  |             block_mig_state.total_sector_sum += sectors; | ||||||
|  |  | ||||||
|  |             if (bmds->shared_base) { | ||||||
|  |                 monitor_printf(mon, "Start migration for %s with shared base " | ||||||
|  |                                     "image\n", | ||||||
|  |                                bs->device_name); | ||||||
|  |             } else { | ||||||
|  |                 monitor_printf(mon, "Start full migration for %s\n", | ||||||
|  |                                bs->device_name); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f, int is_async) | ||||||
| { | { | ||||||
|     int64_t completed_sector_sum = 0; |     int64_t completed_sector_sum = 0; | ||||||
|     BlkMigDevState *bmds; |     BlkMigDevState *bmds; | ||||||
| @@ -341,7 +269,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | |||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { |     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||||
|         if (bmds->bulk_completed == 0) { |         if (bmds->bulk_completed == 0) { | ||||||
|             if (mig_save_device_bulk(mon, f, bmds) == 1) { |             if (mig_save_device_bulk(mon, f, bmds, is_async) == 1) { | ||||||
|                 /* completed bulk section for this device */ |                 /* completed bulk section for this device */ | ||||||
|                 bmds->bulk_completed = 1; |                 bmds->bulk_completed = 1; | ||||||
|             } |             } | ||||||
| @@ -353,12 +281,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (block_mig_state.total_sector_sum != 0) { |     progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum; | ||||||
|         progress = completed_sector_sum * 100 / |  | ||||||
|                    block_mig_state.total_sector_sum; |  | ||||||
|     } else { |  | ||||||
|         progress = 100; |  | ||||||
|     } |  | ||||||
|     if (progress != block_mig_state.prev_progress) { |     if (progress != block_mig_state.prev_progress) { | ||||||
|         block_mig_state.prev_progress = progress; |         block_mig_state.prev_progress = progress; | ||||||
|         qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) |         qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) | ||||||
| @@ -370,102 +293,46 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_mig_reset_dirty_cursor(void) | #define MAX_NUM_BLOCKS 4 | ||||||
|  |  | ||||||
|  | static void blk_mig_save_dirty_blocks(Monitor *mon, QEMUFile *f) | ||||||
| { | { | ||||||
|     BlkMigDevState *bmds; |     BlkMigDevState *bmds; | ||||||
|  |     BlkMigBlock blk; | ||||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { |  | ||||||
|         bmds->cur_dirty = 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, |  | ||||||
|                                  BlkMigDevState *bmds, int is_async) |  | ||||||
| { |  | ||||||
|     BlkMigBlock *blk; |  | ||||||
|     int64_t total_sectors = bmds->total_sectors; |  | ||||||
|     int64_t sector; |     int64_t sector; | ||||||
|     int nr_sectors; |  | ||||||
|  |  | ||||||
|     for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { |     blk.buf = qemu_malloc(BLOCK_SIZE); | ||||||
|         if (bmds_aio_inflight(bmds, sector)) { |  | ||||||
|             qemu_aio_flush(); |  | ||||||
|         } |  | ||||||
|         if (bdrv_get_dirty(bmds->bs, sector)) { |  | ||||||
|  |  | ||||||
|             if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { |  | ||||||
|                 nr_sectors = total_sectors - sector; |  | ||||||
|             } else { |  | ||||||
|                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|             } |  | ||||||
|             blk = qemu_malloc(sizeof(BlkMigBlock)); |  | ||||||
|             blk->buf = qemu_malloc(BLOCK_SIZE); |  | ||||||
|             blk->bmds = bmds; |  | ||||||
|             blk->sector = sector; |  | ||||||
|             blk->nr_sectors = nr_sectors; |  | ||||||
|  |  | ||||||
|             if (is_async) { |  | ||||||
|                 blk->iov.iov_base = blk->buf; |  | ||||||
|                 blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; |  | ||||||
|                 qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); |  | ||||||
|  |  | ||||||
|                 blk->time = qemu_get_clock_ns(rt_clock); |  | ||||||
|  |  | ||||||
|                 blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov, |  | ||||||
|                                             nr_sectors, blk_mig_read_cb, blk); |  | ||||||
|                 if (!blk->aiocb) { |  | ||||||
|                     goto error; |  | ||||||
|                 } |  | ||||||
|                 block_mig_state.submitted++; |  | ||||||
|                 bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); |  | ||||||
|             } else { |  | ||||||
|                 if (bdrv_read(bmds->bs, sector, blk->buf, |  | ||||||
|                               nr_sectors) < 0) { |  | ||||||
|                     goto error; |  | ||||||
|                 } |  | ||||||
|                 blk_send(f, blk); |  | ||||||
|  |  | ||||||
|                 qemu_free(blk->buf); |  | ||||||
|                 qemu_free(blk); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             bdrv_reset_dirty(bmds->bs, sector, nr_sectors); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         sector += BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|         bmds->cur_dirty = sector; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return (bmds->cur_dirty >= bmds->total_sectors); |  | ||||||
|  |  | ||||||
| error: |  | ||||||
|     monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector); |  | ||||||
|     qemu_file_set_error(f); |  | ||||||
|     qemu_free(blk->buf); |  | ||||||
|     qemu_free(blk); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async) |  | ||||||
| { |  | ||||||
|     BlkMigDevState *bmds; |  | ||||||
|     int ret = 0; |  | ||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { |     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||||
|         if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) { |         for (sector = 0; sector < bmds->cur_sector;) { | ||||||
|             ret = 1; |             if (bdrv_get_dirty(bmds->bs, sector)) { | ||||||
|             break; |                 if (bdrv_read(bmds->bs, sector, blk.buf, | ||||||
|  |                               BDRV_SECTORS_PER_DIRTY_CHUNK) < 0) { | ||||||
|  |                     monitor_printf(mon, "Error reading sector %" PRId64 "\n", | ||||||
|  |                                    sector); | ||||||
|  |                     qemu_file_set_error(f); | ||||||
|  |                     qemu_free(blk.buf); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 blk.bmds = bmds; | ||||||
|  |                 blk.sector = sector; | ||||||
|  |                 blk_send(f, &blk); | ||||||
|  |  | ||||||
|  |                 bdrv_reset_dirty(bmds->bs, sector, | ||||||
|  |                                  BDRV_SECTORS_PER_DIRTY_CHUNK); | ||||||
|  |             } | ||||||
|  |             sector += BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ret; |     qemu_free(blk.buf); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void flush_blks(QEMUFile* f) | static void flush_blks(QEMUFile* f) | ||||||
| { | { | ||||||
|     BlkMigBlock *blk; |     BlkMigBlock *blk; | ||||||
|  |  | ||||||
|     DPRINTF("%s Enter submitted %d read_done %d transferred %d\n", |     dprintf("%s Enter submitted %d read_done %d transferred %d\n", | ||||||
|             __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, |             __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, | ||||||
|             block_mig_state.transferred); |             block_mig_state.transferred); | ||||||
|  |  | ||||||
| @@ -488,47 +355,26 @@ static void flush_blks(QEMUFile* f) | |||||||
|         assert(block_mig_state.read_done >= 0); |         assert(block_mig_state.read_done >= 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, |     dprintf("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, | ||||||
|             block_mig_state.submitted, block_mig_state.read_done, |             block_mig_state.submitted, block_mig_state.read_done, | ||||||
|             block_mig_state.transferred); |             block_mig_state.transferred); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t get_remaining_dirty(void) |  | ||||||
| { |  | ||||||
|     BlkMigDevState *bmds; |  | ||||||
|     int64_t dirty = 0; |  | ||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { |  | ||||||
|         dirty += bdrv_get_dirty_count(bmds->bs); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return dirty * BLOCK_SIZE; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int is_stage2_completed(void) | static int is_stage2_completed(void) | ||||||
| { | { | ||||||
|     int64_t remaining_dirty; |     BlkMigDevState *bmds; | ||||||
|     long double bwidth; |  | ||||||
|  |  | ||||||
|     if (block_mig_state.bulk_completed == 1) { |     if (block_mig_state.submitted > 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|         remaining_dirty = get_remaining_dirty(); |     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||||
|         if (remaining_dirty == 0) { |         if (bmds->bulk_completed == 0) { | ||||||
|             return 1; |             return 0; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bwidth = compute_read_bwidth(); |  | ||||||
|  |  | ||||||
|         if ((remaining_dirty / bwidth) <= |  | ||||||
|             migrate_max_downtime()) { |  | ||||||
|             /* finish stage2 because we think that we can finish remaing work |  | ||||||
|                below max_downtime */ |  | ||||||
|  |  | ||||||
|             return 1; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_mig_cleanup(Monitor *mon) | static void blk_mig_cleanup(Monitor *mon) | ||||||
| @@ -536,13 +382,8 @@ static void blk_mig_cleanup(Monitor *mon) | |||||||
|     BlkMigDevState *bmds; |     BlkMigDevState *bmds; | ||||||
|     BlkMigBlock *blk; |     BlkMigBlock *blk; | ||||||
|  |  | ||||||
|     set_dirty_tracking(0); |  | ||||||
|  |  | ||||||
|     while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { |     while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { | ||||||
|         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); |         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); | ||||||
|         bdrv_set_in_use(bmds->bs, 0); |  | ||||||
|         drive_put_ref(drive_get_by_blockdev(bmds->bs)); |  | ||||||
|         qemu_free(bmds->aio_bitmap); |  | ||||||
|         qemu_free(bmds); |         qemu_free(bmds); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -552,12 +393,14 @@ static void blk_mig_cleanup(Monitor *mon) | |||||||
|         qemu_free(blk); |         qemu_free(blk); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     set_dirty_tracking(0); | ||||||
|  |  | ||||||
|     monitor_printf(mon, "\n"); |     monitor_printf(mon, "\n"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | ||||||
| { | { | ||||||
|     DPRINTF("Enter save live stage %d submitted %d transferred %d\n", |     dprintf("Enter save live stage %d submitted %d transferred %d\n", | ||||||
|             stage, block_mig_state.submitted, block_mig_state.transferred); |             stage, block_mig_state.submitted, block_mig_state.transferred); | ||||||
|  |  | ||||||
|     if (stage < 0) { |     if (stage < 0) { | ||||||
| @@ -585,41 +428,29 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     blk_mig_reset_dirty_cursor(); |     /* control the rate of transfer */ | ||||||
|  |     while ((block_mig_state.submitted + | ||||||
|     if (stage == 2) { |             block_mig_state.read_done) * BLOCK_SIZE < | ||||||
|         /* control the rate of transfer */ |            qemu_file_get_rate_limit(f)) { | ||||||
|         while ((block_mig_state.submitted + |         if (blk_mig_save_bulked_block(mon, f, 1) == 0) { | ||||||
|                 block_mig_state.read_done) * BLOCK_SIZE < |             /* no more bulk blocks for now */ | ||||||
|                qemu_file_get_rate_limit(f)) { |             break; | ||||||
|             if (block_mig_state.bulk_completed == 0) { |  | ||||||
|                 /* first finish the bulk phase */ |  | ||||||
|                 if (blk_mig_save_bulked_block(mon, f) == 0) { |  | ||||||
|                     /* finished saving bulk on all devices */ |  | ||||||
|                     block_mig_state.bulk_completed = 1; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 if (blk_mig_save_dirty_block(mon, f, 1) == 0) { |  | ||||||
|                     /* no more dirty blocks */ |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         flush_blks(f); |  | ||||||
|  |  | ||||||
|         if (qemu_file_has_error(f)) { |  | ||||||
|             blk_mig_cleanup(mon); |  | ||||||
|             return 0; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (stage == 3) { |     flush_blks(f); | ||||||
|         /* we know for sure that save bulk is completed and |  | ||||||
|            all async read completed */ |  | ||||||
|         assert(block_mig_state.submitted == 0); |  | ||||||
|  |  | ||||||
|         while (blk_mig_save_dirty_block(mon, f, 0) != 0); |     if (qemu_file_has_error(f)) { | ||||||
|  |         blk_mig_cleanup(mon); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (stage == 3) { | ||||||
|  |         while (blk_mig_save_bulked_block(mon, f, 0) != 0) { | ||||||
|  |             /* empty */ | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         blk_mig_save_dirty_blocks(mon, f); | ||||||
|         blk_mig_cleanup(mon); |         blk_mig_cleanup(mon); | ||||||
|  |  | ||||||
|         /* report completion */ |         /* report completion */ | ||||||
| @@ -643,10 +474,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | |||||||
|     int len, flags; |     int len, flags; | ||||||
|     char device_name[256]; |     char device_name[256]; | ||||||
|     int64_t addr; |     int64_t addr; | ||||||
|     BlockDriverState *bs, *bs_prev = NULL; |     BlockDriverState *bs; | ||||||
|     uint8_t *buf; |     uint8_t *buf; | ||||||
|     int64_t total_sectors = 0; |  | ||||||
|     int nr_sectors; |  | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|         addr = qemu_get_be64(f); |         addr = qemu_get_be64(f); | ||||||
| @@ -655,7 +484,6 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | |||||||
|         addr >>= BDRV_SECTOR_BITS; |         addr >>= BDRV_SECTOR_BITS; | ||||||
|  |  | ||||||
|         if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { |         if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { | ||||||
|             int ret; |  | ||||||
|             /* get device name */ |             /* get device name */ | ||||||
|             len = qemu_get_byte(f); |             len = qemu_get_byte(f); | ||||||
|             qemu_get_buffer(f, (uint8_t *)device_name, len); |             qemu_get_buffer(f, (uint8_t *)device_name, len); | ||||||
| @@ -668,31 +496,12 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | |||||||
|                 return -EINVAL; |                 return -EINVAL; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (bs != bs_prev) { |  | ||||||
|                 bs_prev = bs; |  | ||||||
|                 total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; |  | ||||||
|                 if (total_sectors <= 0) { |  | ||||||
|                     error_report("Error getting length of block device %s\n", |  | ||||||
|                                  device_name); |  | ||||||
|                     return -EINVAL; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { |  | ||||||
|                 nr_sectors = total_sectors - addr; |  | ||||||
|             } else { |  | ||||||
|                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             buf = qemu_malloc(BLOCK_SIZE); |             buf = qemu_malloc(BLOCK_SIZE); | ||||||
|  |  | ||||||
|             qemu_get_buffer(f, buf, BLOCK_SIZE); |             qemu_get_buffer(f, buf, BLOCK_SIZE); | ||||||
|             ret = bdrv_write(bs, addr, buf, nr_sectors); |             bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK); | ||||||
|  |  | ||||||
|             qemu_free(buf); |             qemu_free(buf); | ||||||
|             if (ret < 0) { |  | ||||||
|                 return ret; |  | ||||||
|             } |  | ||||||
|         } else if (flags & BLK_MIG_FLAG_PROGRESS) { |         } else if (flags & BLK_MIG_FLAG_PROGRESS) { | ||||||
|             if (!banner_printed) { |             if (!banner_printed) { | ||||||
|                 printf("Receiving block device images\n"); |                 printf("Receiving block device images\n"); | ||||||
| @@ -727,6 +536,6 @@ void blk_mig_init(void) | |||||||
|     QSIMPLEQ_INIT(&block_mig_state.bmds_list); |     QSIMPLEQ_INIT(&block_mig_state.bmds_list); | ||||||
|     QSIMPLEQ_INIT(&block_mig_state.blk_list); |     QSIMPLEQ_INIT(&block_mig_state.blk_list); | ||||||
|  |  | ||||||
|     register_savevm_live(NULL, "block", 0, 1, block_set_params, |     register_savevm_live("block", 0, 1, block_set_params, block_save_live, | ||||||
|                          block_save_live, NULL, block_load, &block_mig_state); |                          NULL, block_load, &block_mig_state); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										133
									
								
								block.h
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								block.h
									
									
									
									
									
								
							| @@ -27,31 +27,25 @@ typedef struct QEMUSnapshotInfo { | |||||||
|     uint64_t vm_clock_nsec; /* VM clock relative to boot */ |     uint64_t vm_clock_nsec; /* VM clock relative to boot */ | ||||||
| } QEMUSnapshotInfo; | } QEMUSnapshotInfo; | ||||||
|  |  | ||||||
|  | #define BDRV_O_RDONLY      0x0000 | ||||||
| #define BDRV_O_RDWR        0x0002 | #define BDRV_O_RDWR        0x0002 | ||||||
|  | #define BDRV_O_ACCESS      0x0003 | ||||||
|  | #define BDRV_O_CREAT       0x0004 /* create an empty file */ | ||||||
| #define BDRV_O_SNAPSHOT    0x0008 /* open the file read only and save writes in a snapshot */ | #define BDRV_O_SNAPSHOT    0x0008 /* open the file read only and save writes in a snapshot */ | ||||||
|  | #define BDRV_O_FILE        0x0010 /* open as a raw file (do not try to | ||||||
|  |                                      use a disk image format on top of | ||||||
|  |                                      it (default for | ||||||
|  |                                      bdrv_file_open()) */ | ||||||
| #define BDRV_O_NOCACHE     0x0020 /* do not use the host page cache */ | #define BDRV_O_NOCACHE     0x0020 /* do not use the host page cache */ | ||||||
| #define BDRV_O_CACHE_WB    0x0040 /* use write-back caching */ | #define BDRV_O_CACHE_WB    0x0040 /* use write-back caching */ | ||||||
| #define BDRV_O_NATIVE_AIO  0x0080 /* use native AIO instead of the thread pool */ | #define BDRV_O_NATIVE_AIO  0x0080 /* use native AIO instead of the thread pool */ | ||||||
| #define BDRV_O_NO_BACKING  0x0100 /* don't open the backing file */ |  | ||||||
| #define BDRV_O_NO_FLUSH    0x0200 /* disable flushing on this disk */ |  | ||||||
|  |  | ||||||
| #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) | #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB) | ||||||
|  |  | ||||||
| #define BDRV_SECTOR_BITS   9 | #define BDRV_SECTOR_BITS   9 | ||||||
| #define BDRV_SECTOR_SIZE   (1ULL << BDRV_SECTOR_BITS) | #define BDRV_SECTOR_SIZE   (1 << BDRV_SECTOR_BITS) | ||||||
| #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1) | #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1); | ||||||
|  |  | ||||||
| typedef enum { |  | ||||||
|     BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC, |  | ||||||
|     BLOCK_ERR_STOP_ANY |  | ||||||
| } BlockErrorAction; |  | ||||||
|  |  | ||||||
| typedef enum { |  | ||||||
|     BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP |  | ||||||
| } BlockMonEventAction; |  | ||||||
|  |  | ||||||
| void bdrv_mon_event(const BlockDriverState *bdrv, |  | ||||||
|                     BlockMonEventAction action, int is_read); |  | ||||||
| void bdrv_info_print(Monitor *mon, const QObject *data); | void bdrv_info_print(Monitor *mon, const QObject *data); | ||||||
| void bdrv_info(Monitor *mon, QObject **ret_data); | void bdrv_info(Monitor *mon, QObject **ret_data); | ||||||
| void bdrv_stats_print(Monitor *mon, const QObject *data); | void bdrv_stats_print(Monitor *mon, const QObject *data); | ||||||
| @@ -59,22 +53,22 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data); | |||||||
|  |  | ||||||
| void bdrv_init(void); | void bdrv_init(void); | ||||||
| void bdrv_init_with_whitelist(void); | void bdrv_init_with_whitelist(void); | ||||||
| BlockDriver *bdrv_find_protocol(const char *filename); |  | ||||||
| BlockDriver *bdrv_find_format(const char *format_name); | BlockDriver *bdrv_find_format(const char *format_name); | ||||||
| BlockDriver *bdrv_find_whitelisted_format(const char *format_name); | BlockDriver *bdrv_find_whitelisted_format(const char *format_name); | ||||||
| int bdrv_create(BlockDriver *drv, const char* filename, | int bdrv_create(BlockDriver *drv, const char* filename, | ||||||
|     QEMUOptionParameter *options); |     QEMUOptionParameter *options); | ||||||
| int bdrv_create_file(const char* filename, QEMUOptionParameter *options); | int bdrv_create2(BlockDriver *drv, | ||||||
|  |                  const char *filename, int64_t size_in_sectors, | ||||||
|  |                  const char *backing_file, const char *backing_format, | ||||||
|  |                  int flags); | ||||||
| BlockDriverState *bdrv_new(const char *device_name); | BlockDriverState *bdrv_new(const char *device_name); | ||||||
| void bdrv_make_anon(BlockDriverState *bs); |  | ||||||
| void bdrv_delete(BlockDriverState *bs); | void bdrv_delete(BlockDriverState *bs); | ||||||
| int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); | int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); | ||||||
| int bdrv_open(BlockDriverState *bs, const char *filename, int flags, | int bdrv_open(BlockDriverState *bs, const char *filename, int flags); | ||||||
|               BlockDriver *drv); | int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, | ||||||
|  |                BlockDriver *drv); | ||||||
| void bdrv_close(BlockDriverState *bs); | void bdrv_close(BlockDriverState *bs); | ||||||
| int bdrv_attach(BlockDriverState *bs, DeviceState *qdev); | int bdrv_check(BlockDriverState *bs); | ||||||
| void bdrv_detach(BlockDriverState *bs, DeviceState *qdev); |  | ||||||
| DeviceState *bdrv_get_attached(BlockDriverState *bs); |  | ||||||
| int bdrv_read(BlockDriverState *bs, int64_t sector_num, | int bdrv_read(BlockDriverState *bs, int64_t sector_num, | ||||||
|               uint8_t *buf, int nb_sectors); |               uint8_t *buf, int nb_sectors); | ||||||
| int bdrv_write(BlockDriverState *bs, int64_t sector_num, | int bdrv_write(BlockDriverState *bs, int64_t sector_num, | ||||||
| @@ -92,20 +86,8 @@ int64_t bdrv_getlength(BlockDriverState *bs); | |||||||
| void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); | void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); | ||||||
| void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); | void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); | ||||||
| int bdrv_commit(BlockDriverState *bs); | int bdrv_commit(BlockDriverState *bs); | ||||||
| void bdrv_commit_all(void); |  | ||||||
| int bdrv_change_backing_file(BlockDriverState *bs, |  | ||||||
|     const char *backing_file, const char *backing_fmt); |  | ||||||
| void bdrv_register(BlockDriver *bdrv); | void bdrv_register(BlockDriver *bdrv); | ||||||
|  |  | ||||||
|  |  | ||||||
| typedef struct BdrvCheckResult { |  | ||||||
|     int corruptions; |  | ||||||
|     int leaks; |  | ||||||
|     int check_errors; |  | ||||||
| } BdrvCheckResult; |  | ||||||
|  |  | ||||||
| int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res); |  | ||||||
|  |  | ||||||
| /* async block I/O */ | /* async block I/O */ | ||||||
| typedef struct BlockDriverAIOCB BlockDriverAIOCB; | typedef struct BlockDriverAIOCB BlockDriverAIOCB; | ||||||
| typedef void BlockDriverCompletionFunc(void *opaque, int ret); | typedef void BlockDriverCompletionFunc(void *opaque, int ret); | ||||||
| @@ -143,12 +125,9 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, | |||||||
|         BlockDriverCompletionFunc *cb, void *opaque); |         BlockDriverCompletionFunc *cb, void *opaque); | ||||||
|  |  | ||||||
| /* Ensure contents are flushed to disk.  */ | /* Ensure contents are flushed to disk.  */ | ||||||
| int bdrv_flush(BlockDriverState *bs); | void bdrv_flush(BlockDriverState *bs); | ||||||
| void bdrv_flush_all(void); | void bdrv_flush_all(void); | ||||||
| void bdrv_close_all(void); |  | ||||||
|  |  | ||||||
| int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); |  | ||||||
| int bdrv_has_zero_init(BlockDriverState *bs); |  | ||||||
| int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||||
| 	int *pnum); | 	int *pnum); | ||||||
|  |  | ||||||
| @@ -169,12 +148,9 @@ void bdrv_get_geometry_hint(BlockDriverState *bs, | |||||||
|                             int *pcyls, int *pheads, int *psecs); |                             int *pcyls, int *pheads, int *psecs); | ||||||
| int bdrv_get_type_hint(BlockDriverState *bs); | int bdrv_get_type_hint(BlockDriverState *bs); | ||||||
| int bdrv_get_translation_hint(BlockDriverState *bs); | int bdrv_get_translation_hint(BlockDriverState *bs); | ||||||
| void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, |  | ||||||
|                        BlockErrorAction on_write_error); |  | ||||||
| BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read); |  | ||||||
| void bdrv_set_removable(BlockDriverState *bs, int removable); |  | ||||||
| int bdrv_is_removable(BlockDriverState *bs); | int bdrv_is_removable(BlockDriverState *bs); | ||||||
| int bdrv_is_read_only(BlockDriverState *bs); | int bdrv_is_read_only(BlockDriverState *bs); | ||||||
|  | int bdrv_set_read_only(BlockDriverState *bs, int read_only); | ||||||
| int bdrv_is_sg(BlockDriverState *bs); | int bdrv_is_sg(BlockDriverState *bs); | ||||||
| int bdrv_enable_write_cache(BlockDriverState *bs); | int bdrv_enable_write_cache(BlockDriverState *bs); | ||||||
| int bdrv_is_inserted(BlockDriverState *bs); | int bdrv_is_inserted(BlockDriverState *bs); | ||||||
| @@ -183,11 +159,9 @@ int bdrv_is_locked(BlockDriverState *bs); | |||||||
| void bdrv_set_locked(BlockDriverState *bs, int locked); | void bdrv_set_locked(BlockDriverState *bs, int locked); | ||||||
| int bdrv_eject(BlockDriverState *bs, int eject_flag); | int bdrv_eject(BlockDriverState *bs, int eject_flag); | ||||||
| void bdrv_set_change_cb(BlockDriverState *bs, | void bdrv_set_change_cb(BlockDriverState *bs, | ||||||
|                         void (*change_cb)(void *opaque, int reason), |                         void (*change_cb)(void *opaque), void *opaque); | ||||||
|                         void *opaque); |  | ||||||
| void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); | void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); | ||||||
| BlockDriverState *bdrv_find(const char *name); | BlockDriverState *bdrv_find(const char *name); | ||||||
| BlockDriverState *bdrv_next(BlockDriverState *bs); |  | ||||||
| void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), | void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), | ||||||
|                   void *opaque); |                   void *opaque); | ||||||
| int bdrv_is_encrypted(BlockDriverState *bs); | int bdrv_is_encrypted(BlockDriverState *bs); | ||||||
| @@ -204,9 +178,6 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); | |||||||
| const char *bdrv_get_encrypted_filename(BlockDriverState *bs); | const char *bdrv_get_encrypted_filename(BlockDriverState *bs); | ||||||
| void bdrv_get_backing_filename(BlockDriverState *bs, | void bdrv_get_backing_filename(BlockDriverState *bs, | ||||||
|                                char *filename, int filename_size); |                                char *filename, int filename_size); | ||||||
| int bdrv_can_snapshot(BlockDriverState *bs); |  | ||||||
| int bdrv_is_snapshot(BlockDriverState *bs); |  | ||||||
| BlockDriverState *bdrv_snapshots(void); |  | ||||||
| int bdrv_snapshot_create(BlockDriverState *bs, | int bdrv_snapshot_create(BlockDriverState *bs, | ||||||
|                          QEMUSnapshotInfo *sn_info); |                          QEMUSnapshotInfo *sn_info); | ||||||
| int bdrv_snapshot_goto(BlockDriverState *bs, | int bdrv_snapshot_goto(BlockDriverState *bs, | ||||||
| @@ -214,8 +185,6 @@ int bdrv_snapshot_goto(BlockDriverState *bs, | |||||||
| int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | ||||||
| int bdrv_snapshot_list(BlockDriverState *bs, | int bdrv_snapshot_list(BlockDriverState *bs, | ||||||
|                        QEMUSnapshotInfo **psn_info); |                        QEMUSnapshotInfo **psn_info); | ||||||
| int bdrv_snapshot_load_tmp(BlockDriverState *bs, |  | ||||||
|                            const char *snapshot_name); |  | ||||||
| char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); | char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); | ||||||
|  |  | ||||||
| char *get_human_readable_size(char *buf, int buf_size, int64_t size); | char *get_human_readable_size(char *buf, int buf_size, int64_t size); | ||||||
| @@ -230,70 +199,10 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, | |||||||
| int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, | int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, | ||||||
|                       int64_t pos, int size); |                       int64_t pos, int size); | ||||||
|  |  | ||||||
| int bdrv_img_create(const char *filename, const char *fmt, |  | ||||||
|                     const char *base_filename, const char *base_fmt, |  | ||||||
|                     char *options, uint64_t img_size, int flags); |  | ||||||
|  |  | ||||||
| #define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 | #define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 | ||||||
|  |  | ||||||
| void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); | void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); | ||||||
| int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); | int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); | ||||||
| void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, | void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, | ||||||
|                       int nr_sectors); |                       int nr_sectors); | ||||||
| int64_t bdrv_get_dirty_count(BlockDriverState *bs); |  | ||||||
|  |  | ||||||
| void bdrv_set_in_use(BlockDriverState *bs, int in_use); |  | ||||||
| int bdrv_in_use(BlockDriverState *bs); |  | ||||||
|  |  | ||||||
| typedef enum { |  | ||||||
|     BLKDBG_L1_UPDATE, |  | ||||||
|  |  | ||||||
|     BLKDBG_L1_GROW_ALLOC_TABLE, |  | ||||||
|     BLKDBG_L1_GROW_WRITE_TABLE, |  | ||||||
|     BLKDBG_L1_GROW_ACTIVATE_TABLE, |  | ||||||
|  |  | ||||||
|     BLKDBG_L2_LOAD, |  | ||||||
|     BLKDBG_L2_UPDATE, |  | ||||||
|     BLKDBG_L2_UPDATE_COMPRESSED, |  | ||||||
|     BLKDBG_L2_ALLOC_COW_READ, |  | ||||||
|     BLKDBG_L2_ALLOC_WRITE, |  | ||||||
|  |  | ||||||
|     BLKDBG_READ, |  | ||||||
|     BLKDBG_READ_AIO, |  | ||||||
|     BLKDBG_READ_BACKING, |  | ||||||
|     BLKDBG_READ_BACKING_AIO, |  | ||||||
|     BLKDBG_READ_COMPRESSED, |  | ||||||
|  |  | ||||||
|     BLKDBG_WRITE_AIO, |  | ||||||
|     BLKDBG_WRITE_COMPRESSED, |  | ||||||
|  |  | ||||||
|     BLKDBG_VMSTATE_LOAD, |  | ||||||
|     BLKDBG_VMSTATE_SAVE, |  | ||||||
|  |  | ||||||
|     BLKDBG_COW_READ, |  | ||||||
|     BLKDBG_COW_WRITE, |  | ||||||
|  |  | ||||||
|     BLKDBG_REFTABLE_LOAD, |  | ||||||
|     BLKDBG_REFTABLE_GROW, |  | ||||||
|  |  | ||||||
|     BLKDBG_REFBLOCK_LOAD, |  | ||||||
|     BLKDBG_REFBLOCK_UPDATE, |  | ||||||
|     BLKDBG_REFBLOCK_UPDATE_PART, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC_HOOKUP, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, |  | ||||||
|     BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, |  | ||||||
|  |  | ||||||
|     BLKDBG_CLUSTER_ALLOC, |  | ||||||
|     BLKDBG_CLUSTER_ALLOC_BYTES, |  | ||||||
|     BLKDBG_CLUSTER_FREE, |  | ||||||
|  |  | ||||||
|     BLKDBG_EVENT_MAX, |  | ||||||
| } BlkDebugEvent; |  | ||||||
|  |  | ||||||
| #define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) |  | ||||||
| void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										471
									
								
								block/blkdebug.c
									
									
									
									
									
								
							
							
						
						
									
										471
									
								
								block/blkdebug.c
									
									
									
									
									
								
							| @@ -1,471 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Block protocol for I/O error injection |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "block_int.h" |  | ||||||
| #include "module.h" |  | ||||||
|  |  | ||||||
| typedef struct BlkdebugVars { |  | ||||||
|     int state; |  | ||||||
|  |  | ||||||
|     /* If inject_errno != 0, an error is injected for requests */ |  | ||||||
|     int inject_errno; |  | ||||||
|  |  | ||||||
|     /* Decides if all future requests fail (false) or only the next one and |  | ||||||
|      * after the next request inject_errno is reset to 0 (true) */ |  | ||||||
|     bool inject_once; |  | ||||||
|  |  | ||||||
|     /* Decides if aio_readv/writev fails right away (true) or returns an error |  | ||||||
|      * return value only in the callback (false) */ |  | ||||||
|     bool inject_immediately; |  | ||||||
| } BlkdebugVars; |  | ||||||
|  |  | ||||||
| typedef struct BDRVBlkdebugState { |  | ||||||
|     BlkdebugVars vars; |  | ||||||
|     QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; |  | ||||||
| } BDRVBlkdebugState; |  | ||||||
|  |  | ||||||
| typedef struct BlkdebugAIOCB { |  | ||||||
|     BlockDriverAIOCB common; |  | ||||||
|     QEMUBH *bh; |  | ||||||
|     int ret; |  | ||||||
| } BlkdebugAIOCB; |  | ||||||
|  |  | ||||||
| static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb); |  | ||||||
|  |  | ||||||
| static AIOPool blkdebug_aio_pool = { |  | ||||||
|     .aiocb_size = sizeof(BlkdebugAIOCB), |  | ||||||
|     .cancel     = blkdebug_aio_cancel, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     ACTION_INJECT_ERROR, |  | ||||||
|     ACTION_SET_STATE, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| typedef struct BlkdebugRule { |  | ||||||
|     BlkDebugEvent event; |  | ||||||
|     int action; |  | ||||||
|     int state; |  | ||||||
|     union { |  | ||||||
|         struct { |  | ||||||
|             int error; |  | ||||||
|             int immediately; |  | ||||||
|             int once; |  | ||||||
|         } inject; |  | ||||||
|         struct { |  | ||||||
|             int new_state; |  | ||||||
|         } set_state; |  | ||||||
|     } options; |  | ||||||
|     QLIST_ENTRY(BlkdebugRule) next; |  | ||||||
| } BlkdebugRule; |  | ||||||
|  |  | ||||||
| static QemuOptsList inject_error_opts = { |  | ||||||
|     .name = "inject-error", |  | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), |  | ||||||
|     .desc = { |  | ||||||
|         { |  | ||||||
|             .name = "event", |  | ||||||
|             .type = QEMU_OPT_STRING, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "state", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "errno", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "once", |  | ||||||
|             .type = QEMU_OPT_BOOL, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "immediately", |  | ||||||
|             .type = QEMU_OPT_BOOL, |  | ||||||
|         }, |  | ||||||
|         { /* end of list */ } |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static QemuOptsList set_state_opts = { |  | ||||||
|     .name = "set-state", |  | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), |  | ||||||
|     .desc = { |  | ||||||
|         { |  | ||||||
|             .name = "event", |  | ||||||
|             .type = QEMU_OPT_STRING, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "state", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "new_state", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|         }, |  | ||||||
|         { /* end of list */ } |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static QemuOptsList *config_groups[] = { |  | ||||||
|     &inject_error_opts, |  | ||||||
|     &set_state_opts, |  | ||||||
|     NULL |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const char *event_names[BLKDBG_EVENT_MAX] = { |  | ||||||
|     [BLKDBG_L1_UPDATE]                      = "l1_update", |  | ||||||
|     [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table", |  | ||||||
|     [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table", |  | ||||||
|     [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table", |  | ||||||
|  |  | ||||||
|     [BLKDBG_L2_LOAD]                        = "l2_load", |  | ||||||
|     [BLKDBG_L2_UPDATE]                      = "l2_update", |  | ||||||
|     [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed", |  | ||||||
|     [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read", |  | ||||||
|     [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write", |  | ||||||
|  |  | ||||||
|     [BLKDBG_READ]                           = "read", |  | ||||||
|     [BLKDBG_READ_AIO]                       = "read_aio", |  | ||||||
|     [BLKDBG_READ_BACKING]                   = "read_backing", |  | ||||||
|     [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio", |  | ||||||
|     [BLKDBG_READ_COMPRESSED]                = "read_compressed", |  | ||||||
|  |  | ||||||
|     [BLKDBG_WRITE_AIO]                      = "write_aio", |  | ||||||
|     [BLKDBG_WRITE_COMPRESSED]               = "write_compressed", |  | ||||||
|  |  | ||||||
|     [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load", |  | ||||||
|     [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save", |  | ||||||
|  |  | ||||||
|     [BLKDBG_COW_READ]                       = "cow_read", |  | ||||||
|     [BLKDBG_COW_WRITE]                      = "cow_write", |  | ||||||
|  |  | ||||||
|     [BLKDBG_REFTABLE_LOAD]                  = "reftable_load", |  | ||||||
|     [BLKDBG_REFTABLE_GROW]                  = "reftable_grow", |  | ||||||
|  |  | ||||||
|     [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load", |  | ||||||
|     [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update", |  | ||||||
|     [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table", |  | ||||||
|     [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table", |  | ||||||
|  |  | ||||||
|     [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc", |  | ||||||
|     [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes", |  | ||||||
|     [BLKDBG_CLUSTER_FREE]                   = "cluster_free", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int get_event_by_name(const char *name, BlkDebugEvent *event) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { |  | ||||||
|         if (!strcmp(event_names[i], name)) { |  | ||||||
|             *event = i; |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct add_rule_data { |  | ||||||
|     BDRVBlkdebugState *s; |  | ||||||
|     int action; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int add_rule(QemuOpts *opts, void *opaque) |  | ||||||
| { |  | ||||||
|     struct add_rule_data *d = opaque; |  | ||||||
|     BDRVBlkdebugState *s = d->s; |  | ||||||
|     const char* event_name; |  | ||||||
|     BlkDebugEvent event; |  | ||||||
|     struct BlkdebugRule *rule; |  | ||||||
|  |  | ||||||
|     /* Find the right event for the rule */ |  | ||||||
|     event_name = qemu_opt_get(opts, "event"); |  | ||||||
|     if (!event_name || get_event_by_name(event_name, &event) < 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Set attributes common for all actions */ |  | ||||||
|     rule = qemu_mallocz(sizeof(*rule)); |  | ||||||
|     *rule = (struct BlkdebugRule) { |  | ||||||
|         .event  = event, |  | ||||||
|         .action = d->action, |  | ||||||
|         .state  = qemu_opt_get_number(opts, "state", 0), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /* Parse action-specific options */ |  | ||||||
|     switch (d->action) { |  | ||||||
|     case ACTION_INJECT_ERROR: |  | ||||||
|         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); |  | ||||||
|         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0); |  | ||||||
|         rule->options.inject.immediately = |  | ||||||
|             qemu_opt_get_bool(opts, "immediately", 0); |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case ACTION_SET_STATE: |  | ||||||
|         rule->options.set_state.new_state = |  | ||||||
|             qemu_opt_get_number(opts, "new_state", 0); |  | ||||||
|         break; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /* Add the rule */ |  | ||||||
|     QLIST_INSERT_HEAD(&s->rules[event], rule, next); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int read_config(BDRVBlkdebugState *s, const char *filename) |  | ||||||
| { |  | ||||||
|     FILE *f; |  | ||||||
|     int ret; |  | ||||||
|     struct add_rule_data d; |  | ||||||
|  |  | ||||||
|     f = fopen(filename, "r"); |  | ||||||
|     if (f == NULL) { |  | ||||||
|         return -errno; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qemu_config_parse(f, config_groups, filename); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     d.s = s; |  | ||||||
|     d.action = ACTION_INJECT_ERROR; |  | ||||||
|     qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0); |  | ||||||
|  |  | ||||||
|     d.action = ACTION_SET_STATE; |  | ||||||
|     qemu_opts_foreach(&set_state_opts, add_rule, &d, 0); |  | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| fail: |  | ||||||
|     qemu_opts_reset(&inject_error_opts); |  | ||||||
|     qemu_opts_reset(&set_state_opts); |  | ||||||
|     fclose(f); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ |  | ||||||
| static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|     int ret; |  | ||||||
|     char *config, *c; |  | ||||||
|  |  | ||||||
|     /* Parse the blkdebug: prefix */ |  | ||||||
|     if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|     filename += strlen("blkdebug:"); |  | ||||||
|  |  | ||||||
|     /* Read rules from config file */ |  | ||||||
|     c = strchr(filename, ':'); |  | ||||||
|     if (c == NULL) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     config = strdup(filename); |  | ||||||
|     config[c - filename] = '\0'; |  | ||||||
|     ret = read_config(s, config); |  | ||||||
|     free(config); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|     filename = c + 1; |  | ||||||
|  |  | ||||||
|     /* Set initial state */ |  | ||||||
|     s->vars.state = 1; |  | ||||||
|  |  | ||||||
|     /* Open the backing file */ |  | ||||||
|     ret = bdrv_file_open(&bs->file, filename, flags); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void error_callback_bh(void *opaque) |  | ||||||
| { |  | ||||||
|     struct BlkdebugAIOCB *acb = opaque; |  | ||||||
|     qemu_bh_delete(acb->bh); |  | ||||||
|     acb->common.cb(acb->common.opaque, acb->ret); |  | ||||||
|     qemu_aio_release(acb); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb) |  | ||||||
| { |  | ||||||
|     BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common); |  | ||||||
|     qemu_aio_release(acb); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *inject_error(BlockDriverState *bs, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|     int error = s->vars.inject_errno; |  | ||||||
|     struct BlkdebugAIOCB *acb; |  | ||||||
|     QEMUBH *bh; |  | ||||||
|  |  | ||||||
|     if (s->vars.inject_once) { |  | ||||||
|         s->vars.inject_errno = 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (s->vars.inject_immediately) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque); |  | ||||||
|     acb->ret = -error; |  | ||||||
|  |  | ||||||
|     bh = qemu_bh_new(error_callback_bh, acb); |  | ||||||
|     acb->bh = bh; |  | ||||||
|     qemu_bh_schedule(bh); |  | ||||||
|  |  | ||||||
|     return &acb->common; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs, |  | ||||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     if (s->vars.inject_errno) { |  | ||||||
|         return inject_error(bs, cb, opaque); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     BlockDriverAIOCB *acb = |  | ||||||
|         bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); |  | ||||||
|     return acb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, |  | ||||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     if (s->vars.inject_errno) { |  | ||||||
|         return inject_error(bs, cb, opaque); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     BlockDriverAIOCB *acb = |  | ||||||
|         bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); |  | ||||||
|     return acb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkdebug_close(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|     BlkdebugRule *rule, *next; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { |  | ||||||
|         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { |  | ||||||
|             QLIST_REMOVE(rule, next); |  | ||||||
|             qemu_free(rule); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int blkdebug_flush(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_flush(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     return bdrv_aio_flush(bs->file, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, |  | ||||||
|     BlkdebugVars *old_vars) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|     BlkdebugVars *vars = &s->vars; |  | ||||||
|  |  | ||||||
|     /* Only process rules for the current state */ |  | ||||||
|     if (rule->state && rule->state != old_vars->state) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Take the action */ |  | ||||||
|     switch (rule->action) { |  | ||||||
|     case ACTION_INJECT_ERROR: |  | ||||||
|         vars->inject_errno       = rule->options.inject.error; |  | ||||||
|         vars->inject_once        = rule->options.inject.once; |  | ||||||
|         vars->inject_immediately = rule->options.inject.immediately; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case ACTION_SET_STATE: |  | ||||||
|         vars->state              = rule->options.set_state.new_state; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) |  | ||||||
| { |  | ||||||
|     BDRVBlkdebugState *s = bs->opaque; |  | ||||||
|     struct BlkdebugRule *rule; |  | ||||||
|     BlkdebugVars old_vars = s->vars; |  | ||||||
|  |  | ||||||
|     assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(rule, &s->rules[event], next) { |  | ||||||
|         process_rule(bs, rule, &old_vars); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_blkdebug = { |  | ||||||
|     .format_name        = "blkdebug", |  | ||||||
|     .protocol_name      = "blkdebug", |  | ||||||
|  |  | ||||||
|     .instance_size      = sizeof(BDRVBlkdebugState), |  | ||||||
|  |  | ||||||
|     .bdrv_file_open     = blkdebug_open, |  | ||||||
|     .bdrv_close         = blkdebug_close, |  | ||||||
|     .bdrv_flush         = blkdebug_flush, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = blkdebug_aio_readv, |  | ||||||
|     .bdrv_aio_writev    = blkdebug_aio_writev, |  | ||||||
|     .bdrv_aio_flush     = blkdebug_aio_flush, |  | ||||||
|  |  | ||||||
|     .bdrv_debug_event   = blkdebug_debug_event, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void bdrv_blkdebug_init(void) |  | ||||||
| { |  | ||||||
|     bdrv_register(&bdrv_blkdebug); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| block_init(bdrv_blkdebug_init); |  | ||||||
| @@ -1,383 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Block protocol for block driver correctness testing |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2010 IBM, Corp. |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include "qemu_socket.h" /* for EINPROGRESS on Windows */ |  | ||||||
| #include "block_int.h" |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BlockDriverState *test_file; |  | ||||||
| } BDRVBlkverifyState; |  | ||||||
|  |  | ||||||
| typedef struct BlkverifyAIOCB BlkverifyAIOCB; |  | ||||||
| struct BlkverifyAIOCB { |  | ||||||
|     BlockDriverAIOCB common; |  | ||||||
|     QEMUBH *bh; |  | ||||||
|  |  | ||||||
|     /* Request metadata */ |  | ||||||
|     bool is_write; |  | ||||||
|     int64_t sector_num; |  | ||||||
|     int nb_sectors; |  | ||||||
|  |  | ||||||
|     int ret;                    /* first completed request's result */ |  | ||||||
|     unsigned int done;          /* completion counter */ |  | ||||||
|     bool *finished;             /* completion signal for cancel */ |  | ||||||
|  |  | ||||||
|     QEMUIOVector *qiov;         /* user I/O vector */ |  | ||||||
|     QEMUIOVector raw_qiov;      /* cloned I/O vector for raw file */ |  | ||||||
|     void *buf;                  /* buffer for raw file I/O */ |  | ||||||
|  |  | ||||||
|     void (*verify)(BlkverifyAIOCB *acb); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb) |  | ||||||
| { |  | ||||||
|     BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb; |  | ||||||
|     bool finished = false; |  | ||||||
|  |  | ||||||
|     /* Wait until request completes, invokes its callback, and frees itself */ |  | ||||||
|     acb->finished = &finished; |  | ||||||
|     while (!finished) { |  | ||||||
|         qemu_aio_wait(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static AIOPool blkverify_aio_pool = { |  | ||||||
|     .aiocb_size         = sizeof(BlkverifyAIOCB), |  | ||||||
|     .cancel             = blkverify_aio_cancel, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, |  | ||||||
|                                              const char *fmt, ...) |  | ||||||
| { |  | ||||||
|     va_list ap; |  | ||||||
|  |  | ||||||
|     va_start(ap, fmt); |  | ||||||
|     fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", |  | ||||||
|             acb->is_write ? "write" : "read", acb->sector_num, |  | ||||||
|             acb->nb_sectors); |  | ||||||
|     vfprintf(stderr, fmt, ap); |  | ||||||
|     fprintf(stderr, "\n"); |  | ||||||
|     va_end(ap); |  | ||||||
|     exit(1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ |  | ||||||
| static int blkverify_open(BlockDriverState *bs, const char *filename, int flags) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|     int ret; |  | ||||||
|     char *raw, *c; |  | ||||||
|  |  | ||||||
|     /* Parse the blkverify: prefix */ |  | ||||||
|     if (strncmp(filename, "blkverify:", strlen("blkverify:"))) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|     filename += strlen("blkverify:"); |  | ||||||
|  |  | ||||||
|     /* Parse the raw image filename */ |  | ||||||
|     c = strchr(filename, ':'); |  | ||||||
|     if (c == NULL) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     raw = strdup(filename); |  | ||||||
|     raw[c - filename] = '\0'; |  | ||||||
|     ret = bdrv_file_open(&bs->file, raw, flags); |  | ||||||
|     free(raw); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|     filename = c + 1; |  | ||||||
|  |  | ||||||
|     /* Open the test file */ |  | ||||||
|     s->test_file = bdrv_new(""); |  | ||||||
|     ret = bdrv_open(s->test_file, filename, flags, NULL); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         bdrv_delete(s->test_file); |  | ||||||
|         s->test_file = NULL; |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkverify_close(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     bdrv_delete(s->test_file); |  | ||||||
|     s->test_file = NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int blkverify_flush(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     /* Only flush test file, the raw file is not important */ |  | ||||||
|     return bdrv_flush(s->test_file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int64_t blkverify_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     return bdrv_getlength(s->test_file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Check that I/O vector contents are identical |  | ||||||
|  * |  | ||||||
|  * @a:          I/O vector |  | ||||||
|  * @b:          I/O vector |  | ||||||
|  * @ret:        Offset to first mismatching byte or -1 if match |  | ||||||
|  */ |  | ||||||
| static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|     ssize_t offset = 0; |  | ||||||
|  |  | ||||||
|     assert(a->niov == b->niov); |  | ||||||
|     for (i = 0; i < a->niov; i++) { |  | ||||||
|         size_t len = 0; |  | ||||||
|         uint8_t *p = (uint8_t *)a->iov[i].iov_base; |  | ||||||
|         uint8_t *q = (uint8_t *)b->iov[i].iov_base; |  | ||||||
|  |  | ||||||
|         assert(a->iov[i].iov_len == b->iov[i].iov_len); |  | ||||||
|         while (len < a->iov[i].iov_len && *p++ == *q++) { |  | ||||||
|             len++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         offset += len; |  | ||||||
|  |  | ||||||
|         if (len != a->iov[i].iov_len) { |  | ||||||
|             return offset; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     int src_index; |  | ||||||
|     struct iovec *src_iov; |  | ||||||
|     void *dest_base; |  | ||||||
| } IOVectorSortElem; |  | ||||||
|  |  | ||||||
| static int sortelem_cmp_src_base(const void *a, const void *b) |  | ||||||
| { |  | ||||||
|     const IOVectorSortElem *elem_a = a; |  | ||||||
|     const IOVectorSortElem *elem_b = b; |  | ||||||
|  |  | ||||||
|     /* Don't overflow */ |  | ||||||
|     if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { |  | ||||||
|         return -1; |  | ||||||
|     } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { |  | ||||||
|         return 1; |  | ||||||
|     } else { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int sortelem_cmp_src_index(const void *a, const void *b) |  | ||||||
| { |  | ||||||
|     const IOVectorSortElem *elem_a = a; |  | ||||||
|     const IOVectorSortElem *elem_b = b; |  | ||||||
|  |  | ||||||
|     return elem_a->src_index - elem_b->src_index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Copy contents of I/O vector |  | ||||||
|  * |  | ||||||
|  * The relative relationships of overlapping iovecs are preserved.  This is |  | ||||||
|  * necessary to ensure identical semantics in the cloned I/O vector. |  | ||||||
|  */ |  | ||||||
| static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, |  | ||||||
|                                   void *buf) |  | ||||||
| { |  | ||||||
|     IOVectorSortElem sortelems[src->niov]; |  | ||||||
|     void *last_end; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     /* Sort by source iovecs by base address */ |  | ||||||
|     for (i = 0; i < src->niov; i++) { |  | ||||||
|         sortelems[i].src_index = i; |  | ||||||
|         sortelems[i].src_iov = &src->iov[i]; |  | ||||||
|     } |  | ||||||
|     qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); |  | ||||||
|  |  | ||||||
|     /* Allocate buffer space taking into account overlapping iovecs */ |  | ||||||
|     last_end = NULL; |  | ||||||
|     for (i = 0; i < src->niov; i++) { |  | ||||||
|         struct iovec *cur = sortelems[i].src_iov; |  | ||||||
|         ptrdiff_t rewind = 0; |  | ||||||
|  |  | ||||||
|         /* Detect overlap */ |  | ||||||
|         if (last_end && last_end > cur->iov_base) { |  | ||||||
|             rewind = last_end - cur->iov_base; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sortelems[i].dest_base = buf - rewind; |  | ||||||
|         buf += cur->iov_len - MIN(rewind, cur->iov_len); |  | ||||||
|         last_end = MAX(cur->iov_base + cur->iov_len, last_end); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Sort by source iovec index and build destination iovec */ |  | ||||||
|     qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); |  | ||||||
|     for (i = 0; i < src->niov; i++) { |  | ||||||
|         qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, |  | ||||||
|                                          int64_t sector_num, QEMUIOVector *qiov, |  | ||||||
|                                          int nb_sectors, |  | ||||||
|                                          BlockDriverCompletionFunc *cb, |  | ||||||
|                                          void *opaque) |  | ||||||
| { |  | ||||||
|     BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque); |  | ||||||
|  |  | ||||||
|     acb->bh = NULL; |  | ||||||
|     acb->is_write = is_write; |  | ||||||
|     acb->sector_num = sector_num; |  | ||||||
|     acb->nb_sectors = nb_sectors; |  | ||||||
|     acb->ret = -EINPROGRESS; |  | ||||||
|     acb->done = 0; |  | ||||||
|     acb->qiov = qiov; |  | ||||||
|     acb->buf = NULL; |  | ||||||
|     acb->verify = NULL; |  | ||||||
|     acb->finished = NULL; |  | ||||||
|     return acb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkverify_aio_bh(void *opaque) |  | ||||||
| { |  | ||||||
|     BlkverifyAIOCB *acb = opaque; |  | ||||||
|  |  | ||||||
|     qemu_bh_delete(acb->bh); |  | ||||||
|     if (acb->buf) { |  | ||||||
|         qemu_iovec_destroy(&acb->raw_qiov); |  | ||||||
|         qemu_vfree(acb->buf); |  | ||||||
|     } |  | ||||||
|     acb->common.cb(acb->common.opaque, acb->ret); |  | ||||||
|     if (acb->finished) { |  | ||||||
|         *acb->finished = true; |  | ||||||
|     } |  | ||||||
|     qemu_aio_release(acb); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkverify_aio_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     BlkverifyAIOCB *acb = opaque; |  | ||||||
|  |  | ||||||
|     switch (++acb->done) { |  | ||||||
|     case 1: |  | ||||||
|         acb->ret = ret; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case 2: |  | ||||||
|         if (acb->ret != ret) { |  | ||||||
|             blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (acb->verify) { |  | ||||||
|             acb->verify(acb); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         acb->bh = qemu_bh_new(blkverify_aio_bh, acb); |  | ||||||
|         qemu_bh_schedule(acb->bh); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blkverify_verify_readv(BlkverifyAIOCB *acb) |  | ||||||
| { |  | ||||||
|     ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov); |  | ||||||
|     if (offset != -1) { |  | ||||||
|         blkverify_err(acb, "contents mismatch in sector %" PRId64, |  | ||||||
|                       acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, |  | ||||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|     BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, |  | ||||||
|                                             nb_sectors, cb, opaque); |  | ||||||
|  |  | ||||||
|     acb->verify = blkverify_verify_readv; |  | ||||||
|     acb->buf = qemu_blockalign(bs->file, qiov->size); |  | ||||||
|     qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); |  | ||||||
|     blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf); |  | ||||||
|  |  | ||||||
|     if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, |  | ||||||
|                         blkverify_aio_cb, acb)) { |  | ||||||
|         blkverify_aio_cb(acb, -EIO); |  | ||||||
|     } |  | ||||||
|     if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, |  | ||||||
|                         blkverify_aio_cb, acb)) { |  | ||||||
|         blkverify_aio_cb(acb, -EIO); |  | ||||||
|     } |  | ||||||
|     return &acb->common; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs, |  | ||||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|     BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, |  | ||||||
|                                             nb_sectors, cb, opaque); |  | ||||||
|  |  | ||||||
|     if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, |  | ||||||
|                          blkverify_aio_cb, acb)) { |  | ||||||
|         blkverify_aio_cb(acb, -EIO); |  | ||||||
|     } |  | ||||||
|     if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, |  | ||||||
|                          blkverify_aio_cb, acb)) { |  | ||||||
|         blkverify_aio_cb(acb, -EIO); |  | ||||||
|     } |  | ||||||
|     return &acb->common; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs, |  | ||||||
|                                              BlockDriverCompletionFunc *cb, |  | ||||||
|                                              void *opaque) |  | ||||||
| { |  | ||||||
|     BDRVBlkverifyState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     /* Only flush test file, the raw file is not important */ |  | ||||||
|     return bdrv_aio_flush(s->test_file, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_blkverify = { |  | ||||||
|     .format_name        = "blkverify", |  | ||||||
|     .protocol_name      = "blkverify", |  | ||||||
|  |  | ||||||
|     .instance_size      = sizeof(BDRVBlkverifyState), |  | ||||||
|  |  | ||||||
|     .bdrv_getlength     = blkverify_getlength, |  | ||||||
|  |  | ||||||
|     .bdrv_file_open     = blkverify_open, |  | ||||||
|     .bdrv_close         = blkverify_close, |  | ||||||
|     .bdrv_flush         = blkverify_flush, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = blkverify_aio_readv, |  | ||||||
|     .bdrv_aio_writev    = blkverify_aio_writev, |  | ||||||
|     .bdrv_aio_flush     = blkverify_aio_flush, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void bdrv_blkverify_init(void) |  | ||||||
| { |  | ||||||
|     bdrv_register(&bdrv_blkverify); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| block_init(bdrv_blkverify_init); |  | ||||||
| @@ -80,6 +80,8 @@ struct bochs_header { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct BDRVBochsState { | typedef struct BDRVBochsState { | ||||||
|  |     int fd; | ||||||
|  |  | ||||||
|     uint32_t *catalog_bitmap; |     uint32_t *catalog_bitmap; | ||||||
|     int catalog_size; |     int catalog_size; | ||||||
|  |  | ||||||
| @@ -107,16 +109,25 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int bochs_open(BlockDriverState *bs, int flags) | static int bochs_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVBochsState *s = bs->opaque; |     BDRVBochsState *s = bs->opaque; | ||||||
|     int i; |     int fd, i; | ||||||
|     struct bochs_header bochs; |     struct bochs_header bochs; | ||||||
|     struct bochs_header_v1 header_v1; |     struct bochs_header_v1 header_v1; | ||||||
|  |  | ||||||
|  |     fd = open(filename, O_RDWR | O_BINARY); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         fd = open(filename, O_RDONLY | O_BINARY); | ||||||
|  |         if (fd < 0) | ||||||
|  |             return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->read_only = 1; // no write support yet |     bs->read_only = 1; // no write support yet | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) { |     s->fd = fd; | ||||||
|  |  | ||||||
|  |     if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -135,10 +146,12 @@ static int bochs_open(BlockDriverState *bs, int flags) | |||||||
|       bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; |       bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET); | ||||||
|  |  | ||||||
|     s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); |     s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); | ||||||
|     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); |     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); | ||||||
|     if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, |     if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != | ||||||
|                    s->catalog_size * 4) != s->catalog_size * 4) | 	s->catalog_size * 4) | ||||||
| 	goto fail; | 	goto fail; | ||||||
|     for (i = 0; i < s->catalog_size; i++) |     for (i = 0; i < s->catalog_size; i++) | ||||||
| 	le32_to_cpus(&s->catalog_bitmap[i]); | 	le32_to_cpus(&s->catalog_bitmap[i]); | ||||||
| @@ -152,53 +165,68 @@ static int bochs_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  fail: |  fail: | ||||||
|  |     close(fd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||||
| { | { | ||||||
|     BDRVBochsState *s = bs->opaque; |     BDRVBochsState *s = bs->opaque; | ||||||
|     int64_t offset = sector_num * 512; |     int64_t offset = sector_num * 512; | ||||||
|     int64_t extent_index, extent_offset, bitmap_offset; |     int64_t extent_index, extent_offset, bitmap_offset, block_offset; | ||||||
|     char bitmap_entry; |     char bitmap_entry; | ||||||
|  |  | ||||||
|     // seek to sector |     // seek to sector | ||||||
|     extent_index = offset / s->extent_size; |     extent_index = offset / s->extent_size; | ||||||
|     extent_offset = (offset % s->extent_size) / 512; |     extent_offset = (offset % s->extent_size) / 512; | ||||||
|  |  | ||||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) { |     if (s->catalog_bitmap[extent_index] == 0xffffffff) | ||||||
| 	return -1; /* not allocated */ |     { | ||||||
|  | //	fprintf(stderr, "page not allocated [%x - %x:%x]\n", | ||||||
|  | //	    sector_num, extent_index, extent_offset); | ||||||
|  | 	return -1; // not allocated | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * |     bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * | ||||||
| 	(s->extent_blocks + s->bitmap_blocks)); | 	(s->extent_blocks + s->bitmap_blocks)); | ||||||
|  |     block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); | ||||||
|  |  | ||||||
|     /* read in bitmap for current extent */ | //    fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n", | ||||||
|     if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), | //	sector_num, extent_index, extent_offset, | ||||||
|                    &bitmap_entry, 1) != 1) { | //	le32_to_cpu(s->catalog_bitmap[extent_index]), | ||||||
|         return -1; | //	bitmap_offset, block_offset); | ||||||
|  |  | ||||||
|  |     // read in bitmap for current extent | ||||||
|  |     lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET); | ||||||
|  |  | ||||||
|  |     read(s->fd, &bitmap_entry, 1); | ||||||
|  |  | ||||||
|  |     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) | ||||||
|  |     { | ||||||
|  | //	fprintf(stderr, "sector (%x) in bitmap not allocated\n", | ||||||
|  | //	    sector_num); | ||||||
|  | 	return -1; // not allocated | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { |     lseek(s->fd, block_offset, SEEK_SET); | ||||||
| 	return -1; /* not allocated */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int bochs_read(BlockDriverState *bs, int64_t sector_num, | static int bochs_read(BlockDriverState *bs, int64_t sector_num, | ||||||
|                     uint8_t *buf, int nb_sectors) |                     uint8_t *buf, int nb_sectors) | ||||||
| { | { | ||||||
|  |     BDRVBochsState *s = bs->opaque; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         int64_t block_offset = seek_to_sector(bs, sector_num); | 	if (!seek_to_sector(bs, sector_num)) | ||||||
|         if (block_offset >= 0) { | 	{ | ||||||
|             ret = bdrv_pread(bs->file, block_offset, buf, 512); | 	    ret = read(s->fd, buf, 512); | ||||||
|             if (ret != 512) { | 	    if (ret != 512) | ||||||
|                 return -1; | 		return -1; | ||||||
|             } | 	} | ||||||
|         } else | 	else | ||||||
|             memset(buf, 0, 512); |             memset(buf, 0, 512); | ||||||
|         nb_sectors--; |         nb_sectors--; | ||||||
|         sector_num++; |         sector_num++; | ||||||
| @@ -211,6 +239,7 @@ static void bochs_close(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     BDRVBochsState *s = bs->opaque; |     BDRVBochsState *s = bs->opaque; | ||||||
|     qemu_free(s->catalog_bitmap); |     qemu_free(s->catalog_bitmap); | ||||||
|  |     close(s->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockDriver bdrv_bochs = { | static BlockDriver bdrv_bochs = { | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ | |||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
|  |  | ||||||
| typedef struct BDRVCloopState { | typedef struct BDRVCloopState { | ||||||
|  |     int fd; | ||||||
|     uint32_t block_size; |     uint32_t block_size; | ||||||
|     uint32_t n_blocks; |     uint32_t n_blocks; | ||||||
|     uint64_t* offsets; |     uint64_t* offsets; | ||||||
| @@ -50,31 +51,34 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cloop_open(BlockDriverState *bs, int flags) | static int cloop_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVCloopState *s = bs->opaque; |     BDRVCloopState *s = bs->opaque; | ||||||
|     uint32_t offsets_size,max_compressed_block_size=1,i; |     uint32_t offsets_size,max_compressed_block_size=1,i; | ||||||
|  |  | ||||||
|  |     s->fd = open(filename, O_RDONLY | O_BINARY); | ||||||
|  |     if (s->fd < 0) | ||||||
|  |         return -errno; | ||||||
|     bs->read_only = 1; |     bs->read_only = 1; | ||||||
|  |  | ||||||
|     /* read header */ |     /* read header */ | ||||||
|     if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) { |     if(lseek(s->fd,128,SEEK_SET)<0) { | ||||||
|         goto cloop_close; | cloop_close: | ||||||
|  | 	close(s->fd); | ||||||
|  | 	return -1; | ||||||
|     } |     } | ||||||
|     s->block_size = be32_to_cpu(s->block_size); |     if(read(s->fd,&s->block_size,4)<4) | ||||||
|  | 	goto cloop_close; | ||||||
|     if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) { |     s->block_size=be32_to_cpu(s->block_size); | ||||||
|         goto cloop_close; |     if(read(s->fd,&s->n_blocks,4)<4) | ||||||
|     } | 	goto cloop_close; | ||||||
|     s->n_blocks = be32_to_cpu(s->n_blocks); |     s->n_blocks=be32_to_cpu(s->n_blocks); | ||||||
|  |  | ||||||
|     /* read offsets */ |     /* read offsets */ | ||||||
|     offsets_size = s->n_blocks * sizeof(uint64_t); |     offsets_size=s->n_blocks*sizeof(uint64_t); | ||||||
|     s->offsets = qemu_malloc(offsets_size); |     s->offsets=(uint64_t*)qemu_malloc(offsets_size); | ||||||
|     if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) < |     if(read(s->fd,s->offsets,offsets_size)<offsets_size) | ||||||
|             offsets_size) { |  | ||||||
| 	goto cloop_close; | 	goto cloop_close; | ||||||
|     } |  | ||||||
|     for(i=0;i<s->n_blocks;i++) { |     for(i=0;i<s->n_blocks;i++) { | ||||||
| 	s->offsets[i]=be64_to_cpu(s->offsets[i]); | 	s->offsets[i]=be64_to_cpu(s->offsets[i]); | ||||||
| 	if(i>0) { | 	if(i>0) { | ||||||
| @@ -94,21 +98,16 @@ static int cloop_open(BlockDriverState *bs, int flags) | |||||||
|     s->sectors_per_block = s->block_size/512; |     s->sectors_per_block = s->block_size/512; | ||||||
|     bs->total_sectors = s->n_blocks*s->sectors_per_block; |     bs->total_sectors = s->n_blocks*s->sectors_per_block; | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| cloop_close: |  | ||||||
|     return -1; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline int cloop_read_block(BlockDriverState *bs, int block_num) | static inline int cloop_read_block(BDRVCloopState *s,int block_num) | ||||||
| { | { | ||||||
|     BDRVCloopState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     if(s->current_block != block_num) { |     if(s->current_block != block_num) { | ||||||
| 	int ret; | 	int ret; | ||||||
|         uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num]; |         uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num]; | ||||||
|  |  | ||||||
|         ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, | 	lseek(s->fd, s->offsets[block_num], SEEK_SET); | ||||||
|                          bytes); |         ret = read(s->fd, s->compressed_block, bytes); | ||||||
|         if (ret != bytes) |         if (ret != bytes) | ||||||
|             return -1; |             return -1; | ||||||
|  |  | ||||||
| @@ -137,7 +136,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|     for(i=0;i<nb_sectors;i++) { |     for(i=0;i<nb_sectors;i++) { | ||||||
| 	uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block), | 	uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block), | ||||||
| 	    block_num=(sector_num+i)/s->sectors_per_block; | 	    block_num=(sector_num+i)/s->sectors_per_block; | ||||||
| 	if(cloop_read_block(bs, block_num) != 0) | 	if(cloop_read_block(s, block_num) != 0) | ||||||
| 	    return -1; | 	    return -1; | ||||||
| 	memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512); | 	memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512); | ||||||
|     } |     } | ||||||
| @@ -147,6 +146,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num, | |||||||
| static void cloop_close(BlockDriverState *bs) | static void cloop_close(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVCloopState *s = bs->opaque; |     BDRVCloopState *s = bs->opaque; | ||||||
|  |     close(s->fd); | ||||||
|     if(s->n_blocks>0) |     if(s->n_blocks>0) | ||||||
| 	free(s->offsets); | 	free(s->offsets); | ||||||
|     free(s->compressed_block); |     free(s->compressed_block); | ||||||
|   | |||||||
							
								
								
									
										149
									
								
								block/cow.c
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								block/cow.c
									
									
									
									
									
								
							| @@ -21,9 +21,11 @@ | |||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  | #ifndef _WIN32 | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "block_int.h" | #include "block_int.h" | ||||||
| #include "module.h" | #include "module.h" | ||||||
|  | #include <sys/mman.h> | ||||||
|  |  | ||||||
| /**************************************************************/ | /**************************************************************/ | ||||||
| /* COW block driver using file system holes */ | /* COW block driver using file system holes */ | ||||||
| @@ -42,6 +44,10 @@ struct cow_header_v2 { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct BDRVCowState { | typedef struct BDRVCowState { | ||||||
|  |     int fd; | ||||||
|  |     uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ | ||||||
|  |     uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ | ||||||
|  |     int cow_bitmap_size; | ||||||
|     int64_t cow_sectors_offset; |     int64_t cow_sectors_offset; | ||||||
| } BDRVCowState; | } BDRVCowState; | ||||||
|  |  | ||||||
| @@ -57,16 +63,22 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|         return 0; |         return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cow_open(BlockDriverState *bs, int flags) | static int cow_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVCowState *s = bs->opaque; |     BDRVCowState *s = bs->opaque; | ||||||
|  |     int fd; | ||||||
|     struct cow_header_v2 cow_header; |     struct cow_header_v2 cow_header; | ||||||
|     int bitmap_size; |  | ||||||
|     int64_t size; |     int64_t size; | ||||||
|  |  | ||||||
|  |     fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||||
|  |         if (fd < 0) | ||||||
|  |             return -1; | ||||||
|  |     } | ||||||
|  |     s->fd = fd; | ||||||
|     /* see if it is a cow image */ |     /* see if it is a cow image */ | ||||||
|     if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) != |     if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { | ||||||
|             sizeof(cow_header)) { |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -82,91 +94,61 @@ static int cow_open(BlockDriverState *bs, int flags) | |||||||
|     pstrcpy(bs->backing_file, sizeof(bs->backing_file), |     pstrcpy(bs->backing_file, sizeof(bs->backing_file), | ||||||
|             cow_header.backing_file); |             cow_header.backing_file); | ||||||
|  |  | ||||||
|     bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); |     /* mmap the bitmap */ | ||||||
|     s->cow_sectors_offset = (bitmap_size + 511) & ~511; |     s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | ||||||
|  |     s->cow_bitmap_addr = (void *)mmap(get_mmap_addr(s->cow_bitmap_size), | ||||||
|  |                                       s->cow_bitmap_size, | ||||||
|  |                                       PROT_READ | PROT_WRITE, | ||||||
|  |                                       MAP_SHARED, s->fd, 0); | ||||||
|  |     if (s->cow_bitmap_addr == MAP_FAILED) | ||||||
|  |         goto fail; | ||||||
|  |     s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); | ||||||
|  |     s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; | ||||||
|     return 0; |     return 0; | ||||||
|  fail: |  fail: | ||||||
|  |     close(fd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) | ||||||
|  * XXX(hch): right now these functions are extremly ineffcient. |  | ||||||
|  * We should just read the whole bitmap we'll need in one go instead. |  | ||||||
|  */ |  | ||||||
| static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) |  | ||||||
| { | { | ||||||
|     uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; |     bitmap[bitnum / 8] |= (1 << (bitnum%8)); | ||||||
|     uint8_t bitmap; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); |  | ||||||
|     if (ret < 0) { |  | ||||||
|        return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bitmap |= (1 << (bitnum % 8)); |  | ||||||
|  |  | ||||||
|     ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap)); |  | ||||||
|     if (ret < 0) { |  | ||||||
|        return ret; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) | static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) | ||||||
| { | { | ||||||
|     uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; |     return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); | ||||||
|     uint8_t bitmap; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); |  | ||||||
|     if (ret < 0) { |  | ||||||
|        return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return !!(bitmap & (1 << (bitnum % 8))); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Return true if first block has been changed (ie. current version is | /* Return true if first block has been changed (ie. current version is | ||||||
|  * in COW file).  Set the number of continuous blocks for which that |  * in COW file).  Set the number of continuous blocks for which that | ||||||
|  * is true. */ |  * is true. */ | ||||||
| static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | static inline int is_changed(uint8_t *bitmap, | ||||||
|         int nb_sectors, int *num_same) |                              int64_t sector_num, int nb_sectors, | ||||||
|  |                              int *num_same) | ||||||
| { | { | ||||||
|     int changed; |     int changed; | ||||||
|  |  | ||||||
|     if (nb_sectors == 0) { |     if (!bitmap || nb_sectors == 0) { | ||||||
| 	*num_same = nb_sectors; | 	*num_same = nb_sectors; | ||||||
| 	return 0; | 	return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     changed = is_bit_set(bs, sector_num); |     changed = is_bit_set(bitmap, sector_num); | ||||||
|     if (changed < 0) { |  | ||||||
|         return 0; /* XXX: how to return I/O errors? */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { |     for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { | ||||||
| 	if (is_bit_set(bs, sector_num + *num_same) != changed) | 	if (is_bit_set(bitmap, sector_num + *num_same) != changed) | ||||||
| 	    break; | 	    break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return changed; |     return changed; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, | static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||||
|         int nb_sectors) |                             int nb_sectors, int *pnum) | ||||||
| { | { | ||||||
|     int error = 0; |     BDRVCowState *s = bs->opaque; | ||||||
|     int i; |     return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); | ||||||
|  |  | ||||||
|     for (i = 0; i < nb_sectors; i++) { |  | ||||||
|         error = cow_set_bit(bs, sector_num + i); |  | ||||||
|         if (error) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return error; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cow_read(BlockDriverState *bs, int64_t sector_num, | static int cow_read(BlockDriverState *bs, int64_t sector_num, | ||||||
| @@ -176,10 +158,9 @@ static int cow_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|     int ret, n; |     int ret, n; | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) { |         if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { | ||||||
|             ret = bdrv_pread(bs->file, |             lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | ||||||
|                         s->cow_sectors_offset + sector_num * 512, |             ret = read(s->fd, buf, n * 512); | ||||||
|                         buf, n * 512); |  | ||||||
|             if (ret != n * 512) |             if (ret != n * 512) | ||||||
|                 return -1; |                 return -1; | ||||||
|         } else { |         } else { | ||||||
| @@ -203,18 +184,22 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num, | |||||||
|                      const uint8_t *buf, int nb_sectors) |                      const uint8_t *buf, int nb_sectors) | ||||||
| { | { | ||||||
|     BDRVCowState *s = bs->opaque; |     BDRVCowState *s = bs->opaque; | ||||||
|     int ret; |     int ret, i; | ||||||
|  |  | ||||||
|     ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512, |     lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | ||||||
|                       buf, nb_sectors * 512); |     ret = write(s->fd, buf, nb_sectors * 512); | ||||||
|     if (ret != nb_sectors * 512) |     if (ret != nb_sectors * 512) | ||||||
|         return -1; |         return -1; | ||||||
|  |     for (i = 0; i < nb_sectors; i++) | ||||||
|     return cow_update_bitmap(bs, sector_num, nb_sectors); |         cow_set_bit(s->cow_bitmap, sector_num + i); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cow_close(BlockDriverState *bs) | static void cow_close(BlockDriverState *bs) | ||||||
| { | { | ||||||
|  |     BDRVCowState *s = bs->opaque; | ||||||
|  |     munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size); | ||||||
|  |     close(s->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cow_create(const char *filename, QEMUOptionParameter *options) | static int cow_create(const char *filename, QEMUOptionParameter *options) | ||||||
| @@ -224,7 +209,6 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     struct stat st; |     struct stat st; | ||||||
|     int64_t image_sectors = 0; |     int64_t image_sectors = 0; | ||||||
|     const char *image_filename = NULL; |     const char *image_filename = NULL; | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     /* Read out options */ |     /* Read out options */ | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
| @@ -239,7 +223,7 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, |     cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | ||||||
|               0644); |               0644); | ||||||
|     if (cow_fd < 0) |     if (cow_fd < 0) | ||||||
|         return -errno; |         return -1; | ||||||
|     memset(&cow_header, 0, sizeof(cow_header)); |     memset(&cow_header, 0, sizeof(cow_header)); | ||||||
|     cow_header.magic = cpu_to_be32(COW_MAGIC); |     cow_header.magic = cpu_to_be32(COW_MAGIC); | ||||||
|     cow_header.version = cpu_to_be32(COW_VERSION); |     cow_header.version = cpu_to_be32(COW_VERSION); | ||||||
| @@ -264,27 +248,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     } |     } | ||||||
|     cow_header.sectorsize = cpu_to_be32(512); |     cow_header.sectorsize = cpu_to_be32(512); | ||||||
|     cow_header.size = cpu_to_be64(image_sectors * 512); |     cow_header.size = cpu_to_be64(image_sectors * 512); | ||||||
|     ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header)); |     write(cow_fd, &cow_header, sizeof(cow_header)); | ||||||
|     if (ret != sizeof(cow_header)) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* resize to include at least all the bitmap */ |     /* resize to include at least all the bitmap */ | ||||||
|     ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); |     ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); | ||||||
|     if (ret) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| exit: |  | ||||||
|     close(cow_fd); |     close(cow_fd); | ||||||
|     return ret; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cow_flush(BlockDriverState *bs) | static void cow_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     return bdrv_flush(bs->file); |     BDRVCowState *s = bs->opaque; | ||||||
|  |     qemu_fdatasync(s->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static QEMUOptionParameter cow_create_options[] = { | static QEMUOptionParameter cow_create_options[] = { | ||||||
| @@ -322,3 +296,4 @@ static void bdrv_cow_init(void) | |||||||
| } | } | ||||||
|  |  | ||||||
| block_init(bdrv_cow_init); | block_init(bdrv_cow_init); | ||||||
|  | #endif | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -29,9 +29,9 @@ | |||||||
| // #define DEBUG_VERBOSE | // #define DEBUG_VERBOSE | ||||||
|  |  | ||||||
| #ifdef DEBUG_CURL | #ifdef DEBUG_CURL | ||||||
| #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) | #define dprintf(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) | ||||||
| #else | #else | ||||||
| #define DPRINTF(fmt, ...) do { } while (0) | #define dprintf(fmt, ...) do { } while (0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define CURL_NUM_STATES 8 | #define CURL_NUM_STATES 8 | ||||||
| @@ -80,7 +80,7 @@ static void curl_multi_do(void *arg); | |||||||
| static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||||||
|                         void *s, void *sp) |                         void *s, void *sp) | ||||||
| { | { | ||||||
|     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); |     dprintf("CURL (AIO): Sock action %d on fd %d\n", action, fd); | ||||||
|     switch (action) { |     switch (action) { | ||||||
|         case CURL_POLL_IN: |         case CURL_POLL_IN: | ||||||
|             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s); |             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s); | ||||||
| @@ -104,11 +104,10 @@ static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | |||||||
| { | { | ||||||
|     CURLState *s = ((CURLState*)opaque); |     CURLState *s = ((CURLState*)opaque); | ||||||
|     size_t realsize = size * nmemb; |     size_t realsize = size * nmemb; | ||||||
|     size_t fsize; |     long long fsize; | ||||||
|  |  | ||||||
|     if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) { |     if(sscanf(ptr, "Content-Length: %lld", &fsize) == 1) | ||||||
|         s->s->len = fsize; |         s->s->len = fsize; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return realsize; |     return realsize; | ||||||
| } | } | ||||||
| @@ -119,7 +118,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | |||||||
|     size_t realsize = size * nmemb; |     size_t realsize = size * nmemb; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     DPRINTF("CURL: Just reading %zd bytes\n", realsize); |     dprintf("CURL: Just reading %lld bytes\n", (unsigned long long)realsize); | ||||||
|  |  | ||||||
|     if (!s || !s->orig_buf) |     if (!s || !s->orig_buf) | ||||||
|         goto read_end; |         goto read_end; | ||||||
| @@ -340,7 +339,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((s->readahead_size & 0x1ff) != 0) { |     if ((s->readahead_size & 0x1ff) != 0) { | ||||||
|         fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n", |         fprintf(stderr, "HTTP_READAHEAD_SIZE %Zd is not a multiple of 512\n", | ||||||
|                 s->readahead_size); |                 s->readahead_size); | ||||||
|         goto out_noclean; |         goto out_noclean; | ||||||
|     } |     } | ||||||
| @@ -350,7 +349,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|         inited = 1; |         inited = 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DPRINTF("CURL: Opening %s\n", file); |     dprintf("CURL: Opening %s\n", file); | ||||||
|     s->url = file; |     s->url = file; | ||||||
|     state = curl_init_state(s); |     state = curl_init_state(s); | ||||||
|     if (!state) |     if (!state) | ||||||
| @@ -369,7 +368,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|         s->len = (size_t)d; |         s->len = (size_t)d; | ||||||
|     else if(!s->len) |     else if(!s->len) | ||||||
|         goto out; |         goto out; | ||||||
|     DPRINTF("CURL: Size = %zd\n", s->len); |     dprintf("CURL: Size = %lld\n", (long long)s->len); | ||||||
|  |  | ||||||
|     curl_clean_state(state); |     curl_clean_state(state); | ||||||
|     curl_easy_cleanup(state->curl); |     curl_easy_cleanup(state->curl); | ||||||
| @@ -451,9 +450,8 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, | |||||||
|     state->orig_buf = qemu_malloc(state->buf_len); |     state->orig_buf = qemu_malloc(state->buf_len); | ||||||
|     state->acb[0] = acb; |     state->acb[0] = acb; | ||||||
|  |  | ||||||
|     snprintf(state->range, 127, "%zd-%zd", start, end); |     snprintf(state->range, 127, "%lld-%lld", (long long)start, (long long)end); | ||||||
|     DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n", |     dprintf("CURL (AIO): Reading %d at %lld (%s)\n", (nb_sectors * SECTOR_SIZE), start, state->range); | ||||||
|             (nb_sectors * SECTOR_SIZE), start, state->range); |  | ||||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); |     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||||
|  |  | ||||||
|     curl_multi_add_handle(s->multi, state->curl); |     curl_multi_add_handle(s->multi, state->curl); | ||||||
| @@ -467,7 +465,7 @@ static void curl_close(BlockDriverState *bs) | |||||||
|     BDRVCURLState *s = bs->opaque; |     BDRVCURLState *s = bs->opaque; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     DPRINTF("CURL: Close\n"); |     dprintf("CURL: Close\n"); | ||||||
|     for (i=0; i<CURL_NUM_STATES; i++) { |     for (i=0; i<CURL_NUM_STATES; i++) { | ||||||
|         if (s->states[i].in_use) |         if (s->states[i].in_use) | ||||||
|             curl_clean_state(&s->states[i]); |             curl_clean_state(&s->states[i]); | ||||||
| @@ -497,7 +495,7 @@ static BlockDriver bdrv_http = { | |||||||
|     .protocol_name   = "http", |     .protocol_name   = "http", | ||||||
|  |  | ||||||
|     .instance_size   = sizeof(BDRVCURLState), |     .instance_size   = sizeof(BDRVCURLState), | ||||||
|     .bdrv_file_open  = curl_open, |     .bdrv_open       = curl_open, | ||||||
|     .bdrv_close      = curl_close, |     .bdrv_close      = curl_close, | ||||||
|     .bdrv_getlength  = curl_getlength, |     .bdrv_getlength  = curl_getlength, | ||||||
|  |  | ||||||
| @@ -509,7 +507,7 @@ static BlockDriver bdrv_https = { | |||||||
|     .protocol_name   = "https", |     .protocol_name   = "https", | ||||||
|  |  | ||||||
|     .instance_size   = sizeof(BDRVCURLState), |     .instance_size   = sizeof(BDRVCURLState), | ||||||
|     .bdrv_file_open  = curl_open, |     .bdrv_open       = curl_open, | ||||||
|     .bdrv_close      = curl_close, |     .bdrv_close      = curl_close, | ||||||
|     .bdrv_getlength  = curl_getlength, |     .bdrv_getlength  = curl_getlength, | ||||||
|  |  | ||||||
| @@ -521,7 +519,7 @@ static BlockDriver bdrv_ftp = { | |||||||
|     .protocol_name   = "ftp", |     .protocol_name   = "ftp", | ||||||
|  |  | ||||||
|     .instance_size   = sizeof(BDRVCURLState), |     .instance_size   = sizeof(BDRVCURLState), | ||||||
|     .bdrv_file_open  = curl_open, |     .bdrv_open       = curl_open, | ||||||
|     .bdrv_close      = curl_close, |     .bdrv_close      = curl_close, | ||||||
|     .bdrv_getlength  = curl_getlength, |     .bdrv_getlength  = curl_getlength, | ||||||
|  |  | ||||||
| @@ -533,7 +531,7 @@ static BlockDriver bdrv_ftps = { | |||||||
|     .protocol_name   = "ftps", |     .protocol_name   = "ftps", | ||||||
|  |  | ||||||
|     .instance_size   = sizeof(BDRVCURLState), |     .instance_size   = sizeof(BDRVCURLState), | ||||||
|     .bdrv_file_open  = curl_open, |     .bdrv_open       = curl_open, | ||||||
|     .bdrv_close      = curl_close, |     .bdrv_close      = curl_close, | ||||||
|     .bdrv_getlength  = curl_getlength, |     .bdrv_getlength  = curl_getlength, | ||||||
|  |  | ||||||
| @@ -545,7 +543,7 @@ static BlockDriver bdrv_tftp = { | |||||||
|     .protocol_name   = "tftp", |     .protocol_name   = "tftp", | ||||||
|  |  | ||||||
|     .instance_size   = sizeof(BDRVCURLState), |     .instance_size   = sizeof(BDRVCURLState), | ||||||
|     .bdrv_file_open  = curl_open, |     .bdrv_open       = curl_open, | ||||||
|     .bdrv_close      = curl_close, |     .bdrv_close      = curl_close, | ||||||
|     .bdrv_getlength  = curl_getlength, |     .bdrv_getlength  = curl_getlength, | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -28,6 +28,8 @@ | |||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
|  |  | ||||||
| typedef struct BDRVDMGState { | typedef struct BDRVDMGState { | ||||||
|  |     int fd; | ||||||
|  |  | ||||||
|     /* each chunk contains a certain number of sectors, |     /* each chunk contains a certain number of sectors, | ||||||
|      * offsets[i] is the offset in the .dmg file, |      * offsets[i] is the offset in the .dmg file, | ||||||
|      * lengths[i] is the length of the compressed chunk, |      * lengths[i] is the length of the compressed chunk, | ||||||
| @@ -56,75 +58,69 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static off_t read_off(BlockDriverState *bs, int64_t offset) | static off_t read_off(int fd) | ||||||
| { | { | ||||||
| 	uint64_t buffer; | 	uint64_t buffer; | ||||||
| 	if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) | 	if(read(fd,&buffer,8)<8) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	return be64_to_cpu(buffer); | 	return be64_to_cpu(buffer); | ||||||
| } | } | ||||||
|  |  | ||||||
| static off_t read_uint32(BlockDriverState *bs, int64_t offset) | static off_t read_uint32(int fd) | ||||||
| { | { | ||||||
| 	uint32_t buffer; | 	uint32_t buffer; | ||||||
| 	if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) | 	if(read(fd,&buffer,4)<4) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	return be32_to_cpu(buffer); | 	return be32_to_cpu(buffer); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int dmg_open(BlockDriverState *bs, int flags) | static int dmg_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVDMGState *s = bs->opaque; |     BDRVDMGState *s = bs->opaque; | ||||||
|     off_t info_begin,info_end,last_in_offset,last_out_offset; |     off_t info_begin,info_end,last_in_offset,last_out_offset; | ||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; |     uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; | ||||||
|     int64_t offset; |  | ||||||
|  |  | ||||||
|  |     s->fd = open(filename, O_RDONLY | O_BINARY); | ||||||
|  |     if (s->fd < 0) | ||||||
|  |         return -errno; | ||||||
|     bs->read_only = 1; |     bs->read_only = 1; | ||||||
|     s->n_chunks = 0; |     s->n_chunks = 0; | ||||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; |     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||||
|  |  | ||||||
|     /* read offset of info blocks */ |     /* read offset of info blocks */ | ||||||
|     offset = bdrv_getlength(bs->file); |     if(lseek(s->fd,-0x1d8,SEEK_END)<0) { | ||||||
|     if (offset < 0) { |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|     offset -= 0x1d8; |  | ||||||
|  |  | ||||||
|     info_begin = read_off(bs, offset); |     info_begin=read_off(s->fd); | ||||||
|     if (info_begin == 0) { |     if(info_begin==0) | ||||||
|  | 	goto fail; | ||||||
|  |     if(lseek(s->fd,info_begin,SEEK_SET)<0) | ||||||
|  | 	goto fail; | ||||||
|  |     if(read_uint32(s->fd)!=0x100) | ||||||
|  | 	goto fail; | ||||||
|  |     if((count = read_uint32(s->fd))==0) | ||||||
|  | 	goto fail; | ||||||
|  |     info_end = info_begin+count; | ||||||
|  |     if(lseek(s->fd,0xf8,SEEK_CUR)<0) | ||||||
| 	goto fail; | 	goto fail; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (read_uint32(bs, info_begin) != 0x100) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     count = read_uint32(bs, info_begin + 4); |  | ||||||
|     if (count == 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|     info_end = info_begin + count; |  | ||||||
|  |  | ||||||
|     offset = info_begin + 0x100; |  | ||||||
|  |  | ||||||
|     /* read offsets */ |     /* read offsets */ | ||||||
|     last_in_offset = last_out_offset = 0; |     last_in_offset = last_out_offset = 0; | ||||||
|     while (offset < info_end) { |     while(lseek(s->fd,0,SEEK_CUR)<info_end) { | ||||||
|         uint32_t type; |         uint32_t type; | ||||||
|  |  | ||||||
| 	count = read_uint32(bs, offset); | 	count = read_uint32(s->fd); | ||||||
| 	if(count==0) | 	if(count==0) | ||||||
| 	    goto fail; | 	    goto fail; | ||||||
|         offset += 4; | 	type = read_uint32(s->fd); | ||||||
|  | 	if(type!=0x6d697368 || count<244) | ||||||
| 	type = read_uint32(bs, offset); | 	    lseek(s->fd,count-4,SEEK_CUR); | ||||||
| 	if (type == 0x6d697368 && count >= 244) { | 	else { | ||||||
| 	    int new_size, chunk_count; | 	    int new_size, chunk_count; | ||||||
|  | 	    if(lseek(s->fd,200,SEEK_CUR)<0) | ||||||
|             offset += 4; | 	        goto fail; | ||||||
|             offset += 200; |  | ||||||
|  |  | ||||||
| 	    chunk_count = (count-204)/40; | 	    chunk_count = (count-204)/40; | ||||||
| 	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); | 	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); | ||||||
| 	    s->types = qemu_realloc(s->types, new_size/2); | 	    s->types = qemu_realloc(s->types, new_size/2); | ||||||
| @@ -134,8 +130,7 @@ static int dmg_open(BlockDriverState *bs, int flags) | |||||||
| 	    s->sectorcounts = qemu_realloc(s->sectorcounts, new_size); | 	    s->sectorcounts = qemu_realloc(s->sectorcounts, new_size); | ||||||
|  |  | ||||||
| 	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) { | 	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) { | ||||||
| 		s->types[i] = read_uint32(bs, offset); | 		s->types[i] = read_uint32(s->fd); | ||||||
| 		offset += 4; |  | ||||||
| 		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { | 		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { | ||||||
| 		    if(s->types[i]==0xffffffff) { | 		    if(s->types[i]==0xffffffff) { | ||||||
| 			last_in_offset = s->offsets[i-1]+s->lengths[i-1]; | 			last_in_offset = s->offsets[i-1]+s->lengths[i-1]; | ||||||
| @@ -143,23 +138,15 @@ static int dmg_open(BlockDriverState *bs, int flags) | |||||||
| 		    } | 		    } | ||||||
| 		    chunk_count--; | 		    chunk_count--; | ||||||
| 		    i--; | 		    i--; | ||||||
| 		    offset += 36; | 		    if(lseek(s->fd,36,SEEK_CUR)<0) | ||||||
|  | 			goto fail; | ||||||
| 		    continue; | 		    continue; | ||||||
| 		} | 		} | ||||||
| 		offset += 4; | 		read_uint32(s->fd); | ||||||
|  | 		s->sectors[i] = last_out_offset+read_off(s->fd); | ||||||
| 		s->sectors[i] = last_out_offset+read_off(bs, offset); | 		s->sectorcounts[i] = read_off(s->fd); | ||||||
| 		offset += 8; | 		s->offsets[i] = last_in_offset+read_off(s->fd); | ||||||
|  | 		s->lengths[i] = read_off(s->fd); | ||||||
| 		s->sectorcounts[i] = read_off(bs, offset); |  | ||||||
| 		offset += 8; |  | ||||||
|  |  | ||||||
| 		s->offsets[i] = last_in_offset+read_off(bs, offset); |  | ||||||
| 		offset += 8; |  | ||||||
|  |  | ||||||
| 		s->lengths[i] = read_off(bs, offset); |  | ||||||
| 		offset += 8; |  | ||||||
|  |  | ||||||
| 		if(s->lengths[i]>max_compressed_size) | 		if(s->lengths[i]>max_compressed_size) | ||||||
| 		    max_compressed_size = s->lengths[i]; | 		    max_compressed_size = s->lengths[i]; | ||||||
| 		if(s->sectorcounts[i]>max_sectors_per_chunk) | 		if(s->sectorcounts[i]>max_sectors_per_chunk) | ||||||
| @@ -179,6 +166,7 @@ static int dmg_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| fail: | fail: | ||||||
|  |     close(s->fd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -208,10 +196,8 @@ static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num) | |||||||
|     return s->n_chunks; /* error */ |     return s->n_chunks; /* error */ | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num) | ||||||
| { | { | ||||||
|     BDRVDMGState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { |     if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { | ||||||
| 	int ret; | 	int ret; | ||||||
| 	uint32_t chunk = search_chunk(s,sector_num); | 	uint32_t chunk = search_chunk(s,sector_num); | ||||||
| @@ -224,12 +210,15 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | |||||||
| 	case 0x80000005: { /* zlib compressed */ | 	case 0x80000005: { /* zlib compressed */ | ||||||
| 	    int i; | 	    int i; | ||||||
|  |  | ||||||
|  | 	    ret = lseek(s->fd, s->offsets[chunk], SEEK_SET); | ||||||
|  | 	    if(ret<0) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
| 	    /* we need to buffer, because only the chunk as whole can be | 	    /* we need to buffer, because only the chunk as whole can be | ||||||
| 	     * inflated. */ | 	     * inflated. */ | ||||||
| 	    i=0; | 	    i=0; | ||||||
| 	    do { | 	    do { | ||||||
|                 ret = bdrv_pread(bs->file, s->offsets[chunk] + i, | 		ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i); | ||||||
|                                  s->compressed_chunk+i, s->lengths[chunk]-i); |  | ||||||
| 		if(ret<0 && errno==EINTR) | 		if(ret<0 && errno==EINTR) | ||||||
| 		    ret=0; | 		    ret=0; | ||||||
| 		i+=ret; | 		i+=ret; | ||||||
| @@ -250,8 +239,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | |||||||
| 		return -1; | 		return -1; | ||||||
| 	    break; } | 	    break; } | ||||||
| 	case 1: /* copy */ | 	case 1: /* copy */ | ||||||
| 	    ret = bdrv_pread(bs->file, s->offsets[chunk], | 	    ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]); | ||||||
|                              s->uncompressed_chunk, s->lengths[chunk]); |  | ||||||
| 	    if (ret != s->lengths[chunk]) | 	    if (ret != s->lengths[chunk]) | ||||||
| 		return -1; | 		return -1; | ||||||
| 	    break; | 	    break; | ||||||
| @@ -272,7 +260,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|  |  | ||||||
|     for(i=0;i<nb_sectors;i++) { |     for(i=0;i<nb_sectors;i++) { | ||||||
| 	uint32_t sector_offset_in_chunk; | 	uint32_t sector_offset_in_chunk; | ||||||
| 	if(dmg_read_chunk(bs, sector_num+i) != 0) | 	if(dmg_read_chunk(s, sector_num+i) != 0) | ||||||
| 	    return -1; | 	    return -1; | ||||||
| 	sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk]; | 	sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk]; | ||||||
| 	memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); | 	memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); | ||||||
| @@ -283,6 +271,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, | |||||||
| static void dmg_close(BlockDriverState *bs) | static void dmg_close(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVDMGState *s = bs->opaque; |     BDRVDMGState *s = bs->opaque; | ||||||
|  |     close(s->fd); | ||||||
|     if(s->n_chunks>0) { |     if(s->n_chunks>0) { | ||||||
| 	free(s->types); | 	free(s->types); | ||||||
| 	free(s->offsets); | 	free(s->offsets); | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								block/nbd.c
									
									
									
									
									
								
							| @@ -33,8 +33,6 @@ | |||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #define EN_OPTSTR ":exportname=" |  | ||||||
|  |  | ||||||
| typedef struct BDRVNBDState { | typedef struct BDRVNBDState { | ||||||
|     int sock; |     int sock; | ||||||
|     off_t size; |     off_t size; | ||||||
| @@ -44,81 +42,58 @@ typedef struct BDRVNBDState { | |||||||
| static int nbd_open(BlockDriverState *bs, const char* filename, int flags) | static int nbd_open(BlockDriverState *bs, const char* filename, int flags) | ||||||
| { | { | ||||||
|     BDRVNBDState *s = bs->opaque; |     BDRVNBDState *s = bs->opaque; | ||||||
|     uint32_t nbdflags; |  | ||||||
|  |  | ||||||
|     char *file; |  | ||||||
|     char *name; |  | ||||||
|     const char *host; |     const char *host; | ||||||
|     const char *unixpath; |     const char *unixpath; | ||||||
|     int sock; |     int sock; | ||||||
|     off_t size; |     off_t size; | ||||||
|     size_t blocksize; |     size_t blocksize; | ||||||
|     int ret; |     int ret; | ||||||
|     int err = -EINVAL; |  | ||||||
|  |  | ||||||
|     file = qemu_strdup(filename); |     if ((flags & BDRV_O_CREAT)) | ||||||
|  |         return -EINVAL; | ||||||
|  |  | ||||||
|     name = strstr(file, EN_OPTSTR); |     if (!strstart(filename, "nbd:", &host)) | ||||||
|     if (name) { |         return -EINVAL; | ||||||
|         if (name[strlen(EN_OPTSTR)] == 0) { |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|         name[0] = 0; |  | ||||||
|         name += strlen(EN_OPTSTR); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!strstart(file, "nbd:", &host)) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (strstart(host, "unix:", &unixpath)) { |     if (strstart(host, "unix:", &unixpath)) { | ||||||
|  |  | ||||||
|         if (unixpath[0] != '/') { |         if (unixpath[0] != '/') | ||||||
|             goto out; |             return -EINVAL; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sock = unix_socket_outgoing(unixpath); |         sock = unix_socket_outgoing(unixpath); | ||||||
|  |  | ||||||
|     } else { |     } else { | ||||||
|         uint16_t port = NBD_DEFAULT_PORT; |         uint16_t port; | ||||||
|         char *p, *r; |         char *p, *r; | ||||||
|         char hostname[128]; |         char hostname[128]; | ||||||
|  |  | ||||||
|         pstrcpy(hostname, 128, host); |         pstrcpy(hostname, 128, host); | ||||||
|  |  | ||||||
|         p = strchr(hostname, ':'); |         p = strchr(hostname, ':'); | ||||||
|         if (p != NULL) { |         if (p == NULL) | ||||||
|             *p = '\0'; |             return -EINVAL; | ||||||
|             p++; |  | ||||||
|  |  | ||||||
|             port = strtol(p, &r, 0); |         *p = '\0'; | ||||||
|             if (r == p) { |         p++; | ||||||
|                 goto out; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |         port = strtol(p, &r, 0); | ||||||
|  |         if (r == p) | ||||||
|  |             return -EINVAL; | ||||||
|         sock = tcp_socket_outgoing(hostname, port); |         sock = tcp_socket_outgoing(hostname, port); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (sock == -1) { |     if (sock == -1) | ||||||
|         err = -errno; |         return -errno; | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); |     ret = nbd_receive_negotiate(sock, &size, &blocksize); | ||||||
|     if (ret == -1) { |     if (ret == -1) | ||||||
|         err = -errno; |         return -errno; | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->sock = sock; |     s->sock = sock; | ||||||
|     s->size = size; |     s->size = size; | ||||||
|     s->blocksize = blocksize; |     s->blocksize = blocksize; | ||||||
|     err = 0; |  | ||||||
|  |  | ||||||
| out: |     return 0; | ||||||
|     qemu_free(file); |  | ||||||
|     return err; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_read(BlockDriverState *bs, int64_t sector_num, | static int nbd_read(BlockDriverState *bs, int64_t sector_num, | ||||||
| @@ -205,7 +180,7 @@ static int64_t nbd_getlength(BlockDriverState *bs) | |||||||
| static BlockDriver bdrv_nbd = { | static BlockDriver bdrv_nbd = { | ||||||
|     .format_name	= "nbd", |     .format_name	= "nbd", | ||||||
|     .instance_size	= sizeof(BDRVNBDState), |     .instance_size	= sizeof(BDRVNBDState), | ||||||
|     .bdrv_file_open	= nbd_open, |     .bdrv_open		= nbd_open, | ||||||
|     .bdrv_read		= nbd_read, |     .bdrv_read		= nbd_read, | ||||||
|     .bdrv_write		= nbd_write, |     .bdrv_write		= nbd_write, | ||||||
|     .bdrv_close		= nbd_close, |     .bdrv_close		= nbd_close, | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ struct parallels_header { | |||||||
| } __attribute__((packed)); | } __attribute__((packed)); | ||||||
|  |  | ||||||
| typedef struct BDRVParallelsState { | typedef struct BDRVParallelsState { | ||||||
|  |     int fd; | ||||||
|  |  | ||||||
|     uint32_t *catalog_bitmap; |     uint32_t *catalog_bitmap; | ||||||
|     int catalog_size; |     int catalog_size; | ||||||
| @@ -67,15 +68,24 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parallels_open(BlockDriverState *bs, int flags) | static int parallels_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVParallelsState *s = bs->opaque; |     BDRVParallelsState *s = bs->opaque; | ||||||
|     int i; |     int fd, i; | ||||||
|     struct parallels_header ph; |     struct parallels_header ph; | ||||||
|  |  | ||||||
|  |     fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||||
|  |         if (fd < 0) | ||||||
|  |             return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->read_only = 1; // no write support yet |     bs->read_only = 1; // no write support yet | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph)) |     s->fd = fd; | ||||||
|  |  | ||||||
|  |     if (read(fd, &ph, sizeof(ph)) != sizeof(ph)) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     if (memcmp(ph.magic, HEADER_MAGIC, 16) || |     if (memcmp(ph.magic, HEADER_MAGIC, 16) || | ||||||
| @@ -85,11 +95,14 @@ static int parallels_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     bs->total_sectors = le32_to_cpu(ph.nb_sectors); |     bs->total_sectors = le32_to_cpu(ph.nb_sectors); | ||||||
|  |  | ||||||
|  |     if (lseek(s->fd, 64, SEEK_SET) != 64) | ||||||
|  | 	goto fail; | ||||||
|  |  | ||||||
|     s->tracks = le32_to_cpu(ph.tracks); |     s->tracks = le32_to_cpu(ph.tracks); | ||||||
|  |  | ||||||
|     s->catalog_size = le32_to_cpu(ph.catalog_entries); |     s->catalog_size = le32_to_cpu(ph.catalog_entries); | ||||||
|     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); |     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); | ||||||
|     if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) != |     if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != | ||||||
| 	s->catalog_size * 4) | 	s->catalog_size * 4) | ||||||
| 	goto fail; | 	goto fail; | ||||||
|     for (i = 0; i < s->catalog_size; i++) |     for (i = 0; i < s->catalog_size; i++) | ||||||
| @@ -99,34 +112,45 @@ static int parallels_open(BlockDriverState *bs, int flags) | |||||||
| fail: | fail: | ||||||
|     if (s->catalog_bitmap) |     if (s->catalog_bitmap) | ||||||
| 	qemu_free(s->catalog_bitmap); | 	qemu_free(s->catalog_bitmap); | ||||||
|  |     close(fd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||||
| { | { | ||||||
|     BDRVParallelsState *s = bs->opaque; |     BDRVParallelsState *s = bs->opaque; | ||||||
|     uint32_t index, offset; |     uint32_t index, offset; | ||||||
|  |     uint64_t position; | ||||||
|  |  | ||||||
|     index = sector_num / s->tracks; |     index = sector_num / s->tracks; | ||||||
|     offset = sector_num % s->tracks; |     offset = sector_num % s->tracks; | ||||||
|  |  | ||||||
|     /* not allocated */ |     // not allocated | ||||||
|     if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0)) |     if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0)) | ||||||
| 	return -1; | 	return -1; | ||||||
|     return (uint64_t)(s->catalog_bitmap[index] + offset) * 512; |  | ||||||
|  |     position = (uint64_t)(s->catalog_bitmap[index] + offset) * 512; | ||||||
|  |  | ||||||
|  | //    fprintf(stderr, "sector: %llx index=%x offset=%x pointer=%x position=%x\n", | ||||||
|  | //	sector_num, index, offset, s->catalog_bitmap[index], position); | ||||||
|  |  | ||||||
|  |     if (lseek(s->fd, position, SEEK_SET) != position) | ||||||
|  | 	return -1; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parallels_read(BlockDriverState *bs, int64_t sector_num, | static int parallels_read(BlockDriverState *bs, int64_t sector_num, | ||||||
|                     uint8_t *buf, int nb_sectors) |                     uint8_t *buf, int nb_sectors) | ||||||
| { | { | ||||||
|  |     BDRVParallelsState *s = bs->opaque; | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         int64_t position = seek_to_sector(bs, sector_num); | 	if (!seek_to_sector(bs, sector_num)) { | ||||||
|         if (position >= 0) { | 	    if (read(s->fd, buf, 512) != 512) | ||||||
|             if (bdrv_pread(bs->file, position, buf, 512) != 512) | 		return -1; | ||||||
|                 return -1; | 	} else | ||||||
|         } else { |  | ||||||
|             memset(buf, 0, 512); |             memset(buf, 0, 512); | ||||||
|         } |  | ||||||
|         nb_sectors--; |         nb_sectors--; | ||||||
|         sector_num++; |         sector_num++; | ||||||
|         buf += 512; |         buf += 512; | ||||||
| @@ -138,6 +162,7 @@ static void parallels_close(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     BDRVParallelsState *s = bs->opaque; |     BDRVParallelsState *s = bs->opaque; | ||||||
|     qemu_free(s->catalog_bitmap); |     qemu_free(s->catalog_bitmap); | ||||||
|  |     close(s->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockDriver bdrv_parallels = { | static BlockDriver bdrv_parallels = { | ||||||
|   | |||||||
							
								
								
									
										103
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -54,6 +54,7 @@ typedef struct QCowHeader { | |||||||
| #define L2_CACHE_SIZE 16 | #define L2_CACHE_SIZE 16 | ||||||
|  |  | ||||||
| typedef struct BDRVQcowState { | typedef struct BDRVQcowState { | ||||||
|  |     BlockDriverState *hd; | ||||||
|     int cluster_bits; |     int cluster_bits; | ||||||
|     int cluster_size; |     int cluster_size; | ||||||
|     int cluster_sectors; |     int cluster_sectors; | ||||||
| @@ -75,7 +76,7 @@ typedef struct BDRVQcowState { | |||||||
|     AES_KEY aes_decrypt_key; |     AES_KEY aes_decrypt_key; | ||||||
| } BDRVQcowState; | } BDRVQcowState; | ||||||
|  |  | ||||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); | ||||||
|  |  | ||||||
| static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||||
| { | { | ||||||
| @@ -89,13 +90,16 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|         return 0; |         return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow_open(BlockDriverState *bs, int flags) | static int qcow_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int len, i, shift; |     int len, i, shift, ret; | ||||||
|     QCowHeader header; |     QCowHeader header; | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header)) |     ret = bdrv_file_open(&s->hd, filename, flags); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  |     if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) | ||||||
|         goto fail; |         goto fail; | ||||||
|     be32_to_cpus(&header.magic); |     be32_to_cpus(&header.magic); | ||||||
|     be32_to_cpus(&header.version); |     be32_to_cpus(&header.version); | ||||||
| @@ -131,7 +135,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | |||||||
|     s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); |     s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); | ||||||
|     if (!s->l1_table) |     if (!s->l1_table) | ||||||
|         goto fail; |         goto fail; | ||||||
|     if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != |     if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != | ||||||
|         s->l1_size * sizeof(uint64_t)) |         s->l1_size * sizeof(uint64_t)) | ||||||
|         goto fail; |         goto fail; | ||||||
|     for(i = 0;i < s->l1_size; i++) { |     for(i = 0;i < s->l1_size; i++) { | ||||||
| @@ -154,7 +158,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | |||||||
|         len = header.backing_file_size; |         len = header.backing_file_size; | ||||||
|         if (len > 1023) |         if (len > 1023) | ||||||
|             len = 1023; |             len = 1023; | ||||||
|         if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len) |         if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) | ||||||
|             goto fail; |             goto fail; | ||||||
|         bs->backing_file[len] = '\0'; |         bs->backing_file[len] = '\0'; | ||||||
|     } |     } | ||||||
| @@ -165,6 +169,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | |||||||
|     qemu_free(s->l2_cache); |     qemu_free(s->l2_cache); | ||||||
|     qemu_free(s->cluster_cache); |     qemu_free(s->cluster_cache); | ||||||
|     qemu_free(s->cluster_data); |     qemu_free(s->cluster_data); | ||||||
|  |     bdrv_delete(s->hd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -266,13 +271,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|         if (!allocate) |         if (!allocate) | ||||||
|             return 0; |             return 0; | ||||||
|         /* allocate a new l2 entry */ |         /* allocate a new l2 entry */ | ||||||
|         l2_offset = bdrv_getlength(bs->file); |         l2_offset = bdrv_getlength(s->hd); | ||||||
|         /* round to cluster size */ |         /* round to cluster size */ | ||||||
|         l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); |         l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); | ||||||
|         /* update the L1 entry */ |         /* update the L1 entry */ | ||||||
|         s->l1_table[l1_index] = l2_offset; |         s->l1_table[l1_index] = l2_offset; | ||||||
|         tmp = cpu_to_be64(l2_offset); |         tmp = cpu_to_be64(l2_offset); | ||||||
|         if (bdrv_pwrite_sync(bs->file, |         if (bdrv_pwrite_sync(s->hd, | ||||||
|                 s->l1_table_offset + l1_index * sizeof(tmp), |                 s->l1_table_offset + l1_index * sizeof(tmp), | ||||||
|                 &tmp, sizeof(tmp)) < 0) |                 &tmp, sizeof(tmp)) < 0) | ||||||
|             return 0; |             return 0; | ||||||
| @@ -302,11 +307,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|     l2_table = s->l2_cache + (min_index << s->l2_bits); |     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||||
|     if (new_l2_table) { |     if (new_l2_table) { | ||||||
|         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); |         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, |         if (bdrv_pwrite_sync(s->hd, l2_offset, l2_table, | ||||||
|                 s->l2_size * sizeof(uint64_t)) < 0) |                 s->l2_size * sizeof(uint64_t)) < 0) | ||||||
|             return 0; |             return 0; | ||||||
|     } else { |     } else { | ||||||
|         if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != |         if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||||
|             s->l2_size * sizeof(uint64_t)) |             s->l2_size * sizeof(uint64_t)) | ||||||
|             return 0; |             return 0; | ||||||
|     } |     } | ||||||
| @@ -325,22 +330,22 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|             /* if the cluster is already compressed, we must |             /* if the cluster is already compressed, we must | ||||||
|                decompress it in the case it is not completely |                decompress it in the case it is not completely | ||||||
|                overwritten */ |                overwritten */ | ||||||
|             if (decompress_cluster(bs, cluster_offset) < 0) |             if (decompress_cluster(s, cluster_offset) < 0) | ||||||
|                 return 0; |                 return 0; | ||||||
|             cluster_offset = bdrv_getlength(bs->file); |             cluster_offset = bdrv_getlength(s->hd); | ||||||
|             cluster_offset = (cluster_offset + s->cluster_size - 1) & |             cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||||
|                 ~(s->cluster_size - 1); |                 ~(s->cluster_size - 1); | ||||||
|             /* write the cluster content */ |             /* write the cluster content */ | ||||||
|             if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != |             if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) != | ||||||
|                 s->cluster_size) |                 s->cluster_size) | ||||||
|                 return -1; |                 return -1; | ||||||
|         } else { |         } else { | ||||||
|             cluster_offset = bdrv_getlength(bs->file); |             cluster_offset = bdrv_getlength(s->hd); | ||||||
|             if (allocate == 1) { |             if (allocate == 1) { | ||||||
|                 /* round to cluster size */ |                 /* round to cluster size */ | ||||||
|                 cluster_offset = (cluster_offset + s->cluster_size - 1) & |                 cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||||
|                     ~(s->cluster_size - 1); |                     ~(s->cluster_size - 1); | ||||||
|                 bdrv_truncate(bs->file, cluster_offset + s->cluster_size); |                 bdrv_truncate(s->hd, cluster_offset + s->cluster_size); | ||||||
|                 /* if encrypted, we must initialize the cluster |                 /* if encrypted, we must initialize the cluster | ||||||
|                    content which won't be written */ |                    content which won't be written */ | ||||||
|                 if (s->crypt_method && |                 if (s->crypt_method && | ||||||
| @@ -354,7 +359,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|                                             s->cluster_data, |                                             s->cluster_data, | ||||||
|                                             s->cluster_data + 512, 1, 1, |                                             s->cluster_data + 512, 1, 1, | ||||||
|                                             &s->aes_encrypt_key); |                                             &s->aes_encrypt_key); | ||||||
|                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512, |                             if (bdrv_pwrite(s->hd, cluster_offset + i * 512, | ||||||
|                                             s->cluster_data, 512) != 512) |                                             s->cluster_data, 512) != 512) | ||||||
|                                 return -1; |                                 return -1; | ||||||
|                         } |                         } | ||||||
| @@ -368,7 +373,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | |||||||
|         /* update L2 table */ |         /* update L2 table */ | ||||||
|         tmp = cpu_to_be64(cluster_offset); |         tmp = cpu_to_be64(cluster_offset); | ||||||
|         l2_table[l2_index] = tmp; |         l2_table[l2_index] = tmp; | ||||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), |         if (bdrv_pwrite_sync(s->hd, l2_offset + l2_index * sizeof(tmp), | ||||||
|                 &tmp, sizeof(tmp)) < 0) |                 &tmp, sizeof(tmp)) < 0) | ||||||
|             return 0; |             return 0; | ||||||
|     } |     } | ||||||
| @@ -418,9 +423,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     int ret, csize; |     int ret, csize; | ||||||
|     uint64_t coffset; |     uint64_t coffset; | ||||||
|  |  | ||||||
| @@ -428,7 +432,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | |||||||
|     if (s->cluster_cache_offset != coffset) { |     if (s->cluster_cache_offset != coffset) { | ||||||
|         csize = cluster_offset >> (63 - s->cluster_bits); |         csize = cluster_offset >> (63 - s->cluster_bits); | ||||||
|         csize &= (s->cluster_size - 1); |         csize &= (s->cluster_size - 1); | ||||||
|         ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); |         ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize); | ||||||
|         if (ret != csize) |         if (ret != csize) | ||||||
|             return -1; |             return -1; | ||||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, |         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||||
| @@ -465,11 +469,11 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|                 memset(buf, 0, 512 * n); |                 memset(buf, 0, 512 * n); | ||||||
|             } |             } | ||||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { |         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||||
|             if (decompress_cluster(bs, cluster_offset) < 0) |             if (decompress_cluster(s, cluster_offset) < 0) | ||||||
|                 return -1; |                 return -1; | ||||||
|             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); |             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||||
|         } else { |         } else { | ||||||
|             ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); |             ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||||
|             if (ret != n * 512) |             if (ret != n * 512) | ||||||
|                 return -1; |                 return -1; | ||||||
|             if (s->crypt_method) { |             if (s->crypt_method) { | ||||||
| @@ -502,7 +506,7 @@ typedef struct QCowAIOCB { | |||||||
|  |  | ||||||
| static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) | static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) | ||||||
| { | { | ||||||
|     QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common); |     QCowAIOCB *acb = (QCowAIOCB *)blockacb; | ||||||
|     if (acb->hd_aiocb) |     if (acb->hd_aiocb) | ||||||
|         bdrv_aio_cancel(acb->hd_aiocb); |         bdrv_aio_cancel(acb->hd_aiocb); | ||||||
|     qemu_aio_release(acb); |     qemu_aio_release(acb); | ||||||
| @@ -598,7 +602,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) | |||||||
|         } |         } | ||||||
|     } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { |     } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||||
|         /* add AIO support for compressed blocks ? */ |         /* add AIO support for compressed blocks ? */ | ||||||
|         if (decompress_cluster(bs, acb->cluster_offset) < 0) |         if (decompress_cluster(s, acb->cluster_offset) < 0) | ||||||
|             goto done; |             goto done; | ||||||
|         memcpy(acb->buf, |         memcpy(acb->buf, | ||||||
|                s->cluster_cache + index_in_cluster * 512, 512 * acb->n); |                s->cluster_cache + index_in_cluster * 512, 512 * acb->n); | ||||||
| @@ -611,7 +615,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) | |||||||
|         acb->hd_iov.iov_base = (void *)acb->buf; |         acb->hd_iov.iov_base = (void *)acb->buf; | ||||||
|         acb->hd_iov.iov_len = acb->n * 512; |         acb->hd_iov.iov_len = acb->n * 512; | ||||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|         acb->hd_aiocb = bdrv_aio_readv(bs->file, |         acb->hd_aiocb = bdrv_aio_readv(s->hd, | ||||||
|                             (acb->cluster_offset >> 9) + index_in_cluster, |                             (acb->cluster_offset >> 9) + index_in_cluster, | ||||||
|                             &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); |                             &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); | ||||||
|         if (acb->hd_aiocb == NULL) |         if (acb->hd_aiocb == NULL) | ||||||
| @@ -696,7 +700,7 @@ static void qcow_aio_write_cb(void *opaque, int ret) | |||||||
|     acb->hd_iov.iov_base = (void *)src_buf; |     acb->hd_iov.iov_base = (void *)src_buf; | ||||||
|     acb->hd_iov.iov_len = acb->n * 512; |     acb->hd_iov.iov_len = acb->n * 512; | ||||||
|     qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |     qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|     acb->hd_aiocb = bdrv_aio_writev(bs->file, |     acb->hd_aiocb = bdrv_aio_writev(s->hd, | ||||||
|                                     (cluster_offset >> 9) + index_in_cluster, |                                     (cluster_offset >> 9) + index_in_cluster, | ||||||
|                                     &acb->hd_qiov, acb->n, |                                     &acb->hd_qiov, acb->n, | ||||||
|                                     qcow_aio_write_cb, acb); |                                     qcow_aio_write_cb, acb); | ||||||
| @@ -736,6 +740,7 @@ static void qcow_close(BlockDriverState *bs) | |||||||
|     qemu_free(s->l2_cache); |     qemu_free(s->l2_cache); | ||||||
|     qemu_free(s->cluster_cache); |     qemu_free(s->cluster_cache); | ||||||
|     qemu_free(s->cluster_data); |     qemu_free(s->cluster_data); | ||||||
|  |     bdrv_delete(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow_create(const char *filename, QEMUOptionParameter *options) | static int qcow_create(const char *filename, QEMUOptionParameter *options) | ||||||
| @@ -746,7 +751,6 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     int64_t total_size = 0; |     int64_t total_size = 0; | ||||||
|     const char *backing_file = NULL; |     const char *backing_file = NULL; | ||||||
|     int flags = 0; |     int flags = 0; | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     /* Read out options */ |     /* Read out options */ | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
| @@ -762,7 +766,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|  |  | ||||||
|     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); |     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); | ||||||
|     if (fd < 0) |     if (fd < 0) | ||||||
|         return -errno; |         return -1; | ||||||
|     memset(&header, 0, sizeof(header)); |     memset(&header, 0, sizeof(header)); | ||||||
|     header.magic = cpu_to_be32(QCOW_MAGIC); |     header.magic = cpu_to_be32(QCOW_MAGIC); | ||||||
|     header.version = cpu_to_be32(QCOW_VERSION); |     header.version = cpu_to_be32(QCOW_VERSION); | ||||||
| @@ -798,34 +802,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* write all the data */ |     /* write all the data */ | ||||||
|     ret = qemu_write_full(fd, &header, sizeof(header)); |     write(fd, &header, sizeof(header)); | ||||||
|     if (ret != sizeof(header)) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (backing_file) { |     if (backing_file) { | ||||||
|         ret = qemu_write_full(fd, backing_file, backing_filename_len); |         write(fd, backing_file, backing_filename_len); | ||||||
|         if (ret != backing_filename_len) { |  | ||||||
|             ret = -errno; |  | ||||||
|             goto exit; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |     } | ||||||
|     lseek(fd, header_size, SEEK_SET); |     lseek(fd, header_size, SEEK_SET); | ||||||
|     tmp = 0; |     tmp = 0; | ||||||
|     for(i = 0;i < l1_size; i++) { |     for(i = 0;i < l1_size; i++) { | ||||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); |         write(fd, &tmp, sizeof(tmp)); | ||||||
|         if (ret != sizeof(tmp)) { |  | ||||||
|             ret = -errno; |  | ||||||
|             goto exit; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| exit: |  | ||||||
|     close(fd); |     close(fd); | ||||||
|     return ret; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow_make_empty(BlockDriverState *bs) | static int qcow_make_empty(BlockDriverState *bs) | ||||||
| @@ -835,10 +822,10 @@ static int qcow_make_empty(BlockDriverState *bs) | |||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     memset(s->l1_table, 0, l1_length); |     memset(s->l1_table, 0, l1_length); | ||||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, |     if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, s->l1_table, | ||||||
|             l1_length) < 0) |             l1_length) < 0) | ||||||
|         return -1; |         return -1; | ||||||
|     ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); |     ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|  |  | ||||||
| @@ -899,7 +886,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, | |||||||
|         cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, |         cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, | ||||||
|                                             out_len, 0, 0); |                                             out_len, 0, 0); | ||||||
|         cluster_offset &= s->cluster_offset_mask; |         cluster_offset &= s->cluster_offset_mask; | ||||||
|         if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) { |         if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { | ||||||
|             qemu_free(out_buf); |             qemu_free(out_buf); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -909,15 +896,10 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow_flush(BlockDriverState *bs) | static void qcow_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     return bdrv_flush(bs->file); |     BDRVQcowState *s = bs->opaque; | ||||||
| } |     bdrv_flush(s->hd); | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs, |  | ||||||
|         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     return bdrv_aio_flush(bs->file, cb, opaque); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||||
| @@ -960,7 +942,6 @@ static BlockDriver bdrv_qcow = { | |||||||
|     .bdrv_make_empty	= qcow_make_empty, |     .bdrv_make_empty	= qcow_make_empty, | ||||||
|     .bdrv_aio_readv	= qcow_aio_readv, |     .bdrv_aio_readv	= qcow_aio_readv, | ||||||
|     .bdrv_aio_writev	= qcow_aio_writev, |     .bdrv_aio_writev	= qcow_aio_writev, | ||||||
|     .bdrv_aio_flush	= qcow_aio_flush, |  | ||||||
|     .bdrv_write_compressed = qcow_write_compressed, |     .bdrv_write_compressed = qcow_write_compressed, | ||||||
|     .bdrv_get_info	= qcow_get_info, |     .bdrv_get_info	= qcow_get_info, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,314 +0,0 @@ | |||||||
| /* |  | ||||||
|  * L2/refcount table cache for the QCOW2 format |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "block_int.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "qcow2.h" |  | ||||||
|  |  | ||||||
| typedef struct Qcow2CachedTable { |  | ||||||
|     void*   table; |  | ||||||
|     int64_t offset; |  | ||||||
|     bool    dirty; |  | ||||||
|     int     cache_hits; |  | ||||||
|     int     ref; |  | ||||||
| } Qcow2CachedTable; |  | ||||||
|  |  | ||||||
| struct Qcow2Cache { |  | ||||||
|     Qcow2CachedTable*       entries; |  | ||||||
|     struct Qcow2Cache*      depends; |  | ||||||
|     int                     size; |  | ||||||
|     bool                    depends_on_flush; |  | ||||||
|     bool                    writethrough; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, |  | ||||||
|     bool writethrough) |  | ||||||
| { |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     Qcow2Cache *c; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     c = qemu_mallocz(sizeof(*c)); |  | ||||||
|     c->size = num_tables; |  | ||||||
|     c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables); |  | ||||||
|     c->writethrough = writethrough; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         c->entries[i].table = qemu_blockalign(bs, s->cluster_size); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return c; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         assert(c->entries[i].ref == 0); |  | ||||||
|         qemu_vfree(c->entries[i].table); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_free(c->entries); |  | ||||||
|     qemu_free(c); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     ret = qcow2_cache_flush(bs, c->depends); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     c->depends = NULL; |  | ||||||
|     c->depends_on_flush = false; |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) |  | ||||||
| { |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     int ret = 0; |  | ||||||
|  |  | ||||||
|     if (!c->entries[i].dirty || !c->entries[i].offset) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (c->depends) { |  | ||||||
|         ret = qcow2_cache_flush_dependency(bs, c); |  | ||||||
|     } else if (c->depends_on_flush) { |  | ||||||
|         ret = bdrv_flush(bs->file); |  | ||||||
|         if (ret >= 0) { |  | ||||||
|             c->depends_on_flush = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (c == s->refcount_block_cache) { |  | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); |  | ||||||
|     } else if (c == s->l2_table_cache) { |  | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, |  | ||||||
|         s->cluster_size); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     c->entries[i].dirty = false; |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) |  | ||||||
| { |  | ||||||
|     int result = 0; |  | ||||||
|     int ret; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         ret = qcow2_cache_entry_flush(bs, c, i); |  | ||||||
|         if (ret < 0 && result != -ENOSPC) { |  | ||||||
|             result = ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (result == 0) { |  | ||||||
|         ret = bdrv_flush(bs->file); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             result = ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, |  | ||||||
|     Qcow2Cache *dependency) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     if (dependency->depends) { |  | ||||||
|         ret = qcow2_cache_flush_dependency(bs, dependency); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (c->depends && (c->depends != dependency)) { |  | ||||||
|         ret = qcow2_cache_flush_dependency(bs, c); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     c->depends = dependency; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qcow2_cache_depends_on_flush(Qcow2Cache *c) |  | ||||||
| { |  | ||||||
|     c->depends_on_flush = true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|     int min_count = INT_MAX; |  | ||||||
|     int min_index = -1; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         if (c->entries[i].ref) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (c->entries[i].cache_hits < min_count) { |  | ||||||
|             min_index = i; |  | ||||||
|             min_count = c->entries[i].cache_hits; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Give newer hits priority */ |  | ||||||
|         /* TODO Check how to optimize the replacement strategy */ |  | ||||||
|         c->entries[i].cache_hits /= 2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (min_index == -1) { |  | ||||||
|         /* This can't happen in current synchronous code, but leave the check |  | ||||||
|          * here as a reminder for whoever starts using AIO with the cache */ |  | ||||||
|         abort(); |  | ||||||
|     } |  | ||||||
|     return min_index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, |  | ||||||
|     uint64_t offset, void **table, bool read_from_disk) |  | ||||||
| { |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     int i; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     /* Check if the table is already cached */ |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         if (c->entries[i].offset == offset) { |  | ||||||
|             goto found; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* If not, write a table back and replace it */ |  | ||||||
|     i = qcow2_cache_find_entry_to_replace(c); |  | ||||||
|     if (i < 0) { |  | ||||||
|         return i; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qcow2_cache_entry_flush(bs, c, i); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     c->entries[i].offset = 0; |  | ||||||
|     if (read_from_disk) { |  | ||||||
|         if (c == s->l2_table_cache) { |  | ||||||
|             BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Give the table some hits for the start so that it won't be replaced |  | ||||||
|      * immediately. The number 32 is completely arbitrary. */ |  | ||||||
|     c->entries[i].cache_hits = 32; |  | ||||||
|     c->entries[i].offset = offset; |  | ||||||
|  |  | ||||||
|     /* And return the right table */ |  | ||||||
| found: |  | ||||||
|     c->entries[i].cache_hits++; |  | ||||||
|     c->entries[i].ref++; |  | ||||||
|     *table = c->entries[i].table; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, |  | ||||||
|     void **table) |  | ||||||
| { |  | ||||||
|     return qcow2_cache_do_get(bs, c, offset, table, true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, |  | ||||||
|     void **table) |  | ||||||
| { |  | ||||||
|     return qcow2_cache_do_get(bs, c, offset, table, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         if (c->entries[i].table == *table) { |  | ||||||
|             goto found; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return -ENOENT; |  | ||||||
|  |  | ||||||
| found: |  | ||||||
|     c->entries[i].ref--; |  | ||||||
|     *table = NULL; |  | ||||||
|  |  | ||||||
|     assert(c->entries[i].ref >= 0); |  | ||||||
|  |  | ||||||
|     if (c->writethrough) { |  | ||||||
|         return qcow2_cache_entry_flush(bs, c, i); |  | ||||||
|     } else { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < c->size; i++) { |  | ||||||
|         if (c->entries[i].table == table) { |  | ||||||
|             goto found; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     abort(); |  | ||||||
|  |  | ||||||
| found: |  | ||||||
|     c->entries[i].dirty = true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -28,7 +28,7 @@ | |||||||
| #include "block_int.h" | #include "block_int.h" | ||||||
| #include "block/qcow2.h" | #include "block/qcow2.h" | ||||||
|  |  | ||||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int new_l1_size, new_l1_size2, ret, i; |     int new_l1_size, new_l1_size2, ret, i; | ||||||
| @@ -36,22 +36,15 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | |||||||
|     int64_t new_l1_table_offset; |     int64_t new_l1_table_offset; | ||||||
|     uint8_t data[12]; |     uint8_t data[12]; | ||||||
|  |  | ||||||
|     if (min_size <= s->l1_size) |     new_l1_size = s->l1_size; | ||||||
|  |     if (min_size <= new_l1_size) | ||||||
|         return 0; |         return 0; | ||||||
|  |     if (new_l1_size == 0) { | ||||||
|     if (exact_size) { |         new_l1_size = 1; | ||||||
|         new_l1_size = min_size; |     } | ||||||
|     } else { |     while (min_size > new_l1_size) { | ||||||
|         /* Bump size up to reduce the number of times we have to grow */ |         new_l1_size = (new_l1_size * 3 + 1) / 2; | ||||||
|         new_l1_size = s->l1_size; |  | ||||||
|         if (new_l1_size == 0) { |  | ||||||
|             new_l1_size = 1; |  | ||||||
|         } |  | ||||||
|         while (min_size > new_l1_size) { |  | ||||||
|             new_l1_size = (new_l1_size * 3 + 1) / 2; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef DEBUG_ALLOC2 | #ifdef DEBUG_ALLOC2 | ||||||
|     printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size); |     printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size); | ||||||
| #endif | #endif | ||||||
| @@ -61,32 +54,24 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | |||||||
|     memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); |     memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); | ||||||
|  |  | ||||||
|     /* write new table (align to cluster) */ |     /* write new table (align to cluster) */ | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); |  | ||||||
|     new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); |     new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); | ||||||
|     if (new_l1_table_offset < 0) { |     if (new_l1_table_offset < 0) { | ||||||
|         qemu_free(new_l1_table); |         qemu_free(new_l1_table); | ||||||
|         return new_l1_table_offset; |         return new_l1_table_offset; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); |  | ||||||
|     for(i = 0; i < s->l1_size; i++) |     for(i = 0; i < s->l1_size; i++) | ||||||
|         new_l1_table[i] = cpu_to_be64(new_l1_table[i]); |         new_l1_table[i] = cpu_to_be64(new_l1_table[i]); | ||||||
|     ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); |     ret = bdrv_pwrite_sync(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|     for(i = 0; i < s->l1_size; i++) |     for(i = 0; i < s->l1_size; i++) | ||||||
|         new_l1_table[i] = be64_to_cpu(new_l1_table[i]); |         new_l1_table[i] = be64_to_cpu(new_l1_table[i]); | ||||||
|  |  | ||||||
|     /* set new table */ |     /* set new table */ | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); |  | ||||||
|     cpu_to_be32w((uint32_t*)data, new_l1_size); |     cpu_to_be32w((uint32_t*)data, new_l1_size); | ||||||
|     cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset); |     cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset); | ||||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); |     ret = bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, l1_size), data,sizeof(data)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
| @@ -102,6 +87,63 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void qcow2_l2_cache_reset(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  |     BDRVQcowState *s = bs->opaque; | ||||||
|  |  | ||||||
|  |     memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); | ||||||
|  |     memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); | ||||||
|  |     memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline int l2_cache_new_entry(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  |     BDRVQcowState *s = bs->opaque; | ||||||
|  |     uint32_t min_count; | ||||||
|  |     int min_index, i; | ||||||
|  |  | ||||||
|  |     /* find a new entry in the least used one */ | ||||||
|  |     min_index = 0; | ||||||
|  |     min_count = 0xffffffff; | ||||||
|  |     for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||||
|  |         if (s->l2_cache_counts[i] < min_count) { | ||||||
|  |             min_count = s->l2_cache_counts[i]; | ||||||
|  |             min_index = i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return min_index; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * seek_l2_table | ||||||
|  |  * | ||||||
|  |  * seek l2_offset in the l2_cache table | ||||||
|  |  * if not found, return NULL, | ||||||
|  |  * if found, | ||||||
|  |  *   increments the l2 cache hit count of the entry, | ||||||
|  |  *   if counter overflow, divide by two all counters | ||||||
|  |  *   return the pointer to the l2 cache entry | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset) | ||||||
|  | { | ||||||
|  |     int i, j; | ||||||
|  |  | ||||||
|  |     for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||||
|  |         if (l2_offset == s->l2_cache_offsets[i]) { | ||||||
|  |             /* increment the hit count */ | ||||||
|  |             if (++s->l2_cache_counts[i] == 0xffffffff) { | ||||||
|  |                 for(j = 0; j < L2_CACHE_SIZE; j++) { | ||||||
|  |                     s->l2_cache_counts[j] >>= 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return s->l2_cache + (i << s->l2_bits); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * l2_load |  * l2_load | ||||||
|  * |  * | ||||||
| @@ -112,15 +154,29 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | |||||||
|  * the image file failed. |  * the image file failed. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset) | ||||||
|     uint64_t **l2_table) |  | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int ret; |     int min_index; | ||||||
|  |     uint64_t *l2_table; | ||||||
|  |  | ||||||
|     ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); |     /* seek if the table for the given offset is in the cache */ | ||||||
|  |  | ||||||
|     return ret; |     l2_table = seek_l2_table(s, l2_offset); | ||||||
|  |     if (l2_table != NULL) | ||||||
|  |         return l2_table; | ||||||
|  |  | ||||||
|  |     /* not found: load a new entry in the least used one */ | ||||||
|  |  | ||||||
|  |     min_index = l2_cache_new_entry(bs); | ||||||
|  |     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||||
|  |     if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||||
|  |         s->l2_size * sizeof(uint64_t)) | ||||||
|  |         return NULL; | ||||||
|  |     s->l2_cache_offsets[min_index] = l2_offset; | ||||||
|  |     s->l2_cache_counts[min_index] = 1; | ||||||
|  |  | ||||||
|  |     return l2_table; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -128,9 +184,8 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | |||||||
|  * and we really don't want bdrv_pread to perform a read-modify-write) |  * and we really don't want bdrv_pread to perform a read-modify-write) | ||||||
|  */ |  */ | ||||||
| #define L1_ENTRIES_PER_SECTOR (512 / 8) | #define L1_ENTRIES_PER_SECTOR (512 / 8) | ||||||
| static int write_l1_entry(BlockDriverState *bs, int l1_index) | static int write_l1_entry(BDRVQcowState *s, int l1_index) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     uint64_t buf[L1_ENTRIES_PER_SECTOR]; |     uint64_t buf[L1_ENTRIES_PER_SECTOR]; | ||||||
|     int l1_start_index; |     int l1_start_index; | ||||||
|     int i, ret; |     int i, ret; | ||||||
| @@ -140,8 +195,7 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index) | |||||||
|         buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); |         buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); |     ret = bdrv_pwrite_sync(s->hd, s->l1_table_offset + 8 * l1_start_index, | ||||||
|     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, |  | ||||||
|         buf, sizeof(buf)); |         buf, sizeof(buf)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         return ret; |         return ret; | ||||||
| @@ -160,9 +214,10 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index) | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|  |     int min_index; | ||||||
|     uint64_t old_l2_offset; |     uint64_t old_l2_offset; | ||||||
|     uint64_t *l2_table; |     uint64_t *l2_table; | ||||||
|     int64_t l2_offset; |     int64_t l2_offset; | ||||||
| @@ -174,68 +229,48 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | |||||||
|  |  | ||||||
|     l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); |     l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); | ||||||
|     if (l2_offset < 0) { |     if (l2_offset < 0) { | ||||||
|         return l2_offset; |         return NULL; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* allocate a new entry in the l2 cache */ |     /* allocate a new entry in the l2 cache */ | ||||||
|  |  | ||||||
|     ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); |     min_index = l2_cache_new_entry(bs); | ||||||
|     if (ret < 0) { |     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     l2_table = *table; |  | ||||||
|  |  | ||||||
|     if (old_l2_offset == 0) { |     if (old_l2_offset == 0) { | ||||||
|         /* if there was no old l2 table, clear the new table */ |         /* if there was no old l2 table, clear the new table */ | ||||||
|         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); |         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||||||
|     } else { |     } else { | ||||||
|         uint64_t* old_table; |  | ||||||
|  |  | ||||||
|         /* if there was an old l2 table, read it from the disk */ |         /* if there was an old l2 table, read it from the disk */ | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); |         if (bdrv_pread(s->hd, old_l2_offset, | ||||||
|         ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, |                        l2_table, s->l2_size * sizeof(uint64_t)) != | ||||||
|             (void**) &old_table); |             s->l2_size * sizeof(uint64_t)) | ||||||
|         if (ret < 0) { |  | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         memcpy(l2_table, old_table, s->cluster_size); |  | ||||||
|  |  | ||||||
|         ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             goto fail; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* write the l2 table to the file */ |     /* write the l2 table to the file */ | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); |     ret = bdrv_pwrite_sync(s->hd, l2_offset, l2_table, | ||||||
|  |         s->l2_size * sizeof(uint64_t)); | ||||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); |  | ||||||
|     ret = qcow2_cache_flush(bs, s->l2_table_cache); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* update the L1 entry */ |     /* update the L1 entry */ | ||||||
|     s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; |     s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; | ||||||
|     ret = write_l1_entry(bs, l1_index); |     if (write_l1_entry(s, l1_index) < 0) { | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *table = l2_table; |     /* update the l2 cache entry */ | ||||||
|     return 0; |  | ||||||
|  |     s->l2_cache_offsets[min_index] = l2_offset; | ||||||
|  |     s->l2_cache_counts[min_index] = 1; | ||||||
|  |  | ||||||
|  |     return l2_table; | ||||||
|  |  | ||||||
| fail: | fail: | ||||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) table); |  | ||||||
|     s->l1_table[l1_index] = old_l2_offset; |     s->l1_table[l1_index] = old_l2_offset; | ||||||
|     return ret; |     qcow2_l2_cache_reset(bs); | ||||||
|  |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, | static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, | ||||||
| @@ -290,35 +325,22 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static int qcow2_read(BlockDriverState *bs, int64_t sector_num, | static int qcow_read(BlockDriverState *bs, int64_t sector_num, | ||||||
|                       uint8_t *buf, int nb_sectors) |                      uint8_t *buf, int nb_sectors) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int ret, index_in_cluster, n, n1; |     int ret, index_in_cluster, n, n1; | ||||||
|     uint64_t cluster_offset; |     uint64_t cluster_offset; | ||||||
|     struct iovec iov; |  | ||||||
|     QEMUIOVector qiov; |  | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         n = nb_sectors; |         n = nb_sectors; | ||||||
|  |         cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n); | ||||||
|         ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n, |  | ||||||
|             &cluster_offset); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); |         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||||
|         if (!cluster_offset) { |         if (!cluster_offset) { | ||||||
|             if (bs->backing_hd) { |             if (bs->backing_hd) { | ||||||
|                 /* read from the base image */ |                 /* read from the base image */ | ||||||
|                 iov.iov_base = buf; |                 n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n); | ||||||
|                 iov.iov_len = n * 512; |  | ||||||
|                 qemu_iovec_init_external(&qiov, &iov, 1); |  | ||||||
|  |  | ||||||
|                 n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n); |  | ||||||
|                 if (n1 > 0) { |                 if (n1 > 0) { | ||||||
|                     BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING); |  | ||||||
|                     ret = bdrv_read(bs->backing_hd, sector_num, buf, n1); |                     ret = bdrv_read(bs->backing_hd, sector_num, buf, n1); | ||||||
|                     if (ret < 0) |                     if (ret < 0) | ||||||
|                         return -1; |                         return -1; | ||||||
| @@ -327,12 +349,11 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|                 memset(buf, 0, 512 * n); |                 memset(buf, 0, 512 * n); | ||||||
|             } |             } | ||||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { |         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||||
|             if (qcow2_decompress_cluster(bs, cluster_offset) < 0) |             if (qcow2_decompress_cluster(s, cluster_offset) < 0) | ||||||
|                 return -1; |                 return -1; | ||||||
|             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); |             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||||
|         } else { |         } else { | ||||||
|             BLKDBG_EVENT(bs->file, BLKDBG_READ); |             ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||||
|             ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); |  | ||||||
|             if (ret != n * 512) |             if (ret != n * 512) | ||||||
|                 return -1; |                 return -1; | ||||||
|             if (s->crypt_method) { |             if (s->crypt_method) { | ||||||
| @@ -356,8 +377,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | |||||||
|     n = n_end - n_start; |     n = n_end - n_start; | ||||||
|     if (n <= 0) |     if (n <= 0) | ||||||
|         return 0; |         return 0; | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); |     ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); | ||||||
|     ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); |  | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|     if (s->crypt_method) { |     if (s->crypt_method) { | ||||||
| @@ -366,8 +386,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | |||||||
|                         s->cluster_data, n, 1, |                         s->cluster_data, n, 1, | ||||||
|                         &s->aes_encrypt_key); |                         &s->aes_encrypt_key); | ||||||
|     } |     } | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); |     ret = bdrv_write_sync(s->hd, (cluster_offset >> 9) + n_start, | ||||||
|     ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start, |  | ||||||
|         s->cluster_data, n); |         s->cluster_data, n); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
| @@ -378,29 +397,28 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | |||||||
| /* | /* | ||||||
|  * get_cluster_offset |  * get_cluster_offset | ||||||
|  * |  * | ||||||
|  * For a given offset of the disk image, find the cluster offset in |  * For a given offset of the disk image, return cluster offset in | ||||||
|  * qcow2 file. The offset is stored in *cluster_offset. |  * qcow2 file. | ||||||
|  * |  * | ||||||
|  * on entry, *num is the number of contiguous clusters we'd like to |  * on entry, *num is the number of contiguous clusters we'd like to | ||||||
|  * access following offset. |  * access following offset. | ||||||
|  * |  * | ||||||
|  * on exit, *num is the number of contiguous clusters we can read. |  * on exit, *num is the number of contiguous clusters we can read. | ||||||
|  * |  * | ||||||
|  * Return 0, if the offset is found |  * Return 1, if the offset is found | ||||||
|  * Return -errno, otherwise. |  * Return 0, otherwise. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||||
|     int *num, uint64_t *cluster_offset) |     int *num) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     unsigned int l1_index, l2_index; |     unsigned int l1_index, l2_index; | ||||||
|     uint64_t l2_offset, *l2_table; |     uint64_t l2_offset, *l2_table, cluster_offset; | ||||||
|     int l1_bits, c; |     int l1_bits, c; | ||||||
|     unsigned int index_in_cluster, nb_clusters; |     unsigned int index_in_cluster, nb_clusters; | ||||||
|     uint64_t nb_available, nb_needed; |     uint64_t nb_available, nb_needed; | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); |     index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); | ||||||
|     nb_needed = *num + index_in_cluster; |     nb_needed = *num + index_in_cluster; | ||||||
| @@ -421,7 +439,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|         nb_needed = nb_available; |         nb_needed = nb_available; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *cluster_offset = 0; |     cluster_offset = 0; | ||||||
|  |  | ||||||
|     /* seek the the l2 offset in the l1 table */ |     /* seek the the l2 offset in the l1 table */ | ||||||
|  |  | ||||||
| @@ -439,18 +457,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|     /* load the l2 table in memory */ |     /* load the l2 table in memory */ | ||||||
|  |  | ||||||
|     l2_offset &= ~QCOW_OFLAG_COPIED; |     l2_offset &= ~QCOW_OFLAG_COPIED; | ||||||
|     ret = l2_load(bs, l2_offset, &l2_table); |     l2_table = l2_load(bs, l2_offset); | ||||||
|     if (ret < 0) { |     if (l2_table == NULL) | ||||||
|         return ret; |         return 0; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* find the cluster offset for the given disk offset */ |     /* find the cluster offset for the given disk offset */ | ||||||
|  |  | ||||||
|     l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); |     l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); | ||||||
|     *cluster_offset = be64_to_cpu(l2_table[l2_index]); |     cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||||||
|     nb_clusters = size_to_clusters(s, nb_needed << 9); |     nb_clusters = size_to_clusters(s, nb_needed << 9); | ||||||
|  |  | ||||||
|     if (!*cluster_offset) { |     if (!cluster_offset) { | ||||||
|         /* how many empty clusters ? */ |         /* how many empty clusters ? */ | ||||||
|         c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); |         c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); | ||||||
|     } else { |     } else { | ||||||
| @@ -459,8 +476,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|                 &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); |                 &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|  |  | ||||||
|    nb_available = (c * s->cluster_sectors); |    nb_available = (c * s->cluster_sectors); | ||||||
| out: | out: | ||||||
|     if (nb_available > nb_needed) |     if (nb_available > nb_needed) | ||||||
| @@ -468,8 +483,7 @@ out: | |||||||
|  |  | ||||||
|     *num = nb_available - index_in_cluster; |     *num = nb_available - index_in_cluster; | ||||||
|  |  | ||||||
|     *cluster_offset &=~QCOW_OFLAG_COPIED; |     return cluster_offset & ~QCOW_OFLAG_COPIED; | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -490,15 +504,14 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | |||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     unsigned int l1_index, l2_index; |     unsigned int l1_index, l2_index; | ||||||
|     uint64_t l2_offset; |     uint64_t l2_offset, *l2_table; | ||||||
|     uint64_t *l2_table = NULL; |  | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     /* seek the the l2 offset in the l1 table */ |     /* seek the the l2 offset in the l1 table */ | ||||||
|  |  | ||||||
|     l1_index = offset >> (s->l2_bits + s->cluster_bits); |     l1_index = offset >> (s->l2_bits + s->cluster_bits); | ||||||
|     if (l1_index >= s->l1_size) { |     if (l1_index >= s->l1_size) { | ||||||
|         ret = qcow2_grow_l1_table(bs, l1_index + 1, false); |         ret = qcow2_grow_l1_table(bs, l1_index + 1); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
| @@ -510,20 +523,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | |||||||
|     if (l2_offset & QCOW_OFLAG_COPIED) { |     if (l2_offset & QCOW_OFLAG_COPIED) { | ||||||
|         /* load the l2 table in memory */ |         /* load the l2 table in memory */ | ||||||
|         l2_offset &= ~QCOW_OFLAG_COPIED; |         l2_offset &= ~QCOW_OFLAG_COPIED; | ||||||
|         ret = l2_load(bs, l2_offset, &l2_table); |         l2_table = l2_load(bs, l2_offset); | ||||||
|         if (ret < 0) { |         if (l2_table == NULL) { | ||||||
|             return ret; |             return -EIO; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         /* First allocate a new L2 table (and do COW if needed) */ |         if (l2_offset) | ||||||
|         ret = l2_allocate(bs, l1_index, &l2_table); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Then decrease the refcount of the old table */ |  | ||||||
|         if (l2_offset) { |  | ||||||
|             qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); |             qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); | ||||||
|  |         l2_table = l2_allocate(bs, l1_index); | ||||||
|  |         if (l2_table == NULL) { | ||||||
|  |             return -EIO; | ||||||
|         } |         } | ||||||
|         l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; |         l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; | ||||||
|     } |     } | ||||||
| @@ -576,7 +585,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     cluster_offset = qcow2_alloc_bytes(bs, compressed_size); |     cluster_offset = qcow2_alloc_bytes(bs, compressed_size); | ||||||
|     if (cluster_offset < 0) { |     if (cluster_offset < 0) { | ||||||
|         qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -590,24 +598,45 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     /* compressed clusters never have the copied flag */ |     /* compressed clusters never have the copied flag */ | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); |  | ||||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); |  | ||||||
|     l2_table[l2_index] = cpu_to_be64(cluster_offset); |     l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |     if (bdrv_pwrite_sync(s->hd, | ||||||
|     if (ret < 0) { |                     l2_offset + l2_index * sizeof(uint64_t), | ||||||
|  |                     l2_table + l2_index, | ||||||
|  |                     sizeof(uint64_t)) < 0) | ||||||
|         return 0; |         return 0; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return cluster_offset; |     return cluster_offset; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Write L2 table updates to disk, writing whole sectors to avoid a | ||||||
|  |  * read-modify-write in bdrv_pwrite | ||||||
|  |  */ | ||||||
|  | #define L2_ENTRIES_PER_SECTOR (512 / 8) | ||||||
|  | static int write_l2_entries(BDRVQcowState *s, uint64_t *l2_table, | ||||||
|  |     uint64_t l2_offset, int l2_index, int num) | ||||||
|  | { | ||||||
|  |     int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1); | ||||||
|  |     int start_offset = (8 * l2_index) & ~511; | ||||||
|  |     int end_offset = (8 * (l2_index + num) + 511) & ~511; | ||||||
|  |     size_t len = end_offset - start_offset; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     ret = bdrv_pwrite_sync(s->hd, l2_offset + start_offset, | ||||||
|  |         &l2_table[l2_start_index], len); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int i, j = 0, l2_index, ret; |     int i, j = 0, l2_index, ret; | ||||||
|     uint64_t *old_cluster, start_sect, l2_offset, *l2_table; |     uint64_t *old_cluster, start_sect, l2_offset, *l2_table; | ||||||
|     uint64_t cluster_offset = m->cluster_offset; |     uint64_t cluster_offset = m->cluster_offset; | ||||||
|     bool cow = false; |  | ||||||
|  |  | ||||||
|     if (m->nb_clusters == 0) |     if (m->nb_clusters == 0) | ||||||
|         return 0; |         return 0; | ||||||
| @@ -617,7 +646,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | |||||||
|     /* copy content of unmodified sectors */ |     /* copy content of unmodified sectors */ | ||||||
|     start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; |     start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; | ||||||
|     if (m->n_start) { |     if (m->n_start) { | ||||||
|         cow = true; |  | ||||||
|         ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); |         ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); | ||||||
|         if (ret < 0) |         if (ret < 0) | ||||||
|             goto err; |             goto err; | ||||||
| @@ -625,30 +653,17 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | |||||||
|  |  | ||||||
|     if (m->nb_available & (s->cluster_sectors - 1)) { |     if (m->nb_available & (s->cluster_sectors - 1)) { | ||||||
|         uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); |         uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); | ||||||
|         cow = true; |  | ||||||
|         ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), |         ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), | ||||||
|                 m->nb_available - end, s->cluster_sectors); |                 m->nb_available - end, s->cluster_sectors); | ||||||
|         if (ret < 0) |         if (ret < 0) | ||||||
|             goto err; |             goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* |     /* update L2 table */ | ||||||
|      * Update L2 table. |  | ||||||
|      * |  | ||||||
|      * Before we update the L2 table to actually point to the new cluster, we |  | ||||||
|      * need to be sure that the refcounts have been increased and COW was |  | ||||||
|      * handled. |  | ||||||
|      */ |  | ||||||
|     if (cow) { |  | ||||||
|         qcow2_cache_depends_on_flush(s->l2_table_cache); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); |  | ||||||
|     ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); |     ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < m->nb_clusters; i++) { |     for (i = 0; i < m->nb_clusters; i++) { | ||||||
|         /* if two concurrent writes happen to the same unallocated cluster |         /* if two concurrent writes happen to the same unallocated cluster | ||||||
| @@ -664,22 +679,15 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | |||||||
|                     (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); |                     (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); | ||||||
|      } |      } | ||||||
|  |  | ||||||
|  |     ret = write_l2_entries(s, l2_table, l2_offset, l2_index, m->nb_clusters); | ||||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|  |         qcow2_l2_cache_reset(bs); | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* |     for (i = 0; i < j; i++) | ||||||
|      * If this was a COW, we need to decrease the refcount of the old cluster. |         qcow2_free_any_clusters(bs, | ||||||
|      * Also flush bs->file to get the right order for L2 and refcount update. |             be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); | ||||||
|      */ |  | ||||||
|     if (j != 0) { |  | ||||||
|         for (i = 0; i < j; i++) { |  | ||||||
|             qcow2_free_any_clusters(bs, |  | ||||||
|                 be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| err: | err: | ||||||
| @@ -796,8 +804,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|                 m->depends_on = old_alloc; |                 m->depends_on = old_alloc; | ||||||
|                 m->nb_clusters = 0; |                 m->nb_clusters = 0; | ||||||
|                 *num = 0; |                 *num = 0; | ||||||
|                 ret = 0; |                 return 0; | ||||||
|                 goto fail; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -813,8 +820,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|     cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); |     cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); | ||||||
|     if (cluster_offset < 0) { |     if (cluster_offset < 0) { | ||||||
|         QLIST_REMOVE(m, next_in_flight); |         QLIST_REMOVE(m, next_in_flight); | ||||||
|         ret = cluster_offset; |         return cluster_offset; | ||||||
|         goto fail; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* save info needed for meta data update */ |     /* save info needed for meta data update */ | ||||||
| @@ -823,21 +829,12 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | |||||||
|     m->nb_clusters = nb_clusters; |     m->nb_clusters = nb_clusters; | ||||||
|  |  | ||||||
| out: | out: | ||||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); |     m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); | ||||||
|     m->cluster_offset = cluster_offset; |     m->cluster_offset = cluster_offset; | ||||||
|  |  | ||||||
|     *num = m->nb_available - n_start; |     *num = m->nb_available - n_start; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| fail: |  | ||||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||||
| @@ -867,9 +864,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     int ret, csize, nb_csectors, sector_offset; |     int ret, csize, nb_csectors, sector_offset; | ||||||
|     uint64_t coffset; |     uint64_t coffset; | ||||||
|  |  | ||||||
| @@ -878,98 +874,15 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | |||||||
|         nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; |         nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; | ||||||
|         sector_offset = coffset & 511; |         sector_offset = coffset & 511; | ||||||
|         csize = nb_csectors * 512 - sector_offset; |         csize = nb_csectors * 512 - sector_offset; | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); |         ret = bdrv_read(s->hd, coffset >> 9, s->cluster_data, nb_csectors); | ||||||
|         ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); |  | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             return ret; |             return -1; | ||||||
|         } |         } | ||||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, |         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||||
|                               s->cluster_data + sector_offset, csize) < 0) { |                               s->cluster_data + sector_offset, csize) < 0) { | ||||||
|             return -EIO; |             return -1; | ||||||
|         } |         } | ||||||
|         s->cluster_cache_offset = coffset; |         s->cluster_cache_offset = coffset; | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * This discards as many clusters of nb_clusters as possible at once (i.e. |  | ||||||
|  * all clusters in the same L2 table) and returns the number of discarded |  | ||||||
|  * clusters. |  | ||||||
|  */ |  | ||||||
| static int discard_single_l2(BlockDriverState *bs, uint64_t offset, |  | ||||||
|     unsigned int nb_clusters) |  | ||||||
| { |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     uint64_t l2_offset, *l2_table; |  | ||||||
|     int l2_index; |  | ||||||
|     int ret; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Limit nb_clusters to one L2 table */ |  | ||||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < nb_clusters; i++) { |  | ||||||
|         uint64_t old_offset; |  | ||||||
|  |  | ||||||
|         old_offset = be64_to_cpu(l2_table[l2_index + i]); |  | ||||||
|         old_offset &= ~QCOW_OFLAG_COPIED; |  | ||||||
|  |  | ||||||
|         if (old_offset == 0) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* First remove L2 entries */ |  | ||||||
|         qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); |  | ||||||
|         l2_table[l2_index + i] = cpu_to_be64(0); |  | ||||||
|  |  | ||||||
|         /* Then decrease the refcount */ |  | ||||||
|         qcow2_free_any_clusters(bs, old_offset, 1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return nb_clusters; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, |  | ||||||
|     int nb_sectors) |  | ||||||
| { |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     uint64_t end_offset; |  | ||||||
|     unsigned int nb_clusters; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); |  | ||||||
|  |  | ||||||
|     /* Round start up and end down */ |  | ||||||
|     offset = align_offset(offset, s->cluster_size); |  | ||||||
|     end_offset &= ~(s->cluster_size - 1); |  | ||||||
|  |  | ||||||
|     if (offset > end_offset) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     nb_clusters = size_to_clusters(s, end_offset - offset); |  | ||||||
|  |  | ||||||
|     /* Each L2 table is handled by its own loop iteration */ |  | ||||||
|     while (nb_clusters > 0) { |  | ||||||
|         ret = discard_single_l2(bs, offset, nb_clusters); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         nb_clusters -= ret; |  | ||||||
|         offset += (ret * s->cluster_size); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -79,7 +79,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | |||||||
|     s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); |     s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); | ||||||
|     for(i = 0; i < s->nb_snapshots; i++) { |     for(i = 0; i < s->nb_snapshots; i++) { | ||||||
|         offset = align_offset(offset, 8); |         offset = align_offset(offset, 8); | ||||||
|         if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h)) |         if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h)) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += sizeof(h); |         offset += sizeof(h); | ||||||
|         sn = s->snapshots + i; |         sn = s->snapshots + i; | ||||||
| @@ -97,13 +97,13 @@ int qcow2_read_snapshots(BlockDriverState *bs) | |||||||
|         offset += extra_data_size; |         offset += extra_data_size; | ||||||
|  |  | ||||||
|         sn->id_str = qemu_malloc(id_str_size + 1); |         sn->id_str = qemu_malloc(id_str_size + 1); | ||||||
|         if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size) |         if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += id_str_size; |         offset += id_str_size; | ||||||
|         sn->id_str[id_str_size] = '\0'; |         sn->id_str[id_str_size] = '\0'; | ||||||
|  |  | ||||||
|         sn->name = qemu_malloc(name_size + 1); |         sn->name = qemu_malloc(name_size + 1); | ||||||
|         if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size) |         if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += name_size; |         offset += name_size; | ||||||
|         sn->name[name_size] = '\0'; |         sn->name[name_size] = '\0'; | ||||||
| @@ -116,7 +116,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* add at the end of the file a new list of snapshots */ | /* add at the end of the file a new list of snapshots */ | ||||||
| static int qcow2_write_snapshots(BlockDriverState *bs) | static int qcow_write_snapshots(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     QCowSnapshot *sn; |     QCowSnapshot *sn; | ||||||
| @@ -138,7 +138,6 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | |||||||
|     snapshots_size = offset; |     snapshots_size = offset; | ||||||
|  |  | ||||||
|     snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); |     snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); | ||||||
|     bdrv_flush(bs->file); |  | ||||||
|     offset = snapshots_offset; |     offset = snapshots_offset; | ||||||
|     if (offset < 0) { |     if (offset < 0) { | ||||||
|         return offset; |         return offset; | ||||||
| @@ -159,24 +158,24 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | |||||||
|         h.id_str_size = cpu_to_be16(id_str_size); |         h.id_str_size = cpu_to_be16(id_str_size); | ||||||
|         h.name_size = cpu_to_be16(name_size); |         h.name_size = cpu_to_be16(name_size); | ||||||
|         offset = align_offset(offset, 8); |         offset = align_offset(offset, 8); | ||||||
|         if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0) |         if (bdrv_pwrite_sync(s->hd, offset, &h, sizeof(h)) < 0) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += sizeof(h); |         offset += sizeof(h); | ||||||
|         if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0) |         if (bdrv_pwrite_sync(s->hd, offset, sn->id_str, id_str_size) < 0) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += id_str_size; |         offset += id_str_size; | ||||||
|         if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0) |         if (bdrv_pwrite_sync(s->hd, offset, sn->name, name_size) < 0) | ||||||
|             goto fail; |             goto fail; | ||||||
|         offset += name_size; |         offset += name_size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* update the various header fields */ |     /* update the various header fields */ | ||||||
|     data64 = cpu_to_be64(snapshots_offset); |     data64 = cpu_to_be64(snapshots_offset); | ||||||
|     if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset), |     if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, snapshots_offset), | ||||||
|                     &data64, sizeof(data64)) < 0) |                     &data64, sizeof(data64)) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|     data32 = cpu_to_be32(s->nb_snapshots); |     data32 = cpu_to_be32(s->nb_snapshots); | ||||||
|     if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), |     if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, nb_snapshots), | ||||||
|                     &data32, sizeof(data32)) < 0) |                     &data32, sizeof(data32)) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
| @@ -272,7 +271,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | |||||||
|     if (l1_table_offset < 0) { |     if (l1_table_offset < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|     bdrv_flush(bs->file); |  | ||||||
|  |  | ||||||
|     sn->l1_table_offset = l1_table_offset; |     sn->l1_table_offset = l1_table_offset; | ||||||
|     sn->l1_size = s->l1_size; |     sn->l1_size = s->l1_size; | ||||||
| @@ -286,7 +284,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | |||||||
|     for(i = 0; i < s->l1_size; i++) { |     for(i = 0; i < s->l1_size; i++) { | ||||||
|         l1_table[i] = cpu_to_be64(s->l1_table[i]); |         l1_table[i] = cpu_to_be64(s->l1_table[i]); | ||||||
|     } |     } | ||||||
|     if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset, |     if (bdrv_pwrite_sync(s->hd, sn->l1_table_offset, | ||||||
|                     l1_table, s->l1_size * sizeof(uint64_t)) < 0) |                     l1_table, s->l1_size * sizeof(uint64_t)) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|     qemu_free(l1_table); |     qemu_free(l1_table); | ||||||
| @@ -300,7 +298,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | |||||||
|     s->snapshots = snapshots1; |     s->snapshots = snapshots1; | ||||||
|     s->snapshots[s->nb_snapshots++] = *sn; |     s->snapshots[s->nb_snapshots++] = *sn; | ||||||
|  |  | ||||||
|     if (qcow2_write_snapshots(bs) < 0) |     if (qcow_write_snapshots(bs) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
| #ifdef DEBUG_ALLOC | #ifdef DEBUG_ALLOC | ||||||
|     qcow2_check_refcounts(bs); |     qcow2_check_refcounts(bs); | ||||||
| @@ -327,16 +325,16 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | |||||||
|     if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) |     if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0) |     if (qcow2_grow_l1_table(bs, sn->l1_size) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     s->l1_size = sn->l1_size; |     s->l1_size = sn->l1_size; | ||||||
|     l1_size2 = s->l1_size * sizeof(uint64_t); |     l1_size2 = s->l1_size * sizeof(uint64_t); | ||||||
|     /* copy the snapshot l1 table to the current l1 table */ |     /* copy the snapshot l1 table to the current l1 table */ | ||||||
|     if (bdrv_pread(bs->file, sn->l1_table_offset, |     if (bdrv_pread(s->hd, sn->l1_table_offset, | ||||||
|                    s->l1_table, l1_size2) != l1_size2) |                    s->l1_table, l1_size2) != l1_size2) | ||||||
|         goto fail; |         goto fail; | ||||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, |     if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, | ||||||
|                     s->l1_table, l1_size2) < 0) |                     s->l1_table, l1_size2) < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|     for(i = 0;i < s->l1_size; i++) { |     for(i = 0;i < s->l1_size; i++) { | ||||||
| @@ -378,7 +376,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) | |||||||
|     qemu_free(sn->name); |     qemu_free(sn->name); | ||||||
|     memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); |     memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); | ||||||
|     s->nb_snapshots--; |     s->nb_snapshots--; | ||||||
|     ret = qcow2_write_snapshots(bs); |     ret = qcow_write_snapshots(bs); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         /* XXX: restore snapshot if error ? */ |         /* XXX: restore snapshot if error ? */ | ||||||
|         return ret; |         return ret; | ||||||
| @@ -418,34 +416,3 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) | |||||||
|     return s->nb_snapshots; |     return s->nb_snapshots; | ||||||
| } | } | ||||||
|  |  | ||||||
| int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) |  | ||||||
| { |  | ||||||
|     int i, snapshot_index, l1_size2; |  | ||||||
|     BDRVQcowState *s = bs->opaque; |  | ||||||
|     QCowSnapshot *sn; |  | ||||||
|  |  | ||||||
|     snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name); |  | ||||||
|     if (snapshot_index < 0) { |  | ||||||
|         return -ENOENT; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sn = &s->snapshots[snapshot_index]; |  | ||||||
|     s->l1_size = sn->l1_size; |  | ||||||
|     l1_size2 = s->l1_size * sizeof(uint64_t); |  | ||||||
|     if (s->l1_table != NULL) { |  | ||||||
|         qemu_free(s->l1_table); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->l1_table_offset = sn->l1_table_offset; |  | ||||||
|     s->l1_table = qemu_mallocz(align_offset(l1_size2, 512)); |  | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, sn->l1_table_offset, |  | ||||||
|                    s->l1_table, l1_size2) != l1_size2) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for(i = 0;i < s->l1_size; i++) { |  | ||||||
|         be64_to_cpus(&s->l1_table[i]); |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										922
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										922
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -51,9 +51,6 @@ | |||||||
|  |  | ||||||
| #define L2_CACHE_SIZE 16 | #define L2_CACHE_SIZE 16 | ||||||
|  |  | ||||||
| /* Must be at least 4 to cover all cases of refcount table growth */ |  | ||||||
| #define REFCOUNT_CACHE_SIZE 4 |  | ||||||
|  |  | ||||||
| typedef struct QCowHeader { | typedef struct QCowHeader { | ||||||
|     uint32_t magic; |     uint32_t magic; | ||||||
|     uint32_t version; |     uint32_t version; | ||||||
| @@ -81,10 +78,8 @@ typedef struct QCowSnapshot { | |||||||
|     uint64_t vm_clock_nsec; |     uint64_t vm_clock_nsec; | ||||||
| } QCowSnapshot; | } QCowSnapshot; | ||||||
|  |  | ||||||
| struct Qcow2Cache; |  | ||||||
| typedef struct Qcow2Cache Qcow2Cache; |  | ||||||
|  |  | ||||||
| typedef struct BDRVQcowState { | typedef struct BDRVQcowState { | ||||||
|  |     BlockDriverState *hd; | ||||||
|     int cluster_bits; |     int cluster_bits; | ||||||
|     int cluster_size; |     int cluster_size; | ||||||
|     int cluster_sectors; |     int cluster_sectors; | ||||||
| @@ -97,10 +92,9 @@ typedef struct BDRVQcowState { | |||||||
|     uint64_t cluster_offset_mask; |     uint64_t cluster_offset_mask; | ||||||
|     uint64_t l1_table_offset; |     uint64_t l1_table_offset; | ||||||
|     uint64_t *l1_table; |     uint64_t *l1_table; | ||||||
|  |     uint64_t *l2_cache; | ||||||
|     Qcow2Cache* l2_table_cache; |     uint64_t l2_cache_offsets[L2_CACHE_SIZE]; | ||||||
|     Qcow2Cache* refcount_block_cache; |     uint32_t l2_cache_counts[L2_CACHE_SIZE]; | ||||||
|  |  | ||||||
|     uint8_t *cluster_cache; |     uint8_t *cluster_cache; | ||||||
|     uint8_t *cluster_data; |     uint8_t *cluster_data; | ||||||
|     uint64_t cluster_cache_offset; |     uint64_t cluster_cache_offset; | ||||||
| @@ -109,6 +103,8 @@ typedef struct BDRVQcowState { | |||||||
|     uint64_t *refcount_table; |     uint64_t *refcount_table; | ||||||
|     uint64_t refcount_table_offset; |     uint64_t refcount_table_offset; | ||||||
|     uint32_t refcount_table_size; |     uint32_t refcount_table_size; | ||||||
|  |     uint64_t refcount_block_cache_offset; | ||||||
|  |     uint16_t *refcount_block_cache; | ||||||
|     int64_t free_cluster_index; |     int64_t free_cluster_index; | ||||||
|     int64_t free_byte_offset; |     int64_t free_byte_offset; | ||||||
|  |  | ||||||
| @@ -154,12 +150,6 @@ static inline int size_to_clusters(BDRVQcowState *s, int64_t size) | |||||||
|     return (size + (s->cluster_size - 1)) >> s->cluster_bits; |     return (size + (s->cluster_size - 1)) >> s->cluster_bits; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline int size_to_l1(BDRVQcowState *s, int64_t size) |  | ||||||
| { |  | ||||||
|     int shift = s->cluster_bits + s->l2_bits; |  | ||||||
|     return (size + (1ULL << shift) - 1) >> shift; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline int64_t align_offset(int64_t offset, int n) | static inline int64_t align_offset(int64_t offset, int n) | ||||||
| { | { | ||||||
|     offset = (offset + n - 1) & ~(n - 1); |     offset = (offset + n - 1) & ~(n - 1); | ||||||
| @@ -170,8 +160,8 @@ static inline int64_t align_offset(int64_t offset, int n) | |||||||
| // FIXME Need qcow2_ prefix to global functions | // FIXME Need qcow2_ prefix to global functions | ||||||
|  |  | ||||||
| /* qcow2.c functions */ | /* qcow2.c functions */ | ||||||
| int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, | int qcow2_backing_read1(BlockDriverState *bs, | ||||||
|                   int64_t sector_num, int nb_sectors); |                   int64_t sector_num, uint8_t *buf, int nb_sectors); | ||||||
|  |  | ||||||
| /* qcow2-refcount.c functions */ | /* qcow2-refcount.c functions */ | ||||||
| int qcow2_refcount_init(BlockDriverState *bs); | int qcow2_refcount_init(BlockDriverState *bs); | ||||||
| @@ -189,19 +179,19 @@ void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset, | |||||||
| int qcow2_update_snapshot_refcount(BlockDriverState *bs, | int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||||
|     int64_t l1_table_offset, int l1_size, int addend); |     int64_t l1_table_offset, int l1_size, int addend); | ||||||
|  |  | ||||||
| int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res); | int qcow2_check_refcounts(BlockDriverState *bs); | ||||||
|  |  | ||||||
| /* qcow2-cluster.c functions */ | /* qcow2-cluster.c functions */ | ||||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size); | int qcow2_grow_l1_table(BlockDriverState *bs, int min_size); | ||||||
| void qcow2_l2_cache_reset(BlockDriverState *bs); | void qcow2_l2_cache_reset(BlockDriverState *bs); | ||||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); | ||||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||||
|                      uint8_t *out_buf, const uint8_t *in_buf, |                      uint8_t *out_buf, const uint8_t *in_buf, | ||||||
|                      int nb_sectors, int enc, |                      int nb_sectors, int enc, | ||||||
|                      const AES_KEY *key); |                      const AES_KEY *key); | ||||||
|  |  | ||||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||||
|     int *num, uint64_t *cluster_offset); |     int *num); | ||||||
| int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||||
|     int n_start, int n_end, int *num, QCowL2Meta *m); |     int n_start, int n_end, int *num, QCowL2Meta *m); | ||||||
| uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||||
| @@ -209,34 +199,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | |||||||
|                                          int compressed_size); |                                          int compressed_size); | ||||||
|  |  | ||||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); | int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); | ||||||
| int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, |  | ||||||
|     int nb_sectors); |  | ||||||
|  |  | ||||||
| /* qcow2-snapshot.c functions */ | /* qcow2-snapshot.c functions */ | ||||||
| int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); | int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); | ||||||
| int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); | int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); | ||||||
| int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | ||||||
| int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); | int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); | ||||||
| int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); |  | ||||||
|  |  | ||||||
| void qcow2_free_snapshots(BlockDriverState *bs); | void qcow2_free_snapshots(BlockDriverState *bs); | ||||||
| int qcow2_read_snapshots(BlockDriverState *bs); | int qcow2_read_snapshots(BlockDriverState *bs); | ||||||
|  |  | ||||||
| /* qcow2-cache.c functions */ |  | ||||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, |  | ||||||
|     bool writethrough); |  | ||||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); |  | ||||||
|  |  | ||||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); |  | ||||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); |  | ||||||
| int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, |  | ||||||
|     Qcow2Cache *dependency); |  | ||||||
| void qcow2_cache_depends_on_flush(Qcow2Cache *c); |  | ||||||
|  |  | ||||||
| int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, |  | ||||||
|     void **table); |  | ||||||
| int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, |  | ||||||
|     void **table); |  | ||||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,210 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format Consistency Check |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qed.h" |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BDRVQEDState *s; |  | ||||||
|     BdrvCheckResult *result; |  | ||||||
|     bool fix;                           /* whether to fix invalid offsets */ |  | ||||||
|  |  | ||||||
|     uint64_t nclusters; |  | ||||||
|     uint32_t *used_clusters;            /* referenced cluster bitmap */ |  | ||||||
|  |  | ||||||
|     QEDRequest request; |  | ||||||
| } QEDCheck; |  | ||||||
|  |  | ||||||
| static bool qed_test_bit(uint32_t *bitmap, uint64_t n) { |  | ||||||
|     return !!(bitmap[n / 32] & (1 << (n % 32))); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qed_set_bit(uint32_t *bitmap, uint64_t n) { |  | ||||||
|     bitmap[n / 32] |= 1 << (n % 32); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Set bitmap bits for clusters |  | ||||||
|  * |  | ||||||
|  * @check:          Check structure |  | ||||||
|  * @offset:         Starting offset in bytes |  | ||||||
|  * @n:              Number of clusters |  | ||||||
|  */ |  | ||||||
| static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset, |  | ||||||
|                                   unsigned int n) |  | ||||||
| { |  | ||||||
|     uint64_t cluster = qed_bytes_to_clusters(check->s, offset); |  | ||||||
|     unsigned int corruptions = 0; |  | ||||||
|  |  | ||||||
|     while (n-- != 0) { |  | ||||||
|         /* Clusters should only be referenced once */ |  | ||||||
|         if (qed_test_bit(check->used_clusters, cluster)) { |  | ||||||
|             corruptions++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         qed_set_bit(check->used_clusters, cluster); |  | ||||||
|         cluster++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     check->result->corruptions += corruptions; |  | ||||||
|     return corruptions == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Check an L2 table |  | ||||||
|  * |  | ||||||
|  * @ret:            Number of invalid cluster offsets |  | ||||||
|  */ |  | ||||||
| static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) |  | ||||||
| { |  | ||||||
|     BDRVQEDState *s = check->s; |  | ||||||
|     unsigned int i, num_invalid = 0; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < s->table_nelems; i++) { |  | ||||||
|         uint64_t offset = table->offsets[i]; |  | ||||||
|  |  | ||||||
|         if (!offset) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Detect invalid cluster offset */ |  | ||||||
|         if (!qed_check_cluster_offset(s, offset)) { |  | ||||||
|             if (check->fix) { |  | ||||||
|                 table->offsets[i] = 0; |  | ||||||
|             } else { |  | ||||||
|                 check->result->corruptions++; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             num_invalid++; |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         qed_set_used_clusters(check, offset, 1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return num_invalid; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Descend tables and check each cluster is referenced once only |  | ||||||
|  */ |  | ||||||
| static int qed_check_l1_table(QEDCheck *check, QEDTable *table) |  | ||||||
| { |  | ||||||
|     BDRVQEDState *s = check->s; |  | ||||||
|     unsigned int i, num_invalid_l1 = 0; |  | ||||||
|     int ret, last_error = 0; |  | ||||||
|  |  | ||||||
|     /* Mark L1 table clusters used */ |  | ||||||
|     qed_set_used_clusters(check, s->header.l1_table_offset, |  | ||||||
|                           s->header.table_size); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < s->table_nelems; i++) { |  | ||||||
|         unsigned int num_invalid_l2; |  | ||||||
|         uint64_t offset = table->offsets[i]; |  | ||||||
|  |  | ||||||
|         if (!offset) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Detect invalid L2 offset */ |  | ||||||
|         if (!qed_check_table_offset(s, offset)) { |  | ||||||
|             /* Clear invalid offset */ |  | ||||||
|             if (check->fix) { |  | ||||||
|                 table->offsets[i] = 0; |  | ||||||
|             } else { |  | ||||||
|                 check->result->corruptions++; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             num_invalid_l1++; |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!qed_set_used_clusters(check, offset, s->header.table_size)) { |  | ||||||
|             continue; /* skip an invalid table */ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ret = qed_read_l2_table_sync(s, &check->request, offset); |  | ||||||
|         if (ret) { |  | ||||||
|             check->result->check_errors++; |  | ||||||
|             last_error = ret; |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         num_invalid_l2 = qed_check_l2_table(check, |  | ||||||
|                                             check->request.l2_table->table); |  | ||||||
|  |  | ||||||
|         /* Write out fixed L2 table */ |  | ||||||
|         if (num_invalid_l2 > 0 && check->fix) { |  | ||||||
|             ret = qed_write_l2_table_sync(s, &check->request, 0, |  | ||||||
|                                           s->table_nelems, false); |  | ||||||
|             if (ret) { |  | ||||||
|                 check->result->check_errors++; |  | ||||||
|                 last_error = ret; |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Drop reference to final table */ |  | ||||||
|     qed_unref_l2_cache_entry(check->request.l2_table); |  | ||||||
|     check->request.l2_table = NULL; |  | ||||||
|  |  | ||||||
|     /* Write out fixed L1 table */ |  | ||||||
|     if (num_invalid_l1 > 0 && check->fix) { |  | ||||||
|         ret = qed_write_l1_table_sync(s, 0, s->table_nelems); |  | ||||||
|         if (ret) { |  | ||||||
|             check->result->check_errors++; |  | ||||||
|             last_error = ret; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return last_error; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Check for unreferenced (leaked) clusters |  | ||||||
|  */ |  | ||||||
| static void qed_check_for_leaks(QEDCheck *check) |  | ||||||
| { |  | ||||||
|     BDRVQEDState *s = check->s; |  | ||||||
|     uint64_t i; |  | ||||||
|  |  | ||||||
|     for (i = s->header.header_size; i < check->nclusters; i++) { |  | ||||||
|         if (!qed_test_bit(check->used_clusters, i)) { |  | ||||||
|             check->result->leaks++; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) |  | ||||||
| { |  | ||||||
|     QEDCheck check = { |  | ||||||
|         .s = s, |  | ||||||
|         .result = result, |  | ||||||
|         .nclusters = qed_bytes_to_clusters(s, s->file_size), |  | ||||||
|         .request = { .l2_table = NULL }, |  | ||||||
|         .fix = fix, |  | ||||||
|     }; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) * |  | ||||||
|                                        sizeof(check.used_clusters[0])); |  | ||||||
|  |  | ||||||
|     ret = qed_check_l1_table(&check, s->l1_table); |  | ||||||
|     if (ret == 0) { |  | ||||||
|         /* Only check for leaks if entire image was scanned successfully */ |  | ||||||
|         qed_check_for_leaks(&check); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_free(check.used_clusters); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| @@ -1,154 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format Cluster functions |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> |  | ||||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qed.h" |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Count the number of contiguous data clusters |  | ||||||
|  * |  | ||||||
|  * @s:              QED state |  | ||||||
|  * @table:          L2 table |  | ||||||
|  * @index:          First cluster index |  | ||||||
|  * @n:              Maximum number of clusters |  | ||||||
|  * @offset:         Set to first cluster offset |  | ||||||
|  * |  | ||||||
|  * This function scans tables for contiguous allocated or free clusters. |  | ||||||
|  */ |  | ||||||
| static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s, |  | ||||||
|                                                   QEDTable *table, |  | ||||||
|                                                   unsigned int index, |  | ||||||
|                                                   unsigned int n, |  | ||||||
|                                                   uint64_t *offset) |  | ||||||
| { |  | ||||||
|     unsigned int end = MIN(index + n, s->table_nelems); |  | ||||||
|     uint64_t last = table->offsets[index]; |  | ||||||
|     unsigned int i; |  | ||||||
|  |  | ||||||
|     *offset = last; |  | ||||||
|  |  | ||||||
|     for (i = index + 1; i < end; i++) { |  | ||||||
|         if (last == 0) { |  | ||||||
|             /* Counting free clusters */ |  | ||||||
|             if (table->offsets[i] != 0) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             /* Counting allocated clusters */ |  | ||||||
|             if (table->offsets[i] != last + s->header.cluster_size) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             last = table->offsets[i]; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return i - index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BDRVQEDState *s; |  | ||||||
|     uint64_t pos; |  | ||||||
|     size_t len; |  | ||||||
|  |  | ||||||
|     QEDRequest *request; |  | ||||||
|  |  | ||||||
|     /* User callback */ |  | ||||||
|     QEDFindClusterFunc *cb; |  | ||||||
|     void *opaque; |  | ||||||
| } QEDFindClusterCB; |  | ||||||
|  |  | ||||||
| static void qed_find_cluster_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     QEDFindClusterCB *find_cluster_cb = opaque; |  | ||||||
|     BDRVQEDState *s = find_cluster_cb->s; |  | ||||||
|     QEDRequest *request = find_cluster_cb->request; |  | ||||||
|     uint64_t offset = 0; |  | ||||||
|     size_t len = 0; |  | ||||||
|     unsigned int index; |  | ||||||
|     unsigned int n; |  | ||||||
|  |  | ||||||
|     if (ret) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     index = qed_l2_index(s, find_cluster_cb->pos); |  | ||||||
|     n = qed_bytes_to_clusters(s, |  | ||||||
|                               qed_offset_into_cluster(s, find_cluster_cb->pos) + |  | ||||||
|                               find_cluster_cb->len); |  | ||||||
|     n = qed_count_contiguous_clusters(s, request->l2_table->table, |  | ||||||
|                                       index, n, &offset); |  | ||||||
|  |  | ||||||
|     ret = offset ? QED_CLUSTER_FOUND : QED_CLUSTER_L2; |  | ||||||
|     len = MIN(find_cluster_cb->len, n * s->header.cluster_size - |  | ||||||
|               qed_offset_into_cluster(s, find_cluster_cb->pos)); |  | ||||||
|  |  | ||||||
|     if (offset && !qed_check_cluster_offset(s, offset)) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); |  | ||||||
|     qemu_free(find_cluster_cb); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Find the offset of a data cluster |  | ||||||
|  * |  | ||||||
|  * @s:          QED state |  | ||||||
|  * @request:    L2 cache entry |  | ||||||
|  * @pos:        Byte position in device |  | ||||||
|  * @len:        Number of bytes |  | ||||||
|  * @cb:         Completion function |  | ||||||
|  * @opaque:     User data for completion function |  | ||||||
|  * |  | ||||||
|  * This function translates a position in the block device to an offset in the |  | ||||||
|  * image file.  It invokes the cb completion callback to report back the |  | ||||||
|  * translated offset or unallocated range in the image file. |  | ||||||
|  * |  | ||||||
|  * If the L2 table exists, request->l2_table points to the L2 table cache entry |  | ||||||
|  * and the caller must free the reference when they are finished.  The cache |  | ||||||
|  * entry is exposed in this way to avoid callers having to read the L2 table |  | ||||||
|  * again later during request processing.  If request->l2_table is non-NULL it |  | ||||||
|  * will be unreferenced before taking on the new cache entry. |  | ||||||
|  */ |  | ||||||
| void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, |  | ||||||
|                       size_t len, QEDFindClusterFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     QEDFindClusterCB *find_cluster_cb; |  | ||||||
|     uint64_t l2_offset; |  | ||||||
|  |  | ||||||
|     /* Limit length to L2 boundary.  Requests are broken up at the L2 boundary |  | ||||||
|      * so that a request acts on one L2 table at a time. |  | ||||||
|      */ |  | ||||||
|     len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos); |  | ||||||
|  |  | ||||||
|     l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)]; |  | ||||||
|     if (!l2_offset) { |  | ||||||
|         cb(opaque, QED_CLUSTER_L1, 0, len); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     if (!qed_check_table_offset(s, l2_offset)) { |  | ||||||
|         cb(opaque, -EINVAL, 0, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb)); |  | ||||||
|     find_cluster_cb->s = s; |  | ||||||
|     find_cluster_cb->pos = pos; |  | ||||||
|     find_cluster_cb->len = len; |  | ||||||
|     find_cluster_cb->cb = cb; |  | ||||||
|     find_cluster_cb->opaque = opaque; |  | ||||||
|     find_cluster_cb->request = request; |  | ||||||
|  |  | ||||||
|     qed_read_l2_table(s, request, l2_offset, |  | ||||||
|                       qed_find_cluster_cb, find_cluster_cb); |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qed.h" |  | ||||||
|  |  | ||||||
| void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     GenericCB *gencb = qemu_malloc(len); |  | ||||||
|     gencb->cb = cb; |  | ||||||
|     gencb->opaque = opaque; |  | ||||||
|     return gencb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void gencb_complete(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     GenericCB *gencb = opaque; |  | ||||||
|     BlockDriverCompletionFunc *cb = gencb->cb; |  | ||||||
|     void *user_opaque = gencb->opaque; |  | ||||||
|  |  | ||||||
|     qemu_free(gencb); |  | ||||||
|     cb(user_opaque, ret); |  | ||||||
| } |  | ||||||
| @@ -1,173 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format L2 Cache |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * L2 table cache usage is as follows: |  | ||||||
|  * |  | ||||||
|  * An open image has one L2 table cache that is used to avoid accessing the |  | ||||||
|  * image file for recently referenced L2 tables. |  | ||||||
|  * |  | ||||||
|  * Cluster offset lookup translates the logical offset within the block device |  | ||||||
|  * to a cluster offset within the image file.  This is done by indexing into |  | ||||||
|  * the L1 and L2 tables which store cluster offsets.  It is here where the L2 |  | ||||||
|  * table cache serves up recently referenced L2 tables. |  | ||||||
|  * |  | ||||||
|  * If there is a cache miss, that L2 table is read from the image file and |  | ||||||
|  * committed to the cache.  Subsequent accesses to that L2 table will be served |  | ||||||
|  * from the cache until the table is evicted from the cache. |  | ||||||
|  * |  | ||||||
|  * L2 tables are also committed to the cache when new L2 tables are allocated |  | ||||||
|  * in the image file.  Since the L2 table cache is write-through, the new L2 |  | ||||||
|  * table is first written out to the image file and then committed to the |  | ||||||
|  * cache. |  | ||||||
|  * |  | ||||||
|  * Multiple I/O requests may be using an L2 table cache entry at any given |  | ||||||
|  * time.  That means an entry may be in use across several requests and |  | ||||||
|  * reference counting is needed to free the entry at the correct time.  In |  | ||||||
|  * particular, an entry evicted from the cache will only be freed once all |  | ||||||
|  * references are dropped. |  | ||||||
|  * |  | ||||||
|  * An in-flight I/O request will hold a reference to a L2 table cache entry for |  | ||||||
|  * the period during which it needs to access the L2 table.  This includes |  | ||||||
|  * cluster offset lookup, L2 table allocation, and L2 table update when a new |  | ||||||
|  * data cluster has been allocated. |  | ||||||
|  * |  | ||||||
|  * An interesting case occurs when two requests need to access an L2 table that |  | ||||||
|  * is not in the cache.  Since the operation to read the table from the image |  | ||||||
|  * file takes some time to complete, both requests may see a cache miss and |  | ||||||
|  * start reading the L2 table from the image file.  The first to finish will |  | ||||||
|  * commit its L2 table into the cache.  When the second tries to commit its |  | ||||||
|  * table will be deleted in favor of the existing cache entry. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "trace.h" |  | ||||||
| #include "qed.h" |  | ||||||
|  |  | ||||||
| /* Each L2 holds 2GB so this let's us fully cache a 100GB disk */ |  | ||||||
| #define MAX_L2_CACHE_SIZE 50 |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Initialize the L2 cache |  | ||||||
|  */ |  | ||||||
| void qed_init_l2_cache(L2TableCache *l2_cache) |  | ||||||
| { |  | ||||||
|     QTAILQ_INIT(&l2_cache->entries); |  | ||||||
|     l2_cache->n_entries = 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Free the L2 cache |  | ||||||
|  */ |  | ||||||
| void qed_free_l2_cache(L2TableCache *l2_cache) |  | ||||||
| { |  | ||||||
|     CachedL2Table *entry, *next_entry; |  | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) { |  | ||||||
|         qemu_vfree(entry->table); |  | ||||||
|         qemu_free(entry); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Allocate an uninitialized entry from the cache |  | ||||||
|  * |  | ||||||
|  * The returned entry has a reference count of 1 and is owned by the caller. |  | ||||||
|  * The caller must allocate the actual table field for this entry and it must |  | ||||||
|  * be freeable using qemu_vfree(). |  | ||||||
|  */ |  | ||||||
| CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache) |  | ||||||
| { |  | ||||||
|     CachedL2Table *entry; |  | ||||||
|  |  | ||||||
|     entry = qemu_mallocz(sizeof(*entry)); |  | ||||||
|     entry->ref++; |  | ||||||
|  |  | ||||||
|     trace_qed_alloc_l2_cache_entry(l2_cache, entry); |  | ||||||
|  |  | ||||||
|     return entry; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decrease an entry's reference count and free if necessary when the reference |  | ||||||
|  * count drops to zero. |  | ||||||
|  */ |  | ||||||
| void qed_unref_l2_cache_entry(CachedL2Table *entry) |  | ||||||
| { |  | ||||||
|     if (!entry) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     entry->ref--; |  | ||||||
|     trace_qed_unref_l2_cache_entry(entry, entry->ref); |  | ||||||
|     if (entry->ref == 0) { |  | ||||||
|         qemu_vfree(entry->table); |  | ||||||
|         qemu_free(entry); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Find an entry in the L2 cache.  This may return NULL and it's up to the |  | ||||||
|  * caller to satisfy the cache miss. |  | ||||||
|  * |  | ||||||
|  * For a cached entry, this function increases the reference count and returns |  | ||||||
|  * the entry. |  | ||||||
|  */ |  | ||||||
| CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset) |  | ||||||
| { |  | ||||||
|     CachedL2Table *entry; |  | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH(entry, &l2_cache->entries, node) { |  | ||||||
|         if (entry->offset == offset) { |  | ||||||
|             trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref); |  | ||||||
|             entry->ref++; |  | ||||||
|             return entry; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Commit an L2 cache entry into the cache.  This is meant to be used as part of |  | ||||||
|  * the process to satisfy a cache miss.  A caller would allocate an entry which |  | ||||||
|  * is not actually in the L2 cache and then once the entry was valid and |  | ||||||
|  * present on disk, the entry can be committed into the cache. |  | ||||||
|  * |  | ||||||
|  * Since the cache is write-through, it's important that this function is not |  | ||||||
|  * called until the entry is present on disk and the L1 has been updated to |  | ||||||
|  * point to the entry. |  | ||||||
|  * |  | ||||||
|  * N.B. This function steals a reference to the l2_table from the caller so the |  | ||||||
|  * caller must obtain a new reference by issuing a call to |  | ||||||
|  * qed_find_l2_cache_entry(). |  | ||||||
|  */ |  | ||||||
| void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table) |  | ||||||
| { |  | ||||||
|     CachedL2Table *entry; |  | ||||||
|  |  | ||||||
|     entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset); |  | ||||||
|     if (entry) { |  | ||||||
|         qed_unref_l2_cache_entry(entry); |  | ||||||
|         qed_unref_l2_cache_entry(l2_table); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) { |  | ||||||
|         entry = QTAILQ_FIRST(&l2_cache->entries); |  | ||||||
|         QTAILQ_REMOVE(&l2_cache->entries, entry, node); |  | ||||||
|         l2_cache->n_entries--; |  | ||||||
|         qed_unref_l2_cache_entry(entry); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     l2_cache->n_entries++; |  | ||||||
|     QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node); |  | ||||||
| } |  | ||||||
| @@ -1,319 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format Table I/O |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> |  | ||||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "trace.h" |  | ||||||
| #include "qemu_socket.h" /* for EINPROGRESS on Windows */ |  | ||||||
| #include "qed.h" |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     GenericCB gencb; |  | ||||||
|     BDRVQEDState *s; |  | ||||||
|     QEDTable *table; |  | ||||||
|  |  | ||||||
|     struct iovec iov; |  | ||||||
|     QEMUIOVector qiov; |  | ||||||
| } QEDReadTableCB; |  | ||||||
|  |  | ||||||
| static void qed_read_table_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     QEDReadTableCB *read_table_cb = opaque; |  | ||||||
|     QEDTable *table = read_table_cb->table; |  | ||||||
|     int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t); |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     /* Handle I/O error */ |  | ||||||
|     if (ret) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Byteswap offsets */ |  | ||||||
|     for (i = 0; i < noffsets; i++) { |  | ||||||
|         table->offsets[i] = le64_to_cpu(table->offsets[i]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     /* Completion */ |  | ||||||
|     trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret); |  | ||||||
|     gencb_complete(&read_table_cb->gencb, ret); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, |  | ||||||
|                            BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb), |  | ||||||
|                                                 cb, opaque); |  | ||||||
|     QEMUIOVector *qiov = &read_table_cb->qiov; |  | ||||||
|     BlockDriverAIOCB *aiocb; |  | ||||||
|  |  | ||||||
|     trace_qed_read_table(s, offset, table); |  | ||||||
|  |  | ||||||
|     read_table_cb->s = s; |  | ||||||
|     read_table_cb->table = table; |  | ||||||
|     read_table_cb->iov.iov_base = table->offsets, |  | ||||||
|     read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, |  | ||||||
|  |  | ||||||
|     qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); |  | ||||||
|     aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, |  | ||||||
|                            read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, |  | ||||||
|                            qed_read_table_cb, read_table_cb); |  | ||||||
|     if (!aiocb) { |  | ||||||
|         qed_read_table_cb(read_table_cb, -EIO); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     GenericCB gencb; |  | ||||||
|     BDRVQEDState *s; |  | ||||||
|     QEDTable *orig_table; |  | ||||||
|     QEDTable *table; |  | ||||||
|     bool flush;             /* flush after write? */ |  | ||||||
|  |  | ||||||
|     struct iovec iov; |  | ||||||
|     QEMUIOVector qiov; |  | ||||||
| } QEDWriteTableCB; |  | ||||||
|  |  | ||||||
| static void qed_write_table_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     QEDWriteTableCB *write_table_cb = opaque; |  | ||||||
|  |  | ||||||
|     trace_qed_write_table_cb(write_table_cb->s, |  | ||||||
|                              write_table_cb->orig_table, |  | ||||||
|                              write_table_cb->flush, |  | ||||||
|                              ret); |  | ||||||
|  |  | ||||||
|     if (ret) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (write_table_cb->flush) { |  | ||||||
|         /* We still need to flush first */ |  | ||||||
|         write_table_cb->flush = false; |  | ||||||
|         bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, |  | ||||||
|                        write_table_cb); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     qemu_vfree(write_table_cb->table); |  | ||||||
|     gencb_complete(&write_table_cb->gencb, ret); |  | ||||||
|     return; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Write out an updated part or all of a table |  | ||||||
|  * |  | ||||||
|  * @s:          QED state |  | ||||||
|  * @offset:     Offset of table in image file, in bytes |  | ||||||
|  * @table:      Table |  | ||||||
|  * @index:      Index of first element |  | ||||||
|  * @n:          Number of elements |  | ||||||
|  * @flush:      Whether or not to sync to disk |  | ||||||
|  * @cb:         Completion function |  | ||||||
|  * @opaque:     Argument for completion function |  | ||||||
|  */ |  | ||||||
| static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, |  | ||||||
|                             unsigned int index, unsigned int n, bool flush, |  | ||||||
|                             BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     QEDWriteTableCB *write_table_cb; |  | ||||||
|     BlockDriverAIOCB *aiocb; |  | ||||||
|     unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; |  | ||||||
|     unsigned int start, end, i; |  | ||||||
|     size_t len_bytes; |  | ||||||
|  |  | ||||||
|     trace_qed_write_table(s, offset, table, index, n); |  | ||||||
|  |  | ||||||
|     /* Calculate indices of the first and one after last elements */ |  | ||||||
|     start = index & ~sector_mask; |  | ||||||
|     end = (index + n + sector_mask) & ~sector_mask; |  | ||||||
|  |  | ||||||
|     len_bytes = (end - start) * sizeof(uint64_t); |  | ||||||
|  |  | ||||||
|     write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque); |  | ||||||
|     write_table_cb->s = s; |  | ||||||
|     write_table_cb->orig_table = table; |  | ||||||
|     write_table_cb->flush = flush; |  | ||||||
|     write_table_cb->table = qemu_blockalign(s->bs, len_bytes); |  | ||||||
|     write_table_cb->iov.iov_base = write_table_cb->table->offsets; |  | ||||||
|     write_table_cb->iov.iov_len = len_bytes; |  | ||||||
|     qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1); |  | ||||||
|  |  | ||||||
|     /* Byteswap table */ |  | ||||||
|     for (i = start; i < end; i++) { |  | ||||||
|         uint64_t le_offset = cpu_to_le64(table->offsets[i]); |  | ||||||
|         write_table_cb->table->offsets[i - start] = le_offset; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Adjust for offset into table */ |  | ||||||
|     offset += start * sizeof(uint64_t); |  | ||||||
|  |  | ||||||
|     aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, |  | ||||||
|                             &write_table_cb->qiov, |  | ||||||
|                             write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, |  | ||||||
|                             qed_write_table_cb, write_table_cb); |  | ||||||
|     if (!aiocb) { |  | ||||||
|         qed_write_table_cb(write_table_cb, -EIO); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Propagate return value from async callback |  | ||||||
|  */ |  | ||||||
| static void qed_sync_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     *(int *)opaque = ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qed_read_l1_table_sync(BDRVQEDState *s) |  | ||||||
| { |  | ||||||
|     int ret = -EINPROGRESS; |  | ||||||
|  |  | ||||||
|     async_context_push(); |  | ||||||
|  |  | ||||||
|     qed_read_table(s, s->header.l1_table_offset, |  | ||||||
|                    s->l1_table, qed_sync_cb, &ret); |  | ||||||
|     while (ret == -EINPROGRESS) { |  | ||||||
|         qemu_aio_wait(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async_context_pop(); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, |  | ||||||
|                         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); |  | ||||||
|     qed_write_table(s, s->header.l1_table_offset, |  | ||||||
|                     s->l1_table, index, n, false, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, |  | ||||||
|                             unsigned int n) |  | ||||||
| { |  | ||||||
|     int ret = -EINPROGRESS; |  | ||||||
|  |  | ||||||
|     async_context_push(); |  | ||||||
|  |  | ||||||
|     qed_write_l1_table(s, index, n, qed_sync_cb, &ret); |  | ||||||
|     while (ret == -EINPROGRESS) { |  | ||||||
|         qemu_aio_wait(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async_context_pop(); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     GenericCB gencb; |  | ||||||
|     BDRVQEDState *s; |  | ||||||
|     uint64_t l2_offset; |  | ||||||
|     QEDRequest *request; |  | ||||||
| } QEDReadL2TableCB; |  | ||||||
|  |  | ||||||
| static void qed_read_l2_table_cb(void *opaque, int ret) |  | ||||||
| { |  | ||||||
|     QEDReadL2TableCB *read_l2_table_cb = opaque; |  | ||||||
|     QEDRequest *request = read_l2_table_cb->request; |  | ||||||
|     BDRVQEDState *s = read_l2_table_cb->s; |  | ||||||
|     CachedL2Table *l2_table = request->l2_table; |  | ||||||
|  |  | ||||||
|     if (ret) { |  | ||||||
|         /* can't trust loaded L2 table anymore */ |  | ||||||
|         qed_unref_l2_cache_entry(l2_table); |  | ||||||
|         request->l2_table = NULL; |  | ||||||
|     } else { |  | ||||||
|         l2_table->offset = read_l2_table_cb->l2_offset; |  | ||||||
|  |  | ||||||
|         qed_commit_l2_cache_entry(&s->l2_cache, l2_table); |  | ||||||
|  |  | ||||||
|         /* This is guaranteed to succeed because we just committed the entry |  | ||||||
|          * to the cache. |  | ||||||
|          */ |  | ||||||
|         request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, |  | ||||||
|                                                     l2_table->offset); |  | ||||||
|         assert(request->l2_table != NULL); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     gencb_complete(&read_l2_table_cb->gencb, ret); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, |  | ||||||
|                        BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     QEDReadL2TableCB *read_l2_table_cb; |  | ||||||
|  |  | ||||||
|     qed_unref_l2_cache_entry(request->l2_table); |  | ||||||
|  |  | ||||||
|     /* Check for cached L2 entry */ |  | ||||||
|     request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); |  | ||||||
|     if (request->l2_table) { |  | ||||||
|         cb(opaque, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); |  | ||||||
|     request->l2_table->table = qed_alloc_table(s); |  | ||||||
|  |  | ||||||
|     read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque); |  | ||||||
|     read_l2_table_cb->s = s; |  | ||||||
|     read_l2_table_cb->l2_offset = offset; |  | ||||||
|     read_l2_table_cb->request = request; |  | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); |  | ||||||
|     qed_read_table(s, offset, request->l2_table->table, |  | ||||||
|                    qed_read_l2_table_cb, read_l2_table_cb); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset) |  | ||||||
| { |  | ||||||
|     int ret = -EINPROGRESS; |  | ||||||
|  |  | ||||||
|     async_context_push(); |  | ||||||
|  |  | ||||||
|     qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); |  | ||||||
|     while (ret == -EINPROGRESS) { |  | ||||||
|         qemu_aio_wait(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async_context_pop(); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, |  | ||||||
|                         unsigned int index, unsigned int n, bool flush, |  | ||||||
|                         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); |  | ||||||
|     qed_write_table(s, request->l2_table->offset, |  | ||||||
|                     request->l2_table->table, index, n, flush, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, |  | ||||||
|                             unsigned int index, unsigned int n, bool flush) |  | ||||||
| { |  | ||||||
|     int ret = -EINPROGRESS; |  | ||||||
|  |  | ||||||
|     async_context_push(); |  | ||||||
|  |  | ||||||
|     qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); |  | ||||||
|     while (ret == -EINPROGRESS) { |  | ||||||
|         qemu_aio_wait(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async_context_pop(); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
							
								
								
									
										1372
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										1372
									
								
								block/qed.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										301
									
								
								block/qed.h
									
									
									
									
									
								
							
							
						
						
									
										301
									
								
								block/qed.h
									
									
									
									
									
								
							| @@ -1,301 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Enhanced Disk Format |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2010 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> |  | ||||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef BLOCK_QED_H |  | ||||||
| #define BLOCK_QED_H |  | ||||||
|  |  | ||||||
| #include "block_int.h" |  | ||||||
|  |  | ||||||
| /* The layout of a QED file is as follows: |  | ||||||
|  * |  | ||||||
|  * +--------+----------+----------+----------+-----+ |  | ||||||
|  * | header | L1 table | cluster0 | cluster1 | ... | |  | ||||||
|  * +--------+----------+----------+----------+-----+ |  | ||||||
|  * |  | ||||||
|  * There is a 2-level pagetable for cluster allocation: |  | ||||||
|  * |  | ||||||
|  *                     +----------+ |  | ||||||
|  *                     | L1 table | |  | ||||||
|  *                     +----------+ |  | ||||||
|  *                ,------'  |  '------. |  | ||||||
|  *           +----------+   |    +----------+ |  | ||||||
|  *           | L2 table |  ...   | L2 table | |  | ||||||
|  *           +----------+        +----------+ |  | ||||||
|  *       ,------'  |  '------. |  | ||||||
|  *  +----------+   |    +----------+ |  | ||||||
|  *  |   Data   |  ...   |   Data   | |  | ||||||
|  *  +----------+        +----------+ |  | ||||||
|  * |  | ||||||
|  * The L1 table is fixed size and always present.  L2 tables are allocated on |  | ||||||
|  * demand.  The L1 table size determines the maximum possible image size; it |  | ||||||
|  * can be influenced using the cluster_size and table_size values. |  | ||||||
|  * |  | ||||||
|  * All fields are little-endian on disk. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24, |  | ||||||
|  |  | ||||||
|     /* The image supports a backing file */ |  | ||||||
|     QED_F_BACKING_FILE = 0x01, |  | ||||||
|  |  | ||||||
|     /* The image needs a consistency check before use */ |  | ||||||
|     QED_F_NEED_CHECK = 0x02, |  | ||||||
|  |  | ||||||
|     /* The backing file format must not be probed, treat as raw image */ |  | ||||||
|     QED_F_BACKING_FORMAT_NO_PROBE = 0x04, |  | ||||||
|  |  | ||||||
|     /* Feature bits must be used when the on-disk format changes */ |  | ||||||
|     QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */ |  | ||||||
|                        QED_F_NEED_CHECK | |  | ||||||
|                        QED_F_BACKING_FORMAT_NO_PROBE, |  | ||||||
|     QED_COMPAT_FEATURE_MASK = 0,            /* supported compat feature bits */ |  | ||||||
|     QED_AUTOCLEAR_FEATURE_MASK = 0,         /* supported autoclear feature bits */ |  | ||||||
|  |  | ||||||
|     /* Data is stored in groups of sectors called clusters.  Cluster size must |  | ||||||
|      * be large to avoid keeping too much metadata.  I/O requests that have |  | ||||||
|      * sub-cluster size will require read-modify-write. |  | ||||||
|      */ |  | ||||||
|     QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */ |  | ||||||
|     QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024, |  | ||||||
|     QED_DEFAULT_CLUSTER_SIZE = 64 * 1024, |  | ||||||
|  |  | ||||||
|     /* Allocated clusters are tracked using a 2-level pagetable.  Table size is |  | ||||||
|      * a multiple of clusters so large maximum image sizes can be supported |  | ||||||
|      * without jacking up the cluster size too much. |  | ||||||
|      */ |  | ||||||
|     QED_MIN_TABLE_SIZE = 1,        /* in clusters */ |  | ||||||
|     QED_MAX_TABLE_SIZE = 16, |  | ||||||
|     QED_DEFAULT_TABLE_SIZE = 4, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     uint32_t magic;                 /* QED\0 */ |  | ||||||
|  |  | ||||||
|     uint32_t cluster_size;          /* in bytes */ |  | ||||||
|     uint32_t table_size;            /* for L1 and L2 tables, in clusters */ |  | ||||||
|     uint32_t header_size;           /* in clusters */ |  | ||||||
|  |  | ||||||
|     uint64_t features;              /* format feature bits */ |  | ||||||
|     uint64_t compat_features;       /* compatible feature bits */ |  | ||||||
|     uint64_t autoclear_features;    /* self-resetting feature bits */ |  | ||||||
|  |  | ||||||
|     uint64_t l1_table_offset;       /* in bytes */ |  | ||||||
|     uint64_t image_size;            /* total logical image size, in bytes */ |  | ||||||
|  |  | ||||||
|     /* if (features & QED_F_BACKING_FILE) */ |  | ||||||
|     uint32_t backing_filename_offset; /* in bytes from start of header */ |  | ||||||
|     uint32_t backing_filename_size;   /* in bytes */ |  | ||||||
| } QEDHeader; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     uint64_t offsets[0];            /* in bytes */ |  | ||||||
| } QEDTable; |  | ||||||
|  |  | ||||||
| /* The L2 cache is a simple write-through cache for L2 structures */ |  | ||||||
| typedef struct CachedL2Table { |  | ||||||
|     QEDTable *table; |  | ||||||
|     uint64_t offset;    /* offset=0 indicates an invalidate entry */ |  | ||||||
|     QTAILQ_ENTRY(CachedL2Table) node; |  | ||||||
|     int ref; |  | ||||||
| } CachedL2Table; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     QTAILQ_HEAD(, CachedL2Table) entries; |  | ||||||
|     unsigned int n_entries; |  | ||||||
| } L2TableCache; |  | ||||||
|  |  | ||||||
| typedef struct QEDRequest { |  | ||||||
|     CachedL2Table *l2_table; |  | ||||||
| } QEDRequest; |  | ||||||
|  |  | ||||||
| typedef struct QEDAIOCB { |  | ||||||
|     BlockDriverAIOCB common; |  | ||||||
|     QEMUBH *bh; |  | ||||||
|     int bh_ret;                     /* final return status for completion bh */ |  | ||||||
|     QSIMPLEQ_ENTRY(QEDAIOCB) next;  /* next request */ |  | ||||||
|     bool is_write;                  /* false - read, true - write */ |  | ||||||
|     bool *finished;                 /* signal for cancel completion */ |  | ||||||
|     uint64_t end_pos;               /* request end on block device, in bytes */ |  | ||||||
|  |  | ||||||
|     /* User scatter-gather list */ |  | ||||||
|     QEMUIOVector *qiov; |  | ||||||
|     size_t qiov_offset;             /* byte count already processed */ |  | ||||||
|  |  | ||||||
|     /* Current cluster scatter-gather list */ |  | ||||||
|     QEMUIOVector cur_qiov; |  | ||||||
|     uint64_t cur_pos;               /* position on block device, in bytes */ |  | ||||||
|     uint64_t cur_cluster;           /* cluster offset in image file */ |  | ||||||
|     unsigned int cur_nclusters;     /* number of clusters being accessed */ |  | ||||||
|     int find_cluster_ret;           /* used for L1/L2 update */ |  | ||||||
|  |  | ||||||
|     QEDRequest request; |  | ||||||
| } QEDAIOCB; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BlockDriverState *bs;           /* device */ |  | ||||||
|     uint64_t file_size;             /* length of image file, in bytes */ |  | ||||||
|  |  | ||||||
|     QEDHeader header;               /* always cpu-endian */ |  | ||||||
|     QEDTable *l1_table; |  | ||||||
|     L2TableCache l2_cache;          /* l2 table cache */ |  | ||||||
|     uint32_t table_nelems; |  | ||||||
|     uint32_t l1_shift; |  | ||||||
|     uint32_t l2_shift; |  | ||||||
|     uint32_t l2_mask; |  | ||||||
|  |  | ||||||
|     /* Allocating write request queue */ |  | ||||||
|     QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs; |  | ||||||
| } BDRVQEDState; |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     QED_CLUSTER_FOUND,         /* cluster found */ |  | ||||||
|     QED_CLUSTER_L2,            /* cluster missing in L2 */ |  | ||||||
|     QED_CLUSTER_L1,            /* cluster missing in L1 */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * qed_find_cluster() completion callback |  | ||||||
|  * |  | ||||||
|  * @opaque:     User data for completion callback |  | ||||||
|  * @ret:        QED_CLUSTER_FOUND   Success |  | ||||||
|  *              QED_CLUSTER_L2      Data cluster unallocated in L2 |  | ||||||
|  *              QED_CLUSTER_L1      L2 unallocated in L1 |  | ||||||
|  *              -errno              POSIX error occurred |  | ||||||
|  * @offset:     Data cluster offset |  | ||||||
|  * @len:        Contiguous bytes starting from cluster offset |  | ||||||
|  * |  | ||||||
|  * This function is invoked when qed_find_cluster() completes. |  | ||||||
|  * |  | ||||||
|  * On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range |  | ||||||
|  * in the image file. |  | ||||||
|  * |  | ||||||
|  * On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1 |  | ||||||
|  * table offset, respectively.  len is number of contiguous unallocated bytes. |  | ||||||
|  */ |  | ||||||
| typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Generic callback for chaining async callbacks |  | ||||||
|  */ |  | ||||||
| typedef struct { |  | ||||||
|     BlockDriverCompletionFunc *cb; |  | ||||||
|     void *opaque; |  | ||||||
| } GenericCB; |  | ||||||
|  |  | ||||||
| void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque); |  | ||||||
| void gencb_complete(void *opaque, int ret); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * L2 cache functions |  | ||||||
|  */ |  | ||||||
| void qed_init_l2_cache(L2TableCache *l2_cache); |  | ||||||
| void qed_free_l2_cache(L2TableCache *l2_cache); |  | ||||||
| CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache); |  | ||||||
| void qed_unref_l2_cache_entry(CachedL2Table *entry); |  | ||||||
| CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset); |  | ||||||
| void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Table I/O functions |  | ||||||
|  */ |  | ||||||
| int qed_read_l1_table_sync(BDRVQEDState *s); |  | ||||||
| void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, |  | ||||||
|                         BlockDriverCompletionFunc *cb, void *opaque); |  | ||||||
| int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, |  | ||||||
|                             unsigned int n); |  | ||||||
| int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, |  | ||||||
|                            uint64_t offset); |  | ||||||
| void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, |  | ||||||
|                        BlockDriverCompletionFunc *cb, void *opaque); |  | ||||||
| void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, |  | ||||||
|                         unsigned int index, unsigned int n, bool flush, |  | ||||||
|                         BlockDriverCompletionFunc *cb, void *opaque); |  | ||||||
| int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, |  | ||||||
|                             unsigned int index, unsigned int n, bool flush); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Cluster functions |  | ||||||
|  */ |  | ||||||
| void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, |  | ||||||
|                       size_t len, QEDFindClusterFunc *cb, void *opaque); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Consistency check |  | ||||||
|  */ |  | ||||||
| int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); |  | ||||||
|  |  | ||||||
| QEDTable *qed_alloc_table(BDRVQEDState *s); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Round down to the start of a cluster |  | ||||||
|  */ |  | ||||||
| static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset) |  | ||||||
| { |  | ||||||
|     return offset & ~(uint64_t)(s->header.cluster_size - 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset) |  | ||||||
| { |  | ||||||
|     return offset & (s->header.cluster_size - 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes) |  | ||||||
| { |  | ||||||
|     return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) / |  | ||||||
|            (s->header.cluster_size - 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos) |  | ||||||
| { |  | ||||||
|     return pos >> s->l1_shift; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos) |  | ||||||
| { |  | ||||||
|     return (pos >> s->l2_shift) & s->l2_mask; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Test if a cluster offset is valid |  | ||||||
|  */ |  | ||||||
| static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset) |  | ||||||
| { |  | ||||||
|     uint64_t header_size = (uint64_t)s->header.header_size * |  | ||||||
|                            s->header.cluster_size; |  | ||||||
|  |  | ||||||
|     if (offset & (s->header.cluster_size - 1)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return offset >= header_size && offset < s->file_size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Test if a table offset is valid |  | ||||||
|  */ |  | ||||||
| static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset) |  | ||||||
| { |  | ||||||
|     uint64_t end_offset = offset + (s->header.table_size - 1) * |  | ||||||
|                           s->header.cluster_size; |  | ||||||
|  |  | ||||||
|     /* Overflow check */ |  | ||||||
|     if (end_offset <= offset) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return qed_check_cluster_offset(s, offset) && |  | ||||||
|            qed_check_cluster_offset(s, end_offset); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif /* BLOCK_QED_H */ |  | ||||||
| @@ -27,6 +27,8 @@ | |||||||
| #include "qemu-log.h" | #include "qemu-log.h" | ||||||
| #include "block_int.h" | #include "block_int.h" | ||||||
| #include "module.h" | #include "module.h" | ||||||
|  | #include "compatfd.h" | ||||||
|  | #include <assert.h> | ||||||
| #include "block/raw-posix-aio.h" | #include "block/raw-posix-aio.h" | ||||||
|  |  | ||||||
| #ifdef CONFIG_COCOA | #ifdef CONFIG_COCOA | ||||||
| @@ -48,7 +50,6 @@ | |||||||
| #endif | #endif | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
| #include <sys/param.h> |  | ||||||
| #include <linux/cdrom.h> | #include <linux/cdrom.h> | ||||||
| #include <linux/fd.h> | #include <linux/fd.h> | ||||||
| #endif | #endif | ||||||
| @@ -69,10 +70,6 @@ | |||||||
| #include <sys/diskslice.h> | #include <sys/diskslice.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef CONFIG_XFS |  | ||||||
| #include <xfs/xfs.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| //#define DEBUG_FLOPPY | //#define DEBUG_FLOPPY | ||||||
|  |  | ||||||
| //#define DEBUG_BLOCK | //#define DEBUG_BLOCK | ||||||
| @@ -101,15 +98,16 @@ | |||||||
| #define FTYPE_CD     1 | #define FTYPE_CD     1 | ||||||
| #define FTYPE_FD     2 | #define FTYPE_FD     2 | ||||||
|  |  | ||||||
| /* if the FD is not accessed during that time (in ns), we try to | #define ALIGNED_BUFFER_SIZE (32 * 512) | ||||||
|    reopen it to see if the disk has been changed */ |  | ||||||
| #define FD_OPEN_TIMEOUT (1000000000) |  | ||||||
|  |  | ||||||
| #define MAX_BLOCKSIZE	4096 | /* if the FD is not accessed during that time (in ms), we try to | ||||||
|  |    reopen it to see if the disk has been changed */ | ||||||
|  | #define FD_OPEN_TIMEOUT 1000 | ||||||
|  |  | ||||||
| typedef struct BDRVRawState { | typedef struct BDRVRawState { | ||||||
|     int fd; |     int fd; | ||||||
|     int type; |     int type; | ||||||
|  |     unsigned int lseek_err_cnt; | ||||||
|     int open_flags; |     int open_flags; | ||||||
| #if defined(__linux__) | #if defined(__linux__) | ||||||
|     /* linux floppy specific */ |     /* linux floppy specific */ | ||||||
| @@ -122,11 +120,7 @@ typedef struct BDRVRawState { | |||||||
|     int use_aio; |     int use_aio; | ||||||
|     void *aio_ctx; |     void *aio_ctx; | ||||||
| #endif | #endif | ||||||
|     uint8_t *aligned_buf; |     uint8_t* aligned_buf; | ||||||
|     unsigned aligned_buf_size; |  | ||||||
| #ifdef CONFIG_XFS |  | ||||||
|     bool is_xfs : 1; |  | ||||||
| #endif |  | ||||||
| } BDRVRawState; | } BDRVRawState; | ||||||
|  |  | ||||||
| static int fd_open(BlockDriverState *bs); | static int fd_open(BlockDriverState *bs); | ||||||
| @@ -142,12 +136,15 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | |||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     int fd, ret; |     int fd, ret; | ||||||
|  |  | ||||||
|  |     s->lseek_err_cnt = 0; | ||||||
|  |  | ||||||
|     s->open_flags = open_flags | O_BINARY; |     s->open_flags = open_flags | O_BINARY; | ||||||
|     s->open_flags &= ~O_ACCMODE; |     s->open_flags &= ~O_ACCMODE; | ||||||
|     if (bdrv_flags & BDRV_O_RDWR) { |     if ((bdrv_flags & BDRV_O_ACCESS) == BDRV_O_RDWR) { | ||||||
|         s->open_flags |= O_RDWR; |         s->open_flags |= O_RDWR; | ||||||
|     } else { |     } else { | ||||||
|         s->open_flags |= O_RDONLY; |         s->open_flags |= O_RDONLY; | ||||||
|  |         bs->read_only = 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Use O_DSYNC for write-through caching, no flags for write-back caching, |     /* Use O_DSYNC for write-through caching, no flags for write-back caching, | ||||||
| @@ -169,12 +166,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | |||||||
|     s->aligned_buf = NULL; |     s->aligned_buf = NULL; | ||||||
|  |  | ||||||
|     if ((bdrv_flags & BDRV_O_NOCACHE)) { |     if ((bdrv_flags & BDRV_O_NOCACHE)) { | ||||||
|         /* |         s->aligned_buf = qemu_blockalign(bs, ALIGNED_BUFFER_SIZE); | ||||||
|          * Allocate a buffer for read/modify/write cycles.  Chose the size |  | ||||||
|          * pessimistically as we don't know the block size yet. |  | ||||||
|          */ |  | ||||||
|         s->aligned_buf_size = 32 * MAX_BLOCKSIZE; |  | ||||||
|         s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size); |  | ||||||
|         if (s->aligned_buf == NULL) { |         if (s->aligned_buf == NULL) { | ||||||
|             goto out_close; |             goto out_close; | ||||||
|         } |         } | ||||||
| @@ -203,12 +195,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | |||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef CONFIG_XFS |  | ||||||
|     if (platform_test_xfs_fd(s->fd)) { |  | ||||||
|         s->is_xfs = 1; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| out_free_buf: | out_free_buf: | ||||||
| @@ -221,9 +207,13 @@ out_close: | |||||||
| static int raw_open(BlockDriverState *bs, const char *filename, int flags) | static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|  |     int open_flags = 0; | ||||||
|  |  | ||||||
|     s->type = FTYPE_FILE; |     s->type = FTYPE_FILE; | ||||||
|     return raw_open_common(bs, filename, flags, 0); |     if (flags & BDRV_O_CREAT) | ||||||
|  |         open_flags = O_CREAT | O_TRUNC; | ||||||
|  |  | ||||||
|  |     return raw_open_common(bs, filename, flags, open_flags); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* XXX: use host sector size if necessary with: | /* XXX: use host sector size if necessary with: | ||||||
| @@ -236,7 +226,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|         } |         } | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_COCOA | #ifdef CONFIG_COCOA | ||||||
|         uint32_t blockSize = 512; |         u_int32_t   blockSize = 512; | ||||||
|         if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { |         if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { | ||||||
|             bufsize = blockSize; |             bufsize = blockSize; | ||||||
|         } |         } | ||||||
| @@ -260,16 +250,29 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | |||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|  |  | ||||||
|     ret = pread(s->fd, buf, count, offset); |     if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { | ||||||
|  |         ++(s->lseek_err_cnt); | ||||||
|  |         if(s->lseek_err_cnt <= 10) { | ||||||
|  |             DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||||
|  |                               "] lseek failed : %d = %s\n", | ||||||
|  |                               s->fd, bs->filename, offset, buf, count, | ||||||
|  |                               bs->total_sectors, errno, strerror(errno)); | ||||||
|  |         } | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     s->lseek_err_cnt=0; | ||||||
|  |  | ||||||
|  |     ret = read(s->fd, buf, count); | ||||||
|     if (ret == count) |     if (ret == count) | ||||||
|         return ret; |         goto label__raw_read__success; | ||||||
|  |  | ||||||
|     /* Allow reads beyond the end (needed for pwrite) */ |     /* Allow reads beyond the end (needed for pwrite) */ | ||||||
|     if ((ret == 0) && bs->growable) { |     if ((ret == 0) && bs->growable) { | ||||||
|         int64_t size = raw_getlength(bs); |         int64_t size = raw_getlength(bs); | ||||||
|         if (offset >= size) { |         if (offset >= size) { | ||||||
|             memset(buf, 0, count); |             memset(buf, 0, count); | ||||||
|             return count; |             ret = count; | ||||||
|  |             goto label__raw_read__success; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -279,13 +282,15 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | |||||||
|                       bs->total_sectors, ret, errno, strerror(errno)); |                       bs->total_sectors, ret, errno, strerror(errno)); | ||||||
|  |  | ||||||
|     /* Try harder for CDrom. */ |     /* Try harder for CDrom. */ | ||||||
|     if (s->type != FTYPE_FILE) { |     if (bs->type == BDRV_TYPE_CDROM) { | ||||||
|         ret = pread(s->fd, buf, count, offset); |         lseek(s->fd, offset, SEEK_SET); | ||||||
|  |         ret = read(s->fd, buf, count); | ||||||
|         if (ret == count) |         if (ret == count) | ||||||
|             return ret; |             goto label__raw_read__success; | ||||||
|         ret = pread(s->fd, buf, count, offset); |         lseek(s->fd, offset, SEEK_SET); | ||||||
|  |         ret = read(s->fd, buf, count); | ||||||
|         if (ret == count) |         if (ret == count) | ||||||
|             return ret; |             goto label__raw_read__success; | ||||||
|  |  | ||||||
|         DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 |         DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||||
|                           "] retry read failed %d : %d = %s\n", |                           "] retry read failed %d : %d = %s\n", | ||||||
| @@ -293,13 +298,14 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | |||||||
|                           bs->total_sectors, ret, errno, strerror(errno)); |                           bs->total_sectors, ret, errno, strerror(errno)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | label__raw_read__success: | ||||||
|  |  | ||||||
|     return  (ret < 0) ? -errno : ret; |     return  (ret < 0) ? -errno : ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * offset and count are in bytes, but must be multiples of the sector size |  * offset and count are in bytes, but must be multiples of 512 for files | ||||||
|  * for files opened with O_DIRECT. buf must be aligned to sector size bytes |  * opened with O_DIRECT. buf must be aligned to 512 bytes then. | ||||||
|  * then. |  | ||||||
|  * |  * | ||||||
|  * This function may be called without alignment if the caller ensures |  * This function may be called without alignment if the caller ensures | ||||||
|  * that O_DIRECT is not in effect. |  * that O_DIRECT is not in effect. | ||||||
| @@ -314,15 +320,29 @@ static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, | |||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return -errno; |         return -errno; | ||||||
|  |  | ||||||
|     ret = pwrite(s->fd, buf, count, offset); |     if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { | ||||||
|  |         ++(s->lseek_err_cnt); | ||||||
|  |         if(s->lseek_err_cnt) { | ||||||
|  |             DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" | ||||||
|  |                               PRId64 "] lseek failed : %d = %s\n", | ||||||
|  |                               s->fd, bs->filename, offset, buf, count, | ||||||
|  |                               bs->total_sectors, errno, strerror(errno)); | ||||||
|  |         } | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     s->lseek_err_cnt = 0; | ||||||
|  |  | ||||||
|  |     ret = write(s->fd, buf, count); | ||||||
|     if (ret == count) |     if (ret == count) | ||||||
|         return ret; |         goto label__raw_write__success; | ||||||
|  |  | ||||||
|     DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 |     DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||||
|                       "] write failed %d : %d = %s\n", |                       "] write failed %d : %d = %s\n", | ||||||
|                       s->fd, bs->filename, offset, buf, count, |                       s->fd, bs->filename, offset, buf, count, | ||||||
|                       bs->total_sectors, ret, errno, strerror(errno)); |                       bs->total_sectors, ret, errno, strerror(errno)); | ||||||
|  |  | ||||||
|  | label__raw_write__success: | ||||||
|  |  | ||||||
|     return  (ret < 0) ? -errno : ret; |     return  (ret < 0) ? -errno : ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -336,25 +356,24 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, | |||||||
|                      uint8_t *buf, int count) |                      uint8_t *buf, int count) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     unsigned sector_mask = bs->buffer_alignment - 1; |  | ||||||
|     int size, ret, shift, sum; |     int size, ret, shift, sum; | ||||||
|  |  | ||||||
|     sum = 0; |     sum = 0; | ||||||
|  |  | ||||||
|     if (s->aligned_buf != NULL)  { |     if (s->aligned_buf != NULL)  { | ||||||
|  |  | ||||||
|         if (offset & sector_mask) { |         if (offset & 0x1ff) { | ||||||
|             /* align offset on a sector size bytes boundary */ |             /* align offset on a 512 bytes boundary */ | ||||||
|  |  | ||||||
|             shift = offset & sector_mask; |             shift = offset & 0x1ff; | ||||||
|             size = (shift + count + sector_mask) & ~sector_mask; |             size = (shift + count + 0x1ff) & ~0x1ff; | ||||||
|             if (size > s->aligned_buf_size) |             if (size > ALIGNED_BUFFER_SIZE) | ||||||
|                 size = s->aligned_buf_size; |                 size = ALIGNED_BUFFER_SIZE; | ||||||
|             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size); |             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size); | ||||||
|             if (ret < 0) |             if (ret < 0) | ||||||
|                 return ret; |                 return ret; | ||||||
|  |  | ||||||
|             size = bs->buffer_alignment - shift; |             size = 512 - shift; | ||||||
|             if (size > count) |             if (size > count) | ||||||
|                 size = count; |                 size = count; | ||||||
|             memcpy(buf, s->aligned_buf + shift, size); |             memcpy(buf, s->aligned_buf + shift, size); | ||||||
| @@ -367,23 +386,19 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, | |||||||
|             if (count == 0) |             if (count == 0) | ||||||
|                 return sum; |                 return sum; | ||||||
|         } |         } | ||||||
|         if (count & sector_mask || (uintptr_t) buf & sector_mask) { |         if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { | ||||||
|  |  | ||||||
|             /* read on aligned buffer */ |             /* read on aligned buffer */ | ||||||
|  |  | ||||||
|             while (count) { |             while (count) { | ||||||
|  |  | ||||||
|                 size = (count + sector_mask) & ~sector_mask; |                 size = (count + 0x1ff) & ~0x1ff; | ||||||
|                 if (size > s->aligned_buf_size) |                 if (size > ALIGNED_BUFFER_SIZE) | ||||||
|                     size = s->aligned_buf_size; |                     size = ALIGNED_BUFFER_SIZE; | ||||||
|  |  | ||||||
|                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, size); |                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, size); | ||||||
|                 if (ret < 0) { |                 if (ret < 0) | ||||||
|                     return ret; |                     return ret; | ||||||
|                 } else if (ret == 0) { |  | ||||||
|                     fprintf(stderr, "raw_pread: read beyond end of file\n"); |  | ||||||
|                     abort(); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 size = ret; |                 size = ret; | ||||||
|                 if (size > count) |                 if (size > count) | ||||||
| @@ -409,9 +424,8 @@ static int raw_read(BlockDriverState *bs, int64_t sector_num, | |||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = raw_pread(bs, sector_num * BDRV_SECTOR_SIZE, buf, |     ret = raw_pread(bs, sector_num * 512, buf, nb_sectors * 512); | ||||||
|                     nb_sectors * BDRV_SECTOR_SIZE); |     if (ret == (nb_sectors * 512)) | ||||||
|     if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) |  | ||||||
|         ret = 0; |         ret = 0; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -425,28 +439,25 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | |||||||
|                       const uint8_t *buf, int count) |                       const uint8_t *buf, int count) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     unsigned sector_mask = bs->buffer_alignment - 1; |  | ||||||
|     int size, ret, shift, sum; |     int size, ret, shift, sum; | ||||||
|  |  | ||||||
|     sum = 0; |     sum = 0; | ||||||
|  |  | ||||||
|     if (s->aligned_buf != NULL) { |     if (s->aligned_buf != NULL) { | ||||||
|  |  | ||||||
|         if (offset & sector_mask) { |         if (offset & 0x1ff) { | ||||||
|             /* align offset on a sector size bytes boundary */ |             /* align offset on a 512 bytes boundary */ | ||||||
|             shift = offset & sector_mask; |             shift = offset & 0x1ff; | ||||||
|             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, |             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512); | ||||||
|                                     bs->buffer_alignment); |  | ||||||
|             if (ret < 0) |             if (ret < 0) | ||||||
|                 return ret; |                 return ret; | ||||||
|  |  | ||||||
|             size = bs->buffer_alignment - shift; |             size = 512 - shift; | ||||||
|             if (size > count) |             if (size > count) | ||||||
|                 size = count; |                 size = count; | ||||||
|             memcpy(s->aligned_buf + shift, buf, size); |             memcpy(s->aligned_buf + shift, buf, size); | ||||||
|  |  | ||||||
|             ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, |             ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512); | ||||||
|                                      bs->buffer_alignment); |  | ||||||
|             if (ret < 0) |             if (ret < 0) | ||||||
|                 return ret; |                 return ret; | ||||||
|  |  | ||||||
| @@ -458,12 +469,12 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | |||||||
|             if (count == 0) |             if (count == 0) | ||||||
|                 return sum; |                 return sum; | ||||||
|         } |         } | ||||||
|         if (count & sector_mask || (uintptr_t) buf & sector_mask) { |         if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { | ||||||
|  |  | ||||||
|             while ((size = (count & ~sector_mask)) != 0) { |             while ((size = (count & ~0x1ff)) != 0) { | ||||||
|  |  | ||||||
|                 if (size > s->aligned_buf_size) |                 if (size > ALIGNED_BUFFER_SIZE) | ||||||
|                     size = s->aligned_buf_size; |                     size = ALIGNED_BUFFER_SIZE; | ||||||
|  |  | ||||||
|                 memcpy(s->aligned_buf, buf, size); |                 memcpy(s->aligned_buf, buf, size); | ||||||
|  |  | ||||||
| @@ -476,16 +487,14 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | |||||||
|                 count -= ret; |                 count -= ret; | ||||||
|                 sum += ret; |                 sum += ret; | ||||||
|             } |             } | ||||||
|             /* here, count < sector_size because (count & ~sector_mask) == 0 */ |             /* here, count < 512 because (count & ~0x1ff) == 0 */ | ||||||
|             if (count) { |             if (count) { | ||||||
|                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, |                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512); | ||||||
|                                      bs->buffer_alignment); |  | ||||||
|                 if (ret < 0) |                 if (ret < 0) | ||||||
|                     return ret; |                     return ret; | ||||||
|                  memcpy(s->aligned_buf, buf, count); |                  memcpy(s->aligned_buf, buf, count); | ||||||
|  |  | ||||||
|                  ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, |                  ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512); | ||||||
|                                      bs->buffer_alignment); |  | ||||||
|                  if (ret < 0) |                  if (ret < 0) | ||||||
|                      return ret; |                      return ret; | ||||||
|                  if (count < ret) |                  if (count < ret) | ||||||
| @@ -503,9 +512,8 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | |||||||
|                      const uint8_t *buf, int nb_sectors) |                      const uint8_t *buf, int nb_sectors) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
|     ret = raw_pwrite(bs, sector_num * BDRV_SECTOR_SIZE, buf, |     ret = raw_pwrite(bs, sector_num * 512, buf, nb_sectors * 512); | ||||||
|                      nb_sectors * BDRV_SECTOR_SIZE); |     if (ret == (nb_sectors * 512)) | ||||||
|     if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) |  | ||||||
|         ret = 0; |         ret = 0; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -513,12 +521,12 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | |||||||
| /* | /* | ||||||
|  * Check if all memory in this vector is sector aligned. |  * Check if all memory in this vector is sector aligned. | ||||||
|  */ |  */ | ||||||
| static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) | static int qiov_is_aligned(QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < qiov->niov; i++) { |     for (i = 0; i < qiov->niov; i++) { | ||||||
|         if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) { |         if ((uintptr_t) qiov->iov[i].iov_base % 512) { | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -541,7 +549,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs, | |||||||
|      * driver that it needs to copy the buffer. |      * driver that it needs to copy the buffer. | ||||||
|      */ |      */ | ||||||
|     if (s->aligned_buf) { |     if (s->aligned_buf) { | ||||||
|         if (!qiov_is_aligned(bs, qiov)) { |         if (!qiov_is_aligned(qiov)) { | ||||||
|             type |= QEMU_AIO_MISALIGNED; |             type |= QEMU_AIO_MISALIGNED; | ||||||
| #ifdef CONFIG_LINUX_AIO | #ifdef CONFIG_LINUX_AIO | ||||||
|         } else if (s->use_aio) { |         } else if (s->use_aio) { | ||||||
| @@ -622,41 +630,21 @@ static int64_t raw_getlength(BlockDriverState *bs) | |||||||
|     } else |     } else | ||||||
|         return st.st_size; |         return st.st_size; | ||||||
| } | } | ||||||
| #elif defined(__sun__) | #else /* !__OpenBSD__ */ | ||||||
| static int64_t raw_getlength(BlockDriverState *bs) | static int64_t  raw_getlength(BlockDriverState *bs) | ||||||
| { |  | ||||||
|     BDRVRawState *s = bs->opaque; |  | ||||||
|     struct dk_minfo minfo; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     ret = fd_open(bs); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * Use the DKIOCGMEDIAINFO ioctl to read the size. |  | ||||||
|      */ |  | ||||||
|     ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); |  | ||||||
|     if (ret != -1) { |  | ||||||
|         return minfo.dki_lbsize * minfo.dki_capacity; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * There are reports that lseek on some devices fails, but |  | ||||||
|      * irc discussion said that contingency on contingency was overkill. |  | ||||||
|      */ |  | ||||||
|     return lseek(s->fd, 0, SEEK_END); |  | ||||||
| } |  | ||||||
| #elif defined(CONFIG_BSD) |  | ||||||
| static int64_t raw_getlength(BlockDriverState *bs) |  | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     int fd = s->fd; |     int fd = s->fd; | ||||||
|     int64_t size; |     int64_t size; | ||||||
|  | #ifdef CONFIG_BSD | ||||||
|     struct stat sb; |     struct stat sb; | ||||||
| #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||||
|     int reopened = 0; |     int reopened = 0; | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  | #ifdef __sun__ | ||||||
|  |     struct dk_minfo minfo; | ||||||
|  |     int rv; | ||||||
| #endif | #endif | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
| @@ -664,6 +652,7 @@ static int64_t raw_getlength(BlockDriverState *bs) | |||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|  |  | ||||||
|  | #ifdef CONFIG_BSD | ||||||
| #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||||
| again: | again: | ||||||
| #endif | #endif | ||||||
| @@ -698,24 +687,24 @@ again: | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| #endif | #endif | ||||||
|     } else { |     } else | ||||||
|  | #endif | ||||||
|  | #ifdef __sun__ | ||||||
|  |     /* | ||||||
|  |      * use the DKIOCGMEDIAINFO ioctl to read the size. | ||||||
|  |      */ | ||||||
|  |     rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); | ||||||
|  |     if ( rv != -1 ) { | ||||||
|  |         size = minfo.dki_lbsize * minfo.dki_capacity; | ||||||
|  |     } else /* there are reports that lseek on some devices | ||||||
|  |               fails, but irc discussion said that contingency | ||||||
|  |               on contingency was overkill */ | ||||||
|  | #endif | ||||||
|  |     { | ||||||
|         size = lseek(fd, 0, SEEK_END); |         size = lseek(fd, 0, SEEK_END); | ||||||
|     } |     } | ||||||
|     return size; |     return size; | ||||||
| } | } | ||||||
| #else |  | ||||||
| static int64_t raw_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVRawState *s = bs->opaque; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     ret = fd_open(bs); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return lseek(s->fd, 0, SEEK_END); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int raw_create(const char *filename, QEMUOptionParameter *options) | static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||||
| @@ -727,7 +716,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     /* Read out options */ |     /* Read out options */ | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
|         if (!strcmp(options->name, BLOCK_OPT_SIZE)) { |         if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | ||||||
|             total_size = options->value.n / BDRV_SECTOR_SIZE; |             total_size = options->value.n / 512; | ||||||
|         } |         } | ||||||
|         options++; |         options++; | ||||||
|     } |     } | ||||||
| @@ -737,7 +726,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     if (fd < 0) { |     if (fd < 0) { | ||||||
|         result = -errno; |         result = -errno; | ||||||
|     } else { |     } else { | ||||||
|         if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) { |         if (ftruncate(fd, total_size * 512) != 0) { | ||||||
|             result = -errno; |             result = -errno; | ||||||
|         } |         } | ||||||
|         if (close(fd) != 0) { |         if (close(fd) != 0) { | ||||||
| @@ -747,43 +736,12 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int raw_flush(BlockDriverState *bs) | static void raw_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     return qemu_fdatasync(s->fd); |     qemu_fdatasync(s->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef CONFIG_XFS |  | ||||||
| static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors) |  | ||||||
| { |  | ||||||
|     struct xfs_flock64 fl; |  | ||||||
|  |  | ||||||
|     memset(&fl, 0, sizeof(fl)); |  | ||||||
|     fl.l_whence = SEEK_SET; |  | ||||||
|     fl.l_start = sector_num << 9; |  | ||||||
|     fl.l_len = (int64_t)nb_sectors << 9; |  | ||||||
|  |  | ||||||
|     if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { |  | ||||||
|         DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); |  | ||||||
|         return -errno; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_XFS |  | ||||||
|     BDRVRawState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     if (s->is_xfs) { |  | ||||||
|         return xfs_discard(s, sector_num, nb_sectors); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static QEMUOptionParameter raw_create_options[] = { | static QEMUOptionParameter raw_create_options[] = { | ||||||
|     { |     { | ||||||
| @@ -794,18 +752,16 @@ static QEMUOptionParameter raw_create_options[] = { | |||||||
|     { NULL } |     { NULL } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_file = { | static BlockDriver bdrv_raw = { | ||||||
|     .format_name = "file", |     .format_name = "raw", | ||||||
|     .protocol_name = "file", |  | ||||||
|     .instance_size = sizeof(BDRVRawState), |     .instance_size = sizeof(BDRVRawState), | ||||||
|     .bdrv_probe = NULL, /* no probe for protocols */ |     .bdrv_probe = NULL, /* no probe for protocols */ | ||||||
|     .bdrv_file_open = raw_open, |     .bdrv_open = raw_open, | ||||||
|     .bdrv_read = raw_read, |     .bdrv_read = raw_read, | ||||||
|     .bdrv_write = raw_write, |     .bdrv_write = raw_write, | ||||||
|     .bdrv_close = raw_close, |     .bdrv_close = raw_close, | ||||||
|     .bdrv_create = raw_create, |     .bdrv_create = raw_create, | ||||||
|     .bdrv_flush = raw_flush, |     .bdrv_flush = raw_flush, | ||||||
|     .bdrv_discard = raw_discard, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_readv = raw_aio_readv, |     .bdrv_aio_readv = raw_aio_readv, | ||||||
|     .bdrv_aio_writev = raw_aio_writev, |     .bdrv_aio_writev = raw_aio_writev, | ||||||
| @@ -927,13 +883,8 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|  |  | ||||||
|     s->type = FTYPE_FILE; |     s->type = FTYPE_FILE; | ||||||
| #if defined(__linux__) | #if defined(__linux__) | ||||||
|     { |     if (strstart(filename, "/dev/sg", NULL)) { | ||||||
|         char resolved_path[ MAXPATHLEN ], *temp; |         bs->sg = 1; | ||||||
|  |  | ||||||
|         temp = realpath(filename, resolved_path); |  | ||||||
|         if (temp && strstart(temp, "/dev/sg", NULL)) { |  | ||||||
|             bs->sg = 1; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -953,7 +904,7 @@ static int fd_open(BlockDriverState *bs) | |||||||
|         return 0; |         return 0; | ||||||
|     last_media_present = (s->fd >= 0); |     last_media_present = (s->fd >= 0); | ||||||
|     if (s->fd >= 0 && |     if (s->fd >= 0 && | ||||||
|         (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) { |         (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { | ||||||
|         close(s->fd); |         close(s->fd); | ||||||
|         s->fd = -1; |         s->fd = -1; | ||||||
| #ifdef DEBUG_FLOPPY | #ifdef DEBUG_FLOPPY | ||||||
| @@ -962,7 +913,7 @@ static int fd_open(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|     if (s->fd < 0) { |     if (s->fd < 0) { | ||||||
|         if (s->fd_got_error && |         if (s->fd_got_error && | ||||||
|             (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) { |             (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) { | ||||||
| #ifdef DEBUG_FLOPPY | #ifdef DEBUG_FLOPPY | ||||||
|             printf("No floppy (open delayed)\n"); |             printf("No floppy (open delayed)\n"); | ||||||
| #endif | #endif | ||||||
| @@ -970,7 +921,7 @@ static int fd_open(BlockDriverState *bs) | |||||||
|         } |         } | ||||||
|         s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK); |         s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK); | ||||||
|         if (s->fd < 0) { |         if (s->fd < 0) { | ||||||
|             s->fd_error_time = get_clock(); |             s->fd_error_time = qemu_get_clock(rt_clock); | ||||||
|             s->fd_got_error = 1; |             s->fd_got_error = 1; | ||||||
|             if (last_media_present) |             if (last_media_present) | ||||||
|                 s->fd_media_changed = 1; |                 s->fd_media_changed = 1; | ||||||
| @@ -985,7 +936,7 @@ static int fd_open(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|     if (!last_media_present) |     if (!last_media_present) | ||||||
|         s->fd_media_changed = 1; |         s->fd_media_changed = 1; | ||||||
|     s->fd_open_time = get_clock(); |     s->fd_open_time = qemu_get_clock(rt_clock); | ||||||
|     s->fd_got_error = 0; |     s->fd_got_error = 0; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -1037,41 +988,35 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     /* Read out options */ |     /* Read out options */ | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
|         if (!strcmp(options->name, "size")) { |         if (!strcmp(options->name, "size")) { | ||||||
|             total_size = options->value.n / BDRV_SECTOR_SIZE; |             total_size = options->value.n / 512; | ||||||
|         } |         } | ||||||
|         options++; |         options++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fd = open(filename, O_WRONLY | O_BINARY); |     fd = open(filename, O_WRONLY | O_BINARY); | ||||||
|     if (fd < 0) |     if (fd < 0) | ||||||
|         return -errno; |         return -EIO; | ||||||
|  |  | ||||||
|     if (fstat(fd, &stat_buf) < 0) |     if (fstat(fd, &stat_buf) < 0) | ||||||
|         ret = -errno; |         ret = -EIO; | ||||||
|     else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) |     else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) | ||||||
|         ret = -ENODEV; |         ret = -EIO; | ||||||
|     else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE) |     else if (lseek(fd, 0, SEEK_END) < total_size * 512) | ||||||
|         ret = -ENOSPC; |         ret = -ENOSPC; | ||||||
|  |  | ||||||
|     close(fd); |     close(fd); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int hdev_has_zero_init(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_host_device = { | static BlockDriver bdrv_host_device = { | ||||||
|     .format_name        = "host_device", |     .format_name        = "host_device", | ||||||
|     .protocol_name        = "host_device", |  | ||||||
|     .instance_size      = sizeof(BDRVRawState), |     .instance_size      = sizeof(BDRVRawState), | ||||||
|     .bdrv_probe_device  = hdev_probe_device, |     .bdrv_probe_device  = hdev_probe_device, | ||||||
|     .bdrv_file_open     = hdev_open, |     .bdrv_open          = hdev_open, | ||||||
|     .bdrv_close         = raw_close, |     .bdrv_close         = raw_close, | ||||||
|     .bdrv_create        = hdev_create, |     .bdrv_create        = hdev_create, | ||||||
|     .create_options     = raw_create_options, |     .create_options     = raw_create_options, | ||||||
|     .bdrv_has_zero_init = hdev_has_zero_init, |     .no_zero_init       = 1, | ||||||
|     .bdrv_flush         = raw_flush, |     .bdrv_flush         = raw_flush, | ||||||
|  |  | ||||||
|     .bdrv_aio_readv	= raw_aio_readv, |     .bdrv_aio_readv	= raw_aio_readv, | ||||||
| @@ -1112,26 +1057,9 @@ static int floppy_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|  |  | ||||||
| static int floppy_probe_device(const char *filename) | static int floppy_probe_device(const char *filename) | ||||||
| { | { | ||||||
|     int fd, ret; |  | ||||||
|     int prio = 0; |  | ||||||
|     struct floppy_struct fdparam; |  | ||||||
|  |  | ||||||
|     if (strstart(filename, "/dev/fd", NULL)) |     if (strstart(filename, "/dev/fd", NULL)) | ||||||
|         prio = 50; |         return 100; | ||||||
|  |     return 0; | ||||||
|     fd = open(filename, O_RDONLY | O_NONBLOCK); |  | ||||||
|     if (fd < 0) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Attempt to detect via a floppy specific ioctl */ |  | ||||||
|     ret = ioctl(fd, FDGETPRM, &fdparam); |  | ||||||
|     if (ret >= 0) |  | ||||||
|         prio = 100; |  | ||||||
|  |  | ||||||
|     close(fd); |  | ||||||
| out: |  | ||||||
|     return prio; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1179,14 +1107,13 @@ static int floppy_eject(BlockDriverState *bs, int eject_flag) | |||||||
|  |  | ||||||
| static BlockDriver bdrv_host_floppy = { | static BlockDriver bdrv_host_floppy = { | ||||||
|     .format_name        = "host_floppy", |     .format_name        = "host_floppy", | ||||||
|     .protocol_name      = "host_floppy", |  | ||||||
|     .instance_size      = sizeof(BDRVRawState), |     .instance_size      = sizeof(BDRVRawState), | ||||||
|     .bdrv_probe_device	= floppy_probe_device, |     .bdrv_probe_device	= floppy_probe_device, | ||||||
|     .bdrv_file_open     = floppy_open, |     .bdrv_open          = floppy_open, | ||||||
|     .bdrv_close         = raw_close, |     .bdrv_close         = raw_close, | ||||||
|     .bdrv_create        = hdev_create, |     .bdrv_create        = hdev_create, | ||||||
|     .create_options     = raw_create_options, |     .create_options     = raw_create_options, | ||||||
|     .bdrv_has_zero_init = hdev_has_zero_init, |     .no_zero_init       = 1, | ||||||
|     .bdrv_flush         = raw_flush, |     .bdrv_flush         = raw_flush, | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = raw_aio_readv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
| @@ -1215,22 +1142,9 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|  |  | ||||||
| static int cdrom_probe_device(const char *filename) | static int cdrom_probe_device(const char *filename) | ||||||
| { | { | ||||||
|     int fd, ret; |     if (strstart(filename, "/dev/cd", NULL)) | ||||||
|     int prio = 0; |         return 100; | ||||||
|  |     return 0; | ||||||
|     fd = open(filename, O_RDONLY | O_NONBLOCK); |  | ||||||
|     if (fd < 0) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Attempt to detect via a CDROM specific ioctl */ |  | ||||||
|     ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); |  | ||||||
|     if (ret >= 0) |  | ||||||
|         prio = 100; |  | ||||||
|  |  | ||||||
|     close(fd); |  | ||||||
| out: |  | ||||||
|     return prio; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cdrom_is_inserted(BlockDriverState *bs) | static int cdrom_is_inserted(BlockDriverState *bs) | ||||||
| @@ -1276,14 +1190,13 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) | |||||||
|  |  | ||||||
| static BlockDriver bdrv_host_cdrom = { | static BlockDriver bdrv_host_cdrom = { | ||||||
|     .format_name        = "host_cdrom", |     .format_name        = "host_cdrom", | ||||||
|     .protocol_name      = "host_cdrom", |  | ||||||
|     .instance_size      = sizeof(BDRVRawState), |     .instance_size      = sizeof(BDRVRawState), | ||||||
|     .bdrv_probe_device	= cdrom_probe_device, |     .bdrv_probe_device	= cdrom_probe_device, | ||||||
|     .bdrv_file_open     = cdrom_open, |     .bdrv_open          = cdrom_open, | ||||||
|     .bdrv_close         = raw_close, |     .bdrv_close         = raw_close, | ||||||
|     .bdrv_create        = hdev_create, |     .bdrv_create        = hdev_create, | ||||||
|     .create_options     = raw_create_options, |     .create_options     = raw_create_options, | ||||||
|     .bdrv_has_zero_init = hdev_has_zero_init, |     .no_zero_init       = 1, | ||||||
|     .bdrv_flush         = raw_flush, |     .bdrv_flush         = raw_flush, | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = raw_aio_readv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
| @@ -1399,14 +1312,13 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) | |||||||
|  |  | ||||||
| static BlockDriver bdrv_host_cdrom = { | static BlockDriver bdrv_host_cdrom = { | ||||||
|     .format_name        = "host_cdrom", |     .format_name        = "host_cdrom", | ||||||
|     .protocol_name      = "host_cdrom", |  | ||||||
|     .instance_size      = sizeof(BDRVRawState), |     .instance_size      = sizeof(BDRVRawState), | ||||||
|     .bdrv_probe_device	= cdrom_probe_device, |     .bdrv_probe_device	= cdrom_probe_device, | ||||||
|     .bdrv_file_open     = cdrom_open, |     .bdrv_open          = cdrom_open, | ||||||
|     .bdrv_close         = raw_close, |     .bdrv_close         = raw_close, | ||||||
|     .bdrv_create        = hdev_create, |     .bdrv_create        = hdev_create, | ||||||
|     .create_options     = raw_create_options, |     .create_options     = raw_create_options, | ||||||
|     .bdrv_has_zero_init = hdev_has_zero_init, |     .no_zero_init       = 1, | ||||||
|     .bdrv_flush         = raw_flush, |     .bdrv_flush         = raw_flush, | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = raw_aio_readv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
| @@ -1424,13 +1336,13 @@ static BlockDriver bdrv_host_cdrom = { | |||||||
| }; | }; | ||||||
| #endif /* __FreeBSD__ */ | #endif /* __FreeBSD__ */ | ||||||
|  |  | ||||||
| static void bdrv_file_init(void) | static void bdrv_raw_init(void) | ||||||
| { | { | ||||||
|     /* |     /* | ||||||
|      * Register all the drivers.  Note that order is important, the driver |      * Register all the drivers.  Note that order is important, the driver | ||||||
|      * registered last will get probed first. |      * registered last will get probed first. | ||||||
|      */ |      */ | ||||||
|     bdrv_register(&bdrv_file); |     bdrv_register(&bdrv_raw); | ||||||
|     bdrv_register(&bdrv_host_device); |     bdrv_register(&bdrv_host_device); | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|     bdrv_register(&bdrv_host_floppy); |     bdrv_register(&bdrv_host_floppy); | ||||||
| @@ -1441,4 +1353,4 @@ static void bdrv_file_init(void) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| block_init(bdrv_file_init); | block_init(bdrv_raw_init); | ||||||
|   | |||||||
| @@ -76,17 +76,21 @@ static int set_sparse(int fd) | |||||||
| static int raw_open(BlockDriverState *bs, const char *filename, int flags) | static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     int access_flags; |     int access_flags, create_flags; | ||||||
|     DWORD overlapped; |     DWORD overlapped; | ||||||
|  |  | ||||||
|     s->type = FTYPE_FILE; |     s->type = FTYPE_FILE; | ||||||
|  |  | ||||||
|     if (flags & BDRV_O_RDWR) { |     if ((flags & BDRV_O_ACCESS) == O_RDWR) { | ||||||
|         access_flags = GENERIC_READ | GENERIC_WRITE; |         access_flags = GENERIC_READ | GENERIC_WRITE; | ||||||
|     } else { |     } else { | ||||||
|         access_flags = GENERIC_READ; |         access_flags = GENERIC_READ; | ||||||
|     } |     } | ||||||
|  |     if (flags & BDRV_O_CREAT) { | ||||||
|  |         create_flags = CREATE_ALWAYS; | ||||||
|  |     } else { | ||||||
|  |         create_flags = OPEN_EXISTING; | ||||||
|  |     } | ||||||
|     overlapped = FILE_ATTRIBUTE_NORMAL; |     overlapped = FILE_ATTRIBUTE_NORMAL; | ||||||
|     if ((flags & BDRV_O_NOCACHE)) |     if ((flags & BDRV_O_NOCACHE)) | ||||||
|         overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; |         overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; | ||||||
| @@ -94,7 +98,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|         overlapped |= FILE_FLAG_WRITE_THROUGH; |         overlapped |= FILE_FLAG_WRITE_THROUGH; | ||||||
|     s->hfile = CreateFile(filename, access_flags, |     s->hfile = CreateFile(filename, access_flags, | ||||||
|                           FILE_SHARE_READ, NULL, |                           FILE_SHARE_READ, NULL, | ||||||
|                           OPEN_EXISTING, overlapped, NULL); |                           create_flags, overlapped, NULL); | ||||||
|     if (s->hfile == INVALID_HANDLE_VALUE) { |     if (s->hfile == INVALID_HANDLE_VALUE) { | ||||||
|         int err = GetLastError(); |         int err = GetLastError(); | ||||||
|  |  | ||||||
| @@ -147,17 +151,10 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | |||||||
|     return ret_count; |     return ret_count; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int raw_flush(BlockDriverState *bs) | static void raw_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     int ret; |     FlushFileBuffers(s->hfile); | ||||||
|  |  | ||||||
|     ret = FlushFileBuffers(s->hfile); |  | ||||||
|     if (ret == 0) { |  | ||||||
|         return -EIO; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void raw_close(BlockDriverState *bs) | static void raw_close(BlockDriverState *bs) | ||||||
| @@ -245,11 +242,10 @@ static QEMUOptionParameter raw_create_options[] = { | |||||||
|     { NULL } |     { NULL } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_file = { | static BlockDriver bdrv_raw = { | ||||||
|     .format_name	= "file", |     .format_name	= "raw", | ||||||
|     .protocol_name	= "file", |  | ||||||
|     .instance_size	= sizeof(BDRVRawState), |     .instance_size	= sizeof(BDRVRawState), | ||||||
|     .bdrv_file_open	= raw_open, |     .bdrv_open		= raw_open, | ||||||
|     .bdrv_close		= raw_close, |     .bdrv_close		= raw_close, | ||||||
|     .bdrv_create	= raw_create, |     .bdrv_create	= raw_create, | ||||||
|     .bdrv_flush		= raw_flush, |     .bdrv_flush		= raw_flush, | ||||||
| @@ -341,7 +337,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | |||||||
|     } |     } | ||||||
|     s->type = find_device_type(bs, filename); |     s->type = find_device_type(bs, filename); | ||||||
|  |  | ||||||
|     if (flags & BDRV_O_RDWR) { |     if ((flags & BDRV_O_ACCESS) == O_RDWR) { | ||||||
|         access_flags = GENERIC_READ | GENERIC_WRITE; |         access_flags = GENERIC_READ | GENERIC_WRITE; | ||||||
|     } else { |     } else { | ||||||
|         access_flags = GENERIC_READ; |         access_flags = GENERIC_READ; | ||||||
| @@ -401,30 +397,23 @@ static int raw_set_locked(BlockDriverState *bs, int locked) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int hdev_has_zero_init(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_host_device = { | static BlockDriver bdrv_host_device = { | ||||||
|     .format_name	= "host_device", |     .format_name	= "host_device", | ||||||
|     .protocol_name	= "host_device", |  | ||||||
|     .instance_size	= sizeof(BDRVRawState), |     .instance_size	= sizeof(BDRVRawState), | ||||||
|     .bdrv_probe_device	= hdev_probe_device, |     .bdrv_probe_device	= hdev_probe_device, | ||||||
|     .bdrv_file_open	= hdev_open, |     .bdrv_open		= hdev_open, | ||||||
|     .bdrv_close		= raw_close, |     .bdrv_close		= raw_close, | ||||||
|     .bdrv_flush		= raw_flush, |     .bdrv_flush		= raw_flush, | ||||||
|     .bdrv_has_zero_init = hdev_has_zero_init, |  | ||||||
|  |  | ||||||
|     .bdrv_read		= raw_read, |     .bdrv_read		= raw_read, | ||||||
|     .bdrv_write	        = raw_write, |     .bdrv_write	        = raw_write, | ||||||
|     .bdrv_getlength	= raw_getlength, |     .bdrv_getlength	= raw_getlength, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void bdrv_file_init(void) | static void bdrv_raw_init(void) | ||||||
| { | { | ||||||
|     bdrv_register(&bdrv_file); |     bdrv_register(&bdrv_raw); | ||||||
|     bdrv_register(&bdrv_host_device); |     bdrv_register(&bdrv_host_device); | ||||||
| } | } | ||||||
|  |  | ||||||
| block_init(bdrv_file_init); | block_init(bdrv_raw_init); | ||||||
|   | |||||||
							
								
								
									
										156
									
								
								block/raw.c
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								block/raw.c
									
									
									
									
									
								
							| @@ -1,156 +0,0 @@ | |||||||
|  |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "block_int.h" |  | ||||||
| #include "module.h" |  | ||||||
|  |  | ||||||
| static int raw_open(BlockDriverState *bs, int flags) |  | ||||||
| { |  | ||||||
|     bs->sg = bs->file->sg; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_read(BlockDriverState *bs, int64_t sector_num, |  | ||||||
|                     uint8_t *buf, int nb_sectors) |  | ||||||
| { |  | ||||||
|     return bdrv_read(bs->file, sector_num, buf, nb_sectors); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_write(BlockDriverState *bs, int64_t sector_num, |  | ||||||
|                      const uint8_t *buf, int nb_sectors) |  | ||||||
| { |  | ||||||
|     return bdrv_write(bs->file, sector_num, buf, nb_sectors); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, |  | ||||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, |  | ||||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void raw_close(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_flush(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_flush(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs, |  | ||||||
|     BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|     return bdrv_aio_flush(bs->file, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int64_t raw_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_getlength(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset) |  | ||||||
| { |  | ||||||
|     return bdrv_truncate(bs->file, offset); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) |  | ||||||
| { |  | ||||||
|    return 1; /* everything can be opened as raw image */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) |  | ||||||
| { |  | ||||||
|     return bdrv_discard(bs->file, sector_num, nb_sectors); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_is_inserted(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_is_inserted(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_eject(BlockDriverState *bs, int eject_flag) |  | ||||||
| { |  | ||||||
|     return bdrv_eject(bs->file, eject_flag); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_set_locked(BlockDriverState *bs, int locked) |  | ||||||
| { |  | ||||||
|     bdrv_set_locked(bs->file, locked); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) |  | ||||||
| { |  | ||||||
|    return bdrv_ioctl(bs->file, req, buf); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, |  | ||||||
|         unsigned long int req, void *buf, |  | ||||||
|         BlockDriverCompletionFunc *cb, void *opaque) |  | ||||||
| { |  | ||||||
|    return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int raw_create(const char *filename, QEMUOptionParameter *options) |  | ||||||
| { |  | ||||||
|     return bdrv_create_file(filename, options); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static QEMUOptionParameter raw_create_options[] = { |  | ||||||
|     { |  | ||||||
|         .name = BLOCK_OPT_SIZE, |  | ||||||
|         .type = OPT_SIZE, |  | ||||||
|         .help = "Virtual disk size" |  | ||||||
|     }, |  | ||||||
|     { NULL } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int raw_has_zero_init(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_has_zero_init(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_raw = { |  | ||||||
|     .format_name        = "raw", |  | ||||||
|  |  | ||||||
|     /* It's really 0, but we need to make qemu_malloc() happy */ |  | ||||||
|     .instance_size      = 1, |  | ||||||
|  |  | ||||||
|     .bdrv_open          = raw_open, |  | ||||||
|     .bdrv_close         = raw_close, |  | ||||||
|     .bdrv_read          = raw_read, |  | ||||||
|     .bdrv_write         = raw_write, |  | ||||||
|     .bdrv_flush         = raw_flush, |  | ||||||
|     .bdrv_probe         = raw_probe, |  | ||||||
|     .bdrv_getlength     = raw_getlength, |  | ||||||
|     .bdrv_truncate      = raw_truncate, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_readv     = raw_aio_readv, |  | ||||||
|     .bdrv_aio_writev    = raw_aio_writev, |  | ||||||
|     .bdrv_aio_flush     = raw_aio_flush, |  | ||||||
|     .bdrv_discard       = raw_discard, |  | ||||||
|  |  | ||||||
|     .bdrv_is_inserted   = raw_is_inserted, |  | ||||||
|     .bdrv_eject         = raw_eject, |  | ||||||
|     .bdrv_set_locked    = raw_set_locked, |  | ||||||
|     .bdrv_ioctl         = raw_ioctl, |  | ||||||
|     .bdrv_aio_ioctl     = raw_aio_ioctl, |  | ||||||
|  |  | ||||||
|     .bdrv_create        = raw_create, |  | ||||||
|     .create_options     = raw_create_options, |  | ||||||
|     .bdrv_has_zero_init = raw_has_zero_init, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void bdrv_raw_init(void) |  | ||||||
| { |  | ||||||
|     bdrv_register(&bdrv_raw); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| block_init(bdrv_raw_init); |  | ||||||
							
								
								
									
										1059
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										1059
									
								
								block/rbd.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,71 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Ceph - scalable distributed file system |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2004-2010 Sage Weil <sage@newdream.net> |  | ||||||
|  * |  | ||||||
|  * This is free software; you can redistribute it and/or |  | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  | ||||||
|  * License version 2.1, as published by the Free Software |  | ||||||
|  * Foundation.  See file COPYING.LIB. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef CEPH_RBD_TYPES_H |  | ||||||
| #define CEPH_RBD_TYPES_H |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * rbd image 'foo' consists of objects |  | ||||||
|  *   foo.rbd      - image metadata |  | ||||||
|  *   foo.00000000 |  | ||||||
|  *   foo.00000001 |  | ||||||
|  *   ...          - data |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #define RBD_SUFFIX              ".rbd" |  | ||||||
| #define RBD_DIRECTORY           "rbd_directory" |  | ||||||
| #define RBD_INFO                "rbd_info" |  | ||||||
|  |  | ||||||
| #define RBD_DEFAULT_OBJ_ORDER   22   /* 4MB */ |  | ||||||
|  |  | ||||||
| #define RBD_MAX_OBJ_NAME_SIZE   96 |  | ||||||
| #define RBD_MAX_BLOCK_NAME_SIZE 24 |  | ||||||
| #define RBD_MAX_SEG_NAME_SIZE   128 |  | ||||||
|  |  | ||||||
| #define RBD_COMP_NONE           0 |  | ||||||
| #define RBD_CRYPT_NONE          0 |  | ||||||
|  |  | ||||||
| #define RBD_HEADER_TEXT         "<<< Rados Block Device Image >>>\n" |  | ||||||
| #define RBD_HEADER_SIGNATURE    "RBD" |  | ||||||
| #define RBD_HEADER_VERSION      "001.005" |  | ||||||
|  |  | ||||||
| struct rbd_info { |  | ||||||
|     uint64_t max_id; |  | ||||||
| } __attribute__ ((packed)); |  | ||||||
|  |  | ||||||
| struct rbd_obj_snap_ondisk { |  | ||||||
|     uint64_t id; |  | ||||||
|     uint64_t image_size; |  | ||||||
| } __attribute__((packed)); |  | ||||||
|  |  | ||||||
| struct rbd_obj_header_ondisk { |  | ||||||
|     char text[40]; |  | ||||||
|     char block_name[RBD_MAX_BLOCK_NAME_SIZE]; |  | ||||||
|     char signature[4]; |  | ||||||
|     char version[8]; |  | ||||||
|     struct { |  | ||||||
|         uint8_t order; |  | ||||||
|         uint8_t crypt_type; |  | ||||||
|         uint8_t comp_type; |  | ||||||
|         uint8_t unused; |  | ||||||
|     } __attribute__((packed)) options; |  | ||||||
|     uint64_t image_size; |  | ||||||
|     uint64_t snap_seq; |  | ||||||
|     uint32_t snap_count; |  | ||||||
|     uint32_t reserved; |  | ||||||
|     uint64_t snap_names_len; |  | ||||||
|     struct rbd_obj_snap_ondisk snaps[0]; |  | ||||||
| } __attribute__((packed)); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
							
								
								
									
										2037
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										2037
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										57
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								block/vdi.c
									
									
									
									
									
								
							| @@ -119,13 +119,13 @@ void uuid_unparse(const uuid_t uu, char *out); | |||||||
| #if !defined(CONFIG_UUID) | #if !defined(CONFIG_UUID) | ||||||
| void uuid_generate(uuid_t out) | void uuid_generate(uuid_t out) | ||||||
| { | { | ||||||
|     memset(out, 0, sizeof(uuid_t)); |     memset(out, 0, sizeof(out)); | ||||||
| } | } | ||||||
|  |  | ||||||
| int uuid_is_null(const uuid_t uu) | int uuid_is_null(const uuid_t uu) | ||||||
| { | { | ||||||
|     uuid_t null_uuid = { 0 }; |     uuid_t null_uuid = { 0 }; | ||||||
|     return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0; |     return memcmp(uu, null_uuid, sizeof(uu)) == 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void uuid_unparse(const uuid_t uu, char *out) | void uuid_unparse(const uuid_t uu, char *out) | ||||||
| @@ -186,6 +186,7 @@ typedef struct { | |||||||
| } VdiHeader; | } VdiHeader; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|  |     BlockDriverState *hd; | ||||||
|     /* The block map entries are little endian (even in memory). */ |     /* The block map entries are little endian (even in memory). */ | ||||||
|     uint32_t *bmap; |     uint32_t *bmap; | ||||||
|     /* Size of block (bytes). */ |     /* Size of block (bytes). */ | ||||||
| @@ -290,10 +291,11 @@ static void vdi_header_print(VdiHeader *header) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | static int vdi_check(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     /* TODO: additional checks possible. */ |     /* TODO: additional checks possible. */ | ||||||
|     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; |     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; | ||||||
|  |     int n_errors = 0; | ||||||
|     uint32_t blocks_allocated = 0; |     uint32_t blocks_allocated = 0; | ||||||
|     uint32_t block; |     uint32_t block; | ||||||
|     uint32_t *bmap; |     uint32_t *bmap; | ||||||
| @@ -313,12 +315,11 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | |||||||
|                 } else { |                 } else { | ||||||
|                     fprintf(stderr, "ERROR: block index %" PRIu32 |                     fprintf(stderr, "ERROR: block index %" PRIu32 | ||||||
|                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry); |                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry); | ||||||
|                     res->corruptions++; |  | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 fprintf(stderr, "ERROR: block index %" PRIu32 |                 fprintf(stderr, "ERROR: block index %" PRIu32 | ||||||
|                         " too large, is %" PRIu32 "\n", block, bmap_entry); |                         " too large, is %" PRIu32 "\n", block, bmap_entry); | ||||||
|                 res->corruptions++; |                 n_errors++; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -326,12 +327,12 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | |||||||
|         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32 |         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32 | ||||||
|                ", should be %" PRIu32 "\n", |                ", should be %" PRIu32 "\n", | ||||||
|                blocks_allocated, s->header.blocks_allocated); |                blocks_allocated, s->header.blocks_allocated); | ||||||
|         res->corruptions++; |         n_errors++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qemu_free(bmap); |     qemu_free(bmap); | ||||||
|  |  | ||||||
|     return 0; |     return n_errors; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||||
| @@ -375,15 +376,21 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vdi_open(BlockDriverState *bs, int flags) | static int vdi_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVVdiState *s = bs->opaque; |     BDRVVdiState *s = bs->opaque; | ||||||
|     VdiHeader header; |     VdiHeader header; | ||||||
|     size_t bmap_size; |     size_t bmap_size; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|     logout("\n"); |     logout("\n"); | ||||||
|  |  | ||||||
|     if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) { |     ret = bdrv_file_open(&s->hd, filename, flags); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (bdrv_read(s->hd, 0, (uint8_t *)&header, 1) < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -440,10 +447,8 @@ static int vdi_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     bmap_size = header.blocks_in_image * sizeof(uint32_t); |     bmap_size = header.blocks_in_image * sizeof(uint32_t); | ||||||
|     bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; |     bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; | ||||||
|     if (bmap_size > 0) { |     s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); | ||||||
|         s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); |     if (bdrv_read(s->hd, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { | ||||||
|     } |  | ||||||
|     if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { |  | ||||||
|         goto fail_free_bmap; |         goto fail_free_bmap; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -453,6 +458,7 @@ static int vdi_open(BlockDriverState *bs, int flags) | |||||||
|     qemu_free(s->bmap); |     qemu_free(s->bmap); | ||||||
|  |  | ||||||
|  fail: |  fail: | ||||||
|  |     bdrv_delete(s->hd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -476,7 +482,7 @@ static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num, | |||||||
| static void vdi_aio_cancel(BlockDriverAIOCB *blockacb) | static void vdi_aio_cancel(BlockDriverAIOCB *blockacb) | ||||||
| { | { | ||||||
|     /* TODO: This code is untested. How can I get it executed? */ |     /* TODO: This code is untested. How can I get it executed? */ | ||||||
|     VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common); |     VdiAIOCB *acb = (VdiAIOCB *)blockacb; | ||||||
|     logout("\n"); |     logout("\n"); | ||||||
|     if (acb->hd_aiocb) { |     if (acb->hd_aiocb) { | ||||||
|         bdrv_aio_cancel(acb->hd_aiocb); |         bdrv_aio_cancel(acb->hd_aiocb); | ||||||
| @@ -607,7 +613,7 @@ static void vdi_aio_read_cb(void *opaque, int ret) | |||||||
|         acb->hd_iov.iov_base = (void *)acb->buf; |         acb->hd_iov.iov_base = (void *)acb->buf; | ||||||
|         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; |         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; | ||||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|         acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov, |         acb->hd_aiocb = bdrv_aio_readv(s->hd, offset, &acb->hd_qiov, | ||||||
|                                        n_sectors, vdi_aio_read_cb, acb); |                                        n_sectors, vdi_aio_read_cb, acb); | ||||||
|         if (acb->hd_aiocb == NULL) { |         if (acb->hd_aiocb == NULL) { | ||||||
|             goto done; |             goto done; | ||||||
| @@ -670,7 +676,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | |||||||
|             acb->hd_iov.iov_base = acb->block_buffer; |             acb->hd_iov.iov_base = acb->block_buffer; | ||||||
|             acb->hd_iov.iov_len = SECTOR_SIZE; |             acb->hd_iov.iov_len = SECTOR_SIZE; | ||||||
|             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|             acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1, |             acb->hd_aiocb = bdrv_aio_writev(s->hd, 0, &acb->hd_qiov, 1, | ||||||
|                                             vdi_aio_write_cb, acb); |                                             vdi_aio_write_cb, acb); | ||||||
|             if (acb->hd_aiocb == NULL) { |             if (acb->hd_aiocb == NULL) { | ||||||
|                 goto done; |                 goto done; | ||||||
| @@ -699,7 +705,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | |||||||
|             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|             logout("will write %u block map sectors starting from entry %u\n", |             logout("will write %u block map sectors starting from entry %u\n", | ||||||
|                    n_sectors, bmap_first); |                    n_sectors, bmap_first); | ||||||
|             acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, |             acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, | ||||||
|                                             n_sectors, vdi_aio_write_cb, acb); |                                             n_sectors, vdi_aio_write_cb, acb); | ||||||
|             if (acb->hd_aiocb == NULL) { |             if (acb->hd_aiocb == NULL) { | ||||||
|                 goto done; |                 goto done; | ||||||
| @@ -748,7 +754,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | |||||||
|         acb->hd_iov.iov_base = (void *)block; |         acb->hd_iov.iov_base = (void *)block; | ||||||
|         acb->hd_iov.iov_len = s->block_size; |         acb->hd_iov.iov_len = s->block_size; | ||||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|         acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, |         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, | ||||||
|                                         &acb->hd_qiov, s->block_sectors, |                                         &acb->hd_qiov, s->block_sectors, | ||||||
|                                         vdi_aio_write_cb, acb); |                                         vdi_aio_write_cb, acb); | ||||||
|         if (acb->hd_aiocb == NULL) { |         if (acb->hd_aiocb == NULL) { | ||||||
| @@ -761,7 +767,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | |||||||
|         acb->hd_iov.iov_base = (void *)acb->buf; |         acb->hd_iov.iov_base = (void *)acb->buf; | ||||||
|         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; |         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; | ||||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); |         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||||
|         acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, |         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, | ||||||
|                                         n_sectors, vdi_aio_write_cb, acb); |                                         n_sectors, vdi_aio_write_cb, acb); | ||||||
|         if (acb->hd_aiocb == NULL) { |         if (acb->hd_aiocb == NULL) { | ||||||
|             goto done; |             goto done; | ||||||
| @@ -867,10 +873,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options) | |||||||
|         result = -errno; |         result = -errno; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bmap = NULL; |     bmap = (uint32_t *)qemu_mallocz(bmap_size); | ||||||
|     if (bmap_size > 0) { |  | ||||||
|         bmap = (uint32_t *)qemu_mallocz(bmap_size); |  | ||||||
|     } |  | ||||||
|     for (i = 0; i < blocks; i++) { |     for (i = 0; i < blocks; i++) { | ||||||
|         if (image_type == VDI_TYPE_STATIC) { |         if (image_type == VDI_TYPE_STATIC) { | ||||||
|             bmap[i] = i; |             bmap[i] = i; | ||||||
| @@ -897,12 +900,16 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options) | |||||||
|  |  | ||||||
| static void vdi_close(BlockDriverState *bs) | static void vdi_close(BlockDriverState *bs) | ||||||
| { | { | ||||||
|  |     BDRVVdiState *s = bs->opaque; | ||||||
|  |     logout("\n"); | ||||||
|  |     bdrv_delete(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vdi_flush(BlockDriverState *bs) | static void vdi_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|  |     BDRVVdiState *s = bs->opaque; | ||||||
|     logout("\n"); |     logout("\n"); | ||||||
|     return bdrv_flush(bs->file); |     bdrv_flush(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										251
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								block/vmdk.c
									
									
									
									
									
								
							| @@ -61,6 +61,7 @@ typedef struct { | |||||||
| #define L2_CACHE_SIZE 16 | #define L2_CACHE_SIZE 16 | ||||||
|  |  | ||||||
| typedef struct BDRVVmdkState { | typedef struct BDRVVmdkState { | ||||||
|  |     BlockDriverState *hd; | ||||||
|     int64_t l1_table_offset; |     int64_t l1_table_offset; | ||||||
|     int64_t l1_backup_table_offset; |     int64_t l1_backup_table_offset; | ||||||
|     uint32_t *l1_table; |     uint32_t *l1_table; | ||||||
| @@ -75,6 +76,7 @@ typedef struct BDRVVmdkState { | |||||||
|  |  | ||||||
|     unsigned int cluster_sectors; |     unsigned int cluster_sectors; | ||||||
|     uint32_t parent_cid; |     uint32_t parent_cid; | ||||||
|  |     int is_parent; | ||||||
| } BDRVVmdkState; | } BDRVVmdkState; | ||||||
|  |  | ||||||
| typedef struct VmdkMetaData { | typedef struct VmdkMetaData { | ||||||
| @@ -107,13 +109,14 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|  |  | ||||||
| static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | ||||||
| { | { | ||||||
|  |     BDRVVmdkState *s = bs->opaque; | ||||||
|     char desc[DESC_SIZE]; |     char desc[DESC_SIZE]; | ||||||
|     uint32_t cid; |     uint32_t cid; | ||||||
|     const char *p_name, *cid_str; |     const char *p_name, *cid_str; | ||||||
|     size_t cid_str_size; |     size_t cid_str_size; | ||||||
|  |  | ||||||
|     /* the descriptor offset = 0x200 */ |     /* the descriptor offset = 0x200 */ | ||||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) |     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|     if (parent) { |     if (parent) { | ||||||
| @@ -134,11 +137,12 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | |||||||
|  |  | ||||||
| static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) | static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) | ||||||
| { | { | ||||||
|  |     BDRVVmdkState *s = bs->opaque; | ||||||
|     char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; |     char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; | ||||||
|     char *p_name, *tmp_str; |     char *p_name, *tmp_str; | ||||||
|  |  | ||||||
|     /* the descriptor offset = 0x200 */ |     /* the descriptor offset = 0x200 */ | ||||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) |     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     tmp_str = strstr(desc,"parentCID"); |     tmp_str = strstr(desc,"parentCID"); | ||||||
| @@ -149,7 +153,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) | |||||||
|         pstrcat(desc, sizeof(desc), tmp_desc); |         pstrcat(desc, sizeof(desc), tmp_desc); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) |     if (bdrv_pwrite_sync(s->hd, 0x200, desc, DESC_SIZE) < 0) | ||||||
|         return -1; |         return -1; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -175,7 +179,6 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) | |||||||
| static int vmdk_snapshot_create(const char *filename, const char *backing_file) | static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||||
| { | { | ||||||
|     int snp_fd, p_fd; |     int snp_fd, p_fd; | ||||||
|     int ret; |  | ||||||
|     uint32_t p_cid; |     uint32_t p_cid; | ||||||
|     char *p_name, *gd_buf, *rgd_buf; |     char *p_name, *gd_buf, *rgd_buf; | ||||||
|     const char *real_filename, *temp_str; |     const char *real_filename, *temp_str; | ||||||
| @@ -200,49 +203,34 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | |||||||
|  |  | ||||||
|     snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); |     snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); | ||||||
|     if (snp_fd < 0) |     if (snp_fd < 0) | ||||||
|         return -errno; |         return -1; | ||||||
|     p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); |     p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||||
|     if (p_fd < 0) { |     if (p_fd < 0) { | ||||||
|         close(snp_fd); |         close(snp_fd); | ||||||
|         return -errno; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* read the header */ |     /* read the header */ | ||||||
|     if (lseek(p_fd, 0x0, SEEK_SET) == -1) { |     if (lseek(p_fd, 0x0, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) | ||||||
|     if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* write the header */ |     /* write the header */ | ||||||
|     if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { |     if (lseek(snp_fd, 0x0, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     if (write(snp_fd, hdr, HEADER_SIZE) == -1) | ||||||
|     if (write(snp_fd, hdr, HEADER_SIZE) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     memset(&header, 0, sizeof(header)); |     memset(&header, 0, sizeof(header)); | ||||||
|     memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC |     memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC | ||||||
|  |  | ||||||
|     if (ftruncate(snp_fd, header.grain_offset << 9)) { |     ftruncate(snp_fd, header.grain_offset << 9); | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|     /* the descriptor offset = 0x200 */ |     /* the descriptor offset = 0x200 */ | ||||||
|     if (lseek(p_fd, 0x200, SEEK_SET) == -1) { |     if (lseek(p_fd, 0x200, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) | ||||||
|     if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((p_name = strstr(p_desc,"CID")) != NULL) { |     if ((p_name = strstr(p_desc,"CID")) != NULL) { | ||||||
|         p_name += sizeof("CID"); |         p_name += sizeof("CID"); | ||||||
| @@ -261,14 +249,10 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | |||||||
|              (uint32_t)header.capacity, real_filename); |              (uint32_t)header.capacity, real_filename); | ||||||
|  |  | ||||||
|     /* write the descriptor */ |     /* write the descriptor */ | ||||||
|     if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { |     if (lseek(snp_fd, 0x200, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     if (write(snp_fd, s_desc, strlen(s_desc)) == -1) | ||||||
|     if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table |     gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table | ||||||
|     rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table |     rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table | ||||||
| @@ -278,73 +262,70 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | |||||||
|      * 512 GTE per GT, each GTE points to grain |      * 512 GTE per GT, each GTE points to grain | ||||||
|      */ |      */ | ||||||
|     gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; |     gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; | ||||||
|     if (!gt_size) { |     if (!gt_size) | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |  | ||||||
|     gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde |     gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde | ||||||
|     gd_size = gde_entries * sizeof(uint32_t); |     gd_size = gde_entries * sizeof(uint32_t); | ||||||
|  |  | ||||||
|     /* write RGD */ |     /* write RGD */ | ||||||
|     rgd_buf = qemu_malloc(gd_size); |     rgd_buf = qemu_malloc(gd_size); | ||||||
|     if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { |     if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_rgd; |         goto fail_rgd; | ||||||
|     } |     if (read(p_fd, rgd_buf, gd_size) != gd_size) | ||||||
|     if (read(p_fd, rgd_buf, gd_size) != gd_size) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_rgd; |         goto fail_rgd; | ||||||
|     } |     if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) | ||||||
|     if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_rgd; |         goto fail_rgd; | ||||||
|     } |     if (write(snp_fd, rgd_buf, gd_size) == -1) | ||||||
|     if (write(snp_fd, rgd_buf, gd_size) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_rgd; |         goto fail_rgd; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* write GD */ |     /* write GD */ | ||||||
|     gd_buf = qemu_malloc(gd_size); |     gd_buf = qemu_malloc(gd_size); | ||||||
|     if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { |     if (lseek(p_fd, gd_offset, SEEK_SET) == -1) | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_gd; |         goto fail_gd; | ||||||
|     } |     if (read(p_fd, gd_buf, gd_size) != gd_size) | ||||||
|     if (read(p_fd, gd_buf, gd_size) != gd_size) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_gd; |         goto fail_gd; | ||||||
|     } |     if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) | ||||||
|     if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_gd; |         goto fail_gd; | ||||||
|     } |     if (write(snp_fd, gd_buf, gd_size) == -1) | ||||||
|     if (write(snp_fd, gd_buf, gd_size) == -1) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto fail_gd; |         goto fail_gd; | ||||||
|     } |  | ||||||
|     ret = 0; |  | ||||||
|  |  | ||||||
| fail_gd: |  | ||||||
|     qemu_free(gd_buf); |     qemu_free(gd_buf); | ||||||
| fail_rgd: |  | ||||||
|     qemu_free(rgd_buf); |     qemu_free(rgd_buf); | ||||||
| fail: |  | ||||||
|     close(p_fd); |     close(p_fd); | ||||||
|     close(snp_fd); |     close(snp_fd); | ||||||
|     return ret; |     return 0; | ||||||
|  |  | ||||||
|  |     fail_gd: | ||||||
|  |     qemu_free(gd_buf); | ||||||
|  |     fail_rgd: | ||||||
|  |     qemu_free(rgd_buf); | ||||||
|  |     fail: | ||||||
|  |     close(p_fd); | ||||||
|  |     close(snp_fd); | ||||||
|  |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vmdk_parent_open(BlockDriverState *bs) | static void vmdk_parent_close(BlockDriverState *bs) | ||||||
| { | { | ||||||
|  |     if (bs->backing_hd) | ||||||
|  |         bdrv_close(bs->backing_hd); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int parent_open = 0; | ||||||
|  | static int vmdk_parent_open(BlockDriverState *bs, const char * filename) | ||||||
|  | { | ||||||
|  |     BDRVVmdkState *s = bs->opaque; | ||||||
|     char *p_name; |     char *p_name; | ||||||
|     char desc[DESC_SIZE]; |     char desc[DESC_SIZE]; | ||||||
|  |     char parent_img_name[1024]; | ||||||
|  |  | ||||||
|     /* the descriptor offset = 0x200 */ |     /* the descriptor offset = 0x200 */ | ||||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) |     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { |     if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { | ||||||
|         char *end_name; |         char *end_name; | ||||||
|  |         struct stat file_buf; | ||||||
|  |  | ||||||
|         p_name += sizeof("parentFileNameHint") + 1; |         p_name += sizeof("parentFileNameHint") + 1; | ||||||
|         if ((end_name = strchr(p_name,'\"')) == NULL) |         if ((end_name = strchr(p_name,'\"')) == NULL) | ||||||
| @@ -353,25 +334,50 @@ static int vmdk_parent_open(BlockDriverState *bs) | |||||||
|             return -1; |             return -1; | ||||||
|  |  | ||||||
|         pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); |         pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); | ||||||
|  |         if (stat(bs->backing_file, &file_buf) != 0) { | ||||||
|  |             path_combine(parent_img_name, sizeof(parent_img_name), | ||||||
|  |                          filename, bs->backing_file); | ||||||
|  |         } else { | ||||||
|  |             pstrcpy(parent_img_name, sizeof(parent_img_name), | ||||||
|  |                     bs->backing_file); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bs->backing_hd = bdrv_new(""); | ||||||
|  |         if (!bs->backing_hd) { | ||||||
|  |             failure: | ||||||
|  |             bdrv_close(s->hd); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |         parent_open = 1; | ||||||
|  |         if (bdrv_open(bs->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0) | ||||||
|  |             goto failure; | ||||||
|  |         parent_open = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vmdk_open(BlockDriverState *bs, int flags) | static int vmdk_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVVmdkState *s = bs->opaque; |     BDRVVmdkState *s = bs->opaque; | ||||||
|     uint32_t magic; |     uint32_t magic; | ||||||
|     int l1_size, i; |     int l1_size, i, ret; | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) |     if (parent_open) | ||||||
|  |         // Parent must be opened as RO. | ||||||
|  |         flags = BDRV_O_RDONLY; | ||||||
|  |  | ||||||
|  |     ret = bdrv_file_open(&s->hd, filename, flags); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  |     if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic)) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     magic = be32_to_cpu(magic); |     magic = be32_to_cpu(magic); | ||||||
|     if (magic == VMDK3_MAGIC) { |     if (magic == VMDK3_MAGIC) { | ||||||
|         VMDK3Header header; |         VMDK3Header header; | ||||||
|  |  | ||||||
|         if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) |         if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||||
|             goto fail; |             goto fail; | ||||||
|         s->cluster_sectors = le32_to_cpu(header.granularity); |         s->cluster_sectors = le32_to_cpu(header.granularity); | ||||||
|         s->l2_size = 1 << 9; |         s->l2_size = 1 << 9; | ||||||
| @@ -383,7 +389,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | |||||||
|     } else if (magic == VMDK4_MAGIC) { |     } else if (magic == VMDK4_MAGIC) { | ||||||
|         VMDK4Header header; |         VMDK4Header header; | ||||||
|  |  | ||||||
|         if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) |         if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||||
|             goto fail; |             goto fail; | ||||||
|         bs->total_sectors = le64_to_cpu(header.capacity); |         bs->total_sectors = le64_to_cpu(header.capacity); | ||||||
|         s->cluster_sectors = le64_to_cpu(header.granularity); |         s->cluster_sectors = le64_to_cpu(header.granularity); | ||||||
| @@ -396,8 +402,13 @@ static int vmdk_open(BlockDriverState *bs, int flags) | |||||||
|         s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; |         s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; | ||||||
|         s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; |         s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; | ||||||
|  |  | ||||||
|  |         if (parent_open) | ||||||
|  |             s->is_parent = 1; | ||||||
|  |         else | ||||||
|  |             s->is_parent = 0; | ||||||
|  |  | ||||||
|         // try to open parent images, if exist |         // try to open parent images, if exist | ||||||
|         if (vmdk_parent_open(bs) != 0) |         if (vmdk_parent_open(bs, filename) != 0) | ||||||
|             goto fail; |             goto fail; | ||||||
|         // write the CID once after the image creation |         // write the CID once after the image creation | ||||||
|         s->parent_cid = vmdk_read_cid(bs,1); |         s->parent_cid = vmdk_read_cid(bs,1); | ||||||
| @@ -408,7 +419,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | |||||||
|     /* read the L1 table */ |     /* read the L1 table */ | ||||||
|     l1_size = s->l1_size * sizeof(uint32_t); |     l1_size = s->l1_size * sizeof(uint32_t); | ||||||
|     s->l1_table = qemu_malloc(l1_size); |     s->l1_table = qemu_malloc(l1_size); | ||||||
|     if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size) |     if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size) | ||||||
|         goto fail; |         goto fail; | ||||||
|     for(i = 0; i < s->l1_size; i++) { |     for(i = 0; i < s->l1_size; i++) { | ||||||
|         le32_to_cpus(&s->l1_table[i]); |         le32_to_cpus(&s->l1_table[i]); | ||||||
| @@ -416,7 +427,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     if (s->l1_backup_table_offset) { |     if (s->l1_backup_table_offset) { | ||||||
|         s->l1_backup_table = qemu_malloc(l1_size); |         s->l1_backup_table = qemu_malloc(l1_size); | ||||||
|         if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) |         if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) | ||||||
|             goto fail; |             goto fail; | ||||||
|         for(i = 0; i < s->l1_size; i++) { |         for(i = 0; i < s->l1_size; i++) { | ||||||
|             le32_to_cpus(&s->l1_backup_table[i]); |             le32_to_cpus(&s->l1_backup_table[i]); | ||||||
| @@ -429,6 +440,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | |||||||
|     qemu_free(s->l1_backup_table); |     qemu_free(s->l1_backup_table); | ||||||
|     qemu_free(s->l1_table); |     qemu_free(s->l1_table); | ||||||
|     qemu_free(s->l2_cache); |     qemu_free(s->l2_cache); | ||||||
|  |     bdrv_delete(s->hd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -456,7 +468,7 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         //Write grain only into the active image |         //Write grain only into the active image | ||||||
|         ret = bdrv_write(bs->file, cluster_offset, whole_grain, |         ret = bdrv_write(s->hd, cluster_offset, whole_grain, | ||||||
|             s->cluster_sectors); |             s->cluster_sectors); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             return -1; |             return -1; | ||||||
| @@ -470,13 +482,13 @@ static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data) | |||||||
|     BDRVVmdkState *s = bs->opaque; |     BDRVVmdkState *s = bs->opaque; | ||||||
|  |  | ||||||
|     /* update L2 table */ |     /* update L2 table */ | ||||||
|     if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), |     if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||||
|                     &(m_data->offset), sizeof(m_data->offset)) < 0) |                     &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||||
|         return -1; |         return -1; | ||||||
|     /* update backup L2 table */ |     /* update backup L2 table */ | ||||||
|     if (s->l1_backup_table_offset != 0) { |     if (s->l1_backup_table_offset != 0) { | ||||||
|         m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; |         m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; | ||||||
|         if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), |         if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||||
|                         &(m_data->offset), sizeof(m_data->offset)) < 0) |                         &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||||
|             return -1; |             return -1; | ||||||
|     } |     } | ||||||
| @@ -524,7 +536,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     l2_table = s->l2_cache + (min_index * s->l2_size); |     l2_table = s->l2_cache + (min_index * s->l2_size); | ||||||
|     if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != |     if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != | ||||||
|                                                                         s->l2_size * sizeof(uint32_t)) |                                                                         s->l2_size * sizeof(uint32_t)) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
| @@ -537,15 +549,15 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | |||||||
|     if (!cluster_offset) { |     if (!cluster_offset) { | ||||||
|         if (!allocate) |         if (!allocate) | ||||||
|             return 0; |             return 0; | ||||||
|  |  | ||||||
|         // Avoid the L2 tables update for the images that have snapshots. |         // Avoid the L2 tables update for the images that have snapshots. | ||||||
|         cluster_offset = bdrv_getlength(bs->file); |         if (!s->is_parent) { | ||||||
|         bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); |             cluster_offset = bdrv_getlength(s->hd); | ||||||
|  |             bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9)); | ||||||
|         cluster_offset >>= 9; |  | ||||||
|         tmp = cpu_to_le32(cluster_offset); |  | ||||||
|         l2_table[l2_index] = tmp; |  | ||||||
|  |  | ||||||
|  |             cluster_offset >>= 9; | ||||||
|  |             tmp = cpu_to_le32(cluster_offset); | ||||||
|  |             l2_table[l2_index] = tmp; | ||||||
|  |         } | ||||||
|         /* First of all we write grain itself, to avoid race condition |         /* First of all we write grain itself, to avoid race condition | ||||||
|          * that may to corrupt the image. |          * that may to corrupt the image. | ||||||
|          * This problem may occur because of insufficient space on host disk |          * This problem may occur because of insufficient space on host disk | ||||||
| @@ -607,7 +619,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|                 memset(buf, 0, 512 * n); |                 memset(buf, 0, 512 * n); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) |             if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||||
|                 return -1; |                 return -1; | ||||||
|         } |         } | ||||||
|         nb_sectors -= n; |         nb_sectors -= n; | ||||||
| @@ -643,7 +655,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, | |||||||
|         if (!cluster_offset) |         if (!cluster_offset) | ||||||
|             return -1; |             return -1; | ||||||
|  |  | ||||||
|         if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) |         if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||||
|             return -1; |             return -1; | ||||||
|         if (m_data.valid) { |         if (m_data.valid) { | ||||||
|             /* update L2 tables */ |             /* update L2 tables */ | ||||||
| @@ -691,7 +703,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     int64_t total_size = 0; |     int64_t total_size = 0; | ||||||
|     const char *backing_file = NULL; |     const char *backing_file = NULL; | ||||||
|     int flags = 0; |     int flags = 0; | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     // Read out options |     // Read out options | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
| @@ -713,7 +724,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, |     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | ||||||
|               0644); |               0644); | ||||||
|     if (fd < 0) |     if (fd < 0) | ||||||
|         return -errno; |         return -1; | ||||||
|     magic = cpu_to_be32(VMDK4_MAGIC); |     magic = cpu_to_be32(VMDK4_MAGIC); | ||||||
|     memset(&header, 0, sizeof(header)); |     memset(&header, 0, sizeof(header)); | ||||||
|     header.version = cpu_to_le32(1); |     header.version = cpu_to_le32(1); | ||||||
| @@ -748,44 +759,22 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     header.check_bytes[3] = 0xa; |     header.check_bytes[3] = 0xa; | ||||||
|  |  | ||||||
|     /* write all the data */ |     /* write all the data */ | ||||||
|     ret = qemu_write_full(fd, &magic, sizeof(magic)); |     write(fd, &magic, sizeof(magic)); | ||||||
|     if (ret != sizeof(magic)) { |     write(fd, &header, sizeof(header)); | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|     ret = qemu_write_full(fd, &header, sizeof(header)); |  | ||||||
|     if (ret != sizeof(header)) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = ftruncate(fd, header.grain_offset << 9); |     ftruncate(fd, header.grain_offset << 9); | ||||||
|     if (ret < 0) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* write grain directory */ |     /* write grain directory */ | ||||||
|     lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); |     lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); | ||||||
|     for (i = 0, tmp = header.rgd_offset + gd_size; |     for (i = 0, tmp = header.rgd_offset + gd_size; | ||||||
|          i < gt_count; i++, tmp += gt_size) { |          i < gt_count; i++, tmp += gt_size) | ||||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); |         write(fd, &tmp, sizeof(tmp)); | ||||||
|         if (ret != sizeof(tmp)) { |  | ||||||
|             ret = -errno; |  | ||||||
|             goto exit; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* write backup grain directory */ |     /* write backup grain directory */ | ||||||
|     lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); |     lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); | ||||||
|     for (i = 0, tmp = header.gd_offset + gd_size; |     for (i = 0, tmp = header.gd_offset + gd_size; | ||||||
|          i < gt_count; i++, tmp += gt_size) { |          i < gt_count; i++, tmp += gt_size) | ||||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); |         write(fd, &tmp, sizeof(tmp)); | ||||||
|         if (ret != sizeof(tmp)) { |  | ||||||
|             ret = -errno; |  | ||||||
|             goto exit; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* compose the descriptor */ |     /* compose the descriptor */ | ||||||
|     real_filename = filename; |     real_filename = filename; | ||||||
| @@ -802,16 +791,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | |||||||
|  |  | ||||||
|     /* write the descriptor */ |     /* write the descriptor */ | ||||||
|     lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); |     lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); | ||||||
|     ret = qemu_write_full(fd, desc, strlen(desc)); |     write(fd, desc, strlen(desc)); | ||||||
|     if (ret != strlen(desc)) { |  | ||||||
|         ret = -errno; |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| exit: |  | ||||||
|     close(fd); |     close(fd); | ||||||
|     return ret; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void vmdk_close(BlockDriverState *bs) | static void vmdk_close(BlockDriverState *bs) | ||||||
| @@ -820,11 +803,15 @@ static void vmdk_close(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     qemu_free(s->l1_table); |     qemu_free(s->l1_table); | ||||||
|     qemu_free(s->l2_cache); |     qemu_free(s->l2_cache); | ||||||
|  |     // try to close parent image, if exist | ||||||
|  |     vmdk_parent_close(s->hd); | ||||||
|  |     bdrv_delete(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vmdk_flush(BlockDriverState *bs) | static void vmdk_flush(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     return bdrv_flush(bs->file); |     BDRVVmdkState *s = bs->opaque; | ||||||
|  |     bdrv_flush(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -851,7 +838,7 @@ static BlockDriver bdrv_vmdk = { | |||||||
|     .format_name	= "vmdk", |     .format_name	= "vmdk", | ||||||
|     .instance_size	= sizeof(BDRVVmdkState), |     .instance_size	= sizeof(BDRVVmdkState), | ||||||
|     .bdrv_probe		= vmdk_probe, |     .bdrv_probe		= vmdk_probe, | ||||||
|     .bdrv_open      = vmdk_open, |     .bdrv_open		= vmdk_open, | ||||||
|     .bdrv_read		= vmdk_read, |     .bdrv_read		= vmdk_read, | ||||||
|     .bdrv_write		= vmdk_write, |     .bdrv_write		= vmdk_write, | ||||||
|     .bdrv_close		= vmdk_close, |     .bdrv_close		= vmdk_close, | ||||||
|   | |||||||
							
								
								
									
										135
									
								
								block/vpc.c
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								block/vpc.c
									
									
									
									
									
								
							| @@ -110,6 +110,8 @@ struct vhd_dyndisk_header { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct BDRVVPCState { | typedef struct BDRVVPCState { | ||||||
|  |     BlockDriverState *hd; | ||||||
|  |  | ||||||
|     uint8_t footer_buf[HEADER_SIZE]; |     uint8_t footer_buf[HEADER_SIZE]; | ||||||
|     uint64_t free_data_block_offset; |     uint64_t free_data_block_offset; | ||||||
|     int max_table_entries; |     int max_table_entries; | ||||||
| @@ -148,16 +150,20 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vpc_open(BlockDriverState *bs, int flags) | static int vpc_open(BlockDriverState *bs, const char *filename, int flags) | ||||||
| { | { | ||||||
|     BDRVVPCState *s = bs->opaque; |     BDRVVPCState *s = bs->opaque; | ||||||
|     int i; |     int ret, i; | ||||||
|     struct vhd_footer* footer; |     struct vhd_footer* footer; | ||||||
|     struct vhd_dyndisk_header* dyndisk_header; |     struct vhd_dyndisk_header* dyndisk_header; | ||||||
|     uint8_t buf[HEADER_SIZE]; |     uint8_t buf[HEADER_SIZE]; | ||||||
|     uint32_t checksum; |     uint32_t checksum; | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) |     ret = bdrv_file_open(&s->hd, filename, flags); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  |  | ||||||
|  |     if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     footer = (struct vhd_footer*) s->footer_buf; |     footer = (struct vhd_footer*) s->footer_buf; | ||||||
| @@ -168,7 +174,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | |||||||
|     footer->checksum = 0; |     footer->checksum = 0; | ||||||
|     if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) |     if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) | ||||||
|         fprintf(stderr, "block-vpc: The header checksum of '%s' is " |         fprintf(stderr, "block-vpc: The header checksum of '%s' is " | ||||||
|             "incorrect.\n", bs->filename); |             "incorrect.\n", filename); | ||||||
|  |  | ||||||
|     // The visible size of a image in Virtual PC depends on the geometry |     // The visible size of a image in Virtual PC depends on the geometry | ||||||
|     // rather than on the size stored in the footer (the size in the footer |     // rather than on the size stored in the footer (the size in the footer | ||||||
| @@ -176,7 +182,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | |||||||
|     bs->total_sectors = (int64_t) |     bs->total_sectors = (int64_t) | ||||||
|         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; |         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; | ||||||
|  |  | ||||||
|     if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) |     if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) | ||||||
|             != HEADER_SIZE) |             != HEADER_SIZE) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
| @@ -193,7 +199,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | |||||||
|     s->pagetable = qemu_malloc(s->max_table_entries * 4); |     s->pagetable = qemu_malloc(s->max_table_entries * 4); | ||||||
|  |  | ||||||
|     s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); |     s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); | ||||||
|     if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, |     if (bdrv_pread(s->hd, s->bat_offset, s->pagetable, | ||||||
|             s->max_table_entries * 4) != s->max_table_entries * 4) |             s->max_table_entries * 4) != s->max_table_entries * 4) | ||||||
| 	    goto fail; | 	    goto fail; | ||||||
|  |  | ||||||
| @@ -222,6 +228,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | |||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  fail: |  fail: | ||||||
|  |     bdrv_delete(s->hd); | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -259,7 +266,7 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, | |||||||
|  |  | ||||||
|         s->last_bitmap_offset = bitmap_offset; |         s->last_bitmap_offset = bitmap_offset; | ||||||
|         memset(bitmap, 0xff, s->bitmap_size); |         memset(bitmap, 0xff, s->bitmap_size); | ||||||
|         bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); |         bdrv_pwrite_sync(s->hd, bitmap_offset, bitmap, s->bitmap_size); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| //    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", | //    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", | ||||||
| @@ -309,7 +316,7 @@ static int rewrite_footer(BlockDriverState* bs) | |||||||
|     BDRVVPCState *s = bs->opaque; |     BDRVVPCState *s = bs->opaque; | ||||||
|     int64_t offset = s->free_data_block_offset; |     int64_t offset = s->free_data_block_offset; | ||||||
|  |  | ||||||
|     ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); |     ret = bdrv_pwrite_sync(s->hd, offset, s->footer_buf, HEADER_SIZE); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         return ret; |         return ret; | ||||||
|  |  | ||||||
| @@ -344,7 +351,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) | |||||||
|  |  | ||||||
|     // Initialize the block's bitmap |     // Initialize the block's bitmap | ||||||
|     memset(bitmap, 0xff, s->bitmap_size); |     memset(bitmap, 0xff, s->bitmap_size); | ||||||
|     bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, |     bdrv_pwrite_sync(s->hd, s->free_data_block_offset, bitmap, | ||||||
|         s->bitmap_size); |         s->bitmap_size); | ||||||
|  |  | ||||||
|     // Write new footer (the old one will be overwritten) |     // Write new footer (the old one will be overwritten) | ||||||
| @@ -356,7 +363,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) | |||||||
|     // Write BAT entry to disk |     // Write BAT entry to disk | ||||||
|     bat_offset = s->bat_offset + (4 * index); |     bat_offset = s->bat_offset + (4 * index); | ||||||
|     bat_value = be32_to_cpu(s->pagetable[index]); |     bat_value = be32_to_cpu(s->pagetable[index]); | ||||||
|     ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); |     ret = bdrv_pwrite_sync(s->hd, bat_offset, &bat_value, 4); | ||||||
|     if (ret < 0) |     if (ret < 0) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
| @@ -373,30 +380,21 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, | |||||||
|     BDRVVPCState *s = bs->opaque; |     BDRVVPCState *s = bs->opaque; | ||||||
|     int ret; |     int ret; | ||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     int64_t sectors, sectors_per_block; |  | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         offset = get_sector_offset(bs, sector_num, 0); |         offset = get_sector_offset(bs, sector_num, 0); | ||||||
|  |  | ||||||
|         sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; |  | ||||||
|         sectors = sectors_per_block - (sector_num % sectors_per_block); |  | ||||||
|         if (sectors > nb_sectors) { |  | ||||||
|             sectors = nb_sectors; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (offset == -1) { |         if (offset == -1) { | ||||||
|             memset(buf, 0, sectors * BDRV_SECTOR_SIZE); |             memset(buf, 0, 512); | ||||||
|         } else { |         } else { | ||||||
|             ret = bdrv_pread(bs->file, offset, buf, |             ret = bdrv_pread(s->hd, offset, buf, 512); | ||||||
|                 sectors * BDRV_SECTOR_SIZE); |             if (ret != 512) | ||||||
|             if (ret != sectors * BDRV_SECTOR_SIZE) { |  | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         nb_sectors -= sectors; |         nb_sectors--; | ||||||
|         sector_num += sectors; |         sector_num++; | ||||||
|         buf += sectors * BDRV_SECTOR_SIZE; |         buf += 512; | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -406,41 +404,29 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, | |||||||
| { | { | ||||||
|     BDRVVPCState *s = bs->opaque; |     BDRVVPCState *s = bs->opaque; | ||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     int64_t sectors, sectors_per_block; |  | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
|         offset = get_sector_offset(bs, sector_num, 1); |         offset = get_sector_offset(bs, sector_num, 1); | ||||||
|  |  | ||||||
|         sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; |  | ||||||
|         sectors = sectors_per_block - (sector_num % sectors_per_block); |  | ||||||
|         if (sectors > nb_sectors) { |  | ||||||
|             sectors = nb_sectors; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (offset == -1) { |         if (offset == -1) { | ||||||
|             offset = alloc_block(bs, sector_num); |             offset = alloc_block(bs, sector_num); | ||||||
|             if (offset < 0) |             if (offset < 0) | ||||||
|                 return -1; |                 return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); |         ret = bdrv_pwrite(s->hd, offset, buf, 512); | ||||||
|         if (ret != sectors * BDRV_SECTOR_SIZE) { |         if (ret != 512) | ||||||
|             return -1; |             return -1; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         nb_sectors -= sectors; |         nb_sectors--; | ||||||
|         sector_num += sectors; |         sector_num++; | ||||||
|         buf += sectors * BDRV_SECTOR_SIZE; |         buf += 512; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vpc_flush(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_flush(bs->file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Calculates the number of cylinders, heads and sectors per cylinder |  * Calculates the number of cylinders, heads and sectors per cylinder | ||||||
| @@ -502,7 +488,6 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     uint8_t secs_per_cyl = 0; |     uint8_t secs_per_cyl = 0; | ||||||
|     size_t block_size, num_bat_entries; |     size_t block_size, num_bat_entries; | ||||||
|     int64_t total_sectors = 0; |     int64_t total_sectors = 0; | ||||||
|     int ret = -EIO; |  | ||||||
|  |  | ||||||
|     // Read out options |     // Read out options | ||||||
|     while (options && options->name) { |     while (options && options->name) { | ||||||
| @@ -522,8 +507,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { |     for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { | ||||||
|         if (calculate_geometry(total_sectors + i, |         if (calculate_geometry(total_sectors + i, | ||||||
|                                &cyls, &heads, &secs_per_cyl)) { |                                &cyls, &heads, &secs_per_cyl)) { | ||||||
|             ret = -EFBIG; |             return -EFBIG; | ||||||
|             goto fail; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     total_sectors = (int64_t) cyls * heads * secs_per_cyl; |     total_sectors = (int64_t) cyls * heads * secs_per_cyl; | ||||||
| @@ -562,28 +546,22 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     block_size = 0x200000; |     block_size = 0x200000; | ||||||
|     num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); |     num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); | ||||||
|  |  | ||||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { |     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | ||||||
|         goto fail; |         return -EIO; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) { |     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) | ||||||
|         goto fail; |         return -EIO; | ||||||
|     } |     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | ||||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { |         return -EIO; | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Write the initial BAT |     // Write the initial BAT | ||||||
|     if (lseek(fd, 3 * 512, SEEK_SET) < 0) { |     if (lseek(fd, 3 * 512, SEEK_SET) < 0) | ||||||
|         goto fail; |         return -EIO; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     memset(buf, 0xFF, 512); |     memset(buf, 0xFF, 512); | ||||||
|     for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { |     for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) | ||||||
|         if (write(fd, buf, 512) != 512) { |         if (write(fd, buf, 512) != 512) | ||||||
|             goto fail; |             return -EIO; | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // Prepare the Dynamic Disk Header |     // Prepare the Dynamic Disk Header | ||||||
| @@ -600,18 +578,13 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | |||||||
|     dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); |     dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); | ||||||
|  |  | ||||||
|     // Write the header |     // Write the header | ||||||
|     if (lseek(fd, 512, SEEK_SET) < 0) { |     if (lseek(fd, 512, SEEK_SET) < 0) | ||||||
|         goto fail; |         return -EIO; | ||||||
|     } |     if (write(fd, buf, 1024) != 1024) | ||||||
|  |         return -EIO; | ||||||
|  |  | ||||||
|     if (write(fd, buf, 1024) != 1024) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|     ret = 0; |  | ||||||
|  |  | ||||||
|  fail: |  | ||||||
|     close(fd); |     close(fd); | ||||||
|     return ret; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void vpc_close(BlockDriverState *bs) | static void vpc_close(BlockDriverState *bs) | ||||||
| @@ -621,6 +594,7 @@ static void vpc_close(BlockDriverState *bs) | |||||||
| #ifdef CACHE | #ifdef CACHE | ||||||
|     qemu_free(s->pageentry_u8); |     qemu_free(s->pageentry_u8); | ||||||
| #endif | #endif | ||||||
|  |     bdrv_delete(s->hd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static QEMUOptionParameter vpc_create_options[] = { | static QEMUOptionParameter vpc_create_options[] = { | ||||||
| @@ -633,15 +607,14 @@ static QEMUOptionParameter vpc_create_options[] = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_vpc = { | static BlockDriver bdrv_vpc = { | ||||||
|     .format_name    = "vpc", |     .format_name	= "vpc", | ||||||
|     .instance_size  = sizeof(BDRVVPCState), |     .instance_size	= sizeof(BDRVVPCState), | ||||||
|     .bdrv_probe     = vpc_probe, |     .bdrv_probe		= vpc_probe, | ||||||
|     .bdrv_open      = vpc_open, |     .bdrv_open		= vpc_open, | ||||||
|     .bdrv_read      = vpc_read, |     .bdrv_read		= vpc_read, | ||||||
|     .bdrv_write     = vpc_write, |     .bdrv_write		= vpc_write, | ||||||
|     .bdrv_flush     = vpc_flush, |     .bdrv_close		= vpc_close, | ||||||
|     .bdrv_close     = vpc_close, |     .bdrv_create	= vpc_create, | ||||||
|     .bdrv_create    = vpc_create, |  | ||||||
|  |  | ||||||
|     .create_options = vpc_create_options, |     .create_options = vpc_create_options, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -512,7 +512,7 @@ static inline uint8_t fat_chksum(const direntry_t* entry) | |||||||
|     for(i=0;i<11;i++) { |     for(i=0;i<11;i++) { | ||||||
|         unsigned char c; |         unsigned char c; | ||||||
|  |  | ||||||
|         c = (i < 8) ? entry->name[i] : entry->extension[i-8]; |         c = (i <= 8) ? entry->name[i] : entry->extension[i-8]; | ||||||
|         chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c; |         chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -756,7 +756,6 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) | |||||||
|         if (st.st_size > 0x7fffffff) { |         if (st.st_size > 0x7fffffff) { | ||||||
| 	    fprintf(stderr, "File %s is larger than 2GB\n", buffer); | 	    fprintf(stderr, "File %s is larger than 2GB\n", buffer); | ||||||
| 	    free(buffer); | 	    free(buffer); | ||||||
|             closedir(dir); |  | ||||||
| 	    return -2; | 	    return -2; | ||||||
|         } |         } | ||||||
| 	direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); | 	direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); | ||||||
| @@ -1100,8 +1099,8 @@ static inline void vvfat_close_current_file(BDRVVVFATState *s) | |||||||
|  */ |  */ | ||||||
| static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) | static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) | ||||||
| { | { | ||||||
|  |     int index3=index1+1; | ||||||
|     while(1) { |     while(1) { | ||||||
|         int index3; |  | ||||||
| 	mapping_t* mapping; | 	mapping_t* mapping; | ||||||
| 	index3=(index1+index2)/2; | 	index3=(index1+index2)/2; | ||||||
| 	mapping=array_get(&(s->mapping),index3); | 	mapping=array_get(&(s->mapping),index3); | ||||||
| @@ -1245,7 +1244,7 @@ static void print_direntry(const direntry_t* direntry) | |||||||
|     int j = 0; |     int j = 0; | ||||||
|     char buffer[1024]; |     char buffer[1024]; | ||||||
|  |  | ||||||
|     fprintf(stderr, "direntry %p: ", direntry); |     fprintf(stderr, "direntry 0x%x: ", (int)direntry); | ||||||
|     if(!direntry) |     if(!direntry) | ||||||
| 	return; | 	return; | ||||||
|     if(is_long_name(direntry)) { |     if(is_long_name(direntry)) { | ||||||
| @@ -1274,11 +1273,7 @@ static void print_direntry(const direntry_t* direntry) | |||||||
|  |  | ||||||
| static void print_mapping(const mapping_t* mapping) | static void print_mapping(const mapping_t* mapping) | ||||||
| { | { | ||||||
|     fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " |     fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); | ||||||
|         "first_mapping_index = %d, name = %s, mode = 0x%x, " , |  | ||||||
|         mapping, mapping->begin, mapping->end, mapping->dir_index, |  | ||||||
|         mapping->first_mapping_index, mapping->path, mapping->mode); |  | ||||||
|  |  | ||||||
|     if (mapping->mode & MODE_DIRECTORY) |     if (mapping->mode & MODE_DIRECTORY) | ||||||
| 	fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); | 	fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); | ||||||
|     else |     else | ||||||
| @@ -1643,7 +1638,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | |||||||
| 	    /* new file */ | 	    /* new file */ | ||||||
| 	    schedule_new_file(s, qemu_strdup(path), cluster_num); | 	    schedule_new_file(s, qemu_strdup(path), cluster_num); | ||||||
| 	else { | 	else { | ||||||
|             abort(); | 	    assert(0); | ||||||
| 	    return 0; | 	    return 0; | ||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
| @@ -1664,7 +1659,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | |||||||
| 		    if (offset != mapping->info.file.offset + s->cluster_size | 		    if (offset != mapping->info.file.offset + s->cluster_size | ||||||
| 			    * (cluster_num - mapping->begin)) { | 			    * (cluster_num - mapping->begin)) { | ||||||
| 			/* offset of this cluster in file chain has changed */ | 			/* offset of this cluster in file chain has changed */ | ||||||
|                         abort(); | 			assert(0); | ||||||
| 			copy_it = 1; | 			copy_it = 1; | ||||||
| 		    } else if (offset == 0) { | 		    } else if (offset == 0) { | ||||||
| 			const char* basename = get_basename(mapping->path); | 			const char* basename = get_basename(mapping->path); | ||||||
| @@ -1676,7 +1671,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | |||||||
|  |  | ||||||
| 		    if (mapping->first_mapping_index != first_mapping_index | 		    if (mapping->first_mapping_index != first_mapping_index | ||||||
| 			    && mapping->info.file.offset > 0) { | 			    && mapping->info.file.offset > 0) { | ||||||
|                         abort(); | 			assert(0); | ||||||
| 			copy_it = 1; | 			copy_it = 1; | ||||||
| 		    } | 		    } | ||||||
|  |  | ||||||
| @@ -1842,7 +1837,7 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i) | |||||||
| 		    goto fail; | 		    goto fail; | ||||||
| 		} | 		} | ||||||
| 	    } else | 	    } else | ||||||
|                 abort(); /* cluster_count = 0; */ | 		assert(0); /* cluster_count = 0; */ | ||||||
|  |  | ||||||
| 	    ret += cluster_count; | 	    ret += cluster_count; | ||||||
| 	} | 	} | ||||||
| @@ -2283,6 +2278,7 @@ static void check1(BDRVVVFATState* s) | |||||||
| 	    fprintf(stderr, "deleted\n"); | 	    fprintf(stderr, "deleted\n"); | ||||||
| 	    continue; | 	    continue; | ||||||
| 	} | 	} | ||||||
|  | 	assert(mapping->dir_index >= 0); | ||||||
| 	assert(mapping->dir_index < s->directory.next); | 	assert(mapping->dir_index < s->directory.next); | ||||||
| 	direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); | 	direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); | ||||||
| 	assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); | 	assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); | ||||||
| @@ -2462,17 +2458,14 @@ static int handle_commits(BDRVVVFATState* s) | |||||||
| 	commit_t* commit = array_get(&(s->commits), i); | 	commit_t* commit = array_get(&(s->commits), i); | ||||||
| 	switch(commit->action) { | 	switch(commit->action) { | ||||||
| 	case ACTION_RENAME: case ACTION_MKDIR: | 	case ACTION_RENAME: case ACTION_MKDIR: | ||||||
|             abort(); | 	    assert(0); | ||||||
| 	    fail = -2; | 	    fail = -2; | ||||||
| 	    break; | 	    break; | ||||||
| 	case ACTION_WRITEOUT: { | 	case ACTION_WRITEOUT: { | ||||||
| #ifndef NDEBUG |  | ||||||
|             /* these variables are only used by assert() below */ |  | ||||||
| 	    direntry_t* entry = array_get(&(s->directory), | 	    direntry_t* entry = array_get(&(s->directory), | ||||||
| 		    commit->param.writeout.dir_index); | 		    commit->param.writeout.dir_index); | ||||||
| 	    uint32_t begin = begin_of_direntry(entry); | 	    uint32_t begin = begin_of_direntry(entry); | ||||||
| 	    mapping_t* mapping = find_mapping_for_cluster(s, begin); | 	    mapping_t* mapping = find_mapping_for_cluster(s, begin); | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	    assert(mapping); | 	    assert(mapping); | ||||||
| 	    assert(mapping->begin == begin); | 	    assert(mapping->begin == begin); | ||||||
| @@ -2523,7 +2516,7 @@ static int handle_commits(BDRVVVFATState* s) | |||||||
| 	    break; | 	    break; | ||||||
| 	} | 	} | ||||||
| 	default: | 	default: | ||||||
|             abort(); | 	    assert(0); | ||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
|     if (i > 0 && array_remove_slice(&(s->commits), 0, i)) |     if (i > 0 && array_remove_slice(&(s->commits), 0, i)) | ||||||
| @@ -2611,7 +2604,7 @@ static int do_commit(BDRVVVFATState* s) | |||||||
|     ret = handle_renames_and_mkdirs(s); |     ret = handle_renames_and_mkdirs(s); | ||||||
|     if (ret) { |     if (ret) { | ||||||
| 	fprintf(stderr, "Error handling renames (%d)\n", ret); | 	fprintf(stderr, "Error handling renames (%d)\n", ret); | ||||||
|         abort(); | 	assert(0); | ||||||
| 	return ret; | 	return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -2622,21 +2615,21 @@ static int do_commit(BDRVVVFATState* s) | |||||||
|     ret = commit_direntries(s, 0, -1); |     ret = commit_direntries(s, 0, -1); | ||||||
|     if (ret) { |     if (ret) { | ||||||
| 	fprintf(stderr, "Fatal: error while committing (%d)\n", ret); | 	fprintf(stderr, "Fatal: error while committing (%d)\n", ret); | ||||||
|         abort(); | 	assert(0); | ||||||
| 	return ret; | 	return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = handle_commits(s); |     ret = handle_commits(s); | ||||||
|     if (ret) { |     if (ret) { | ||||||
| 	fprintf(stderr, "Error handling commits (%d)\n", ret); | 	fprintf(stderr, "Error handling commits (%d)\n", ret); | ||||||
|         abort(); | 	assert(0); | ||||||
| 	return ret; | 	return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = handle_deletes(s); |     ret = handle_deletes(s); | ||||||
|     if (ret) { |     if (ret) { | ||||||
| 	fprintf(stderr, "Error deleting\n"); | 	fprintf(stderr, "Error deleting\n"); | ||||||
|         abort(); |         assert(0); | ||||||
| 	return ret; | 	return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -2665,11 +2658,6 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num, | |||||||
|  |  | ||||||
| DLOG(checkpoint()); | DLOG(checkpoint()); | ||||||
|  |  | ||||||
|     /* Check if we're operating in read-only mode */ |  | ||||||
|     if (s->qcow == NULL) { |  | ||||||
|         return -EACCES; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     vvfat_close_current_file(s); |     vvfat_close_current_file(s); | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
| @@ -2768,12 +2756,12 @@ static int vvfat_is_allocated(BlockDriverState *bs, | |||||||
|  |  | ||||||
| static int write_target_commit(BlockDriverState *bs, int64_t sector_num, | static int write_target_commit(BlockDriverState *bs, int64_t sector_num, | ||||||
| 	const uint8_t* buffer, int nb_sectors) { | 	const uint8_t* buffer, int nb_sectors) { | ||||||
|     BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); |     BDRVVVFATState* s = bs->opaque; | ||||||
|     return try_commit(s); |     return try_commit(s); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void write_target_close(BlockDriverState *bs) { | static void write_target_close(BlockDriverState *bs) { | ||||||
|     BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); |     BDRVVVFATState* s = bs->opaque; | ||||||
|     bdrv_delete(s->qcow); |     bdrv_delete(s->qcow); | ||||||
|     free(s->qcow_filename); |     free(s->qcow_filename); | ||||||
| } | } | ||||||
| @@ -2788,7 +2776,6 @@ static int enable_write_target(BDRVVVFATState *s) | |||||||
| { | { | ||||||
|     BlockDriver *bdrv_qcow; |     BlockDriver *bdrv_qcow; | ||||||
|     QEMUOptionParameter *options; |     QEMUOptionParameter *options; | ||||||
|     int ret; |  | ||||||
|     int size = sector2cluster(s, s->sector_count); |     int size = sector2cluster(s, s->sector_count); | ||||||
|     s->used_clusters = calloc(size, 1); |     s->used_clusters = calloc(size, 1); | ||||||
|  |  | ||||||
| @@ -2804,17 +2791,9 @@ static int enable_write_target(BDRVVVFATState *s) | |||||||
|  |  | ||||||
|     if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) |     if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) | ||||||
| 	return -1; | 	return -1; | ||||||
|  |  | ||||||
|     s->qcow = bdrv_new(""); |     s->qcow = bdrv_new(""); | ||||||
|     if (s->qcow == NULL) { |     if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) | ||||||
|         return -1; | 	return -1; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = bdrv_open(s->qcow, s->qcow_filename, |  | ||||||
|             BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); |  | ||||||
|     if (ret < 0) { |  | ||||||
| 	return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     unlink(s->qcow_filename); |     unlink(s->qcow_filename); | ||||||
| @@ -2822,8 +2801,7 @@ static int enable_write_target(BDRVVVFATState *s) | |||||||
|  |  | ||||||
|     s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); |     s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); | ||||||
|     s->bs->backing_hd->drv = &vvfat_write_target; |     s->bs->backing_hd->drv = &vvfat_write_target; | ||||||
|     s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*)); |     s->bs->backing_hd->opaque = s; | ||||||
|     *(void**)s->bs->backing_hd->opaque = s; |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -2843,7 +2821,7 @@ static void vvfat_close(BlockDriverState *bs) | |||||||
| static BlockDriver bdrv_vvfat = { | static BlockDriver bdrv_vvfat = { | ||||||
|     .format_name	= "vvfat", |     .format_name	= "vvfat", | ||||||
|     .instance_size	= sizeof(BDRVVVFATState), |     .instance_size	= sizeof(BDRVVVFATState), | ||||||
|     .bdrv_file_open	= vvfat_open, |     .bdrv_open		= vvfat_open, | ||||||
|     .bdrv_read		= vvfat_read, |     .bdrv_read		= vvfat_read, | ||||||
|     .bdrv_write		= vvfat_write, |     .bdrv_write		= vvfat_write, | ||||||
|     .bdrv_close		= vvfat_close, |     .bdrv_close		= vvfat_close, | ||||||
| @@ -2881,7 +2859,7 @@ static void checkpoint(void) { | |||||||
|     return; |     return; | ||||||
|     /* avoid compiler warnings: */ |     /* avoid compiler warnings: */ | ||||||
|     hexdump(NULL, 100); |     hexdump(NULL, 100); | ||||||
|     remove_mapping(vvv, 0); |     remove_mapping(vvv, NULL); | ||||||
|     print_mapping(NULL); |     print_mapping(NULL); | ||||||
|     print_direntry(NULL); |     print_direntry(NULL); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								block_int.h
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								block_int.h
									
									
									
									
									
								
							| @@ -26,9 +26,9 @@ | |||||||
|  |  | ||||||
| #include "block.h" | #include "block.h" | ||||||
| #include "qemu-option.h" | #include "qemu-option.h" | ||||||
| #include "qemu-queue.h" |  | ||||||
|  |  | ||||||
| #define BLOCK_FLAG_ENCRYPT	1 | #define BLOCK_FLAG_ENCRYPT	1 | ||||||
|  | #define BLOCK_FLAG_COMPRESS	2 | ||||||
| #define BLOCK_FLAG_COMPAT6	4 | #define BLOCK_FLAG_COMPAT6	4 | ||||||
|  |  | ||||||
| #define BLOCK_OPT_SIZE          "size" | #define BLOCK_OPT_SIZE          "size" | ||||||
| @@ -37,7 +37,6 @@ | |||||||
| #define BLOCK_OPT_BACKING_FILE  "backing_file" | #define BLOCK_OPT_BACKING_FILE  "backing_file" | ||||||
| #define BLOCK_OPT_BACKING_FMT   "backing_fmt" | #define BLOCK_OPT_BACKING_FMT   "backing_fmt" | ||||||
| #define BLOCK_OPT_CLUSTER_SIZE  "cluster_size" | #define BLOCK_OPT_CLUSTER_SIZE  "cluster_size" | ||||||
| #define BLOCK_OPT_TABLE_SIZE    "table_size" |  | ||||||
| #define BLOCK_OPT_PREALLOC      "preallocation" | #define BLOCK_OPT_PREALLOC      "preallocation" | ||||||
|  |  | ||||||
| typedef struct AIOPool { | typedef struct AIOPool { | ||||||
| @@ -51,15 +50,14 @@ struct BlockDriver { | |||||||
|     int instance_size; |     int instance_size; | ||||||
|     int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); |     int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); | ||||||
|     int (*bdrv_probe_device)(const char *filename); |     int (*bdrv_probe_device)(const char *filename); | ||||||
|     int (*bdrv_open)(BlockDriverState *bs, int flags); |     int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags); | ||||||
|     int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags); |  | ||||||
|     int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, |     int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, | ||||||
|                      uint8_t *buf, int nb_sectors); |                      uint8_t *buf, int nb_sectors); | ||||||
|     int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, |     int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, | ||||||
|                       const uint8_t *buf, int nb_sectors); |                       const uint8_t *buf, int nb_sectors); | ||||||
|     void (*bdrv_close)(BlockDriverState *bs); |     void (*bdrv_close)(BlockDriverState *bs); | ||||||
|     int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); |     int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); | ||||||
|     int (*bdrv_flush)(BlockDriverState *bs); |     void (*bdrv_flush)(BlockDriverState *bs); | ||||||
|     int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, |     int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, | ||||||
|                              int nb_sectors, int *pnum); |                              int nb_sectors, int *pnum); | ||||||
|     int (*bdrv_set_key)(BlockDriverState *bs, const char *key); |     int (*bdrv_set_key)(BlockDriverState *bs, const char *key); | ||||||
| @@ -73,8 +71,6 @@ struct BlockDriver { | |||||||
|         BlockDriverCompletionFunc *cb, void *opaque); |         BlockDriverCompletionFunc *cb, void *opaque); | ||||||
|     BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, |     BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, | ||||||
|         BlockDriverCompletionFunc *cb, void *opaque); |         BlockDriverCompletionFunc *cb, void *opaque); | ||||||
|     int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num, |  | ||||||
|                         int nb_sectors); |  | ||||||
|  |  | ||||||
|     int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs, |     int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs, | ||||||
|         int num_reqs); |         int num_reqs); | ||||||
| @@ -95,8 +91,6 @@ struct BlockDriver { | |||||||
|     int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); |     int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); | ||||||
|     int (*bdrv_snapshot_list)(BlockDriverState *bs, |     int (*bdrv_snapshot_list)(BlockDriverState *bs, | ||||||
|                               QEMUSnapshotInfo **psn_info); |                               QEMUSnapshotInfo **psn_info); | ||||||
|     int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, |  | ||||||
|                                   const char *snapshot_name); |  | ||||||
|     int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); |     int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); | ||||||
|  |  | ||||||
|     int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, |     int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, | ||||||
| @@ -104,9 +98,6 @@ struct BlockDriver { | |||||||
|     int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, |     int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, | ||||||
|                              int64_t pos, int size); |                              int64_t pos, int size); | ||||||
|  |  | ||||||
|     int (*bdrv_change_backing_file)(BlockDriverState *bs, |  | ||||||
|         const char *backing_file, const char *backing_fmt); |  | ||||||
|  |  | ||||||
|     /* removable device specific */ |     /* removable device specific */ | ||||||
|     int (*bdrv_is_inserted)(BlockDriverState *bs); |     int (*bdrv_is_inserted)(BlockDriverState *bs); | ||||||
|     int (*bdrv_media_changed)(BlockDriverState *bs); |     int (*bdrv_media_changed)(BlockDriverState *bs); | ||||||
| @@ -123,44 +114,32 @@ struct BlockDriver { | |||||||
|     QEMUOptionParameter *create_options; |     QEMUOptionParameter *create_options; | ||||||
|  |  | ||||||
|  |  | ||||||
|     /* |     /* Returns number of errors in image, -errno for internal errors */ | ||||||
|      * Returns 0 for completed check, -errno for internal errors. |     int (*bdrv_check)(BlockDriverState* bs); | ||||||
|      * The check results are stored in result. |  | ||||||
|      */ |  | ||||||
|     int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result); |  | ||||||
|  |  | ||||||
|     void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); |     /* Set if newly created images are not guaranteed to contain only zeros */ | ||||||
|  |     int no_zero_init; | ||||||
|  |  | ||||||
|     /* |     struct BlockDriver *next; | ||||||
|      * Returns 1 if newly created images are guaranteed to contain only |  | ||||||
|      * zeros, 0 otherwise. |  | ||||||
|      */ |  | ||||||
|     int (*bdrv_has_zero_init)(BlockDriverState *bs); |  | ||||||
|  |  | ||||||
|     QLIST_ENTRY(BlockDriver) list; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct BlockDriverState { | struct BlockDriverState { | ||||||
|     int64_t total_sectors; /* if we are reading a disk image, give its |     int64_t total_sectors; /* if we are reading a disk image, give its | ||||||
|                               size in sectors */ |                               size in sectors */ | ||||||
|     int read_only; /* if true, the media is read only */ |     int read_only; /* if true, the media is read only */ | ||||||
|     int keep_read_only; /* if true, the media was requested to stay read only */ |  | ||||||
|     int open_flags; /* flags used to open the file, re-used for re-open */ |     int open_flags; /* flags used to open the file, re-used for re-open */ | ||||||
|     int removable; /* if true, the media can be removed */ |     int removable; /* if true, the media can be removed */ | ||||||
|     int locked;    /* if true, the media cannot temporarily be ejected */ |     int locked;    /* if true, the media cannot temporarily be ejected */ | ||||||
|     int tray_open; /* if true, the virtual tray is open */ |  | ||||||
|     int encrypted; /* if true, the media is encrypted */ |     int encrypted; /* if true, the media is encrypted */ | ||||||
|     int valid_key; /* if true, a valid encryption key has been set */ |     int valid_key; /* if true, a valid encryption key has been set */ | ||||||
|     int sg;        /* if true, the device is a /dev/sg* */ |     int sg;        /* if true, the device is a /dev/sg* */ | ||||||
|     /* event callback when inserting/removing */ |     /* event callback when inserting/removing */ | ||||||
|     void (*change_cb)(void *opaque, int reason); |     void (*change_cb)(void *opaque); | ||||||
|     void *change_opaque; |     void *change_opaque; | ||||||
|  |  | ||||||
|     BlockDriver *drv; /* NULL means no media */ |     BlockDriver *drv; /* NULL means no media */ | ||||||
|     void *opaque; |     void *opaque; | ||||||
|  |  | ||||||
|     DeviceState *peer; |  | ||||||
|  |  | ||||||
|     char filename[1024]; |     char filename[1024]; | ||||||
|     char backing_file[1024]; /* if non zero, the image is a diff of |     char backing_file[1024]; /* if non zero, the image is a diff of | ||||||
|                                 this file image */ |                                 this file image */ | ||||||
| @@ -169,8 +148,6 @@ struct BlockDriverState { | |||||||
|     int media_changed; |     int media_changed; | ||||||
|  |  | ||||||
|     BlockDriverState *backing_hd; |     BlockDriverState *backing_hd; | ||||||
|     BlockDriverState *file; |  | ||||||
|  |  | ||||||
|     /* async read/write emulation */ |     /* async read/write emulation */ | ||||||
|  |  | ||||||
|     void *sync_aiocb; |     void *sync_aiocb; | ||||||
| @@ -180,7 +157,6 @@ struct BlockDriverState { | |||||||
|     uint64_t wr_bytes; |     uint64_t wr_bytes; | ||||||
|     uint64_t rd_ops; |     uint64_t rd_ops; | ||||||
|     uint64_t wr_ops; |     uint64_t wr_ops; | ||||||
|     uint64_t wr_highest_sector; |  | ||||||
|  |  | ||||||
|     /* Whether the disk can expand beyond total_sectors */ |     /* Whether the disk can expand beyond total_sectors */ | ||||||
|     int growable; |     int growable; | ||||||
| @@ -195,18 +171,12 @@ struct BlockDriverState { | |||||||
|        drivers. They are not used by the block driver */ |        drivers. They are not used by the block driver */ | ||||||
|     int cyls, heads, secs, translation; |     int cyls, heads, secs, translation; | ||||||
|     int type; |     int type; | ||||||
|     BlockErrorAction on_read_error, on_write_error; |  | ||||||
|     char device_name[32]; |     char device_name[32]; | ||||||
|     unsigned long *dirty_bitmap; |     unsigned long *dirty_bitmap; | ||||||
|     int64_t dirty_count; |     BlockDriverState *next; | ||||||
|     int in_use; /* users other than guest access, eg. block migration */ |  | ||||||
|     QTAILQ_ENTRY(BlockDriverState) list; |  | ||||||
|     void *private; |     void *private; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define CHANGE_MEDIA	0x01 |  | ||||||
| #define CHANGE_SIZE	0x02 |  | ||||||
|  |  | ||||||
| struct BlockDriverAIOCB { | struct BlockDriverAIOCB { | ||||||
|     AIOPool *pool; |     AIOPool *pool; | ||||||
|     BlockDriverState *bs; |     BlockDriverState *bs; | ||||||
| @@ -223,43 +193,10 @@ void qemu_aio_release(void *p); | |||||||
|  |  | ||||||
| void *qemu_blockalign(BlockDriverState *bs, size_t size); | void *qemu_blockalign(BlockDriverState *bs, size_t size); | ||||||
|  |  | ||||||
|  | extern BlockDriverState *bdrv_first; | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| int is_windows_drive(const char *filename); | int is_windows_drive(const char *filename); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| typedef struct BlockConf { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     uint16_t physical_block_size; |  | ||||||
|     uint16_t logical_block_size; |  | ||||||
|     uint16_t min_io_size; |  | ||||||
|     uint32_t opt_io_size; |  | ||||||
|     int32_t bootindex; |  | ||||||
|     uint32_t discard_granularity; |  | ||||||
| } BlockConf; |  | ||||||
|  |  | ||||||
| static inline unsigned int get_physical_block_exp(BlockConf *conf) |  | ||||||
| { |  | ||||||
|     unsigned int exp = 0, size; |  | ||||||
|  |  | ||||||
|     for (size = conf->physical_block_size; |  | ||||||
|         size > conf->logical_block_size; |  | ||||||
|         size >>= 1) { |  | ||||||
|         exp++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return exp; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define DEFINE_BLOCK_PROPERTIES(_state, _conf)                          \ |  | ||||||
|     DEFINE_PROP_DRIVE("drive", _state, _conf.bs),                       \ |  | ||||||
|     DEFINE_PROP_UINT16("logical_block_size", _state,                    \ |  | ||||||
|                        _conf.logical_block_size, 512),                  \ |  | ||||||
|     DEFINE_PROP_UINT16("physical_block_size", _state,                   \ |  | ||||||
|                        _conf.physical_block_size, 512),                 \ |  | ||||||
|     DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0),  \ |  | ||||||
|     DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0),    \ |  | ||||||
|     DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1),        \ |  | ||||||
|     DEFINE_PROP_UINT32("discard_granularity", _state, \ |  | ||||||
|                        _conf.discard_granularity, 0) |  | ||||||
|  |  | ||||||
| #endif /* BLOCK_INT_H */ | #endif /* BLOCK_INT_H */ | ||||||
|   | |||||||
							
								
								
									
										787
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										787
									
								
								blockdev.c
									
									
									
									
									
								
							| @@ -1,787 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU host block devices |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or |  | ||||||
|  * later.  See the COPYING file in the top-level directory. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "block.h" |  | ||||||
| #include "blockdev.h" |  | ||||||
| #include "monitor.h" |  | ||||||
| #include "qerror.h" |  | ||||||
| #include "qemu-option.h" |  | ||||||
| #include "qemu-config.h" |  | ||||||
| #include "sysemu.h" |  | ||||||
| #include "hw/qdev.h" |  | ||||||
| #include "block_int.h" |  | ||||||
|  |  | ||||||
| static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); |  | ||||||
|  |  | ||||||
| static const char *const if_name[IF_COUNT] = { |  | ||||||
|     [IF_NONE] = "none", |  | ||||||
|     [IF_IDE] = "ide", |  | ||||||
|     [IF_SCSI] = "scsi", |  | ||||||
|     [IF_FLOPPY] = "floppy", |  | ||||||
|     [IF_PFLASH] = "pflash", |  | ||||||
|     [IF_MTD] = "mtd", |  | ||||||
|     [IF_SD] = "sd", |  | ||||||
|     [IF_VIRTIO] = "virtio", |  | ||||||
|     [IF_XEN] = "xen", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const int if_max_devs[IF_COUNT] = { |  | ||||||
|     /* |  | ||||||
|      * Do not change these numbers!  They govern how drive option |  | ||||||
|      * index maps to unit and bus.  That mapping is ABI. |  | ||||||
|      * |  | ||||||
|      * All controllers used to imlement if=T drives need to support |  | ||||||
|      * if_max_devs[T] units, for any T with if_max_devs[T] != 0. |  | ||||||
|      * Otherwise, some index values map to "impossible" bus, unit |  | ||||||
|      * values. |  | ||||||
|      * |  | ||||||
|      * For instance, if you change [IF_SCSI] to 255, -drive |  | ||||||
|      * if=scsi,index=12 no longer means bus=1,unit=5, but |  | ||||||
|      * bus=0,unit=12.  With an lsi53c895a controller (7 units max), |  | ||||||
|      * the drive can't be set up.  Regression. |  | ||||||
|      */ |  | ||||||
|     [IF_IDE] = 2, |  | ||||||
|     [IF_SCSI] = 7, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * We automatically delete the drive when a device using it gets |  | ||||||
|  * unplugged.  Questionable feature, but we can't just drop it. |  | ||||||
|  * Device models call blockdev_mark_auto_del() to schedule the |  | ||||||
|  * automatic deletion, and generic qdev code calls blockdev_auto_del() |  | ||||||
|  * when deletion is actually safe. |  | ||||||
|  */ |  | ||||||
| void blockdev_mark_auto_del(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     DriveInfo *dinfo = drive_get_by_blockdev(bs); |  | ||||||
|  |  | ||||||
|     if (dinfo) { |  | ||||||
|         dinfo->auto_del = 1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void blockdev_auto_del(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     DriveInfo *dinfo = drive_get_by_blockdev(bs); |  | ||||||
|  |  | ||||||
|     if (dinfo && dinfo->auto_del) { |  | ||||||
|         drive_put_ref(dinfo); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int drive_index_to_bus_id(BlockInterfaceType type, int index) |  | ||||||
| { |  | ||||||
|     int max_devs = if_max_devs[type]; |  | ||||||
|     return max_devs ? index / max_devs : 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int drive_index_to_unit_id(BlockInterfaceType type, int index) |  | ||||||
| { |  | ||||||
|     int max_devs = if_max_devs[type]; |  | ||||||
|     return max_devs ? index % max_devs : index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QemuOpts *drive_def(const char *optstr) |  | ||||||
| { |  | ||||||
|     return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, |  | ||||||
|                     const char *optstr) |  | ||||||
| { |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     char buf[32]; |  | ||||||
|  |  | ||||||
|     opts = drive_def(optstr); |  | ||||||
|     if (!opts) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     if (type != IF_DEFAULT) { |  | ||||||
|         qemu_opt_set(opts, "if", if_name[type]); |  | ||||||
|     } |  | ||||||
|     if (index >= 0) { |  | ||||||
|         snprintf(buf, sizeof(buf), "%d", index); |  | ||||||
|         qemu_opt_set(opts, "index", buf); |  | ||||||
|     } |  | ||||||
|     if (file) |  | ||||||
|         qemu_opt_set(opts, "file", file); |  | ||||||
|     return opts; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) |  | ||||||
| { |  | ||||||
|     DriveInfo *dinfo; |  | ||||||
|  |  | ||||||
|     /* seek interface, bus and unit */ |  | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { |  | ||||||
|         if (dinfo->type == type && |  | ||||||
| 	    dinfo->bus == bus && |  | ||||||
| 	    dinfo->unit == unit) |  | ||||||
|             return dinfo; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) |  | ||||||
| { |  | ||||||
|     return drive_get(type, |  | ||||||
|                      drive_index_to_bus_id(type, index), |  | ||||||
|                      drive_index_to_unit_id(type, index)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int drive_get_max_bus(BlockInterfaceType type) |  | ||||||
| { |  | ||||||
|     int max_bus; |  | ||||||
|     DriveInfo *dinfo; |  | ||||||
|  |  | ||||||
|     max_bus = -1; |  | ||||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { |  | ||||||
|         if(dinfo->type == type && |  | ||||||
|            dinfo->bus > max_bus) |  | ||||||
|             max_bus = dinfo->bus; |  | ||||||
|     } |  | ||||||
|     return max_bus; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Get a block device.  This should only be used for single-drive devices |  | ||||||
|    (e.g. SD/Floppy/MTD).  Multi-disk devices (scsi/ide) should use the |  | ||||||
|    appropriate bus.  */ |  | ||||||
| DriveInfo *drive_get_next(BlockInterfaceType type) |  | ||||||
| { |  | ||||||
|     static int next_block_unit[IF_COUNT]; |  | ||||||
|  |  | ||||||
|     return drive_get(type, 0, next_block_unit[type]++); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     DriveInfo *dinfo; |  | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { |  | ||||||
|         if (dinfo->bdrv == bs) { |  | ||||||
|             return dinfo; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void bdrv_format_print(void *opaque, const char *name) |  | ||||||
| { |  | ||||||
|     error_printf(" %s", name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void drive_uninit(DriveInfo *dinfo) |  | ||||||
| { |  | ||||||
|     qemu_opts_del(dinfo->opts); |  | ||||||
|     bdrv_delete(dinfo->bdrv); |  | ||||||
|     qemu_free(dinfo->id); |  | ||||||
|     QTAILQ_REMOVE(&drives, dinfo, next); |  | ||||||
|     qemu_free(dinfo); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void drive_put_ref(DriveInfo *dinfo) |  | ||||||
| { |  | ||||||
|     assert(dinfo->refcount); |  | ||||||
|     if (--dinfo->refcount == 0) { |  | ||||||
|         drive_uninit(dinfo); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void drive_get_ref(DriveInfo *dinfo) |  | ||||||
| { |  | ||||||
|     dinfo->refcount++; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int parse_block_error_action(const char *buf, int is_read) |  | ||||||
| { |  | ||||||
|     if (!strcmp(buf, "ignore")) { |  | ||||||
|         return BLOCK_ERR_IGNORE; |  | ||||||
|     } else if (!is_read && !strcmp(buf, "enospc")) { |  | ||||||
|         return BLOCK_ERR_STOP_ENOSPC; |  | ||||||
|     } else if (!strcmp(buf, "stop")) { |  | ||||||
|         return BLOCK_ERR_STOP_ANY; |  | ||||||
|     } else if (!strcmp(buf, "report")) { |  | ||||||
|         return BLOCK_ERR_REPORT; |  | ||||||
|     } else { |  | ||||||
|         error_report("'%s' invalid %s error action", |  | ||||||
|                      buf, is_read ? "read" : "write"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) |  | ||||||
| { |  | ||||||
|     const char *buf; |  | ||||||
|     const char *file = NULL; |  | ||||||
|     char devname[128]; |  | ||||||
|     const char *serial; |  | ||||||
|     const char *mediastr = ""; |  | ||||||
|     BlockInterfaceType type; |  | ||||||
|     enum { MEDIA_DISK, MEDIA_CDROM } media; |  | ||||||
|     int bus_id, unit_id; |  | ||||||
|     int cyls, heads, secs, translation; |  | ||||||
|     BlockDriver *drv = NULL; |  | ||||||
|     int max_devs; |  | ||||||
|     int index; |  | ||||||
|     int ro = 0; |  | ||||||
|     int bdrv_flags = 0; |  | ||||||
|     int on_read_error, on_write_error; |  | ||||||
|     const char *devaddr; |  | ||||||
|     DriveInfo *dinfo; |  | ||||||
|     int snapshot = 0; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     translation = BIOS_ATA_TRANSLATION_AUTO; |  | ||||||
|  |  | ||||||
|     if (default_to_scsi) { |  | ||||||
|         type = IF_SCSI; |  | ||||||
|         pstrcpy(devname, sizeof(devname), "scsi"); |  | ||||||
|     } else { |  | ||||||
|         type = IF_IDE; |  | ||||||
|         pstrcpy(devname, sizeof(devname), "ide"); |  | ||||||
|     } |  | ||||||
|     media = MEDIA_DISK; |  | ||||||
|  |  | ||||||
|     /* extract parameters */ |  | ||||||
|     bus_id  = qemu_opt_get_number(opts, "bus", 0); |  | ||||||
|     unit_id = qemu_opt_get_number(opts, "unit", -1); |  | ||||||
|     index   = qemu_opt_get_number(opts, "index", -1); |  | ||||||
|  |  | ||||||
|     cyls  = qemu_opt_get_number(opts, "cyls", 0); |  | ||||||
|     heads = qemu_opt_get_number(opts, "heads", 0); |  | ||||||
|     secs  = qemu_opt_get_number(opts, "secs", 0); |  | ||||||
|  |  | ||||||
|     snapshot = qemu_opt_get_bool(opts, "snapshot", 0); |  | ||||||
|     ro = qemu_opt_get_bool(opts, "readonly", 0); |  | ||||||
|  |  | ||||||
|     file = qemu_opt_get(opts, "file"); |  | ||||||
|     serial = qemu_opt_get(opts, "serial"); |  | ||||||
|  |  | ||||||
|     if ((buf = qemu_opt_get(opts, "if")) != NULL) { |  | ||||||
|         pstrcpy(devname, sizeof(devname), buf); |  | ||||||
|         for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++) |  | ||||||
|             ; |  | ||||||
|         if (type == IF_COUNT) { |  | ||||||
|             error_report("unsupported bus type '%s'", buf); |  | ||||||
|             return NULL; |  | ||||||
| 	} |  | ||||||
|     } |  | ||||||
|     max_devs = if_max_devs[type]; |  | ||||||
|  |  | ||||||
|     if (cyls || heads || secs) { |  | ||||||
|         if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { |  | ||||||
|             error_report("invalid physical cyls number"); |  | ||||||
| 	    return NULL; |  | ||||||
| 	} |  | ||||||
|         if (heads < 1 || (type == IF_IDE && heads > 16)) { |  | ||||||
|             error_report("invalid physical heads number"); |  | ||||||
| 	    return NULL; |  | ||||||
| 	} |  | ||||||
|         if (secs < 1 || (type == IF_IDE && secs > 63)) { |  | ||||||
|             error_report("invalid physical secs number"); |  | ||||||
| 	    return NULL; |  | ||||||
| 	} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((buf = qemu_opt_get(opts, "trans")) != NULL) { |  | ||||||
|         if (!cyls) { |  | ||||||
|             error_report("'%s' trans must be used with cyls,heads and secs", |  | ||||||
|                          buf); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|         if (!strcmp(buf, "none")) |  | ||||||
|             translation = BIOS_ATA_TRANSLATION_NONE; |  | ||||||
|         else if (!strcmp(buf, "lba")) |  | ||||||
|             translation = BIOS_ATA_TRANSLATION_LBA; |  | ||||||
|         else if (!strcmp(buf, "auto")) |  | ||||||
|             translation = BIOS_ATA_TRANSLATION_AUTO; |  | ||||||
| 	else { |  | ||||||
|             error_report("'%s' invalid translation type", buf); |  | ||||||
| 	    return NULL; |  | ||||||
| 	} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((buf = qemu_opt_get(opts, "media")) != NULL) { |  | ||||||
|         if (!strcmp(buf, "disk")) { |  | ||||||
| 	    media = MEDIA_DISK; |  | ||||||
| 	} else if (!strcmp(buf, "cdrom")) { |  | ||||||
|             if (cyls || secs || heads) { |  | ||||||
|                 error_report("'%s' invalid physical CHS format", buf); |  | ||||||
| 	        return NULL; |  | ||||||
|             } |  | ||||||
| 	    media = MEDIA_CDROM; |  | ||||||
| 	} else { |  | ||||||
| 	    error_report("'%s' invalid media", buf); |  | ||||||
| 	    return NULL; |  | ||||||
| 	} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((buf = qemu_opt_get(opts, "cache")) != NULL) { |  | ||||||
|         if (!strcmp(buf, "off") || !strcmp(buf, "none")) { |  | ||||||
|             bdrv_flags |= BDRV_O_NOCACHE; |  | ||||||
|         } else if (!strcmp(buf, "writeback")) { |  | ||||||
|             bdrv_flags |= BDRV_O_CACHE_WB; |  | ||||||
|         } else if (!strcmp(buf, "unsafe")) { |  | ||||||
|             bdrv_flags |= BDRV_O_CACHE_WB; |  | ||||||
|             bdrv_flags |= BDRV_O_NO_FLUSH; |  | ||||||
|         } else if (!strcmp(buf, "writethrough")) { |  | ||||||
|             /* this is the default */ |  | ||||||
|         } else { |  | ||||||
|            error_report("invalid cache option"); |  | ||||||
|            return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_LINUX_AIO |  | ||||||
|     if ((buf = qemu_opt_get(opts, "aio")) != NULL) { |  | ||||||
|         if (!strcmp(buf, "native")) { |  | ||||||
|             bdrv_flags |= BDRV_O_NATIVE_AIO; |  | ||||||
|         } else if (!strcmp(buf, "threads")) { |  | ||||||
|             /* this is the default */ |  | ||||||
|         } else { |  | ||||||
|            error_report("invalid aio option"); |  | ||||||
|            return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     if ((buf = qemu_opt_get(opts, "format")) != NULL) { |  | ||||||
|        if (strcmp(buf, "?") == 0) { |  | ||||||
|            error_printf("Supported formats:"); |  | ||||||
|            bdrv_iterate_format(bdrv_format_print, NULL); |  | ||||||
|            error_printf("\n"); |  | ||||||
|            return NULL; |  | ||||||
|         } |  | ||||||
|         drv = bdrv_find_whitelisted_format(buf); |  | ||||||
|         if (!drv) { |  | ||||||
|             error_report("'%s' invalid format", buf); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     on_write_error = BLOCK_ERR_STOP_ENOSPC; |  | ||||||
|     if ((buf = qemu_opt_get(opts, "werror")) != NULL) { |  | ||||||
|         if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { |  | ||||||
|             error_report("werror is not supported by this bus type"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         on_write_error = parse_block_error_action(buf, 0); |  | ||||||
|         if (on_write_error < 0) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     on_read_error = BLOCK_ERR_REPORT; |  | ||||||
|     if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { |  | ||||||
|         if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { |  | ||||||
|             error_report("rerror is not supported by this bus type"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         on_read_error = parse_block_error_action(buf, 1); |  | ||||||
|         if (on_read_error < 0) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { |  | ||||||
|         if (type != IF_VIRTIO) { |  | ||||||
|             error_report("addr is not supported by this bus type"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* compute bus and unit according index */ |  | ||||||
|  |  | ||||||
|     if (index != -1) { |  | ||||||
|         if (bus_id != 0 || unit_id != -1) { |  | ||||||
|             error_report("index cannot be used with bus and unit"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|         bus_id = drive_index_to_bus_id(type, index); |  | ||||||
|         unit_id = drive_index_to_unit_id(type, index); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* if user doesn't specify a unit_id, |  | ||||||
|      * try to find the first free |  | ||||||
|      */ |  | ||||||
|  |  | ||||||
|     if (unit_id == -1) { |  | ||||||
|        unit_id = 0; |  | ||||||
|        while (drive_get(type, bus_id, unit_id) != NULL) { |  | ||||||
|            unit_id++; |  | ||||||
|            if (max_devs && unit_id >= max_devs) { |  | ||||||
|                unit_id -= max_devs; |  | ||||||
|                bus_id++; |  | ||||||
|            } |  | ||||||
|        } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* check unit id */ |  | ||||||
|  |  | ||||||
|     if (max_devs && unit_id >= max_devs) { |  | ||||||
|         error_report("unit %d too big (max is %d)", |  | ||||||
|                      unit_id, max_devs - 1); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * catch multiple definitions |  | ||||||
|      */ |  | ||||||
|  |  | ||||||
|     if (drive_get(type, bus_id, unit_id) != NULL) { |  | ||||||
|         error_report("drive with bus=%d, unit=%d (index=%d) exists", |  | ||||||
|                      bus_id, unit_id, index); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* init */ |  | ||||||
|  |  | ||||||
|     dinfo = qemu_mallocz(sizeof(*dinfo)); |  | ||||||
|     if ((buf = qemu_opts_id(opts)) != NULL) { |  | ||||||
|         dinfo->id = qemu_strdup(buf); |  | ||||||
|     } else { |  | ||||||
|         /* no id supplied -> create one */ |  | ||||||
|         dinfo->id = qemu_mallocz(32); |  | ||||||
|         if (type == IF_IDE || type == IF_SCSI) |  | ||||||
|             mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; |  | ||||||
|         if (max_devs) |  | ||||||
|             snprintf(dinfo->id, 32, "%s%i%s%i", |  | ||||||
|                      devname, bus_id, mediastr, unit_id); |  | ||||||
|         else |  | ||||||
|             snprintf(dinfo->id, 32, "%s%s%i", |  | ||||||
|                      devname, mediastr, unit_id); |  | ||||||
|     } |  | ||||||
|     dinfo->bdrv = bdrv_new(dinfo->id); |  | ||||||
|     dinfo->devaddr = devaddr; |  | ||||||
|     dinfo->type = type; |  | ||||||
|     dinfo->bus = bus_id; |  | ||||||
|     dinfo->unit = unit_id; |  | ||||||
|     dinfo->opts = opts; |  | ||||||
|     dinfo->refcount = 1; |  | ||||||
|     if (serial) |  | ||||||
|         strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1); |  | ||||||
|     QTAILQ_INSERT_TAIL(&drives, dinfo, next); |  | ||||||
|  |  | ||||||
|     bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); |  | ||||||
|  |  | ||||||
|     switch(type) { |  | ||||||
|     case IF_IDE: |  | ||||||
|     case IF_SCSI: |  | ||||||
|     case IF_XEN: |  | ||||||
|     case IF_NONE: |  | ||||||
|         switch(media) { |  | ||||||
| 	case MEDIA_DISK: |  | ||||||
|             if (cyls != 0) { |  | ||||||
|                 bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs); |  | ||||||
|                 bdrv_set_translation_hint(dinfo->bdrv, translation); |  | ||||||
|             } |  | ||||||
| 	    break; |  | ||||||
| 	case MEDIA_CDROM: |  | ||||||
|             bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM); |  | ||||||
| 	    break; |  | ||||||
| 	} |  | ||||||
|         break; |  | ||||||
|     case IF_SD: |  | ||||||
|         /* FIXME: This isn't really a floppy, but it's a reasonable |  | ||||||
|            approximation.  */ |  | ||||||
|     case IF_FLOPPY: |  | ||||||
|         bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY); |  | ||||||
|         break; |  | ||||||
|     case IF_PFLASH: |  | ||||||
|     case IF_MTD: |  | ||||||
|         break; |  | ||||||
|     case IF_VIRTIO: |  | ||||||
|         /* add virtio block device */ |  | ||||||
|         opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); |  | ||||||
|         qemu_opt_set(opts, "driver", "virtio-blk-pci"); |  | ||||||
|         qemu_opt_set(opts, "drive", dinfo->id); |  | ||||||
|         if (devaddr) |  | ||||||
|             qemu_opt_set(opts, "addr", devaddr); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         abort(); |  | ||||||
|     } |  | ||||||
|     if (!file || !*file) { |  | ||||||
|         return dinfo; |  | ||||||
|     } |  | ||||||
|     if (snapshot) { |  | ||||||
|         /* always use cache=unsafe with snapshot */ |  | ||||||
|         bdrv_flags &= ~BDRV_O_CACHE_MASK; |  | ||||||
|         bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (media == MEDIA_CDROM) { |  | ||||||
|         /* CDROM is fine for any interface, don't check.  */ |  | ||||||
|         ro = 1; |  | ||||||
|     } else if (ro == 1) { |  | ||||||
|         if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) { |  | ||||||
|             error_report("readonly not supported by this bus type"); |  | ||||||
|             goto err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bdrv_flags |= ro ? 0 : BDRV_O_RDWR; |  | ||||||
|  |  | ||||||
|     ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         error_report("could not open disk image %s: %s", |  | ||||||
|                      file, strerror(-ret)); |  | ||||||
|         goto err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (bdrv_key_required(dinfo->bdrv)) |  | ||||||
|         autostart = 0; |  | ||||||
|     return dinfo; |  | ||||||
|  |  | ||||||
| err: |  | ||||||
|     bdrv_delete(dinfo->bdrv); |  | ||||||
|     qemu_free(dinfo->id); |  | ||||||
|     QTAILQ_REMOVE(&drives, dinfo, next); |  | ||||||
|     qemu_free(dinfo); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void do_commit(Monitor *mon, const QDict *qdict) |  | ||||||
| { |  | ||||||
|     const char *device = qdict_get_str(qdict, "device"); |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|  |  | ||||||
|     if (!strcmp(device, "all")) { |  | ||||||
|         bdrv_commit_all(); |  | ||||||
|     } else { |  | ||||||
|         bs = bdrv_find(device); |  | ||||||
|         if (!bs) { |  | ||||||
|             qerror_report(QERR_DEVICE_NOT_FOUND, device); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         bdrv_commit(bs); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) |  | ||||||
| { |  | ||||||
|     const char *device = qdict_get_str(qdict, "device"); |  | ||||||
|     const char *filename = qdict_get_try_str(qdict, "snapshot_file"); |  | ||||||
|     const char *format = qdict_get_try_str(qdict, "format"); |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     BlockDriver *drv, *proto_drv; |  | ||||||
|     int ret = 0; |  | ||||||
|     int flags; |  | ||||||
|  |  | ||||||
|     if (!filename) { |  | ||||||
|         qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); |  | ||||||
|         ret = -1; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(device); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); |  | ||||||
|         ret = -1; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!format) { |  | ||||||
|         format = "qcow2"; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     drv = bdrv_find_format(format); |  | ||||||
|     if (!drv) { |  | ||||||
|         qerror_report(QERR_INVALID_BLOCK_FORMAT, format); |  | ||||||
|         ret = -1; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     proto_drv = bdrv_find_protocol(filename); |  | ||||||
|     if (!proto_drv) { |  | ||||||
|         qerror_report(QERR_INVALID_BLOCK_FORMAT, format); |  | ||||||
|         ret = -1; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = bdrv_img_create(filename, format, bs->filename, |  | ||||||
|                           bs->drv->format_name, NULL, -1, bs->open_flags); |  | ||||||
|     if (ret) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_aio_flush(); |  | ||||||
|     bdrv_flush(bs); |  | ||||||
|  |  | ||||||
|     flags = bs->open_flags; |  | ||||||
|     bdrv_close(bs); |  | ||||||
|     ret = bdrv_open(bs, filename, flags, drv); |  | ||||||
|     /* |  | ||||||
|      * If reopening the image file we just created fails, we really |  | ||||||
|      * are in trouble :( |  | ||||||
|      */ |  | ||||||
|     if (ret != 0) { |  | ||||||
|         abort(); |  | ||||||
|     } |  | ||||||
| out: |  | ||||||
|     if (ret) { |  | ||||||
|         ret = -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int eject_device(Monitor *mon, BlockDriverState *bs, int force) |  | ||||||
| { |  | ||||||
|     if (!force) { |  | ||||||
|         if (!bdrv_is_removable(bs)) { |  | ||||||
|             qerror_report(QERR_DEVICE_NOT_REMOVABLE, |  | ||||||
|                            bdrv_get_device_name(bs)); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|         if (bdrv_is_locked(bs)) { |  | ||||||
|             qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     bdrv_close(bs); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data) |  | ||||||
| { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     int force = qdict_get_try_bool(qdict, "force", 0); |  | ||||||
|     const char *filename = qdict_get_str(qdict, "device"); |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(filename); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, filename); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     return eject_device(mon, bs, force); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int do_block_set_passwd(Monitor *mon, const QDict *qdict, |  | ||||||
|                         QObject **ret_data) |  | ||||||
| { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     int err; |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(qdict_get_str(qdict, "device")); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device")); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = bdrv_set_key(bs, qdict_get_str(qdict, "password")); |  | ||||||
|     if (err == -EINVAL) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); |  | ||||||
|         return -1; |  | ||||||
|     } else if (err < 0) { |  | ||||||
|         qerror_report(QERR_INVALID_PASSWORD); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int do_change_block(Monitor *mon, const char *device, |  | ||||||
|                     const char *filename, const char *fmt) |  | ||||||
| { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     BlockDriver *drv = NULL; |  | ||||||
|     int bdrv_flags; |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(device); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     if (fmt) { |  | ||||||
|         drv = bdrv_find_whitelisted_format(fmt); |  | ||||||
|         if (!drv) { |  | ||||||
|             qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if (eject_device(mon, bs, 0) < 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; |  | ||||||
|     bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; |  | ||||||
|     if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) { |  | ||||||
|         qerror_report(QERR_OPEN_FILE_FAILED, filename); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     return monitor_read_bdrv_key_start(mon, bs, NULL, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) |  | ||||||
| { |  | ||||||
|     const char *id = qdict_get_str(qdict, "id"); |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(id); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, id); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     if (bdrv_in_use(bs)) { |  | ||||||
|         qerror_report(QERR_DEVICE_IN_USE, id); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* quiesce block driver; prevent further io */ |  | ||||||
|     qemu_aio_flush(); |  | ||||||
|     bdrv_flush(bs); |  | ||||||
|     bdrv_close(bs); |  | ||||||
|  |  | ||||||
|     /* if we have a device associated with this BlockDriverState (bs->peer) |  | ||||||
|      * then we need to make the drive anonymous until the device |  | ||||||
|      * can be removed.  If this is a drive with no device backing |  | ||||||
|      * then we can just get rid of the block driver state right here. |  | ||||||
|      */ |  | ||||||
|     if (bs->peer) { |  | ||||||
|         bdrv_make_anon(bs); |  | ||||||
|     } else { |  | ||||||
|         drive_uninit(drive_get_by_blockdev(bs)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the |  | ||||||
|  * existing QERR_ macro mess is cleaned up.  A good example for better |  | ||||||
|  * error reports can be found in the qemu-img resize code. |  | ||||||
|  */ |  | ||||||
| int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) |  | ||||||
| { |  | ||||||
|     const char *device = qdict_get_str(qdict, "device"); |  | ||||||
|     int64_t size = qdict_get_int(qdict, "size"); |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|  |  | ||||||
|     bs = bdrv_find(device); |  | ||||||
|     if (!bs) { |  | ||||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (size < 0) { |  | ||||||
|         qerror_report(QERR_UNDEFINED_ERROR); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (bdrv_truncate(bs, size)) { |  | ||||||
|         qerror_report(QERR_UNDEFINED_ERROR); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
							
								
								
									
										68
									
								
								blockdev.h
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								blockdev.h
									
									
									
									
									
								
							| @@ -1,68 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU host block devices |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or |  | ||||||
|  * later.  See the COPYING file in the top-level directory. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef BLOCKDEV_H |  | ||||||
| #define BLOCKDEV_H |  | ||||||
|  |  | ||||||
| #include "block.h" |  | ||||||
| #include "qemu-queue.h" |  | ||||||
|  |  | ||||||
| void blockdev_mark_auto_del(BlockDriverState *bs); |  | ||||||
| void blockdev_auto_del(BlockDriverState *bs); |  | ||||||
|  |  | ||||||
| #define BLOCK_SERIAL_STRLEN 20 |  | ||||||
|  |  | ||||||
| typedef enum { |  | ||||||
|     IF_DEFAULT = -1,            /* for use with drive_add() only */ |  | ||||||
|     IF_NONE, |  | ||||||
|     IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, |  | ||||||
|     IF_COUNT |  | ||||||
| } BlockInterfaceType; |  | ||||||
|  |  | ||||||
| struct DriveInfo { |  | ||||||
|     BlockDriverState *bdrv; |  | ||||||
|     char *id; |  | ||||||
|     const char *devaddr; |  | ||||||
|     BlockInterfaceType type; |  | ||||||
|     int bus; |  | ||||||
|     int unit; |  | ||||||
|     int auto_del;               /* see blockdev_mark_auto_del() */ |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     char serial[BLOCK_SERIAL_STRLEN + 1]; |  | ||||||
|     QTAILQ_ENTRY(DriveInfo) next; |  | ||||||
|     int refcount; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); |  | ||||||
| DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); |  | ||||||
| int drive_get_max_bus(BlockInterfaceType type); |  | ||||||
| DriveInfo *drive_get_next(BlockInterfaceType type); |  | ||||||
| void drive_get_ref(DriveInfo *dinfo); |  | ||||||
| void drive_put_ref(DriveInfo *dinfo); |  | ||||||
| DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); |  | ||||||
|  |  | ||||||
| QemuOpts *drive_def(const char *optstr); |  | ||||||
| QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, |  | ||||||
|                     const char *optstr); |  | ||||||
| DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi); |  | ||||||
|  |  | ||||||
| /* device-hotplug */ |  | ||||||
|  |  | ||||||
| DriveInfo *add_init_drive(const char *opts); |  | ||||||
|  |  | ||||||
| void do_commit(Monitor *mon, const QDict *qdict); |  | ||||||
| int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data); |  | ||||||
| int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data); |  | ||||||
| int do_change_block(Monitor *mon, const char *device, |  | ||||||
|                     const char *filename, const char *fmt); |  | ||||||
| int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); |  | ||||||
| int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); |  | ||||||
| int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -176,6 +176,8 @@ int loader_exec(const char * filename, char ** argv, char ** envp, | |||||||
|  |  | ||||||
|     retval = prepare_binprm(&bprm); |     retval = prepare_binprm(&bprm); | ||||||
|  |  | ||||||
|  |     infop->host_argv = argv; | ||||||
|  |  | ||||||
|     if(retval>=0) { |     if(retval>=0) { | ||||||
|         if (bprm.buf[0] == 0x7f |         if (bprm.buf[0] == 0x7f | ||||||
|                 && bprm.buf[1] == 'E' |                 && bprm.buf[1] == 'E' | ||||||
|   | |||||||
| @@ -1044,7 +1044,7 @@ static void load_symbols(struct elfhdr *hdr, int fd) | |||||||
|     struct elf_shdr sechdr, symtab, strtab; |     struct elf_shdr sechdr, symtab, strtab; | ||||||
|     char *strings; |     char *strings; | ||||||
|     struct syminfo *s; |     struct syminfo *s; | ||||||
|     struct elf_sym *syms, *new_syms; |     struct elf_sym *syms; | ||||||
|  |  | ||||||
|     lseek(fd, hdr->e_shoff, SEEK_SET); |     lseek(fd, hdr->e_shoff, SEEK_SET); | ||||||
|     for (i = 0; i < hdr->e_shnum; i++) { |     for (i = 0; i < hdr->e_shnum; i++) { | ||||||
| @@ -1072,24 +1072,15 @@ static void load_symbols(struct elfhdr *hdr, int fd) | |||||||
|     /* Now know where the strtab and symtab are.  Snarf them. */ |     /* Now know where the strtab and symtab are.  Snarf them. */ | ||||||
|     s = malloc(sizeof(*s)); |     s = malloc(sizeof(*s)); | ||||||
|     syms = malloc(symtab.sh_size); |     syms = malloc(symtab.sh_size); | ||||||
|     if (!syms) { |     if (!syms) | ||||||
|         free(s); |  | ||||||
|         return; |         return; | ||||||
|     } |  | ||||||
|     s->disas_strtab = strings = malloc(strtab.sh_size); |     s->disas_strtab = strings = malloc(strtab.sh_size); | ||||||
|     if (!s->disas_strtab) { |     if (!s->disas_strtab) | ||||||
|         free(s); |  | ||||||
|         free(syms); |  | ||||||
|         return; |         return; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lseek(fd, symtab.sh_offset, SEEK_SET); |     lseek(fd, symtab.sh_offset, SEEK_SET); | ||||||
|     if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { |     if (read(fd, syms, symtab.sh_size) != symtab.sh_size) | ||||||
|         free(s); |  | ||||||
|         free(syms); |  | ||||||
|         free(strings); |  | ||||||
|         return; |         return; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     nsyms = symtab.sh_size / sizeof(struct elf_sym); |     nsyms = symtab.sh_size / sizeof(struct elf_sym); | ||||||
|  |  | ||||||
| @@ -1114,29 +1105,13 @@ static void load_symbols(struct elfhdr *hdr, int fd) | |||||||
| #endif | #endif | ||||||
|         i++; |         i++; | ||||||
|     } |     } | ||||||
|  |     syms = realloc(syms, nsyms * sizeof(*syms)); | ||||||
|      /* Attempt to free the storage associated with the local symbols |  | ||||||
|         that we threw away.  Whether or not this has any effect on the |  | ||||||
|         memory allocation depends on the malloc implementation and how |  | ||||||
|         many symbols we managed to discard. */ |  | ||||||
|     new_syms = realloc(syms, nsyms * sizeof(*syms)); |  | ||||||
|     if (new_syms == NULL) { |  | ||||||
|         free(s); |  | ||||||
|         free(syms); |  | ||||||
|         free(strings); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     syms = new_syms; |  | ||||||
|  |  | ||||||
|     qsort(syms, nsyms, sizeof(*syms), symcmp); |     qsort(syms, nsyms, sizeof(*syms), symcmp); | ||||||
|  |  | ||||||
|     lseek(fd, strtab.sh_offset, SEEK_SET); |     lseek(fd, strtab.sh_offset, SEEK_SET); | ||||||
|     if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { |     if (read(fd, strings, strtab.sh_size) != strtab.sh_size) | ||||||
|         free(s); |  | ||||||
|         free(syms); |  | ||||||
|         free(strings); |  | ||||||
|         return; |         return; | ||||||
|     } |  | ||||||
|     s->disas_num_syms = nsyms; |     s->disas_num_syms = nsyms; | ||||||
| #if ELF_CLASS == ELFCLASS32 | #if ELF_CLASS == ELFCLASS32 | ||||||
|     s->disas_symtab.elf32 = syms; |     s->disas_symtab.elf32 = syms; | ||||||
|   | |||||||
| @@ -30,8 +30,8 @@ | |||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| /* For tb_lock */ | /* For tb_lock */ | ||||||
| #include "exec-all.h" | #include "exec-all.h" | ||||||
| #include "tcg.h" |  | ||||||
| #include "qemu-timer.h" |  | ||||||
| #include "envlist.h" | #include "envlist.h" | ||||||
|  |  | ||||||
| #define DEBUG_LOGFILE "/tmp/qemu.log" | #define DEBUG_LOGFILE "/tmp/qemu.log" | ||||||
| @@ -43,7 +43,7 @@ unsigned long guest_base; | |||||||
| int have_guest_base; | int have_guest_base; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; | static const char *interp_prefix = CONFIG_QEMU_PREFIX; | ||||||
| const char *qemu_uname_release = CONFIG_UNAME_RELEASE; | const char *qemu_uname_release = CONFIG_UNAME_RELEASE; | ||||||
| extern char **environ; | extern char **environ; | ||||||
| enum BSDType bsd_type; | enum BSDType bsd_type; | ||||||
| @@ -759,10 +759,6 @@ int main(int argc, char **argv) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     cpu_model = NULL; |     cpu_model = NULL; | ||||||
| #if defined(cpudef_setup) |  | ||||||
|     cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     optind = 1; |     optind = 1; | ||||||
|     for(;;) { |     for(;;) { | ||||||
|         if (optind >= argc) |         if (optind >= argc) | ||||||
| @@ -795,12 +791,6 @@ int main(int argc, char **argv) | |||||||
|             r = argv[optind++]; |             r = argv[optind++]; | ||||||
|             if (envlist_setenv(envlist, r) != 0) |             if (envlist_setenv(envlist, r) != 0) | ||||||
|                 usage(); |                 usage(); | ||||||
|         } else if (!strcmp(r, "ignore-environment")) { |  | ||||||
|             envlist_free(envlist); |  | ||||||
|             if ((envlist = envlist_create()) == NULL) { |  | ||||||
|                 (void) fprintf(stderr, "Unable to allocate envlist\n"); |  | ||||||
|                 exit(1); |  | ||||||
|             } |  | ||||||
|         } else if (!strcmp(r, "U")) { |         } else if (!strcmp(r, "U")) { | ||||||
|             r = argv[optind++]; |             r = argv[optind++]; | ||||||
|             if (envlist_unsetenv(envlist, r) != 0) |             if (envlist_unsetenv(envlist, r) != 0) | ||||||
| @@ -976,13 +966,6 @@ int main(int argc, char **argv) | |||||||
|     syscall_init(); |     syscall_init(); | ||||||
|     signal_init(); |     signal_init(); | ||||||
|  |  | ||||||
| #if defined(CONFIG_USE_GUEST_BASE) |  | ||||||
|     /* Now that we've loaded the binary, GUEST_BASE is fixed.  Delay |  | ||||||
|        generating the prologue until now so that the prologue can take |  | ||||||
|        the real value of GUEST_BASE into account.  */ |  | ||||||
|     tcg_prologue_init(&tcg_ctx); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     /* build Task State */ |     /* build Task State */ | ||||||
|     memset(ts, 0, sizeof(TaskState)); |     memset(ts, 0, sizeof(TaskState)); | ||||||
|     init_task_state(ts); |     init_task_state(ts); | ||||||
|   | |||||||
| @@ -77,15 +77,16 @@ void mmap_unlock(void) | |||||||
| void *qemu_vmalloc(size_t size) | void *qemu_vmalloc(size_t size) | ||||||
| { | { | ||||||
|     void *p; |     void *p; | ||||||
|  |     unsigned long addr; | ||||||
|     mmap_lock(); |     mmap_lock(); | ||||||
|     /* Use map and mark the pages as used.  */ |     /* Use map and mark the pages as used.  */ | ||||||
|     p = mmap(NULL, size, PROT_READ | PROT_WRITE, |     p = mmap(NULL, size, PROT_READ | PROT_WRITE, | ||||||
|              MAP_PRIVATE | MAP_ANON, -1, 0); |              MAP_PRIVATE | MAP_ANON, -1, 0); | ||||||
|  |  | ||||||
|     if (h2g_valid(p)) { |     addr = (unsigned long)p; | ||||||
|  |     if (addr == (target_ulong) addr) { | ||||||
|         /* Allocated region overlaps guest address space. |         /* Allocated region overlaps guest address space. | ||||||
|            This may recurse.  */ |            This may recurse.  */ | ||||||
|         abi_ulong addr = h2g(p); |  | ||||||
|         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size), |         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size), | ||||||
|                        PAGE_RESERVED); |                        PAGE_RESERVED); | ||||||
|     } |     } | ||||||
| @@ -239,7 +240,7 @@ static int mmap_frag(abi_ulong real_start, | |||||||
|            possible while it is a shared mapping */ |            possible while it is a shared mapping */ | ||||||
|         if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && |         if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && | ||||||
|             (prot & PROT_WRITE)) |             (prot & PROT_WRITE)) | ||||||
|             return -1; |             return -EINVAL; | ||||||
|  |  | ||||||
|         /* adjust protection to be able to read */ |         /* adjust protection to be able to read */ | ||||||
|         if (!(prot1 & PROT_WRITE)) |         if (!(prot1 & PROT_WRITE)) | ||||||
|   | |||||||
| @@ -50,6 +50,7 @@ struct image_info { | |||||||
|     abi_ulong entry; |     abi_ulong entry; | ||||||
|     abi_ulong code_offset; |     abi_ulong code_offset; | ||||||
|     abi_ulong data_offset; |     abi_ulong data_offset; | ||||||
|  |     char      **host_argv; | ||||||
|     int       personality; |     int       personality; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -138,7 +139,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, | |||||||
| abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, | abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, | ||||||
|                             abi_long arg2, abi_long arg3, abi_long arg4, |                             abi_long arg2, abi_long arg3, abi_long arg4, | ||||||
|                             abi_long arg5, abi_long arg6); |                             abi_long arg5, abi_long arg6); | ||||||
| void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); | void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); | ||||||
| extern THREAD CPUState *thread_env; | extern THREAD CPUState *thread_env; | ||||||
| void cpu_loop(CPUState *env); | void cpu_loop(CPUState *env); | ||||||
| char *target_strerror(int err); | char *target_strerror(int err); | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								bswap.h
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								bswap.h
									
									
									
									
									
								
							| @@ -144,7 +144,6 @@ CPU_CONVERT(le, 64, uint64_t) | |||||||
|  |  | ||||||
| #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) | #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) | ||||||
| #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) | #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) | ||||||
| #define cpu_to_be64wu(p, v) cpu_to_be64w(p, v) |  | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
| @@ -202,28 +201,12 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) | |||||||
|     p1[3] = v & 0xff; |     p1[3] = v & 0xff; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) |  | ||||||
| { |  | ||||||
|     uint8_t *p1 = (uint8_t *)p; |  | ||||||
|  |  | ||||||
|     p1[0] = v >> 56; |  | ||||||
|     p1[1] = v >> 48; |  | ||||||
|     p1[2] = v >> 40; |  | ||||||
|     p1[3] = v >> 32; |  | ||||||
|     p1[4] = v >> 24; |  | ||||||
|     p1[5] = v >> 16; |  | ||||||
|     p1[6] = v >> 8; |  | ||||||
|     p1[7] = v & 0xff; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| #define cpu_to_32wu cpu_to_be32wu | #define cpu_to_32wu cpu_to_be32wu | ||||||
| #define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v) |  | ||||||
| #else | #else | ||||||
| #define cpu_to_32wu cpu_to_le32wu | #define cpu_to_32wu cpu_to_le32wu | ||||||
| #define leul_to_cpu(v) (v) |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #undef le_bswap | #undef le_bswap | ||||||
| @@ -231,10 +214,4 @@ static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) | |||||||
| #undef le_bswaps | #undef le_bswaps | ||||||
| #undef be_bswaps | #undef be_bswaps | ||||||
|  |  | ||||||
| /* len must be one of 1, 2, 4 */ |  | ||||||
| static inline uint32_t qemu_bswap_len(uint32_t value, int len) |  | ||||||
| { |  | ||||||
|     return bswap32(value) >> (32 - 8 * len); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif /* BSWAP_H */ | #endif /* BSWAP_H */ | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								bt-host.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								bt-host.c
									
									
									
									
									
								
							| @@ -50,19 +50,19 @@ static void bt_host_send(struct HCIInfo *hci, | |||||||
|     struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; |     struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; | ||||||
|     uint8_t pkt = type; |     uint8_t pkt = type; | ||||||
|     struct iovec iv[2]; |     struct iovec iv[2]; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|     iv[0].iov_base = (void *)&pkt; |     iv[0].iov_base = (void *)&pkt; | ||||||
|     iv[0].iov_len  = 1; |     iv[0].iov_len  = 1; | ||||||
|     iv[1].iov_base = (void *) data; |     iv[1].iov_base = (void *) data; | ||||||
|     iv[1].iov_len  = len; |     iv[1].iov_len  = len; | ||||||
|  |  | ||||||
|     while (writev(s->fd, iv, 2) < 0) { |     while ((ret = writev(s->fd, iv, 2)) < 0) | ||||||
|         if (errno != EAGAIN && errno != EINTR) { |         if (errno != EAGAIN && errno != EINTR) { | ||||||
|             fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", |             fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", | ||||||
|                             errno); |                             errno); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) | static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) | ||||||
| @@ -80,6 +80,13 @@ static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) | |||||||
|     bt_host_send(hci, HCI_SCODATA_PKT, data, len); |     bt_host_send(hci, HCI_SCODATA_PKT, data, len); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int bt_host_read_poll(void *opaque) | ||||||
|  | { | ||||||
|  |     struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; | ||||||
|  |  | ||||||
|  |     return !!s->hci.evt_recv; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void bt_host_read(void *opaque) | static void bt_host_read(void *opaque) | ||||||
| { | { | ||||||
|     struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; |     struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; | ||||||
| @@ -185,7 +192,7 @@ struct HCIInfo *bt_host_hci(const char *id) | |||||||
|     s->hci.acl_send = bt_host_acl; |     s->hci.acl_send = bt_host_acl; | ||||||
|     s->hci.bdaddr_set = bt_host_bdaddr_set; |     s->hci.bdaddr_set = bt_host_bdaddr_set; | ||||||
|  |  | ||||||
|     qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); |     qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, NULL, s); | ||||||
|  |  | ||||||
|     return &s->hci; |     return &s->hci; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,10 +39,10 @@ typedef struct QEMUFileBuffered | |||||||
| } QEMUFileBuffered; | } QEMUFileBuffered; | ||||||
|  |  | ||||||
| #ifdef DEBUG_BUFFERED_FILE | #ifdef DEBUG_BUFFERED_FILE | ||||||
| #define DPRINTF(fmt, ...) \ | #define dprintf(fmt, ...) \ | ||||||
|     do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0) |     do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0) | ||||||
| #else | #else | ||||||
| #define DPRINTF(fmt, ...) \ | #define dprintf(fmt, ...) \ | ||||||
|     do { } while (0) |     do { } while (0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -52,7 +52,7 @@ static void buffered_append(QEMUFileBuffered *s, | |||||||
|     if (size > (s->buffer_capacity - s->buffer_size)) { |     if (size > (s->buffer_capacity - s->buffer_size)) { | ||||||
|         void *tmp; |         void *tmp; | ||||||
|  |  | ||||||
|         DPRINTF("increasing buffer capacity from %zu by %zu\n", |         dprintf("increasing buffer capacity from %zu by %zu\n", | ||||||
|                 s->buffer_capacity, size + 1024); |                 s->buffer_capacity, size + 1024); | ||||||
|  |  | ||||||
|         s->buffer_capacity += size + 1024; |         s->buffer_capacity += size + 1024; | ||||||
| @@ -75,11 +75,11 @@ static void buffered_flush(QEMUFileBuffered *s) | |||||||
|     size_t offset = 0; |     size_t offset = 0; | ||||||
|  |  | ||||||
|     if (s->has_error) { |     if (s->has_error) { | ||||||
|         DPRINTF("flush when error, bailing\n"); |         dprintf("flush when error, bailing\n"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size); |     dprintf("flushing %zu byte(s) of data\n", s->buffer_size); | ||||||
|  |  | ||||||
|     while (offset < s->buffer_size) { |     while (offset < s->buffer_size) { | ||||||
|         ssize_t ret; |         ssize_t ret; | ||||||
| @@ -87,22 +87,22 @@ static void buffered_flush(QEMUFileBuffered *s) | |||||||
|         ret = s->put_buffer(s->opaque, s->buffer + offset, |         ret = s->put_buffer(s->opaque, s->buffer + offset, | ||||||
|                             s->buffer_size - offset); |                             s->buffer_size - offset); | ||||||
|         if (ret == -EAGAIN) { |         if (ret == -EAGAIN) { | ||||||
|             DPRINTF("backend not ready, freezing\n"); |             dprintf("backend not ready, freezing\n"); | ||||||
|             s->freeze_output = 1; |             s->freeze_output = 1; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (ret <= 0) { |         if (ret <= 0) { | ||||||
|             DPRINTF("error flushing data, %zd\n", ret); |             dprintf("error flushing data, %zd\n", ret); | ||||||
|             s->has_error = 1; |             s->has_error = 1; | ||||||
|             break; |             break; | ||||||
|         } else { |         } else { | ||||||
|             DPRINTF("flushed %zd byte(s)\n", ret); |             dprintf("flushed %zd byte(s)\n", ret); | ||||||
|             offset += ret; |             offset += ret; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); |     dprintf("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); | ||||||
|     memmove(s->buffer, s->buffer + offset, s->buffer_size - offset); |     memmove(s->buffer, s->buffer + offset, s->buffer_size - offset); | ||||||
|     s->buffer_size -= offset; |     s->buffer_size -= offset; | ||||||
| } | } | ||||||
| @@ -113,57 +113,49 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in | |||||||
|     int offset = 0; |     int offset = 0; | ||||||
|     ssize_t ret; |     ssize_t ret; | ||||||
|  |  | ||||||
|     DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); |     dprintf("putting %d bytes at %" PRId64 "\n", size, pos); | ||||||
|  |  | ||||||
|     if (s->has_error) { |     if (s->has_error) { | ||||||
|         DPRINTF("flush when error, bailing\n"); |         dprintf("flush when error, bailing\n"); | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DPRINTF("unfreezing output\n"); |     dprintf("unfreezing output\n"); | ||||||
|     s->freeze_output = 0; |     s->freeze_output = 0; | ||||||
|  |  | ||||||
|     buffered_flush(s); |     buffered_flush(s); | ||||||
|  |  | ||||||
|     while (!s->freeze_output && offset < size) { |     while (!s->freeze_output && offset < size) { | ||||||
|         if (s->bytes_xfer > s->xfer_limit) { |         if (s->bytes_xfer > s->xfer_limit) { | ||||||
|             DPRINTF("transfer limit exceeded when putting\n"); |             dprintf("transfer limit exceeded when putting\n"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ret = s->put_buffer(s->opaque, buf + offset, size - offset); |         ret = s->put_buffer(s->opaque, buf + offset, size - offset); | ||||||
|         if (ret == -EAGAIN) { |         if (ret == -EAGAIN) { | ||||||
|             DPRINTF("backend not ready, freezing\n"); |             dprintf("backend not ready, freezing\n"); | ||||||
|             s->freeze_output = 1; |             s->freeze_output = 1; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (ret <= 0) { |         if (ret <= 0) { | ||||||
|             DPRINTF("error putting\n"); |             dprintf("error putting\n"); | ||||||
|             s->has_error = 1; |             s->has_error = 1; | ||||||
|             offset = -EINVAL; |             offset = -EINVAL; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         DPRINTF("put %zd byte(s)\n", ret); |         dprintf("put %zd byte(s)\n", ret); | ||||||
|         offset += ret; |         offset += ret; | ||||||
|         s->bytes_xfer += ret; |         s->bytes_xfer += ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (offset >= 0) { |     if (offset >= 0) { | ||||||
|         DPRINTF("buffering %d bytes\n", size - offset); |         dprintf("buffering %d bytes\n", size - offset); | ||||||
|         buffered_append(s, buf + offset, size - offset); |         buffered_append(s, buf + offset, size - offset); | ||||||
|         offset = size; |         offset = size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pos == 0 && size == 0) { |  | ||||||
|         DPRINTF("file is ready\n"); |  | ||||||
|         if (s->bytes_xfer <= s->xfer_limit) { |  | ||||||
|             DPRINTF("notifying client\n"); |  | ||||||
|             s->put_ready(s->opaque); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return offset; |     return offset; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -172,7 +164,7 @@ static int buffered_close(void *opaque) | |||||||
|     QEMUFileBuffered *s = opaque; |     QEMUFileBuffered *s = opaque; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     DPRINTF("closing\n"); |     dprintf("closing\n"); | ||||||
|  |  | ||||||
|     while (!s->has_error && s->buffer_size) { |     while (!s->has_error && s->buffer_size) { | ||||||
|         buffered_flush(s); |         buffered_flush(s); | ||||||
| @@ -206,23 +198,20 @@ static int buffered_rate_limit(void *opaque) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) | static size_t buffered_set_rate_limit(void *opaque, size_t new_rate) | ||||||
| { | { | ||||||
|     QEMUFileBuffered *s = opaque; |     QEMUFileBuffered *s = opaque; | ||||||
|  |  | ||||||
|     if (s->has_error) |     if (s->has_error) | ||||||
|         goto out; |         goto out; | ||||||
|  |  | ||||||
|     if (new_rate > SIZE_MAX) { |  | ||||||
|         new_rate = SIZE_MAX; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->xfer_limit = new_rate / 10; |     s->xfer_limit = new_rate / 10; | ||||||
|      |      | ||||||
| out: | out: | ||||||
|     return s->xfer_limit; |     return s->xfer_limit; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int64_t buffered_get_rate_limit(void *opaque) | static size_t buffered_get_rate_limit(void *opaque) | ||||||
| { | { | ||||||
|     QEMUFileBuffered *s = opaque; |     QEMUFileBuffered *s = opaque; | ||||||
|    |    | ||||||
| @@ -233,10 +222,8 @@ static void buffered_rate_tick(void *opaque) | |||||||
| { | { | ||||||
|     QEMUFileBuffered *s = opaque; |     QEMUFileBuffered *s = opaque; | ||||||
|  |  | ||||||
|     if (s->has_error) { |     if (s->has_error) | ||||||
|         buffered_close(s); |  | ||||||
|         return; |         return; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); |     qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -57,30 +57,6 @@ static void ppc_init_cacheline_sizes(void) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |  | ||||||
| #include <errno.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/sysctl.h> |  | ||||||
|  |  | ||||||
| static void ppc_init_cacheline_sizes(void) |  | ||||||
| { |  | ||||||
|     size_t len = 4; |  | ||||||
|     unsigned cacheline; |  | ||||||
|  |  | ||||||
|     if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { |  | ||||||
|         fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", |  | ||||||
|                 strerror(errno)); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_cache_conf.dcache_bsize = cacheline; |  | ||||||
|     qemu_cache_conf.icache_bsize = cacheline; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| void qemu_cache_utils_init(char **envp) | void qemu_cache_utils_init(char **envp) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ struct qemu_cache_conf { | |||||||
|  |  | ||||||
| extern struct qemu_cache_conf qemu_cache_conf; | extern struct qemu_cache_conf qemu_cache_conf; | ||||||
|  |  | ||||||
| void qemu_cache_utils_init(char **envp); | extern void qemu_cache_utils_init(char **envp); | ||||||
|  |  | ||||||
| /* mildly adjusted code from tcg-dyngen.c */ | /* mildly adjusted code from tcg-dyngen.c */ | ||||||
| static inline void flush_icache_range(unsigned long start, unsigned long stop) | static inline void flush_icache_range(unsigned long start, unsigned long stop) | ||||||
| @@ -34,7 +34,28 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop) | |||||||
|     asm volatile ("isync" : : : "memory"); |     asm volatile ("isync" : : : "memory"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Is this correct for PPC? | ||||||
|  |  */ | ||||||
|  | static inline void dma_flush_range(unsigned long start, unsigned long stop) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #elif defined(__ia64__) | ||||||
|  | static inline void flush_icache_range(unsigned long start, unsigned long stop) | ||||||
|  | { | ||||||
|  |     while (start < stop) { | ||||||
|  | 	asm volatile ("fc %0" :: "r"(start)); | ||||||
|  | 	start += 32; | ||||||
|  |     } | ||||||
|  |     asm volatile (";;sync.i;;srlz.i;;"); | ||||||
|  | } | ||||||
|  | #define dma_flush_range(start, end) flush_icache_range(start, end) | ||||||
|  | #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) | ||||||
| #else | #else | ||||||
|  | static inline void dma_flush_range(unsigned long start, unsigned long stop) | ||||||
|  | { | ||||||
|  | } | ||||||
| #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) | #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,9 +5,6 @@ | |||||||
|  * |  * | ||||||
|  * Authors: |  * Authors: | ||||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> |  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  */ |  */ | ||||||
| #include <check.h> | #include <check.h> | ||||||
|  |  | ||||||
| @@ -50,7 +47,7 @@ START_TEST(qdict_put_obj_test) | |||||||
|     qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num))); |     qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num))); | ||||||
|  |  | ||||||
|     fail_unless(qdict_size(qdict) == 1); |     fail_unless(qdict_size(qdict) == 1); | ||||||
|     ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); |     ent = QLIST_FIRST(&qdict->table[12345 % QDICT_HASH_SIZE]); | ||||||
|     qi = qobject_to_qint(ent->value); |     qi = qobject_to_qint(ent->value); | ||||||
|     fail_unless(qint_get_int(qi) == num); |     fail_unless(qint_get_int(qi) == num); | ||||||
|  |  | ||||||
| @@ -194,36 +191,6 @@ START_TEST(qobject_to_qdict_test) | |||||||
| } | } | ||||||
| END_TEST | END_TEST | ||||||
|  |  | ||||||
| START_TEST(qdict_iterapi_test) |  | ||||||
| { |  | ||||||
|     int count; |  | ||||||
|     const QDictEntry *ent; |  | ||||||
|  |  | ||||||
|     fail_unless(qdict_first(tests_dict) == NULL); |  | ||||||
|  |  | ||||||
|     qdict_put(tests_dict, "key1", qint_from_int(1)); |  | ||||||
|     qdict_put(tests_dict, "key2", qint_from_int(2)); |  | ||||||
|     qdict_put(tests_dict, "key3", qint_from_int(3)); |  | ||||||
|  |  | ||||||
|     count = 0; |  | ||||||
|     for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ |  | ||||||
|         fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); |  | ||||||
|         count++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fail_unless(count == qdict_size(tests_dict)); |  | ||||||
|  |  | ||||||
|     /* Do it again to test restarting */ |  | ||||||
|     count = 0; |  | ||||||
|     for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ |  | ||||||
|         fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); |  | ||||||
|         count++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fail_unless(count == qdict_size(tests_dict)); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Errors test-cases |  * Errors test-cases | ||||||
|  */ |  */ | ||||||
| @@ -368,7 +335,6 @@ static Suite *qdict_suite(void) | |||||||
|     tcase_add_test(qdict_public2_tcase, qdict_haskey_test); |     tcase_add_test(qdict_public2_tcase, qdict_haskey_test); | ||||||
|     tcase_add_test(qdict_public2_tcase, qdict_del_test); |     tcase_add_test(qdict_public2_tcase, qdict_del_test); | ||||||
|     tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test); |     tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test); | ||||||
|     tcase_add_test(qdict_public2_tcase, qdict_iterapi_test); |  | ||||||
|  |  | ||||||
|     qdict_errors_tcase = tcase_create("Errors"); |     qdict_errors_tcase = tcase_create("Errors"); | ||||||
|     suite_add_tcase(s, qdict_errors_tcase); |     suite_add_tcase(s, qdict_errors_tcase); | ||||||
|   | |||||||
| @@ -1,6 +1,11 @@ | |||||||
| /* | /* | ||||||
|  * QFloat unit-tests. |  * QFloat unit-tests. | ||||||
|  * |  * | ||||||
|  |  * Copyright (C) 2009 Red Hat Inc. | ||||||
|  |  * | ||||||
|  |  * Authors: | ||||||
|  |  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
|  |  * | ||||||
|  * Copyright IBM, Corp. 2009 |  * Copyright IBM, Corp. 2009 | ||||||
|  * |  * | ||||||
|  * Authors: |  * Authors: | ||||||
|   | |||||||
| @@ -5,9 +5,6 @@ | |||||||
|  * |  * | ||||||
|  * Authors: |  * Authors: | ||||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> |  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |  | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  | ||||||
|  */ |  */ | ||||||
| #include <check.h> | #include <check.h> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										115
									
								
								check-qjson.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								check-qjson.c
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| #include <check.h> | #include <check.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  |  | ||||||
| #include "qstring.h" | #include "qstring.h" | ||||||
| #include "qint.h" | #include "qint.h" | ||||||
| @@ -28,13 +29,6 @@ START_TEST(escaped_string) | |||||||
|         const char *decoded; |         const char *decoded; | ||||||
|         int skip; |         int skip; | ||||||
|     } test_cases[] = { |     } test_cases[] = { | ||||||
|         { "\"\\b\"", "\b" }, |  | ||||||
|         { "\"\\f\"", "\f" }, |  | ||||||
|         { "\"\\n\"", "\n" }, |  | ||||||
|         { "\"\\r\"", "\r" }, |  | ||||||
|         { "\"\\t\"", "\t" }, |  | ||||||
|         { "\"\\/\"", "\\/" }, |  | ||||||
|         { "\"\\\\\"", "\\" }, |  | ||||||
|         { "\"\\\"\"", "\"" }, |         { "\"\\\"\"", "\"" }, | ||||||
|         { "\"hello world \\\"embedded string\\\"\"", |         { "\"hello world \\\"embedded string\\\"\"", | ||||||
|           "hello world \"embedded string\"" }, |           "hello world \"embedded string\"" }, | ||||||
| @@ -55,14 +49,11 @@ START_TEST(escaped_string) | |||||||
|         fail_unless(qobject_type(obj) == QTYPE_QSTRING); |         fail_unless(qobject_type(obj) == QTYPE_QSTRING); | ||||||
|          |          | ||||||
|         str = qobject_to_qstring(obj); |         str = qobject_to_qstring(obj); | ||||||
|         fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0, |         fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); | ||||||
|                     "%s != %s\n", qstring_get_str(str), test_cases[i].decoded); |  | ||||||
|  |  | ||||||
|         if (test_cases[i].skip == 0) { |         if (test_cases[i].skip == 0) { | ||||||
|             str = qobject_to_json(obj); |             str = qobject_to_json(obj); | ||||||
|             fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0, |             fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); | ||||||
|                         "%s != %s\n", qstring_get_str(str), |  | ||||||
|                                       test_cases[i].encoded); |  | ||||||
|  |  | ||||||
|             qobject_decref(obj); |             qobject_decref(obj); | ||||||
|         } |         } | ||||||
| @@ -637,92 +628,11 @@ START_TEST(simple_varargs) | |||||||
| } | } | ||||||
| END_TEST | END_TEST | ||||||
|  |  | ||||||
| START_TEST(empty_input) |  | ||||||
| { |  | ||||||
|     const char *empty = ""; |  | ||||||
|  |  | ||||||
|     QObject *obj = qobject_from_json(empty); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_string) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("\"abc"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_sq_string) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("'abc"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_escape) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("\"abc\\\""); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_array) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("[32"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_array_comma) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("[32,"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(invalid_array_comma) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("[32,}"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_dict) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("{'abc':32"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_dict_comma) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("{'abc':32,"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| #if 0 |  | ||||||
| START_TEST(invalid_dict_comma) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("{'abc':32,}"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
|  |  | ||||||
| START_TEST(unterminated_literal) |  | ||||||
| { |  | ||||||
|     QObject *obj = qobject_from_json("nul"); |  | ||||||
|     fail_unless(obj == NULL); |  | ||||||
| } |  | ||||||
| END_TEST |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static Suite *qjson_suite(void) | static Suite *qjson_suite(void) | ||||||
| { | { | ||||||
|     Suite *suite; |     Suite *suite; | ||||||
|     TCase *string_literals, *number_literals, *keyword_literals; |     TCase *string_literals, *number_literals, *keyword_literals; | ||||||
|     TCase *dicts, *lists, *whitespace, *varargs, *errors; |     TCase *dicts, *lists, *whitespace, *varargs; | ||||||
|  |  | ||||||
|     string_literals = tcase_create("String Literals"); |     string_literals = tcase_create("String Literals"); | ||||||
|     tcase_add_test(string_literals, simple_string); |     tcase_add_test(string_literals, simple_string); | ||||||
| @@ -748,22 +658,6 @@ static Suite *qjson_suite(void) | |||||||
|     varargs = tcase_create("Varargs"); |     varargs = tcase_create("Varargs"); | ||||||
|     tcase_add_test(varargs, simple_varargs); |     tcase_add_test(varargs, simple_varargs); | ||||||
|  |  | ||||||
|     errors = tcase_create("Invalid JSON"); |  | ||||||
|     tcase_add_test(errors, empty_input); |  | ||||||
|     tcase_add_test(errors, unterminated_string); |  | ||||||
|     tcase_add_test(errors, unterminated_escape); |  | ||||||
|     tcase_add_test(errors, unterminated_sq_string); |  | ||||||
|     tcase_add_test(errors, unterminated_array); |  | ||||||
|     tcase_add_test(errors, unterminated_array_comma); |  | ||||||
|     tcase_add_test(errors, invalid_array_comma); |  | ||||||
|     tcase_add_test(errors, unterminated_dict); |  | ||||||
|     tcase_add_test(errors, unterminated_dict_comma); |  | ||||||
| #if 0 |  | ||||||
|     /* FIXME: this print parse error messages on stderr.  */ |  | ||||||
|     tcase_add_test(errors, invalid_dict_comma); |  | ||||||
|     tcase_add_test(errors, unterminated_literal); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     suite = suite_create("QJSON test-suite"); |     suite = suite_create("QJSON test-suite"); | ||||||
|     suite_add_tcase(suite, string_literals); |     suite_add_tcase(suite, string_literals); | ||||||
|     suite_add_tcase(suite, number_literals); |     suite_add_tcase(suite, number_literals); | ||||||
| @@ -772,7 +666,6 @@ static Suite *qjson_suite(void) | |||||||
|     suite_add_tcase(suite, lists); |     suite_add_tcase(suite, lists); | ||||||
|     suite_add_tcase(suite, whitespace); |     suite_add_tcase(suite, whitespace); | ||||||
|     suite_add_tcase(suite, varargs); |     suite_add_tcase(suite, varargs); | ||||||
|     suite_add_tcase(suite, errors); |  | ||||||
|  |  | ||||||
|     return suite; |     return suite; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ | |||||||
|  * Authors: |  * Authors: | ||||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> |  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||||
|  * |  * | ||||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |  * This work is licensed under the terms of the GNU GPL, version 2. See | ||||||
|  * See the COPYING.LIB file in the top-level directory. |  * the COPYING file in the top-level directory. | ||||||
|  */ |  */ | ||||||
| #include <check.h> | #include <check.h> | ||||||
|  |  | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user