Compare commits
469 Commits
pull-input
...
pull-audio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a824e4d74 | ||
|
|
4c565674a2 | ||
|
|
ca89f72092 | ||
|
|
56821559f0 | ||
|
|
2ccbd47c1d | ||
|
|
adb354dd1e | ||
|
|
a764040cc8 | ||
|
|
3936161f1f | ||
|
|
66453cff9e | ||
|
|
2fa356629e | ||
|
|
be9b23c4a5 | ||
|
|
22951aaaeb | ||
|
|
99e15582de | ||
|
|
897eee242b | ||
|
|
17e8f54126 | ||
|
|
8ae5059df5 | ||
|
|
74c505c6fd | ||
|
|
ae3ac6caca | ||
|
|
8f04d26a4e | ||
|
|
cae9d4cdd4 | ||
|
|
bdbae0ef01 | ||
|
|
f4afad3878 | ||
|
|
731fec79ae | ||
|
|
68aecefcd4 | ||
|
|
0980a1c0e2 | ||
|
|
c803129ce5 | ||
|
|
a081ab8f7b | ||
|
|
040e99686a | ||
|
|
6c4672cae7 | ||
|
|
642c1e0546 | ||
|
|
c1ce65f710 | ||
|
|
8ca97a1ff7 | ||
|
|
8ab5700ca0 | ||
|
|
950b31dd17 | ||
|
|
e4f4fb1eca | ||
|
|
e90f2a8c3e | ||
|
|
599c9cb641 | ||
|
|
fefb28a471 | ||
|
|
1bfe5f0586 | ||
|
|
bac3b21218 | ||
|
|
aa3544c371 | ||
|
|
795c40b8bd | ||
|
|
bb890ed551 | ||
|
|
927d663819 | ||
|
|
2bf3aa85f0 | ||
|
|
01cd90b641 | ||
|
|
c0c24b9554 | ||
|
|
d9506cab36 | ||
|
|
1ff7c5986a | ||
|
|
2bb5c936c5 | ||
|
|
28256d8246 | ||
|
|
2125e5ea6e | ||
|
|
3ce6a729b5 | ||
|
|
cdece0467c | ||
|
|
456af34629 | ||
|
|
34db05e7ff | ||
|
|
675a775633 | ||
|
|
612fc05ad2 | ||
|
|
3c2bdbc1e4 | ||
|
|
327c8ebd70 | ||
|
|
96cd599818 | ||
|
|
6a8d834986 | ||
|
|
eba0161990 | ||
|
|
8a813c9868 | ||
|
|
384d9d554a | ||
|
|
7a95434e0c | ||
|
|
ba9915e1f8 | ||
|
|
43ad494c04 | ||
|
|
2f77ec7390 | ||
|
|
57e2d417d3 | ||
|
|
34257c2117 | ||
|
|
143021b26f | ||
|
|
cb32f179e0 | ||
|
|
aa3513176f | ||
|
|
a380f9db96 | ||
|
|
58d2a9aef4 | ||
|
|
b3995c23ed | ||
|
|
ac9707eaf6 | ||
|
|
632056651a | ||
|
|
0fc37a8b0c | ||
|
|
47b9f4d5a4 | ||
|
|
24b09d9d8b | ||
|
|
3968260811 | ||
|
|
a6215749dc | ||
|
|
538fad597d | ||
|
|
1e8e69f08b | ||
|
|
a7c1fadf00 | ||
|
|
4dba4d6fef | ||
|
|
303a9ab887 | ||
|
|
1807aaa565 | ||
|
|
44977a8fe7 | ||
|
|
190b2422e6 | ||
|
|
5bf83628dc | ||
|
|
3a8760664d | ||
|
|
b54933eed5 | ||
|
|
3753e255da | ||
|
|
5651743c90 | ||
|
|
321d1dba8b | ||
|
|
ee29d6adef | ||
|
|
33c53c54e4 | ||
|
|
7c9209e7bf | ||
|
|
761d0f97a4 | ||
|
|
aa612b364e | ||
|
|
99f9aeba5d | ||
|
|
66849dcfbe | ||
|
|
ee56264af8 | ||
|
|
bd4a683505 | ||
|
|
a92ff8c123 | ||
|
|
bb1599b64c | ||
|
|
bc8c946f72 | ||
|
|
151c8e608e | ||
|
|
e1913dbb58 | ||
|
|
9f728c7940 | ||
|
|
c19f4fbce1 | ||
|
|
08b277ac46 | ||
|
|
ab808276f8 | ||
|
|
46baa9007f | ||
|
|
2941020a47 | ||
|
|
419fcdec3c | ||
|
|
1171ae9a5b | ||
|
|
ec78f8114b | ||
|
|
482dfe9a9e | ||
|
|
3b8a8557f7 | ||
|
|
6accfb7823 | ||
|
|
afed5a5a70 | ||
|
|
4ccf5826f9 | ||
|
|
722387e78d | ||
|
|
ea2650724c | ||
|
|
af9b20e8d2 | ||
|
|
7c88e65d9e | ||
|
|
64c2a8f6d3 | ||
|
|
bd4c1bfe3e | ||
|
|
93b2a8cb0b | ||
|
|
0b8497f08c | ||
|
|
ea089eebbd | ||
|
|
d9c34f9c6c | ||
|
|
17d3d0e2d9 | ||
|
|
46de5913b6 | ||
|
|
63baf8bf01 | ||
|
|
fda4096fca | ||
|
|
3bfe57165b | ||
|
|
0f203430dd | ||
|
|
ecc1f5adee | ||
|
|
4ed3d478c6 | ||
|
|
d541e201bd | ||
|
|
8dd30c86dd | ||
|
|
d2cb36af2b | ||
|
|
f10ee139ad | ||
|
|
fbaa6bb3d3 | ||
|
|
e249d51952 | ||
|
|
d9ca2214bd | ||
|
|
06cc5e2b2d | ||
|
|
fdfab37dfe | ||
|
|
3ef9521893 | ||
|
|
4341df8a83 | ||
|
|
4c41cb4955 | ||
|
|
bbd995d830 | ||
|
|
b32cbae111 | ||
|
|
40812d9373 | ||
|
|
430b26a82d | ||
|
|
3dc834f879 | ||
|
|
63188c2450 | ||
|
|
d157ed5f72 | ||
|
|
e0ef439588 | ||
|
|
6f3c90af3c | ||
|
|
4401fdc77c | ||
|
|
1bce6b4ce3 | ||
|
|
698bdfa07d | ||
|
|
c03e7ef12a | ||
|
|
b91127edd0 | ||
|
|
22d5cd82e9 | ||
|
|
9c5e6594f1 | ||
|
|
38701b6aef | ||
|
|
cfa1a5723f | ||
|
|
4417ab7adf | ||
|
|
ace21a5875 | ||
|
|
aa93c834f9 | ||
|
|
293073a56c | ||
|
|
564a6b6938 | ||
|
|
92413c16be | ||
|
|
de9efdb334 | ||
|
|
ba8980784d | ||
|
|
244a566810 | ||
|
|
e8c1094a0e | ||
|
|
13461fdba6 | ||
|
|
fc0932fdcf | ||
|
|
9c77fec2d3 | ||
|
|
1c3a555c35 | ||
|
|
16b48d5d66 | ||
|
|
2420d369a2 | ||
|
|
7ceb4fc114 | ||
|
|
8b084489b0 | ||
|
|
d5b8336a62 | ||
|
|
ecffa63421 | ||
|
|
4797aeabdc | ||
|
|
55e5a3b65e | ||
|
|
aca7063a56 | ||
|
|
459571f7b2 | ||
|
|
a8d16f9ca2 | ||
|
|
335e993784 | ||
|
|
ffd1a5a25c | ||
|
|
5a9347c673 | ||
|
|
5176196c32 | ||
|
|
bfc56535f7 | ||
|
|
ca7f544123 | ||
|
|
2dd285b5f3 | ||
|
|
344a68bf9d | ||
|
|
d755defd5d | ||
|
|
9cfa7ab939 | ||
|
|
e9edd931eb | ||
|
|
eaf87a3976 | ||
|
|
5f3066d8b1 | ||
|
|
9bf502fe12 | ||
|
|
5c6b487d67 | ||
|
|
545d6e2b5c | ||
|
|
d5fee0bbe6 | ||
|
|
c88305027d | ||
|
|
c6fd28fd57 | ||
|
|
6de833070c | ||
|
|
53ecf09df3 | ||
|
|
b50de5cd77 | ||
|
|
fbe9214318 | ||
|
|
0806b30c8d | ||
|
|
229e16fd24 | ||
|
|
063cb7cbc9 | ||
|
|
139d9023f1 | ||
|
|
f0b0685d66 | ||
|
|
a3e53273ad | ||
|
|
4771df23ed | ||
|
|
7f9af1abdc | ||
|
|
253ce7b2cf | ||
|
|
a1a636b8b4 | ||
|
|
8b12e48950 | ||
|
|
153eba4726 | ||
|
|
ef0e8fc768 | ||
|
|
465238d9f8 | ||
|
|
98e753a6e5 | ||
|
|
640601c7cb | ||
|
|
60cd11024f | ||
|
|
cb51ac2ffe | ||
|
|
5ee8534731 | ||
|
|
f465706e59 | ||
|
|
1effe6ad5e | ||
|
|
e1ae9fb6c2 | ||
|
|
36c697bda5 | ||
|
|
3ecb29a328 | ||
|
|
e9c6ab62c7 | ||
|
|
fafa2e6702 | ||
|
|
b7d5a9c2c6 | ||
|
|
6f75023ab8 | ||
|
|
3baa0a6a65 | ||
|
|
6516367fc0 | ||
|
|
1d29b5b049 | ||
|
|
3ba34a7022 | ||
|
|
09d352042f | ||
|
|
bcd711feb0 | ||
|
|
76d20ea0f1 | ||
|
|
a37278169d | ||
|
|
e4a3507e86 | ||
|
|
899833cd65 | ||
|
|
c6a9a9f575 | ||
|
|
dcd3b25d65 | ||
|
|
b13d2ff3de | ||
|
|
daa5a72eba | ||
|
|
c5e397df9e | ||
|
|
aa3b167f21 | ||
|
|
8b2e41d733 | ||
|
|
ed0ba0f47e | ||
|
|
6c02258e14 | ||
|
|
10e37839ed | ||
|
|
0c099fa7e9 | ||
|
|
bd269ebc82 | ||
|
|
62cf396b5d | ||
|
|
dfd100f242 | ||
|
|
4626a19c86 | ||
|
|
0785bd7a7c | ||
|
|
4db5c619a2 | ||
|
|
5229564b83 | ||
|
|
ccb61bdd73 | ||
|
|
28934e0c75 | ||
|
|
ff6ed7141d | ||
|
|
46f5ac205a | ||
|
|
a92c21591b | ||
|
|
de6e7951fe | ||
|
|
a2f3453ebc | ||
|
|
8f16de18f6 | ||
|
|
9edd5338a2 | ||
|
|
cb69166bb8 | ||
|
|
de4598f0c5 | ||
|
|
7ed57b6622 | ||
|
|
1c5d506101 | ||
|
|
2fd463854c | ||
|
|
6225820136 | ||
|
|
32543dbb67 | ||
|
|
f3fddaf60b | ||
|
|
4bf43122db | ||
|
|
3c76c606da | ||
|
|
ec45bbe5f1 | ||
|
|
9879f5ac62 | ||
|
|
21a9ad2f15 | ||
|
|
6b1de1484e | ||
|
|
46bbbec2d3 | ||
|
|
ed1fcd0009 | ||
|
|
61f7c6a0c2 | ||
|
|
2d812d6dff | ||
|
|
d0e31a105e | ||
|
|
2f5a5f5774 | ||
|
|
dd1559bb26 | ||
|
|
f03f9f0c10 | ||
|
|
4aee86c60a | ||
|
|
bc56fd3a23 | ||
|
|
dd76c10f9f | ||
|
|
317134bb54 | ||
|
|
4f3652b3aa | ||
|
|
4f225f343a | ||
|
|
95615ce5a1 | ||
|
|
eae0f54334 | ||
|
|
622e42a71f | ||
|
|
9ff3a5e677 | ||
|
|
7a6ae2cffc | ||
|
|
6668a2af21 | ||
|
|
fdf6fab4df | ||
|
|
bde4d9205e | ||
|
|
6796b4008b | ||
|
|
8a3c3d996e | ||
|
|
6e9389563e | ||
|
|
31f5a726b5 | ||
|
|
f68826989c | ||
|
|
c8c33fca88 | ||
|
|
24dfa9fa2f | ||
|
|
aab9e87e7a | ||
|
|
49e00a1870 | ||
|
|
00fcd100c3 | ||
|
|
8eb57ae3f9 | ||
|
|
b290f3b12e | ||
|
|
12a95f320a | ||
|
|
79c8db5a13 | ||
|
|
bb1490486d | ||
|
|
d47fb5d17c | ||
|
|
9dbaa4ce58 | ||
|
|
a86c83d704 | ||
|
|
e24ef0db4f | ||
|
|
e0b6767b6c | ||
|
|
da2d19b080 | ||
|
|
123676e989 | ||
|
|
bc763d7144 | ||
|
|
bbcde969b2 | ||
|
|
e1f98fe48a | ||
|
|
1e13edf355 | ||
|
|
c5749f7c0b | ||
|
|
6061162e52 | ||
|
|
474628e837 | ||
|
|
2f5d45a150 | ||
|
|
f8df5f9221 | ||
|
|
315dd72d75 | ||
|
|
bed3bb9b7e | ||
|
|
6361813527 | ||
|
|
1db9d8e501 | ||
|
|
be07b0ace8 | ||
|
|
9e8b3009b7 | ||
|
|
e65a27209c | ||
|
|
4996241a4a | ||
|
|
2dc95b4cac | ||
|
|
b847620540 | ||
|
|
ff20b0a3d8 | ||
|
|
6c15e9bfb6 | ||
|
|
ae92cbd542 | ||
|
|
6683061873 | ||
|
|
d905bb7b74 | ||
|
|
d9c7d137c8 | ||
|
|
52b2620512 | ||
|
|
c34c2f3701 | ||
|
|
fd4144d413 | ||
|
|
062d81f0e9 | ||
|
|
6b6712efcc | ||
|
|
9ea5ada76f | ||
|
|
d52ccc0eca | ||
|
|
3fab7b675a | ||
|
|
b3b40917c7 | ||
|
|
e14e09c945 | ||
|
|
ade339896b | ||
|
|
17a1694a56 | ||
|
|
135f5ae197 | ||
|
|
4bf7792aae | ||
|
|
1c742f2b8e | ||
|
|
222e0356fa | ||
|
|
0af81c56bf | ||
|
|
9887e22155 | ||
|
|
8f7e2c2cb7 | ||
|
|
7852b53acc | ||
|
|
8ec734d027 | ||
|
|
7f643fb53a | ||
|
|
7bf10b1de2 | ||
|
|
d2a4a01fa4 | ||
|
|
3795f18095 | ||
|
|
d8586afd8b | ||
|
|
4a796e979e | ||
|
|
882ab9d615 | ||
|
|
2004429e9b | ||
|
|
b6c6919e21 | ||
|
|
68883a4078 | ||
|
|
7bdfd907e7 | ||
|
|
3268a845f4 | ||
|
|
5eaa8e1e0f | ||
|
|
8ac5535145 | ||
|
|
f4d1414a93 | ||
|
|
48a1b62baa | ||
|
|
acf57591c0 | ||
|
|
b75c958d88 | ||
|
|
d89e71e873 | ||
|
|
4597992f62 | ||
|
|
ef3f5b9e7f | ||
|
|
461a4b944f | ||
|
|
6e4e6f0d40 | ||
|
|
38d49e8c15 | ||
|
|
15126cba86 | ||
|
|
b19456dd0e | ||
|
|
6103451aeb | ||
|
|
77af8a2b95 | ||
|
|
99efaa2696 | ||
|
|
c55144ec32 | ||
|
|
7a9762bf89 | ||
|
|
82ca394194 | ||
|
|
9dd7823b70 | ||
|
|
95fa1af854 | ||
|
|
9a22473c70 | ||
|
|
cfe2124a7f | ||
|
|
5559716c98 | ||
|
|
b038411d85 | ||
|
|
bd1badf457 | ||
|
|
7104bae9de | ||
|
|
5fc0fe383f | ||
|
|
262fbae692 | ||
|
|
cc02e89eb4 | ||
|
|
4f38497b0f | ||
|
|
db933fbe06 | ||
|
|
9f1b92add2 | ||
|
|
504c205a0d | ||
|
|
d4a7f45ec9 | ||
|
|
048c5fd1bf | ||
|
|
f59adb3256 | ||
|
|
4bff28b81a | ||
|
|
ed3d2ec98a | ||
|
|
55b9392b98 | ||
|
|
2b4c0a20fb | ||
|
|
48758a8473 | ||
|
|
3258b91141 | ||
|
|
f13ce1be35 | ||
|
|
de234897b6 | ||
|
|
24dfdfd0ff | ||
|
|
8248169497 | ||
|
|
fea41826db | ||
|
|
03a0aa37d2 | ||
|
|
e914404efb | ||
|
|
242e496b39 | ||
|
|
1ea88d359f | ||
|
|
42dc10f17a | ||
|
|
4a0082401a | ||
|
|
312758e237 | ||
|
|
69404d9e47 | ||
|
|
0042fd3663 | ||
|
|
9fd77f9972 | ||
|
|
362b3786eb | ||
|
|
0d5e0bb2f7 | ||
|
|
ad02b7af0c | ||
|
|
0731a50feb | ||
|
|
3fee028d1e | ||
|
|
356a2db3c6 | ||
|
|
1d7cf18d79 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -34,3 +34,6 @@
|
|||||||
[submodule "roms/skiboot"]
|
[submodule "roms/skiboot"]
|
||||||
path = roms/skiboot
|
path = roms/skiboot
|
||||||
url = git://git.qemu.org/skiboot.git
|
url = git://git.qemu.org/skiboot.git
|
||||||
|
[submodule "roms/QemuMacDrivers"]
|
||||||
|
path = roms/QemuMacDrivers
|
||||||
|
url = git://git.qemu.org/QemuMacDrivers.git
|
||||||
|
|||||||
@@ -86,9 +86,6 @@ matrix:
|
|||||||
- env: CONFIG="--enable-trace-backends=ust"
|
- env: CONFIG="--enable-trace-backends=ust"
|
||||||
TEST_CMD=""
|
TEST_CMD=""
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
- env: CONFIG="--with-coroutine=gthread"
|
|
||||||
TEST_CMD=""
|
|
||||||
compiler: gcc
|
|
||||||
- env: CONFIG=""
|
- env: CONFIG=""
|
||||||
os: osx
|
os: osx
|
||||||
compiler: clang
|
compiler: clang
|
||||||
@@ -191,7 +188,7 @@ matrix:
|
|||||||
compiler: none
|
compiler: none
|
||||||
env:
|
env:
|
||||||
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
||||||
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user --with-coroutine=gthread"
|
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
|
||||||
- TEST_CMD=""
|
- TEST_CMD=""
|
||||||
before_script:
|
before_script:
|
||||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
|
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
|
||||||
|
|||||||
36
MAINTAINERS
36
MAINTAINERS
@@ -12,6 +12,8 @@ consult qemu-devel and not any specific individual privately.
|
|||||||
Descriptions of section entries:
|
Descriptions of section entries:
|
||||||
|
|
||||||
M: Mail patches to: FullName <address@domain>
|
M: Mail patches to: FullName <address@domain>
|
||||||
|
R: Designated reviewer: FullName <address@domain>
|
||||||
|
These reviewers should be CCed on patches.
|
||||||
L: Mailing list that is relevant to this area
|
L: Mailing list that is relevant to this area
|
||||||
W: Web-page with status/info
|
W: Web-page with status/info
|
||||||
Q: Patchwork web based patch tracking system site
|
Q: Patchwork web based patch tracking system site
|
||||||
@@ -196,8 +198,8 @@ F: hw/nios2/
|
|||||||
F: disas/nios2.c
|
F: disas/nios2.c
|
||||||
|
|
||||||
OpenRISC
|
OpenRISC
|
||||||
M: Jia Liu <proljc@gmail.com>
|
M: Stafford Horne <shorne@gmail.com>
|
||||||
S: Maintained
|
S: Odd Fixes
|
||||||
F: target/openrisc/
|
F: target/openrisc/
|
||||||
F: hw/openrisc/
|
F: hw/openrisc/
|
||||||
F: tests/tcg/openrisc/
|
F: tests/tcg/openrisc/
|
||||||
@@ -352,6 +354,12 @@ L: qemu-devel@nongnu.org
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
F: *posix*
|
F: *posix*
|
||||||
|
|
||||||
|
NETBSD
|
||||||
|
L: qemu-devel@nongnu.org
|
||||||
|
M: Kamil Rytarowski <kamil@netbsd.org>
|
||||||
|
S: Maintained
|
||||||
|
K: (?i)NetBSD
|
||||||
|
|
||||||
W32, W64
|
W32, W64
|
||||||
L: qemu-devel@nongnu.org
|
L: qemu-devel@nongnu.org
|
||||||
M: Stefan Weil <sw@weilnetz.de>
|
M: Stefan Weil <sw@weilnetz.de>
|
||||||
@@ -1168,6 +1176,7 @@ F: include/block/
|
|||||||
F: qemu-img*
|
F: qemu-img*
|
||||||
F: qemu-io*
|
F: qemu-io*
|
||||||
F: tests/qemu-iotests/
|
F: tests/qemu-iotests/
|
||||||
|
F: util/qemu-progress.c
|
||||||
T: git git://repo.or.cz/qemu/kevin.git block
|
T: git git://repo.or.cz/qemu/kevin.git block
|
||||||
|
|
||||||
Block I/O path
|
Block I/O path
|
||||||
@@ -1175,8 +1184,8 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
|||||||
M: Fam Zheng <famz@redhat.com>
|
M: Fam Zheng <famz@redhat.com>
|
||||||
L: qemu-block@nongnu.org
|
L: qemu-block@nongnu.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: async.c
|
F: util/async.c
|
||||||
F: aio-*.c
|
F: util/aio-*.c
|
||||||
F: block/io.c
|
F: block/io.c
|
||||||
F: migration/block*
|
F: migration/block*
|
||||||
F: include/block/aio.h
|
F: include/block/aio.h
|
||||||
@@ -1305,8 +1314,8 @@ Main loop
|
|||||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: cpus.c
|
F: cpus.c
|
||||||
F: main-loop.c
|
F: util/main-loop.c
|
||||||
F: qemu-timer.c
|
F: util/qemu-timer.c
|
||||||
F: vl.c
|
F: vl.c
|
||||||
|
|
||||||
Human Monitor (HMP)
|
Human Monitor (HMP)
|
||||||
@@ -1393,6 +1402,7 @@ S: Supported
|
|||||||
F: qobject/
|
F: qobject/
|
||||||
F: include/qapi/qmp/
|
F: include/qapi/qmp/
|
||||||
X: include/qapi/qmp/dispatch.h
|
X: include/qapi/qmp/dispatch.h
|
||||||
|
F: scripts/coccinelle/qobject.cocci
|
||||||
F: tests/check-qdict.c
|
F: tests/check-qdict.c
|
||||||
F: tests/check-qfloat.c
|
F: tests/check-qfloat.c
|
||||||
F: tests/check-qint.c
|
F: tests/check-qint.c
|
||||||
@@ -1484,6 +1494,7 @@ S: Maintained
|
|||||||
F: crypto/
|
F: crypto/
|
||||||
F: include/crypto/
|
F: include/crypto/
|
||||||
F: tests/test-crypto-*
|
F: tests/test-crypto-*
|
||||||
|
F: qemu.sasl
|
||||||
|
|
||||||
Coroutines
|
Coroutines
|
||||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
@@ -1546,6 +1557,18 @@ F: net/colo*
|
|||||||
F: net/filter-rewriter.c
|
F: net/filter-rewriter.c
|
||||||
F: net/filter-mirror.c
|
F: net/filter-mirror.c
|
||||||
|
|
||||||
|
Record/replay
|
||||||
|
M: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
|
||||||
|
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
W: http://wiki.qemu.org/Features/record-replay
|
||||||
|
S: Supported
|
||||||
|
F: replay/*
|
||||||
|
F: block/blkreplay.c
|
||||||
|
F: net/filter-replay.c
|
||||||
|
F: include/sysemu/replay.h
|
||||||
|
F: docs/replay.txt
|
||||||
|
F: stubs/replay.c
|
||||||
|
|
||||||
Usermode Emulation
|
Usermode Emulation
|
||||||
------------------
|
------------------
|
||||||
Overall
|
Overall
|
||||||
@@ -1562,6 +1585,7 @@ F: default-configs/*-bsd-user.mak
|
|||||||
|
|
||||||
Linux user
|
Linux user
|
||||||
M: Riku Voipio <riku.voipio@iki.fi>
|
M: Riku Voipio <riku.voipio@iki.fi>
|
||||||
|
R: Laurent Vivier <laurent@vivier.eu>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: linux-user/
|
F: linux-user/
|
||||||
F: default-configs/*-linux-user.mak
|
F: default-configs/*-linux-user.mak
|
||||||
|
|||||||
3
Makefile
3
Makefile
@@ -552,7 +552,8 @@ multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \
|
|||||||
s390-ccw.img \
|
s390-ccw.img \
|
||||||
spapr-rtas.bin slof.bin skiboot.lid \
|
spapr-rtas.bin slof.bin skiboot.lid \
|
||||||
palcode-clipper \
|
palcode-clipper \
|
||||||
u-boot.e500
|
u-boot.e500 \
|
||||||
|
qemu_vga.ndrv
|
||||||
else
|
else
|
||||||
BLOBS=
|
BLOBS=
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
|
|||||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||||
|
|
||||||
common-obj-y += migration/
|
common-obj-y += migration/
|
||||||
common-obj-y += page_cache.o #aio.o
|
|
||||||
|
|
||||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||||
|
|
||||||
|
|||||||
126
arch_init.c
126
arch_init.c
@@ -27,7 +27,7 @@
|
|||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/arch_init.h"
|
#include "sysemu/arch_init.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
@@ -85,130 +85,6 @@ int graphic_depth = 32;
|
|||||||
|
|
||||||
const uint32_t arch_type = QEMU_ARCH;
|
const uint32_t arch_type = QEMU_ARCH;
|
||||||
|
|
||||||
struct soundhw {
|
|
||||||
const char *name;
|
|
||||||
const char *descr;
|
|
||||||
int enabled;
|
|
||||||
int isa;
|
|
||||||
union {
|
|
||||||
int (*init_isa) (ISABus *bus);
|
|
||||||
int (*init_pci) (PCIBus *bus);
|
|
||||||
} init;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct soundhw soundhw[9];
|
|
||||||
static int soundhw_count;
|
|
||||||
|
|
||||||
void isa_register_soundhw(const char *name, const char *descr,
|
|
||||||
int (*init_isa)(ISABus *bus))
|
|
||||||
{
|
|
||||||
assert(soundhw_count < ARRAY_SIZE(soundhw) - 1);
|
|
||||||
soundhw[soundhw_count].name = name;
|
|
||||||
soundhw[soundhw_count].descr = descr;
|
|
||||||
soundhw[soundhw_count].isa = 1;
|
|
||||||
soundhw[soundhw_count].init.init_isa = init_isa;
|
|
||||||
soundhw_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pci_register_soundhw(const char *name, const char *descr,
|
|
||||||
int (*init_pci)(PCIBus *bus))
|
|
||||||
{
|
|
||||||
assert(soundhw_count < ARRAY_SIZE(soundhw) - 1);
|
|
||||||
soundhw[soundhw_count].name = name;
|
|
||||||
soundhw[soundhw_count].descr = descr;
|
|
||||||
soundhw[soundhw_count].isa = 0;
|
|
||||||
soundhw[soundhw_count].init.init_pci = init_pci;
|
|
||||||
soundhw_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void select_soundhw(const char *optarg)
|
|
||||||
{
|
|
||||||
struct soundhw *c;
|
|
||||||
|
|
||||||
if (is_help_option(optarg)) {
|
|
||||||
show_valid_cards:
|
|
||||||
|
|
||||||
if (soundhw_count) {
|
|
||||||
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");
|
|
||||||
} else {
|
|
||||||
printf("Machine has no user-selectable audio hardware "
|
|
||||||
"(it may or may not have always-present audio hardware).\n");
|
|
||||||
}
|
|
||||||
exit(!is_help_option(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) {
|
|
||||||
error_report("Unknown sound card name (too big to show)");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
error_report("Unknown sound card name `%.*s'",
|
|
||||||
(int) l, p);
|
|
||||||
}
|
|
||||||
bad_card = 1;
|
|
||||||
}
|
|
||||||
p += l + (e != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bad_card) {
|
|
||||||
goto show_valid_cards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_init(void)
|
|
||||||
{
|
|
||||||
struct soundhw *c;
|
|
||||||
ISABus *isa_bus = (ISABus *) object_resolve_path_type("", TYPE_ISA_BUS, NULL);
|
|
||||||
PCIBus *pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL);
|
|
||||||
|
|
||||||
for (c = soundhw; c->name; ++c) {
|
|
||||||
if (c->enabled) {
|
|
||||||
if (c->isa) {
|
|
||||||
if (!isa_bus) {
|
|
||||||
error_report("ISA bus not available for %s", c->name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
c->init.init_isa(isa_bus);
|
|
||||||
} else {
|
|
||||||
if (!pci_bus) {
|
|
||||||
error_report("PCI bus not available for %s", c->name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
c->init.init_pci(pci_bus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_available(void)
|
int kvm_available(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KVM
|
#ifdef CONFIG_KVM
|
||||||
|
|||||||
@@ -2028,6 +2028,8 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
|||||||
sw = sw1;
|
sw = sw1;
|
||||||
}
|
}
|
||||||
QLIST_REMOVE (cap, entries);
|
QLIST_REMOVE (cap, entries);
|
||||||
|
g_free (cap->hw.mix_buf);
|
||||||
|
g_free (cap->buf);
|
||||||
g_free (cap);
|
g_free (cap);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ static void wav_capture_destroy (void *opaque)
|
|||||||
WAVState *wav = opaque;
|
WAVState *wav = opaque;
|
||||||
|
|
||||||
AUD_del_capture (wav->cap, wav);
|
AUD_del_capture (wav->cap, wav);
|
||||||
|
g_free (wav);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wav_capture_info (void *opaque)
|
static void wav_capture_info (void *opaque)
|
||||||
|
|||||||
221
block.c
221
block.c
@@ -192,11 +192,20 @@ void path_combine(char *dest, int dest_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns whether the image file is opened as read-only. Note that this can
|
||||||
|
* return false and writing to the image file is still not possible because the
|
||||||
|
* image is inactivated. */
|
||||||
bool bdrv_is_read_only(BlockDriverState *bs)
|
bool bdrv_is_read_only(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bs->read_only;
|
return bs->read_only;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns whether the image file can be written to right now */
|
||||||
|
bool bdrv_is_writable(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
return !bdrv_is_read_only(bs) && !(bs->open_flags & BDRV_O_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
||||||
{
|
{
|
||||||
/* Do not set read_only if copy_on_read is enabled */
|
/* Do not set read_only if copy_on_read is enabled */
|
||||||
@@ -762,6 +771,13 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
|
|||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bdrv_child_cb_inactivate(BdrvChild *child)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = child->opaque;
|
||||||
|
assert(bs->open_flags & BDRV_O_INACTIVE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the options and flags that a temporary snapshot should get, based on
|
* Returns the options and flags that a temporary snapshot should get, based on
|
||||||
* the originally requested flags (the originally requested image will have
|
* the originally requested flags (the originally requested image will have
|
||||||
@@ -800,6 +816,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
|||||||
* the parent. */
|
* the parent. */
|
||||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||||
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
|
||||||
|
|
||||||
/* Inherit the read-only option from the parent if it's not set */
|
/* Inherit the read-only option from the parent if it's not set */
|
||||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
|
||||||
@@ -821,6 +838,7 @@ const BdrvChildRole child_file = {
|
|||||||
.inherit_options = bdrv_inherited_options,
|
.inherit_options = bdrv_inherited_options,
|
||||||
.drained_begin = bdrv_child_cb_drained_begin,
|
.drained_begin = bdrv_child_cb_drained_begin,
|
||||||
.drained_end = bdrv_child_cb_drained_end,
|
.drained_end = bdrv_child_cb_drained_end,
|
||||||
|
.inactivate = bdrv_child_cb_inactivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -842,6 +860,7 @@ const BdrvChildRole child_format = {
|
|||||||
.inherit_options = bdrv_inherited_fmt_options,
|
.inherit_options = bdrv_inherited_fmt_options,
|
||||||
.drained_begin = bdrv_child_cb_drained_begin,
|
.drained_begin = bdrv_child_cb_drained_begin,
|
||||||
.drained_end = bdrv_child_cb_drained_end,
|
.drained_end = bdrv_child_cb_drained_end,
|
||||||
|
.inactivate = bdrv_child_cb_inactivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_backing_attach(BdrvChild *c)
|
static void bdrv_backing_attach(BdrvChild *c)
|
||||||
@@ -908,6 +927,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
|
|||||||
* which is only applied on the top level (BlockBackend) */
|
* which is only applied on the top level (BlockBackend) */
|
||||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||||
|
qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
|
||||||
|
|
||||||
/* backing files always opened read-only */
|
/* backing files always opened read-only */
|
||||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
|
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
|
||||||
@@ -926,6 +946,7 @@ const BdrvChildRole child_backing = {
|
|||||||
.inherit_options = bdrv_backing_options,
|
.inherit_options = bdrv_backing_options,
|
||||||
.drained_begin = bdrv_child_cb_drained_begin,
|
.drained_begin = bdrv_child_cb_drained_begin,
|
||||||
.drained_end = bdrv_child_cb_drained_end,
|
.drained_end = bdrv_child_cb_drained_end,
|
||||||
|
.inactivate = bdrv_child_cb_inactivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||||
@@ -974,16 +995,14 @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
|
|||||||
static void update_options_from_flags(QDict *options, int flags)
|
static void update_options_from_flags(QDict *options, int flags)
|
||||||
{
|
{
|
||||||
if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) {
|
if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) {
|
||||||
qdict_put(options, BDRV_OPT_CACHE_DIRECT,
|
qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE);
|
||||||
qbool_from_bool(flags & BDRV_O_NOCACHE));
|
|
||||||
}
|
}
|
||||||
if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) {
|
if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) {
|
||||||
qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH,
|
qdict_put_bool(options, BDRV_OPT_CACHE_NO_FLUSH,
|
||||||
qbool_from_bool(flags & BDRV_O_NO_FLUSH));
|
flags & BDRV_O_NO_FLUSH);
|
||||||
}
|
}
|
||||||
if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
|
if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
|
||||||
qdict_put(options, BDRV_OPT_READ_ONLY,
|
qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR));
|
||||||
qbool_from_bool(!(flags & BDRV_O_RDWR)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1152,6 +1171,11 @@ QemuOptsList bdrv_runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "discard operation (ignore/off, unmap/on)",
|
.help = "discard operation (ignore/off, unmap/on)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = BDRV_OPT_FORCE_SHARE,
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "always accept other writers (default: off)",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1191,6 +1215,16 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
|||||||
drv = bdrv_find_format(driver_name);
|
drv = bdrv_find_format(driver_name);
|
||||||
assert(drv != NULL);
|
assert(drv != NULL);
|
||||||
|
|
||||||
|
bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false);
|
||||||
|
|
||||||
|
if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) {
|
||||||
|
error_setg(errp,
|
||||||
|
BDRV_OPT_FORCE_SHARE
|
||||||
|
"=on can only be used with read-only images");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail_opts;
|
||||||
|
}
|
||||||
|
|
||||||
if (file != NULL) {
|
if (file != NULL) {
|
||||||
filename = blk_bs(file)->filename;
|
filename = blk_bs(file)->filename;
|
||||||
} else {
|
} else {
|
||||||
@@ -1204,7 +1238,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
|||||||
filename = qdict_get_try_str(options, "filename");
|
filename = qdict_get_try_str(options, "filename");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drv->bdrv_needs_filename && !filename) {
|
if (drv->bdrv_needs_filename && (!filename || !filename[0])) {
|
||||||
error_setg(errp, "The '%s' block driver requires a file name",
|
error_setg(errp, "The '%s' block driver requires a file name",
|
||||||
drv->format_name);
|
drv->format_name);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -1399,7 +1433,7 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
|||||||
/* Fetch the file name from the options QDict if necessary */
|
/* Fetch the file name from the options QDict if necessary */
|
||||||
if (protocol && filename) {
|
if (protocol && filename) {
|
||||||
if (!qdict_haskey(*options, "filename")) {
|
if (!qdict_haskey(*options, "filename")) {
|
||||||
qdict_put(*options, "filename", qstring_from_str(filename));
|
qdict_put_str(*options, "filename", filename);
|
||||||
parse_filename = true;
|
parse_filename = true;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Can't specify 'file' and 'filename' options at "
|
error_setg(errp, "Can't specify 'file' and 'filename' options at "
|
||||||
@@ -1420,7 +1454,7 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
drvname = drv->format_name;
|
drvname = drv->format_name;
|
||||||
qdict_put(*options, "driver", qstring_from_str(drvname));
|
qdict_put_str(*options, "driver", drvname);
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Must specify either driver or file");
|
error_setg(errp, "Must specify either driver or file");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -1450,6 +1484,22 @@ static int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
|
|||||||
static void bdrv_child_abort_perm_update(BdrvChild *c);
|
static void bdrv_child_abort_perm_update(BdrvChild *c);
|
||||||
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
|
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
|
||||||
|
|
||||||
|
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||||
|
BdrvChild *c,
|
||||||
|
const BdrvChildRole *role,
|
||||||
|
uint64_t parent_perm, uint64_t parent_shared,
|
||||||
|
uint64_t *nperm, uint64_t *nshared)
|
||||||
|
{
|
||||||
|
if (bs->drv && bs->drv->bdrv_child_perm) {
|
||||||
|
bs->drv->bdrv_child_perm(bs, c, role,
|
||||||
|
parent_perm, parent_shared,
|
||||||
|
nperm, nshared);
|
||||||
|
}
|
||||||
|
if (child_bs && child_bs->force_share) {
|
||||||
|
*nshared = BLK_PERM_ALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether permissions on this node can be changed in a way that
|
* Check whether permissions on this node can be changed in a way that
|
||||||
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
|
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
|
||||||
@@ -1469,7 +1519,7 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
|||||||
|
|
||||||
/* Write permissions never work with read-only images */
|
/* Write permissions never work with read-only images */
|
||||||
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
||||||
bdrv_is_read_only(bs))
|
!bdrv_is_writable(bs))
|
||||||
{
|
{
|
||||||
error_setg(errp, "Block node is read-only");
|
error_setg(errp, "Block node is read-only");
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
@@ -1494,9 +1544,9 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
|||||||
/* Check all children */
|
/* Check all children */
|
||||||
QLIST_FOREACH(c, &bs->children, next) {
|
QLIST_FOREACH(c, &bs->children, next) {
|
||||||
uint64_t cur_perm, cur_shared;
|
uint64_t cur_perm, cur_shared;
|
||||||
drv->bdrv_child_perm(bs, c, c->role,
|
bdrv_child_perm(bs, c->bs, c, c->role,
|
||||||
cumulative_perms, cumulative_shared_perms,
|
cumulative_perms, cumulative_shared_perms,
|
||||||
&cur_perm, &cur_shared);
|
&cur_perm, &cur_shared);
|
||||||
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
|
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
|
||||||
errp);
|
errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -1556,9 +1606,9 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
|||||||
/* Update all children */
|
/* Update all children */
|
||||||
QLIST_FOREACH(c, &bs->children, next) {
|
QLIST_FOREACH(c, &bs->children, next) {
|
||||||
uint64_t cur_perm, cur_shared;
|
uint64_t cur_perm, cur_shared;
|
||||||
drv->bdrv_child_perm(bs, c, c->role,
|
bdrv_child_perm(bs, c->bs, c, c->role,
|
||||||
cumulative_perms, cumulative_shared_perms,
|
cumulative_perms, cumulative_shared_perms,
|
||||||
&cur_perm, &cur_shared);
|
&cur_perm, &cur_shared);
|
||||||
bdrv_child_set_perm(c, cur_perm, cur_shared);
|
bdrv_child_set_perm(c, cur_perm, cur_shared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1588,7 +1638,7 @@ static char *bdrv_child_user_desc(BdrvChild *c)
|
|||||||
return g_strdup("another user");
|
return g_strdup("another user");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *bdrv_perm_names(uint64_t perm)
|
char *bdrv_perm_names(uint64_t perm)
|
||||||
{
|
{
|
||||||
struct perm_name {
|
struct perm_name {
|
||||||
uint64_t perm;
|
uint64_t perm;
|
||||||
@@ -1754,7 +1804,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
|||||||
bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
|
bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
|
||||||
|
|
||||||
/* Format drivers may touch metadata even if the guest doesn't write */
|
/* Format drivers may touch metadata even if the guest doesn't write */
|
||||||
if (!bdrv_is_read_only(bs)) {
|
if (bdrv_is_writable(bs)) {
|
||||||
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1780,6 +1830,10 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
|||||||
BLK_PERM_WRITE_UNCHANGED;
|
BLK_PERM_WRITE_UNCHANGED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bs->open_flags & BDRV_O_INACTIVE) {
|
||||||
|
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||||
|
}
|
||||||
|
|
||||||
*nperm = perm;
|
*nperm = perm;
|
||||||
*nshared = shared;
|
*nshared = shared;
|
||||||
}
|
}
|
||||||
@@ -1893,8 +1947,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
|||||||
|
|
||||||
assert(parent_bs->drv);
|
assert(parent_bs->drv);
|
||||||
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
|
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
|
||||||
parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
|
bdrv_child_perm(parent_bs, child_bs, NULL, child_role,
|
||||||
perm, shared_perm, &perm, &shared_perm);
|
perm, shared_perm, &perm, &shared_perm);
|
||||||
|
|
||||||
child = bdrv_root_attach_child(child_bs, child_name, child_role,
|
child = bdrv_root_attach_child(child_bs, child_name, child_role,
|
||||||
perm, shared_perm, parent_bs, errp);
|
perm, shared_perm, parent_bs, errp);
|
||||||
@@ -2075,7 +2129,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) {
|
if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) {
|
||||||
qdict_put(options, "driver", qstring_from_str(bs->backing_format));
|
qdict_put_str(options, "driver", bs->backing_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL,
|
backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL,
|
||||||
@@ -2197,7 +2251,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
|||||||
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
||||||
int64_t total_size;
|
int64_t total_size;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
BlockDriverState *bs_snapshot;
|
BlockDriverState *bs_snapshot = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -2230,38 +2284,32 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare options QDict for the temporary file */
|
/* Prepare options QDict for the temporary file */
|
||||||
qdict_put(snapshot_options, "file.driver",
|
qdict_put_str(snapshot_options, "file.driver", "file");
|
||||||
qstring_from_str("file"));
|
qdict_put_str(snapshot_options, "file.filename", tmp_filename);
|
||||||
qdict_put(snapshot_options, "file.filename",
|
qdict_put_str(snapshot_options, "driver", "qcow2");
|
||||||
qstring_from_str(tmp_filename));
|
|
||||||
qdict_put(snapshot_options, "driver",
|
|
||||||
qstring_from_str("qcow2"));
|
|
||||||
|
|
||||||
bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp);
|
bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp);
|
||||||
snapshot_options = NULL;
|
snapshot_options = NULL;
|
||||||
if (!bs_snapshot) {
|
if (!bs_snapshot) {
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bdrv_append() consumes a strong reference to bs_snapshot (i.e. it will
|
/* bdrv_append() consumes a strong reference to bs_snapshot
|
||||||
* call bdrv_unref() on it), so in order to be able to return one, we have
|
* (i.e. it will call bdrv_unref() on it) even on error, so in
|
||||||
* to increase bs_snapshot's refcount here */
|
* order to be able to return one, we have to increase
|
||||||
|
* bs_snapshot's refcount here */
|
||||||
bdrv_ref(bs_snapshot);
|
bdrv_ref(bs_snapshot);
|
||||||
bdrv_append(bs_snapshot, bs, &local_err);
|
bdrv_append(bs_snapshot, bs, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
bs_snapshot = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(tmp_filename);
|
|
||||||
return bs_snapshot;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
QDECREF(snapshot_options);
|
QDECREF(snapshot_options);
|
||||||
g_free(tmp_filename);
|
g_free(tmp_filename);
|
||||||
return NULL;
|
return bs_snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2410,8 +2458,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "file",
|
qdict_put_str(options, "file", bdrv_get_node_name(file_bs));
|
||||||
qstring_from_str(bdrv_get_node_name(file_bs)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2433,8 +2480,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
|||||||
* sure to update both bs->options (which has the full effective
|
* sure to update both bs->options (which has the full effective
|
||||||
* options for bs) and options (which has file.* already removed).
|
* options for bs) and options (which has file.* already removed).
|
||||||
*/
|
*/
|
||||||
qdict_put(bs->options, "driver", qstring_from_str(drv->format_name));
|
qdict_put_str(bs->options, "driver", drv->format_name);
|
||||||
qdict_put(options, "driver", qstring_from_str(drv->format_name));
|
qdict_put_str(options, "driver", drv->format_name);
|
||||||
} else if (!drv) {
|
} else if (!drv) {
|
||||||
error_setg(errp, "Must specify either driver or file");
|
error_setg(errp, "Must specify either driver or file");
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -2810,12 +2857,12 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
|||||||
* that they are checked at the end of this function. */
|
* that they are checked at the end of this function. */
|
||||||
value = qemu_opt_get(opts, "node-name");
|
value = qemu_opt_get(opts, "node-name");
|
||||||
if (value) {
|
if (value) {
|
||||||
qdict_put(reopen_state->options, "node-name", qstring_from_str(value));
|
qdict_put_str(reopen_state->options, "node-name", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = qemu_opt_get(opts, "driver");
|
value = qemu_opt_get(opts, "driver");
|
||||||
if (value) {
|
if (value) {
|
||||||
qdict_put(reopen_state->options, "driver", qstring_from_str(value));
|
qdict_put_str(reopen_state->options, "driver", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we are to stay read-only, do not allow permission change
|
/* If we are to stay read-only, do not allow permission change
|
||||||
@@ -3307,26 +3354,30 @@ exit:
|
|||||||
/**
|
/**
|
||||||
* Truncate file to 'offset' bytes (needed only for file protocols)
|
* Truncate file to 'offset' bytes (needed only for file protocols)
|
||||||
*/
|
*/
|
||||||
int bdrv_truncate(BdrvChild *child, int64_t offset)
|
int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = child->bs;
|
BlockDriverState *bs = child->bs;
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* FIXME: Some format block drivers use this function instead of implicitly
|
assert(child->perm & BLK_PERM_RESIZE);
|
||||||
* growing their file by writing beyond its end.
|
|
||||||
* See bdrv_aligned_pwritev() for an explanation why we currently
|
|
||||||
* cannot assert this permission in that case. */
|
|
||||||
// assert(child->perm & BLK_PERM_RESIZE);
|
|
||||||
|
|
||||||
if (!drv)
|
if (!drv) {
|
||||||
|
error_setg(errp, "No medium inserted");
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
if (!drv->bdrv_truncate)
|
}
|
||||||
|
if (!drv->bdrv_truncate) {
|
||||||
|
error_setg(errp, "Image format driver does not support resize");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
if (bs->read_only)
|
}
|
||||||
|
if (bs->read_only) {
|
||||||
|
error_setg(errp, "Image is read-only");
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
ret = drv->bdrv_truncate(bs, offset);
|
assert(!(bs->open_flags & BDRV_O_INACTIVE));
|
||||||
|
|
||||||
|
ret = drv->bdrv_truncate(bs, offset, errp);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||||
bdrv_dirty_bitmap_truncate(bs);
|
bdrv_dirty_bitmap_truncate(bs);
|
||||||
@@ -3921,7 +3972,8 @@ void bdrv_init_with_whitelist(void)
|
|||||||
|
|
||||||
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BdrvChild *child;
|
BdrvChild *child, *parent;
|
||||||
|
uint64_t perm, shared_perm;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -3957,6 +4009,26 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
|||||||
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update permissions, they may differ for inactive nodes */
|
||||||
|
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||||
|
ret = bdrv_check_perm(bs, perm, shared_perm, NULL, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
bs->open_flags |= BDRV_O_INACTIVE;
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bdrv_set_perm(bs, perm, shared_perm);
|
||||||
|
|
||||||
|
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||||
|
if (parent->role->activate) {
|
||||||
|
parent->role->activate(parent, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_invalidate_cache_all(Error **errp)
|
void bdrv_invalidate_cache_all(Error **errp)
|
||||||
@@ -3981,7 +4053,7 @@ void bdrv_invalidate_cache_all(Error **errp)
|
|||||||
static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||||
bool setting_flag)
|
bool setting_flag)
|
||||||
{
|
{
|
||||||
BdrvChild *child;
|
BdrvChild *child, *parent;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!setting_flag && bs->drv->bdrv_inactivate) {
|
if (!setting_flag && bs->drv->bdrv_inactivate) {
|
||||||
@@ -3991,6 +4063,27 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting_flag) {
|
||||||
|
uint64_t perm, shared_perm;
|
||||||
|
|
||||||
|
bs->open_flags |= BDRV_O_INACTIVE;
|
||||||
|
|
||||||
|
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||||
|
if (parent->role->inactivate) {
|
||||||
|
ret = parent->role->inactivate(parent);
|
||||||
|
if (ret < 0) {
|
||||||
|
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update permissions, they may differ for inactive nodes */
|
||||||
|
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||||
|
bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
|
||||||
|
bdrv_set_perm(bs, perm, shared_perm);
|
||||||
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(child, &bs->children, next) {
|
QLIST_FOREACH(child, &bs->children, next) {
|
||||||
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
|
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -3998,9 +4091,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting_flag) {
|
|
||||||
bs->open_flags |= BDRV_O_INACTIVE;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4302,8 +4392,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||||||
|
|
||||||
if (backing_fmt) {
|
if (backing_fmt) {
|
||||||
backing_options = qdict_new();
|
backing_options = qdict_new();
|
||||||
qdict_put(backing_options, "driver",
|
qdict_put_str(backing_options, "driver", backing_fmt);
|
||||||
qstring_from_str(backing_fmt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
|
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
|
||||||
@@ -4708,11 +4797,9 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||||||
* contain a representation of the filename, therefore the following
|
* contain a representation of the filename, therefore the following
|
||||||
* suffices without querying the (exact_)filename of this BDS. */
|
* suffices without querying the (exact_)filename of this BDS. */
|
||||||
if (bs->file->bs->full_open_options) {
|
if (bs->file->bs->full_open_options) {
|
||||||
qdict_put_obj(opts, "driver",
|
qdict_put_str(opts, "driver", drv->format_name);
|
||||||
QOBJECT(qstring_from_str(drv->format_name)));
|
|
||||||
QINCREF(bs->file->bs->full_open_options);
|
QINCREF(bs->file->bs->full_open_options);
|
||||||
qdict_put_obj(opts, "file",
|
qdict_put(opts, "file", bs->file->bs->full_open_options);
|
||||||
QOBJECT(bs->file->bs->full_open_options));
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
} else {
|
} else {
|
||||||
@@ -4728,8 +4815,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||||||
|
|
||||||
opts = qdict_new();
|
opts = qdict_new();
|
||||||
append_open_options(opts, bs);
|
append_open_options(opts, bs);
|
||||||
qdict_put_obj(opts, "driver",
|
qdict_put_str(opts, "driver", drv->format_name);
|
||||||
QOBJECT(qstring_from_str(drv->format_name)));
|
|
||||||
|
|
||||||
if (bs->exact_filename[0]) {
|
if (bs->exact_filename[0]) {
|
||||||
/* This may not work for all block protocol drivers (some may
|
/* This may not work for all block protocol drivers (some may
|
||||||
@@ -4739,8 +4825,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||||||
* needs some special format of the options QDict, it needs to
|
* needs some special format of the options QDict, it needs to
|
||||||
* implement the driver-specific bdrv_refresh_filename() function.
|
* implement the driver-specific bdrv_refresh_filename() function.
|
||||||
*/
|
*/
|
||||||
qdict_put_obj(opts, "filename",
|
qdict_put_str(opts, "filename", bs->exact_filename);
|
||||||
QOBJECT(qstring_from_str(bs->exact_filename)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
|
|||||||
282
block/blkdebug.c
282
block/blkdebug.c
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Block protocol for I/O error injection
|
* Block protocol for I/O error injection
|
||||||
*
|
*
|
||||||
|
* Copyright (C) 2016-2017 Red Hat, Inc.
|
||||||
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
|
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
@@ -37,7 +38,12 @@
|
|||||||
typedef struct BDRVBlkdebugState {
|
typedef struct BDRVBlkdebugState {
|
||||||
int state;
|
int state;
|
||||||
int new_state;
|
int new_state;
|
||||||
int align;
|
uint64_t align;
|
||||||
|
uint64_t max_transfer;
|
||||||
|
uint64_t opt_write_zero;
|
||||||
|
uint64_t max_write_zero;
|
||||||
|
uint64_t opt_discard;
|
||||||
|
uint64_t max_discard;
|
||||||
|
|
||||||
/* For blkdebug_refresh_filename() */
|
/* For blkdebug_refresh_filename() */
|
||||||
char *config_file;
|
char *config_file;
|
||||||
@@ -301,7 +307,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
|||||||
if (!strstart(filename, "blkdebug:", &filename)) {
|
if (!strstart(filename, "blkdebug:", &filename)) {
|
||||||
/* There was no prefix; therefore, all options have to be already
|
/* There was no prefix; therefore, all options have to be already
|
||||||
present in the QDict (except for the filename) */
|
present in the QDict (except for the filename) */
|
||||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
qdict_put_str(options, "x-image", filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +326,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
|||||||
|
|
||||||
/* TODO Allow multi-level nesting and set file.filename here */
|
/* TODO Allow multi-level nesting and set file.filename here */
|
||||||
filename = c + 1;
|
filename = c + 1;
|
||||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
qdict_put_str(options, "x-image", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList runtime_opts = {
|
static QemuOptsList runtime_opts = {
|
||||||
@@ -342,6 +348,31 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
.help = "Required alignment in bytes",
|
.help = "Required alignment in bytes",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "max-transfer",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Maximum transfer size in bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "opt-write-zero",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Optimum write zero alignment in bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "max-write-zero",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Maximum write zero size in bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "opt-discard",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Optimum discard alignment in bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "max-discard",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Maximum discard size in bytes",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -352,8 +383,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
BDRVBlkdebugState *s = bs->opaque;
|
BDRVBlkdebugState *s = bs->opaque;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
uint64_t align;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
uint64_t align;
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
@@ -382,21 +413,69 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set request alignment */
|
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||||
align = qemu_opt_get_size(opts, "align", 0);
|
bs->file->bs->supported_write_flags;
|
||||||
if (align < INT_MAX && is_power_of_2(align)) {
|
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||||
s->align = align;
|
bs->file->bs->supported_zero_flags;
|
||||||
} else if (align) {
|
ret = -EINVAL;
|
||||||
error_setg(errp, "Invalid alignment");
|
|
||||||
ret = -EINVAL;
|
/* Set alignment overrides */
|
||||||
goto fail_unref;
|
s->align = qemu_opt_get_size(opts, "align", 0);
|
||||||
|
if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with align %" PRIu64,
|
||||||
|
s->align);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
align = MAX(s->align, bs->file->bs->bl.request_alignment);
|
||||||
|
|
||||||
|
s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
|
||||||
|
if (s->max_transfer &&
|
||||||
|
(s->max_transfer >= INT_MAX ||
|
||||||
|
!QEMU_IS_ALIGNED(s->max_transfer, align))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
|
||||||
|
s->max_transfer);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
|
||||||
|
if (s->opt_write_zero &&
|
||||||
|
(s->opt_write_zero >= INT_MAX ||
|
||||||
|
!QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
|
||||||
|
s->opt_write_zero);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
|
||||||
|
if (s->max_write_zero &&
|
||||||
|
(s->max_write_zero >= INT_MAX ||
|
||||||
|
!QEMU_IS_ALIGNED(s->max_write_zero,
|
||||||
|
MAX(s->opt_write_zero, align)))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
|
||||||
|
s->max_write_zero);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
|
||||||
|
if (s->opt_discard &&
|
||||||
|
(s->opt_discard >= INT_MAX ||
|
||||||
|
!QEMU_IS_ALIGNED(s->opt_discard, align))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
|
||||||
|
s->opt_discard);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
|
||||||
|
if (s->max_discard &&
|
||||||
|
(s->max_discard >= INT_MAX ||
|
||||||
|
!QEMU_IS_ALIGNED(s->max_discard,
|
||||||
|
MAX(s->opt_discard, align)))) {
|
||||||
|
error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
|
||||||
|
s->max_discard);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out;
|
|
||||||
|
|
||||||
fail_unref:
|
|
||||||
bdrv_unref_child(bs, bs->file);
|
|
||||||
out:
|
out:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(s->config_file);
|
g_free(s->config_file);
|
||||||
@@ -405,11 +484,30 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
|
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
|
||||||
{
|
{
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
BDRVBlkdebugState *s = bs->opaque;
|
||||||
int error = rule->options.inject.error;
|
BlkdebugRule *rule = NULL;
|
||||||
bool immediately = rule->options.inject.immediately;
|
int error;
|
||||||
|
bool immediately;
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||||
|
uint64_t inject_offset = rule->options.inject.offset;
|
||||||
|
|
||||||
|
if (inject_offset == -1 ||
|
||||||
|
(bytes && inject_offset >= offset &&
|
||||||
|
inject_offset < offset + bytes))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rule || !rule->options.inject.error) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
immediately = rule->options.inject.immediately;
|
||||||
|
error = rule->options.inject.error;
|
||||||
|
|
||||||
if (rule->options.inject.once) {
|
if (rule->options.inject.once) {
|
||||||
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
|
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
|
||||||
@@ -428,21 +526,18 @@ static int coroutine_fn
|
|||||||
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||||
QEMUIOVector *qiov, int flags)
|
QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
int err;
|
||||||
BlkdebugRule *rule = NULL;
|
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
/* Sanity check block layer guarantees */
|
||||||
uint64_t inject_offset = rule->options.inject.offset;
|
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||||
|
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||||
if (inject_offset == -1 ||
|
if (bs->bl.max_transfer) {
|
||||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
assert(bytes <= bs->bl.max_transfer);
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rule && rule->options.inject.error) {
|
err = rule_check(bs, offset, bytes);
|
||||||
return inject_error(bs, rule);
|
if (err) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||||
@@ -452,21 +547,18 @@ static int coroutine_fn
|
|||||||
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||||
QEMUIOVector *qiov, int flags)
|
QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
int err;
|
||||||
BlkdebugRule *rule = NULL;
|
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
/* Sanity check block layer guarantees */
|
||||||
uint64_t inject_offset = rule->options.inject.offset;
|
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||||
|
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||||
if (inject_offset == -1 ||
|
if (bs->bl.max_transfer) {
|
||||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
assert(bytes <= bs->bl.max_transfer);
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rule && rule->options.inject.error) {
|
err = rule_check(bs, offset, bytes);
|
||||||
return inject_error(bs, rule);
|
if (err) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||||
@@ -474,22 +566,81 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
|||||||
|
|
||||||
static int blkdebug_co_flush(BlockDriverState *bs)
|
static int blkdebug_co_flush(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
int err = rule_check(bs, 0, 0);
|
||||||
BlkdebugRule *rule = NULL;
|
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
if (err) {
|
||||||
if (rule->options.inject.offset == -1) {
|
return err;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule && rule->options.inject.error) {
|
|
||||||
return inject_error(bs, rule);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_co_flush(bs->file->bs);
|
return bdrv_co_flush(bs->file->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
|
||||||
|
int64_t offset, int count,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
uint32_t align = MAX(bs->bl.request_alignment,
|
||||||
|
bs->bl.pwrite_zeroes_alignment);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Only pass through requests that are larger than requested
|
||||||
|
* preferred alignment (so that we test the fallback to writes on
|
||||||
|
* unaligned portions), and check that the block layer never hands
|
||||||
|
* us anything unaligned that crosses an alignment boundary. */
|
||||||
|
if (count < align) {
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||||
|
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||||
|
DIV_ROUND_UP(offset, align) ==
|
||||||
|
DIV_ROUND_UP(offset + count, align));
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, align));
|
||||||
|
assert(QEMU_IS_ALIGNED(count, align));
|
||||||
|
if (bs->bl.max_pwrite_zeroes) {
|
||||||
|
assert(count <= bs->bl.max_pwrite_zeroes);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rule_check(bs, offset, count);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
||||||
|
int64_t offset, int count)
|
||||||
|
{
|
||||||
|
uint32_t align = bs->bl.pdiscard_alignment;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Only pass through requests that are larger than requested
|
||||||
|
* minimum alignment, and ensure that unaligned requests do not
|
||||||
|
* cross optimum discard boundaries. */
|
||||||
|
if (count < bs->bl.request_alignment) {
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||||
|
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||||
|
DIV_ROUND_UP(offset, align) ==
|
||||||
|
DIV_ROUND_UP(offset + count, align));
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||||
|
assert(QEMU_IS_ALIGNED(count, bs->bl.request_alignment));
|
||||||
|
if (align && count >= align) {
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, align));
|
||||||
|
assert(QEMU_IS_ALIGNED(count, align));
|
||||||
|
}
|
||||||
|
if (bs->bl.max_pdiscard) {
|
||||||
|
assert(count <= bs->bl.max_pdiscard);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rule_check(bs, offset, count);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bdrv_co_pdiscard(bs->file->bs, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
static void blkdebug_close(BlockDriverState *bs)
|
static void blkdebug_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
@@ -661,9 +812,9 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
|
|||||||
return bdrv_getlength(bs->file->bs);
|
return bdrv_getlength(bs->file->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
|
static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
return bdrv_truncate(bs->file, offset);
|
return bdrv_truncate(bs->file, offset, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||||
@@ -695,10 +846,10 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts = qdict_new();
|
opts = qdict_new();
|
||||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
|
qdict_put_str(opts, "driver", "blkdebug");
|
||||||
|
|
||||||
QINCREF(bs->file->bs->full_open_options);
|
QINCREF(bs->file->bs->full_open_options);
|
||||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
|
qdict_put(opts, "image", bs->file->bs->full_open_options);
|
||||||
|
|
||||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||||
@@ -717,6 +868,21 @@ static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
|||||||
if (s->align) {
|
if (s->align) {
|
||||||
bs->bl.request_alignment = s->align;
|
bs->bl.request_alignment = s->align;
|
||||||
}
|
}
|
||||||
|
if (s->max_transfer) {
|
||||||
|
bs->bl.max_transfer = s->max_transfer;
|
||||||
|
}
|
||||||
|
if (s->opt_write_zero) {
|
||||||
|
bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
|
||||||
|
}
|
||||||
|
if (s->max_write_zero) {
|
||||||
|
bs->bl.max_pwrite_zeroes = s->max_write_zero;
|
||||||
|
}
|
||||||
|
if (s->opt_discard) {
|
||||||
|
bs->bl.pdiscard_alignment = s->opt_discard;
|
||||||
|
}
|
||||||
|
if (s->max_discard) {
|
||||||
|
bs->bl.max_pdiscard = s->max_discard;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
@@ -744,6 +910,8 @@ static BlockDriver bdrv_blkdebug = {
|
|||||||
.bdrv_co_preadv = blkdebug_co_preadv,
|
.bdrv_co_preadv = blkdebug_co_preadv,
|
||||||
.bdrv_co_pwritev = blkdebug_co_pwritev,
|
.bdrv_co_pwritev = blkdebug_co_pwritev,
|
||||||
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
||||||
|
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
|
||||||
|
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
|
||||||
|
|
||||||
.bdrv_debug_event = blkdebug_debug_event,
|
.bdrv_debug_event = blkdebug_debug_event,
|
||||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
if (ret < 0) {
|
|
||||||
bdrv_unref_child(bs, bs->file);
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
|||||||
if (!strstart(filename, "blkverify:", &filename)) {
|
if (!strstart(filename, "blkverify:", &filename)) {
|
||||||
/* There was no prefix; therefore, all options have to be already
|
/* There was no prefix; therefore, all options have to be already
|
||||||
present in the QDict (except for the filename) */
|
present in the QDict (except for the filename) */
|
||||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
qdict_put_str(options, "x-image", filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
|||||||
|
|
||||||
/* TODO Allow multi-level nesting and set file.filename here */
|
/* TODO Allow multi-level nesting and set file.filename here */
|
||||||
filename = c + 1;
|
filename = c + 1;
|
||||||
qdict_put(options, "x-image", qstring_from_str(filename));
|
qdict_put_str(options, "x-image", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList runtime_opts = {
|
static QemuOptsList runtime_opts = {
|
||||||
@@ -142,9 +142,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
if (ret < 0) {
|
|
||||||
bdrv_unref_child(bs, bs->file);
|
|
||||||
}
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -291,13 +288,12 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
&& s->test_file->bs->full_open_options)
|
&& s->test_file->bs->full_open_options)
|
||||||
{
|
{
|
||||||
QDict *opts = qdict_new();
|
QDict *opts = qdict_new();
|
||||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify")));
|
qdict_put_str(opts, "driver", "blkverify");
|
||||||
|
|
||||||
QINCREF(bs->file->bs->full_open_options);
|
QINCREF(bs->file->bs->full_open_options);
|
||||||
qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options));
|
qdict_put(opts, "raw", bs->file->bs->full_open_options);
|
||||||
QINCREF(s->test_file->bs->full_open_options);
|
QINCREF(s->test_file->bs->full_open_options);
|
||||||
qdict_put_obj(opts, "test",
|
qdict_put(opts, "test", s->test_file->bs->full_open_options);
|
||||||
QOBJECT(s->test_file->bs->full_open_options));
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,56 @@ static const char *blk_root_get_name(BdrvChild *child)
|
|||||||
return blk_name(child->opaque);
|
return blk_name(child->opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notifies the user of the BlockBackend that migration has completed. qdev
|
||||||
|
* devices can tighten their permissions in response (specifically revoke
|
||||||
|
* shared write permissions that we needed for storage migration).
|
||||||
|
*
|
||||||
|
* If an error is returned, the VM cannot be allowed to be resumed.
|
||||||
|
*/
|
||||||
|
static void blk_root_activate(BdrvChild *child, Error **errp)
|
||||||
|
{
|
||||||
|
BlockBackend *blk = child->opaque;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (!blk->disable_perm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk->disable_perm = false;
|
||||||
|
|
||||||
|
blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
blk->disable_perm = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blk_root_inactivate(BdrvChild *child)
|
||||||
|
{
|
||||||
|
BlockBackend *blk = child->opaque;
|
||||||
|
|
||||||
|
if (blk->disable_perm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only inactivate BlockBackends for guest devices (which are inactive at
|
||||||
|
* this point because the VM is stopped) and unattached monitor-owned
|
||||||
|
* BlockBackends. If there is still any other user like a block job, then
|
||||||
|
* we simply can't inactivate the image. */
|
||||||
|
if (!blk->dev && !blk->name[0]) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk->disable_perm = true;
|
||||||
|
if (blk->root) {
|
||||||
|
bdrv_child_try_set_perm(blk->root, 0, BLK_PERM_ALL, &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const BdrvChildRole child_root = {
|
static const BdrvChildRole child_root = {
|
||||||
.inherit_options = blk_root_inherit_options,
|
.inherit_options = blk_root_inherit_options,
|
||||||
|
|
||||||
@@ -140,6 +190,9 @@ static const BdrvChildRole child_root = {
|
|||||||
|
|
||||||
.drained_begin = blk_root_drained_begin,
|
.drained_begin = blk_root_drained_begin,
|
||||||
.drained_end = blk_root_drained_end,
|
.drained_end = blk_root_drained_end,
|
||||||
|
|
||||||
|
.activate = blk_root_activate,
|
||||||
|
.inactivate = blk_root_inactivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -420,7 +473,7 @@ void monitor_remove_blk(BlockBackend *blk)
|
|||||||
* Return @blk's name, a non-null string.
|
* Return @blk's name, a non-null string.
|
||||||
* Returns an empty string iff @blk is not referenced by the monitor.
|
* Returns an empty string iff @blk is not referenced by the monitor.
|
||||||
*/
|
*/
|
||||||
const char *blk_name(BlockBackend *blk)
|
const char *blk_name(const BlockBackend *blk)
|
||||||
{
|
{
|
||||||
return blk->name ?: "";
|
return blk->name ?: "";
|
||||||
}
|
}
|
||||||
@@ -601,34 +654,6 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
|
|||||||
*shared_perm = blk->shared_perm;
|
*shared_perm = blk->shared_perm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Notifies the user of all BlockBackends that migration has completed. qdev
|
|
||||||
* devices can tighten their permissions in response (specifically revoke
|
|
||||||
* shared write permissions that we needed for storage migration).
|
|
||||||
*
|
|
||||||
* If an error is returned, the VM cannot be allowed to be resumed.
|
|
||||||
*/
|
|
||||||
void blk_resume_after_migration(Error **errp)
|
|
||||||
{
|
|
||||||
BlockBackend *blk;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
|
|
||||||
if (!blk->disable_perm) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
blk->disable_perm = false;
|
|
||||||
|
|
||||||
blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
blk->disable_perm = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
||||||
{
|
{
|
||||||
if (blk->dev) {
|
if (blk->dev) {
|
||||||
@@ -1746,13 +1771,14 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
|
|||||||
BDRV_REQ_WRITE_COMPRESSED);
|
BDRV_REQ_WRITE_COMPRESSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_truncate(BlockBackend *blk, int64_t offset)
|
int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
if (!blk_is_available(blk)) {
|
if (!blk_is_available(blk)) {
|
||||||
|
error_setg(errp, "No medium inserted");
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_truncate(blk->root, offset);
|
return bdrv_truncate(blk->root, offset, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_pdiscard_entry(void *opaque)
|
static void blk_pdiscard_entry(void *opaque)
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ static void coroutine_fn commit_run(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (base_len < s->common.len) {
|
if (base_len < s->common.len) {
|
||||||
ret = blk_truncate(s->base, s->common.len);
|
ret = blk_truncate(s->base, s->common.len, NULL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -511,8 +511,9 @@ int bdrv_commit(BlockDriverState *bs)
|
|||||||
* grow the backing file image if possible. If not possible,
|
* grow the backing file image if possible. If not possible,
|
||||||
* we must return an error */
|
* we must return an error */
|
||||||
if (length > backing_length) {
|
if (length > backing_length) {
|
||||||
ret = blk_truncate(backing, length);
|
ret = blk_truncate(backing, length, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
goto ro_cleanup;
|
goto ro_cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format,
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
||||||
void *opaque,
|
|
||||||
size_t offset,
|
size_t offset,
|
||||||
uint8_t *buf,
|
uint8_t *buf,
|
||||||
size_t buflen,
|
size_t buflen,
|
||||||
|
void *opaque,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = opaque;
|
BlockDriverState *bs = opaque;
|
||||||
@@ -83,10 +83,10 @@ struct BlockCryptoCreateData {
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
||||||
void *opaque,
|
|
||||||
size_t offset,
|
size_t offset,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t buflen,
|
size_t buflen,
|
||||||
|
void *opaque,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct BlockCryptoCreateData *data = opaque;
|
struct BlockCryptoCreateData *data = opaque;
|
||||||
@@ -102,8 +102,8 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
||||||
void *opaque,
|
|
||||||
size_t headerlen,
|
size_t headerlen,
|
||||||
|
void *opaque,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct BlockCryptoCreateData *data = opaque;
|
struct BlockCryptoCreateData *data = opaque;
|
||||||
@@ -381,7 +381,8 @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset)
|
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockCrypto *crypto = bs->opaque;
|
BlockCrypto *crypto = bs->opaque;
|
||||||
size_t payload_offset =
|
size_t payload_offset =
|
||||||
@@ -389,7 +390,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
|
|
||||||
offset += payload_offset;
|
offset += payload_offset;
|
||||||
|
|
||||||
return bdrv_truncate(bs->file, offset);
|
return bdrv_truncate(bs->file, offset, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_crypto_close(BlockDriverState *bs)
|
static void block_crypto_close(BlockDriverState *bs)
|
||||||
|
|||||||
243
block/curl.c
243
block/curl.c
@@ -76,15 +76,12 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
|||||||
#define CURL_TIMEOUT_DEFAULT 5
|
#define CURL_TIMEOUT_DEFAULT 5
|
||||||
#define CURL_TIMEOUT_MAX 10000
|
#define CURL_TIMEOUT_MAX 10000
|
||||||
|
|
||||||
#define FIND_RET_NONE 0
|
|
||||||
#define FIND_RET_OK 1
|
|
||||||
#define FIND_RET_WAIT 2
|
|
||||||
|
|
||||||
#define CURL_BLOCK_OPT_URL "url"
|
#define CURL_BLOCK_OPT_URL "url"
|
||||||
#define CURL_BLOCK_OPT_READAHEAD "readahead"
|
#define CURL_BLOCK_OPT_READAHEAD "readahead"
|
||||||
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||||
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
||||||
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
||||||
|
#define CURL_BLOCK_OPT_COOKIE_SECRET "cookie-secret"
|
||||||
#define CURL_BLOCK_OPT_USERNAME "username"
|
#define CURL_BLOCK_OPT_USERNAME "username"
|
||||||
#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
|
#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
|
||||||
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
||||||
@@ -93,14 +90,17 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
|||||||
struct BDRVCURLState;
|
struct BDRVCURLState;
|
||||||
|
|
||||||
typedef struct CURLAIOCB {
|
typedef struct CURLAIOCB {
|
||||||
BlockAIOCB common;
|
Coroutine *co;
|
||||||
QEMUIOVector *qiov;
|
QEMUIOVector *qiov;
|
||||||
|
|
||||||
int64_t sector_num;
|
uint64_t offset;
|
||||||
int nb_sectors;
|
uint64_t bytes;
|
||||||
|
int ret;
|
||||||
|
|
||||||
size_t start;
|
size_t start;
|
||||||
size_t end;
|
size_t end;
|
||||||
|
|
||||||
|
QSIMPLEQ_ENTRY(CURLAIOCB) next;
|
||||||
} CURLAIOCB;
|
} CURLAIOCB;
|
||||||
|
|
||||||
typedef struct CURLSocket {
|
typedef struct CURLSocket {
|
||||||
@@ -115,7 +115,7 @@ typedef struct CURLState
|
|||||||
CURL *curl;
|
CURL *curl;
|
||||||
QLIST_HEAD(, CURLSocket) sockets;
|
QLIST_HEAD(, CURLSocket) sockets;
|
||||||
char *orig_buf;
|
char *orig_buf;
|
||||||
size_t buf_start;
|
uint64_t buf_start;
|
||||||
size_t buf_off;
|
size_t buf_off;
|
||||||
size_t buf_len;
|
size_t buf_len;
|
||||||
char range[128];
|
char range[128];
|
||||||
@@ -126,7 +126,7 @@ typedef struct CURLState
|
|||||||
typedef struct BDRVCURLState {
|
typedef struct BDRVCURLState {
|
||||||
CURLM *multi;
|
CURLM *multi;
|
||||||
QEMUTimer timer;
|
QEMUTimer timer;
|
||||||
size_t len;
|
uint64_t len;
|
||||||
CURLState states[CURL_NUM_STATES];
|
CURLState states[CURL_NUM_STATES];
|
||||||
char *url;
|
char *url;
|
||||||
size_t readahead_size;
|
size_t readahead_size;
|
||||||
@@ -136,6 +136,7 @@ typedef struct BDRVCURLState {
|
|||||||
bool accept_range;
|
bool accept_range;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
|
QSIMPLEQ_HEAD(, CURLAIOCB) free_state_waitq;
|
||||||
char *username;
|
char *username;
|
||||||
char *password;
|
char *password;
|
||||||
char *proxyusername;
|
char *proxyusername;
|
||||||
@@ -147,6 +148,7 @@ static void curl_multi_do(void *arg);
|
|||||||
static void curl_multi_read(void *arg);
|
static void curl_multi_read(void *arg);
|
||||||
|
|
||||||
#ifdef NEED_CURL_TIMER_CALLBACK
|
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||||
|
/* Called from curl_multi_do_locked, with s->mutex held. */
|
||||||
static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s = opaque;
|
BDRVCURLState *s = opaque;
|
||||||
@@ -163,6 +165,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Called from curl_multi_do_locked, with s->mutex held. */
|
||||||
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 *userp, void *sp)
|
void *userp, void *sp)
|
||||||
{
|
{
|
||||||
@@ -212,6 +215,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called from curl_multi_do_locked, with s->mutex held. */
|
||||||
static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s = opaque;
|
BDRVCURLState *s = opaque;
|
||||||
@@ -226,6 +230,7 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
|||||||
return realsize;
|
return realsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called from curl_multi_do_locked, with s->mutex held. */
|
||||||
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||||
{
|
{
|
||||||
CURLState *s = ((CURLState*)opaque);
|
CURLState *s = ((CURLState*)opaque);
|
||||||
@@ -253,7 +258,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((s->buf_off >= acb->end)) {
|
if ((s->buf_off >= acb->end)) {
|
||||||
size_t request_length = acb->nb_sectors * BDRV_SECTOR_SIZE;
|
size_t request_length = acb->bytes;
|
||||||
|
|
||||||
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
|
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
|
||||||
acb->end - acb->start);
|
acb->end - acb->start);
|
||||||
@@ -264,9 +269,11 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
|||||||
request_length - offset);
|
request_length - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
acb->common.cb(acb->common.opaque, 0);
|
acb->ret = 0;
|
||||||
qemu_aio_unref(acb);
|
|
||||||
s->acb[i] = NULL;
|
s->acb[i] = NULL;
|
||||||
|
qemu_mutex_unlock(&s->s->mutex);
|
||||||
|
aio_co_wake(acb->co);
|
||||||
|
qemu_mutex_lock(&s->s->mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,18 +282,19 @@ read_end:
|
|||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
/* Called with s->mutex held. */
|
||||||
CURLAIOCB *acb)
|
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
|
||||||
|
CURLAIOCB *acb)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
size_t end = start + len;
|
uint64_t end = start + len;
|
||||||
size_t clamped_end = MIN(end, s->len);
|
uint64_t clamped_end = MIN(end, s->len);
|
||||||
size_t clamped_len = clamped_end - start;
|
uint64_t clamped_len = clamped_end - start;
|
||||||
|
|
||||||
for (i=0; i<CURL_NUM_STATES; i++) {
|
for (i=0; i<CURL_NUM_STATES; i++) {
|
||||||
CURLState *state = &s->states[i];
|
CURLState *state = &s->states[i];
|
||||||
size_t buf_end = (state->buf_start + state->buf_off);
|
uint64_t buf_end = (state->buf_start + state->buf_off);
|
||||||
size_t buf_fend = (state->buf_start + state->buf_len);
|
uint64_t buf_fend = (state->buf_start + state->buf_len);
|
||||||
|
|
||||||
if (!state->orig_buf)
|
if (!state->orig_buf)
|
||||||
continue;
|
continue;
|
||||||
@@ -305,9 +313,8 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
|||||||
if (clamped_len < len) {
|
if (clamped_len < len) {
|
||||||
qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len);
|
qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len);
|
||||||
}
|
}
|
||||||
acb->common.cb(acb->common.opaque, 0);
|
acb->ret = 0;
|
||||||
|
return true;
|
||||||
return FIND_RET_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for unfinished chunks
|
// Wait for unfinished chunks
|
||||||
@@ -325,13 +332,13 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
|||||||
for (j=0; j<CURL_NUM_ACB; j++) {
|
for (j=0; j<CURL_NUM_ACB; j++) {
|
||||||
if (!state->acb[j]) {
|
if (!state->acb[j]) {
|
||||||
state->acb[j] = acb;
|
state->acb[j] = acb;
|
||||||
return FIND_RET_WAIT;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FIND_RET_NONE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with s->mutex held. */
|
/* Called with s->mutex held. */
|
||||||
@@ -376,11 +383,11 @@ static void curl_multi_check_completion(BDRVCURLState *s)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_mutex_unlock(&s->mutex);
|
acb->ret = -EIO;
|
||||||
acb->common.cb(acb->common.opaque, -EIO);
|
|
||||||
qemu_mutex_lock(&s->mutex);
|
|
||||||
qemu_aio_unref(acb);
|
|
||||||
state->acb[i] = NULL;
|
state->acb[i] = NULL;
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
aio_co_wake(acb->co);
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,32 +456,28 @@ static void curl_multi_timeout_do(void *arg)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
/* Called with s->mutex held. */
|
||||||
|
static CURLState *curl_find_state(BDRVCURLState *s)
|
||||||
{
|
{
|
||||||
CURLState *state = NULL;
|
CURLState *state = NULL;
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
do {
|
|
||||||
for (i=0; i<CURL_NUM_STATES; i++) {
|
|
||||||
for (j=0; j<CURL_NUM_ACB; j++)
|
|
||||||
if (s->states[i].acb[j])
|
|
||||||
continue;
|
|
||||||
if (s->states[i].in_use)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
for (i = 0; i < CURL_NUM_STATES; i++) {
|
||||||
|
if (!s->states[i].in_use) {
|
||||||
state = &s->states[i];
|
state = &s->states[i];
|
||||||
state->in_use = 1;
|
state->in_use = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!state) {
|
}
|
||||||
aio_poll(bdrv_get_aio_context(bs), true);
|
return state;
|
||||||
}
|
}
|
||||||
} while(!state);
|
|
||||||
|
|
||||||
|
static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||||
|
{
|
||||||
if (!state->curl) {
|
if (!state->curl) {
|
||||||
state->curl = curl_easy_init();
|
state->curl = curl_easy_init();
|
||||||
if (!state->curl) {
|
if (!state->curl) {
|
||||||
return NULL;
|
return -EIO;
|
||||||
}
|
}
|
||||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||||
@@ -527,11 +530,18 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
|||||||
QLIST_INIT(&state->sockets);
|
QLIST_INIT(&state->sockets);
|
||||||
state->s = s;
|
state->s = s;
|
||||||
|
|
||||||
return state;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with s->mutex held. */
|
||||||
static void curl_clean_state(CURLState *s)
|
static void curl_clean_state(CURLState *s)
|
||||||
{
|
{
|
||||||
|
CURLAIOCB *next;
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < CURL_NUM_ACB; j++) {
|
||||||
|
assert(!s->acb[j]);
|
||||||
|
}
|
||||||
|
|
||||||
if (s->s->multi)
|
if (s->s->multi)
|
||||||
curl_multi_remove_handle(s->s->multi, s->curl);
|
curl_multi_remove_handle(s->s->multi, s->curl);
|
||||||
|
|
||||||
@@ -543,12 +553,20 @@ static void curl_clean_state(CURLState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->in_use = 0;
|
s->in_use = 0;
|
||||||
|
|
||||||
|
next = QSIMPLEQ_FIRST(&s->s->free_state_waitq);
|
||||||
|
if (next) {
|
||||||
|
QSIMPLEQ_REMOVE_HEAD(&s->s->free_state_waitq, next);
|
||||||
|
qemu_mutex_unlock(&s->s->mutex);
|
||||||
|
aio_co_wake(next->co);
|
||||||
|
qemu_mutex_lock(&s->s->mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void curl_parse_filename(const char *filename, QDict *options,
|
static void curl_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename));
|
qdict_put_str(options, CURL_BLOCK_OPT_URL, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void curl_detach_aio_context(BlockDriverState *bs)
|
static void curl_detach_aio_context(BlockDriverState *bs)
|
||||||
@@ -556,6 +574,7 @@ static void curl_detach_aio_context(BlockDriverState *bs)
|
|||||||
BDRVCURLState *s = bs->opaque;
|
BDRVCURLState *s = bs->opaque;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
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]);
|
||||||
@@ -571,6 +590,7 @@ static void curl_detach_aio_context(BlockDriverState *bs)
|
|||||||
curl_multi_cleanup(s->multi);
|
curl_multi_cleanup(s->multi);
|
||||||
s->multi = NULL;
|
s->multi = NULL;
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
timer_del(&s->timer);
|
timer_del(&s->timer);
|
||||||
}
|
}
|
||||||
@@ -623,6 +643,11 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Pass the cookie or list of cookies with each request"
|
.help = "Pass the cookie or list of cookies with each request"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_COOKIE_SECRET,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret used as cookie passed with each request"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = CURL_BLOCK_OPT_USERNAME,
|
.name = CURL_BLOCK_OPT_USERNAME,
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
@@ -657,6 +682,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *file;
|
const char *file;
|
||||||
const char *cookie;
|
const char *cookie;
|
||||||
|
const char *cookie_secret;
|
||||||
double d;
|
double d;
|
||||||
const char *secretid;
|
const char *secretid;
|
||||||
const char *protocol_delimiter;
|
const char *protocol_delimiter;
|
||||||
@@ -668,6 +694,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return -EROFS;
|
return -EROFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_mutex_init(&s->mutex);
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
@@ -693,7 +720,22 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
||||||
|
|
||||||
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
|
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
|
||||||
s->cookie = g_strdup(cookie);
|
cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
|
||||||
|
|
||||||
|
if (cookie && cookie_secret) {
|
||||||
|
error_setg(errp,
|
||||||
|
"curl driver cannot handle both cookie and cookie secret");
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookie_secret) {
|
||||||
|
s->cookie = qcrypto_secret_lookup_as_utf8(cookie_secret, errp);
|
||||||
|
if (!s->cookie) {
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s->cookie = g_strdup(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
|
file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
@@ -736,14 +778,22 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF("CURL: Opening %s\n", file);
|
DPRINTF("CURL: Opening %s\n", file);
|
||||||
|
QSIMPLEQ_INIT(&s->free_state_waitq);
|
||||||
s->aio_context = bdrv_get_aio_context(bs);
|
s->aio_context = bdrv_get_aio_context(bs);
|
||||||
s->url = g_strdup(file);
|
s->url = g_strdup(file);
|
||||||
state = curl_init_state(bs, s);
|
qemu_mutex_lock(&s->mutex);
|
||||||
if (!state)
|
state = curl_find_state(s);
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
if (!state) {
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
|
||||||
// Get file size
|
// Get file size
|
||||||
|
|
||||||
|
if (curl_init_state(s, state) < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
s->accept_range = false;
|
s->accept_range = false;
|
||||||
curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
|
curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION,
|
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION,
|
||||||
@@ -771,7 +821,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s->len = (size_t)d;
|
s->len = d;
|
||||||
|
|
||||||
if ((!strncasecmp(s->url, "http://", strlen("http://"))
|
if ((!strncasecmp(s->url, "http://", strlen("http://"))
|
||||||
|| !strncasecmp(s->url, "https://", strlen("https://")))
|
|| !strncasecmp(s->url, "https://", strlen("https://")))
|
||||||
@@ -780,13 +830,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
"Server does not support 'range' (byte ranges).");
|
"Server does not support 'range' (byte ranges).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
DPRINTF("CURL: Size = %zd\n", s->len);
|
DPRINTF("CURL: Size = %" PRIu64 "\n", s->len);
|
||||||
|
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
curl_easy_cleanup(state->curl);
|
curl_easy_cleanup(state->curl);
|
||||||
state->curl = NULL;
|
state->curl = NULL;
|
||||||
|
|
||||||
qemu_mutex_init(&s->mutex);
|
|
||||||
curl_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
curl_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
@@ -797,53 +848,51 @@ out:
|
|||||||
curl_easy_cleanup(state->curl);
|
curl_easy_cleanup(state->curl);
|
||||||
state->curl = NULL;
|
state->curl = NULL;
|
||||||
out_noclean:
|
out_noclean:
|
||||||
|
qemu_mutex_destroy(&s->mutex);
|
||||||
g_free(s->cookie);
|
g_free(s->cookie);
|
||||||
g_free(s->url);
|
g_free(s->url);
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AIOCBInfo curl_aiocb_info = {
|
static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
.aiocb_size = sizeof(CURLAIOCB),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void curl_readv_bh_cb(void *p)
|
|
||||||
{
|
{
|
||||||
CURLState *state;
|
CURLState *state;
|
||||||
int running;
|
int running;
|
||||||
int ret = -EINPROGRESS;
|
|
||||||
|
|
||||||
CURLAIOCB *acb = p;
|
|
||||||
BlockDriverState *bs = acb->common.bs;
|
|
||||||
BDRVCURLState *s = bs->opaque;
|
BDRVCURLState *s = bs->opaque;
|
||||||
|
|
||||||
size_t start = acb->sector_num * BDRV_SECTOR_SIZE;
|
uint64_t start = acb->offset;
|
||||||
size_t end;
|
uint64_t end;
|
||||||
|
|
||||||
qemu_mutex_lock(&s->mutex);
|
qemu_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
// In case we have the requested data already (e.g. read-ahead),
|
// In case we have the requested data already (e.g. read-ahead),
|
||||||
// we can just call the callback and be done.
|
// we can just call the callback and be done.
|
||||||
switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) {
|
if (curl_find_buf(s, start, acb->bytes, acb)) {
|
||||||
case FIND_RET_OK:
|
goto out;
|
||||||
qemu_aio_unref(acb);
|
|
||||||
// fall through
|
|
||||||
case FIND_RET_WAIT:
|
|
||||||
goto out;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No cache found, so let's start a new request
|
// No cache found, so let's start a new request
|
||||||
state = curl_init_state(acb->common.bs, s);
|
for (;;) {
|
||||||
if (!state) {
|
state = curl_find_state(s);
|
||||||
ret = -EIO;
|
if (state) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&s->free_state_waitq, acb, next);
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curl_init_state(s, state) < 0) {
|
||||||
|
curl_clean_state(state);
|
||||||
|
acb->ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
acb->start = 0;
|
acb->start = 0;
|
||||||
acb->end = MIN(acb->nb_sectors * BDRV_SECTOR_SIZE, s->len - start);
|
acb->end = MIN(acb->bytes, s->len - start);
|
||||||
|
|
||||||
state->buf_off = 0;
|
state->buf_off = 0;
|
||||||
g_free(state->orig_buf);
|
g_free(state->orig_buf);
|
||||||
@@ -853,14 +902,14 @@ static void curl_readv_bh_cb(void *p)
|
|||||||
state->orig_buf = g_try_malloc(state->buf_len);
|
state->orig_buf = g_try_malloc(state->buf_len);
|
||||||
if (state->buf_len && state->orig_buf == NULL) {
|
if (state->buf_len && state->orig_buf == NULL) {
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
ret = -ENOMEM;
|
acb->ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
state->acb[0] = acb;
|
state->acb[0] = acb;
|
||||||
|
|
||||||
snprintf(state->range, 127, "%zd-%zd", start, end);
|
snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
|
||||||
DPRINTF("CURL (AIO): Reading %llu at %zd (%s)\n",
|
DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n",
|
||||||
(acb->nb_sectors * BDRV_SECTOR_SIZE), start, state->range);
|
acb->bytes, 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);
|
||||||
@@ -870,26 +919,24 @@ static void curl_readv_bh_cb(void *p)
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_mutex_unlock(&s->mutex);
|
qemu_mutex_unlock(&s->mutex);
|
||||||
if (ret != -EINPROGRESS) {
|
|
||||||
acb->common.cb(acb->common.opaque, ret);
|
|
||||||
qemu_aio_unref(acb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
|
static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
|
||||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||||
BlockCompletionFunc *cb, void *opaque)
|
|
||||||
{
|
{
|
||||||
CURLAIOCB *acb;
|
CURLAIOCB acb = {
|
||||||
|
.co = qemu_coroutine_self(),
|
||||||
|
.ret = -EINPROGRESS,
|
||||||
|
.qiov = qiov,
|
||||||
|
.offset = offset,
|
||||||
|
.bytes = bytes
|
||||||
|
};
|
||||||
|
|
||||||
acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque);
|
curl_setup_preadv(bs, &acb);
|
||||||
|
while (acb.ret == -EINPROGRESS) {
|
||||||
acb->qiov = qiov;
|
qemu_coroutine_yield();
|
||||||
acb->sector_num = sector_num;
|
}
|
||||||
acb->nb_sectors = nb_sectors;
|
return acb.ret;
|
||||||
|
|
||||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb);
|
|
||||||
return &acb->common;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void curl_close(BlockDriverState *bs)
|
static void curl_close(BlockDriverState *bs)
|
||||||
@@ -920,7 +967,7 @@ static BlockDriver bdrv_http = {
|
|||||||
.bdrv_close = curl_close,
|
.bdrv_close = curl_close,
|
||||||
.bdrv_getlength = curl_getlength,
|
.bdrv_getlength = curl_getlength,
|
||||||
|
|
||||||
.bdrv_aio_readv = curl_aio_readv,
|
.bdrv_co_preadv = curl_co_preadv,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
@@ -936,7 +983,7 @@ static BlockDriver bdrv_https = {
|
|||||||
.bdrv_close = curl_close,
|
.bdrv_close = curl_close,
|
||||||
.bdrv_getlength = curl_getlength,
|
.bdrv_getlength = curl_getlength,
|
||||||
|
|
||||||
.bdrv_aio_readv = curl_aio_readv,
|
.bdrv_co_preadv = curl_co_preadv,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
@@ -952,7 +999,7 @@ static BlockDriver bdrv_ftp = {
|
|||||||
.bdrv_close = curl_close,
|
.bdrv_close = curl_close,
|
||||||
.bdrv_getlength = curl_getlength,
|
.bdrv_getlength = curl_getlength,
|
||||||
|
|
||||||
.bdrv_aio_readv = curl_aio_readv,
|
.bdrv_co_preadv = curl_co_preadv,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
@@ -968,7 +1015,7 @@ static BlockDriver bdrv_ftps = {
|
|||||||
.bdrv_close = curl_close,
|
.bdrv_close = curl_close,
|
||||||
.bdrv_getlength = curl_getlength,
|
.bdrv_getlength = curl_getlength,
|
||||||
|
|
||||||
.bdrv_aio_readv = curl_aio_readv,
|
.bdrv_co_preadv = curl_co_preadv,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
|
|||||||
@@ -25,8 +25,6 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/timer.h"
|
|
||||||
#include "qemu/log.h"
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
@@ -131,12 +129,23 @@ do { \
|
|||||||
|
|
||||||
#define MAX_BLOCKSIZE 4096
|
#define MAX_BLOCKSIZE 4096
|
||||||
|
|
||||||
|
/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes,
|
||||||
|
* leaving a few more bytes for its future use. */
|
||||||
|
#define RAW_LOCK_PERM_BASE 100
|
||||||
|
#define RAW_LOCK_SHARED_BASE 200
|
||||||
|
|
||||||
typedef struct BDRVRawState {
|
typedef struct BDRVRawState {
|
||||||
int fd;
|
int fd;
|
||||||
|
int lock_fd;
|
||||||
|
bool use_lock;
|
||||||
int type;
|
int type;
|
||||||
int open_flags;
|
int open_flags;
|
||||||
size_t buf_align;
|
size_t buf_align;
|
||||||
|
|
||||||
|
/* The current permissions. */
|
||||||
|
uint64_t perm;
|
||||||
|
uint64_t shared_perm;
|
||||||
|
|
||||||
#ifdef CONFIG_XFS
|
#ifdef CONFIG_XFS
|
||||||
bool is_xfs:1;
|
bool is_xfs:1;
|
||||||
#endif
|
#endif
|
||||||
@@ -377,7 +386,7 @@ static void raw_parse_filename(const char *filename, QDict *options,
|
|||||||
* function call can be ignored. */
|
* function call can be ignored. */
|
||||||
strstart(filename, "file:", &filename);
|
strstart(filename, "file:", &filename);
|
||||||
|
|
||||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
qdict_put_str(options, "filename", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList raw_runtime_opts = {
|
static QemuOptsList raw_runtime_opts = {
|
||||||
@@ -394,6 +403,11 @@ static QemuOptsList raw_runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "host AIO implementation (threads, native)",
|
.help = "host AIO implementation (threads, native)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "locking",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "file locking mode (on/off/auto, default: auto)",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -408,6 +422,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||||||
BlockdevAioOptions aio, aio_default;
|
BlockdevAioOptions aio, aio_default;
|
||||||
int fd, ret;
|
int fd, ret;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
OnOffAuto locking;
|
||||||
|
|
||||||
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
@@ -437,6 +452,37 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||||||
}
|
}
|
||||||
s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE);
|
s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE);
|
||||||
|
|
||||||
|
locking = qapi_enum_parse(OnOffAuto_lookup, qemu_opt_get(opts, "locking"),
|
||||||
|
ON_OFF_AUTO__MAX, ON_OFF_AUTO_AUTO, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
switch (locking) {
|
||||||
|
case ON_OFF_AUTO_ON:
|
||||||
|
s->use_lock = true;
|
||||||
|
#ifndef F_OFD_SETLK
|
||||||
|
fprintf(stderr,
|
||||||
|
"File lock requested but OFD locking syscall is unavailable, "
|
||||||
|
"falling back to POSIX file locks.\n"
|
||||||
|
"Due to the implementation, locks can be lost unexpectedly.\n");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case ON_OFF_AUTO_OFF:
|
||||||
|
s->use_lock = false;
|
||||||
|
break;
|
||||||
|
case ON_OFF_AUTO_AUTO:
|
||||||
|
#ifdef F_OFD_SETLK
|
||||||
|
s->use_lock = true;
|
||||||
|
#else
|
||||||
|
s->use_lock = false;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
s->open_flags = open_flags;
|
s->open_flags = open_flags;
|
||||||
raw_parse_flags(bdrv_flags, &s->open_flags);
|
raw_parse_flags(bdrv_flags, &s->open_flags);
|
||||||
|
|
||||||
@@ -452,6 +498,21 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||||||
}
|
}
|
||||||
s->fd = fd;
|
s->fd = fd;
|
||||||
|
|
||||||
|
s->lock_fd = -1;
|
||||||
|
if (s->use_lock) {
|
||||||
|
fd = qemu_open(filename, s->open_flags);
|
||||||
|
if (fd < 0) {
|
||||||
|
ret = -errno;
|
||||||
|
error_setg_errno(errp, errno, "Could not open '%s' for locking",
|
||||||
|
filename);
|
||||||
|
qemu_close(s->fd);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
s->lock_fd = fd;
|
||||||
|
}
|
||||||
|
s->perm = 0;
|
||||||
|
s->shared_perm = BLK_PERM_ALL;
|
||||||
|
|
||||||
#ifdef CONFIG_LINUX_AIO
|
#ifdef CONFIG_LINUX_AIO
|
||||||
/* Currently Linux does AIO only for files opened with O_DIRECT */
|
/* Currently Linux does AIO only for files opened with O_DIRECT */
|
||||||
if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) {
|
if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) {
|
||||||
@@ -539,6 +600,161 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return raw_open_common(bs, options, flags, 0, errp);
|
return raw_open_common(bs, options, flags, 0, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RAW_PL_PREPARE,
|
||||||
|
RAW_PL_COMMIT,
|
||||||
|
RAW_PL_ABORT,
|
||||||
|
} RawPermLockOp;
|
||||||
|
|
||||||
|
#define PERM_FOREACH(i) \
|
||||||
|
for ((i) = 0; (1ULL << (i)) <= BLK_PERM_ALL; i++)
|
||||||
|
|
||||||
|
/* Lock bytes indicated by @perm_lock_bits and @shared_perm_lock_bits in the
|
||||||
|
* file; if @unlock == true, also unlock the unneeded bytes.
|
||||||
|
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
|
||||||
|
*/
|
||||||
|
static int raw_apply_lock_bytes(BDRVRawState *s,
|
||||||
|
uint64_t perm_lock_bits,
|
||||||
|
uint64_t shared_perm_lock_bits,
|
||||||
|
bool unlock, Error **errp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
PERM_FOREACH(i) {
|
||||||
|
int off = RAW_LOCK_PERM_BASE + i;
|
||||||
|
if (perm_lock_bits & (1ULL << i)) {
|
||||||
|
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||||
|
if (ret) {
|
||||||
|
error_setg(errp, "Failed to lock byte %d", off);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (unlock) {
|
||||||
|
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||||
|
if (ret) {
|
||||||
|
error_setg(errp, "Failed to unlock byte %d", off);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PERM_FOREACH(i) {
|
||||||
|
int off = RAW_LOCK_SHARED_BASE + i;
|
||||||
|
if (shared_perm_lock_bits & (1ULL << i)) {
|
||||||
|
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||||
|
if (ret) {
|
||||||
|
error_setg(errp, "Failed to lock byte %d", off);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (unlock) {
|
||||||
|
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||||
|
if (ret) {
|
||||||
|
error_setg(errp, "Failed to unlock byte %d", off);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
|
||||||
|
static int raw_check_lock_bytes(BDRVRawState *s,
|
||||||
|
uint64_t perm, uint64_t shared_perm,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
PERM_FOREACH(i) {
|
||||||
|
int off = RAW_LOCK_SHARED_BASE + i;
|
||||||
|
uint64_t p = 1ULL << i;
|
||||||
|
if (perm & p) {
|
||||||
|
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||||
|
if (ret) {
|
||||||
|
char *perm_name = bdrv_perm_names(p);
|
||||||
|
error_setg(errp,
|
||||||
|
"Failed to get \"%s\" lock",
|
||||||
|
perm_name);
|
||||||
|
g_free(perm_name);
|
||||||
|
error_append_hint(errp,
|
||||||
|
"Is another process using the image?\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PERM_FOREACH(i) {
|
||||||
|
int off = RAW_LOCK_PERM_BASE + i;
|
||||||
|
uint64_t p = 1ULL << i;
|
||||||
|
if (!(shared_perm & p)) {
|
||||||
|
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||||
|
if (ret) {
|
||||||
|
char *perm_name = bdrv_perm_names(p);
|
||||||
|
error_setg(errp,
|
||||||
|
"Failed to get shared \"%s\" lock",
|
||||||
|
perm_name);
|
||||||
|
g_free(perm_name);
|
||||||
|
error_append_hint(errp,
|
||||||
|
"Is another process using the image?\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||||
|
RawPermLockOp op,
|
||||||
|
uint64_t new_perm, uint64_t new_shared,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
int ret = 0;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (!s->use_lock) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(s->lock_fd > 0);
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case RAW_PL_PREPARE:
|
||||||
|
ret = raw_apply_lock_bytes(s, s->perm | new_perm,
|
||||||
|
~s->shared_perm | ~new_shared,
|
||||||
|
false, errp);
|
||||||
|
if (!ret) {
|
||||||
|
ret = raw_check_lock_bytes(s, new_perm, new_shared, errp);
|
||||||
|
if (!ret) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
op = RAW_PL_ABORT;
|
||||||
|
/* fall through to unlock bytes. */
|
||||||
|
case RAW_PL_ABORT:
|
||||||
|
raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
/* Theoretically the above call only unlocks bytes and it cannot
|
||||||
|
* fail. Something weird happened, report it.
|
||||||
|
*/
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RAW_PL_COMMIT:
|
||||||
|
raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
/* Theoretically the above call only unlocks bytes and it cannot
|
||||||
|
* fail. Something weird happened, report it.
|
||||||
|
*/
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||||
BlockReopenQueue *queue, Error **errp)
|
BlockReopenQueue *queue, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -1407,26 +1623,37 @@ static void raw_close(BlockDriverState *bs)
|
|||||||
qemu_close(s->fd);
|
qemu_close(s->fd);
|
||||||
s->fd = -1;
|
s->fd = -1;
|
||||||
}
|
}
|
||||||
|
if (s->lock_fd >= 0) {
|
||||||
|
qemu_close(s->lock_fd);
|
||||||
|
s->lock_fd = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (fstat(s->fd, &st)) {
|
if (fstat(s->fd, &st)) {
|
||||||
return -errno;
|
ret = -errno;
|
||||||
|
error_setg_errno(errp, -ret, "Failed to fstat() the file");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S_ISREG(st.st_mode)) {
|
if (S_ISREG(st.st_mode)) {
|
||||||
if (ftruncate(s->fd, offset) < 0) {
|
if (ftruncate(s->fd, offset) < 0) {
|
||||||
return -errno;
|
ret = -errno;
|
||||||
|
error_setg_errno(errp, -ret, "Failed to resize the file");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
||||||
if (offset > raw_getlength(bs)) {
|
if (offset > raw_getlength(bs)) {
|
||||||
return -EINVAL;
|
error_setg(errp, "Cannot grow device files");
|
||||||
}
|
return -EINVAL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
error_setg(errp, "Resizing this file is not supported");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1944,6 +2171,25 @@ static QemuOptsList raw_create_opts = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
|
||||||
|
s->perm = perm;
|
||||||
|
s->shared_perm = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raw_abort_perm_update(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
BlockDriver bdrv_file = {
|
BlockDriver bdrv_file = {
|
||||||
.format_name = "file",
|
.format_name = "file",
|
||||||
.protocol_name = "file",
|
.protocol_name = "file",
|
||||||
@@ -1974,7 +2220,9 @@ BlockDriver bdrv_file = {
|
|||||||
.bdrv_get_info = raw_get_info,
|
.bdrv_get_info = raw_get_info,
|
||||||
.bdrv_get_allocated_file_size
|
.bdrv_get_allocated_file_size
|
||||||
= raw_get_allocated_file_size,
|
= raw_get_allocated_file_size,
|
||||||
|
.bdrv_check_perm = raw_check_perm,
|
||||||
|
.bdrv_set_perm = raw_set_perm,
|
||||||
|
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||||
.create_opts = &raw_create_opts,
|
.create_opts = &raw_create_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2150,7 +2398,7 @@ static void hdev_parse_filename(const char *filename, QDict *options,
|
|||||||
/* The prefix is optional, just as for "file". */
|
/* The prefix is optional, just as for "file". */
|
||||||
strstart(filename, "host_device:", &filename);
|
strstart(filename, "host_device:", &filename);
|
||||||
|
|
||||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
qdict_put_str(options, "filename", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hdev_is_sg(BlockDriverState *bs)
|
static bool hdev_is_sg(BlockDriverState *bs)
|
||||||
@@ -2239,7 +2487,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto hdev_open_Mac_error;
|
goto hdev_open_Mac_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "filename", qstring_from_str(bsd_path));
|
qdict_put_str(options, "filename", bsd_path);
|
||||||
|
|
||||||
hdev_open_Mac_error:
|
hdev_open_Mac_error:
|
||||||
g_free(mediaType);
|
g_free(mediaType);
|
||||||
@@ -2433,6 +2681,9 @@ static BlockDriver bdrv_host_device = {
|
|||||||
.bdrv_get_info = raw_get_info,
|
.bdrv_get_info = raw_get_info,
|
||||||
.bdrv_get_allocated_file_size
|
.bdrv_get_allocated_file_size
|
||||||
= raw_get_allocated_file_size,
|
= raw_get_allocated_file_size,
|
||||||
|
.bdrv_check_perm = raw_check_perm,
|
||||||
|
.bdrv_set_perm = raw_set_perm,
|
||||||
|
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||||
.bdrv_probe_blocksizes = hdev_probe_blocksizes,
|
.bdrv_probe_blocksizes = hdev_probe_blocksizes,
|
||||||
.bdrv_probe_geometry = hdev_probe_geometry,
|
.bdrv_probe_geometry = hdev_probe_geometry,
|
||||||
|
|
||||||
@@ -2449,7 +2700,7 @@ static void cdrom_parse_filename(const char *filename, QDict *options,
|
|||||||
/* The prefix is optional, just as for "file". */
|
/* The prefix is optional, just as for "file". */
|
||||||
strstart(filename, "host_cdrom:", &filename);
|
strstart(filename, "host_cdrom:", &filename);
|
||||||
|
|
||||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
qdict_put_str(options, "filename", filename);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/timer.h"
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "block/raw-aio.h"
|
#include "block/raw-aio.h"
|
||||||
@@ -282,7 +281,7 @@ static void raw_parse_filename(const char *filename, QDict *options,
|
|||||||
* function call can be ignored. */
|
* function call can be ignored. */
|
||||||
strstart(filename, "file:", &filename);
|
strstart(filename, "file:", &filename);
|
||||||
|
|
||||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
qdict_put_str(options, "filename", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList raw_runtime_opts = {
|
static QemuOptsList raw_runtime_opts = {
|
||||||
@@ -345,6 +344,12 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qdict_get_try_bool(options, "locking", false)) {
|
||||||
|
error_setg(errp, "locking=on is not supported on Windows");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
|
||||||
use_aio = get_aio_option(opts, flags, &local_err);
|
use_aio = get_aio_option(opts, flags, &local_err);
|
||||||
@@ -461,7 +466,7 @@ static void raw_close(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
LONG low, high;
|
LONG low, high;
|
||||||
@@ -476,11 +481,11 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
*/
|
*/
|
||||||
dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN);
|
dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN);
|
||||||
if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||||
fprintf(stderr, "SetFilePointer error: %lu\n", GetLastError());
|
error_setg_win32(errp, GetLastError(), "SetFilePointer error");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (SetEndOfFile(s->hfile) == 0) {
|
if (SetEndOfFile(s->hfile) == 0) {
|
||||||
fprintf(stderr, "SetEndOfFile error: %lu\n", GetLastError());
|
error_setg_win32(errp, GetLastError(), "SetEndOfFile error");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -669,7 +674,7 @@ static void hdev_parse_filename(const char *filename, QDict *options,
|
|||||||
/* The prefix is optional, just as for "file". */
|
/* The prefix is optional, just as for "file". */
|
||||||
strstart(filename, "host_device:", &filename);
|
strstart(filename, "host_device:", &filename);
|
||||||
|
|
||||||
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
qdict_put_str(options, "filename", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
|
|||||||
static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
|
static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
|
||||||
const char *filename)
|
const char *filename)
|
||||||
{
|
{
|
||||||
SocketAddressFlat *gsconf;
|
SocketAddress *gsconf;
|
||||||
URI *uri;
|
URI *uri;
|
||||||
QueryParams *qp = NULL;
|
QueryParams *qp = NULL;
|
||||||
bool is_unix = false;
|
bool is_unix = false;
|
||||||
@@ -332,19 +332,19 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gconf->server = g_new0(SocketAddressFlatList, 1);
|
gconf->server = g_new0(SocketAddressList, 1);
|
||||||
gconf->server->value = gsconf = g_new0(SocketAddressFlat, 1);
|
gconf->server->value = gsconf = g_new0(SocketAddress, 1);
|
||||||
|
|
||||||
/* transport */
|
/* transport */
|
||||||
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
|
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
|
||||||
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
|
gsconf->type = SOCKET_ADDRESS_TYPE_INET;
|
||||||
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
||||||
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
|
gsconf->type = SOCKET_ADDRESS_TYPE_INET;
|
||||||
} else if (!strcmp(uri->scheme, "gluster+unix")) {
|
} else if (!strcmp(uri->scheme, "gluster+unix")) {
|
||||||
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_UNIX;
|
gsconf->type = SOCKET_ADDRESS_TYPE_UNIX;
|
||||||
is_unix = true;
|
is_unix = true;
|
||||||
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
|
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
|
||||||
gsconf->type = SOCKET_ADDRESS_FLAT_TYPE_INET;
|
gsconf->type = SOCKET_ADDRESS_TYPE_INET;
|
||||||
error_report("Warning: rdma feature is not supported, falling "
|
error_report("Warning: rdma feature is not supported, falling "
|
||||||
"back to tcp");
|
"back to tcp");
|
||||||
} else {
|
} else {
|
||||||
@@ -396,7 +396,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
|
|||||||
struct glfs *glfs;
|
struct glfs *glfs;
|
||||||
int ret;
|
int ret;
|
||||||
int old_errno;
|
int old_errno;
|
||||||
SocketAddressFlatList *server;
|
SocketAddressList *server;
|
||||||
unsigned long long port;
|
unsigned long long port;
|
||||||
|
|
||||||
glfs = glfs_find_preopened(gconf->volume);
|
glfs = glfs_find_preopened(gconf->volume);
|
||||||
@@ -413,11 +413,11 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
|
|||||||
|
|
||||||
for (server = gconf->server; server; server = server->next) {
|
for (server = gconf->server; server; server = server->next) {
|
||||||
switch (server->value->type) {
|
switch (server->value->type) {
|
||||||
case SOCKET_ADDRESS_FLAT_TYPE_UNIX:
|
case SOCKET_ADDRESS_TYPE_UNIX:
|
||||||
ret = glfs_set_volfile_server(glfs, "unix",
|
ret = glfs_set_volfile_server(glfs, "unix",
|
||||||
server->value->u.q_unix.path, 0);
|
server->value->u.q_unix.path, 0);
|
||||||
break;
|
break;
|
||||||
case SOCKET_ADDRESS_FLAT_TYPE_INET:
|
case SOCKET_ADDRESS_TYPE_INET:
|
||||||
if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 ||
|
if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 ||
|
||||||
port > 65535) {
|
port > 65535) {
|
||||||
error_setg(errp, "'%s' is not a valid port number",
|
error_setg(errp, "'%s' is not a valid port number",
|
||||||
@@ -429,8 +429,8 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
|
|||||||
server->value->u.inet.host,
|
server->value->u.inet.host,
|
||||||
(int)port);
|
(int)port);
|
||||||
break;
|
break;
|
||||||
case SOCKET_ADDRESS_FLAT_TYPE_VSOCK:
|
case SOCKET_ADDRESS_TYPE_VSOCK:
|
||||||
case SOCKET_ADDRESS_FLAT_TYPE_FD:
|
case SOCKET_ADDRESS_TYPE_FD:
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
@@ -450,7 +450,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
|
|||||||
error_setg(errp, "Gluster connection for volume %s, path %s failed"
|
error_setg(errp, "Gluster connection for volume %s, path %s failed"
|
||||||
" to connect", gconf->volume, gconf->path);
|
" to connect", gconf->volume, gconf->path);
|
||||||
for (server = gconf->server; server; server = server->next) {
|
for (server = gconf->server; server; server = server->next) {
|
||||||
if (server->value->type == SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
|
if (server->value->type == SOCKET_ADDRESS_TYPE_UNIX) {
|
||||||
error_append_hint(errp, "hint: failed on socket %s ",
|
error_append_hint(errp, "hint: failed on socket %s ",
|
||||||
server->value->u.q_unix.path);
|
server->value->u.q_unix.path);
|
||||||
} else {
|
} else {
|
||||||
@@ -487,8 +487,8 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
QDict *options, Error **errp)
|
QDict *options, Error **errp)
|
||||||
{
|
{
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
SocketAddressFlat *gsconf = NULL;
|
SocketAddress *gsconf = NULL;
|
||||||
SocketAddressFlatList *curr = NULL;
|
SocketAddressList *curr = NULL;
|
||||||
QDict *backing_options = NULL;
|
QDict *backing_options = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
@@ -542,14 +542,14 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
}
|
}
|
||||||
gsconf = g_new0(SocketAddressFlat, 1);
|
gsconf = g_new0(SocketAddress, 1);
|
||||||
if (!strcmp(ptr, "tcp")) {
|
if (!strcmp(ptr, "tcp")) {
|
||||||
ptr = "inet"; /* accept legacy "tcp" */
|
ptr = "inet"; /* accept legacy "tcp" */
|
||||||
}
|
}
|
||||||
type = qapi_enum_parse(SocketAddressFlatType_lookup, ptr,
|
type = qapi_enum_parse(SocketAddressType_lookup, ptr,
|
||||||
SOCKET_ADDRESS_FLAT_TYPE__MAX, -1, NULL);
|
SOCKET_ADDRESS_TYPE__MAX, -1, NULL);
|
||||||
if (type != SOCKET_ADDRESS_FLAT_TYPE_INET
|
if (type != SOCKET_ADDRESS_TYPE_INET
|
||||||
&& type != SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
|
&& type != SOCKET_ADDRESS_TYPE_UNIX) {
|
||||||
error_setg(&local_err,
|
error_setg(&local_err,
|
||||||
"Parameter '%s' may be 'inet' or 'unix'",
|
"Parameter '%s' may be 'inet' or 'unix'",
|
||||||
GLUSTER_OPT_TYPE);
|
GLUSTER_OPT_TYPE);
|
||||||
@@ -559,7 +559,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
gsconf->type = type;
|
gsconf->type = type;
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
|
|
||||||
if (gsconf->type == SOCKET_ADDRESS_FLAT_TYPE_INET) {
|
if (gsconf->type == SOCKET_ADDRESS_TYPE_INET) {
|
||||||
/* create opts info from runtime_inet_opts list */
|
/* create opts info from runtime_inet_opts list */
|
||||||
opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, backing_options, &local_err);
|
qemu_opts_absorb_qdict(opts, backing_options, &local_err);
|
||||||
@@ -628,11 +628,11 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gconf->server == NULL) {
|
if (gconf->server == NULL) {
|
||||||
gconf->server = g_new0(SocketAddressFlatList, 1);
|
gconf->server = g_new0(SocketAddressList, 1);
|
||||||
gconf->server->value = gsconf;
|
gconf->server->value = gsconf;
|
||||||
curr = gconf->server;
|
curr = gconf->server;
|
||||||
} else {
|
} else {
|
||||||
curr->next = g_new0(SocketAddressFlatList, 1);
|
curr->next = g_new0(SocketAddressList, 1);
|
||||||
curr->next->value = gsconf;
|
curr->next->value = gsconf;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
@@ -648,7 +648,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
qapi_free_SocketAddressFlat(gsconf);
|
qapi_free_SocketAddress(gsconf);
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
g_free(str);
|
g_free(str);
|
||||||
QDECREF(backing_options);
|
QDECREF(backing_options);
|
||||||
@@ -1092,14 +1092,17 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
|||||||
return acb.ret;
|
return acb.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
|
|
||||||
ret = glfs_ftruncate(s->fd, offset);
|
ret = glfs_ftruncate(s->fd, offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return -errno;
|
ret = -errno;
|
||||||
|
error_setg_errno(errp, -ret, "Failed to truncate file");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
20
block/io.c
20
block/io.c
@@ -1362,16 +1362,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
|
|||||||
assert(!waited || !req->serialising);
|
assert(!waited || !req->serialising);
|
||||||
assert(req->overlap_offset <= offset);
|
assert(req->overlap_offset <= offset);
|
||||||
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
|
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
|
||||||
/* FIXME: Block migration uses the BlockBackend of the guest device at a
|
assert(child->perm & BLK_PERM_WRITE);
|
||||||
* point when it has not yet taken write permissions. This will be
|
assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE);
|
||||||
* fixed by a future patch, but for now we have to bypass this
|
|
||||||
* assertion for block migration to work. */
|
|
||||||
// assert(child->perm & BLK_PERM_WRITE);
|
|
||||||
/* FIXME: Because of the above, we also cannot guarantee that all format
|
|
||||||
* BDS take the BLK_PERM_RESIZE permission on their file BDS, since
|
|
||||||
* they are not obligated to do so if they do not have any parent
|
|
||||||
* that has taken the permission to write to them. */
|
|
||||||
// assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE);
|
|
||||||
|
|
||||||
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
||||||
|
|
||||||
@@ -1452,7 +1444,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
head_padding_bytes = offset & (align - 1);
|
head_padding_bytes = offset & (align - 1);
|
||||||
tail_padding_bytes = align - ((offset + bytes) & (align - 1));
|
tail_padding_bytes = (align - (offset + bytes)) & (align - 1);
|
||||||
|
|
||||||
|
|
||||||
assert(flags & BDRV_REQ_ZERO_WRITE);
|
assert(flags & BDRV_REQ_ZERO_WRITE);
|
||||||
@@ -1792,8 +1784,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (ret & BDRV_BLOCK_RAW) {
|
if (ret & BDRV_BLOCK_RAW) {
|
||||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||||
ret = bdrv_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
|
ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
|
||||||
*pnum, pnum, file);
|
*pnum, pnum, file);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2308,7 +2300,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
|||||||
|
|
||||||
bdrv_inc_in_flight(bs);
|
bdrv_inc_in_flight(bs);
|
||||||
|
|
||||||
if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
|
if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
|
||||||
bdrv_is_sg(bs)) {
|
bdrv_is_sg(bs)) {
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2059,22 +2059,24 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
static int iscsi_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (iscsilun->type != TYPE_DISK) {
|
if (iscsilun->type != TYPE_DISK) {
|
||||||
|
error_setg(errp, "Cannot resize non-disk iSCSI devices");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
iscsi_readcapacity_sync(iscsilun, &local_err);
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
error_free(local_err);
|
error_propagate(errp, local_err);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset > iscsi_getlength(bs)) {
|
if (offset > iscsi_getlength(bs)) {
|
||||||
|
error_setg(errp, "Cannot grow iSCSI devices");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s->bdev_length > base_length) {
|
if (s->bdev_length > base_length) {
|
||||||
ret = blk_truncate(s->target, s->bdev_length);
|
ret = blk_truncate(s->target, s->bdev_length, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
}
|
}
|
||||||
|
|||||||
77
block/nbd.c
77
block/nbd.c
@@ -47,7 +47,7 @@ typedef struct BDRVNBDState {
|
|||||||
NBDClientSession client;
|
NBDClientSession client;
|
||||||
|
|
||||||
/* For nbd_refresh_filename() */
|
/* For nbd_refresh_filename() */
|
||||||
SocketAddressFlat *saddr;
|
SocketAddress *saddr;
|
||||||
char *export, *tlscredsid;
|
char *export, *tlscredsid;
|
||||||
} BDRVNBDState;
|
} BDRVNBDState;
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ static int nbd_parse_uri(const char *filename, QDict *options)
|
|||||||
p = uri->path ? uri->path : "/";
|
p = uri->path ? uri->path : "/";
|
||||||
p += strspn(p, "/");
|
p += strspn(p, "/");
|
||||||
if (p[0]) {
|
if (p[0]) {
|
||||||
qdict_put(options, "export", qstring_from_str(p));
|
qdict_put_str(options, "export", p);
|
||||||
}
|
}
|
||||||
|
|
||||||
qp = query_params_parse(uri->query);
|
qp = query_params_parse(uri->query);
|
||||||
@@ -94,9 +94,8 @@ static int nbd_parse_uri(const char *filename, QDict *options)
|
|||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
qdict_put(options, "server.type", qstring_from_str("unix"));
|
qdict_put_str(options, "server.type", "unix");
|
||||||
qdict_put(options, "server.path",
|
qdict_put_str(options, "server.path", qp->p[0].value);
|
||||||
qstring_from_str(qp->p[0].value));
|
|
||||||
} else {
|
} else {
|
||||||
QString *host;
|
QString *host;
|
||||||
char *port_str;
|
char *port_str;
|
||||||
@@ -115,11 +114,11 @@ static int nbd_parse_uri(const char *filename, QDict *options)
|
|||||||
host = qstring_from_str(uri->server);
|
host = qstring_from_str(uri->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "server.type", qstring_from_str("inet"));
|
qdict_put_str(options, "server.type", "inet");
|
||||||
qdict_put(options, "server.host", host);
|
qdict_put(options, "server.host", host);
|
||||||
|
|
||||||
port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT);
|
port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT);
|
||||||
qdict_put(options, "server.port", qstring_from_str(port_str));
|
qdict_put_str(options, "server.port", port_str);
|
||||||
g_free(port_str);
|
g_free(port_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +180,7 @@ static void nbd_parse_filename(const char *filename, QDict *options,
|
|||||||
export_name[0] = 0; /* truncate 'file' */
|
export_name[0] = 0; /* truncate 'file' */
|
||||||
export_name += strlen(EN_OPTSTR);
|
export_name += strlen(EN_OPTSTR);
|
||||||
|
|
||||||
qdict_put(options, "export", qstring_from_str(export_name));
|
qdict_put_str(options, "export", export_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract the host_spec - fail if it's not nbd:... */
|
/* extract the host_spec - fail if it's not nbd:... */
|
||||||
@@ -196,19 +195,19 @@ static void nbd_parse_filename(const char *filename, QDict *options,
|
|||||||
|
|
||||||
/* are we a UNIX or TCP socket? */
|
/* are we a UNIX or TCP socket? */
|
||||||
if (strstart(host_spec, "unix:", &unixpath)) {
|
if (strstart(host_spec, "unix:", &unixpath)) {
|
||||||
qdict_put(options, "server.type", qstring_from_str("unix"));
|
qdict_put_str(options, "server.type", "unix");
|
||||||
qdict_put(options, "server.path", qstring_from_str(unixpath));
|
qdict_put_str(options, "server.path", unixpath);
|
||||||
} else {
|
} else {
|
||||||
InetSocketAddress *addr = NULL;
|
InetSocketAddress *addr = g_new(InetSocketAddress, 1);
|
||||||
|
|
||||||
addr = inet_parse(host_spec, errp);
|
if (inet_parse(addr, host_spec, errp)) {
|
||||||
if (!addr) {
|
goto out_inet;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "server.type", qstring_from_str("inet"));
|
qdict_put_str(options, "server.type", "inet");
|
||||||
qdict_put(options, "server.host", qstring_from_str(addr->host));
|
qdict_put_str(options, "server.host", addr->host);
|
||||||
qdict_put(options, "server.port", qstring_from_str(addr->port));
|
qdict_put_str(options, "server.port", addr->port);
|
||||||
|
out_inet:
|
||||||
qapi_free_InetSocketAddress(addr);
|
qapi_free_InetSocketAddress(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,22 +246,22 @@ static bool nbd_process_legacy_socket_options(QDict *output_options,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(output_options, "server.type", qstring_from_str("unix"));
|
qdict_put_str(output_options, "server.type", "unix");
|
||||||
qdict_put(output_options, "server.path", qstring_from_str(path));
|
qdict_put_str(output_options, "server.path", path);
|
||||||
} else if (host) {
|
} else if (host) {
|
||||||
qdict_put(output_options, "server.type", qstring_from_str("inet"));
|
qdict_put_str(output_options, "server.type", "inet");
|
||||||
qdict_put(output_options, "server.host", qstring_from_str(host));
|
qdict_put_str(output_options, "server.host", host);
|
||||||
qdict_put(output_options, "server.port",
|
qdict_put_str(output_options, "server.port",
|
||||||
qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT)));
|
port ?: stringify(NBD_DEFAULT_PORT));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SocketAddressFlat *nbd_config(BDRVNBDState *s, QDict *options,
|
static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
SocketAddressFlat *saddr = NULL;
|
SocketAddress *saddr = NULL;
|
||||||
QDict *addr = NULL;
|
QDict *addr = NULL;
|
||||||
QObject *crumpled_addr = NULL;
|
QObject *crumpled_addr = NULL;
|
||||||
Visitor *iv = NULL;
|
Visitor *iv = NULL;
|
||||||
@@ -288,7 +287,7 @@ static SocketAddressFlat *nbd_config(BDRVNBDState *s, QDict *options,
|
|||||||
* visitor expects the former.
|
* visitor expects the former.
|
||||||
*/
|
*/
|
||||||
iv = qobject_input_visitor_new(crumpled_addr);
|
iv = qobject_input_visitor_new(crumpled_addr);
|
||||||
visit_type_SocketAddressFlat(iv, NULL, &saddr, &local_err);
|
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto done;
|
goto done;
|
||||||
@@ -307,10 +306,9 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs)
|
|||||||
return &s->client;
|
return &s->client;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QIOChannelSocket *nbd_establish_connection(SocketAddressFlat *saddr_flat,
|
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
SocketAddress *saddr = socket_address_crumple(saddr_flat);
|
|
||||||
QIOChannelSocket *sioc;
|
QIOChannelSocket *sioc;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
@@ -320,7 +318,6 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddressFlat *saddr_flat,
|
|||||||
qio_channel_socket_connect_sync(sioc,
|
qio_channel_socket_connect_sync(sioc,
|
||||||
saddr,
|
saddr,
|
||||||
&local_err);
|
&local_err);
|
||||||
qapi_free_SocketAddress(saddr);
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -413,7 +410,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translate @host, @port, and @path to a SocketAddressFlat */
|
/* Translate @host, @port, and @path to a SocketAddress */
|
||||||
if (!nbd_process_legacy_socket_options(options, opts, errp)) {
|
if (!nbd_process_legacy_socket_options(options, opts, errp)) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -434,7 +431,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */
|
/* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */
|
||||||
if (s->saddr->type != SOCKET_ADDRESS_FLAT_TYPE_INET) {
|
if (s->saddr->type != SOCKET_ADDRESS_TYPE_INET) {
|
||||||
error_setg(errp, "TLS only supported over IP sockets");
|
error_setg(errp, "TLS only supported over IP sockets");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -461,7 +458,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
object_unref(OBJECT(tlscreds));
|
object_unref(OBJECT(tlscreds));
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qapi_free_SocketAddressFlat(s->saddr);
|
qapi_free_SocketAddress(s->saddr);
|
||||||
g_free(s->export);
|
g_free(s->export);
|
||||||
g_free(s->tlscredsid);
|
g_free(s->tlscredsid);
|
||||||
}
|
}
|
||||||
@@ -487,7 +484,7 @@ static void nbd_close(BlockDriverState *bs)
|
|||||||
|
|
||||||
nbd_client_close(bs);
|
nbd_client_close(bs);
|
||||||
|
|
||||||
qapi_free_SocketAddressFlat(s->saddr);
|
qapi_free_SocketAddress(s->saddr);
|
||||||
g_free(s->export);
|
g_free(s->export);
|
||||||
g_free(s->tlscredsid);
|
g_free(s->tlscredsid);
|
||||||
}
|
}
|
||||||
@@ -518,17 +515,17 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
Visitor *ov;
|
Visitor *ov;
|
||||||
const char *host = NULL, *port = NULL, *path = NULL;
|
const char *host = NULL, *port = NULL, *path = NULL;
|
||||||
|
|
||||||
if (s->saddr->type == SOCKET_ADDRESS_FLAT_TYPE_INET) {
|
if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) {
|
||||||
const InetSocketAddress *inet = &s->saddr->u.inet;
|
const InetSocketAddress *inet = &s->saddr->u.inet;
|
||||||
if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) {
|
if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) {
|
||||||
host = inet->host;
|
host = inet->host;
|
||||||
port = inet->port;
|
port = inet->port;
|
||||||
}
|
}
|
||||||
} else if (s->saddr->type == SOCKET_ADDRESS_FLAT_TYPE_UNIX) {
|
} else if (s->saddr->type == SOCKET_ADDRESS_TYPE_UNIX) {
|
||||||
path = s->saddr->u.q_unix.path;
|
path = s->saddr->u.q_unix.path;
|
||||||
} /* else can't represent as pseudo-filename */
|
} /* else can't represent as pseudo-filename */
|
||||||
|
|
||||||
qdict_put(opts, "driver", qstring_from_str("nbd"));
|
qdict_put_str(opts, "driver", "nbd");
|
||||||
|
|
||||||
if (path && s->export) {
|
if (path && s->export) {
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
@@ -545,16 +542,16 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ov = qobject_output_visitor_new(&saddr_qdict);
|
ov = qobject_output_visitor_new(&saddr_qdict);
|
||||||
visit_type_SocketAddressFlat(ov, NULL, &s->saddr, &error_abort);
|
visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
|
||||||
visit_complete(ov, &saddr_qdict);
|
visit_complete(ov, &saddr_qdict);
|
||||||
visit_free(ov);
|
visit_free(ov);
|
||||||
qdict_put_obj(opts, "server", saddr_qdict);
|
qdict_put_obj(opts, "server", saddr_qdict);
|
||||||
|
|
||||||
if (s->export) {
|
if (s->export) {
|
||||||
qdict_put(opts, "export", qstring_from_str(s->export));
|
qdict_put_str(opts, "export", s->export);
|
||||||
}
|
}
|
||||||
if (s->tlscredsid) {
|
if (s->tlscredsid) {
|
||||||
qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid));
|
qdict_put_str(opts, "tls-creds", s->tlscredsid);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_flatten(opts);
|
qdict_flatten(opts);
|
||||||
|
|||||||
55
block/nfs.c
55
block/nfs.c
@@ -104,9 +104,9 @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "server.host", qstring_from_str(uri->server));
|
qdict_put_str(options, "server.host", uri->server);
|
||||||
qdict_put(options, "server.type", qstring_from_str("inet"));
|
qdict_put_str(options, "server.type", "inet");
|
||||||
qdict_put(options, "path", qstring_from_str(uri->path));
|
qdict_put_str(options, "path", uri->path);
|
||||||
|
|
||||||
for (i = 0; i < qp->n; i++) {
|
for (i = 0; i < qp->n; i++) {
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
@@ -121,23 +121,17 @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!strcmp(qp->p[i].name, "uid")) {
|
if (!strcmp(qp->p[i].name, "uid")) {
|
||||||
qdict_put(options, "user",
|
qdict_put_str(options, "user", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else if (!strcmp(qp->p[i].name, "gid")) {
|
} else if (!strcmp(qp->p[i].name, "gid")) {
|
||||||
qdict_put(options, "group",
|
qdict_put_str(options, "group", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
|
} else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
|
||||||
qdict_put(options, "tcp-syn-count",
|
qdict_put_str(options, "tcp-syn-count", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else if (!strcmp(qp->p[i].name, "readahead")) {
|
} else if (!strcmp(qp->p[i].name, "readahead")) {
|
||||||
qdict_put(options, "readahead-size",
|
qdict_put_str(options, "readahead-size", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else if (!strcmp(qp->p[i].name, "pagecache")) {
|
} else if (!strcmp(qp->p[i].name, "pagecache")) {
|
||||||
qdict_put(options, "page-cache-size",
|
qdict_put_str(options, "page-cache-size", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else if (!strcmp(qp->p[i].name, "debug")) {
|
} else if (!strcmp(qp->p[i].name, "debug")) {
|
||||||
qdict_put(options, "debug",
|
qdict_put_str(options, "debug", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||||
qp->p[i].name);
|
qp->p[i].name);
|
||||||
@@ -764,10 +758,18 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
|||||||
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
|
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset)
|
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
NFSClient *client = bs->opaque;
|
NFSClient *client = bs->opaque;
|
||||||
return nfs_ftruncate(client->context, client->fh, offset);
|
int ret;
|
||||||
|
|
||||||
|
ret = nfs_ftruncate(client->context, client->fh, offset);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to truncate file");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note that this will not re-establish a connection with the NFS server
|
/* Note that this will not re-establish a connection with the NFS server
|
||||||
@@ -811,7 +813,7 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
QObject *server_qdict;
|
QObject *server_qdict;
|
||||||
Visitor *ov;
|
Visitor *ov;
|
||||||
|
|
||||||
qdict_put(opts, "driver", qstring_from_str("nfs"));
|
qdict_put_str(opts, "driver", "nfs");
|
||||||
|
|
||||||
if (client->uid && !client->gid) {
|
if (client->uid && !client->gid) {
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
@@ -834,28 +836,25 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
|
visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
|
||||||
visit_complete(ov, &server_qdict);
|
visit_complete(ov, &server_qdict);
|
||||||
qdict_put_obj(opts, "server", server_qdict);
|
qdict_put_obj(opts, "server", server_qdict);
|
||||||
qdict_put(opts, "path", qstring_from_str(client->path));
|
qdict_put_str(opts, "path", client->path);
|
||||||
|
|
||||||
if (client->uid) {
|
if (client->uid) {
|
||||||
qdict_put(opts, "user", qint_from_int(client->uid));
|
qdict_put_int(opts, "user", client->uid);
|
||||||
}
|
}
|
||||||
if (client->gid) {
|
if (client->gid) {
|
||||||
qdict_put(opts, "group", qint_from_int(client->gid));
|
qdict_put_int(opts, "group", client->gid);
|
||||||
}
|
}
|
||||||
if (client->tcp_syncnt) {
|
if (client->tcp_syncnt) {
|
||||||
qdict_put(opts, "tcp-syn-cnt",
|
qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt);
|
||||||
qint_from_int(client->tcp_syncnt));
|
|
||||||
}
|
}
|
||||||
if (client->readahead) {
|
if (client->readahead) {
|
||||||
qdict_put(opts, "readahead-size",
|
qdict_put_int(opts, "readahead-size", client->readahead);
|
||||||
qint_from_int(client->readahead));
|
|
||||||
}
|
}
|
||||||
if (client->pagecache) {
|
if (client->pagecache) {
|
||||||
qdict_put(opts, "page-cache-size",
|
qdict_put_int(opts, "page-cache-size", client->pagecache);
|
||||||
qint_from_int(client->pagecache));
|
|
||||||
}
|
}
|
||||||
if (client->debug) {
|
if (client->debug) {
|
||||||
qdict_put(opts, "debug", qint_from_int(client->debug));
|
qdict_put_int(opts, "debug", client->debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_free(ov);
|
visit_free(ov);
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
|||||||
bs->drv->format_name);
|
bs->drv->format_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
|
qdict_put_str(opts, "driver", bs->drv->format_name);
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,8 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
|||||||
space << BDRV_SECTOR_BITS, 0);
|
space << BDRV_SECTOR_BITS, 0);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_truncate(bs->file,
|
ret = bdrv_truncate(bs->file,
|
||||||
(s->data_end + space) << BDRV_SECTOR_BITS);
|
(s->data_end + space) << BDRV_SECTOR_BITS,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@@ -456,8 +457,10 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
size - res->image_end_offset);
|
size - res->image_end_offset);
|
||||||
res->leaks += count;
|
res->leaks += count;
|
||||||
if (fix & BDRV_FIX_LEAKS) {
|
if (fix & BDRV_FIX_LEAKS) {
|
||||||
ret = bdrv_truncate(bs->file, res->image_end_offset);
|
Error *local_err = NULL;
|
||||||
|
ret = bdrv_truncate(bs->file, res->image_end_offset, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
res->check_errors++;
|
res->check_errors++;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -504,7 +507,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
blk_set_allow_write_beyond_eof(file, true);
|
blk_set_allow_write_beyond_eof(file, true);
|
||||||
|
|
||||||
ret = blk_truncate(file, 0);
|
ret = blk_truncate(file, 0, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -696,7 +699,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & BDRV_O_RESIZE) || !bdrv_has_zero_init(bs->file->bs) ||
|
if (!(flags & BDRV_O_RESIZE) || !bdrv_has_zero_init(bs->file->bs) ||
|
||||||
bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs)) != 0) {
|
bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs), NULL) != 0) {
|
||||||
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
|
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,7 +742,7 @@ static void parallels_close(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bs->open_flags & BDRV_O_RDWR) {
|
if (bs->open_flags & BDRV_O_RDWR) {
|
||||||
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS);
|
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->bat_dirty_bmap);
|
g_free(s->bat_dirty_bmap);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "crypto/cipher.h"
|
#include "crypto/cipher.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
|
|
||||||
/**************************************************************/
|
/**************************************************************/
|
||||||
/* QEMU COW block driver with compression and encryption support */
|
/* QEMU COW block driver with compression and encryption support */
|
||||||
@@ -473,7 +473,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
|||||||
/* 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(bs->file, cluster_offset + s->cluster_size, NULL);
|
||||||
/* 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 (bs->encrypted &&
|
if (bs->encrypted &&
|
||||||
@@ -833,7 +833,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||||
|
|
||||||
ret = blk_truncate(qcow_blk, 0);
|
ret = blk_truncate(qcow_blk, 0, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -916,7 +916,7 @@ static int qcow_make_empty(BlockDriverState *bs)
|
|||||||
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
|
if (bdrv_pwrite_sync(bs->file, 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(bs->file, s->l1_table_offset + l1_length, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|||||||
@@ -309,14 +309,19 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
|||||||
uint64_t *l2_table, uint64_t stop_flags)
|
uint64_t *l2_table, uint64_t stop_flags)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
QCow2ClusterType first_cluster_type;
|
||||||
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
||||||
uint64_t first_entry = be64_to_cpu(l2_table[0]);
|
uint64_t first_entry = be64_to_cpu(l2_table[0]);
|
||||||
uint64_t offset = first_entry & mask;
|
uint64_t offset = first_entry & mask;
|
||||||
|
|
||||||
if (!offset)
|
if (!offset) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
|
/* must be allocated */
|
||||||
|
first_cluster_type = qcow2_get_cluster_type(first_entry);
|
||||||
|
assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||||
|
first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
||||||
@@ -328,14 +333,21 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int count_contiguous_clusters_by_type(int nb_clusters,
|
/*
|
||||||
uint64_t *l2_table,
|
* Checks how many consecutive unallocated clusters in a given L2
|
||||||
int wanted_type)
|
* table have the same cluster type.
|
||||||
|
*/
|
||||||
|
static int count_contiguous_clusters_unallocated(int nb_clusters,
|
||||||
|
uint64_t *l2_table,
|
||||||
|
QCow2ClusterType wanted_type)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||||
|
wanted_type == QCOW2_CLUSTER_UNALLOCATED);
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
uint64_t entry = be64_to_cpu(l2_table[i]);
|
||||||
|
QCow2ClusterType type = qcow2_get_cluster_type(entry);
|
||||||
|
|
||||||
if (type != wanted_type) {
|
if (type != wanted_type) {
|
||||||
break;
|
break;
|
||||||
@@ -487,6 +499,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
int l1_bits, c;
|
int l1_bits, c;
|
||||||
unsigned int offset_in_cluster;
|
unsigned int offset_in_cluster;
|
||||||
uint64_t bytes_available, bytes_needed, nb_clusters;
|
uint64_t bytes_available, bytes_needed, nb_clusters;
|
||||||
|
QCow2ClusterType type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
offset_in_cluster = offset_into_cluster(s, offset);
|
offset_in_cluster = offset_into_cluster(s, offset);
|
||||||
@@ -509,13 +522,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
l1_index = offset >> l1_bits;
|
l1_index = offset >> l1_bits;
|
||||||
if (l1_index >= s->l1_size) {
|
if (l1_index >= s->l1_size) {
|
||||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
type = QCOW2_CLUSTER_UNALLOCATED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||||
if (!l2_offset) {
|
if (!l2_offset) {
|
||||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
type = QCOW2_CLUSTER_UNALLOCATED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,38 +557,37 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
* true */
|
* true */
|
||||||
assert(nb_clusters <= INT_MAX);
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
ret = qcow2_get_cluster_type(*cluster_offset);
|
type = qcow2_get_cluster_type(*cluster_offset);
|
||||||
switch (ret) {
|
if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||||
|
type == QCOW2_CLUSTER_ZERO_ALLOC)) {
|
||||||
|
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||||
|
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||||
|
", L2 index: %#x)", l2_offset, l2_index);
|
||||||
|
ret = -EIO;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
/* Compressed clusters can only be processed one by one */
|
/* Compressed clusters can only be processed one by one */
|
||||||
c = 1;
|
c = 1;
|
||||||
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
if (s->qcow_version < 3) {
|
|
||||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
|
||||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
|
||||||
", L2 index: %#x)", l2_offset, l2_index);
|
|
||||||
ret = -EIO;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
|
||||||
QCOW2_CLUSTER_ZERO);
|
|
||||||
*cluster_offset = 0;
|
|
||||||
break;
|
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
/* how many empty clusters ? */
|
/* how many empty clusters ? */
|
||||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
c = count_contiguous_clusters_unallocated(nb_clusters,
|
||||||
QCOW2_CLUSTER_UNALLOCATED);
|
&l2_table[l2_index], type);
|
||||||
*cluster_offset = 0;
|
*cluster_offset = 0;
|
||||||
break;
|
break;
|
||||||
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
/* how many allocated clusters ? */
|
/* how many allocated clusters ? */
|
||||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||||
*cluster_offset &= L2E_OFFSET_MASK;
|
*cluster_offset &= L2E_OFFSET_MASK;
|
||||||
if (offset_into_cluster(s, *cluster_offset)) {
|
if (offset_into_cluster(s, *cluster_offset)) {
|
||||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
|
qcow2_signal_corruption(bs, true, -1, -1,
|
||||||
|
"Cluster allocation offset %#"
|
||||||
PRIx64 " unaligned (L2 offset: %#" PRIx64
|
PRIx64 " unaligned (L2 offset: %#" PRIx64
|
||||||
", L2 index: %#x)", *cluster_offset,
|
", L2 index: %#x)", *cluster_offset,
|
||||||
l2_offset, l2_index);
|
l2_offset, l2_index);
|
||||||
@@ -602,7 +614,7 @@ out:
|
|||||||
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
||||||
*bytes = bytes_available - offset_in_cluster;
|
*bytes = bytes_available - offset_in_cluster;
|
||||||
|
|
||||||
return ret;
|
return type;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||||
@@ -835,7 +847,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
|||||||
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
||||||
* clusters), the next write will reuse them anyway.
|
* clusters), the next write will reuse them anyway.
|
||||||
*/
|
*/
|
||||||
if (j != 0) {
|
if (!m->keep_old_clusters && j != 0) {
|
||||||
for (i = 0; i < j; i++) {
|
for (i = 0; i < j; i++) {
|
||||||
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
|
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
|
||||||
QCOW2_DISCARD_NEVER);
|
QCOW2_DISCARD_NEVER);
|
||||||
@@ -860,7 +872,7 @@ static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
|||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
|
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
|
||||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||||
|
|
||||||
switch(cluster_type) {
|
switch(cluster_type) {
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
@@ -870,7 +882,8 @@ static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
|||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@@ -1132,8 +1145,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
|||||||
uint64_t entry;
|
uint64_t entry;
|
||||||
uint64_t nb_clusters;
|
uint64_t nb_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
bool keep_old_clusters = false;
|
||||||
|
|
||||||
uint64_t alloc_cluster_offset;
|
uint64_t alloc_cluster_offset = 0;
|
||||||
|
|
||||||
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
|
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
|
||||||
*bytes);
|
*bytes);
|
||||||
@@ -1170,31 +1184,54 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
|||||||
* wrong with our code. */
|
* wrong with our code. */
|
||||||
assert(nb_clusters > 0);
|
assert(nb_clusters > 0);
|
||||||
|
|
||||||
|
if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
|
||||||
|
(entry & QCOW_OFLAG_COPIED) &&
|
||||||
|
(!*host_offset ||
|
||||||
|
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
|
||||||
|
{
|
||||||
|
/* Try to reuse preallocated zero clusters; contiguous normal clusters
|
||||||
|
* would be fine, too, but count_cow_clusters() above has limited
|
||||||
|
* nb_clusters already to a range of COW clusters */
|
||||||
|
int preallocated_nb_clusters =
|
||||||
|
count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
|
&l2_table[l2_index], QCOW_OFLAG_COPIED);
|
||||||
|
assert(preallocated_nb_clusters > 0);
|
||||||
|
|
||||||
|
nb_clusters = preallocated_nb_clusters;
|
||||||
|
alloc_cluster_offset = entry & L2E_OFFSET_MASK;
|
||||||
|
|
||||||
|
/* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2()
|
||||||
|
* should not free them. */
|
||||||
|
keep_old_clusters = true;
|
||||||
|
}
|
||||||
|
|
||||||
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
|
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
|
||||||
|
|
||||||
/* Allocate, if necessary at a given offset in the image file */
|
|
||||||
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
|
||||||
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
|
||||||
&nb_clusters);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't extend contiguous allocation */
|
|
||||||
if (nb_clusters == 0) {
|
|
||||||
*bytes = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* !*host_offset would overwrite the image header and is reserved for "no
|
|
||||||
* host offset preferred". If 0 was a valid host offset, it'd trigger the
|
|
||||||
* following overlap check; do that now to avoid having an invalid value in
|
|
||||||
* *host_offset. */
|
|
||||||
if (!alloc_cluster_offset) {
|
if (!alloc_cluster_offset) {
|
||||||
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
/* Allocate, if necessary at a given offset in the image file */
|
||||||
nb_clusters * s->cluster_size);
|
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
||||||
assert(ret < 0);
|
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
||||||
goto fail;
|
&nb_clusters);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't extend contiguous allocation */
|
||||||
|
if (nb_clusters == 0) {
|
||||||
|
*bytes = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* !*host_offset would overwrite the image header and is reserved for
|
||||||
|
* "no host offset preferred". If 0 was a valid host offset, it'd
|
||||||
|
* trigger the following overlap check; do that now to avoid having an
|
||||||
|
* invalid value in *host_offset. */
|
||||||
|
if (!alloc_cluster_offset) {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
||||||
|
nb_clusters * s->cluster_size);
|
||||||
|
assert(ret < 0);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1225,6 +1262,8 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
|||||||
.offset = start_of_cluster(s, guest_offset),
|
.offset = start_of_cluster(s, guest_offset),
|
||||||
.nb_clusters = nb_clusters,
|
.nb_clusters = nb_clusters,
|
||||||
|
|
||||||
|
.keep_old_clusters = keep_old_clusters,
|
||||||
|
|
||||||
.cow_start = {
|
.cow_start = {
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.nb_bytes = offset_into_cluster(s, guest_offset),
|
.nb_bytes = offset_into_cluster(s, guest_offset),
|
||||||
@@ -1472,24 +1511,25 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
* but rather fall through to the backing file.
|
* but rather fall through to the backing file.
|
||||||
*/
|
*/
|
||||||
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
if (full_discard || !bs->backing) {
|
if (full_discard || !bs->backing) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
if (!full_discard) {
|
if (!full_discard) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
break;
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First remove L2 entries */
|
/* First remove L2 entries */
|
||||||
@@ -1509,35 +1549,36 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
return nb_clusters;
|
return nb_clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
||||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
uint64_t bytes, enum qcow2_discard_type type,
|
||||||
|
bool full_discard)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t end_offset;
|
uint64_t end_offset = offset + bytes;
|
||||||
uint64_t nb_clusters;
|
uint64_t nb_clusters;
|
||||||
|
int64_t cleared;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
|
/* Caller must pass aligned values, except at image end */
|
||||||
|
|
||||||
/* The caller must cluster-align start; round end down except at EOF */
|
|
||||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||||
if (end_offset != bs->total_sectors * BDRV_SECTOR_SIZE) {
|
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
||||||
end_offset = start_of_cluster(s, end_offset);
|
end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
|
||||||
}
|
|
||||||
|
|
||||||
nb_clusters = size_to_clusters(s, end_offset - offset);
|
nb_clusters = size_to_clusters(s, bytes);
|
||||||
|
|
||||||
s->cache_discards = true;
|
s->cache_discards = true;
|
||||||
|
|
||||||
/* Each L2 table is handled by its own loop iteration */
|
/* Each L2 table is handled by its own loop iteration */
|
||||||
while (nb_clusters > 0) {
|
while (nb_clusters > 0) {
|
||||||
ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard);
|
cleared = discard_single_l2(bs, offset, nb_clusters, type,
|
||||||
if (ret < 0) {
|
full_discard);
|
||||||
|
if (cleared < 0) {
|
||||||
|
ret = cleared;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
nb_clusters -= ret;
|
nb_clusters -= cleared;
|
||||||
offset += (ret * s->cluster_size);
|
offset += (cleared * s->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@@ -1561,6 +1602,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
int l2_index;
|
int l2_index;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP);
|
||||||
|
|
||||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -1573,12 +1615,22 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t old_offset;
|
uint64_t old_offset;
|
||||||
|
QCow2ClusterType cluster_type;
|
||||||
|
|
||||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||||
|
|
||||||
/* Update L2 entries */
|
/*
|
||||||
|
* Minimize L2 changes if the cluster already reads back as
|
||||||
|
* zeroes with correct allocation.
|
||||||
|
*/
|
||||||
|
cluster_type = qcow2_get_cluster_type(old_offset);
|
||||||
|
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||||
|
(cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
|
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
|
||||||
if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) {
|
if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) {
|
||||||
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||||
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
|
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
|
||||||
} else {
|
} else {
|
||||||
@@ -1591,31 +1643,39 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
return nb_clusters;
|
return nb_clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
|
int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
|
||||||
int flags)
|
uint64_t bytes, int flags)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
uint64_t end_offset = offset + bytes;
|
||||||
uint64_t nb_clusters;
|
uint64_t nb_clusters;
|
||||||
|
int64_t cleared;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Caller must pass aligned values, except at image end */
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||||
|
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
||||||
|
end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
|
||||||
|
|
||||||
/* The zero flag is only supported by version 3 and newer */
|
/* The zero flag is only supported by version 3 and newer */
|
||||||
if (s->qcow_version < 3) {
|
if (s->qcow_version < 3) {
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Each L2 table is handled by its own loop iteration */
|
/* Each L2 table is handled by its own loop iteration */
|
||||||
nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
|
nb_clusters = size_to_clusters(s, bytes);
|
||||||
|
|
||||||
s->cache_discards = true;
|
s->cache_discards = true;
|
||||||
|
|
||||||
while (nb_clusters > 0) {
|
while (nb_clusters > 0) {
|
||||||
ret = zero_single_l2(bs, offset, nb_clusters, flags);
|
cleared = zero_single_l2(bs, offset, nb_clusters, flags);
|
||||||
if (ret < 0) {
|
if (cleared < 0) {
|
||||||
|
ret = cleared;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
nb_clusters -= ret;
|
nb_clusters -= cleared;
|
||||||
offset += (ret * s->cluster_size);
|
offset += (cleared * s->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@@ -1699,14 +1759,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||||||
for (j = 0; j < s->l2_size; j++) {
|
for (j = 0; j < s->l2_size; j++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||||
int64_t offset = l2_entry & L2E_OFFSET_MASK;
|
int64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||||
bool preallocated = offset != 0;
|
|
||||||
|
|
||||||
if (cluster_type != QCOW2_CLUSTER_ZERO) {
|
if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN &&
|
||||||
|
cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preallocated) {
|
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||||
if (!bs->backing) {
|
if (!bs->backing) {
|
||||||
/* not backed; therefore we can simply deallocate the
|
/* not backed; therefore we can simply deallocate the
|
||||||
* cluster */
|
* cluster */
|
||||||
@@ -1741,7 +1801,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||||||
"%#" PRIx64 " unaligned (L2 offset: %#"
|
"%#" PRIx64 " unaligned (L2 offset: %#"
|
||||||
PRIx64 ", L2 index: %#x)", offset,
|
PRIx64 ", L2 index: %#x)", offset,
|
||||||
l2_offset, j);
|
l2_offset, j);
|
||||||
if (!preallocated) {
|
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||||
QCOW2_DISCARD_ALWAYS);
|
QCOW2_DISCARD_ALWAYS);
|
||||||
}
|
}
|
||||||
@@ -1751,7 +1811,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||||||
|
|
||||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!preallocated) {
|
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||||
QCOW2_DISCARD_ALWAYS);
|
QCOW2_DISCARD_ALWAYS);
|
||||||
}
|
}
|
||||||
@@ -1760,7 +1820,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||||||
|
|
||||||
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
|
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!preallocated) {
|
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||||
QCOW2_DISCARD_ALWAYS);
|
QCOW2_DISCARD_ALWAYS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1028,18 +1028,17 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
if (l2_entry & L2E_OFFSET_MASK) {
|
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
qcow2_signal_corruption(bs, false, -1, -1,
|
||||||
qcow2_signal_corruption(bs, false, -1, -1,
|
"Cannot free unaligned cluster %#llx",
|
||||||
"Cannot free unaligned cluster %#llx",
|
l2_entry & L2E_OFFSET_MASK);
|
||||||
l2_entry & L2E_OFFSET_MASK);
|
} else {
|
||||||
} else {
|
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
nb_clusters << s->cluster_bits, type);
|
||||||
nb_clusters << s->cluster_bits, type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1059,9 +1058,9 @@ 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)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount;
|
||||||
bool l1_allocated = false;
|
bool l1_allocated = false;
|
||||||
int64_t old_offset, old_l2_offset;
|
int64_t old_entry, old_l2_offset;
|
||||||
int i, j, l1_modified = 0, nb_csectors;
|
int i, j, l1_modified = 0, nb_csectors;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -1089,15 +1088,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0;i < l1_size; i++)
|
for (i = 0; i < l1_size; i++) {
|
||||||
be64_to_cpus(&l1_table[i]);
|
be64_to_cpus(&l1_table[i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(l1_size == s->l1_size);
|
assert(l1_size == s->l1_size);
|
||||||
l1_table = s->l1_table;
|
l1_table = s->l1_table;
|
||||||
l1_allocated = false;
|
l1_allocated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0; i < l1_size; i++) {
|
for (i = 0; i < l1_size; i++) {
|
||||||
l2_offset = l1_table[i];
|
l2_offset = l1_table[i];
|
||||||
if (l2_offset) {
|
if (l2_offset) {
|
||||||
old_l2_offset = l2_offset;
|
old_l2_offset = l2_offset;
|
||||||
@@ -1117,81 +1117,79 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(j = 0; j < s->l2_size; j++) {
|
for (j = 0; j < s->l2_size; j++) {
|
||||||
uint64_t cluster_index;
|
uint64_t cluster_index;
|
||||||
|
uint64_t offset;
|
||||||
|
|
||||||
offset = be64_to_cpu(l2_table[j]);
|
entry = be64_to_cpu(l2_table[j]);
|
||||||
old_offset = offset;
|
old_entry = entry;
|
||||||
offset &= ~QCOW_OFLAG_COPIED;
|
entry &= ~QCOW_OFLAG_COPIED;
|
||||||
|
offset = entry & L2E_OFFSET_MASK;
|
||||||
|
|
||||||
switch (qcow2_get_cluster_type(offset)) {
|
switch (qcow2_get_cluster_type(entry)) {
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
nb_csectors = ((offset >> s->csize_shift) &
|
nb_csectors = ((entry >> s->csize_shift) &
|
||||||
s->csize_mask) + 1;
|
s->csize_mask) + 1;
|
||||||
if (addend != 0) {
|
if (addend != 0) {
|
||||||
ret = update_refcount(bs,
|
ret = update_refcount(bs,
|
||||||
(offset & s->cluster_offset_mask) & ~511,
|
(entry & s->cluster_offset_mask) & ~511,
|
||||||
nb_csectors * 512, abs(addend), addend < 0,
|
nb_csectors * 512, abs(addend), addend < 0,
|
||||||
QCOW2_DISCARD_SNAPSHOT);
|
QCOW2_DISCARD_SNAPSHOT);
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* compressed clusters are never modified */
|
|
||||||
refcount = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
|
||||||
case QCOW2_CLUSTER_ZERO:
|
|
||||||
if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
|
|
||||||
qcow2_signal_corruption(bs, true, -1, -1, "Data "
|
|
||||||
"cluster offset %#llx "
|
|
||||||
"unaligned (L2 offset: %#"
|
|
||||||
PRIx64 ", L2 index: %#x)",
|
|
||||||
offset & L2E_OFFSET_MASK,
|
|
||||||
l2_offset, j);
|
|
||||||
ret = -EIO;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
|
||||||
if (!cluster_index) {
|
|
||||||
/* unallocated */
|
|
||||||
refcount = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (addend != 0) {
|
|
||||||
ret = qcow2_update_cluster_refcount(bs,
|
|
||||||
cluster_index, abs(addend), addend < 0,
|
|
||||||
QCOW2_DISCARD_SNAPSHOT);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
/* compressed clusters are never modified */
|
||||||
|
refcount = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
refcount = 0;
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
break;
|
if (offset_into_cluster(s, offset)) {
|
||||||
|
qcow2_signal_corruption(bs, true, -1, -1, "Cluster "
|
||||||
|
"allocation offset %#" PRIx64
|
||||||
|
" unaligned (L2 offset: %#"
|
||||||
|
PRIx64 ", L2 index: %#x)",
|
||||||
|
offset, l2_offset, j);
|
||||||
|
ret = -EIO;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
cluster_index = offset >> s->cluster_bits;
|
||||||
abort();
|
assert(cluster_index);
|
||||||
|
if (addend != 0) {
|
||||||
|
ret = qcow2_update_cluster_refcount(bs,
|
||||||
|
cluster_index, abs(addend), addend < 0,
|
||||||
|
QCOW2_DISCARD_SNAPSHOT);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
|
refcount = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refcount == 1) {
|
if (refcount == 1) {
|
||||||
offset |= QCOW_OFLAG_COPIED;
|
entry |= QCOW_OFLAG_COPIED;
|
||||||
}
|
}
|
||||||
if (offset != old_offset) {
|
if (entry != old_entry) {
|
||||||
if (addend > 0) {
|
if (addend > 0) {
|
||||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||||
s->refcount_block_cache);
|
s->refcount_block_cache);
|
||||||
}
|
}
|
||||||
l2_table[j] = cpu_to_be64(offset);
|
l2_table[j] = cpu_to_be64(entry);
|
||||||
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
|
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
|
||||||
l2_table);
|
l2_table);
|
||||||
}
|
}
|
||||||
@@ -1441,12 +1439,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
{
|
{
|
||||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
@@ -1476,6 +1469,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1638,10 +1632,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
for (j = 0; j < s->l2_size; j++) {
|
for (j = 0; j < s->l2_size; j++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||||
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
|
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||||
|
|
||||||
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
|
if (cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||||
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
|
cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||||
ret = qcow2_get_refcount(bs,
|
ret = qcow2_get_refcount(bs,
|
||||||
data_offset >> s->cluster_bits,
|
data_offset >> s->cluster_bits,
|
||||||
&refcount);
|
&refcount);
|
||||||
@@ -1728,14 +1722,17 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
|
|
||||||
if (fix & BDRV_FIX_ERRORS) {
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
int64_t new_nb_clusters;
|
int64_t new_nb_clusters;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (offset > INT64_MAX - s->cluster_size) {
|
if (offset > INT64_MAX - s->cluster_size) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto resize_fail;
|
goto resize_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_truncate(bs->file, offset + s->cluster_size);
|
ret = bdrv_truncate(bs->file, offset + s->cluster_size,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
goto resize_fail;
|
goto resize_fail;
|
||||||
}
|
}
|
||||||
size = bdrv_getlength(bs->file->bs);
|
size = bdrv_getlength(bs->file->bs);
|
||||||
|
|||||||
@@ -440,10 +440,9 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||||||
|
|
||||||
/* The VM state isn't needed any more in the active L1 table; in fact, it
|
/* The VM state isn't needed any more in the active L1 table; in fact, it
|
||||||
* hurts by causing expensive COW for the next snapshot. */
|
* hurts by causing expensive COW for the next snapshot. */
|
||||||
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
qcow2_cluster_discard(bs, qcow2_vm_state_offset(s),
|
||||||
align_offset(sn->vm_state_size, s->cluster_size)
|
align_offset(sn->vm_state_size, s->cluster_size),
|
||||||
>> BDRV_SECTOR_BITS,
|
QCOW2_DISCARD_NEVER, false);
|
||||||
QCOW2_DISCARD_NEVER, false);
|
|
||||||
|
|
||||||
#ifdef DEBUG_ALLOC
|
#ifdef DEBUG_ALLOC
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1385,7 +1385,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
|||||||
*file = bs->file->bs;
|
*file = bs->file->bs;
|
||||||
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||||
}
|
}
|
||||||
if (ret == QCOW2_CLUSTER_ZERO) {
|
if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||||
status |= BDRV_BLOCK_ZERO;
|
status |= BDRV_BLOCK_ZERO;
|
||||||
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
||||||
status |= BDRV_BLOCK_DATA;
|
status |= BDRV_BLOCK_DATA;
|
||||||
@@ -1482,7 +1482,8 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_ZERO:
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
|
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2139,7 +2140,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
* too, as long as the bulk is allocated here). Therefore, using
|
* too, as long as the bulk is allocated here). Therefore, using
|
||||||
* floating point arithmetic is fine. */
|
* floating point arithmetic is fine. */
|
||||||
int64_t meta_size = 0;
|
int64_t meta_size = 0;
|
||||||
uint64_t nreftablee, nrefblocke, nl1e, nl2e;
|
uint64_t nreftablee, nrefblocke, nl1e, nl2e, refblock_count;
|
||||||
int64_t aligned_total_size = align_offset(total_size, cluster_size);
|
int64_t aligned_total_size = align_offset(total_size, cluster_size);
|
||||||
int refblock_bits, refblock_size;
|
int refblock_bits, refblock_size;
|
||||||
/* refcount entry size in bytes */
|
/* refcount entry size in bytes */
|
||||||
@@ -2182,11 +2183,12 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
nrefblocke = (aligned_total_size + meta_size + cluster_size)
|
nrefblocke = (aligned_total_size + meta_size + cluster_size)
|
||||||
/ (cluster_size - rces - rces * sizeof(uint64_t)
|
/ (cluster_size - rces - rces * sizeof(uint64_t)
|
||||||
/ cluster_size);
|
/ cluster_size);
|
||||||
meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
|
refblock_count = DIV_ROUND_UP(nrefblocke, refblock_size);
|
||||||
|
meta_size += refblock_count * cluster_size;
|
||||||
|
|
||||||
/* total size of refcount tables */
|
/* total size of refcount tables */
|
||||||
nreftablee = nrefblocke / refblock_size;
|
nreftablee = align_offset(refblock_count,
|
||||||
nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
|
cluster_size / sizeof(uint64_t));
|
||||||
meta_size += nreftablee * sizeof(uint64_t);
|
meta_size += nreftablee * sizeof(uint64_t);
|
||||||
|
|
||||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
|
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
|
||||||
@@ -2265,7 +2267,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
* table)
|
* table)
|
||||||
*/
|
*/
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
qdict_put_str(options, "driver", "qcow2");
|
||||||
blk = blk_new_open(filename, NULL, options,
|
blk = blk_new_open(filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
|
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
|
||||||
&local_err);
|
&local_err);
|
||||||
@@ -2294,9 +2296,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Okay, now that we have a valid image, let's give it the right size */
|
/* Okay, now that we have a valid image, let's give it the right size */
|
||||||
ret = blk_truncate(blk, total_size);
|
ret = blk_truncate(blk, total_size, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not resize image");
|
error_prepend(errp, "Could not resize image: ");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2327,7 +2329,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
|
|
||||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
qdict_put_str(options, "driver", "qcow2");
|
||||||
blk = blk_new_open(filename, NULL, options,
|
blk = blk_new_open(filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_NO_BACKING, &local_err);
|
BDRV_O_RDWR | BDRV_O_NO_BACKING, &local_err);
|
||||||
if (blk == NULL) {
|
if (blk == NULL) {
|
||||||
@@ -2449,6 +2451,10 @@ static bool is_zero_sectors(BlockDriverState *bs, int64_t start,
|
|||||||
BlockDriverState *file;
|
BlockDriverState *file;
|
||||||
int64_t res;
|
int64_t res;
|
||||||
|
|
||||||
|
if (start + count > bs->total_sectors) {
|
||||||
|
count = bs->total_sectors - start;
|
||||||
|
}
|
||||||
|
|
||||||
if (!count) {
|
if (!count) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2467,6 +2473,9 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
uint32_t tail = (offset + count) % s->cluster_size;
|
uint32_t tail = (offset + count) % s->cluster_size;
|
||||||
|
|
||||||
trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, count);
|
trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, count);
|
||||||
|
if (offset + count == bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||||
|
tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (head || tail) {
|
if (head || tail) {
|
||||||
int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
|
int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
|
||||||
@@ -2490,7 +2499,9 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
count = s->cluster_size;
|
count = s->cluster_size;
|
||||||
nr = s->cluster_size;
|
nr = s->cluster_size;
|
||||||
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
|
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
|
||||||
if (ret != QCOW2_CLUSTER_UNALLOCATED && ret != QCOW2_CLUSTER_ZERO) {
|
if (ret != QCOW2_CLUSTER_UNALLOCATED &&
|
||||||
|
ret != QCOW2_CLUSTER_ZERO_PLAIN &&
|
||||||
|
ret != QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
@@ -2501,7 +2512,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count);
|
trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count);
|
||||||
|
|
||||||
/* Whatever is left can use real zero clusters */
|
/* Whatever is left can use real zero clusters */
|
||||||
ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags);
|
ret = qcow2_cluster_zeroize(bs, offset, count, flags);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2515,42 +2526,48 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (!QEMU_IS_ALIGNED(offset | count, s->cluster_size)) {
|
if (!QEMU_IS_ALIGNED(offset | count, s->cluster_size)) {
|
||||||
assert(count < s->cluster_size);
|
assert(count < s->cluster_size);
|
||||||
return -ENOTSUP;
|
/* Ignore partial clusters, except for the special case of the
|
||||||
|
* complete partial cluster at the end of an unaligned file */
|
||||||
|
if (!QEMU_IS_ALIGNED(offset, s->cluster_size) ||
|
||||||
|
offset + count != bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = qcow2_discard_clusters(bs, offset, count >> BDRV_SECTOR_BITS,
|
ret = qcow2_cluster_discard(bs, offset, count, QCOW2_DISCARD_REQUEST,
|
||||||
QCOW2_DISCARD_REQUEST, false);
|
false);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
static int qcow2_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t new_l1_size;
|
int64_t new_l1_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (offset & 511) {
|
if (offset & 511) {
|
||||||
error_report("The new size must be a multiple of 512");
|
error_setg(errp, "The new size must be a multiple of 512");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cannot proceed if image has snapshots */
|
/* cannot proceed if image has snapshots */
|
||||||
if (s->nb_snapshots) {
|
if (s->nb_snapshots) {
|
||||||
error_report("Can't resize an image which has snapshots");
|
error_setg(errp, "Can't resize an image which has snapshots");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shrinking is currently not supported */
|
/* shrinking is currently not supported */
|
||||||
if (offset < bs->total_sectors * 512) {
|
if (offset < bs->total_sectors * 512) {
|
||||||
error_report("qcow2 doesn't support shrinking images yet");
|
error_setg(errp, "qcow2 doesn't support shrinking images yet");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_l1_size = size_to_l1(s, offset);
|
new_l1_size = size_to_l1(s, offset);
|
||||||
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
|
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to grow the L1 table");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2559,6 +2576,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
|
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
|
||||||
&offset, sizeof(uint64_t));
|
&offset, sizeof(uint64_t));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to update the image size");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2584,7 +2602,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
|||||||
/* align end of file to a sector boundary to ease reading with
|
/* align end of file to a sector boundary to ease reading with
|
||||||
sector based I/Os */
|
sector based I/Os */
|
||||||
cluster_offset = bdrv_getlength(bs->file->bs);
|
cluster_offset = bdrv_getlength(bs->file->bs);
|
||||||
return bdrv_truncate(bs->file, cluster_offset);
|
return bdrv_truncate(bs->file, cluster_offset, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = qemu_blockalign(bs, s->cluster_size);
|
buf = qemu_blockalign(bs, s->cluster_size);
|
||||||
@@ -2674,6 +2692,7 @@ fail:
|
|||||||
static int make_completely_empty(BlockDriverState *bs)
|
static int make_completely_empty(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
Error *local_err = NULL;
|
||||||
int ret, l1_clusters;
|
int ret, l1_clusters;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
uint64_t *new_reftable = NULL;
|
uint64_t *new_reftable = NULL;
|
||||||
@@ -2798,8 +2817,10 @@ static int make_completely_empty(BlockDriverState *bs)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size);
|
ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2822,9 +2843,8 @@ fail:
|
|||||||
static int qcow2_make_empty(BlockDriverState *bs)
|
static int qcow2_make_empty(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t start_sector;
|
uint64_t offset, end_offset;
|
||||||
int sector_step = (QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size) /
|
int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size);
|
||||||
BDRV_SECTOR_SIZE);
|
|
||||||
int l1_clusters, ret = 0;
|
int l1_clusters, ret = 0;
|
||||||
|
|
||||||
l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
|
l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
|
||||||
@@ -2841,18 +2861,15 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
|||||||
|
|
||||||
/* This fallback code simply discards every active cluster; this is slow,
|
/* This fallback code simply discards every active cluster; this is slow,
|
||||||
* but works in all cases */
|
* but works in all cases */
|
||||||
for (start_sector = 0; start_sector < bs->total_sectors;
|
end_offset = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||||
start_sector += sector_step)
|
for (offset = 0; offset < end_offset; offset += step) {
|
||||||
{
|
|
||||||
/* As this function is generally used after committing an external
|
/* As this function is generally used after committing an external
|
||||||
* snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the
|
* snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the
|
||||||
* default action for this kind of discard is to pass the discard,
|
* default action for this kind of discard is to pass the discard,
|
||||||
* which will ideally result in an actually smaller image file, as
|
* which will ideally result in an actually smaller image file, as
|
||||||
* is probably desired. */
|
* is probably desired. */
|
||||||
ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE,
|
ret = qcow2_cluster_discard(bs, offset, MIN(step, end_offset - offset),
|
||||||
MIN(sector_step,
|
QCOW2_DISCARD_SNAPSHOT, true);
|
||||||
bs->total_sectors - start_sector),
|
|
||||||
QCOW2_DISCARD_SNAPSHOT, true);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3273,9 +3290,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = blk_truncate(blk, new_size);
|
ret = blk_truncate(blk, new_size, &local_err);
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,6 +322,9 @@ typedef struct QCowL2Meta
|
|||||||
/** Number of newly allocated clusters */
|
/** Number of newly allocated clusters */
|
||||||
int nb_clusters;
|
int nb_clusters;
|
||||||
|
|
||||||
|
/** Do not free the old clusters */
|
||||||
|
bool keep_old_clusters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that overlap with this allocation and wait to be restarted
|
* Requests that overlap with this allocation and wait to be restarted
|
||||||
* when the allocating request has completed.
|
* when the allocating request has completed.
|
||||||
@@ -346,12 +349,13 @@ typedef struct QCowL2Meta
|
|||||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||||
} QCowL2Meta;
|
} QCowL2Meta;
|
||||||
|
|
||||||
enum {
|
typedef enum QCow2ClusterType {
|
||||||
QCOW2_CLUSTER_UNALLOCATED,
|
QCOW2_CLUSTER_UNALLOCATED,
|
||||||
|
QCOW2_CLUSTER_ZERO_PLAIN,
|
||||||
|
QCOW2_CLUSTER_ZERO_ALLOC,
|
||||||
QCOW2_CLUSTER_NORMAL,
|
QCOW2_CLUSTER_NORMAL,
|
||||||
QCOW2_CLUSTER_COMPRESSED,
|
QCOW2_CLUSTER_COMPRESSED,
|
||||||
QCOW2_CLUSTER_ZERO
|
} QCow2ClusterType;
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum QCow2MetadataOverlap {
|
typedef enum QCow2MetadataOverlap {
|
||||||
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
||||||
@@ -440,12 +444,15 @@ static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
|
|||||||
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
|
||||||
{
|
{
|
||||||
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
||||||
return QCOW2_CLUSTER_COMPRESSED;
|
return QCOW2_CLUSTER_COMPRESSED;
|
||||||
} else if (l2_entry & QCOW_OFLAG_ZERO) {
|
} else if (l2_entry & QCOW_OFLAG_ZERO) {
|
||||||
return QCOW2_CLUSTER_ZERO;
|
if (l2_entry & L2E_OFFSET_MASK) {
|
||||||
|
return QCOW2_CLUSTER_ZERO_ALLOC;
|
||||||
|
}
|
||||||
|
return QCOW2_CLUSTER_ZERO_PLAIN;
|
||||||
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
||||||
return QCOW2_CLUSTER_UNALLOCATED;
|
return QCOW2_CLUSTER_UNALLOCATED;
|
||||||
} else {
|
} else {
|
||||||
@@ -544,10 +551,11 @@ 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 qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
||||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard);
|
uint64_t bytes, enum qcow2_discard_type type,
|
||||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
|
bool full_discard);
|
||||||
int flags);
|
int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
|
||||||
|
uint64_t bytes, int flags);
|
||||||
|
|
||||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||||
BlockDriverAmendStatusCB *status_cb,
|
BlockDriverAmendStatusCB *status_cb,
|
||||||
|
|||||||
@@ -635,7 +635,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
blk_set_allow_write_beyond_eof(blk, true);
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/* File must start empty and grow, check truncate is supported */
|
/* File must start empty and grow, check truncate is supported */
|
||||||
ret = blk_truncate(blk, 0);
|
ret = blk_truncate(blk, 0, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1518,7 +1518,7 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
return cb.ret;
|
return cb.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
|
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQEDState *s = bs->opaque;
|
BDRVQEDState *s = bs->opaque;
|
||||||
uint64_t old_image_size;
|
uint64_t old_image_size;
|
||||||
@@ -1526,11 +1526,12 @@ static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
|
|
||||||
if (!qed_is_image_size_valid(offset, s->header.cluster_size,
|
if (!qed_is_image_size_valid(offset, s->header.cluster_size,
|
||||||
s->header.table_size)) {
|
s->header.table_size)) {
|
||||||
|
error_setg(errp, "Invalid image size specified");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shrinking is currently not supported */
|
|
||||||
if ((uint64_t)offset < s->header.image_size) {
|
if ((uint64_t)offset < s->header.image_size) {
|
||||||
|
error_setg(errp, "Shrinking images is currently not supported");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1539,6 +1540,7 @@ static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
ret = qed_write_header_sync(s);
|
ret = qed_write_header_sync(s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
s->header.image_size = old_image_size;
|
s->header.image_size = old_image_size;
|
||||||
|
error_setg_errno(errp, -ret, "Failed to update the image size");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1096,19 +1096,15 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
children = qlist_new();
|
children = qlist_new();
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
QINCREF(s->children[i]->bs->full_open_options);
|
QINCREF(s->children[i]->bs->full_open_options);
|
||||||
qlist_append_obj(children,
|
qlist_append(children, s->children[i]->bs->full_open_options);
|
||||||
QOBJECT(s->children[i]->bs->full_open_options));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qdict_new();
|
opts = qdict_new();
|
||||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("quorum")));
|
qdict_put_str(opts, "driver", "quorum");
|
||||||
qdict_put_obj(opts, QUORUM_OPT_VOTE_THRESHOLD,
|
qdict_put_int(opts, QUORUM_OPT_VOTE_THRESHOLD, s->threshold);
|
||||||
QOBJECT(qint_from_int(s->threshold)));
|
qdict_put_bool(opts, QUORUM_OPT_BLKVERIFY, s->is_blkverify);
|
||||||
qdict_put_obj(opts, QUORUM_OPT_BLKVERIFY,
|
qdict_put_bool(opts, QUORUM_OPT_REWRITE, s->rewrite_corrupted);
|
||||||
QOBJECT(qbool_from_bool(s->is_blkverify)));
|
qdict_put(opts, "children", children);
|
||||||
qdict_put_obj(opts, QUORUM_OPT_REWRITE,
|
|
||||||
QOBJECT(qbool_from_bool(s->rewrite_corrupted)));
|
|
||||||
qdict_put_obj(opts, "children", QOBJECT(children));
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,21 +327,23 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
|
|
||||||
if (s->has_size) {
|
if (s->has_size) {
|
||||||
|
error_setg(errp, "Cannot resize fixed-size raw disks");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (INT64_MAX - offset < s->offset) {
|
if (INT64_MAX - offset < s->offset) {
|
||||||
|
error_setg(errp, "Disk size too large for the chosen offset");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->size = offset;
|
s->size = offset;
|
||||||
offset += s->offset;
|
offset += s->offset;
|
||||||
return bdrv_truncate(bs->file, offset);
|
return bdrv_truncate(bs->file, offset, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_media_changed(BlockDriverState *bs)
|
static int raw_media_changed(BlockDriverState *bs)
|
||||||
|
|||||||
19
block/rbd.c
19
block/rbd.c
@@ -154,20 +154,20 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
qemu_rbd_unescape(found_str);
|
qemu_rbd_unescape(found_str);
|
||||||
qdict_put(options, "pool", qstring_from_str(found_str));
|
qdict_put_str(options, "pool", found_str);
|
||||||
|
|
||||||
if (strchr(p, '@')) {
|
if (strchr(p, '@')) {
|
||||||
found_str = qemu_rbd_next_tok(p, '@', &p);
|
found_str = qemu_rbd_next_tok(p, '@', &p);
|
||||||
qemu_rbd_unescape(found_str);
|
qemu_rbd_unescape(found_str);
|
||||||
qdict_put(options, "image", qstring_from_str(found_str));
|
qdict_put_str(options, "image", found_str);
|
||||||
|
|
||||||
found_str = qemu_rbd_next_tok(p, ':', &p);
|
found_str = qemu_rbd_next_tok(p, ':', &p);
|
||||||
qemu_rbd_unescape(found_str);
|
qemu_rbd_unescape(found_str);
|
||||||
qdict_put(options, "snapshot", qstring_from_str(found_str));
|
qdict_put_str(options, "snapshot", found_str);
|
||||||
} else {
|
} else {
|
||||||
found_str = qemu_rbd_next_tok(p, ':', &p);
|
found_str = qemu_rbd_next_tok(p, ':', &p);
|
||||||
qemu_rbd_unescape(found_str);
|
qemu_rbd_unescape(found_str);
|
||||||
qdict_put(options, "image", qstring_from_str(found_str));
|
qdict_put_str(options, "image", found_str);
|
||||||
}
|
}
|
||||||
if (!p) {
|
if (!p) {
|
||||||
goto done;
|
goto done;
|
||||||
@@ -189,9 +189,9 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||||||
qemu_rbd_unescape(value);
|
qemu_rbd_unescape(value);
|
||||||
|
|
||||||
if (!strcmp(name, "conf")) {
|
if (!strcmp(name, "conf")) {
|
||||||
qdict_put(options, "conf", qstring_from_str(value));
|
qdict_put_str(options, "conf", value);
|
||||||
} else if (!strcmp(name, "id")) {
|
} else if (!strcmp(name, "id")) {
|
||||||
qdict_put(options, "user" , qstring_from_str(value));
|
qdict_put_str(options, "user", value);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We pass these internally to qemu_rbd_set_keypairs(), so
|
* We pass these internally to qemu_rbd_set_keypairs(), so
|
||||||
@@ -204,8 +204,8 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||||||
if (!keypairs) {
|
if (!keypairs) {
|
||||||
keypairs = qlist_new();
|
keypairs = qlist_new();
|
||||||
}
|
}
|
||||||
qlist_append(keypairs, qstring_from_str(name));
|
qlist_append_str(keypairs, name);
|
||||||
qlist_append(keypairs, qstring_from_str(value));
|
qlist_append_str(keypairs, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,13 +916,14 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
|
|||||||
return info.size;
|
return info.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset)
|
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = rbd_resize(s->image, offset);
|
r = rbd_resize(s->image, offset);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
error_setg_errno(errp, -r, "Failed to resize file");
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,17 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "replication.h"
|
#include "replication.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BLOCK_REPLICATION_NONE, /* block replication is not started */
|
||||||
|
BLOCK_REPLICATION_RUNNING, /* block replication is running */
|
||||||
|
BLOCK_REPLICATION_FAILOVER, /* failover is running in background */
|
||||||
|
BLOCK_REPLICATION_FAILOVER_FAILED, /* failover failed */
|
||||||
|
BLOCK_REPLICATION_DONE, /* block replication is done */
|
||||||
|
} ReplicationStage;
|
||||||
|
|
||||||
typedef struct BDRVReplicationState {
|
typedef struct BDRVReplicationState {
|
||||||
ReplicationMode mode;
|
ReplicationMode mode;
|
||||||
int replication_state;
|
ReplicationStage stage;
|
||||||
BdrvChild *active_disk;
|
BdrvChild *active_disk;
|
||||||
BdrvChild *hidden_disk;
|
BdrvChild *hidden_disk;
|
||||||
BdrvChild *secondary_disk;
|
BdrvChild *secondary_disk;
|
||||||
@@ -36,14 +44,6 @@ typedef struct BDRVReplicationState {
|
|||||||
int error;
|
int error;
|
||||||
} BDRVReplicationState;
|
} BDRVReplicationState;
|
||||||
|
|
||||||
enum {
|
|
||||||
BLOCK_REPLICATION_NONE, /* block replication is not started */
|
|
||||||
BLOCK_REPLICATION_RUNNING, /* block replication is running */
|
|
||||||
BLOCK_REPLICATION_FAILOVER, /* failover is running in background */
|
|
||||||
BLOCK_REPLICATION_FAILOVER_FAILED, /* failover failed */
|
|
||||||
BLOCK_REPLICATION_DONE, /* block replication is done */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
static void replication_do_checkpoint(ReplicationState *rs, Error **errp);
|
static void replication_do_checkpoint(ReplicationState *rs, Error **errp);
|
||||||
@@ -141,10 +141,10 @@ static void replication_close(BlockDriverState *bs)
|
|||||||
{
|
{
|
||||||
BDRVReplicationState *s = bs->opaque;
|
BDRVReplicationState *s = bs->opaque;
|
||||||
|
|
||||||
if (s->replication_state == BLOCK_REPLICATION_RUNNING) {
|
if (s->stage == BLOCK_REPLICATION_RUNNING) {
|
||||||
replication_stop(s->rs, false, NULL);
|
replication_stop(s->rs, false, NULL);
|
||||||
}
|
}
|
||||||
if (s->replication_state == BLOCK_REPLICATION_FAILOVER) {
|
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||||
block_job_cancel_sync(s->active_disk->bs->job);
|
block_job_cancel_sync(s->active_disk->bs->job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ static int64_t replication_getlength(BlockDriverState *bs)
|
|||||||
|
|
||||||
static int replication_get_io_status(BDRVReplicationState *s)
|
static int replication_get_io_status(BDRVReplicationState *s)
|
||||||
{
|
{
|
||||||
switch (s->replication_state) {
|
switch (s->stage) {
|
||||||
case BLOCK_REPLICATION_NONE:
|
case BLOCK_REPLICATION_NONE:
|
||||||
return -EIO;
|
return -EIO;
|
||||||
case BLOCK_REPLICATION_RUNNING:
|
case BLOCK_REPLICATION_RUNNING:
|
||||||
@@ -403,7 +403,7 @@ static void backup_job_completed(void *opaque, int ret)
|
|||||||
BlockDriverState *bs = opaque;
|
BlockDriverState *bs = opaque;
|
||||||
BDRVReplicationState *s = bs->opaque;
|
BDRVReplicationState *s = bs->opaque;
|
||||||
|
|
||||||
if (s->replication_state != BLOCK_REPLICATION_FAILOVER) {
|
if (s->stage != BLOCK_REPLICATION_FAILOVER) {
|
||||||
/* The backup job is cancelled unexpectedly */
|
/* The backup job is cancelled unexpectedly */
|
||||||
s->error = -EIO;
|
s->error = -EIO;
|
||||||
}
|
}
|
||||||
@@ -445,7 +445,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
s = bs->opaque;
|
s = bs->opaque;
|
||||||
|
|
||||||
if (s->replication_state != BLOCK_REPLICATION_NONE) {
|
if (s->stage != BLOCK_REPLICATION_NONE) {
|
||||||
error_setg(errp, "Block replication is running or done");
|
error_setg(errp, "Block replication is running or done");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
@@ -545,7 +545,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
s->replication_state = BLOCK_REPLICATION_RUNNING;
|
s->stage = BLOCK_REPLICATION_RUNNING;
|
||||||
|
|
||||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||||
secondary_do_checkpoint(s, errp);
|
secondary_do_checkpoint(s, errp);
|
||||||
@@ -581,7 +581,7 @@ static void replication_get_error(ReplicationState *rs, Error **errp)
|
|||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
s = bs->opaque;
|
s = bs->opaque;
|
||||||
|
|
||||||
if (s->replication_state != BLOCK_REPLICATION_RUNNING) {
|
if (s->stage != BLOCK_REPLICATION_RUNNING) {
|
||||||
error_setg(errp, "Block replication is not running");
|
error_setg(errp, "Block replication is not running");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
@@ -601,7 +601,7 @@ static void replication_done(void *opaque, int ret)
|
|||||||
BDRVReplicationState *s = bs->opaque;
|
BDRVReplicationState *s = bs->opaque;
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
s->replication_state = BLOCK_REPLICATION_DONE;
|
s->stage = BLOCK_REPLICATION_DONE;
|
||||||
|
|
||||||
/* refresh top bs's filename */
|
/* refresh top bs's filename */
|
||||||
bdrv_refresh_filename(bs);
|
bdrv_refresh_filename(bs);
|
||||||
@@ -610,7 +610,7 @@ static void replication_done(void *opaque, int ret)
|
|||||||
s->hidden_disk = NULL;
|
s->hidden_disk = NULL;
|
||||||
s->error = 0;
|
s->error = 0;
|
||||||
} else {
|
} else {
|
||||||
s->replication_state = BLOCK_REPLICATION_FAILOVER_FAILED;
|
s->stage = BLOCK_REPLICATION_FAILOVER_FAILED;
|
||||||
s->error = -EIO;
|
s->error = -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -625,7 +625,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
s = bs->opaque;
|
s = bs->opaque;
|
||||||
|
|
||||||
if (s->replication_state != BLOCK_REPLICATION_RUNNING) {
|
if (s->stage != BLOCK_REPLICATION_RUNNING) {
|
||||||
error_setg(errp, "Block replication is not running");
|
error_setg(errp, "Block replication is not running");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
@@ -633,7 +633,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
|
|
||||||
switch (s->mode) {
|
switch (s->mode) {
|
||||||
case REPLICATION_MODE_PRIMARY:
|
case REPLICATION_MODE_PRIMARY:
|
||||||
s->replication_state = BLOCK_REPLICATION_DONE;
|
s->stage = BLOCK_REPLICATION_DONE;
|
||||||
s->error = 0;
|
s->error = 0;
|
||||||
break;
|
break;
|
||||||
case REPLICATION_MODE_SECONDARY:
|
case REPLICATION_MODE_SECONDARY:
|
||||||
@@ -648,12 +648,12 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
|
|
||||||
if (!failover) {
|
if (!failover) {
|
||||||
secondary_do_checkpoint(s, errp);
|
secondary_do_checkpoint(s, errp);
|
||||||
s->replication_state = BLOCK_REPLICATION_DONE;
|
s->stage = BLOCK_REPLICATION_DONE;
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->replication_state = BLOCK_REPLICATION_FAILOVER;
|
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||||
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
||||||
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||||
NULL, replication_done, bs, true, errp);
|
NULL, replication_done, bs, true, errp);
|
||||||
|
|||||||
@@ -536,14 +536,12 @@ static SocketAddress *sd_socket_address(const char *path,
|
|||||||
SocketAddress *addr = g_new0(SocketAddress, 1);
|
SocketAddress *addr = g_new0(SocketAddress, 1);
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
addr->type = SOCKET_ADDRESS_KIND_UNIX;
|
addr->type = SOCKET_ADDRESS_TYPE_UNIX;
|
||||||
addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
addr->u.q_unix.path = g_strdup(path);
|
||||||
addr->u.q_unix.data->path = g_strdup(path);
|
|
||||||
} else {
|
} else {
|
||||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
addr->type = SOCKET_ADDRESS_TYPE_INET;
|
||||||
addr->u.inet.data = g_new0(InetSocketAddress, 1);
|
addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
|
||||||
addr->u.inet.data->host = g_strdup(host ?: SD_DEFAULT_ADDR);
|
addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
|
||||||
addr->u.inet.data->port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addr;
|
return addr;
|
||||||
@@ -554,7 +552,6 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
|||||||
QDict *server = NULL;
|
QDict *server = NULL;
|
||||||
QObject *crumpled_server = NULL;
|
QObject *crumpled_server = NULL;
|
||||||
Visitor *iv = NULL;
|
Visitor *iv = NULL;
|
||||||
SocketAddressFlat *saddr_flat = NULL;
|
|
||||||
SocketAddress *saddr = NULL;
|
SocketAddress *saddr = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
@@ -574,16 +571,13 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
|||||||
* visitor expects the former.
|
* visitor expects the former.
|
||||||
*/
|
*/
|
||||||
iv = qobject_input_visitor_new(crumpled_server);
|
iv = qobject_input_visitor_new(crumpled_server);
|
||||||
visit_type_SocketAddressFlat(iv, NULL, &saddr_flat, &local_err);
|
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
saddr = socket_address_crumple(saddr_flat);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
qapi_free_SocketAddressFlat(saddr_flat);
|
|
||||||
visit_free(iv);
|
visit_free(iv);
|
||||||
qobject_decref(crumpled_server);
|
qobject_decref(crumpled_server);
|
||||||
QDECREF(server);
|
QDECREF(server);
|
||||||
@@ -597,7 +591,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
|
|||||||
|
|
||||||
fd = socket_connect(s->addr, NULL, NULL, errp);
|
fd = socket_connect(s->addr, NULL, NULL, errp);
|
||||||
|
|
||||||
if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) {
|
if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) {
|
||||||
int ret = socket_set_nodelay(fd);
|
int ret = socket_set_nodelay(fd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("%s", strerror(errno));
|
error_report("%s", strerror(errno));
|
||||||
@@ -2159,9 +2153,8 @@ static int64_t sd_getlength(BlockDriverState *bs)
|
|||||||
return s->inode.vdi_size;
|
return s->inode.vdi_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
static int sd_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
|
||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
int ret, fd;
|
int ret, fd;
|
||||||
unsigned int datalen;
|
unsigned int datalen;
|
||||||
@@ -2169,16 +2162,15 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
|
|
||||||
max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
|
max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
|
||||||
if (offset < s->inode.vdi_size) {
|
if (offset < s->inode.vdi_size) {
|
||||||
error_report("shrinking is not supported");
|
error_setg(errp, "shrinking is not supported");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (offset > max_vdi_size) {
|
} else if (offset > max_vdi_size) {
|
||||||
error_report("too big image size");
|
error_setg(errp, "too big image size");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = connect_to_sdog(s, &local_err);
|
fd = connect_to_sdog(s, errp);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_report_err(local_err);
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2191,7 +2183,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("failed to update an inode.");
|
error_setg_errno(errp, -ret, "failed to update an inode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2456,7 +2448,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
|
|
||||||
if (offset > s->inode.vdi_size) {
|
if (offset > s->inode.vdi_size) {
|
||||||
ret = sd_truncate(bs, offset);
|
ret = sd_truncate(bs, offset, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||||||
|
|
||||||
qdict_extract_subqdict(options, &file_options, "file.");
|
qdict_extract_subqdict(options, &file_options, "file.");
|
||||||
QDECREF(file_options);
|
QDECREF(file_options);
|
||||||
qdict_put(options, "file", qstring_from_str(bdrv_get_node_name(file)));
|
qdict_put_str(options, "file", bdrv_get_node_name(file));
|
||||||
|
|
||||||
drv->bdrv_close(bs);
|
drv->bdrv_close(bs);
|
||||||
bdrv_unref_child(bs, bs->file);
|
bdrv_unref_child(bs, bs->file);
|
||||||
|
|||||||
16
block/ssh.c
16
block/ssh.c
@@ -227,24 +227,23 @@ static int parse_uri(const char *filename, QDict *options, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(uri->user && strcmp(uri->user, "") != 0) {
|
if(uri->user && strcmp(uri->user, "") != 0) {
|
||||||
qdict_put(options, "user", qstring_from_str(uri->user));
|
qdict_put_str(options, "user", uri->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "server.host", qstring_from_str(uri->server));
|
qdict_put_str(options, "server.host", uri->server);
|
||||||
|
|
||||||
port_str = g_strdup_printf("%d", uri->port ?: 22);
|
port_str = g_strdup_printf("%d", uri->port ?: 22);
|
||||||
qdict_put(options, "server.port", qstring_from_str(port_str));
|
qdict_put_str(options, "server.port", port_str);
|
||||||
g_free(port_str);
|
g_free(port_str);
|
||||||
|
|
||||||
qdict_put(options, "path", qstring_from_str(uri->path));
|
qdict_put_str(options, "path", uri->path);
|
||||||
|
|
||||||
/* Pick out any query parameters that we understand, and ignore
|
/* Pick out any query parameters that we understand, and ignore
|
||||||
* the rest.
|
* the rest.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < qp->n; ++i) {
|
for (i = 0; i < qp->n; ++i) {
|
||||||
if (strcmp(qp->p[i].name, "host_key_check") == 0) {
|
if (strcmp(qp->p[i].name, "host_key_check") == 0) {
|
||||||
qdict_put(options, "host_key_check",
|
qdict_put_str(options, "host_key_check", qp->p[i].value);
|
||||||
qstring_from_str(qp->p[i].value));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,9 +573,8 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (host) {
|
if (host) {
|
||||||
qdict_put(output_opts, "server.host", qstring_from_str(host));
|
qdict_put_str(output_opts, "server.host", host);
|
||||||
qdict_put(output_opts, "server.port",
|
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
|
||||||
qstring_from_str(port ?: stringify(22)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
@@ -832,9 +832,9 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (image_type == VDI_TYPE_STATIC) {
|
if (image_type == VDI_TYPE_STATIC) {
|
||||||
ret = blk_truncate(blk, offset + blocks * block_size);
|
ret = blk_truncate(blk, offset + blocks * block_size, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Failed to statically allocate %s", filename);
|
error_prepend(errp, "Failed to statically allocate %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -548,7 +548,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
if (new_file_size % (1024*1024)) {
|
if (new_file_size % (1024*1024)) {
|
||||||
/* round up to nearest 1MB boundary */
|
/* round up to nearest 1MB boundary */
|
||||||
new_file_size = ((new_file_size >> 20) + 1) << 20;
|
new_file_size = ((new_file_size >> 20) + 1) << 20;
|
||||||
bdrv_truncate(bs->file, new_file_size);
|
bdrv_truncate(bs->file, new_file_size, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_vfree(desc_entries);
|
qemu_vfree(desc_entries);
|
||||||
|
|||||||
27
block/vhdx.c
27
block/vhdx.c
@@ -24,7 +24,7 @@
|
|||||||
#include "qemu/crc32c.h"
|
#include "qemu/crc32c.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "block/vhdx.h"
|
#include "block/vhdx.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
|
|
||||||
/* Options for VHDX creation */
|
/* Options for VHDX creation */
|
||||||
@@ -1171,7 +1171,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
/* per the spec, the address for a block is in units of 1MB */
|
/* per the spec, the address for a block is in units of 1MB */
|
||||||
*new_offset = ROUND_UP(*new_offset, 1024 * 1024);
|
*new_offset = ROUND_UP(*new_offset, 1024 * 1024);
|
||||||
|
|
||||||
return bdrv_truncate(bs->file, *new_offset + s->block_size);
|
return bdrv_truncate(bs->file, *new_offset + s->block_size, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1586,7 +1586,7 @@ exit:
|
|||||||
static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
||||||
uint64_t image_size, VHDXImageType type,
|
uint64_t image_size, VHDXImageType type,
|
||||||
bool use_zero_blocks, uint64_t file_offset,
|
bool use_zero_blocks, uint64_t file_offset,
|
||||||
uint32_t length)
|
uint32_t length, Error **errp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t data_file_offset;
|
uint64_t data_file_offset;
|
||||||
@@ -1607,16 +1607,17 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
|||||||
if (type == VHDX_TYPE_DYNAMIC) {
|
if (type == VHDX_TYPE_DYNAMIC) {
|
||||||
/* All zeroes, so we can just extend the file - the end of the BAT
|
/* All zeroes, so we can just extend the file - the end of the BAT
|
||||||
* is the furthest thing we have written yet */
|
* is the furthest thing we have written yet */
|
||||||
ret = blk_truncate(blk, data_file_offset);
|
ret = blk_truncate(blk, data_file_offset, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
} else if (type == VHDX_TYPE_FIXED) {
|
} else if (type == VHDX_TYPE_FIXED) {
|
||||||
ret = blk_truncate(blk, data_file_offset + image_size);
|
ret = blk_truncate(blk, data_file_offset + image_size, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
error_setg(errp, "Unsupported image type");
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -1627,6 +1628,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
|||||||
/* for a fixed file, the default BAT entry is not zero */
|
/* for a fixed file, the default BAT entry is not zero */
|
||||||
s->bat = g_try_malloc0(length);
|
s->bat = g_try_malloc0(length);
|
||||||
if (length && s->bat == NULL) {
|
if (length && s->bat == NULL) {
|
||||||
|
error_setg(errp, "Failed to allocate memory for the BAT");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -1646,6 +1648,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
|||||||
}
|
}
|
||||||
ret = blk_pwrite(blk, file_offset, s->bat, length, 0);
|
ret = blk_pwrite(blk, file_offset, s->bat, length, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write the BAT");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1671,7 +1674,8 @@ static int vhdx_create_new_region_table(BlockBackend *blk,
|
|||||||
uint32_t log_size,
|
uint32_t log_size,
|
||||||
bool use_zero_blocks,
|
bool use_zero_blocks,
|
||||||
VHDXImageType type,
|
VHDXImageType type,
|
||||||
uint64_t *metadata_offset)
|
uint64_t *metadata_offset,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
@@ -1740,7 +1744,7 @@ static int vhdx_create_new_region_table(BlockBackend *blk,
|
|||||||
/* The region table gives us the data we need to create the BAT,
|
/* The region table gives us the data we need to create the BAT,
|
||||||
* so do that now */
|
* so do that now */
|
||||||
ret = vhdx_create_bat(blk, s, image_size, type, use_zero_blocks,
|
ret = vhdx_create_bat(blk, s, image_size, type, use_zero_blocks,
|
||||||
bat_file_offset, bat_length);
|
bat_file_offset, bat_length, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -1749,12 +1753,14 @@ static int vhdx_create_new_region_table(BlockBackend *blk,
|
|||||||
ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer,
|
ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer,
|
||||||
VHDX_HEADER_BLOCK_SIZE, 0);
|
VHDX_HEADER_BLOCK_SIZE, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write first region table");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer,
|
ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer,
|
||||||
VHDX_HEADER_BLOCK_SIZE, 0);
|
VHDX_HEADER_BLOCK_SIZE, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write second region table");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1825,6 +1831,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto exit;
|
goto exit;
|
||||||
} else {
|
} else {
|
||||||
|
error_setg(errp, "Invalid subformat '%s'", type);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -1879,12 +1886,14 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature),
|
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature),
|
||||||
0);
|
0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write file signature");
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
if (creator) {
|
if (creator) {
|
||||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||||
creator, creator_items * sizeof(gunichar2), 0);
|
creator, creator_items * sizeof(gunichar2), 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write creator field");
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1893,13 +1902,14 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
/* Creates (B),(C) */
|
/* Creates (B),(C) */
|
||||||
ret = vhdx_create_new_headers(blk, image_size, log_size);
|
ret = vhdx_create_new_headers(blk, image_size, log_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to write image headers");
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
||||||
ret = vhdx_create_new_region_table(blk, image_size, block_size, 512,
|
ret = vhdx_create_new_region_table(blk, image_size, block_size, 512,
|
||||||
log_size, use_zero_blocks, image_type,
|
log_size, use_zero_blocks, image_type,
|
||||||
&metadata_offset);
|
&metadata_offset, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
@@ -1908,6 +1918,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
ret = vhdx_create_new_metadata(blk, image_size, block_size, 512,
|
ret = vhdx_create_new_metadata(blk, image_size, block_size, 512,
|
||||||
metadata_offset, image_type);
|
metadata_offset, image_type);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to initialize metadata");
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
block/vmdk.c
15
block/vmdk.c
@@ -31,7 +31,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
@@ -1714,10 +1714,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
blk_set_allow_write_beyond_eof(blk, true);
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
if (flat) {
|
if (flat) {
|
||||||
ret = blk_truncate(blk, filesize);
|
ret = blk_truncate(blk, filesize, errp);
|
||||||
if (ret < 0) {
|
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
|
||||||
}
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
magic = cpu_to_be32(VMDK4_MAGIC);
|
magic = cpu_to_be32(VMDK4_MAGIC);
|
||||||
@@ -1780,9 +1777,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9);
|
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2090,10 +2086,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||||
* for description file */
|
* for description file */
|
||||||
if (desc_offset == 0) {
|
if (desc_offset == 0) {
|
||||||
ret = blk_truncate(new_blk, desc_len);
|
ret = blk_truncate(new_blk, desc_len, errp);
|
||||||
if (ret < 0) {
|
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
exit:
|
exit:
|
||||||
if (new_blk) {
|
if (new_blk) {
|
||||||
|
|||||||
15
block/vpc.c
15
block/vpc.c
@@ -28,7 +28,7 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
|
|
||||||
@@ -851,20 +851,21 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||||
int64_t total_size)
|
int64_t total_size, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Add footer to total size */
|
/* Add footer to total size */
|
||||||
total_size += HEADER_SIZE;
|
total_size += HEADER_SIZE;
|
||||||
|
|
||||||
ret = blk_truncate(blk, total_size);
|
ret = blk_truncate(blk, total_size, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE, 0);
|
ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Unable to write VHD header");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,11 +997,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
if (disk_type == VHD_DYNAMIC) {
|
if (disk_type == VHD_DYNAMIC) {
|
||||||
ret = create_dynamic_disk(blk, buf, total_sectors);
|
ret = create_dynamic_disk(blk, buf, total_sectors);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Unable to create or write VHD header");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = create_fixed_disk(blk, buf, total_size);
|
ret = create_fixed_disk(blk, buf, total_size, errp);
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
error_setg(errp, "Unable to create or write VHD header");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
#include "qapi/qmp/qint.h"
|
#include "qapi/qmp/qint.h"
|
||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
@@ -1057,10 +1057,10 @@ static void vvfat_parse_filename(const char *filename, QDict *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in the options QDict */
|
/* Fill in the options QDict */
|
||||||
qdict_put(options, "dir", qstring_from_str(filename));
|
qdict_put_str(options, "dir", filename);
|
||||||
qdict_put(options, "fat-type", qint_from_int(fat_type));
|
qdict_put_int(options, "fat-type", fat_type);
|
||||||
qdict_put(options, "floppy", qbool_from_bool(floppy));
|
qdict_put_bool(options, "floppy", floppy);
|
||||||
qdict_put(options, "rw", qbool_from_bool(rw));
|
qdict_put_bool(options, "rw", rw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
@@ -3051,7 +3051,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
qdict_put(options, "write-target.driver", qstring_from_str("qcow"));
|
qdict_put_str(options, "write-target.driver", "qcow");
|
||||||
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
|
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
|
||||||
&child_vvfat_qcow, false, errp);
|
&child_vvfat_qcow, false, errp);
|
||||||
QDECREF(options);
|
QDECREF(options);
|
||||||
|
|||||||
@@ -182,15 +182,15 @@ static int vxhs_parse_uri(const char *filename, QDict *options)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, VXHS_OPT_SERVER".host", qstring_from_str(uri->server));
|
qdict_put_str(options, VXHS_OPT_SERVER ".host", uri->server);
|
||||||
|
|
||||||
if (uri->port) {
|
if (uri->port) {
|
||||||
port = g_strdup_printf("%d", uri->port);
|
port = g_strdup_printf("%d", uri->port);
|
||||||
qdict_put(options, VXHS_OPT_SERVER".port", qstring_from_str(port));
|
qdict_put_str(options, VXHS_OPT_SERVER ".port", port);
|
||||||
g_free(port);
|
g_free(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(options, "vdisk-id", qstring_from_str(uri->path));
|
qdict_put_str(options, "vdisk-id", uri->path);
|
||||||
|
|
||||||
trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
|
trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
|
||||||
uri_free(uri);
|
uri_free(uri);
|
||||||
|
|||||||
@@ -99,9 +99,8 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void qmp_nbd_server_start(SocketAddress *addr,
|
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||||
bool has_tls_creds, const char *tls_creds,
|
Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
if (nbd_server) {
|
if (nbd_server) {
|
||||||
error_setg(errp, "NBD server already running");
|
error_setg(errp, "NBD server already running");
|
||||||
@@ -118,14 +117,14 @@ void qmp_nbd_server_start(SocketAddress *addr,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_tls_creds) {
|
if (tls_creds) {
|
||||||
nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
|
nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
|
||||||
if (!nbd_server->tlscreds) {
|
if (!nbd_server->tlscreds) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */
|
/* TODO SOCKET_ADDRESS_TYPE_FD where fd has AF_INET or AF_INET6 */
|
||||||
if (addr->type != SOCKET_ADDRESS_KIND_INET) {
|
if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
|
||||||
error_setg(errp, "TLS is only supported with IPv4/IPv6");
|
error_setg(errp, "TLS is only supported with IPv4/IPv6");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -145,6 +144,16 @@ void qmp_nbd_server_start(SocketAddress *addr,
|
|||||||
nbd_server = NULL;
|
nbd_server = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
||||||
|
bool has_tls_creds, const char *tls_creds,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
SocketAddress *addr_flat = socket_address_flatten(addr);
|
||||||
|
|
||||||
|
nbd_server_start(addr_flat, tls_creds, errp);
|
||||||
|
qapi_free_SocketAddress(addr_flat);
|
||||||
|
}
|
||||||
|
|
||||||
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
|||||||
71
blockdev.c
71
blockdev.c
@@ -527,7 +527,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
error_setg(errp, "Cannot specify both 'driver' and 'format'");
|
error_setg(errp, "Cannot specify both 'driver' and 'format'");
|
||||||
goto early_err;
|
goto early_err;
|
||||||
}
|
}
|
||||||
qdict_put(bs_opts, "driver", qstring_from_str(buf));
|
qdict_put_str(bs_opts, "driver", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||||
@@ -903,10 +903,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
copy_on_read = false;
|
copy_on_read = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put(bs_opts, BDRV_OPT_READ_ONLY,
|
qdict_put_str(bs_opts, BDRV_OPT_READ_ONLY, read_only ? "on" : "off");
|
||||||
qstring_from_str(read_only ? "on" : "off"));
|
qdict_put_str(bs_opts, "copy-on-read", copy_on_read ? "on" : "off");
|
||||||
qdict_put(bs_opts, "copy-on-read",
|
|
||||||
qstring_from_str(copy_on_read ? "on" :"off"));
|
|
||||||
|
|
||||||
/* Controller type */
|
/* Controller type */
|
||||||
value = qemu_opt_get(legacy_opts, "if");
|
value = qemu_opt_get(legacy_opts, "if");
|
||||||
@@ -1030,7 +1028,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
new_id = g_strdup_printf("%s%s%i", if_name[type],
|
new_id = g_strdup_printf("%s%s%i", if_name[type],
|
||||||
mediastr, unit_id);
|
mediastr, unit_id);
|
||||||
}
|
}
|
||||||
qdict_put(bs_opts, "id", qstring_from_str(new_id));
|
qdict_put_str(bs_opts, "id", new_id);
|
||||||
g_free(new_id);
|
g_free(new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1067,7 +1065,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
error_report("werror is not supported by this bus type");
|
error_report("werror is not supported by this bus type");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
qdict_put(bs_opts, "werror", qstring_from_str(werror));
|
qdict_put_str(bs_opts, "werror", werror);
|
||||||
}
|
}
|
||||||
|
|
||||||
rerror = qemu_opt_get(legacy_opts, "rerror");
|
rerror = qemu_opt_get(legacy_opts, "rerror");
|
||||||
@@ -1077,7 +1075,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
error_report("rerror is not supported by this bus type");
|
error_report("rerror is not supported by this bus type");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
qdict_put(bs_opts, "rerror", qstring_from_str(rerror));
|
qdict_put_str(bs_opts, "rerror", rerror);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actual block device init: Functionality shared with blockdev-add */
|
/* Actual block device init: Functionality shared with blockdev-add */
|
||||||
@@ -1737,10 +1735,9 @@ static void external_snapshot_prepare(BlkActionState *common,
|
|||||||
|
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
if (s->has_snapshot_node_name) {
|
if (s->has_snapshot_node_name) {
|
||||||
qdict_put(options, "node-name",
|
qdict_put_str(options, "node-name", snapshot_node_name);
|
||||||
qstring_from_str(snapshot_node_name));
|
|
||||||
}
|
}
|
||||||
qdict_put(options, "driver", qstring_from_str(format));
|
qdict_put_str(options, "driver", format);
|
||||||
|
|
||||||
flags |= BDRV_O_NO_BACKING;
|
flags |= BDRV_O_NO_BACKING;
|
||||||
}
|
}
|
||||||
@@ -2579,11 +2576,10 @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
|
|||||||
|
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
|
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
|
||||||
qdict_put(options, "detect-zeroes",
|
qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
|
||||||
qstring_from_str(detect_zeroes ? "on" : "off"));
|
|
||||||
|
|
||||||
if (has_format) {
|
if (has_format) {
|
||||||
qdict_put(options, "driver", qstring_from_str(format));
|
qdict_put_str(options, "driver", format);
|
||||||
}
|
}
|
||||||
|
|
||||||
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
|
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
|
||||||
@@ -2927,29 +2923,9 @@ void qmp_block_resize(bool has_device, const char *device,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* complete all in-flight operations before resizing the device */
|
bdrv_drained_begin(bs);
|
||||||
bdrv_drain_all();
|
ret = blk_truncate(blk, size, errp);
|
||||||
|
bdrv_drained_end(bs);
|
||||||
ret = blk_truncate(blk, size);
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case -ENOMEDIUM:
|
|
||||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
|
||||||
break;
|
|
||||||
case -ENOTSUP:
|
|
||||||
error_setg(errp, QERR_UNSUPPORTED);
|
|
||||||
break;
|
|
||||||
case -EACCES:
|
|
||||||
error_setg(errp, "Device '%s' is read only", device);
|
|
||||||
break;
|
|
||||||
case -EBUSY:
|
|
||||||
error_setg(errp, QERR_DEVICE_IN_USE, device);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error_setg_errno(errp, -ret, "Could not resize");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
@@ -3174,6 +3150,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int flags;
|
int flags;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
|
bool set_backing_hd = false;
|
||||||
|
|
||||||
if (!backup->has_speed) {
|
if (!backup->has_speed) {
|
||||||
backup->speed = 0;
|
backup->speed = 0;
|
||||||
@@ -3224,6 +3201,8 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||||||
}
|
}
|
||||||
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
||||||
source = bs;
|
source = bs;
|
||||||
|
flags |= BDRV_O_NO_BACKING;
|
||||||
|
set_backing_hd = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = bdrv_getlength(bs);
|
size = bdrv_getlength(bs);
|
||||||
@@ -3250,8 +3229,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (backup->format) {
|
if (backup->format) {
|
||||||
options = qdict_new();
|
if (!options) {
|
||||||
qdict_put(options, "driver", qstring_from_str(backup->format));
|
options = qdict_new();
|
||||||
|
}
|
||||||
|
qdict_put_str(options, "driver", backup->format);
|
||||||
}
|
}
|
||||||
|
|
||||||
target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
|
target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
|
||||||
@@ -3261,6 +3242,14 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||||||
|
|
||||||
bdrv_set_aio_context(target_bs, aio_context);
|
bdrv_set_aio_context(target_bs, aio_context);
|
||||||
|
|
||||||
|
if (set_backing_hd) {
|
||||||
|
bdrv_set_backing_hd(target_bs, source, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
bdrv_unref(target_bs);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (backup->has_bitmap) {
|
if (backup->has_bitmap) {
|
||||||
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
||||||
if (!bmap) {
|
if (!bmap) {
|
||||||
@@ -3555,10 +3544,10 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
|||||||
|
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
if (arg->has_node_name) {
|
if (arg->has_node_name) {
|
||||||
qdict_put(options, "node-name", qstring_from_str(arg->node_name));
|
qdict_put_str(options, "node-name", arg->node_name);
|
||||||
}
|
}
|
||||||
if (format) {
|
if (format) {
|
||||||
qdict_put(options, "driver", qstring_from_str(format));
|
qdict_put_str(options, "driver", format);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mirroring takes care of copy-on-write using the source's backing
|
/* Mirroring takes care of copy-on-write using the source's backing
|
||||||
|
|||||||
@@ -744,10 +744,7 @@ int main(int argc, char **argv)
|
|||||||
qemu_init_cpu_list();
|
qemu_init_cpu_list();
|
||||||
module_call_init(MODULE_INIT_QOM);
|
module_call_init(MODULE_INIT_QOM);
|
||||||
|
|
||||||
if ((envlist = envlist_create()) == NULL) {
|
envlist = envlist_create();
|
||||||
(void) fprintf(stderr, "Unable to allocate envlist\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add current environment into the list */
|
/* add current environment into the list */
|
||||||
for (wrk = environ; *wrk != NULL; wrk++) {
|
for (wrk = environ; *wrk != NULL; wrk++) {
|
||||||
@@ -785,10 +782,7 @@ int main(int argc, char **argv)
|
|||||||
usage();
|
usage();
|
||||||
} else if (!strcmp(r, "ignore-environment")) {
|
} else if (!strcmp(r, "ignore-environment")) {
|
||||||
envlist_free(envlist);
|
envlist_free(envlist);
|
||||||
if ((envlist = envlist_create()) == NULL) {
|
envlist = envlist_create();
|
||||||
(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)
|
||||||
@@ -956,10 +950,10 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (wrk = target_environ; *wrk; wrk++) {
|
for (wrk = target_environ; *wrk; wrk++) {
|
||||||
free(*wrk);
|
g_free(*wrk);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(target_environ);
|
g_free(target_environ);
|
||||||
|
|
||||||
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
|
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
|
||||||
qemu_log("guest_base 0x%lx\n", guest_base);
|
qemu_log("guest_base 0x%lx\n", guest_base);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
|||||||
ret = qio_channel_read(
|
ret = qio_channel_read(
|
||||||
chan, (gchar *)buf, len, NULL);
|
chan, (gchar *)buf, len, NULL);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -89,9 +89,9 @@ static void fd_chr_update_read_handler(Chardev *chr,
|
|||||||
{
|
{
|
||||||
FDChardev *s = FD_CHARDEV(chr);
|
FDChardev *s = FD_CHARDEV(chr);
|
||||||
|
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
if (s->ioc_in) {
|
if (s->ioc_in) {
|
||||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
|
chr->gsource = io_add_watch_poll(chr, s->ioc_in,
|
||||||
fd_chr_read_poll,
|
fd_chr_read_poll,
|
||||||
fd_chr_read, chr,
|
fd_chr_read, chr,
|
||||||
context);
|
context);
|
||||||
@@ -103,7 +103,7 @@ static void char_fd_finalize(Object *obj)
|
|||||||
Chardev *chr = CHARDEV(obj);
|
Chardev *chr = CHARDEV(obj);
|
||||||
FDChardev *s = FD_CHARDEV(obj);
|
FDChardev *s = FD_CHARDEV(obj);
|
||||||
|
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
if (s->ioc_in) {
|
if (s->ioc_in) {
|
||||||
object_unref(OBJECT(s->ioc_in));
|
object_unref(OBJECT(s->ioc_in));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ static GSourceFuncs io_watch_poll_funcs = {
|
|||||||
.finalize = io_watch_poll_finalize,
|
.finalize = io_watch_poll_finalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
guint io_add_watch_poll(Chardev *chr,
|
GSource *io_add_watch_poll(Chardev *chr,
|
||||||
QIOChannel *ioc,
|
QIOChannel *ioc,
|
||||||
IOCanReadHandler *fd_can_read,
|
IOCanReadHandler *fd_can_read,
|
||||||
QIOChannelFunc fd_read,
|
QIOChannelFunc fd_read,
|
||||||
@@ -106,7 +106,6 @@ guint io_add_watch_poll(Chardev *chr,
|
|||||||
GMainContext *context)
|
GMainContext *context)
|
||||||
{
|
{
|
||||||
IOWatchPoll *iwp;
|
IOWatchPoll *iwp;
|
||||||
int tag;
|
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
|
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
|
||||||
@@ -122,21 +121,15 @@ guint io_add_watch_poll(Chardev *chr,
|
|||||||
g_source_set_name((GSource *)iwp, name);
|
g_source_set_name((GSource *)iwp, name);
|
||||||
g_free(name);
|
g_free(name);
|
||||||
|
|
||||||
tag = g_source_attach(&iwp->parent, context);
|
g_source_attach(&iwp->parent, context);
|
||||||
g_source_unref(&iwp->parent);
|
g_source_unref(&iwp->parent);
|
||||||
return tag;
|
return (GSource *)iwp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void io_remove_watch_poll(guint tag, GMainContext *context)
|
static void io_remove_watch_poll(GSource *source)
|
||||||
{
|
{
|
||||||
GSource *source;
|
|
||||||
IOWatchPoll *iwp;
|
IOWatchPoll *iwp;
|
||||||
|
|
||||||
g_return_if_fail(tag > 0);
|
|
||||||
|
|
||||||
source = g_main_context_find_source_by_id(context, tag);
|
|
||||||
g_return_if_fail(source != NULL);
|
|
||||||
|
|
||||||
iwp = io_watch_poll_from_source(source);
|
iwp = io_watch_poll_from_source(source);
|
||||||
if (iwp->src) {
|
if (iwp->src) {
|
||||||
g_source_destroy(iwp->src);
|
g_source_destroy(iwp->src);
|
||||||
@@ -146,11 +139,11 @@ static void io_remove_watch_poll(guint tag, GMainContext *context)
|
|||||||
g_source_destroy(&iwp->parent);
|
g_source_destroy(&iwp->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_fd_in_watch(Chardev *chr, GMainContext *context)
|
void remove_fd_in_watch(Chardev *chr)
|
||||||
{
|
{
|
||||||
if (chr->fd_in_tag) {
|
if (chr->gsource) {
|
||||||
io_remove_watch_poll(chr->fd_in_tag, context);
|
io_remove_watch_poll(chr->gsource);
|
||||||
chr->fd_in_tag = 0;
|
chr->gsource = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,14 @@
|
|||||||
#include "sysemu/char.h"
|
#include "sysemu/char.h"
|
||||||
|
|
||||||
/* Can only be used for read */
|
/* Can only be used for read */
|
||||||
guint io_add_watch_poll(Chardev *chr,
|
GSource *io_add_watch_poll(Chardev *chr,
|
||||||
QIOChannel *ioc,
|
QIOChannel *ioc,
|
||||||
IOCanReadHandler *fd_can_read,
|
IOCanReadHandler *fd_can_read,
|
||||||
QIOChannelFunc fd_read,
|
QIOChannelFunc fd_read,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GMainContext *context);
|
GMainContext *context);
|
||||||
|
|
||||||
void remove_fd_in_watch(Chardev *chr, GMainContext *context);
|
void remove_fd_in_watch(Chardev *chr);
|
||||||
|
|
||||||
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len);
|
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len);
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ static void mux_print_help(Chardev *chr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
|
static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
|
||||||
{
|
{
|
||||||
CharBackend *be = d->backends[mux_nr];
|
CharBackend *be = d->backends[mux_nr];
|
||||||
|
|
||||||
@@ -222,9 +222,9 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
|
|
||||||
bool muxes_realized;
|
bool muxes_realized;
|
||||||
|
|
||||||
static void mux_chr_event(void *opaque, int event)
|
void mux_chr_send_all_event(Chardev *chr, int event)
|
||||||
{
|
{
|
||||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
MuxChardev *d = MUX_CHARDEV(chr);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!muxes_realized) {
|
if (!muxes_realized) {
|
||||||
@@ -237,6 +237,11 @@ static void mux_chr_event(void *opaque, int event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mux_chr_event(void *opaque, int event)
|
||||||
|
{
|
||||||
|
mux_chr_send_all_event(CHARDEV(opaque), event);
|
||||||
|
}
|
||||||
|
|
||||||
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
|
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
|
||||||
{
|
{
|
||||||
MuxChardev *d = MUX_CHARDEV(s);
|
MuxChardev *d = MUX_CHARDEV(s);
|
||||||
|
|||||||
@@ -58,6 +58,6 @@ typedef struct MuxChardev {
|
|||||||
|
|
||||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
||||||
void mux_set_focus(Chardev *chr, int focus);
|
void mux_set_focus(Chardev *chr, int focus);
|
||||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
|
void mux_chr_send_all_event(Chardev *chr, int event);
|
||||||
|
|
||||||
#endif /* CHAR_MUX_H */
|
#endif /* CHAR_MUX_H */
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
|
|||||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||||
|
|
||||||
s->open_tag = 0;
|
s->open_tag = 0;
|
||||||
qemu_chr_be_generic_open(chr);
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ static void pty_chr_state(Chardev *chr, int connected)
|
|||||||
g_source_remove(s->open_tag);
|
g_source_remove(s->open_tag);
|
||||||
s->open_tag = 0;
|
s->open_tag = 0;
|
||||||
}
|
}
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
s->connected = 0;
|
s->connected = 0;
|
||||||
/* (re-)connect poll interval for idle guests: once per second.
|
/* (re-)connect poll interval for idle guests: once per second.
|
||||||
* We check more frequently in case the guests sends data to
|
* We check more frequently in case the guests sends data to
|
||||||
@@ -215,8 +215,8 @@ static void pty_chr_state(Chardev *chr, int connected)
|
|||||||
s->connected = 1;
|
s->connected = 1;
|
||||||
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
|
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
|
||||||
}
|
}
|
||||||
if (!chr->fd_in_tag) {
|
if (!chr->gsource) {
|
||||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||||
pty_chr_read_poll,
|
pty_chr_read_poll,
|
||||||
pty_chr_read,
|
pty_chr_read,
|
||||||
chr, NULL);
|
chr, NULL);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ typedef struct {
|
|||||||
SocketAddress *addr;
|
SocketAddress *addr;
|
||||||
bool is_listen;
|
bool is_listen;
|
||||||
bool is_telnet;
|
bool is_telnet;
|
||||||
|
bool is_tn3270;
|
||||||
|
|
||||||
guint reconnect_timer;
|
guint reconnect_timer;
|
||||||
int64_t reconnect_time;
|
int64_t reconnect_time;
|
||||||
@@ -141,19 +142,25 @@ static int tcp_chr_read_poll(void *opaque)
|
|||||||
return s->max_size;
|
return s->max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IAC 255
|
|
||||||
#define IAC_BREAK 243
|
|
||||||
static void tcp_chr_process_IAC_bytes(Chardev *chr,
|
static void tcp_chr_process_IAC_bytes(Chardev *chr,
|
||||||
SocketChardev *s,
|
SocketChardev *s,
|
||||||
uint8_t *buf, int *size)
|
uint8_t *buf, int *size)
|
||||||
{
|
{
|
||||||
/* Handle any telnet client's basic IAC options to satisfy char by
|
/* Handle any telnet or tn3270 client's basic IAC options.
|
||||||
* char mode with no echo. All IAC options will be removed from
|
* For telnet options, it satisfies char by char mode with no echo.
|
||||||
* the buf and the do_telnetopt variable will be used to track the
|
* For tn3270 options, it satisfies binary mode with EOR.
|
||||||
* state of the width of the IAC information.
|
* All IAC options will be removed from the buf and the do_opt
|
||||||
|
* pointer will be used to track the state of the width of the
|
||||||
|
* IAC information.
|
||||||
*
|
*
|
||||||
* IAC commands come in sets of 3 bytes with the exception of the
|
* RFC854: "All TELNET commands consist of at least a two byte sequence.
|
||||||
* "IAC BREAK" command and the double IAC.
|
* The commands dealing with option negotiation are three byte sequences,
|
||||||
|
* the third byte being the code for the option referenced."
|
||||||
|
* "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes.
|
||||||
|
* "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary
|
||||||
|
* for tn3270.
|
||||||
|
* NOP, Break and Interrupt Process(IP) might be encountered during a TN3270
|
||||||
|
* session, and NOP and IP need to be done later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
@@ -174,6 +181,18 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
|
|||||||
/* Handle IAC break commands by sending a serial break */
|
/* Handle IAC break commands by sending a serial break */
|
||||||
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
||||||
s->do_telnetopt++;
|
s->do_telnetopt++;
|
||||||
|
} else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR
|
||||||
|
|| (unsigned char)buf[i] == IAC_SB
|
||||||
|
|| (unsigned char)buf[i] == IAC_SE)
|
||||||
|
&& s->do_telnetopt == 2) {
|
||||||
|
buf[j++] = IAC;
|
||||||
|
buf[j++] = buf[i];
|
||||||
|
s->do_telnetopt++;
|
||||||
|
} else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP
|
||||||
|
|| (unsigned char)buf[i] == IAC_NOP)
|
||||||
|
&& s->do_telnetopt == 2) {
|
||||||
|
/* TODO: IP and NOP need to be implemented later. */
|
||||||
|
s->do_telnetopt++;
|
||||||
}
|
}
|
||||||
s->do_telnetopt++;
|
s->do_telnetopt++;
|
||||||
}
|
}
|
||||||
@@ -327,7 +346,7 @@ static void tcp_chr_free_connection(Chardev *chr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tcp_set_msgfds(chr, NULL, 0);
|
tcp_set_msgfds(chr, NULL, 0);
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
object_unref(OBJECT(s->sioc));
|
object_unref(OBJECT(s->sioc));
|
||||||
s->sioc = NULL;
|
s->sioc = NULL;
|
||||||
object_unref(OBJECT(s->ioc));
|
object_unref(OBJECT(s->ioc));
|
||||||
@@ -341,31 +360,40 @@ static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
|
|||||||
bool is_listen, bool is_telnet)
|
bool is_listen, bool is_telnet)
|
||||||
{
|
{
|
||||||
switch (addr->type) {
|
switch (addr->type) {
|
||||||
case SOCKET_ADDRESS_KIND_INET:
|
case SOCKET_ADDRESS_TYPE_INET:
|
||||||
return g_strdup_printf("%s%s:%s:%s%s", prefix,
|
return g_strdup_printf("%s%s:%s:%s%s", prefix,
|
||||||
is_telnet ? "telnet" : "tcp",
|
is_telnet ? "telnet" : "tcp",
|
||||||
addr->u.inet.data->host,
|
addr->u.inet.host,
|
||||||
addr->u.inet.data->port,
|
addr->u.inet.port,
|
||||||
is_listen ? ",server" : "");
|
is_listen ? ",server" : "");
|
||||||
break;
|
break;
|
||||||
case SOCKET_ADDRESS_KIND_UNIX:
|
case SOCKET_ADDRESS_TYPE_UNIX:
|
||||||
return g_strdup_printf("%sunix:%s%s", prefix,
|
return g_strdup_printf("%sunix:%s%s", prefix,
|
||||||
addr->u.q_unix.data->path,
|
addr->u.q_unix.path,
|
||||||
is_listen ? ",server" : "");
|
is_listen ? ",server" : "");
|
||||||
break;
|
break;
|
||||||
case SOCKET_ADDRESS_KIND_FD:
|
case SOCKET_ADDRESS_TYPE_FD:
|
||||||
return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str,
|
return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str,
|
||||||
is_listen ? ",server" : "");
|
is_listen ? ",server" : "");
|
||||||
break;
|
break;
|
||||||
case SOCKET_ADDRESS_KIND_VSOCK:
|
case SOCKET_ADDRESS_TYPE_VSOCK:
|
||||||
return g_strdup_printf("%svsock:%s:%s", prefix,
|
return g_strdup_printf("%svsock:%s:%s", prefix,
|
||||||
addr->u.vsock.data->cid,
|
addr->u.vsock.cid,
|
||||||
addr->u.vsock.data->port);
|
addr->u.vsock.port);
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_disconnected_filename(SocketChardev *s)
|
||||||
|
{
|
||||||
|
Chardev *chr = CHARDEV(s);
|
||||||
|
|
||||||
|
g_free(chr->filename);
|
||||||
|
chr->filename = SocketAddress_to_str("disconnected:", s->addr,
|
||||||
|
s->is_listen, s->is_telnet);
|
||||||
|
}
|
||||||
|
|
||||||
static void tcp_chr_disconnect(Chardev *chr)
|
static void tcp_chr_disconnect(Chardev *chr)
|
||||||
{
|
{
|
||||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||||
@@ -380,8 +408,7 @@ static void tcp_chr_disconnect(Chardev *chr)
|
|||||||
s->listen_tag = qio_channel_add_watch(
|
s->listen_tag = qio_channel_add_watch(
|
||||||
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
|
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
|
||||||
}
|
}
|
||||||
chr->filename = SocketAddress_to_str("disconnected:", s->addr,
|
update_disconnected_filename(s);
|
||||||
s->is_listen, s->is_telnet);
|
|
||||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||||
if (s->reconnect_time) {
|
if (s->reconnect_time) {
|
||||||
qemu_chr_socket_restart_timer(chr);
|
qemu_chr_socket_restart_timer(chr);
|
||||||
@@ -484,12 +511,12 @@ static void tcp_chr_connect(void *opaque)
|
|||||||
|
|
||||||
s->connected = 1;
|
s->connected = 1;
|
||||||
if (s->ioc) {
|
if (s->ioc) {
|
||||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||||
tcp_chr_read_poll,
|
tcp_chr_read_poll,
|
||||||
tcp_chr_read,
|
tcp_chr_read,
|
||||||
chr, NULL);
|
chr, NULL);
|
||||||
}
|
}
|
||||||
qemu_chr_be_generic_open(chr);
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_chr_update_read_handler(Chardev *chr,
|
static void tcp_chr_update_read_handler(Chardev *chr,
|
||||||
@@ -501,9 +528,9 @@ static void tcp_chr_update_read_handler(Chardev *chr,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
if (s->ioc) {
|
if (s->ioc) {
|
||||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||||
tcp_chr_read_poll,
|
tcp_chr_read_poll,
|
||||||
tcp_chr_read, chr,
|
tcp_chr_read, chr,
|
||||||
context);
|
context);
|
||||||
@@ -512,7 +539,7 @@ static void tcp_chr_update_read_handler(Chardev *chr,
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Chardev *chr;
|
Chardev *chr;
|
||||||
char buf[12];
|
char buf[21];
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
} TCPChardevTelnetInit;
|
} TCPChardevTelnetInit;
|
||||||
|
|
||||||
@@ -550,9 +577,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
|||||||
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
|
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
|
||||||
init->chr = chr;
|
|
||||||
init->buflen = 12;
|
|
||||||
|
|
||||||
#define IACSET(x, a, b, c) \
|
#define IACSET(x, a, b, c) \
|
||||||
do { \
|
do { \
|
||||||
x[n++] = a; \
|
x[n++] = a; \
|
||||||
@@ -560,12 +584,26 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
|||||||
x[n++] = c; \
|
x[n++] = c; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Prep the telnet negotion to put telnet in binary,
|
init->chr = chr;
|
||||||
* no echo, single char mode */
|
if (!s->is_tn3270) {
|
||||||
IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
|
init->buflen = 12;
|
||||||
IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
|
/* Prep the telnet negotion to put telnet in binary,
|
||||||
IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
|
* no echo, single char mode */
|
||||||
IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
|
IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
|
||||||
|
IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
|
||||||
|
IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
|
||||||
|
IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
|
||||||
|
} else {
|
||||||
|
init->buflen = 21;
|
||||||
|
/* Prep the TN3270 negotion based on RFC1576 */
|
||||||
|
IACSET(init->buf, 0xff, 0xfd, 0x19); /* IAC DO EOR */
|
||||||
|
IACSET(init->buf, 0xff, 0xfb, 0x19); /* IAC WILL EOR */
|
||||||
|
IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO BINARY */
|
||||||
|
IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL BINARY */
|
||||||
|
IACSET(init->buf, 0xff, 0xfd, 0x18); /* IAC DO TERMINAL TYPE */
|
||||||
|
IACSET(init->buf, 0xff, 0xfa, 0x18); /* IAC SB TERMINAL TYPE */
|
||||||
|
IACSET(init->buf, 0x01, 0xff, 0xf0); /* SEND IAC SE */
|
||||||
|
}
|
||||||
|
|
||||||
#undef IACSET
|
#undef IACSET
|
||||||
|
|
||||||
@@ -585,7 +623,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
|
|||||||
if (qio_task_propagate_error(task, NULL)) {
|
if (qio_task_propagate_error(task, NULL)) {
|
||||||
tcp_chr_disconnect(chr);
|
tcp_chr_disconnect(chr);
|
||||||
} else {
|
} else {
|
||||||
if (s->do_telnetopt) {
|
/* tn3270 does not support TLS yet */
|
||||||
|
if (s->do_telnetopt && !s->is_tn3270) {
|
||||||
tcp_chr_telnet_init(chr);
|
tcp_chr_telnet_init(chr);
|
||||||
} else {
|
} else {
|
||||||
tcp_chr_connect(chr);
|
tcp_chr_connect(chr);
|
||||||
@@ -609,7 +648,7 @@ static void tcp_chr_tls_init(Chardev *chr)
|
|||||||
} else {
|
} else {
|
||||||
tioc = qio_channel_tls_new_client(
|
tioc = qio_channel_tls_new_client(
|
||||||
s->ioc, s->tls_creds,
|
s->ioc, s->tls_creds,
|
||||||
s->addr->u.inet.data->host,
|
s->addr->u.inet.host,
|
||||||
&err);
|
&err);
|
||||||
}
|
}
|
||||||
if (tioc == NULL) {
|
if (tioc == NULL) {
|
||||||
@@ -820,16 +859,18 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
|||||||
{
|
{
|
||||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||||
ChardevSocket *sock = backend->u.socket.data;
|
ChardevSocket *sock = backend->u.socket.data;
|
||||||
SocketAddress *addr = sock->addr;
|
|
||||||
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
|
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
|
||||||
bool is_listen = sock->has_server ? sock->server : true;
|
bool is_listen = sock->has_server ? sock->server : true;
|
||||||
bool is_telnet = sock->has_telnet ? sock->telnet : false;
|
bool is_telnet = sock->has_telnet ? sock->telnet : false;
|
||||||
|
bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
|
||||||
bool is_waitconnect = sock->has_wait ? sock->wait : false;
|
bool is_waitconnect = sock->has_wait ? sock->wait : false;
|
||||||
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
|
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
|
||||||
QIOChannelSocket *sioc = NULL;
|
QIOChannelSocket *sioc = NULL;
|
||||||
|
SocketAddress *addr;
|
||||||
|
|
||||||
s->is_listen = is_listen;
|
s->is_listen = is_listen;
|
||||||
s->is_telnet = is_telnet;
|
s->is_telnet = is_telnet;
|
||||||
|
s->is_tn3270 = is_tn3270;
|
||||||
s->do_nodelay = do_nodelay;
|
s->do_nodelay = do_nodelay;
|
||||||
if (sock->tls_creds) {
|
if (sock->tls_creds) {
|
||||||
Object *creds;
|
Object *creds;
|
||||||
@@ -864,22 +905,21 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s->addr = QAPI_CLONE(SocketAddress, sock->addr);
|
s->addr = addr = socket_address_flatten(sock->addr);
|
||||||
|
|
||||||
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
|
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
|
||||||
/* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */
|
/* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */
|
||||||
if (addr->type == SOCKET_ADDRESS_KIND_UNIX) {
|
if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) {
|
||||||
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
|
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* be isn't opened until we get a connection */
|
/* be isn't opened until we get a connection */
|
||||||
*be_opened = false;
|
*be_opened = false;
|
||||||
|
|
||||||
chr->filename = SocketAddress_to_str("disconnected:",
|
update_disconnected_filename(s);
|
||||||
addr, is_listen, is_telnet);
|
|
||||||
|
|
||||||
if (is_listen) {
|
if (is_listen) {
|
||||||
if (is_telnet) {
|
if (is_telnet || is_tn3270) {
|
||||||
s->do_telnetopt = 1;
|
s->do_telnetopt = 1;
|
||||||
}
|
}
|
||||||
} else if (reconnect > 0) {
|
} else if (reconnect > 0) {
|
||||||
@@ -904,6 +944,11 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
|||||||
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
|
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qapi_free_SocketAddress(s->addr);
|
||||||
|
s->addr = socket_local_address(sioc->fd, errp);
|
||||||
|
update_disconnected_filename(s);
|
||||||
|
|
||||||
s->listen_ioc = sioc;
|
s->listen_ioc = sioc;
|
||||||
if (is_waitconnect &&
|
if (is_waitconnect &&
|
||||||
qemu_chr_wait_connected(chr, errp) < 0) {
|
qemu_chr_wait_connected(chr, errp) < 0) {
|
||||||
@@ -933,13 +978,14 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
bool is_listen = qemu_opt_get_bool(opts, "server", false);
|
bool is_listen = qemu_opt_get_bool(opts, "server", false);
|
||||||
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
|
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
|
||||||
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
|
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
|
||||||
|
bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false);
|
||||||
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
|
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
|
||||||
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
|
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
|
||||||
const char *path = qemu_opt_get(opts, "path");
|
const char *path = qemu_opt_get(opts, "path");
|
||||||
const char *host = qemu_opt_get(opts, "host");
|
const char *host = qemu_opt_get(opts, "host");
|
||||||
const char *port = qemu_opt_get(opts, "port");
|
const char *port = qemu_opt_get(opts, "port");
|
||||||
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
|
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
|
||||||
SocketAddress *addr;
|
SocketAddressLegacy *addr;
|
||||||
ChardevSocket *sock;
|
ChardevSocket *sock;
|
||||||
|
|
||||||
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
|
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
|
||||||
@@ -968,20 +1014,22 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
sock->server = is_listen;
|
sock->server = is_listen;
|
||||||
sock->has_telnet = true;
|
sock->has_telnet = true;
|
||||||
sock->telnet = is_telnet;
|
sock->telnet = is_telnet;
|
||||||
|
sock->has_tn3270 = true;
|
||||||
|
sock->tn3270 = is_tn3270;
|
||||||
sock->has_wait = true;
|
sock->has_wait = true;
|
||||||
sock->wait = is_waitconnect;
|
sock->wait = is_waitconnect;
|
||||||
sock->has_reconnect = true;
|
sock->has_reconnect = true;
|
||||||
sock->reconnect = reconnect;
|
sock->reconnect = reconnect;
|
||||||
sock->tls_creds = g_strdup(tls_creds);
|
sock->tls_creds = g_strdup(tls_creds);
|
||||||
|
|
||||||
addr = g_new0(SocketAddress, 1);
|
addr = g_new0(SocketAddressLegacy, 1);
|
||||||
if (path) {
|
if (path) {
|
||||||
UnixSocketAddress *q_unix;
|
UnixSocketAddress *q_unix;
|
||||||
addr->type = SOCKET_ADDRESS_KIND_UNIX;
|
addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
|
||||||
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
||||||
q_unix->path = g_strdup(path);
|
q_unix->path = g_strdup(path);
|
||||||
} else {
|
} else {
|
||||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||||
*addr->u.inet.data = (InetSocketAddress) {
|
*addr->u.inet.data = (InetSocketAddress) {
|
||||||
.host = g_strdup(host),
|
.host = g_strdup(host),
|
||||||
@@ -997,6 +1045,23 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
sock->addr = addr;
|
sock->addr = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
char_socket_get_addr(Object *obj, Visitor *v, const char *name,
|
||||||
|
void *opaque, Error **errp)
|
||||||
|
{
|
||||||
|
SocketChardev *s = SOCKET_CHARDEV(obj);
|
||||||
|
|
||||||
|
visit_type_SocketAddress(v, name, &s->addr, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
char_socket_get_connected(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
SocketChardev *s = SOCKET_CHARDEV(obj);
|
||||||
|
|
||||||
|
return s->connected;
|
||||||
|
}
|
||||||
|
|
||||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||||
@@ -1012,6 +1077,13 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
|||||||
cc->chr_add_client = tcp_chr_add_client;
|
cc->chr_add_client = tcp_chr_add_client;
|
||||||
cc->chr_add_watch = tcp_chr_add_watch;
|
cc->chr_add_watch = tcp_chr_add_watch;
|
||||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||||
|
|
||||||
|
object_class_property_add(oc, "addr", "SocketAddress",
|
||||||
|
char_socket_get_addr, NULL,
|
||||||
|
NULL, NULL, &error_abort);
|
||||||
|
|
||||||
|
object_class_property_add_bool(oc, "connected", char_socket_get_connected,
|
||||||
|
NULL, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo char_socket_type_info = {
|
static const TypeInfo char_socket_type_info = {
|
||||||
|
|||||||
@@ -51,6 +51,18 @@ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
|||||||
s->ioc, (const char *)buf, len, NULL);
|
s->ioc, (const char *)buf, len, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void udp_chr_flush_buffer(UdpChardev *s)
|
||||||
|
{
|
||||||
|
Chardev *chr = CHARDEV(s);
|
||||||
|
|
||||||
|
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
||||||
|
int n = MIN(s->max_size, s->bufcnt - s->bufptr);
|
||||||
|
qemu_chr_be_write(chr, &s->buf[s->bufptr], n);
|
||||||
|
s->bufptr += n;
|
||||||
|
s->max_size = qemu_chr_be_can_write(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int udp_chr_read_poll(void *opaque)
|
static int udp_chr_read_poll(void *opaque)
|
||||||
{
|
{
|
||||||
Chardev *chr = CHARDEV(opaque);
|
Chardev *chr = CHARDEV(opaque);
|
||||||
@@ -61,11 +73,8 @@ static int udp_chr_read_poll(void *opaque)
|
|||||||
/* If there were any stray characters in the queue process them
|
/* If there were any stray characters in the queue process them
|
||||||
* first
|
* first
|
||||||
*/
|
*/
|
||||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
udp_chr_flush_buffer(s);
|
||||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
|
||||||
s->bufptr++;
|
|
||||||
s->max_size = qemu_chr_be_can_write(chr);
|
|
||||||
}
|
|
||||||
return s->max_size;
|
return s->max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,17 +90,12 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
|||||||
ret = qio_channel_read(
|
ret = qio_channel_read(
|
||||||
s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
|
s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
s->bufcnt = ret;
|
s->bufcnt = ret;
|
||||||
|
|
||||||
s->bufptr = 0;
|
s->bufptr = 0;
|
||||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
udp_chr_flush_buffer(s);
|
||||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
|
||||||
s->bufptr++;
|
|
||||||
s->max_size = qemu_chr_be_can_write(chr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -101,9 +105,9 @@ static void udp_chr_update_read_handler(Chardev *chr,
|
|||||||
{
|
{
|
||||||
UdpChardev *s = UDP_CHARDEV(chr);
|
UdpChardev *s = UDP_CHARDEV(chr);
|
||||||
|
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
if (s->ioc) {
|
if (s->ioc) {
|
||||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||||
udp_chr_read_poll,
|
udp_chr_read_poll,
|
||||||
udp_chr_read, chr,
|
udp_chr_read, chr,
|
||||||
context);
|
context);
|
||||||
@@ -115,7 +119,7 @@ static void char_udp_finalize(Object *obj)
|
|||||||
Chardev *chr = CHARDEV(obj);
|
Chardev *chr = CHARDEV(obj);
|
||||||
UdpChardev *s = UDP_CHARDEV(obj);
|
UdpChardev *s = UDP_CHARDEV(obj);
|
||||||
|
|
||||||
remove_fd_in_watch(chr, NULL);
|
remove_fd_in_watch(chr);
|
||||||
if (s->ioc) {
|
if (s->ioc) {
|
||||||
object_unref(OBJECT(s->ioc));
|
object_unref(OBJECT(s->ioc));
|
||||||
}
|
}
|
||||||
@@ -130,7 +134,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
const char *localaddr = qemu_opt_get(opts, "localaddr");
|
const char *localaddr = qemu_opt_get(opts, "localaddr");
|
||||||
const char *localport = qemu_opt_get(opts, "localport");
|
const char *localport = qemu_opt_get(opts, "localport");
|
||||||
bool has_local = false;
|
bool has_local = false;
|
||||||
SocketAddress *addr;
|
SocketAddressLegacy *addr;
|
||||||
ChardevUdp *udp;
|
ChardevUdp *udp;
|
||||||
|
|
||||||
backend->type = CHARDEV_BACKEND_KIND_UDP;
|
backend->type = CHARDEV_BACKEND_KIND_UDP;
|
||||||
@@ -155,8 +159,8 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
|
udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
|
||||||
qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
|
qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
|
||||||
|
|
||||||
addr = g_new0(SocketAddress, 1);
|
addr = g_new0(SocketAddressLegacy, 1);
|
||||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||||
*addr->u.inet.data = (InetSocketAddress) {
|
*addr->u.inet.data = (InetSocketAddress) {
|
||||||
.host = g_strdup(host),
|
.host = g_strdup(host),
|
||||||
@@ -170,8 +174,8 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
|
|
||||||
if (has_local) {
|
if (has_local) {
|
||||||
udp->has_local = true;
|
udp->has_local = true;
|
||||||
addr = g_new0(SocketAddress, 1);
|
addr = g_new0(SocketAddressLegacy, 1);
|
||||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||||
*addr->u.inet.data = (InetSocketAddress) {
|
*addr->u.inet.data = (InetSocketAddress) {
|
||||||
.host = g_strdup(localaddr),
|
.host = g_strdup(localaddr),
|
||||||
@@ -187,13 +191,17 @@ static void qmp_chardev_open_udp(Chardev *chr,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ChardevUdp *udp = backend->u.udp.data;
|
ChardevUdp *udp = backend->u.udp.data;
|
||||||
|
SocketAddress *local_addr = socket_address_flatten(udp->local);
|
||||||
|
SocketAddress *remote_addr = socket_address_flatten(udp->remote);
|
||||||
QIOChannelSocket *sioc = qio_channel_socket_new();
|
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||||
char *name;
|
char *name;
|
||||||
UdpChardev *s = UDP_CHARDEV(chr);
|
UdpChardev *s = UDP_CHARDEV(chr);
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (qio_channel_socket_dgram_sync(sioc,
|
ret = qio_channel_socket_dgram_sync(sioc, local_addr, remote_addr, errp);
|
||||||
udp->local, udp->remote,
|
qapi_free_SocketAddress(local_addr);
|
||||||
errp) < 0) {
|
qapi_free_SocketAddress(remote_addr);
|
||||||
|
if (ret < 0) {
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
163
chardev/char.c
163
chardev/char.c
@@ -42,8 +42,10 @@
|
|||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* character device */
|
/* character device */
|
||||||
|
|
||||||
static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
|
static Object *get_chardevs_root(void)
|
||||||
QTAILQ_HEAD_INITIALIZER(chardevs);
|
{
|
||||||
|
return container_get(object_get_root(), "/chardevs");
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_chr_be_event(Chardev *s, int event)
|
void qemu_chr_be_event(Chardev *s, int event)
|
||||||
{
|
{
|
||||||
@@ -66,12 +68,6 @@ void qemu_chr_be_event(Chardev *s, int event)
|
|||||||
be->chr_event(be->opaque, event);
|
be->chr_event(be->opaque, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_be_generic_open(Chardev *s)
|
|
||||||
{
|
|
||||||
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Not reporting errors from writing to logfile, as logs are
|
/* Not reporting errors from writing to logfile, as logs are
|
||||||
* defined to be "best effort" only */
|
* defined to be "best effort" only */
|
||||||
static void qemu_chr_fe_write_log(Chardev *s,
|
static void qemu_chr_fe_write_log(Chardev *s,
|
||||||
@@ -453,26 +449,24 @@ static const TypeInfo char_type_info = {
|
|||||||
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||||
* immediately.
|
* immediately.
|
||||||
*/
|
*/
|
||||||
|
static int open_muxes(Object *child, void *opaque)
|
||||||
|
{
|
||||||
|
if (CHARDEV_IS_MUX(child)) {
|
||||||
|
/* send OPENED to all already-attached FEs */
|
||||||
|
mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||||
|
/* mark mux as OPENED so any new FEs will immediately receive
|
||||||
|
* OPENED event
|
||||||
|
*/
|
||||||
|
qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void muxes_realize_done(Notifier *notifier, void *unused)
|
static void muxes_realize_done(Notifier *notifier, void *unused)
|
||||||
{
|
{
|
||||||
Chardev *chr;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
|
||||||
if (CHARDEV_IS_MUX(chr)) {
|
|
||||||
MuxChardev *d = MUX_CHARDEV(chr);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* send OPENED to all already-attached FEs */
|
|
||||||
for (i = 0; i < d->mux_cnt; i++) {
|
|
||||||
mux_chr_send_event(d, i, CHR_EVENT_OPENED);
|
|
||||||
}
|
|
||||||
/* mark mux as OPENED so any new FEs will immediately receive
|
|
||||||
* OPENED event
|
|
||||||
*/
|
|
||||||
qemu_chr_be_generic_open(chr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
muxes_realized = true;
|
muxes_realized = true;
|
||||||
|
object_child_foreach(get_chardevs_root(), open_muxes, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Notifier muxes_realize_notify = {
|
static Notifier muxes_realize_notify = {
|
||||||
@@ -560,7 +554,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
|
|||||||
cc = CHARDEV_GET_CLASS(s);
|
cc = CHARDEV_GET_CLASS(s);
|
||||||
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
||||||
fe_open = 0;
|
fe_open = 0;
|
||||||
remove_fd_in_watch(s, context);
|
remove_fd_in_watch(s);
|
||||||
} else {
|
} else {
|
||||||
fe_open = 1;
|
fe_open = 1;
|
||||||
}
|
}
|
||||||
@@ -581,7 +575,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
|
|||||||
/* We're connecting to an already opened device, so let's make sure we
|
/* We're connecting to an already opened device, so let's make sure we
|
||||||
also get the open event */
|
also get the open event */
|
||||||
if (s->be_open) {
|
if (s->be_open) {
|
||||||
qemu_chr_be_generic_open(s);
|
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,7 +690,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
|||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
if (strstart(filename, "tcp:", &p) ||
|
if (strstart(filename, "tcp:", &p) ||
|
||||||
strstart(filename, "telnet:", &p)) {
|
strstart(filename, "telnet:", &p) ||
|
||||||
|
strstart(filename, "tn3270:", &p)) {
|
||||||
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
|
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
|
||||||
host[0] = 0;
|
host[0] = 0;
|
||||||
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
|
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
|
||||||
@@ -712,8 +707,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (strstart(filename, "telnet:", &p))
|
if (strstart(filename, "telnet:", &p)) {
|
||||||
qemu_opt_set(opts, "telnet", "on", &error_abort);
|
qemu_opt_set(opts, "telnet", "on", &error_abort);
|
||||||
|
} else if (strstart(filename, "tn3270:", &p)) {
|
||||||
|
qemu_opt_set(opts, "tn3270", "on", &error_abort);
|
||||||
|
}
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
if (strstart(filename, "udp:", &p)) {
|
if (strstart(filename, "udp:", &p)) {
|
||||||
@@ -770,7 +768,7 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
|
|||||||
const char *logfile = qemu_opt_get(opts, "logfile");
|
const char *logfile = qemu_opt_get(opts, "logfile");
|
||||||
|
|
||||||
backend->has_logfile = logfile != NULL;
|
backend->has_logfile = logfile != NULL;
|
||||||
backend->logfile = logfile ? g_strdup(logfile) : NULL;
|
backend->logfile = g_strdup(logfile);
|
||||||
|
|
||||||
backend->has_logappend = true;
|
backend->has_logappend = true;
|
||||||
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
|
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
|
||||||
@@ -805,26 +803,6 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
|
|||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Chardev *qemu_chardev_add(const char *id, const char *typename,
|
|
||||||
ChardevBackend *backend, Error **errp)
|
|
||||||
{
|
|
||||||
Chardev *chr;
|
|
||||||
|
|
||||||
chr = qemu_chr_find(id);
|
|
||||||
if (chr) {
|
|
||||||
error_setg(errp, "Chardev '%s' already exists", id);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
chr = qemu_chardev_new(id, typename, backend, errp);
|
|
||||||
if (!chr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
|
|
||||||
return chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ChardevAlias {
|
static const struct ChardevAlias {
|
||||||
const char *typename;
|
const char *typename;
|
||||||
const char *alias;
|
const char *alias;
|
||||||
@@ -941,9 +919,10 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
backend->u.null.data = ccom; /* Any ChardevCommon member would work */
|
backend->u.null.data = ccom; /* Any ChardevCommon member would work */
|
||||||
}
|
}
|
||||||
|
|
||||||
chr = qemu_chardev_add(bid ? bid : id,
|
chr = qemu_chardev_new(bid ? bid : id,
|
||||||
object_class_get_name(OBJECT_CLASS(cc)),
|
object_class_get_name(OBJECT_CLASS(cc)),
|
||||||
backend, errp);
|
backend, errp);
|
||||||
|
|
||||||
if (chr == NULL) {
|
if (chr == NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -955,9 +934,9 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
||||||
backend->u.mux.data = g_new0(ChardevMux, 1);
|
backend->u.mux.data = g_new0(ChardevMux, 1);
|
||||||
backend->u.mux.data->chardev = g_strdup(bid);
|
backend->u.mux.data->chardev = g_strdup(bid);
|
||||||
mux = qemu_chardev_add(id, TYPE_CHARDEV_MUX, backend, errp);
|
mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
|
||||||
if (mux == NULL) {
|
if (mux == NULL) {
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
chr = NULL;
|
chr = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1071,27 +1050,29 @@ void qemu_chr_fe_disconnect(CharBackend *be)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_delete(Chardev *chr)
|
static int qmp_query_chardev_foreach(Object *obj, void *data)
|
||||||
{
|
{
|
||||||
QTAILQ_REMOVE(&chardevs, chr, next);
|
Chardev *chr = CHARDEV(obj);
|
||||||
object_unref(OBJECT(chr));
|
ChardevInfoList **list = data;
|
||||||
|
ChardevInfoList *info = g_malloc0(sizeof(*info));
|
||||||
|
|
||||||
|
info->value = g_malloc0(sizeof(*info->value));
|
||||||
|
info->value->label = g_strdup(chr->label);
|
||||||
|
info->value->filename = g_strdup(chr->filename);
|
||||||
|
info->value->frontend_open = chr->be && chr->be->fe_open;
|
||||||
|
|
||||||
|
info->next = *list;
|
||||||
|
*list = info;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChardevInfoList *qmp_query_chardev(Error **errp)
|
ChardevInfoList *qmp_query_chardev(Error **errp)
|
||||||
{
|
{
|
||||||
ChardevInfoList *chr_list = NULL;
|
ChardevInfoList *chr_list = NULL;
|
||||||
Chardev *chr;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
object_child_foreach(get_chardevs_root(),
|
||||||
ChardevInfoList *info = g_malloc0(sizeof(*info));
|
qmp_query_chardev_foreach, &chr_list);
|
||||||
info->value = g_malloc0(sizeof(*info->value));
|
|
||||||
info->value->label = g_strdup(chr->label);
|
|
||||||
info->value->filename = g_strdup(chr->filename);
|
|
||||||
info->value->frontend_open = chr->be && chr->be->fe_open;
|
|
||||||
|
|
||||||
info->next = chr_list;
|
|
||||||
chr_list = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return chr_list;
|
return chr_list;
|
||||||
}
|
}
|
||||||
@@ -1119,14 +1100,9 @@ ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
|
|||||||
|
|
||||||
Chardev *qemu_chr_find(const char *name)
|
Chardev *qemu_chr_find(const char *name)
|
||||||
{
|
{
|
||||||
Chardev *chr;
|
Object *obj = object_resolve_path_component(get_chardevs_root(), name);
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
return obj ? CHARDEV(obj) : NULL;
|
||||||
if (strcmp(chr->label, name) != 0)
|
|
||||||
continue;
|
|
||||||
return chr;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QemuOptsList qemu_chardev_opts = {
|
QemuOptsList qemu_chardev_opts = {
|
||||||
@@ -1176,6 +1152,9 @@ QemuOptsList qemu_chardev_opts = {
|
|||||||
},{
|
},{
|
||||||
.name = "telnet",
|
.name = "telnet",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
|
},{
|
||||||
|
.name = "tn3270",
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
},{
|
},{
|
||||||
.name = "tls-creds",
|
.name = "tls-creds",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
@@ -1236,22 +1215,23 @@ void qemu_chr_set_feature(Chardev *chr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Chardev *qemu_chardev_new(const char *id, const char *typename,
|
Chardev *qemu_chardev_new(const char *id, const char *typename,
|
||||||
ChardevBackend *backend, Error **errp)
|
ChardevBackend *backend,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
Object *obj;
|
||||||
Chardev *chr = NULL;
|
Chardev *chr = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool be_opened = true;
|
bool be_opened = true;
|
||||||
|
|
||||||
assert(g_str_has_prefix(typename, "chardev-"));
|
assert(g_str_has_prefix(typename, "chardev-"));
|
||||||
|
|
||||||
chr = CHARDEV(object_new(typename));
|
obj = object_new(typename);
|
||||||
|
chr = CHARDEV(obj);
|
||||||
chr->label = g_strdup(id);
|
chr->label = g_strdup(id);
|
||||||
|
|
||||||
qemu_char_open(chr, backend, &be_opened, &local_err);
|
qemu_char_open(chr, backend, &be_opened, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
goto end;
|
||||||
object_unref(OBJECT(chr));
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chr->filename) {
|
if (!chr->filename) {
|
||||||
@@ -1261,6 +1241,21 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
|
|||||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
object_property_add_child(get_chardevs_root(), id, obj, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
object_unref(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
object_unref(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return chr;
|
return chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,7 +1271,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
chr = qemu_chardev_add(id, object_class_get_name(OBJECT_CLASS(cc)),
|
chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
|
||||||
backend, errp);
|
backend, errp);
|
||||||
if (!chr) {
|
if (!chr) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1309,16 +1304,12 @@ void qmp_chardev_remove(const char *id, Error **errp)
|
|||||||
"Chardev '%s' cannot be unplugged in record/replay mode", id);
|
"Chardev '%s' cannot be unplugged in record/replay mode", id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_cleanup(void)
|
void qemu_chr_cleanup(void)
|
||||||
{
|
{
|
||||||
Chardev *chr, *tmp;
|
object_unparent(get_chardevs_root());
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
|
|
||||||
qemu_chr_delete(chr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void register_types(void)
|
static void register_types(void)
|
||||||
|
|||||||
44
configure
vendored
44
configure
vendored
@@ -611,6 +611,7 @@ NetBSD)
|
|||||||
audio_possible_drivers="oss sdl"
|
audio_possible_drivers="oss sdl"
|
||||||
oss_lib="-lossaudio"
|
oss_lib="-lossaudio"
|
||||||
HOST_VARIANT_DIR="netbsd"
|
HOST_VARIANT_DIR="netbsd"
|
||||||
|
supported_os="yes"
|
||||||
;;
|
;;
|
||||||
OpenBSD)
|
OpenBSD)
|
||||||
bsd="yes"
|
bsd="yes"
|
||||||
@@ -1334,7 +1335,7 @@ Advanced options (experts only):
|
|||||||
--oss-lib path to OSS library
|
--oss-lib path to OSS library
|
||||||
--cpu=CPU Build for host CPU [$cpu]
|
--cpu=CPU Build for host CPU [$cpu]
|
||||||
--with-coroutine=BACKEND coroutine backend. Supported options:
|
--with-coroutine=BACKEND coroutine backend. Supported options:
|
||||||
gthread, ucontext, sigaltstack, windows
|
ucontext, sigaltstack, windows
|
||||||
--enable-gcov enable test coverage analysis with gcov
|
--enable-gcov enable test coverage analysis with gcov
|
||||||
--gcov=GCOV use specified gcov [$gcov_tool]
|
--gcov=GCOV use specified gcov [$gcov_tool]
|
||||||
--disable-blobs disable installing provided firmware blobs
|
--disable-blobs disable installing provided firmware blobs
|
||||||
@@ -2014,7 +2015,7 @@ if test "$xen" != "no" ; then
|
|||||||
else
|
else
|
||||||
|
|
||||||
xen_libs="-lxenstore -lxenctrl -lxenguest"
|
xen_libs="-lxenstore -lxenctrl -lxenguest"
|
||||||
xen_stable_libs="-lxencall -lxenforeignmemory -lxengnttab -lxenevtchn"
|
xen_stable_libs="-lxenforeignmemory -lxengnttab -lxenevtchn"
|
||||||
|
|
||||||
# First we test whether Xen headers and libraries are available.
|
# First we test whether Xen headers and libraries are available.
|
||||||
# If no, we are done and there is no Xen support.
|
# If no, we are done and there is no Xen support.
|
||||||
@@ -4418,10 +4419,8 @@ fi
|
|||||||
# check and set a backend for coroutine
|
# check and set a backend for coroutine
|
||||||
|
|
||||||
# We prefer ucontext, but it's not always possible. The fallback
|
# We prefer ucontext, but it's not always possible. The fallback
|
||||||
# is sigcontext. gthread is not selectable except explicitly, because
|
# is sigcontext. On Windows the only valid backend is the Windows
|
||||||
# it is not functional enough to run QEMU proper. (It is occasionally
|
# specific one.
|
||||||
# useful for debugging purposes.) On Windows the only valid backend
|
|
||||||
# is the Windows-specific one.
|
|
||||||
|
|
||||||
ucontext_works=no
|
ucontext_works=no
|
||||||
if test "$darwin" != "yes"; then
|
if test "$darwin" != "yes"; then
|
||||||
@@ -4460,7 +4459,7 @@ else
|
|||||||
feature_not_found "ucontext"
|
feature_not_found "ucontext"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
gthread|sigaltstack)
|
sigaltstack)
|
||||||
if test "$mingw32" = "yes"; then
|
if test "$mingw32" = "yes"; then
|
||||||
error_exit "only the 'windows' coroutine backend is valid for Windows"
|
error_exit "only the 'windows' coroutine backend is valid for Windows"
|
||||||
fi
|
fi
|
||||||
@@ -4472,14 +4471,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$coroutine_pool" = ""; then
|
if test "$coroutine_pool" = ""; then
|
||||||
if test "$coroutine" = "gthread"; then
|
coroutine_pool=yes
|
||||||
coroutine_pool=no
|
|
||||||
else
|
|
||||||
coroutine_pool=yes
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then
|
|
||||||
error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$debug_stack_usage" = "yes"; then
|
if test "$debug_stack_usage" = "yes"; then
|
||||||
@@ -4852,6 +4844,20 @@ EOF
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# check for _Static_assert()
|
||||||
|
|
||||||
|
have_static_assert=no
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
_Static_assert(1, "success");
|
||||||
|
int main(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "" ; then
|
||||||
|
have_static_assert=yes
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# End of CC checks
|
# End of CC checks
|
||||||
# After here, no more $cc or $ld runs
|
# After here, no more $cc or $ld runs
|
||||||
@@ -5848,6 +5854,10 @@ if test "$have_sysmacros" = "yes" ; then
|
|||||||
echo "CONFIG_SYSMACROS=y" >> $config_host_mak
|
echo "CONFIG_SYSMACROS=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$have_static_assert" = "yes" ; then
|
||||||
|
echo "CONFIG_STATIC_ASSERT=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
# Hold two types of flag:
|
# Hold two types of flag:
|
||||||
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
|
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
|
||||||
# a thread we have a handle to
|
# a thread we have a handle to
|
||||||
@@ -6023,9 +6033,11 @@ TARGET_ABI_DIR=""
|
|||||||
|
|
||||||
case "$target_name" in
|
case "$target_name" in
|
||||||
i386)
|
i386)
|
||||||
|
gdb_xml_files="i386-32bit-core.xml"
|
||||||
;;
|
;;
|
||||||
x86_64)
|
x86_64)
|
||||||
TARGET_BASE_ARCH=i386
|
TARGET_BASE_ARCH=i386
|
||||||
|
gdb_xml_files="i386-64bit-core.xml"
|
||||||
;;
|
;;
|
||||||
alpha)
|
alpha)
|
||||||
mttcg="yes"
|
mttcg="yes"
|
||||||
@@ -6090,12 +6102,14 @@ case "$target_name" in
|
|||||||
ppc64)
|
ppc64)
|
||||||
TARGET_BASE_ARCH=ppc
|
TARGET_BASE_ARCH=ppc
|
||||||
TARGET_ABI_DIR=ppc
|
TARGET_ABI_DIR=ppc
|
||||||
|
mttcg=yes
|
||||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||||
;;
|
;;
|
||||||
ppc64le)
|
ppc64le)
|
||||||
TARGET_ARCH=ppc64
|
TARGET_ARCH=ppc64
|
||||||
TARGET_BASE_ARCH=ppc
|
TARGET_BASE_ARCH=ppc
|
||||||
TARGET_ABI_DIR=ppc
|
TARGET_ABI_DIR=ppc
|
||||||
|
mttcg=yes
|
||||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||||
;;
|
;;
|
||||||
ppc64abi32)
|
ppc64abi32)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ vu_panic(VuDev *dev, const char *msg, ...)
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
(void)vasprintf(&buf, msg, ap);
|
buf = g_strdup_vprintf(msg, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
dev->broken = true;
|
dev->broken = true;
|
||||||
@@ -1031,6 +1031,11 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
|
|||||||
idx = vq->last_avail_idx;
|
idx = vq->last_avail_idx;
|
||||||
|
|
||||||
total_bufs = in_total = out_total = 0;
|
total_bufs = in_total = out_total = 0;
|
||||||
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
while ((rc = virtqueue_num_heads(dev, vq, idx)) > 0) {
|
while ((rc = virtqueue_num_heads(dev, vq, idx)) > 0) {
|
||||||
unsigned int max, num_bufs, indirect = 0;
|
unsigned int max, num_bufs, indirect = 0;
|
||||||
struct vring_desc *desc;
|
struct vring_desc *desc;
|
||||||
@@ -1121,11 +1126,16 @@ vu_queue_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int in_bytes,
|
|||||||
|
|
||||||
/* Fetch avail_idx from VQ memory only when we really need to know if
|
/* Fetch avail_idx from VQ memory only when we really need to know if
|
||||||
* guest has added some buffers. */
|
* guest has added some buffers. */
|
||||||
int
|
bool
|
||||||
vu_queue_empty(VuDev *dev, VuVirtq *vq)
|
vu_queue_empty(VuDev *dev, VuVirtq *vq)
|
||||||
{
|
{
|
||||||
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (vq->shadow_avail_idx != vq->last_avail_idx) {
|
if (vq->shadow_avail_idx != vq->last_avail_idx) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vring_avail_idx(vq) == vq->last_avail_idx;
|
return vring_avail_idx(vq) == vq->last_avail_idx;
|
||||||
@@ -1174,7 +1184,8 @@ vring_notify(VuDev *dev, VuVirtq *vq)
|
|||||||
void
|
void
|
||||||
vu_queue_notify(VuDev *dev, VuVirtq *vq)
|
vu_queue_notify(VuDev *dev, VuVirtq *vq)
|
||||||
{
|
{
|
||||||
if (unlikely(dev->broken)) {
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1291,7 +1302,8 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz)
|
|||||||
struct vring_desc *desc;
|
struct vring_desc *desc;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (unlikely(dev->broken)) {
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1445,7 +1457,8 @@ vu_queue_fill(VuDev *dev, VuVirtq *vq,
|
|||||||
{
|
{
|
||||||
struct vring_used_elem uelem;
|
struct vring_used_elem uelem;
|
||||||
|
|
||||||
if (unlikely(dev->broken)) {
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1474,7 +1487,8 @@ vu_queue_flush(VuDev *dev, VuVirtq *vq, unsigned int count)
|
|||||||
{
|
{
|
||||||
uint16_t old, new;
|
uint16_t old, new;
|
||||||
|
|
||||||
if (unlikely(dev->broken)) {
|
if (unlikely(dev->broken) ||
|
||||||
|
unlikely(!vq->vring.avail)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,13 +327,13 @@ void vu_queue_set_notification(VuDev *dev, VuVirtq *vq, int enable);
|
|||||||
bool vu_queue_enabled(VuDev *dev, VuVirtq *vq);
|
bool vu_queue_enabled(VuDev *dev, VuVirtq *vq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vu_queue_enabled:
|
* vu_queue_empty:
|
||||||
* @dev: a VuDev context
|
* @dev: a VuDev context
|
||||||
* @vq: a VuVirtq queue
|
* @vq: a VuVirtq queue
|
||||||
*
|
*
|
||||||
* Returns: whether the queue is empty.
|
* Returns: true if the queue is empty or not ready.
|
||||||
*/
|
*/
|
||||||
int vu_queue_empty(VuDev *dev, VuVirtq *vq);
|
bool vu_queue_empty(VuDev *dev, VuVirtq *vq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vu_queue_notify:
|
* vu_queue_notify:
|
||||||
|
|||||||
16
cpus.c
16
cpus.c
@@ -50,6 +50,7 @@
|
|||||||
#include "qapi-event.h"
|
#include "qapi-event.h"
|
||||||
#include "hw/nmi.h"
|
#include "hw/nmi.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
|
||||||
#ifdef CONFIG_LINUX
|
#ifdef CONFIG_LINUX
|
||||||
|
|
||||||
@@ -1483,6 +1484,12 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
|||||||
/* Ignore everything else? */
|
/* Ignore everything else? */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (cpu->unplug) {
|
||||||
|
qemu_tcg_destroy_vcpu(cpu);
|
||||||
|
cpu->created = false;
|
||||||
|
qemu_cond_signal(&qemu_cpu_cond);
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_mb_set(&cpu->exit_request, 0);
|
atomic_mb_set(&cpu->exit_request, 0);
|
||||||
@@ -1859,6 +1866,8 @@ void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
|||||||
|
|
||||||
CpuInfoList *qmp_query_cpus(Error **errp)
|
CpuInfoList *qmp_query_cpus(Error **errp)
|
||||||
{
|
{
|
||||||
|
MachineState *ms = MACHINE(qdev_get_machine());
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||||
CpuInfoList *head = NULL, *cur_item = NULL;
|
CpuInfoList *head = NULL, *cur_item = NULL;
|
||||||
CPUState *cpu;
|
CPUState *cpu;
|
||||||
|
|
||||||
@@ -1909,6 +1918,13 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
|||||||
#else
|
#else
|
||||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||||
#endif
|
#endif
|
||||||
|
info->value->has_props = !!mc->cpu_index_to_instance_props;
|
||||||
|
if (info->value->has_props) {
|
||||||
|
CpuInstanceProperties *props;
|
||||||
|
props = g_malloc0(sizeof(*props));
|
||||||
|
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
|
||||||
|
info->value->props = props;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: waiting for the qapi to support GSList */
|
/* XXX: waiting for the qapi to support GSList */
|
||||||
if (!cur_item) {
|
if (!cur_item) {
|
||||||
|
|||||||
8
cputlb.c
8
cputlb.c
@@ -930,7 +930,13 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
|||||||
tlb_addr = tlbe->addr_write;
|
tlb_addr = tlbe->addr_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notice an IO access, or a notdirty page. */
|
/* Check notdirty */
|
||||||
|
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
|
||||||
|
tlb_set_dirty(ENV_GET_CPU(env), addr);
|
||||||
|
tlb_addr = tlb_addr & ~TLB_NOTDIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notice an IO access */
|
||||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||||
/* There's really nothing that can be done to
|
/* There's really nothing that can be done to
|
||||||
support this apart from stop-the-world. */
|
support this apart from stop-the-world. */
|
||||||
|
|||||||
@@ -473,9 +473,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
|
|||||||
* then encrypted.
|
* then encrypted.
|
||||||
*/
|
*/
|
||||||
rv = readfunc(block,
|
rv = readfunc(block,
|
||||||
opaque,
|
|
||||||
slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
||||||
splitkey, splitkeylen,
|
splitkey, splitkeylen,
|
||||||
|
opaque,
|
||||||
errp);
|
errp);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -676,9 +676,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
|
|||||||
|
|
||||||
/* Read the entire LUKS header, minus the key material from
|
/* Read the entire LUKS header, minus the key material from
|
||||||
* the underlying device */
|
* the underlying device */
|
||||||
rv = readfunc(block, opaque, 0,
|
rv = readfunc(block, 0,
|
||||||
(uint8_t *)&luks->header,
|
(uint8_t *)&luks->header,
|
||||||
sizeof(luks->header),
|
sizeof(luks->header),
|
||||||
|
opaque,
|
||||||
errp);
|
errp);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
ret = rv;
|
ret = rv;
|
||||||
@@ -1245,7 +1246,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
|
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
|
||||||
|
|
||||||
/* Reserve header space to match payload offset */
|
/* Reserve header space to match payload offset */
|
||||||
initfunc(block, opaque, block->payload_offset, &local_err);
|
initfunc(block, block->payload_offset, opaque, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto error;
|
goto error;
|
||||||
@@ -1267,9 +1268,10 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
|
|
||||||
|
|
||||||
/* Write out the partition header and key slot headers */
|
/* Write out the partition header and key slot headers */
|
||||||
writefunc(block, opaque, 0,
|
writefunc(block, 0,
|
||||||
(const uint8_t *)&luks->header,
|
(const uint8_t *)&luks->header,
|
||||||
sizeof(luks->header),
|
sizeof(luks->header),
|
||||||
|
opaque,
|
||||||
&local_err);
|
&local_err);
|
||||||
|
|
||||||
/* Delay checking local_err until we've byte-swapped */
|
/* Delay checking local_err until we've byte-swapped */
|
||||||
@@ -1295,10 +1297,11 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
|
|
||||||
/* Write out the master key material, starting at the
|
/* Write out the master key material, starting at the
|
||||||
* sector immediately following the partition header. */
|
* sector immediately following the partition header. */
|
||||||
if (writefunc(block, opaque,
|
if (writefunc(block,
|
||||||
luks->header.key_slots[0].key_offset *
|
luks->header.key_slots[0].key_offset *
|
||||||
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
||||||
splitkey, splitkeylen,
|
splitkey, splitkeylen,
|
||||||
|
opaque,
|
||||||
errp) != splitkeylen) {
|
errp) != splitkeylen) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include <gcrypt.h>
|
#include <gcrypt.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "crypto/random.h"
|
||||||
|
|
||||||
/* #define DEBUG_GNUTLS */
|
/* #define DEBUG_GNUTLS */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -146,5 +148,9 @@ int qcrypto_init(Error **errp)
|
|||||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (qcrypto_random_init(errp) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,3 +31,5 @@ int qcrypto_random_bytes(uint8_t *buf,
|
|||||||
gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM);
|
gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int qcrypto_random_init(Error **errp G_GNUC_UNUSED) { return 0; }
|
||||||
|
|||||||
@@ -41,3 +41,6 @@ int qcrypto_random_bytes(uint8_t *buf,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int qcrypto_random_init(Error **errp G_GNUC_UNUSED) { return 0; }
|
||||||
|
|||||||
@@ -22,14 +22,16 @@
|
|||||||
|
|
||||||
#include "crypto/random.h"
|
#include "crypto/random.h"
|
||||||
|
|
||||||
int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
|
#ifdef _WIN32
|
||||||
size_t buflen G_GNUC_UNUSED,
|
#include <wincrypt.h>
|
||||||
Error **errp)
|
static HCRYPTPROV hCryptProv;
|
||||||
{
|
#else
|
||||||
int fd;
|
static int fd; /* a file handle to either /dev/urandom or /dev/random */
|
||||||
int ret = -1;
|
#endif
|
||||||
int got;
|
|
||||||
|
|
||||||
|
int qcrypto_random_init(Error **errp)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
/* TBD perhaps also add support for BSD getentropy / Linux
|
/* TBD perhaps also add support for BSD getentropy / Linux
|
||||||
* getrandom syscalls directly */
|
* getrandom syscalls directly */
|
||||||
fd = open("/dev/urandom", O_RDONLY);
|
fd = open("/dev/urandom", O_RDONLY);
|
||||||
@@ -41,6 +43,25 @@ int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
|
|||||||
error_setg(errp, "No /dev/urandom or /dev/random found");
|
error_setg(errp, "No /dev/urandom or /dev/random found");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
|
||||||
|
CRYPT_SILENT | CRYPT_VERIFYCONTEXT)) {
|
||||||
|
error_setg_win32(errp, GetLastError(),
|
||||||
|
"Unable to create cryptographic provider");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
|
||||||
|
size_t buflen G_GNUC_UNUSED,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
int ret = -1;
|
||||||
|
int got;
|
||||||
|
|
||||||
while (buflen > 0) {
|
while (buflen > 0) {
|
||||||
got = read(fd, buf, buflen);
|
got = read(fd, buf, buflen);
|
||||||
@@ -59,6 +80,14 @@ int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
cleanup:
|
cleanup:
|
||||||
close(fd);
|
|
||||||
return ret;
|
return ret;
|
||||||
|
#else
|
||||||
|
if (!CryptGenRandom(hCryptProv, buflen, buf)) {
|
||||||
|
error_setg_win32(errp, GetLastError(),
|
||||||
|
"Unable to read random bytes");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ CONFIG_PCI=y
|
|||||||
CONFIG_VIRTIO_PCI=y
|
CONFIG_VIRTIO_PCI=y
|
||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
|
CONFIG_TERMINAL3270=y
|
||||||
CONFIG_S390_FLIC=y
|
CONFIG_S390_FLIC=y
|
||||||
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
|
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
|
||||||
CONFIG_WDT_DIAG288=y
|
CONFIG_WDT_DIAG288=y
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ static void read_fstree(void *fdt, const char *dirname)
|
|||||||
d = opendir(dirname);
|
d = opendir(dirname);
|
||||||
if (!d) {
|
if (!d) {
|
||||||
error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
|
error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((de = readdir(d)) != NULL) {
|
while ((de = readdir(d)) != NULL) {
|
||||||
|
|||||||
@@ -182,15 +182,13 @@ The appropriate DEVNAME depends on the machine type. For type "pc":
|
|||||||
|
|
||||||
This lets you control I/O ports and IRQs.
|
This lets you control I/O ports and IRQs.
|
||||||
|
|
||||||
* -usbdevice serial:vendorid=VID,productid=PRID becomes
|
* -usbdevice serial::chardev becomes -device usb-serial,chardev=dev.
|
||||||
-device usb-serial,vendorid=VID,productid=PRID
|
|
||||||
|
|
||||||
* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always
|
* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always
|
||||||
uses "braille". With -device, this useful default is gone, so you
|
uses "braille". With -device, this useful default is gone, so you
|
||||||
have to use something like
|
have to use something like
|
||||||
|
|
||||||
-device usb-braille,chardev=braille,vendorid=VID,productid=PRID
|
-device usb-braille,chardev=braille -chardev braille,id=braille
|
||||||
-chardev braille,id=braille
|
|
||||||
|
|
||||||
* -virtioconsole becomes
|
* -virtioconsole becomes
|
||||||
-device virtio-serial-pci,class=C,vectors=V,ioeventfd=IOEVENTFD,max_ports=N
|
-device virtio-serial-pci,class=C,vectors=V,ioeventfd=IOEVENTFD,max_ports=N
|
||||||
|
|||||||
7
dump.c
7
dump.c
@@ -77,7 +77,13 @@ static int dump_cleanup(DumpState *s)
|
|||||||
memory_mapping_list_free(&s->list);
|
memory_mapping_list_free(&s->list);
|
||||||
close(s->fd);
|
close(s->fd);
|
||||||
if (s->resume) {
|
if (s->resume) {
|
||||||
|
if (s->detached) {
|
||||||
|
qemu_mutex_lock_iothread();
|
||||||
|
}
|
||||||
vm_start();
|
vm_start();
|
||||||
|
if (s->detached) {
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1804,6 +1810,7 @@ void qmp_dump_guest_memory(bool paging, const char *file,
|
|||||||
|
|
||||||
if (detach_p) {
|
if (detach_p) {
|
||||||
/* detached dump */
|
/* detached dump */
|
||||||
|
s->detached = true;
|
||||||
qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
|
qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
|
||||||
s, QEMU_THREAD_DETACHED);
|
s, QEMU_THREAD_DETACHED);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
167
exec.c
167
exec.c
@@ -71,6 +71,8 @@
|
|||||||
#include "qemu/mmap-alloc.h"
|
#include "qemu/mmap-alloc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "monitor/monitor.h"
|
||||||
|
|
||||||
//#define DEBUG_SUBPAGE
|
//#define DEBUG_SUBPAGE
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
@@ -463,42 +465,12 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Called from RCU critical section */
|
/* Called from RCU critical section */
|
||||||
IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
|
static MemoryRegionSection address_space_do_translate(AddressSpace *as,
|
||||||
bool is_write)
|
hwaddr addr,
|
||||||
{
|
hwaddr *xlat,
|
||||||
IOMMUTLBEntry iotlb = {0};
|
hwaddr *plen,
|
||||||
MemoryRegionSection *section;
|
bool is_write,
|
||||||
MemoryRegion *mr;
|
bool is_mmio)
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
|
|
||||||
section = address_space_lookup_region(d, addr, false);
|
|
||||||
addr = addr - section->offset_within_address_space
|
|
||||||
+ section->offset_within_region;
|
|
||||||
mr = section->mr;
|
|
||||||
|
|
||||||
if (!mr->iommu_ops) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
iotlb = mr->iommu_ops->translate(mr, addr, is_write);
|
|
||||||
if (!(iotlb.perm & (1 << is_write))) {
|
|
||||||
iotlb.target_as = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
|
|
||||||
| (addr & iotlb.addr_mask));
|
|
||||||
as = iotlb.target_as;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iotlb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called from RCU critical section */
|
|
||||||
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
|
||||||
hwaddr *xlat, hwaddr *plen,
|
|
||||||
bool is_write)
|
|
||||||
{
|
{
|
||||||
IOMMUTLBEntry iotlb;
|
IOMMUTLBEntry iotlb;
|
||||||
MemoryRegionSection *section;
|
MemoryRegionSection *section;
|
||||||
@@ -506,7 +478,7 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
|
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
|
||||||
section = address_space_translate_internal(d, addr, &addr, plen, true);
|
section = address_space_translate_internal(d, addr, &addr, plen, is_mmio);
|
||||||
mr = section->mr;
|
mr = section->mr;
|
||||||
|
|
||||||
if (!mr->iommu_ops) {
|
if (!mr->iommu_ops) {
|
||||||
@@ -518,19 +490,84 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
|||||||
| (addr & iotlb.addr_mask));
|
| (addr & iotlb.addr_mask));
|
||||||
*plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1);
|
*plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1);
|
||||||
if (!(iotlb.perm & (1 << is_write))) {
|
if (!(iotlb.perm & (1 << is_write))) {
|
||||||
mr = &io_mem_unassigned;
|
goto translate_fail;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
as = iotlb.target_as;
|
as = iotlb.target_as;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*xlat = addr;
|
||||||
|
|
||||||
|
return *section;
|
||||||
|
|
||||||
|
translate_fail:
|
||||||
|
return (MemoryRegionSection) { .mr = &io_mem_unassigned };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from RCU critical section */
|
||||||
|
IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
MemoryRegionSection section;
|
||||||
|
hwaddr xlat, plen;
|
||||||
|
|
||||||
|
/* Try to get maximum page mask during translation. */
|
||||||
|
plen = (hwaddr)-1;
|
||||||
|
|
||||||
|
/* This can never be MMIO. */
|
||||||
|
section = address_space_do_translate(as, addr, &xlat, &plen,
|
||||||
|
is_write, false);
|
||||||
|
|
||||||
|
/* Illegal translation */
|
||||||
|
if (section.mr == &io_mem_unassigned) {
|
||||||
|
goto iotlb_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert memory region offset into address space offset */
|
||||||
|
xlat += section.offset_within_address_space -
|
||||||
|
section.offset_within_region;
|
||||||
|
|
||||||
|
if (plen == (hwaddr)-1) {
|
||||||
|
/*
|
||||||
|
* We use default page size here. Logically it only happens
|
||||||
|
* for identity mappings.
|
||||||
|
*/
|
||||||
|
plen = TARGET_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert to address mask */
|
||||||
|
plen -= 1;
|
||||||
|
|
||||||
|
return (IOMMUTLBEntry) {
|
||||||
|
.target_as = section.address_space,
|
||||||
|
.iova = addr & ~plen,
|
||||||
|
.translated_addr = xlat & ~plen,
|
||||||
|
.addr_mask = plen,
|
||||||
|
/* IOTLBs are for DMAs, and DMA only allows on RAMs. */
|
||||||
|
.perm = IOMMU_RW,
|
||||||
|
};
|
||||||
|
|
||||||
|
iotlb_fail:
|
||||||
|
return (IOMMUTLBEntry) {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from RCU critical section */
|
||||||
|
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
||||||
|
hwaddr *xlat, hwaddr *plen,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
MemoryRegion *mr;
|
||||||
|
MemoryRegionSection section;
|
||||||
|
|
||||||
|
/* This can be MMIO, so setup MMIO bit. */
|
||||||
|
section = address_space_do_translate(as, addr, xlat, plen, is_write, true);
|
||||||
|
mr = section.mr;
|
||||||
|
|
||||||
if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
|
if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
|
||||||
hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
|
hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
|
||||||
*plen = MIN(page, *plen);
|
*plen = MIN(page, *plen);
|
||||||
}
|
}
|
||||||
|
|
||||||
*xlat = addr;
|
|
||||||
return mr;
|
return mr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,7 +1015,7 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
|
|||||||
if (block && addr - block->offset < block->max_length) {
|
if (block && addr - block->offset < block->max_length) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
if (addr - block->offset < block->max_length) {
|
if (addr - block->offset < block->max_length) {
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
@@ -1333,6 +1370,26 @@ void qemu_mutex_unlock_ramlist(void)
|
|||||||
qemu_mutex_unlock(&ram_list.mutex);
|
qemu_mutex_unlock(&ram_list.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ram_block_dump(Monitor *mon)
|
||||||
|
{
|
||||||
|
RAMBlock *block;
|
||||||
|
char *psize;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
monitor_printf(mon, "%24s %8s %18s %18s %18s\n",
|
||||||
|
"Block Name", "PSize", "Offset", "Used", "Total");
|
||||||
|
RAMBLOCK_FOREACH(block) {
|
||||||
|
psize = size_to_str(block->page_size);
|
||||||
|
monitor_printf(mon, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64
|
||||||
|
" 0x%016" PRIx64 "\n", block->idstr, psize,
|
||||||
|
(uint64_t)block->offset,
|
||||||
|
(uint64_t)block->used_length,
|
||||||
|
(uint64_t)block->max_length);
|
||||||
|
g_free(psize);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
/*
|
/*
|
||||||
* FIXME TOCTTOU: this iterates over memory backends' mem-path, which
|
* FIXME TOCTTOU: this iterates over memory backends' mem-path, which
|
||||||
@@ -1578,12 +1635,12 @@ static ram_addr_t find_ram_offset(ram_addr_t size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
ram_addr_t end, next = RAM_ADDR_MAX;
|
ram_addr_t end, next = RAM_ADDR_MAX;
|
||||||
|
|
||||||
end = block->offset + block->max_length;
|
end = block->offset + block->max_length;
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(next_block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(next_block) {
|
||||||
if (next_block->offset >= end) {
|
if (next_block->offset >= end) {
|
||||||
next = MIN(next, next_block->offset);
|
next = MIN(next, next_block->offset);
|
||||||
}
|
}
|
||||||
@@ -1609,7 +1666,7 @@ unsigned long last_ram_page(void)
|
|||||||
ram_addr_t last = 0;
|
ram_addr_t last = 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
last = MAX(last, block->offset + block->max_length);
|
last = MAX(last, block->offset + block->max_length);
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
@@ -1659,7 +1716,7 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
|
|||||||
pstrcat(new_block->idstr, sizeof(new_block->idstr), name);
|
pstrcat(new_block->idstr, sizeof(new_block->idstr), name);
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
if (block != new_block &&
|
if (block != new_block &&
|
||||||
!strcmp(block->idstr, new_block->idstr)) {
|
!strcmp(block->idstr, new_block->idstr)) {
|
||||||
fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n",
|
fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n",
|
||||||
@@ -1693,7 +1750,7 @@ size_t qemu_ram_pagesize_largest(void)
|
|||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
size_t largest = 0;
|
size_t largest = 0;
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
largest = MAX(largest, qemu_ram_pagesize(block));
|
largest = MAX(largest, qemu_ram_pagesize(block));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1839,7 +1896,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
* QLIST (which has an RCU-friendly variant) does not have insertion at
|
* QLIST (which has an RCU-friendly variant) does not have insertion at
|
||||||
* tail, so save the last element in last_block.
|
* tail, so save the last element in last_block.
|
||||||
*/
|
*/
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
last_block = block;
|
last_block = block;
|
||||||
if (block->max_length < new_block->max_length) {
|
if (block->max_length < new_block->max_length) {
|
||||||
break;
|
break;
|
||||||
@@ -2021,7 +2078,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
|
|||||||
int flags;
|
int flags;
|
||||||
void *area, *vaddr;
|
void *area, *vaddr;
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
offset = addr - block->offset;
|
offset = addr - block->offset;
|
||||||
if (offset < block->max_length) {
|
if (offset < block->max_length) {
|
||||||
vaddr = ramblock_ptr(block, offset);
|
vaddr = ramblock_ptr(block, offset);
|
||||||
@@ -2084,10 +2141,10 @@ void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
|
|||||||
* In that case just map until the end of the page.
|
* In that case just map until the end of the page.
|
||||||
*/
|
*/
|
||||||
if (block->offset == 0) {
|
if (block->offset == 0) {
|
||||||
return xen_map_cache(addr, 0, 0);
|
return xen_map_cache(addr, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
block->host = xen_map_cache(block->offset, block->max_length, 1, false);
|
||||||
}
|
}
|
||||||
return ramblock_ptr(block, addr);
|
return ramblock_ptr(block, addr);
|
||||||
}
|
}
|
||||||
@@ -2117,10 +2174,10 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
|||||||
* In that case just map the requested area.
|
* In that case just map the requested area.
|
||||||
*/
|
*/
|
||||||
if (block->offset == 0) {
|
if (block->offset == 0) {
|
||||||
return xen_map_cache(addr, *size, 1);
|
return xen_map_cache(addr, *size, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
block->host = xen_map_cache(block->offset, block->max_length, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ramblock_ptr(block, addr);
|
return ramblock_ptr(block, addr);
|
||||||
@@ -2167,7 +2224,7 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
|||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
/* This case append when the block is not mapped. */
|
/* This case append when the block is not mapped. */
|
||||||
if (block->host == NULL) {
|
if (block->host == NULL) {
|
||||||
continue;
|
continue;
|
||||||
@@ -2200,7 +2257,7 @@ RAMBlock *qemu_ram_block_by_name(const char *name)
|
|||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
|
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
if (!strcmp(name, block->idstr)) {
|
if (!strcmp(name, block->idstr)) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@@ -3424,7 +3481,7 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
RAMBLOCK_FOREACH(block) {
|
||||||
ret = func(block->idstr, block->host, block->offset,
|
ret = func(block->idstr, block->host, block->offset,
|
||||||
block->used_length, opaque);
|
block->used_length, opaque);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|||||||
65
gdb-xml/i386-32bit-core.xml
Normal file
65
gdb-xml/i386-32bit-core.xml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- Copyright (C) 2010-2015 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. -->
|
||||||
|
|
||||||
|
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||||
|
<feature name="org.gnu.gdb.i386.core">
|
||||||
|
<flags id="i386_eflags" size="4">
|
||||||
|
<field name="CF" start="0" end="0"/>
|
||||||
|
<field name="" start="1" end="1"/>
|
||||||
|
<field name="PF" start="2" end="2"/>
|
||||||
|
<field name="AF" start="4" end="4"/>
|
||||||
|
<field name="ZF" start="6" end="6"/>
|
||||||
|
<field name="SF" start="7" end="7"/>
|
||||||
|
<field name="TF" start="8" end="8"/>
|
||||||
|
<field name="IF" start="9" end="9"/>
|
||||||
|
<field name="DF" start="10" end="10"/>
|
||||||
|
<field name="OF" start="11" end="11"/>
|
||||||
|
<field name="NT" start="14" end="14"/>
|
||||||
|
<field name="RF" start="16" end="16"/>
|
||||||
|
<field name="VM" start="17" end="17"/>
|
||||||
|
<field name="AC" start="18" end="18"/>
|
||||||
|
<field name="VIF" start="19" end="19"/>
|
||||||
|
<field name="VIP" start="20" end="20"/>
|
||||||
|
<field name="ID" start="21" end="21"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<reg name="eax" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ecx" bitsize="32" type="int32"/>
|
||||||
|
<reg name="edx" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ebx" bitsize="32" type="int32"/>
|
||||||
|
<reg name="esp" bitsize="32" type="data_ptr"/>
|
||||||
|
<reg name="ebp" bitsize="32" type="data_ptr"/>
|
||||||
|
<reg name="esi" bitsize="32" type="int32"/>
|
||||||
|
<reg name="edi" bitsize="32" type="int32"/>
|
||||||
|
|
||||||
|
<reg name="eip" bitsize="32" type="code_ptr"/>
|
||||||
|
<reg name="eflags" bitsize="32" type="i386_eflags"/>
|
||||||
|
<reg name="cs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ss" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ds" bitsize="32" type="int32"/>
|
||||||
|
<reg name="es" bitsize="32" type="int32"/>
|
||||||
|
<reg name="fs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="gs" bitsize="32" type="int32"/>
|
||||||
|
|
||||||
|
<reg name="st0" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st1" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st2" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st3" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st4" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st5" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st6" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st7" bitsize="80" type="i387_ext"/>
|
||||||
|
|
||||||
|
<reg name="fctrl" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fstat" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="ftag" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fiseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fioff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="foseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fooff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fop" bitsize="32" type="int" group="float"/>
|
||||||
|
</feature>
|
||||||
73
gdb-xml/i386-64bit-core.xml
Normal file
73
gdb-xml/i386-64bit-core.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- Copyright (C) 2010-2015 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. -->
|
||||||
|
|
||||||
|
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||||
|
<feature name="org.gnu.gdb.i386.core">
|
||||||
|
<flags id="i386_eflags" size="4">
|
||||||
|
<field name="CF" start="0" end="0"/>
|
||||||
|
<field name="" start="1" end="1"/>
|
||||||
|
<field name="PF" start="2" end="2"/>
|
||||||
|
<field name="AF" start="4" end="4"/>
|
||||||
|
<field name="ZF" start="6" end="6"/>
|
||||||
|
<field name="SF" start="7" end="7"/>
|
||||||
|
<field name="TF" start="8" end="8"/>
|
||||||
|
<field name="IF" start="9" end="9"/>
|
||||||
|
<field name="DF" start="10" end="10"/>
|
||||||
|
<field name="OF" start="11" end="11"/>
|
||||||
|
<field name="NT" start="14" end="14"/>
|
||||||
|
<field name="RF" start="16" end="16"/>
|
||||||
|
<field name="VM" start="17" end="17"/>
|
||||||
|
<field name="AC" start="18" end="18"/>
|
||||||
|
<field name="VIF" start="19" end="19"/>
|
||||||
|
<field name="VIP" start="20" end="20"/>
|
||||||
|
<field name="ID" start="21" end="21"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<reg name="rax" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rbx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rcx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rdx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rsi" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rdi" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rbp" bitsize="64" type="data_ptr"/>
|
||||||
|
<reg name="rsp" bitsize="64" type="data_ptr"/>
|
||||||
|
<reg name="r8" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r9" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r10" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r11" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r12" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r13" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r14" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r15" bitsize="64" type="int64"/>
|
||||||
|
|
||||||
|
<reg name="rip" bitsize="64" type="code_ptr"/>
|
||||||
|
<reg name="eflags" bitsize="32" type="i386_eflags"/>
|
||||||
|
<reg name="cs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ss" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ds" bitsize="32" type="int32"/>
|
||||||
|
<reg name="es" bitsize="32" type="int32"/>
|
||||||
|
<reg name="fs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="gs" bitsize="32" type="int32"/>
|
||||||
|
|
||||||
|
<reg name="st0" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st1" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st2" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st3" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st4" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st5" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st6" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st7" bitsize="80" type="i387_ext"/>
|
||||||
|
|
||||||
|
<reg name="fctrl" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fstat" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="ftag" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fiseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fioff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="foseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fooff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fop" bitsize="32" type="int" group="float"/>
|
||||||
|
</feature>
|
||||||
114
gdbstub.c
114
gdbstub.c
@@ -286,6 +286,8 @@ enum RSState {
|
|||||||
RS_INACTIVE,
|
RS_INACTIVE,
|
||||||
RS_IDLE,
|
RS_IDLE,
|
||||||
RS_GETLINE,
|
RS_GETLINE,
|
||||||
|
RS_GETLINE_ESC,
|
||||||
|
RS_GETLINE_RLE,
|
||||||
RS_CHKSUM1,
|
RS_CHKSUM1,
|
||||||
RS_CHKSUM2,
|
RS_CHKSUM2,
|
||||||
};
|
};
|
||||||
@@ -296,7 +298,8 @@ typedef struct GDBState {
|
|||||||
enum RSState state; /* parsing state */
|
enum RSState state; /* parsing state */
|
||||||
char line_buf[MAX_PACKET_LENGTH];
|
char line_buf[MAX_PACKET_LENGTH];
|
||||||
int line_buf_index;
|
int line_buf_index;
|
||||||
int line_csum;
|
int line_sum; /* running checksum */
|
||||||
|
int line_csum; /* checksum at the end of the packet */
|
||||||
uint8_t last_packet[MAX_PACKET_LENGTH + 4];
|
uint8_t last_packet[MAX_PACKET_LENGTH + 4];
|
||||||
int last_packet_len;
|
int last_packet_len;
|
||||||
int signal;
|
int signal;
|
||||||
@@ -1508,7 +1511,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
|
|||||||
|
|
||||||
static void gdb_read_byte(GDBState *s, int ch)
|
static void gdb_read_byte(GDBState *s, int ch)
|
||||||
{
|
{
|
||||||
int i, csum;
|
|
||||||
uint8_t reply;
|
uint8_t reply;
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
@@ -1542,35 +1544,123 @@ static void gdb_read_byte(GDBState *s, int ch)
|
|||||||
switch(s->state) {
|
switch(s->state) {
|
||||||
case RS_IDLE:
|
case RS_IDLE:
|
||||||
if (ch == '$') {
|
if (ch == '$') {
|
||||||
|
/* start of command packet */
|
||||||
s->line_buf_index = 0;
|
s->line_buf_index = 0;
|
||||||
|
s->line_sum = 0;
|
||||||
s->state = RS_GETLINE;
|
s->state = RS_GETLINE;
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub received garbage between packets: 0x%x\n", ch);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RS_GETLINE:
|
case RS_GETLINE:
|
||||||
if (ch == '#') {
|
if (ch == '}') {
|
||||||
s->state = RS_CHKSUM1;
|
/* start escape sequence */
|
||||||
|
s->state = RS_GETLINE_ESC;
|
||||||
|
s->line_sum += ch;
|
||||||
|
} else if (ch == '*') {
|
||||||
|
/* start run length encoding sequence */
|
||||||
|
s->state = RS_GETLINE_RLE;
|
||||||
|
s->line_sum += ch;
|
||||||
|
} else if (ch == '#') {
|
||||||
|
/* end of command, start of checksum*/
|
||||||
|
s->state = RS_CHKSUM1;
|
||||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub command buffer overrun, dropping command\n");
|
||||||
|
#endif
|
||||||
s->state = RS_IDLE;
|
s->state = RS_IDLE;
|
||||||
} else {
|
} else {
|
||||||
s->line_buf[s->line_buf_index++] = ch;
|
/* unescaped command character */
|
||||||
|
s->line_buf[s->line_buf_index++] = ch;
|
||||||
|
s->line_sum += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RS_GETLINE_ESC:
|
||||||
|
if (ch == '#') {
|
||||||
|
/* unexpected end of command in escape sequence */
|
||||||
|
s->state = RS_CHKSUM1;
|
||||||
|
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||||
|
/* command buffer overrun */
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub command buffer overrun, dropping command\n");
|
||||||
|
#endif
|
||||||
|
s->state = RS_IDLE;
|
||||||
|
} else {
|
||||||
|
/* parse escaped character and leave escape state */
|
||||||
|
s->line_buf[s->line_buf_index++] = ch ^ 0x20;
|
||||||
|
s->line_sum += ch;
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RS_GETLINE_RLE:
|
||||||
|
if (ch < ' ') {
|
||||||
|
/* invalid RLE count encoding */
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub got invalid RLE count: 0x%x\n", ch);
|
||||||
|
#endif
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
} else {
|
||||||
|
/* decode repeat length */
|
||||||
|
int repeat = (unsigned char)ch - ' ' + 3;
|
||||||
|
if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) {
|
||||||
|
/* that many repeats would overrun the command buffer */
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub command buffer overrun,"
|
||||||
|
" dropping command\n");
|
||||||
|
#endif
|
||||||
|
s->state = RS_IDLE;
|
||||||
|
} else if (s->line_buf_index < 1) {
|
||||||
|
/* got a repeat but we have nothing to repeat */
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub got invalid RLE sequence\n");
|
||||||
|
#endif
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
} else {
|
||||||
|
/* repeat the last character */
|
||||||
|
memset(s->line_buf + s->line_buf_index,
|
||||||
|
s->line_buf[s->line_buf_index - 1], repeat);
|
||||||
|
s->line_buf_index += repeat;
|
||||||
|
s->line_sum += ch;
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RS_CHKSUM1:
|
case RS_CHKSUM1:
|
||||||
|
/* get high hex digit of checksum */
|
||||||
|
if (!isxdigit(ch)) {
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub got invalid command checksum digit\n");
|
||||||
|
#endif
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
s->line_buf[s->line_buf_index] = '\0';
|
s->line_buf[s->line_buf_index] = '\0';
|
||||||
s->line_csum = fromhex(ch) << 4;
|
s->line_csum = fromhex(ch) << 4;
|
||||||
s->state = RS_CHKSUM2;
|
s->state = RS_CHKSUM2;
|
||||||
break;
|
break;
|
||||||
case RS_CHKSUM2:
|
case RS_CHKSUM2:
|
||||||
s->line_csum |= fromhex(ch);
|
/* get low hex digit of checksum */
|
||||||
csum = 0;
|
if (!isxdigit(ch)) {
|
||||||
for(i = 0; i < s->line_buf_index; i++) {
|
#ifdef DEBUG_GDB
|
||||||
csum += s->line_buf[i];
|
printf("gdbstub got invalid command checksum digit\n");
|
||||||
|
#endif
|
||||||
|
s->state = RS_GETLINE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (s->line_csum != (csum & 0xff)) {
|
s->line_csum |= fromhex(ch);
|
||||||
|
|
||||||
|
if (s->line_csum != (s->line_sum & 0xff)) {
|
||||||
|
/* send NAK reply */
|
||||||
reply = '-';
|
reply = '-';
|
||||||
put_buffer(s, &reply, 1);
|
put_buffer(s, &reply, 1);
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub got command packet with incorrect checksum\n");
|
||||||
|
#endif
|
||||||
s->state = RS_IDLE;
|
s->state = RS_IDLE;
|
||||||
} else {
|
} else {
|
||||||
|
/* send ACK reply */
|
||||||
reply = '+';
|
reply = '+';
|
||||||
put_buffer(s, &reply, 1);
|
put_buffer(s, &reply, 1);
|
||||||
s->state = gdb_handle_packet(s, s->line_buf);
|
s->state = gdb_handle_packet(s, s->line_buf);
|
||||||
@@ -1611,7 +1701,7 @@ void gdb_exit(CPUArchState *env, int code)
|
|||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
qemu_chr_fe_deinit(&s->chr);
|
qemu_chr_fe_deinit(&s->chr);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1912,7 +2002,7 @@ int gdbserver_start(const char *device)
|
|||||||
monitor_init(mon_chr, 0);
|
monitor_init(mon_chr, 0);
|
||||||
} else {
|
} else {
|
||||||
if (qemu_chr_fe_get_driver(&s->chr)) {
|
if (qemu_chr_fe_get_driver(&s->chr)) {
|
||||||
qemu_chr_delete(qemu_chr_fe_get_driver(&s->chr));
|
object_unparent(OBJECT(qemu_chr_fe_get_driver(&s->chr)));
|
||||||
}
|
}
|
||||||
mon_chr = s->mon_chr;
|
mon_chr = s->mon_chr;
|
||||||
memset(s, 0, sizeof(GDBState));
|
memset(s, 0, sizeof(GDBState));
|
||||||
|
|||||||
@@ -785,6 +785,20 @@ STEXI
|
|||||||
@item info dump
|
@item info dump
|
||||||
@findex dump
|
@findex dump
|
||||||
Display the latest dump status.
|
Display the latest dump status.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "ramblock",
|
||||||
|
.args_type = "",
|
||||||
|
.params = "",
|
||||||
|
.help = "Display system ramblock information",
|
||||||
|
.cmd = hmp_info_ramblock,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item info ramblock
|
||||||
|
@findex ramblock
|
||||||
|
Dump all the ramblocks of the system.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
188
hmp.c
188
hmp.c
@@ -19,6 +19,7 @@
|
|||||||
#include "net/eth.h"
|
#include "net/eth.h"
|
||||||
#include "sysemu/char.h"
|
#include "sysemu/char.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
@@ -33,10 +34,12 @@
|
|||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
|
#include "block/nbd.h"
|
||||||
#include "block/qapi.h"
|
#include "block/qapi.h"
|
||||||
#include "qemu-io.h"
|
#include "qemu-io.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "exec/ramlist.h"
|
||||||
#include "hw/intc/intc.h"
|
#include "hw/intc/intc.h"
|
||||||
|
|
||||||
#ifdef CONFIG_SPICE
|
#ifdef CONFIG_SPICE
|
||||||
@@ -1268,6 +1271,184 @@ void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict)
|
|||||||
hmp_handle_error(mon, &err);
|
hmp_handle_error(mon, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_loadvm(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
int saved_vm_running = runstate_is_running();
|
||||||
|
const char *name = qdict_get_str(qdict, "name");
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
vm_stop(RUN_STATE_RESTORE_VM);
|
||||||
|
|
||||||
|
if (load_vmstate(name, &err) == 0 && saved_vm_running) {
|
||||||
|
vm_start();
|
||||||
|
}
|
||||||
|
hmp_handle_error(mon, &err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmp_savevm(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
save_vmstate(qdict_get_try_str(qdict, "name"), &err);
|
||||||
|
hmp_handle_error(mon, &err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmp_delvm(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs;
|
||||||
|
Error *err;
|
||||||
|
const char *name = qdict_get_str(qdict, "name");
|
||||||
|
|
||||||
|
if (bdrv_all_delete_snapshot(name, &bs, &err) < 0) {
|
||||||
|
error_reportf_err(err,
|
||||||
|
"Error while deleting snapshot on device '%s': ",
|
||||||
|
bdrv_get_device_name(bs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs, *bs1;
|
||||||
|
BdrvNextIterator it1;
|
||||||
|
QEMUSnapshotInfo *sn_tab, *sn;
|
||||||
|
bool no_snapshot = true;
|
||||||
|
int nb_sns, i;
|
||||||
|
int total;
|
||||||
|
int *global_snapshots;
|
||||||
|
AioContext *aio_context;
|
||||||
|
|
||||||
|
typedef struct SnapshotEntry {
|
||||||
|
QEMUSnapshotInfo sn;
|
||||||
|
QTAILQ_ENTRY(SnapshotEntry) next;
|
||||||
|
} SnapshotEntry;
|
||||||
|
|
||||||
|
typedef struct ImageEntry {
|
||||||
|
const char *imagename;
|
||||||
|
QTAILQ_ENTRY(ImageEntry) next;
|
||||||
|
QTAILQ_HEAD(, SnapshotEntry) snapshots;
|
||||||
|
} ImageEntry;
|
||||||
|
|
||||||
|
QTAILQ_HEAD(, ImageEntry) image_list =
|
||||||
|
QTAILQ_HEAD_INITIALIZER(image_list);
|
||||||
|
|
||||||
|
ImageEntry *image_entry, *next_ie;
|
||||||
|
SnapshotEntry *snapshot_entry;
|
||||||
|
|
||||||
|
bs = bdrv_all_find_vmstate_bs();
|
||||||
|
if (!bs) {
|
||||||
|
monitor_printf(mon, "No available block device supports snapshots\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aio_context = bdrv_get_aio_context(bs);
|
||||||
|
|
||||||
|
aio_context_acquire(aio_context);
|
||||||
|
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||||
|
aio_context_release(aio_context);
|
||||||
|
|
||||||
|
if (nb_sns < 0) {
|
||||||
|
monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) {
|
||||||
|
int bs1_nb_sns = 0;
|
||||||
|
ImageEntry *ie;
|
||||||
|
SnapshotEntry *se;
|
||||||
|
AioContext *ctx = bdrv_get_aio_context(bs1);
|
||||||
|
|
||||||
|
aio_context_acquire(ctx);
|
||||||
|
if (bdrv_can_snapshot(bs1)) {
|
||||||
|
sn = NULL;
|
||||||
|
bs1_nb_sns = bdrv_snapshot_list(bs1, &sn);
|
||||||
|
if (bs1_nb_sns > 0) {
|
||||||
|
no_snapshot = false;
|
||||||
|
ie = g_new0(ImageEntry, 1);
|
||||||
|
ie->imagename = bdrv_get_device_name(bs1);
|
||||||
|
QTAILQ_INIT(&ie->snapshots);
|
||||||
|
QTAILQ_INSERT_TAIL(&image_list, ie, next);
|
||||||
|
for (i = 0; i < bs1_nb_sns; i++) {
|
||||||
|
se = g_new0(SnapshotEntry, 1);
|
||||||
|
se->sn = sn[i];
|
||||||
|
QTAILQ_INSERT_TAIL(&ie->snapshots, se, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(sn);
|
||||||
|
}
|
||||||
|
aio_context_release(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (no_snapshot) {
|
||||||
|
monitor_printf(mon, "There is no snapshot available.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_snapshots = g_new0(int, nb_sns);
|
||||||
|
total = 0;
|
||||||
|
for (i = 0; i < nb_sns; i++) {
|
||||||
|
SnapshotEntry *next_sn;
|
||||||
|
if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) {
|
||||||
|
global_snapshots[total] = i;
|
||||||
|
total++;
|
||||||
|
QTAILQ_FOREACH(image_entry, &image_list, next) {
|
||||||
|
QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots,
|
||||||
|
next, next_sn) {
|
||||||
|
if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) {
|
||||||
|
QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry,
|
||||||
|
next);
|
||||||
|
g_free(snapshot_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_printf(mon, "List of snapshots present on all disks:\n");
|
||||||
|
|
||||||
|
if (total > 0) {
|
||||||
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
|
for (i = 0; i < total; i++) {
|
||||||
|
sn = &sn_tab[global_snapshots[i]];
|
||||||
|
/* The ID is not guaranteed to be the same on all images, so
|
||||||
|
* overwrite it.
|
||||||
|
*/
|
||||||
|
pstrcpy(sn->id_str, sizeof(sn->id_str), "--");
|
||||||
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
monitor_printf(mon, "None\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(image_entry, &image_list, next) {
|
||||||
|
if (QTAILQ_EMPTY(&image_entry->snapshots)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
monitor_printf(mon,
|
||||||
|
"\nList of partial (non-loadable) snapshots on '%s':\n",
|
||||||
|
image_entry->imagename);
|
||||||
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
|
QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) {
|
||||||
|
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon,
|
||||||
|
&snapshot_entry->sn);
|
||||||
|
monitor_printf(mon, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) {
|
||||||
|
SnapshotEntry *next_sn;
|
||||||
|
QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next,
|
||||||
|
next_sn) {
|
||||||
|
g_free(snapshot_entry);
|
||||||
|
}
|
||||||
|
g_free(image_entry);
|
||||||
|
}
|
||||||
|
g_free(sn_tab);
|
||||||
|
g_free(global_snapshots);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
|
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
qmp_migrate_cancel(NULL);
|
qmp_migrate_cancel(NULL);
|
||||||
@@ -1947,7 +2128,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
qmp_nbd_server_start(addr, false, NULL, &local_err);
|
nbd_server_start(addr, NULL, &local_err);
|
||||||
qapi_free_SocketAddress(addr);
|
qapi_free_SocketAddress(addr);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -2563,6 +2744,11 @@ void hmp_info_dump(Monitor *mon, const QDict *qdict)
|
|||||||
qapi_free_DumpQueryResult(result);
|
qapi_free_DumpQueryResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_info_ramblock(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
ram_block_dump(mon);
|
||||||
|
}
|
||||||
|
|
||||||
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
|
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|||||||
5
hmp.h
5
hmp.h
@@ -63,6 +63,10 @@ void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict);
|
void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
|
void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_drive_backup(Monitor *mon, const QDict *qdict);
|
void hmp_drive_backup(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_loadvm(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_savevm(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_delvm(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
|
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_migrate_incoming(Monitor *mon, const QDict *qdict);
|
void hmp_migrate_incoming(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
|
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
|
||||||
@@ -136,6 +140,7 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
|
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
|
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_info_dump(Monitor *mon, const QDict *qdict);
|
void hmp_info_dump(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_info_ramblock(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
|
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
|
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
|
|||||||
@@ -452,6 +452,11 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
|
|||||||
return telldir(fs->dir.stream);
|
return telldir(fs->dir.stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
|
||||||
|
{
|
||||||
|
return !strcmp(name, VIRTFS_META_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
|
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
|
||||||
{
|
{
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
@@ -465,8 +470,8 @@ again:
|
|||||||
if (ctx->export_flags & V9FS_SM_MAPPED) {
|
if (ctx->export_flags & V9FS_SM_MAPPED) {
|
||||||
entry->d_type = DT_UNKNOWN;
|
entry->d_type = DT_UNKNOWN;
|
||||||
} else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
|
} else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
|
||||||
if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
|
if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
|
||||||
/* skp the meta data directory */
|
/* skip the meta data directory */
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
entry->d_type = DT_UNKNOWN;
|
entry->d_type = DT_UNKNOWN;
|
||||||
@@ -559,6 +564,12 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
|
|||||||
int err = -1;
|
int err = -1;
|
||||||
int dirfd;
|
int dirfd;
|
||||||
|
|
||||||
|
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(fs_ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
||||||
if (dirfd == -1) {
|
if (dirfd == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -605,6 +616,12 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
|
|||||||
int err = -1;
|
int err = -1;
|
||||||
int dirfd;
|
int dirfd;
|
||||||
|
|
||||||
|
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(fs_ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
||||||
if (dirfd == -1) {
|
if (dirfd == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -694,6 +711,12 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
|
|||||||
int err = -1;
|
int err = -1;
|
||||||
int dirfd;
|
int dirfd;
|
||||||
|
|
||||||
|
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(fs_ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark all the open to not follow symlinks
|
* Mark all the open to not follow symlinks
|
||||||
*/
|
*/
|
||||||
@@ -752,6 +775,12 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
|
|||||||
int err = -1;
|
int err = -1;
|
||||||
int dirfd;
|
int dirfd;
|
||||||
|
|
||||||
|
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(fs_ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
|
||||||
if (dirfd == -1) {
|
if (dirfd == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -826,6 +855,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
|
|||||||
int ret = -1;
|
int ret = -1;
|
||||||
int odirfd, ndirfd;
|
int odirfd, ndirfd;
|
||||||
|
|
||||||
|
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
odirfd = local_opendir_nofollow(ctx, odirpath);
|
odirfd = local_opendir_nofollow(ctx, odirpath);
|
||||||
if (odirfd == -1) {
|
if (odirfd == -1) {
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1096,6 +1131,12 @@ static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
|
|||||||
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
|
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
|
||||||
const char *name, V9fsPath *target)
|
const char *name, V9fsPath *target)
|
||||||
{
|
{
|
||||||
|
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (dir_path) {
|
if (dir_path) {
|
||||||
v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
|
v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
|
||||||
} else if (strcmp(name, "/")) {
|
} else if (strcmp(name, "/")) {
|
||||||
@@ -1116,6 +1157,13 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
|
|||||||
int ret;
|
int ret;
|
||||||
int odirfd, ndirfd;
|
int odirfd, ndirfd;
|
||||||
|
|
||||||
|
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
(local_is_mapped_file_metadata(ctx, old_name) ||
|
||||||
|
local_is_mapped_file_metadata(ctx, new_name))) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
odirfd = local_opendir_nofollow(ctx, olddir->data);
|
odirfd = local_opendir_nofollow(ctx, olddir->data);
|
||||||
if (odirfd == -1) {
|
if (odirfd == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1206,6 +1254,12 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
|
|||||||
int ret;
|
int ret;
|
||||||
int dirfd;
|
int dirfd;
|
||||||
|
|
||||||
|
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
|
||||||
|
local_is_mapped_file_metadata(ctx, name)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
dirfd = local_opendir_nofollow(ctx, dir->data);
|
dirfd = local_opendir_nofollow(ctx, dir->data);
|
||||||
if (dirfd == -1) {
|
if (dirfd == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "9p-xattr.h"
|
#include "9p-xattr.h"
|
||||||
#include "coth.h"
|
#include "coth.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/blocker.h"
|
||||||
|
|
||||||
int open_fd_hw;
|
int open_fd_hw;
|
||||||
int total_open_fd;
|
int total_open_fd;
|
||||||
|
|||||||
@@ -332,12 +332,14 @@ static int xen_9pfs_connect(struct XenDevice *xendev)
|
|||||||
str = g_strdup_printf("ring-ref%u", i);
|
str = g_strdup_printf("ring-ref%u", i);
|
||||||
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
||||||
&xen_9pdev->rings[i].ref) == -1) {
|
&xen_9pdev->rings[i].ref) == -1) {
|
||||||
|
g_free(str);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
g_free(str);
|
g_free(str);
|
||||||
str = g_strdup_printf("event-channel-%u", i);
|
str = g_strdup_printf("event-channel-%u", i);
|
||||||
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
||||||
&xen_9pdev->rings[i].evtchn) == -1) {
|
&xen_9pdev->rings[i].evtchn) == -1) {
|
||||||
|
g_free(str);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
g_free(str);
|
g_free(str);
|
||||||
@@ -378,7 +380,7 @@ static int xen_9pfs_connect(struct XenDevice *xendev)
|
|||||||
if (xen_9pdev->rings[i].evtchndev == NULL) {
|
if (xen_9pdev->rings[i].evtchndev == NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
fcntl(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), F_SETFD, FD_CLOEXEC);
|
qemu_set_cloexec(xenevtchn_fd(xen_9pdev->rings[i].evtchndev));
|
||||||
xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain
|
xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain
|
||||||
(xen_9pdev->rings[i].evtchndev,
|
(xen_9pdev->rings[i].evtchndev,
|
||||||
xendev->dom,
|
xendev->dom,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "hw/acpi/aml-build.h"
|
#include "hw/acpi/aml-build.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
#include "sysemu/numa.h"
|
||||||
|
|
||||||
static GArray *build_alloc_array(void)
|
static GArray *build_alloc_array(void)
|
||||||
{
|
{
|
||||||
@@ -1599,6 +1600,33 @@ build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets,
|
|||||||
(void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id);
|
(void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Build xsdt table */
|
||||||
|
void
|
||||||
|
build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets,
|
||||||
|
const char *oem_id, const char *oem_table_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned xsdt_entries_offset;
|
||||||
|
AcpiXsdtDescriptorRev2 *xsdt;
|
||||||
|
const unsigned table_data_len = (sizeof(uint64_t) * table_offsets->len);
|
||||||
|
const unsigned xsdt_entry_size = sizeof(xsdt->table_offset_entry[0]);
|
||||||
|
const size_t xsdt_len = sizeof(*xsdt) + table_data_len;
|
||||||
|
|
||||||
|
xsdt = acpi_data_push(table_data, xsdt_len);
|
||||||
|
xsdt_entries_offset = (char *)xsdt->table_offset_entry - table_data->data;
|
||||||
|
for (i = 0; i < table_offsets->len; ++i) {
|
||||||
|
uint64_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i);
|
||||||
|
uint64_t xsdt_entry_offset = xsdt_entries_offset + xsdt_entry_size * i;
|
||||||
|
|
||||||
|
/* xsdt->table_offset_entry to be filled by Guest linker */
|
||||||
|
bios_linker_loader_add_pointer(linker,
|
||||||
|
ACPI_BUILD_TABLE_FILE, xsdt_entry_offset, xsdt_entry_size,
|
||||||
|
ACPI_BUILD_TABLE_FILE, ref_tbl_offset);
|
||||||
|
}
|
||||||
|
build_header(linker, table_data,
|
||||||
|
(void *)xsdt, "XSDT", xsdt_len, 1, oem_id, oem_table_id);
|
||||||
|
}
|
||||||
|
|
||||||
void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
||||||
uint64_t len, int node, MemoryAffinityFlags flags)
|
uint64_t len, int node, MemoryAffinityFlags flags)
|
||||||
{
|
{
|
||||||
@@ -1609,3 +1637,28 @@ void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
|||||||
numamem->base_addr = cpu_to_le64(base);
|
numamem->base_addr = cpu_to_le64(base);
|
||||||
numamem->range_length = cpu_to_le64(len);
|
numamem->range_length = cpu_to_le64(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ACPI spec 5.2.17 System Locality Distance Information Table
|
||||||
|
* (Revision 2.0 or later)
|
||||||
|
*/
|
||||||
|
void build_slit(GArray *table_data, BIOSLinker *linker)
|
||||||
|
{
|
||||||
|
int slit_start, i, j;
|
||||||
|
slit_start = table_data->len;
|
||||||
|
|
||||||
|
acpi_data_push(table_data, sizeof(AcpiTableHeader));
|
||||||
|
|
||||||
|
build_append_int_noprefix(table_data, nb_numa_nodes, 8);
|
||||||
|
for (i = 0; i < nb_numa_nodes; i++) {
|
||||||
|
for (j = 0; j < nb_numa_nodes; j++) {
|
||||||
|
assert(numa_info[i].distance[j]);
|
||||||
|
build_append_int_noprefix(table_data, numa_info[i].distance[j], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build_header(linker, table_data,
|
||||||
|
(void *)(table_data->data + slit_start),
|
||||||
|
"SLIT",
|
||||||
|
table_data->len - slit_start, 1, NULL, NULL);
|
||||||
|
}
|
||||||
|
|||||||
@@ -503,7 +503,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
|||||||
|
|
||||||
/* build Processor object for each processor */
|
/* build Processor object for each processor */
|
||||||
for (i = 0; i < arch_ids->len; i++) {
|
for (i = 0; i < arch_ids->len; i++) {
|
||||||
int j;
|
|
||||||
Aml *dev;
|
Aml *dev;
|
||||||
Aml *uid = aml_int(i);
|
Aml *uid = aml_int(i);
|
||||||
GArray *madt_buf = g_array_new(0, 1, 1);
|
GArray *madt_buf = g_array_new(0, 1, 1);
|
||||||
@@ -557,9 +556,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
|||||||
* as a result _PXM is required for all CPUs which might
|
* as a result _PXM is required for all CPUs which might
|
||||||
* be hot-plugged. For simplicity, add it for all CPUs.
|
* be hot-plugged. For simplicity, add it for all CPUs.
|
||||||
*/
|
*/
|
||||||
j = numa_get_node_for_cpu(i);
|
if (arch_ids->cpus[i].props.has_node_id) {
|
||||||
if (j < nb_numa_nodes) {
|
aml_append(dev, aml_name_decl("_PXM",
|
||||||
aml_append(dev, aml_name_decl("_PXM", aml_int(j)));
|
aml_int(arch_ids->cpus[i].props.node_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
aml_append(cpus_dev, dev);
|
aml_append(cpus_dev, dev);
|
||||||
|
|||||||
@@ -385,7 +385,10 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev,
|
|||||||
dev, errp);
|
dev, errp);
|
||||||
}
|
}
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp);
|
if (!xen_enabled()) {
|
||||||
|
acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev,
|
||||||
|
errp);
|
||||||
|
}
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||||
if (s->cpu_hotplug_legacy) {
|
if (s->cpu_hotplug_legacy) {
|
||||||
legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp);
|
legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp);
|
||||||
@@ -408,8 +411,10 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
|||||||
acpi_memory_unplug_request_cb(hotplug_dev, &s->acpi_memory_hotplug,
|
acpi_memory_unplug_request_cb(hotplug_dev, &s->acpi_memory_hotplug,
|
||||||
dev, errp);
|
dev, errp);
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev,
|
if (!xen_enabled()) {
|
||||||
errp);
|
acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev,
|
||||||
|
errp);
|
||||||
|
}
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
|
||||||
!s->cpu_hotplug_legacy) {
|
!s->cpu_hotplug_legacy) {
|
||||||
acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
|
acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
|
||||||
@@ -700,7 +705,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
|
|||||||
* Reason: part of PIIX4 southbridge, needs to be wired up,
|
* Reason: part of PIIX4 southbridge, needs to be wired up,
|
||||||
* e.g. by mips_malta_init()
|
* e.g. by mips_malta_init()
|
||||||
*/
|
*/
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
dc->user_creatable = false;
|
||||||
dc->hotpluggable = false;
|
dc->hotpluggable = false;
|
||||||
hc->plug = piix4_device_plug_cb;
|
hc->plug = piix4_device_plug_cb;
|
||||||
hc->unplug_request = piix4_device_unplug_request_cb;
|
hc->unplug_request = piix4_device_unplug_request_cb;
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ static void sl_nand_class_init(ObjectClass *klass, void *data)
|
|||||||
dc->vmsd = &vmstate_sl_nand_info;
|
dc->vmsd = &vmstate_sl_nand_info;
|
||||||
dc->props = sl_nand_properties;
|
dc->props = sl_nand_properties;
|
||||||
/* Reason: init() method uses drive_get() */
|
/* Reason: init() method uses drive_get() */
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
dc->user_creatable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo sl_nand_info = {
|
static const TypeInfo sl_nand_info = {
|
||||||
|
|||||||
@@ -364,12 +364,12 @@ static void acpi_dsdt_add_power_button(Aml *scope)
|
|||||||
|
|
||||||
/* RSDP */
|
/* RSDP */
|
||||||
static GArray *
|
static GArray *
|
||||||
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
|
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned xsdt_tbl_offset)
|
||||||
{
|
{
|
||||||
AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);
|
AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);
|
||||||
unsigned rsdt_pa_size = sizeof(rsdp->rsdt_physical_address);
|
unsigned xsdt_pa_size = sizeof(rsdp->xsdt_physical_address);
|
||||||
unsigned rsdt_pa_offset =
|
unsigned xsdt_pa_offset =
|
||||||
(char *)&rsdp->rsdt_physical_address - rsdp_table->data;
|
(char *)&rsdp->xsdt_physical_address - rsdp_table->data;
|
||||||
|
|
||||||
bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16,
|
bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16,
|
||||||
true /* fseg memory */);
|
true /* fseg memory */);
|
||||||
@@ -381,8 +381,8 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
|
|||||||
|
|
||||||
/* Address to be filled by Guest linker */
|
/* Address to be filled by Guest linker */
|
||||||
bios_linker_loader_add_pointer(linker,
|
bios_linker_loader_add_pointer(linker,
|
||||||
ACPI_BUILD_RSDP_FILE, rsdt_pa_offset, rsdt_pa_size,
|
ACPI_BUILD_RSDP_FILE, xsdt_pa_offset, xsdt_pa_size,
|
||||||
ACPI_BUILD_TABLE_FILE, rsdt_tbl_offset);
|
ACPI_BUILD_TABLE_FILE, xsdt_tbl_offset);
|
||||||
|
|
||||||
/* Checksum to be filled by Guest linker */
|
/* Checksum to be filled by Guest linker */
|
||||||
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
||||||
@@ -486,30 +486,25 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
|||||||
AcpiSystemResourceAffinityTable *srat;
|
AcpiSystemResourceAffinityTable *srat;
|
||||||
AcpiSratProcessorGiccAffinity *core;
|
AcpiSratProcessorGiccAffinity *core;
|
||||||
AcpiSratMemoryAffinity *numamem;
|
AcpiSratMemoryAffinity *numamem;
|
||||||
int i, j, srat_start;
|
int i, srat_start;
|
||||||
uint64_t mem_base;
|
uint64_t mem_base;
|
||||||
uint32_t *cpu_node = g_malloc0(vms->smp_cpus * sizeof(uint32_t));
|
MachineClass *mc = MACHINE_GET_CLASS(vms);
|
||||||
|
const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(MACHINE(vms));
|
||||||
for (i = 0; i < vms->smp_cpus; i++) {
|
|
||||||
j = numa_get_node_for_cpu(i);
|
|
||||||
if (j < nb_numa_nodes) {
|
|
||||||
cpu_node[i] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srat_start = table_data->len;
|
srat_start = table_data->len;
|
||||||
srat = acpi_data_push(table_data, sizeof(*srat));
|
srat = acpi_data_push(table_data, sizeof(*srat));
|
||||||
srat->reserved1 = cpu_to_le32(1);
|
srat->reserved1 = cpu_to_le32(1);
|
||||||
|
|
||||||
for (i = 0; i < vms->smp_cpus; ++i) {
|
for (i = 0; i < cpu_list->len; ++i) {
|
||||||
|
int node_id = cpu_list->cpus[i].props.has_node_id ?
|
||||||
|
cpu_list->cpus[i].props.node_id : 0;
|
||||||
core = acpi_data_push(table_data, sizeof(*core));
|
core = acpi_data_push(table_data, sizeof(*core));
|
||||||
core->type = ACPI_SRAT_PROCESSOR_GICC;
|
core->type = ACPI_SRAT_PROCESSOR_GICC;
|
||||||
core->length = sizeof(*core);
|
core->length = sizeof(*core);
|
||||||
core->proximity = cpu_to_le32(cpu_node[i]);
|
core->proximity = cpu_to_le32(node_id);
|
||||||
core->acpi_processor_uid = cpu_to_le32(i);
|
core->acpi_processor_uid = cpu_to_le32(i);
|
||||||
core->flags = cpu_to_le32(1);
|
core->flags = cpu_to_le32(1);
|
||||||
}
|
}
|
||||||
g_free(cpu_node);
|
|
||||||
|
|
||||||
mem_base = vms->memmap[VIRT_MEM].base;
|
mem_base = vms->memmap[VIRT_MEM].base;
|
||||||
for (i = 0; i < nb_numa_nodes; ++i) {
|
for (i = 0; i < nb_numa_nodes; ++i) {
|
||||||
@@ -659,7 +654,7 @@ static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
|||||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||||
{
|
{
|
||||||
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||||
unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;
|
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||||
uint16_t bootflags;
|
uint16_t bootflags;
|
||||||
|
|
||||||
switch (vms->psci_conduit) {
|
switch (vms->psci_conduit) {
|
||||||
@@ -685,7 +680,7 @@ static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
|||||||
|
|
||||||
/* DSDT address to be filled by Guest linker */
|
/* DSDT address to be filled by Guest linker */
|
||||||
bios_linker_loader_add_pointer(linker,
|
bios_linker_loader_add_pointer(linker,
|
||||||
ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
|
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||||
|
|
||||||
build_header(linker, table_data,
|
build_header(linker, table_data,
|
||||||
@@ -748,7 +743,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
|
|||||||
{
|
{
|
||||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||||
GArray *table_offsets;
|
GArray *table_offsets;
|
||||||
unsigned dsdt, rsdt;
|
unsigned dsdt, xsdt;
|
||||||
GArray *tables_blob = tables->table_data;
|
GArray *tables_blob = tables->table_data;
|
||||||
|
|
||||||
table_offsets = g_array_new(false, true /* clear */,
|
table_offsets = g_array_new(false, true /* clear */,
|
||||||
@@ -788,12 +783,12 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
|
|||||||
build_iort(tables_blob, tables->linker);
|
build_iort(tables_blob, tables->linker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RSDT is pointed to by RSDP */
|
/* XSDT is pointed to by RSDP */
|
||||||
rsdt = tables_blob->len;
|
xsdt = tables_blob->len;
|
||||||
build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
|
build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
|
||||||
|
|
||||||
/* RSDP is in FSEG memory, so allocate it separately */
|
/* RSDP is in FSEG memory, so allocate it separately */
|
||||||
build_rsdp(tables->rsdp, tables->linker, rsdt);
|
build_rsdp(tables->rsdp, tables->linker, xsdt);
|
||||||
|
|
||||||
/* Cleanup memory that's no longer used. */
|
/* Cleanup memory that's no longer used. */
|
||||||
g_array_free(table_offsets, true);
|
g_array_free(table_offsets, true);
|
||||||
|
|||||||
123
hw/arm/virt.c
123
hw/arm/virt.c
@@ -338,7 +338,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
|||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
int addr_cells = 1;
|
int addr_cells = 1;
|
||||||
unsigned int i;
|
const MachineState *ms = MACHINE(vms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From Documentation/devicetree/bindings/arm/cpus.txt
|
* From Documentation/devicetree/bindings/arm/cpus.txt
|
||||||
@@ -369,6 +369,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
|||||||
for (cpu = vms->smp_cpus - 1; cpu >= 0; cpu--) {
|
for (cpu = vms->smp_cpus - 1; cpu >= 0; cpu--) {
|
||||||
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||||
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
|
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
|
||||||
|
CPUState *cs = CPU(armcpu);
|
||||||
|
|
||||||
qemu_fdt_add_subnode(vms->fdt, nodename);
|
qemu_fdt_add_subnode(vms->fdt, nodename);
|
||||||
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu");
|
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu");
|
||||||
@@ -389,9 +390,9 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
|||||||
armcpu->mp_affinity);
|
armcpu->mp_affinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
i = numa_get_node_for_cpu(cpu);
|
if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
|
||||||
if (i < nb_numa_nodes) {
|
qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id",
|
||||||
qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id", i);
|
ms->possible_cpus->cpus[cs->cpu_index].props.node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(nodename);
|
g_free(nodename);
|
||||||
@@ -1194,10 +1195,35 @@ void virt_machine_done(Notifier *notifier, void *data)
|
|||||||
virt_build_smbios(vms);
|
virt_build_smbios(vms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
|
||||||
|
{
|
||||||
|
uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
|
||||||
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||||
|
|
||||||
|
if (!vmc->disallow_affinity_adjustment) {
|
||||||
|
/* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
|
||||||
|
* GIC's target-list limitations. 32-bit KVM hosts currently
|
||||||
|
* always create clusters of 4 CPUs, but that is expected to
|
||||||
|
* change when they gain support for gicv3. When KVM is enabled
|
||||||
|
* it will override the changes we make here, therefore our
|
||||||
|
* purposes are to make TCG consistent (with 64-bit KVM hosts)
|
||||||
|
* and to improve SGI efficiency.
|
||||||
|
*/
|
||||||
|
if (vms->gic_version == 3) {
|
||||||
|
clustersz = GICV3_TARGETLIST_BITS;
|
||||||
|
} else {
|
||||||
|
clustersz = GIC_TARGETLIST_BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arm_cpu_mp_affinity(idx, clustersz);
|
||||||
|
}
|
||||||
|
|
||||||
static void machvirt_init(MachineState *machine)
|
static void machvirt_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
VirtMachineState *vms = VIRT_MACHINE(machine);
|
VirtMachineState *vms = VIRT_MACHINE(machine);
|
||||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||||
|
const CPUArchIdList *possible_cpus;
|
||||||
qemu_irq pic[NUM_IRQS];
|
qemu_irq pic[NUM_IRQS];
|
||||||
MemoryRegion *sysmem = get_system_memory();
|
MemoryRegion *sysmem = get_system_memory();
|
||||||
MemoryRegion *secure_sysmem = NULL;
|
MemoryRegion *secure_sysmem = NULL;
|
||||||
@@ -1210,7 +1236,6 @@ static void machvirt_init(MachineState *machine)
|
|||||||
CPUClass *cc;
|
CPUClass *cc;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||||
uint8_t clustersz;
|
|
||||||
|
|
||||||
if (!cpu_model) {
|
if (!cpu_model) {
|
||||||
cpu_model = "cortex-a15";
|
cpu_model = "cortex-a15";
|
||||||
@@ -1263,10 +1288,8 @@ static void machvirt_init(MachineState *machine)
|
|||||||
*/
|
*/
|
||||||
if (vms->gic_version == 3) {
|
if (vms->gic_version == 3) {
|
||||||
virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000;
|
virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000;
|
||||||
clustersz = GICV3_TARGETLIST_BITS;
|
|
||||||
} else {
|
} else {
|
||||||
virt_max_cpus = GIC_NCPU;
|
virt_max_cpus = GIC_NCPU;
|
||||||
clustersz = GIC_TARGETLIST_BITS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_cpus > virt_max_cpus) {
|
if (max_cpus > virt_max_cpus) {
|
||||||
@@ -1324,21 +1347,35 @@ static void machvirt_init(MachineState *machine)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n = 0; n < smp_cpus; n++) {
|
possible_cpus = mc->possible_cpu_arch_ids(machine);
|
||||||
Object *cpuobj = object_new(typename);
|
for (n = 0; n < possible_cpus->len; n++) {
|
||||||
if (!vmc->disallow_affinity_adjustment) {
|
Object *cpuobj;
|
||||||
/* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
|
CPUState *cs;
|
||||||
* GIC's target-list limitations. 32-bit KVM hosts currently
|
int node_id;
|
||||||
* always create clusters of 4 CPUs, but that is expected to
|
|
||||||
* change when they gain support for gicv3. When KVM is enabled
|
if (n >= smp_cpus) {
|
||||||
* it will override the changes we make here, therefore our
|
break;
|
||||||
* purposes are to make TCG consistent (with 64-bit KVM hosts)
|
}
|
||||||
* and to improve SGI efficiency.
|
|
||||||
*/
|
cpuobj = object_new(typename);
|
||||||
uint8_t aff1 = n / clustersz;
|
object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
|
||||||
uint8_t aff0 = n % clustersz;
|
"mp-affinity", NULL);
|
||||||
object_property_set_int(cpuobj, (aff1 << ARM_AFF1_SHIFT) | aff0,
|
|
||||||
"mp-affinity", NULL);
|
cs = CPU(cpuobj);
|
||||||
|
cs->cpu_index = n;
|
||||||
|
|
||||||
|
node_id = possible_cpus->cpus[cs->cpu_index].props.node_id;
|
||||||
|
if (!possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
|
||||||
|
/* by default CPUState::numa_node was 0 if it's not set via CLI
|
||||||
|
* keep it this way for now but in future we probably should
|
||||||
|
* refuse to start up with incomplete numa mapping */
|
||||||
|
node_id = 0;
|
||||||
|
}
|
||||||
|
if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) {
|
||||||
|
cs->numa_node = node_id;
|
||||||
|
} else {
|
||||||
|
/* CPU isn't device_add compatible yet, this shouldn't happen */
|
||||||
|
error_setg(&error_abort, "user set node-id not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vms->secure) {
|
if (!vms->secure) {
|
||||||
@@ -1518,6 +1555,46 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CpuInstanceProperties
|
||||||
|
virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
||||||
|
{
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||||
|
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||||
|
|
||||||
|
assert(cpu_index < possible_cpus->len);
|
||||||
|
return possible_cpus->cpus[cpu_index].props;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
VirtMachineState *vms = VIRT_MACHINE(ms);
|
||||||
|
|
||||||
|
if (ms->possible_cpus) {
|
||||||
|
assert(ms->possible_cpus->len == max_cpus);
|
||||||
|
return ms->possible_cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||||
|
sizeof(CPUArchId) * max_cpus);
|
||||||
|
ms->possible_cpus->len = max_cpus;
|
||||||
|
for (n = 0; n < ms->possible_cpus->len; n++) {
|
||||||
|
ms->possible_cpus->cpus[n].arch_id =
|
||||||
|
virt_cpu_mp_affinity(vms, n);
|
||||||
|
ms->possible_cpus->cpus[n].props.has_thread_id = true;
|
||||||
|
ms->possible_cpus->cpus[n].props.thread_id = n;
|
||||||
|
|
||||||
|
/* default distribution of CPUs over NUMA nodes */
|
||||||
|
if (nb_numa_nodes) {
|
||||||
|
/* preset values but do not enable them i.e. 'has_node_id = false',
|
||||||
|
* numa init code will enable them later if manual mapping wasn't
|
||||||
|
* present on CLI */
|
||||||
|
ms->possible_cpus->cpus[n].props.node_id = n % nb_numa_nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ms->possible_cpus;
|
||||||
|
}
|
||||||
|
|
||||||
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_CLASS(oc);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
@@ -1534,6 +1611,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
|||||||
mc->pci_allow_0_address = true;
|
mc->pci_allow_0_address = true;
|
||||||
/* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
|
/* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
|
||||||
mc->minimum_page_bits = 12;
|
mc->minimum_page_bits = 12;
|
||||||
|
mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids;
|
||||||
|
mc->cpu_index_to_instance_props = virt_cpu_index_to_props;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo virt_machine_info = {
|
static const TypeInfo virt_machine_info = {
|
||||||
|
|||||||
@@ -15,4 +15,4 @@ common-obj-$(CONFIG_CS4231) += cs4231.o
|
|||||||
common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o
|
common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o
|
||||||
common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o
|
common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o
|
||||||
|
|
||||||
$(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
|
common-obj-y += soundhw.o
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
|
|
||||||
@@ -33,11 +33,7 @@
|
|||||||
|
|
||||||
#define ADLIB_KILL_TIMERS 1
|
#define ADLIB_KILL_TIMERS 1
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
|
||||||
#define ADLIB_DESC "Yamaha YMF262 (OPL3)"
|
|
||||||
#else
|
|
||||||
#define ADLIB_DESC "Yamaha YM3812 (OPL2)"
|
#define ADLIB_DESC "Yamaha YM3812 (OPL2)"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
@@ -50,14 +46,8 @@
|
|||||||
#define ldebug(...)
|
#define ldebug(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
|
||||||
#include "ymf262.h"
|
|
||||||
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
|
|
||||||
#define SHIFT 2
|
|
||||||
#else
|
|
||||||
#include "fmopl.h"
|
#include "fmopl.h"
|
||||||
#define SHIFT 1
|
#define SHIFT 1
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TYPE_ADLIB "adlib"
|
#define TYPE_ADLIB "adlib"
|
||||||
#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
|
#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
|
||||||
@@ -80,9 +70,7 @@ typedef struct {
|
|||||||
SWVoiceOut *voice;
|
SWVoiceOut *voice;
|
||||||
int left, pos, samples;
|
int left, pos, samples;
|
||||||
QEMUAudioTimeStamp ats;
|
QEMUAudioTimeStamp ats;
|
||||||
#ifndef HAS_YMF262
|
|
||||||
FM_OPL *opl;
|
FM_OPL *opl;
|
||||||
#endif
|
|
||||||
PortioList port_list;
|
PortioList port_list;
|
||||||
} AdlibState;
|
} AdlibState;
|
||||||
|
|
||||||
@@ -90,11 +78,7 @@ static AdlibState *glob_adlib;
|
|||||||
|
|
||||||
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
|
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
|
||||||
{
|
{
|
||||||
#ifdef HAS_YMF262
|
|
||||||
YMF262TimerOver (0, n);
|
|
||||||
#else
|
|
||||||
OPLTimerOver (s->opl, n);
|
OPLTimerOver (s->opl, n);
|
||||||
#endif
|
|
||||||
s->ticking[n] = 0;
|
s->ticking[n] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +115,7 @@ static void adlib_write(void *opaque, uint32_t nport, uint32_t val)
|
|||||||
|
|
||||||
adlib_kill_timers (s);
|
adlib_kill_timers (s);
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
|
||||||
YMF262Write (0, a, val);
|
|
||||||
#else
|
|
||||||
OPLWrite (s->opl, a, val);
|
OPLWrite (s->opl, a, val);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t adlib_read(void *opaque, uint32_t nport)
|
static uint32_t adlib_read(void *opaque, uint32_t nport)
|
||||||
@@ -145,12 +125,8 @@ static uint32_t adlib_read(void *opaque, uint32_t nport)
|
|||||||
int a = nport & 3;
|
int a = nport & 3;
|
||||||
|
|
||||||
adlib_kill_timers (s);
|
adlib_kill_timers (s);
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
|
||||||
data = YMF262Read (0, a);
|
|
||||||
#else
|
|
||||||
data = OPLRead (s->opl, a);
|
data = OPLRead (s->opl, a);
|
||||||
#endif
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,11 +216,7 @@ static void adlib_callback (void *opaque, int free)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
|
||||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
|
||||||
#else
|
|
||||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||||
#endif
|
|
||||||
|
|
||||||
while (samples) {
|
while (samples) {
|
||||||
written = write_audio (s, samples);
|
written = write_audio (s, samples);
|
||||||
@@ -263,14 +235,10 @@ static void adlib_callback (void *opaque, int free)
|
|||||||
|
|
||||||
static void Adlib_fini (AdlibState *s)
|
static void Adlib_fini (AdlibState *s)
|
||||||
{
|
{
|
||||||
#ifdef HAS_YMF262
|
|
||||||
YMF262Shutdown ();
|
|
||||||
#else
|
|
||||||
if (s->opl) {
|
if (s->opl) {
|
||||||
OPLDestroy (s->opl);
|
OPLDestroy (s->opl);
|
||||||
s->opl = NULL;
|
s->opl = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
g_free(s->mixbuf);
|
g_free(s->mixbuf);
|
||||||
|
|
||||||
@@ -297,17 +265,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
glob_adlib = s;
|
glob_adlib = s;
|
||||||
|
|
||||||
#ifdef HAS_YMF262
|
s->opl = OPLCreate (3579545, s->freq);
|
||||||
if (YMF262Init (1, 14318180, s->freq)) {
|
|
||||||
error_setg (errp, "YMF262Init %d failed", s->freq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
YMF262SetTimerHandler (0, timer_handler, 0);
|
|
||||||
s->enabled = 1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, s->freq);
|
|
||||||
if (!s->opl) {
|
if (!s->opl) {
|
||||||
error_setg (errp, "OPLCreate %d failed", s->freq);
|
error_setg (errp, "OPLCreate %d failed", s->freq);
|
||||||
return;
|
return;
|
||||||
@@ -316,7 +274,6 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
|
|||||||
OPLSetTimerHandler (s->opl, timer_handler, 0);
|
OPLSetTimerHandler (s->opl, timer_handler, 0);
|
||||||
s->enabled = 1;
|
s->enabled = 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
as.freq = s->freq;
|
as.freq = s->freq;
|
||||||
as.nchannels = SHIFT;
|
as.nchannels = SHIFT;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
#include "hw/qdev.h"
|
#include "hw/qdev.h"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
|
|||||||
277
hw/audio/fmopl.c
277
hw/audio/fmopl.c
@@ -30,21 +30,15 @@
|
|||||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define HAS_YM3812 1
|
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
//#include "driver.h" /* use M.A.M.E. */
|
//#include "driver.h" /* use M.A.M.E. */
|
||||||
#include "fmopl.h"
|
#include "fmopl.h"
|
||||||
|
#include "qemu/osdep.h"
|
||||||
#ifndef PI
|
#ifndef PI
|
||||||
#define PI 3.14159265358979323846
|
#define PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ARRAY_SIZE
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* -------------------- for debug --------------------- */
|
/* -------------------- for debug --------------------- */
|
||||||
/* #define OPL_OUTPUT_LOG */
|
/* #define OPL_OUTPUT_LOG */
|
||||||
#ifdef OPL_OUTPUT_LOG
|
#ifdef OPL_OUTPUT_LOG
|
||||||
@@ -124,7 +118,7 @@ static const int slot_array[32]=
|
|||||||
/* key scale level */
|
/* key scale level */
|
||||||
/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
|
/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
|
||||||
#define DV (EG_STEP/2)
|
#define DV (EG_STEP/2)
|
||||||
static const UINT32 KSL_TABLE[8*16]=
|
static const uint32_t KSL_TABLE[8*16]=
|
||||||
{
|
{
|
||||||
/* OCT 0 */
|
/* OCT 0 */
|
||||||
0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
|
0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
|
||||||
@@ -172,7 +166,7 @@ static const UINT32 KSL_TABLE[8*16]=
|
|||||||
/* sustain lebel table (3db per step) */
|
/* sustain lebel table (3db per step) */
|
||||||
/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
|
/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
|
||||||
#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
|
#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
|
||||||
static const INT32 SL_TABLE[16]={
|
static const int32_t SL_TABLE[16]={
|
||||||
SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
|
SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
|
||||||
SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
|
SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
|
||||||
};
|
};
|
||||||
@@ -182,22 +176,22 @@ static const INT32 SL_TABLE[16]={
|
|||||||
/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
|
/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
|
||||||
/* TL_TABLE[ 0 to TL_MAX ] : plus section */
|
/* TL_TABLE[ 0 to TL_MAX ] : plus section */
|
||||||
/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
|
/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
|
||||||
static INT32 *TL_TABLE;
|
static int32_t *TL_TABLE;
|
||||||
|
|
||||||
/* pointers to TL_TABLE with sinwave output offset */
|
/* pointers to TL_TABLE with sinwave output offset */
|
||||||
static INT32 **SIN_TABLE;
|
static int32_t **SIN_TABLE;
|
||||||
|
|
||||||
/* LFO table */
|
/* LFO table */
|
||||||
static INT32 *AMS_TABLE;
|
static int32_t *AMS_TABLE;
|
||||||
static INT32 *VIB_TABLE;
|
static int32_t *VIB_TABLE;
|
||||||
|
|
||||||
/* envelope output curve table */
|
/* envelope output curve table */
|
||||||
/* attack + decay + OFF */
|
/* attack + decay + OFF */
|
||||||
static INT32 ENV_CURVE[2*EG_ENT+1];
|
static int32_t ENV_CURVE[2*EG_ENT+1];
|
||||||
|
|
||||||
/* multiple table */
|
/* multiple table */
|
||||||
#define ML 2
|
#define ML 2
|
||||||
static const UINT32 MUL_TABLE[16]= {
|
static const uint32_t MUL_TABLE[16]= {
|
||||||
/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
|
/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
|
||||||
0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
|
0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
|
||||||
8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
|
8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
|
||||||
@@ -205,7 +199,7 @@ static const UINT32 MUL_TABLE[16]= {
|
|||||||
#undef ML
|
#undef ML
|
||||||
|
|
||||||
/* dummy attack / decay rate ( when rate == 0 ) */
|
/* dummy attack / decay rate ( when rate == 0 ) */
|
||||||
static INT32 RATE_0[16]=
|
static int32_t RATE_0[16]=
|
||||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||||
|
|
||||||
/* -------------------- static state --------------------- */
|
/* -------------------- static state --------------------- */
|
||||||
@@ -221,14 +215,14 @@ static OPL_CH *S_CH;
|
|||||||
static OPL_CH *E_CH;
|
static OPL_CH *E_CH;
|
||||||
static OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
|
static OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
|
||||||
|
|
||||||
static INT32 outd[1];
|
static int32_t outd[1];
|
||||||
static INT32 ams;
|
static int32_t ams;
|
||||||
static INT32 vib;
|
static int32_t vib;
|
||||||
static INT32 *ams_table;
|
static int32_t *ams_table;
|
||||||
static INT32 *vib_table;
|
static int32_t *vib_table;
|
||||||
static INT32 amsIncr;
|
static int32_t amsIncr;
|
||||||
static INT32 vibIncr;
|
static int32_t vibIncr;
|
||||||
static INT32 feedback2; /* connect for SLOT 2 */
|
static int32_t feedback2; /* connect for SLOT 2 */
|
||||||
|
|
||||||
/* log output level */
|
/* log output level */
|
||||||
#define LOG_ERR 3 /* ERROR */
|
#define LOG_ERR 3 /* ERROR */
|
||||||
@@ -262,8 +256,6 @@ static inline void OPL_STATUS_SET(FM_OPL *OPL,int flag)
|
|||||||
if(OPL->status & OPL->statusmask)
|
if(OPL->status & OPL->statusmask)
|
||||||
{ /* IRQ on */
|
{ /* IRQ on */
|
||||||
OPL->status |= 0x80;
|
OPL->status |= 0x80;
|
||||||
/* callback user interrupt handler (IRQ is OFF to ON) */
|
|
||||||
if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,8 +270,6 @@ static inline void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
|
|||||||
if (!(OPL->status & OPL->statusmask) )
|
if (!(OPL->status & OPL->statusmask) )
|
||||||
{
|
{
|
||||||
OPL->status &= 0x7f;
|
OPL->status &= 0x7f;
|
||||||
/* callback user interrupt handler (IRQ is ON to OFF) */
|
|
||||||
if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +311,7 @@ static inline void OPL_KEYOFF(OPL_SLOT *SLOT)
|
|||||||
|
|
||||||
/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
|
/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
|
||||||
/* return : envelope output */
|
/* return : envelope output */
|
||||||
static inline UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
|
static inline uint32_t OPL_CALC_SLOT( OPL_SLOT *SLOT )
|
||||||
{
|
{
|
||||||
/* calcrate envelope generator */
|
/* calcrate envelope generator */
|
||||||
if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
|
if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
|
||||||
@@ -361,7 +351,7 @@ static inline UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
|
|||||||
/* set algorithm connection */
|
/* set algorithm connection */
|
||||||
static void set_algorithm( OPL_CH *CH)
|
static void set_algorithm( OPL_CH *CH)
|
||||||
{
|
{
|
||||||
INT32 *carrier = &outd[0];
|
int32_t *carrier = &outd[0];
|
||||||
CH->connect1 = CH->CON ? carrier : &feedback2;
|
CH->connect1 = CH->CON ? carrier : &feedback2;
|
||||||
CH->connect2 = carrier;
|
CH->connect2 = carrier;
|
||||||
}
|
}
|
||||||
@@ -453,7 +443,7 @@ static inline void set_sl_rr(FM_OPL *OPL,int slot,int v)
|
|||||||
/* ---------- calcrate one of channel ---------- */
|
/* ---------- calcrate one of channel ---------- */
|
||||||
static inline void OPL_CALC_CH( OPL_CH *CH )
|
static inline void OPL_CALC_CH( OPL_CH *CH )
|
||||||
{
|
{
|
||||||
UINT32 env_out;
|
uint32_t env_out;
|
||||||
OPL_SLOT *SLOT;
|
OPL_SLOT *SLOT;
|
||||||
|
|
||||||
feedback2 = 0;
|
feedback2 = 0;
|
||||||
@@ -498,9 +488,9 @@ static inline void OPL_CALC_CH( OPL_CH *CH )
|
|||||||
#define WHITE_NOISE_db 6.0
|
#define WHITE_NOISE_db 6.0
|
||||||
static inline void OPL_CALC_RH( OPL_CH *CH )
|
static inline void OPL_CALC_RH( OPL_CH *CH )
|
||||||
{
|
{
|
||||||
UINT32 env_tam,env_sd,env_top,env_hh;
|
uint32_t env_tam,env_sd,env_top,env_hh;
|
||||||
int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
|
int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
|
||||||
INT32 tone8;
|
int32_t tone8;
|
||||||
|
|
||||||
OPL_SLOT *SLOT;
|
OPL_SLOT *SLOT;
|
||||||
int env_out;
|
int env_out;
|
||||||
@@ -618,20 +608,20 @@ static int OPLOpenTable( void )
|
|||||||
double pom;
|
double pom;
|
||||||
|
|
||||||
/* allocate dynamic tables */
|
/* allocate dynamic tables */
|
||||||
if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
|
if( (TL_TABLE = malloc(TL_MAX*2*sizeof(int32_t))) == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
|
if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(int32_t *))) == NULL)
|
||||||
{
|
{
|
||||||
free(TL_TABLE);
|
free(TL_TABLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
|
if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(int32_t))) == NULL)
|
||||||
{
|
{
|
||||||
free(TL_TABLE);
|
free(TL_TABLE);
|
||||||
free(SIN_TABLE);
|
free(SIN_TABLE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
|
if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(int32_t))) == NULL)
|
||||||
{
|
{
|
||||||
free(TL_TABLE);
|
free(TL_TABLE);
|
||||||
free(SIN_TABLE);
|
free(SIN_TABLE);
|
||||||
@@ -763,18 +753,15 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v)
|
|||||||
{
|
{
|
||||||
case 0x01:
|
case 0x01:
|
||||||
/* wave selector enable */
|
/* wave selector enable */
|
||||||
if(OPL->type&OPL_TYPE_WAVESEL)
|
OPL->wavesel = v&0x20;
|
||||||
|
if(!OPL->wavesel)
|
||||||
{
|
{
|
||||||
OPL->wavesel = v&0x20;
|
/* preset compatible mode */
|
||||||
if(!OPL->wavesel)
|
int c;
|
||||||
|
for(c=0;c<OPL->max_ch;c++)
|
||||||
{
|
{
|
||||||
/* preset compatible mode */
|
OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
|
||||||
int c;
|
OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
|
||||||
for(c=0;c<OPL->max_ch;c++)
|
|
||||||
{
|
|
||||||
OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
|
|
||||||
OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -791,8 +778,8 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* set IRQ mask ,timer enable*/
|
{ /* set IRQ mask ,timer enable*/
|
||||||
UINT8 st1 = v&1;
|
uint8_t st1 = v&1;
|
||||||
UINT8 st2 = (v>>1)&1;
|
uint8_t st2 = (v>>1)&1;
|
||||||
/* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
|
/* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
|
||||||
OPL_STATUS_RESET(OPL,v&0x78);
|
OPL_STATUS_RESET(OPL,v&0x78);
|
||||||
OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
|
OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
|
||||||
@@ -812,57 +799,6 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
#if BUILD_Y8950
|
|
||||||
case 0x06: /* Key Board OUT */
|
|
||||||
if(OPL->type&OPL_TYPE_KEYBOARD)
|
|
||||||
{
|
|
||||||
if(OPL->keyboardhandler_w)
|
|
||||||
OPL->keyboardhandler_w(OPL->keyboard_param,v);
|
|
||||||
else
|
|
||||||
LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case 0x07: /* DELTA-T control : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
|
|
||||||
if(OPL->type&OPL_TYPE_ADPCM)
|
|
||||||
YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
|
|
||||||
return;
|
|
||||||
case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
|
|
||||||
OPL->mode = v;
|
|
||||||
v&=0x1f; /* for DELTA-T unit */
|
|
||||||
case 0x09: /* START ADD */
|
|
||||||
case 0x0a:
|
|
||||||
case 0x0b: /* STOP ADD */
|
|
||||||
case 0x0c:
|
|
||||||
case 0x0d: /* PRESCALE */
|
|
||||||
case 0x0e:
|
|
||||||
case 0x0f: /* ADPCM data */
|
|
||||||
case 0x10: /* DELTA-N */
|
|
||||||
case 0x11: /* DELTA-N */
|
|
||||||
case 0x12: /* EG-CTRL */
|
|
||||||
if(OPL->type&OPL_TYPE_ADPCM)
|
|
||||||
YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
|
|
||||||
return;
|
|
||||||
#if 0
|
|
||||||
case 0x15: /* DAC data */
|
|
||||||
case 0x16:
|
|
||||||
case 0x17: /* SHIFT */
|
|
||||||
return;
|
|
||||||
case 0x18: /* I/O CTRL (Direction) */
|
|
||||||
if(OPL->type&OPL_TYPE_IO)
|
|
||||||
OPL->portDirection = v&0x0f;
|
|
||||||
return;
|
|
||||||
case 0x19: /* I/O DATA */
|
|
||||||
if(OPL->type&OPL_TYPE_IO)
|
|
||||||
{
|
|
||||||
OPL->portLatch = v;
|
|
||||||
if(OPL->porthandler_w)
|
|
||||||
OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case 0x1a: /* PCM data */
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x20: /* am,vib,ksr,eg type,mul */
|
case 0x20: /* am,vib,ksr,eg type,mul */
|
||||||
@@ -891,7 +827,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v)
|
|||||||
case 0xbd:
|
case 0xbd:
|
||||||
/* amsep,vibdep,r,bd,sd,tom,tc,hh */
|
/* amsep,vibdep,r,bd,sd,tom,tc,hh */
|
||||||
{
|
{
|
||||||
UINT8 rkey = OPL->rhythm^v;
|
uint8_t rkey = OPL->rhythm^v;
|
||||||
OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
|
OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
|
||||||
OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
|
OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
|
||||||
OPL->rhythm = v&0x3f;
|
OPL->rhythm = v&0x3f;
|
||||||
@@ -1032,20 +968,19 @@ static void OPL_UnLockTable(void)
|
|||||||
OPLCloseTable();
|
OPLCloseTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (BUILD_YM3812 || BUILD_YM3526)
|
|
||||||
/*******************************************************************************/
|
/*******************************************************************************/
|
||||||
/* YM3812 local section */
|
/* YM3812 local section */
|
||||||
/*******************************************************************************/
|
/*******************************************************************************/
|
||||||
|
|
||||||
/* ---------- update one of chip ----------- */
|
/* ---------- update one of chip ----------- */
|
||||||
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
|
void YM3812UpdateOne(FM_OPL *OPL, int16_t *buffer, int length)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int data;
|
int data;
|
||||||
OPLSAMPLE *buf = buffer;
|
int16_t *buf = buffer;
|
||||||
UINT32 amsCnt = OPL->amsCnt;
|
uint32_t amsCnt = OPL->amsCnt;
|
||||||
UINT32 vibCnt = OPL->vibCnt;
|
uint32_t vibCnt = OPL->vibCnt;
|
||||||
UINT8 rhythm = OPL->rhythm&0x20;
|
uint8_t rhythm = OPL->rhythm&0x20;
|
||||||
OPL_CH *CH,*R_CH;
|
OPL_CH *CH,*R_CH;
|
||||||
|
|
||||||
if( (void *)OPL != cur_chip ){
|
if( (void *)OPL != cur_chip ){
|
||||||
@@ -1095,72 +1030,9 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif /* (BUILD_YM3812 || BUILD_YM3526) */
|
|
||||||
|
|
||||||
#if BUILD_Y8950
|
|
||||||
|
|
||||||
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int data;
|
|
||||||
OPLSAMPLE *buf = buffer;
|
|
||||||
UINT32 amsCnt = OPL->amsCnt;
|
|
||||||
UINT32 vibCnt = OPL->vibCnt;
|
|
||||||
UINT8 rhythm = OPL->rhythm&0x20;
|
|
||||||
OPL_CH *CH,*R_CH;
|
|
||||||
YM_DELTAT *DELTAT = OPL->deltat;
|
|
||||||
|
|
||||||
/* setup DELTA-T unit */
|
|
||||||
YM_DELTAT_DECODE_PRESET(DELTAT);
|
|
||||||
|
|
||||||
if( (void *)OPL != cur_chip ){
|
|
||||||
cur_chip = (void *)OPL;
|
|
||||||
/* channel pointers */
|
|
||||||
S_CH = OPL->P_CH;
|
|
||||||
E_CH = &S_CH[9];
|
|
||||||
/* rhythm slot */
|
|
||||||
SLOT7_1 = &S_CH[7].SLOT[SLOT1];
|
|
||||||
SLOT7_2 = &S_CH[7].SLOT[SLOT2];
|
|
||||||
SLOT8_1 = &S_CH[8].SLOT[SLOT1];
|
|
||||||
SLOT8_2 = &S_CH[8].SLOT[SLOT2];
|
|
||||||
/* LFO state */
|
|
||||||
amsIncr = OPL->amsIncr;
|
|
||||||
vibIncr = OPL->vibIncr;
|
|
||||||
ams_table = OPL->ams_table;
|
|
||||||
vib_table = OPL->vib_table;
|
|
||||||
}
|
|
||||||
R_CH = rhythm ? &S_CH[6] : E_CH;
|
|
||||||
for( i=0; i < length ; i++ )
|
|
||||||
{
|
|
||||||
/* channel A channel B channel C */
|
|
||||||
/* LFO */
|
|
||||||
ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
|
|
||||||
vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
|
|
||||||
outd[0] = 0;
|
|
||||||
/* deltaT ADPCM */
|
|
||||||
if( DELTAT->portstate )
|
|
||||||
YM_DELTAT_ADPCM_CALC(DELTAT);
|
|
||||||
/* FM part */
|
|
||||||
for(CH=S_CH ; CH < R_CH ; CH++)
|
|
||||||
OPL_CALC_CH(CH);
|
|
||||||
/* Rythn part */
|
|
||||||
if(rhythm)
|
|
||||||
OPL_CALC_RH(S_CH);
|
|
||||||
/* limit check */
|
|
||||||
data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
|
|
||||||
/* store to sound buffer */
|
|
||||||
buf[i] = data >> OPL_OUTSB;
|
|
||||||
}
|
|
||||||
OPL->amsCnt = amsCnt;
|
|
||||||
OPL->vibCnt = vibCnt;
|
|
||||||
/* deltaT START flag */
|
|
||||||
if( !DELTAT->portstate )
|
|
||||||
OPL->status &= 0xfe;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ---------- reset one of chip ---------- */
|
/* ---------- reset one of chip ---------- */
|
||||||
void OPLResetChip(FM_OPL *OPL)
|
static void OPLResetChip(FM_OPL *OPL)
|
||||||
{
|
{
|
||||||
int c,s;
|
int c,s;
|
||||||
int i;
|
int i;
|
||||||
@@ -1189,23 +1061,11 @@ void OPLResetChip(FM_OPL *OPL)
|
|||||||
CH->SLOT[s].evs = 0;
|
CH->SLOT[s].evs = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if BUILD_Y8950
|
|
||||||
if(OPL->type&OPL_TYPE_ADPCM)
|
|
||||||
{
|
|
||||||
YM_DELTAT *DELTAT = OPL->deltat;
|
|
||||||
|
|
||||||
DELTAT->freqbase = OPL->freqbase;
|
|
||||||
DELTAT->output_pointer = outd;
|
|
||||||
DELTAT->portshift = 5;
|
|
||||||
DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
|
|
||||||
YM_DELTAT_ADPCM_Reset(DELTAT,0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Create one of vietual YM3812 ---------- */
|
/* ---------- Create one of vietual YM3812 ---------- */
|
||||||
/* 'rate' is sampling rate and 'bufsiz' is the size of the */
|
/* 'rate' is sampling rate and 'bufsiz' is the size of the */
|
||||||
FM_OPL *OPLCreate(int type, int clock, int rate)
|
FM_OPL *OPLCreate(int clock, int rate)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
FM_OPL *OPL;
|
FM_OPL *OPL;
|
||||||
@@ -1216,9 +1076,6 @@ FM_OPL *OPLCreate(int type, int clock, int rate)
|
|||||||
/* allocate OPL state space */
|
/* allocate OPL state space */
|
||||||
state_size = sizeof(FM_OPL);
|
state_size = sizeof(FM_OPL);
|
||||||
state_size += sizeof(OPL_CH)*max_ch;
|
state_size += sizeof(OPL_CH)*max_ch;
|
||||||
#if BUILD_Y8950
|
|
||||||
if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
|
|
||||||
#endif
|
|
||||||
/* allocate memory block */
|
/* allocate memory block */
|
||||||
ptr = malloc(state_size);
|
ptr = malloc(state_size);
|
||||||
if(ptr==NULL) return NULL;
|
if(ptr==NULL) return NULL;
|
||||||
@@ -1226,11 +1083,7 @@ FM_OPL *OPLCreate(int type, int clock, int rate)
|
|||||||
memset(ptr,0,state_size);
|
memset(ptr,0,state_size);
|
||||||
OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
|
OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
|
||||||
OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
|
OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
|
||||||
#if BUILD_Y8950
|
|
||||||
if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
|
|
||||||
#endif
|
|
||||||
/* set channel state pointer */
|
/* set channel state pointer */
|
||||||
OPL->type = type;
|
|
||||||
OPL->clock = clock;
|
OPL->clock = clock;
|
||||||
OPL->rate = rate;
|
OPL->rate = rate;
|
||||||
OPL->max_ch = max_ch;
|
OPL->max_ch = max_ch;
|
||||||
@@ -1280,31 +1133,7 @@ void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOff
|
|||||||
OPL->TimerHandler = TimerHandler;
|
OPL->TimerHandler = TimerHandler;
|
||||||
OPL->TimerParam = channelOffset;
|
OPL->TimerParam = channelOffset;
|
||||||
}
|
}
|
||||||
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
|
|
||||||
{
|
|
||||||
OPL->IRQHandler = IRQHandler;
|
|
||||||
OPL->IRQParam = param;
|
|
||||||
}
|
|
||||||
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
|
|
||||||
{
|
|
||||||
OPL->UpdateHandler = UpdateHandler;
|
|
||||||
OPL->UpdateParam = param;
|
|
||||||
}
|
|
||||||
#if BUILD_Y8950
|
|
||||||
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
|
|
||||||
{
|
|
||||||
OPL->porthandler_w = PortHandler_w;
|
|
||||||
OPL->porthandler_r = PortHandler_r;
|
|
||||||
OPL->port_param = param;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
|
|
||||||
{
|
|
||||||
OPL->keyboardhandler_w = KeyboardHandler_w;
|
|
||||||
OPL->keyboardhandler_r = KeyboardHandler_r;
|
|
||||||
OPL->keyboard_param = param;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* ---------- YM3812 I/O interface ---------- */
|
/* ---------- YM3812 I/O interface ---------- */
|
||||||
int OPLWrite(FM_OPL *OPL,int a,int v)
|
int OPLWrite(FM_OPL *OPL,int a,int v)
|
||||||
{
|
{
|
||||||
@@ -1314,7 +1143,6 @@ int OPLWrite(FM_OPL *OPL,int a,int v)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* data port */
|
{ /* data port */
|
||||||
if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
|
|
||||||
#ifdef OPL_OUTPUT_LOG
|
#ifdef OPL_OUTPUT_LOG
|
||||||
if(opl_dbg_fp)
|
if(opl_dbg_fp)
|
||||||
{
|
{
|
||||||
@@ -1338,28 +1166,12 @@ unsigned char OPLRead(FM_OPL *OPL,int a)
|
|||||||
switch(OPL->address)
|
switch(OPL->address)
|
||||||
{
|
{
|
||||||
case 0x05: /* KeyBoard IN */
|
case 0x05: /* KeyBoard IN */
|
||||||
if(OPL->type&OPL_TYPE_KEYBOARD)
|
|
||||||
{
|
|
||||||
if(OPL->keyboardhandler_r)
|
|
||||||
return OPL->keyboardhandler_r(OPL->keyboard_param);
|
|
||||||
else {
|
|
||||||
LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
#if 0
|
#if 0
|
||||||
case 0x0f: /* ADPCM-DATA */
|
case 0x0f: /* ADPCM-DATA */
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
case 0x19: /* I/O DATA */
|
case 0x19: /* I/O DATA */
|
||||||
if(OPL->type&OPL_TYPE_IO)
|
|
||||||
{
|
|
||||||
if(OPL->porthandler_r)
|
|
||||||
return OPL->porthandler_r(OPL->port_param);
|
|
||||||
else {
|
|
||||||
LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
case 0x1a: /* PCM-DATA */
|
case 0x1a: /* PCM-DATA */
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1380,7 +1192,6 @@ int OPLTimerOver(FM_OPL *OPL,int c)
|
|||||||
if( OPL->mode & 0x80 )
|
if( OPL->mode & 0x80 )
|
||||||
{ /* CSM mode total level latch and auto key on */
|
{ /* CSM mode total level latch and auto key on */
|
||||||
int ch;
|
int ch;
|
||||||
if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
|
|
||||||
for(ch=0;ch<9;ch++)
|
for(ch=0;ch<9;ch++)
|
||||||
CSMKeyControll( &OPL->P_CH[ch] );
|
CSMKeyControll( &OPL->P_CH[ch] );
|
||||||
}
|
}
|
||||||
|
|||||||
175
hw/audio/fmopl.h
175
hw/audio/fmopl.h
@@ -1,174 +1,103 @@
|
|||||||
#ifndef FMOPL_H
|
#ifndef FMOPL_H
|
||||||
#define FMOPL_H
|
#define FMOPL_H
|
||||||
|
|
||||||
/* --- select emulation chips --- */
|
#include <stdint.h>
|
||||||
#define BUILD_YM3812 (HAS_YM3812)
|
|
||||||
//#define BUILD_YM3526 (HAS_YM3526)
|
|
||||||
//#define BUILD_Y8950 (HAS_Y8950)
|
|
||||||
|
|
||||||
/* --- system optimize --- */
|
|
||||||
/* select bit size of output : 8 or 16 */
|
|
||||||
#define OPL_OUTPUT_BIT 16
|
|
||||||
|
|
||||||
/* compiler dependence */
|
|
||||||
#ifndef OSD_CPU_H
|
|
||||||
#define OSD_CPU_H
|
|
||||||
typedef unsigned char UINT8; /* unsigned 8bit */
|
|
||||||
typedef unsigned short UINT16; /* unsigned 16bit */
|
|
||||||
typedef unsigned int UINT32; /* unsigned 32bit */
|
|
||||||
typedef signed char INT8; /* signed 8bit */
|
|
||||||
typedef signed short INT16; /* signed 16bit */
|
|
||||||
typedef signed int INT32; /* signed 32bit */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (OPL_OUTPUT_BIT==16)
|
|
||||||
typedef INT16 OPLSAMPLE;
|
|
||||||
#endif
|
|
||||||
#if (OPL_OUTPUT_BIT==8)
|
|
||||||
typedef unsigned char OPLSAMPLE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if BUILD_Y8950
|
|
||||||
#include "ymdeltat.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
||||||
typedef void (*OPL_IRQHANDLER)(int param,int irq);
|
|
||||||
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
|
|
||||||
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
|
|
||||||
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
|
|
||||||
|
|
||||||
/* !!!!! here is private section , do not access there member direct !!!!! */
|
/* !!!!! here is private section , do not access there member direct !!!!! */
|
||||||
|
|
||||||
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
|
|
||||||
#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
|
|
||||||
#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
|
|
||||||
#define OPL_TYPE_IO 0x08 /* I/O port */
|
|
||||||
|
|
||||||
/* Saving is necessary for member of the 'R' mark for suspend/resume */
|
/* Saving is necessary for member of the 'R' mark for suspend/resume */
|
||||||
/* ---------- OPL one of slot ---------- */
|
/* ---------- OPL one of slot ---------- */
|
||||||
typedef struct fm_opl_slot {
|
typedef struct fm_opl_slot {
|
||||||
INT32 TL; /* total level :TL << 8 */
|
int32_t TL; /* total level :TL << 8 */
|
||||||
INT32 TLL; /* adjusted now TL */
|
int32_t TLL; /* adjusted now TL */
|
||||||
UINT8 KSR; /* key scale rate :(shift down bit) */
|
uint8_t KSR; /* key scale rate :(shift down bit) */
|
||||||
INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
|
int32_t *AR; /* attack rate :&AR_TABLE[AR<<2] */
|
||||||
INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
|
int32_t *DR; /* decay rate :&DR_TALBE[DR<<2] */
|
||||||
INT32 SL; /* sustin level :SL_TALBE[SL] */
|
int32_t SL; /* sustin level :SL_TALBE[SL] */
|
||||||
INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
|
int32_t *RR; /* release rate :&DR_TABLE[RR<<2] */
|
||||||
UINT8 ksl; /* keyscale level :(shift down bits) */
|
uint8_t ksl; /* keyscale level :(shift down bits) */
|
||||||
UINT8 ksr; /* key scale rate :kcode>>KSR */
|
uint8_t ksr; /* key scale rate :kcode>>KSR */
|
||||||
UINT32 mul; /* multiple :ML_TABLE[ML] */
|
uint32_t mul; /* multiple :ML_TABLE[ML] */
|
||||||
UINT32 Cnt; /* frequency count : */
|
uint32_t Cnt; /* frequency count : */
|
||||||
UINT32 Incr; /* frequency step : */
|
uint32_t Incr; /* frequency step : */
|
||||||
/* envelope generator state */
|
/* envelope generator state */
|
||||||
UINT8 eg_typ; /* envelope type flag */
|
uint8_t eg_typ; /* envelope type flag */
|
||||||
UINT8 evm; /* envelope phase */
|
uint8_t evm; /* envelope phase */
|
||||||
INT32 evc; /* envelope counter */
|
int32_t evc; /* envelope counter */
|
||||||
INT32 eve; /* envelope counter end point */
|
int32_t eve; /* envelope counter end point */
|
||||||
INT32 evs; /* envelope counter step */
|
int32_t evs; /* envelope counter step */
|
||||||
INT32 evsa; /* envelope step for AR :AR[ksr] */
|
int32_t evsa; /* envelope step for AR :AR[ksr] */
|
||||||
INT32 evsd; /* envelope step for DR :DR[ksr] */
|
int32_t evsd; /* envelope step for DR :DR[ksr] */
|
||||||
INT32 evsr; /* envelope step for RR :RR[ksr] */
|
int32_t evsr; /* envelope step for RR :RR[ksr] */
|
||||||
/* LFO */
|
/* LFO */
|
||||||
UINT8 ams; /* ams flag */
|
uint8_t ams; /* ams flag */
|
||||||
UINT8 vib; /* vibrate flag */
|
uint8_t vib; /* vibrate flag */
|
||||||
/* wave selector */
|
/* wave selector */
|
||||||
INT32 **wavetable;
|
int32_t **wavetable;
|
||||||
}OPL_SLOT;
|
}OPL_SLOT;
|
||||||
|
|
||||||
/* ---------- OPL one of channel ---------- */
|
/* ---------- OPL one of channel ---------- */
|
||||||
typedef struct fm_opl_channel {
|
typedef struct fm_opl_channel {
|
||||||
OPL_SLOT SLOT[2];
|
OPL_SLOT SLOT[2];
|
||||||
UINT8 CON; /* connection type */
|
uint8_t CON; /* connection type */
|
||||||
UINT8 FB; /* feed back :(shift down bit) */
|
uint8_t FB; /* feed back :(shift down bit) */
|
||||||
INT32 *connect1; /* slot1 output pointer */
|
int32_t *connect1; /* slot1 output pointer */
|
||||||
INT32 *connect2; /* slot2 output pointer */
|
int32_t *connect2; /* slot2 output pointer */
|
||||||
INT32 op1_out[2]; /* slot1 output for selfeedback */
|
int32_t op1_out[2]; /* slot1 output for selfeedback */
|
||||||
/* phase generator state */
|
/* phase generator state */
|
||||||
UINT32 block_fnum; /* block+fnum : */
|
uint32_t block_fnum; /* block+fnum : */
|
||||||
UINT8 kcode; /* key code : KeyScaleCode */
|
uint8_t kcode; /* key code : KeyScaleCode */
|
||||||
UINT32 fc; /* Freq. Increment base */
|
uint32_t fc; /* Freq. Increment base */
|
||||||
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
uint32_t ksl_base; /* KeyScaleLevel Base step */
|
||||||
UINT8 keyon; /* key on/off flag */
|
uint8_t keyon; /* key on/off flag */
|
||||||
} OPL_CH;
|
} OPL_CH;
|
||||||
|
|
||||||
/* OPL state */
|
/* OPL state */
|
||||||
typedef struct fm_opl_f {
|
typedef struct fm_opl_f {
|
||||||
UINT8 type; /* chip type */
|
|
||||||
int clock; /* master clock (Hz) */
|
int clock; /* master clock (Hz) */
|
||||||
int rate; /* sampling rate (Hz) */
|
int rate; /* sampling rate (Hz) */
|
||||||
double freqbase; /* frequency base */
|
double freqbase; /* frequency base */
|
||||||
double TimerBase; /* Timer base time (==sampling time) */
|
double TimerBase; /* Timer base time (==sampling time) */
|
||||||
UINT8 address; /* address register */
|
uint8_t address; /* address register */
|
||||||
UINT8 status; /* status flag */
|
uint8_t status; /* status flag */
|
||||||
UINT8 statusmask; /* status mask */
|
uint8_t statusmask; /* status mask */
|
||||||
UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
|
uint32_t mode; /* Reg.08 : CSM , notesel,etc. */
|
||||||
/* Timer */
|
/* Timer */
|
||||||
int T[2]; /* timer counter */
|
int T[2]; /* timer counter */
|
||||||
UINT8 st[2]; /* timer enable */
|
uint8_t st[2]; /* timer enable */
|
||||||
/* FM channel slots */
|
/* FM channel slots */
|
||||||
OPL_CH *P_CH; /* pointer of CH */
|
OPL_CH *P_CH; /* pointer of CH */
|
||||||
int max_ch; /* maximum channel */
|
int max_ch; /* maximum channel */
|
||||||
/* Rhythm sention */
|
/* Rhythm sention */
|
||||||
UINT8 rhythm; /* Rhythm mode , key flag */
|
uint8_t rhythm; /* Rhythm mode , key flag */
|
||||||
#if BUILD_Y8950
|
|
||||||
/* Delta-T ADPCM unit (Y8950) */
|
|
||||||
YM_DELTAT *deltat; /* DELTA-T ADPCM */
|
|
||||||
#endif
|
|
||||||
/* Keyboard / I/O interface unit (Y8950) */
|
|
||||||
UINT8 portDirection;
|
|
||||||
UINT8 portLatch;
|
|
||||||
OPL_PORTHANDLER_R porthandler_r;
|
|
||||||
OPL_PORTHANDLER_W porthandler_w;
|
|
||||||
int port_param;
|
|
||||||
OPL_PORTHANDLER_R keyboardhandler_r;
|
|
||||||
OPL_PORTHANDLER_W keyboardhandler_w;
|
|
||||||
int keyboard_param;
|
|
||||||
/* time tables */
|
/* time tables */
|
||||||
INT32 AR_TABLE[75]; /* atttack rate tables */
|
int32_t AR_TABLE[75]; /* atttack rate tables */
|
||||||
INT32 DR_TABLE[75]; /* decay rate tables */
|
int32_t DR_TABLE[75]; /* decay rate tables */
|
||||||
UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
|
uint32_t FN_TABLE[1024]; /* fnumber -> increment counter */
|
||||||
/* LFO */
|
/* LFO */
|
||||||
INT32 *ams_table;
|
int32_t *ams_table;
|
||||||
INT32 *vib_table;
|
int32_t *vib_table;
|
||||||
INT32 amsCnt;
|
int32_t amsCnt;
|
||||||
INT32 amsIncr;
|
int32_t amsIncr;
|
||||||
INT32 vibCnt;
|
int32_t vibCnt;
|
||||||
INT32 vibIncr;
|
int32_t vibIncr;
|
||||||
/* wave selector enable flag */
|
/* wave selector enable flag */
|
||||||
UINT8 wavesel;
|
uint8_t wavesel;
|
||||||
/* external event callback handler */
|
/* external event callback handler */
|
||||||
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
|
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
|
||||||
int TimerParam; /* TIMER parameter */
|
int TimerParam; /* TIMER parameter */
|
||||||
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
|
|
||||||
int IRQParam; /* IRQ parameter */
|
|
||||||
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
|
|
||||||
int UpdateParam; /* stream update parameter */
|
|
||||||
} FM_OPL;
|
} FM_OPL;
|
||||||
|
|
||||||
/* ---------- Generic interface section ---------- */
|
/* ---------- Generic interface section ---------- */
|
||||||
#define OPL_TYPE_YM3526 (0)
|
FM_OPL *OPLCreate(int clock, int rate);
|
||||||
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
|
|
||||||
#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
|
|
||||||
|
|
||||||
FM_OPL *OPLCreate(int type, int clock, int rate);
|
|
||||||
void OPLDestroy(FM_OPL *OPL);
|
void OPLDestroy(FM_OPL *OPL);
|
||||||
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
|
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
|
||||||
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
|
|
||||||
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
|
|
||||||
/* Y8950 port handlers */
|
|
||||||
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
|
|
||||||
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
|
|
||||||
|
|
||||||
void OPLResetChip(FM_OPL *OPL);
|
|
||||||
int OPLWrite(FM_OPL *OPL,int a,int v);
|
int OPLWrite(FM_OPL *OPL,int a,int v);
|
||||||
unsigned char OPLRead(FM_OPL *OPL,int a);
|
unsigned char OPLRead(FM_OPL *OPL,int a);
|
||||||
int OPLTimerOver(FM_OPL *OPL,int c);
|
int OPLTimerOver(FM_OPL *OPL,int c);
|
||||||
|
|
||||||
/* YM3626/YM3812 local section */
|
void YM3812UpdateOne(FM_OPL *OPL, int16_t *buffer, int length);
|
||||||
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
|
||||||
|
|
||||||
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/audio/audio.h"
|
#include "hw/audio/soundhw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
#include "gusemu.h"
|
#include "gusemu.h"
|
||||||
@@ -53,7 +53,7 @@ typedef struct GUSState {
|
|||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
uint32_t port;
|
uint32_t port;
|
||||||
int pos, left, shift, irqs;
|
int pos, left, shift, irqs;
|
||||||
GUSsample *mixbuf;
|
int16_t *mixbuf;
|
||||||
uint8_t himem[1024 * 1024 + 32 + 4096];
|
uint8_t himem[1024 * 1024 + 32 + 4096];
|
||||||
int samples;
|
int samples;
|
||||||
SWVoiceOut *voice;
|
SWVoiceOut *voice;
|
||||||
|
|||||||
@@ -25,26 +25,10 @@
|
|||||||
#ifndef GUSEMU_H
|
#ifndef GUSEMU_H
|
||||||
#define GUSEMU_H
|
#define GUSEMU_H
|
||||||
|
|
||||||
/* data types (need to be adjusted if neither a VC6 nor a C99 compatible compiler is used) */
|
|
||||||
|
|
||||||
#if defined _WIN32 && defined _MSC_VER /* doesn't support other win32 compilers yet, do it yourself... */
|
|
||||||
typedef unsigned char GUSbyte;
|
|
||||||
typedef unsigned short GUSword;
|
|
||||||
typedef unsigned int GUSdword;
|
|
||||||
typedef signed char GUSchar;
|
|
||||||
typedef signed short GUSsample;
|
|
||||||
#else
|
|
||||||
typedef int8_t GUSchar;
|
|
||||||
typedef uint8_t GUSbyte;
|
|
||||||
typedef uint16_t GUSword;
|
|
||||||
typedef uint32_t GUSdword;
|
|
||||||
typedef int16_t GUSsample;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _GUSEmuState
|
typedef struct _GUSEmuState
|
||||||
{
|
{
|
||||||
GUSbyte *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */
|
uint8_t *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */
|
||||||
GUSbyte *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */
|
uint8_t *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */
|
||||||
uint32_t gusirq;
|
uint32_t gusirq;
|
||||||
uint32_t gusdma;
|
uint32_t gusdma;
|
||||||
unsigned int timer1fraction;
|
unsigned int timer1fraction;
|
||||||
@@ -92,7 +76,7 @@ void gus_dma_transferdata(GUSEmuState *state, char *dma_addr, unsigned int count
|
|||||||
/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */
|
/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */
|
||||||
/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */
|
/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */
|
||||||
|
|
||||||
void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, GUSsample *bufferpos);
|
void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, int16_t *bufferpos);
|
||||||
/* recommended range: 10 < numsamples < 100 */
|
/* recommended range: 10 < numsamples < 100 */
|
||||||
/* lower values may result in increased rounding error, higher values often cause audible timing delays */
|
/* lower values may result in increased rounding error, higher values often cause audible timing delays */
|
||||||
|
|
||||||
|
|||||||
@@ -31,15 +31,15 @@
|
|||||||
#include "gusemu.h"
|
#include "gusemu.h"
|
||||||
|
|
||||||
#define GUSregb(position) (* (gusptr+(position)))
|
#define GUSregb(position) (* (gusptr+(position)))
|
||||||
#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
|
#define GUSregw(position) (*(uint16_t *) (gusptr+(position)))
|
||||||
#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
|
#define GUSregd(position) (*(uint16_t *)(gusptr+(position)))
|
||||||
|
|
||||||
/* size given in bytes */
|
/* size given in bytes */
|
||||||
unsigned int gus_read(GUSEmuState * state, int port, int size)
|
unsigned int gus_read(GUSEmuState * state, int port, int size)
|
||||||
{
|
{
|
||||||
int value_read = 0;
|
int value_read = 0;
|
||||||
|
|
||||||
GUSbyte *gusptr;
|
uint8_t *gusptr;
|
||||||
gusptr = state->gusdatapos;
|
gusptr = state->gusdatapos;
|
||||||
GUSregd(portaccesses)++;
|
GUSregd(portaccesses)++;
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ unsigned int gus_read(GUSEmuState * state, int port, int size)
|
|||||||
if (!GUSregb(IRQStatReg2x6))
|
if (!GUSregb(IRQStatReg2x6))
|
||||||
GUS_irqclear(state, state->gusirq);
|
GUS_irqclear(state, state->gusirq);
|
||||||
}
|
}
|
||||||
return (GUSbyte) value_read;
|
return (uint8_t) value_read;
|
||||||
/* DramDMAmemPosReg */
|
/* DramDMAmemPosReg */
|
||||||
/* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
|
/* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
|
||||||
/* 43h+44h write only */
|
/* 43h+44h write only */
|
||||||
@@ -173,12 +173,12 @@ unsigned int gus_read(GUSEmuState * state, int port, int size)
|
|||||||
value_read = value_read >> 8;
|
value_read = value_read >> 8;
|
||||||
value_read &= 0xff;
|
value_read &= 0xff;
|
||||||
}
|
}
|
||||||
return (GUSword) value_read;
|
return (uint16_t) value_read;
|
||||||
/* case 0x306: */ /* Mixer/Version info */
|
/* case 0x306: */ /* Mixer/Version info */
|
||||||
/* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
|
/* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
|
||||||
case 0x307: /* DRAMaccess */
|
case 0x307: /* DRAMaccess */
|
||||||
{
|
{
|
||||||
GUSbyte *adr;
|
uint8_t *adr;
|
||||||
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
|
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
|
||||||
return *adr;
|
return *adr;
|
||||||
}
|
}
|
||||||
@@ -189,14 +189,14 @@ unsigned int gus_read(GUSEmuState * state, int port, int size)
|
|||||||
|
|
||||||
void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
||||||
{
|
{
|
||||||
GUSbyte *gusptr;
|
uint8_t *gusptr;
|
||||||
gusptr = state->gusdatapos;
|
gusptr = state->gusdatapos;
|
||||||
GUSregd(portaccesses)++;
|
GUSregd(portaccesses)++;
|
||||||
|
|
||||||
switch (port & 0xff0f)
|
switch (port & 0xff0f)
|
||||||
{
|
{
|
||||||
case 0x200: /* MixerCtrlReg */
|
case 0x200: /* MixerCtrlReg */
|
||||||
GUSregb(MixerCtrlReg2x0) = (GUSbyte) data;
|
GUSregb(MixerCtrlReg2x0) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x206: /* IRQstatReg / SB2x6IRQ */
|
case 0x206: /* IRQstatReg / SB2x6IRQ */
|
||||||
if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
|
if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
|
||||||
@@ -208,7 +208,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
break;
|
break;
|
||||||
case 0x308: /* AdLib 388h */
|
case 0x308: /* AdLib 388h */
|
||||||
case 0x208: /* AdLibCommandReg */
|
case 0x208: /* AdLibCommandReg */
|
||||||
GUSregb(AdLibCommand2xA) = (GUSbyte) data;
|
GUSregb(AdLibCommand2xA) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x309: /* AdLib 389h */
|
case 0x309: /* AdLib 389h */
|
||||||
case 0x209: /* AdLibDataReg */
|
case 0x209: /* AdLibDataReg */
|
||||||
@@ -217,11 +217,11 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
if (data & 0x80)
|
if (data & 0x80)
|
||||||
GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
|
GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
|
||||||
else
|
else
|
||||||
GUSregb(TimerDataReg2x9) = (GUSbyte) data;
|
GUSregb(TimerDataReg2x9) = (uint8_t) data;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUSregb(AdLibData2x9) = (GUSbyte) data;
|
GUSregb(AdLibData2x9) = (uint8_t) data;
|
||||||
if (GUSregb(GUS45TimerCtrl) & 0x02)
|
if (GUSregb(GUS45TimerCtrl) & 0x02)
|
||||||
{
|
{
|
||||||
GUSregb(TimerStatus2x8) |= 0x01;
|
GUSregb(TimerStatus2x8) |= 0x01;
|
||||||
@@ -231,16 +231,16 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x20A:
|
case 0x20A:
|
||||||
GUSregb(AdLibStatus2x8) = (GUSbyte) data;
|
GUSregb(AdLibStatus2x8) = (uint8_t) data;
|
||||||
break; /* AdLibStatus2x8 */
|
break; /* AdLibStatus2x8 */
|
||||||
case 0x20B: /* GUS hidden registers */
|
case 0x20B: /* GUS hidden registers */
|
||||||
switch (GUSregb(RegCtrl_2xF) & 0x7)
|
switch (GUSregb(RegCtrl_2xF) & 0x7)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if (GUSregb(MixerCtrlReg2x0) & 0x40)
|
if (GUSregb(MixerCtrlReg2x0) & 0x40)
|
||||||
GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */
|
GUSregb(IRQ_2xB) = (uint8_t) data; /* control register select bit */
|
||||||
else
|
else
|
||||||
GUSregb(DMA_2xB) = (GUSbyte) data;
|
GUSregb(DMA_2xB) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
/* case 1-4: general purpose emulation regs */
|
/* case 1-4: general purpose emulation regs */
|
||||||
case 5: /* clear stat reg 2xF */
|
case 5: /* clear stat reg 2xF */
|
||||||
@@ -249,7 +249,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
GUS_irqclear(state, state->gusirq);
|
GUS_irqclear(state, state->gusirq);
|
||||||
break;
|
break;
|
||||||
case 6: /* Jumper reg (Joystick/MIDI enable) */
|
case 6: /* Jumper reg (Joystick/MIDI enable) */
|
||||||
GUSregb(Jumper_2xB) = (GUSbyte) data;
|
GUSregb(Jumper_2xB) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
@@ -262,20 +262,20 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
GUS_irqrequest(state, state->gusirq, 1);
|
GUS_irqrequest(state, state->gusirq, 1);
|
||||||
}
|
}
|
||||||
case 0x20D: /* SB2xCd no IRQ */
|
case 0x20D: /* SB2xCd no IRQ */
|
||||||
GUSregb(SB2xCd) = (GUSbyte) data;
|
GUSregb(SB2xCd) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x20E: /* SB2xE */
|
case 0x20E: /* SB2xE */
|
||||||
GUSregb(SB2xE) = (GUSbyte) data;
|
GUSregb(SB2xE) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x20F:
|
case 0x20F:
|
||||||
GUSregb(RegCtrl_2xF) = (GUSbyte) data;
|
GUSregb(RegCtrl_2xF) = (uint8_t) data;
|
||||||
break; /* CtrlReg2xF */
|
break; /* CtrlReg2xF */
|
||||||
case 0x302: /* VoiceSelReg */
|
case 0x302: /* VoiceSelReg */
|
||||||
GUSregb(VoiceSelReg3x2) = (GUSbyte) data;
|
GUSregb(VoiceSelReg3x2) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x303: /* FunkSelReg */
|
case 0x303: /* FunkSelReg */
|
||||||
GUSregb(FunkSelReg3x3) = (GUSbyte) data;
|
GUSregb(FunkSelReg3x3) = (uint8_t) data;
|
||||||
if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
|
if ((uint8_t) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
|
||||||
{
|
{
|
||||||
int voice;
|
int voice;
|
||||||
if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
|
if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
|
||||||
@@ -318,15 +318,15 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
case 0x304:
|
case 0x304:
|
||||||
case 0x305:
|
case 0x305:
|
||||||
{
|
{
|
||||||
GUSword writedata = (GUSword) data;
|
uint16_t writedata = (uint16_t) data;
|
||||||
GUSword readmask = 0x0000;
|
uint16_t readmask = 0x0000;
|
||||||
if (size == 1)
|
if (size == 1)
|
||||||
{
|
{
|
||||||
readmask = 0xff00;
|
readmask = 0xff00;
|
||||||
writedata &= 0xff;
|
writedata &= 0xff;
|
||||||
if ((port & 0xff0f) == 0x305)
|
if ((port & 0xff0f) == 0x305)
|
||||||
{
|
{
|
||||||
writedata = (GUSword) (writedata << 8);
|
writedata = (uint16_t) (writedata << 8);
|
||||||
readmask = 0x00ff;
|
readmask = 0x00ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,17 +353,17 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
break; /* reset flag active? */
|
break; /* reset flag active? */
|
||||||
offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
|
offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
|
||||||
offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
|
offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
|
||||||
GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata);
|
GUSregw(offset) = (uint16_t) ((GUSregw(offset) & readmask) | writedata);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* voice unspecific functions */
|
/* voice unspecific functions */
|
||||||
case 0x0e: /* NumVoices */
|
case 0x0e: /* NumVoices */
|
||||||
GUSregb(NumVoices) = (GUSbyte) data;
|
GUSregb(NumVoices) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
/* case 0x0f: */ /* read only */
|
/* case 0x0f: */ /* read only */
|
||||||
/* common functions */
|
/* common functions */
|
||||||
case 0x41: /* DramDMAContrReg */
|
case 0x41: /* DramDMAContrReg */
|
||||||
GUSregb(GUS41DMACtrl) = (GUSbyte) data;
|
GUSregb(GUS41DMACtrl) = (uint8_t) data;
|
||||||
if (data & 0x01)
|
if (data & 0x01)
|
||||||
GUS_dmarequest(state);
|
GUS_dmarequest(state);
|
||||||
break;
|
break;
|
||||||
@@ -380,7 +380,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
(GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
|
(GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
|
||||||
break;
|
break;
|
||||||
case 0x45: /* TCtrlReg */
|
case 0x45: /* TCtrlReg */
|
||||||
GUSregb(GUS45TimerCtrl) = (GUSbyte) data;
|
GUSregb(GUS45TimerCtrl) = (uint8_t) data;
|
||||||
if (!(data & 0x20))
|
if (!(data & 0x20))
|
||||||
GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
|
GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
|
||||||
if (!(data & 0x02))
|
if (!(data & 0x02))
|
||||||
@@ -434,18 +434,18 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
GUS_irqclear(state, state->gusirq);
|
GUS_irqclear(state, state->gusirq);
|
||||||
break;
|
break;
|
||||||
case 0x46: /* Counter1 */
|
case 0x46: /* Counter1 */
|
||||||
GUSregb(GUS46Counter1) = (GUSbyte) data;
|
GUSregb(GUS46Counter1) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
case 0x47: /* Counter2 */
|
case 0x47: /* Counter2 */
|
||||||
GUSregb(GUS47Counter2) = (GUSbyte) data;
|
GUSregb(GUS47Counter2) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
/* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */
|
/* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */
|
||||||
case 0x49: /* SampCtrlReg */
|
case 0x49: /* SampCtrlReg */
|
||||||
GUSregb(GUS49SampCtrl) = (GUSbyte) data;
|
GUSregb(GUS49SampCtrl) = (uint8_t) data;
|
||||||
break;
|
break;
|
||||||
/* case 0x4b: */ /* joystick trim not emulated */
|
/* case 0x4b: */ /* joystick trim not emulated */
|
||||||
case 0x4c: /* GUSreset */
|
case 0x4c: /* GUSreset */
|
||||||
GUSregb(GUS4cReset) = (GUSbyte) data;
|
GUSregb(GUS4cReset) = (uint8_t) data;
|
||||||
if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
|
if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
|
||||||
{
|
{
|
||||||
GUSregd(voicewavetableirq) = 0;
|
GUSregd(voicewavetableirq) = 0;
|
||||||
@@ -471,9 +471,9 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
|||||||
break;
|
break;
|
||||||
case 0x307: /* DRAMaccess */
|
case 0x307: /* DRAMaccess */
|
||||||
{
|
{
|
||||||
GUSbyte *adr;
|
uint8_t *adr;
|
||||||
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
|
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
|
||||||
*adr = (GUSbyte) data;
|
*adr = (uint8_t) data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -510,7 +510,7 @@ void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int coun
|
|||||||
char *srcaddr;
|
char *srcaddr;
|
||||||
char *destaddr;
|
char *destaddr;
|
||||||
char msbmask = 0;
|
char msbmask = 0;
|
||||||
GUSbyte *gusptr;
|
uint8_t *gusptr;
|
||||||
gusptr = state->gusdatapos;
|
gusptr = state->gusdatapos;
|
||||||
|
|
||||||
srcaddr = dma_addr; /* system memory address */
|
srcaddr = dma_addr; /* system memory address */
|
||||||
@@ -521,8 +521,8 @@ void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int coun
|
|||||||
destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */
|
destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */
|
||||||
}
|
}
|
||||||
|
|
||||||
GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */
|
GUSregw(GUS42DMAStart) += (uint16_t) (count >> 4); /* ToDo: add 16bit GUS page limit? */
|
||||||
GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
|
GUSregb(GUS50DMAHigh) = (uint8_t) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
|
||||||
|
|
||||||
if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */
|
if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,26 +27,26 @@
|
|||||||
#include "gustate.h"
|
#include "gustate.h"
|
||||||
|
|
||||||
#define GUSregb(position) (* (gusptr+(position)))
|
#define GUSregb(position) (* (gusptr+(position)))
|
||||||
#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
|
#define GUSregw(position) (*(uint16_t *) (gusptr+(position)))
|
||||||
#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
|
#define GUSregd(position) (*(uint16_t *)(gusptr+(position)))
|
||||||
|
|
||||||
#define GUSvoice(position) (*(GUSword *)(voiceptr+(position)))
|
#define GUSvoice(position) (*(uint16_t *)(voiceptr+(position)))
|
||||||
|
|
||||||
/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
|
/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
|
||||||
void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
|
void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
|
||||||
GUSsample *bufferpos)
|
int16_t *bufferpos)
|
||||||
{
|
{
|
||||||
/* note that byte registers are stored in the upper half of each voice register! */
|
/* note that byte registers are stored in the upper half of each voice register! */
|
||||||
GUSbyte *gusptr;
|
uint8_t *gusptr;
|
||||||
int Voice;
|
int Voice;
|
||||||
GUSword *voiceptr;
|
uint16_t *voiceptr;
|
||||||
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
for (count = 0; count < numsamples * 2; count++)
|
for (count = 0; count < numsamples * 2; count++)
|
||||||
*(bufferpos + count) = 0; /* clear */
|
*(bufferpos + count) = 0; /* clear */
|
||||||
|
|
||||||
gusptr = state->gusdatapos;
|
gusptr = state->gusdatapos;
|
||||||
voiceptr = (GUSword *) gusptr;
|
voiceptr = (uint16_t *) gusptr;
|
||||||
if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */
|
if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -85,16 +85,16 @@ void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int
|
|||||||
if (GUSvoice(wVSRControl) & 0x400) /* 16bit */
|
if (GUSvoice(wVSRControl) & 0x400) /* 16bit */
|
||||||
{
|
{
|
||||||
int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
|
int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
|
||||||
GUSchar *adr;
|
int8_t *adr;
|
||||||
adr = (GUSchar *) state->himemaddr + offset;
|
adr = (int8_t *) state->himemaddr + offset;
|
||||||
sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
|
sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
|
||||||
sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
|
sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
|
||||||
}
|
}
|
||||||
else /* 8bit */
|
else /* 8bit */
|
||||||
{
|
{
|
||||||
int offset = (CurrPos >> 9) & 0xfffff;
|
int offset = (CurrPos >> 9) & 0xfffff;
|
||||||
GUSchar *adr;
|
int8_t *adr;
|
||||||
adr = (GUSchar *) state->himemaddr + offset;
|
adr = (int8_t *) state->himemaddr + offset;
|
||||||
sample1 = (*adr) * 256;
|
sample1 = (*adr) * 256;
|
||||||
sample2 = (*(adr + 1)) * 256;
|
sample2 = (*(adr + 1)) * 256;
|
||||||
}
|
}
|
||||||
@@ -171,8 +171,8 @@ void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* mix samples into buffer */
|
/* mix samples into buffer */
|
||||||
*(bufferpos + 2 * sample) += (GUSsample) ((sample1 * PanningPos) >> 4); /* right */
|
*(bufferpos + 2 * sample) += (int16_t) ((sample1 * PanningPos) >> 4); /* right */
|
||||||
*(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */
|
*(bufferpos + 2 * sample + 1) += (int16_t) ((sample1 * (15 - PanningPos)) >> 4); /* left */
|
||||||
}
|
}
|
||||||
/* write back voice and volume */
|
/* write back voice and volume */
|
||||||
GUSvoice(wVSRCurrVol) = Volume32 / 32;
|
GUSvoice(wVSRCurrVol) = Volume32 / 32;
|
||||||
@@ -187,7 +187,7 @@ void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
|
|||||||
/* time given in microseconds */
|
/* time given in microseconds */
|
||||||
{
|
{
|
||||||
int requestedIRQs = 0;
|
int requestedIRQs = 0;
|
||||||
GUSbyte *gusptr;
|
uint8_t *gusptr;
|
||||||
gusptr = state->gusdatapos;
|
gusptr = state->gusdatapos;
|
||||||
if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
|
if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user