Compare commits
764 Commits
pull-ui-20
...
queue/ui+i
Author | SHA1 | Date | |
---|---|---|---|
|
6e24ee0c1e | ||
|
954ee55bd5 | ||
|
8498bb8d2e | ||
|
85970a627f | ||
|
51dbea77a2 | ||
|
77b0359bf4 | ||
|
d3b787fa7d | ||
|
e18a639164 | ||
|
469819a3e8 | ||
|
22a9e1fd63 | ||
|
84e3d0725b | ||
|
db7a99cdc1 | ||
|
32b9ca9868 | ||
|
2a747008cb | ||
|
92bd1e465b | ||
|
492734b5da | ||
|
d2a44865e8 | ||
|
a825ca0613 | ||
|
a08fc2f8cc | ||
|
c34647c18a | ||
|
2e1d6bdcce | ||
|
c9c06eb832 | ||
|
905bf0ee8a | ||
|
32809e7f7b | ||
|
8a98bfc6e3 | ||
|
8a48be0e87 | ||
|
96e659d006 | ||
|
cf80eb8d09 | ||
|
2c1c31ed55 | ||
|
438d116872 | ||
|
4c84f662c2 | ||
|
2499ee9fad | ||
|
7af25f9f6a | ||
|
896b6757f9 | ||
|
95e92000c8 | ||
|
3f8f1313e0 | ||
|
a4f113fd69 | ||
|
371c4ef637 | ||
|
d8dc67e119 | ||
|
6fafc26014 | ||
|
8dfaf23ae1 | ||
|
e85c0d1401 | ||
|
65a0e3e842 | ||
|
7e56accdaf | ||
|
269c20b2bb | ||
|
ad664c1d4c | ||
|
709fa704f6 | ||
|
61d7c14437 | ||
|
9848619a3b | ||
|
07fe095452 | ||
|
1eb4243434 | ||
|
77a7a36760 | ||
|
4ccd89d294 | ||
|
5d7fb0f254 | ||
|
c7b4efb4a0 | ||
|
1ea1572adf | ||
|
605553654f | ||
|
b81bdbf3c7 | ||
|
35f91e5069 | ||
|
6d13643a3a | ||
|
c5c6c47ce3 | ||
|
19e9cdf040 | ||
|
c03d83d55a | ||
|
b053ef6106 | ||
|
9ed442b8ae | ||
|
822335eb51 | ||
|
446de8b68a | ||
|
d528227d4c | ||
|
3fb2111fc9 | ||
|
76318657a8 | ||
|
85bbd1e7a4 | ||
|
1e507bb0fd | ||
|
d015c4ea6f | ||
|
3152779cd6 | ||
|
5923f85fb8 | ||
|
2bc7cfea09 | ||
|
61a8f418b2 | ||
|
36aeb6094f | ||
|
60390d2dc8 | ||
|
01b2ffcedd | ||
|
5135a1056d | ||
|
5837aaac25 | ||
|
8da54b2507 | ||
|
542f70c22e | ||
|
54e1d4ed1d | ||
|
b97a879de9 | ||
|
308714e6bc | ||
|
9c39b94f14 | ||
|
acb0b292b6 | ||
|
3fb53fb4d1 | ||
|
cc74d332ff | ||
|
2b48e10f88 | ||
|
6e3b2bfd6a | ||
|
b255b2c8a5 | ||
|
30ff7d1d0b | ||
|
cef8fd6836 | ||
|
a1fbe750fd | ||
|
58634047b7 | ||
|
c1214ad3dc | ||
|
7c877c8030 | ||
|
560f19f162 | ||
|
a2740ad584 | ||
|
7f3cf2d6e7 | ||
|
e7a3b91fdf | ||
|
b9313021f3 | ||
|
7feb51b709 | ||
|
e691ef6991 | ||
|
8bbf4aa96e | ||
|
2f295167e0 | ||
|
5b50bf77ce | ||
|
9caa6f3dbe | ||
|
39c1b4254e | ||
|
b64bd51efa | ||
|
c0bad49946 | ||
|
2119882c7e | ||
|
3783fa3dd3 | ||
|
47fec59941 | ||
|
f7946da274 | ||
|
ae2d489c34 | ||
|
93001e9d87 | ||
|
3b170dc867 | ||
|
7258ed930c | ||
|
850d54a2a9 | ||
|
e2a6ae7fe5 | ||
|
20fc71b25c | ||
|
d993b85804 | ||
|
414c2ec358 | ||
|
d3faa13e5f | ||
|
79f24568e5 | ||
|
80c58a5b1b | ||
|
73a27bbb69 | ||
|
f9f65a4af0 | ||
|
edf8bc9842 | ||
|
49cc0340f8 | ||
|
f12c1ebddf | ||
|
44cb280d33 | ||
|
d59157ea05 | ||
|
067b913619 | ||
|
e947738e38 | ||
|
8c372a02e0 | ||
|
2e5c9ad6f4 | ||
|
d9faeed854 | ||
|
7798d3aab9 | ||
|
c84087f2f5 | ||
|
ee898b870f | ||
|
2a6e128bfa | ||
|
572b97e722 | ||
|
a0dc63a6b7 | ||
|
2b0bbc4f88 | ||
|
44298024d3 | ||
|
d1fdf257d5 | ||
|
92229a57bb | ||
|
244f144134 | ||
|
d9bb58e510 | ||
|
a9ded6017e | ||
|
041e32b8d9 | ||
|
0c9390d978 | ||
|
457e03559d | ||
|
1d78a3c3ab | ||
|
a20fa79fa5 | ||
|
428952cfa9 | ||
|
87e459a810 | ||
|
b356807fcd | ||
|
36c327a69d | ||
|
5104fac853 | ||
|
24c0c77af5 | ||
|
134550bf81 | ||
|
660174fc1b | ||
|
6b9911d0b6 | ||
|
8381d89bec | ||
|
fea617c58b | ||
|
38b3362dd1 | ||
|
8d37b030fe | ||
|
e45e7ae281 | ||
|
73aa4692ec | ||
|
69e698220f | ||
|
c88f8107b1 | ||
|
e5b0cbe8e8 | ||
|
043b936ef6 | ||
|
5c3ad1a6a8 | ||
|
91589d9e5c | ||
|
3416ab5bb4 | ||
|
edc60127e4 | ||
|
1adc1ceef7 | ||
|
62a0265852 | ||
|
68a4a2fda1 | ||
|
fe5c44f9c9 | ||
|
8f4ea9cd0b | ||
|
5fe309ff0d | ||
|
bfefa6d7d6 | ||
|
af8862b2a2 | ||
|
076d4d39b6 | ||
|
becf8217de | ||
|
465aec4617 | ||
|
3f0602927b | ||
|
252a7a6a96 | ||
|
d5aa0c229a | ||
|
cddafd8f35 | ||
|
556969e938 | ||
|
1403f36447 | ||
|
a87e81b9b5 | ||
|
fe3874b6a1 | ||
|
d1bb099f63 | ||
|
a14f9b8292 | ||
|
5f7f22ffe1 | ||
|
9e883790dd | ||
|
310150c000 | ||
|
a2f2f6249b | ||
|
986924f875 | ||
|
54ab9927d1 | ||
|
92e5d7e222 | ||
|
ee78356eba | ||
|
6f153ceb9b | ||
|
b097efc002 | ||
|
79cad8b46b | ||
|
735286a4f8 | ||
|
e0b4891ae6 | ||
|
9746211baa | ||
|
ad3c5412f2 | ||
|
8e3cf49c47 | ||
|
6666c96aac | ||
|
c4b63b7cc5 | ||
|
84a899de8c | ||
|
2ce3bf1aa9 | ||
|
da6f17903f | ||
|
c3d2e2e76c | ||
|
b7722747e4 | ||
|
f2a8f0a631 | ||
|
f8d806c992 | ||
|
543147116e | ||
|
c8f9f4f402 | ||
|
930ac04c22 | ||
|
250561e1ae | ||
|
0425dc9762 | ||
|
f4f3082b0c | ||
|
9bba618f18 | ||
|
d54fddea98 | ||
|
5093f028ce | ||
|
2a8469aaab | ||
|
475df9d809 | ||
|
56faeb9bb6 | ||
|
719fc28c80 | ||
|
272545cf21 | ||
|
c3971b883a | ||
|
19ebd13ed4 | ||
|
49695eeb74 | ||
|
362fdf170c | ||
|
f07fa4cbf0 | ||
|
93c26503e0 | ||
|
593080936a | ||
|
b1fd36c363 | ||
|
9ed656631d | ||
|
100f738850 | ||
|
ad265631c0 | ||
|
3b95410507 | ||
|
bc277a52fb | ||
|
20fdef58a0 | ||
|
277238f9f4 | ||
|
27d4c3789d | ||
|
73119c2864 | ||
|
d203c64398 | ||
|
4871dd4c3f | ||
|
a4d4edce7a | ||
|
67b544d65f | ||
|
7980833619 | ||
|
6304fd27ef | ||
|
0be4e88621 | ||
|
cd74d27e42 | ||
|
7b7258f810 | ||
|
454b580ae9 | ||
|
f224d35be9 | ||
|
2c5534776b | ||
|
8a9e0e7b89 | ||
|
2d3e302ec2 | ||
|
ec69355bef | ||
|
bbfa326fc8 | ||
|
ac06724a71 | ||
|
90bb0c0421 | ||
|
d870cfdea5 | ||
|
6bdcc018a6 | ||
|
b8158192fa | ||
|
d45fc087c2 | ||
|
c25a67f0c3 | ||
|
7e01838510 | ||
|
e2b6c1712e | ||
|
df8ad9f128 | ||
|
ad9579aaa1 | ||
|
5b003a40bb | ||
|
f8c45c6550 | ||
|
c8bc83a4dd | ||
|
64175afc69 | ||
|
11cde1c810 | ||
|
b55a69fe5f | ||
|
528f449f59 | ||
|
3a586d2f0b | ||
|
f652402487 | ||
|
462e5d5065 | ||
|
249e9f792c | ||
|
0db1851bec | ||
|
b187e2b530 | ||
|
18059c9e16 | ||
|
8b3e9ca74c | ||
|
eefff991d0 | ||
|
53518d9448 | ||
|
9360447d34 | ||
|
c00e092832 | ||
|
83c13382e4 | ||
|
338182c83c | ||
|
114f5aee02 | ||
|
6701e5514b | ||
|
4cee3cf35c | ||
|
49921d6886 | ||
|
3190dfc5e1 | ||
|
c0080f1bdb | ||
|
c21b610f58 | ||
|
e22dfdb28d | ||
|
4065ae7634 | ||
|
5d4a655a41 | ||
|
1541778721 | ||
|
1a35f08a22 | ||
|
4e256bef65 | ||
|
3bd3d6d302 | ||
|
16f2e4b841 | ||
|
31006af3bb | ||
|
d332712134 | ||
|
22f04c3198 | ||
|
5c2b48a8f0 | ||
|
84aa07f109 | ||
|
29a58fd85f | ||
|
a65047afe5 | ||
|
a5c3cedd73 | ||
|
01f8db8857 | ||
|
fdc0a7474a | ||
|
256dab6fe8 | ||
|
6c9deca8a1 | ||
|
9c8be59836 | ||
|
76c574906e | ||
|
0c0974d785 | ||
|
6699adfc18 | ||
|
1f58720c5f | ||
|
8a4719f527 | ||
|
51a718bf3d | ||
|
fc7fbcbc48 | ||
|
d376f123c7 | ||
|
303c681a8f | ||
|
06fc03486c | ||
|
99e57856f6 | ||
|
b26de9518d | ||
|
31a18b4575 | ||
|
a72da8b7f5 | ||
|
8350079329 | ||
|
b90fb26bde | ||
|
b157fbe6a9 | ||
|
1f3ca41665 | ||
|
aef2b01a50 | ||
|
75d6240c59 | ||
|
1b642a732c | ||
|
97ae2149af | ||
|
2c7e5f8c25 | ||
|
d46cd62ff8 | ||
|
981a8ea0c5 | ||
|
84e1b98ba6 | ||
|
498644e99f | ||
|
4546137957 | ||
|
453e4c077d | ||
|
7390fb79fd | ||
|
44cf6c2e4b | ||
|
9393c020bf | ||
|
08a4cb793f | ||
|
7cf96fca4c | ||
|
3cc8ca3dab | ||
|
4663e82244 | ||
|
868b5cbd91 | ||
|
e79f56f4d6 | ||
|
d3696812e3 | ||
|
9c009e88e3 | ||
|
6fc2606e58 | ||
|
349d078a26 | ||
|
a5cfc2235b | ||
|
23cf9659b4 | ||
|
f79f1ca4a2 | ||
|
2cbe2de545 | ||
|
003a0cf2cd | ||
|
be41c100c0 | ||
|
e44ed99d19 | ||
|
f260956536 | ||
|
f5d406fe86 | ||
|
f250a42dda | ||
|
7e6478e7d4 | ||
|
fd56356422 | ||
|
993b1f4b2c | ||
|
9ba35d0b86 | ||
|
bd618eab76 | ||
|
e0c8b950d1 | ||
|
388ad5d296 | ||
|
4aa70a0e9c | ||
|
369b41359a | ||
|
9a6e2dcfdd | ||
|
65dfad62a1 | ||
|
572db7cd69 | ||
|
e02bbe1956 | ||
|
dd7b952b79 | ||
|
8128b3e079 | ||
|
347ec03093 | ||
|
30c2afd151 | ||
|
a65afaae0f | ||
|
fbe8202ea8 | ||
|
64bc98f4b9 | ||
|
c68f4503e0 | ||
|
4e19b57b0e | ||
|
8ed179c937 | ||
|
c645d5acee | ||
|
a1af255f06 | ||
|
8ee47a886f | ||
|
f6f55affd1 | ||
|
91dcb1ffa6 | ||
|
1693ea1685 | ||
|
b8fdd530be | ||
|
fbf5539718 | ||
|
2d33581899 | ||
|
a32e900b8a | ||
|
c4e13492af | ||
|
7032d92ac8 | ||
|
0524951788 | ||
|
60694bc678 | ||
|
a09f7443bc | ||
|
0b55aa91c9 | ||
|
4f65ce00ab | ||
|
88af6ea568 | ||
|
b89b3d3929 | ||
|
75e972dab5 | ||
|
1b6e748246 | ||
|
23ea4f3032 | ||
|
b2b8d98675 | ||
|
37bbcd5757 | ||
|
99861ecbc5 | ||
|
15f8b14228 | ||
|
f75cd44de0 | ||
|
d41f3e750d | ||
|
60bed6a30a | ||
|
a0ceb640d0 | ||
|
1f43571604 | ||
|
a0d4aac746 | ||
|
2d826cdc8a | ||
|
bec5e2b975 | ||
|
e350d8ca3a | ||
|
d9a9acde64 | ||
|
e75449a346 | ||
|
e78722368c | ||
|
4137cb83fa | ||
|
6350001e83 | ||
|
5786e0683c | ||
|
085c648bef | ||
|
702a947484 | ||
|
46644483ca | ||
|
38f81dc593 | ||
|
b19f0c2e7d | ||
|
0c240785a8 | ||
|
6f1653180f | ||
|
b4aa297781 | ||
|
fe62089563 | ||
|
1ebb1af1b8 | ||
|
8a6b28c7b5 | ||
|
7ad55b4ffd | ||
|
5cb4ef80f6 | ||
|
cedbcb0152 | ||
|
374aae6534 | ||
|
f1079bb8f9 | ||
|
1639a965d3 | ||
|
199e19ee53 | ||
|
03e947f9c2 | ||
|
cb8b8ef457 | ||
|
22c3aea8db | ||
|
543f8f13e2 | ||
|
f603164ae4 | ||
|
7064024dee | ||
|
3975bb56a7 | ||
|
244d04db58 | ||
|
2283adfb0a | ||
|
016b4a93e7 | ||
|
1f6fb58d05 | ||
|
bb0b6f39c0 | ||
|
95e9a242e2 | ||
|
f85d66f47f | ||
|
e8758b6229 | ||
|
cc16ee9d4e | ||
|
271f37abb5 | ||
|
7c933ad61b | ||
|
7a0bbd55e5 | ||
|
1c9f3b887b | ||
|
2f8d8f01c8 | ||
|
3f2ce724f1 | ||
|
c6e84fbd44 | ||
|
6dcdd06e3b | ||
|
4bbeeba023 | ||
|
2152f3fead | ||
|
020e571b8b | ||
|
fc58bd0d97 | ||
|
46764fe09c | ||
|
b0ac429f13 | ||
|
f811f97040 | ||
|
e32fb6da7e | ||
|
df3a429ae8 | ||
|
1448228af3 | ||
|
d47a851cae | ||
|
7693cd7cb6 | ||
|
c7637c04be | ||
|
94a66456f1 | ||
|
2cf6cb500c | ||
|
4960f084cf | ||
|
d0efdc1686 | ||
|
ddabca757a | ||
|
3bef701256 | ||
|
29c483a506 | ||
|
5dd0641d23 | ||
|
790a11503c | ||
|
3a00d560bc | ||
|
c9f9f1246d | ||
|
e9235c6983 | ||
|
06312febfb | ||
|
f50cd31413 | ||
|
452a095526 | ||
|
e7b921c2d9 | ||
|
8bd5c82030 | ||
|
e517d95b63 | ||
|
2b3ffa9292 | ||
|
a89ff39ee9 | ||
|
8193d4617c | ||
|
f5dc1b7767 | ||
|
a18e93125d | ||
|
993063fbb5 | ||
|
6b10e573d1 | ||
|
1ce2610c10 | ||
|
a9b1ca38c2 | ||
|
4d43a603c7 | ||
|
c90e9392ef | ||
|
93a78e4124 | ||
|
7566c6efe7 | ||
|
8228e353d8 | ||
|
f664b88247 | ||
|
541815ff7f | ||
|
ef0f272f38 | ||
|
221e659c3f | ||
|
6ce8e0eb58 | ||
|
b88ee02594 | ||
|
c7e47c63e0 | ||
|
78fb261db1 | ||
|
dc8b6dd984 | ||
|
4410b94cce | ||
|
c04e34a982 | ||
|
46eedc0e69 | ||
|
1c958ad300 | ||
|
8c612079e0 | ||
|
b229a5765b | ||
|
2c9e6fec89 | ||
|
7b1e1a2202 | ||
|
5e22479ae2 | ||
|
e1a3ecee3b | ||
|
41d64227ed | ||
|
61e8b14880 | ||
|
7fcac4a2cc | ||
|
f4dbe1bf34 | ||
|
08a0aee15c | ||
|
107da9acb5 | ||
|
660819b1df | ||
|
3482655bbc | ||
|
3a011c26bc | ||
|
c2355ad47d | ||
|
0f42f65781 | ||
|
43771d5d92 | ||
|
c077a998eb | ||
|
e5cac10a3b | ||
|
61462af65a | ||
|
066ae4f829 | ||
|
70f31414e7 | ||
|
c0644771eb | ||
|
8168ca8ea3 | ||
|
8339fa266c | ||
|
5891c388bb | ||
|
b4a3c64b16 | ||
|
d2a4d85a8a | ||
|
d693c6f10f | ||
|
9884db2814 | ||
|
20a519a05a | ||
|
f892291eee | ||
|
5e39d89d20 | ||
|
be53081a61 | ||
|
5c6f3eb7db | ||
|
9a562ae7ba | ||
|
73479c5c87 | ||
|
324189babb | ||
|
0748b3526e | ||
|
697e42dec8 | ||
|
a3203e7dd3 | ||
|
08f44282c1 | ||
|
7b6badb6a9 | ||
|
5bb0d22cb4 | ||
|
d0eda02938 | ||
|
62e570b1c5 | ||
|
42a4812841 | ||
|
03c320d803 | ||
|
0d54a6fed3 | ||
|
bcb07dba92 | ||
|
adb998c12a | ||
|
29cf933635 | ||
|
305b4c60f2 | ||
|
ea204ddac7 | ||
|
83d4bf943e | ||
|
caa31bf28c | ||
|
a7a6a2bffc | ||
|
63c8ef2890 | ||
|
3bfecee2cb | ||
|
26022652c6 | ||
|
6361bbc7e2 | ||
|
2da077a881 | ||
|
0bbb2f3df1 | ||
|
e14935df26 | ||
|
b813bed1ab | ||
|
a358a3af45 | ||
|
3ae7eb88c4 | ||
|
ba9fcea1cb | ||
|
f196c3700d | ||
|
243e0fe550 | ||
|
cf8b8bfc50 | ||
|
c1a402a7ae | ||
|
d8b6d892c6 | ||
|
a8617d8c2f | ||
|
5162264e43 | ||
|
65424cc456 | ||
58de8b9684 | |||
|
b936cb50aa | ||
|
c4e316cfb5 | ||
|
43046b5a07 | ||
|
562a20b4ef | ||
|
04b9bcf911 | ||
|
811bf15114 | ||
|
ede24a0264 | ||
|
2e30230aa9 | ||
|
7d8246960e | ||
|
c7990a2648 | ||
|
a896f7f26a | ||
|
cf1cd117e2 | ||
|
0bb0aea4ba | ||
|
525989a50a | ||
|
3cf7daf8c3 | ||
|
75ebec11af | ||
|
c10595fb34 | ||
|
dbaabb25f4 | ||
|
f80c98740e | ||
|
5a38cb5940 | ||
|
127ff5c356 | ||
|
8f7d7161dd | ||
|
0b77d30a43 | ||
|
ad523590f6 | ||
|
bf55b7afce | ||
|
81ffbf5ab1 | ||
|
3dbcf27334 | ||
|
f57f587857 | ||
|
4fa62005d0 | ||
|
fcdcf1eed2 | ||
|
24df3371d9 | ||
|
4be56c1959 | ||
|
6a87e7929f | ||
|
a17d8659c4 | ||
|
57a0aa6b50 | ||
|
506f327582 | ||
|
62f94fc94f | ||
|
16ee99805e | ||
|
a50919dddf | ||
|
318347234d | ||
|
0cffce56ae | ||
|
223a23c198 | ||
|
eb05e011e2 | ||
|
4fb588e95b | ||
|
7e74a73499 | ||
|
c8ab5c2dde | ||
|
4c241cf5d6 | ||
|
2caf63a903 | ||
|
88691b37f8 | ||
|
f321dcb57f | ||
|
05b0d8e3b8 | ||
|
9f086abbe4 | ||
|
6573d9c638 | ||
|
e1fe27a208 | ||
|
c871bc70bb | ||
|
459264ef24 | ||
|
80c33d343f | ||
|
3d85885a1b | ||
|
249127d0df | ||
|
bff3063837 | ||
|
07572c0653 | ||
|
de86eccc0c | ||
|
06ec79e865 | ||
|
c8a98293f7 | ||
|
175d2aa038 | ||
|
f63ebfe0ac | ||
|
a8b7373421 | ||
|
9964e96dc9 | ||
|
8c1bc1e9d7 | ||
|
579cf1d104 | ||
|
08fba7ac9b | ||
|
cf83f14005 | ||
|
802f045a5f | ||
|
aedbe19297 | ||
|
7af88279e4 | ||
|
fc0f005958 | ||
|
fe2f74af2b | ||
|
82342e91b6 | ||
|
61fcc16af6 | ||
|
e05dc4cf56 | ||
|
e2f8401638 | ||
|
559964a1ad | ||
|
f583dca9ad | ||
|
f989c30cf8 | ||
|
f5ab20a468 | ||
|
4348300e75 | ||
|
c6ff347c80 | ||
|
730a6e875b | ||
|
0bb8cacd95 | ||
|
415c382483 | ||
|
a4657d4b91 | ||
|
14c1f7deb4 | ||
|
cb4f4bc353 | ||
|
5eb74557cd | ||
|
334e76850b | ||
|
bab482d740 | ||
|
8ca2b376b4 | ||
|
4886b3e9f0 | ||
|
c14e706ce9 | ||
|
1dcac3e152 | ||
|
a8eac9431a | ||
|
817d4a6bc8 | ||
|
8f3cf0128c | ||
|
274250c301 | ||
|
74c98e20a6 | ||
|
f881bbdf72 | ||
|
de4e3ae408 | ||
|
fe921fc8b7 | ||
|
8edfe85bef | ||
|
9c12359c57 | ||
|
5ffd4a3c2d | ||
|
98d3c52435 | ||
|
77c76392b0 | ||
|
8a824e4d74 | ||
|
4c565674a2 | ||
|
ca89f72092 | ||
|
46d702b106 | ||
|
51180423a2 | ||
|
68ba3b0743 | ||
|
987772d9e7 | ||
|
82b9d0f06a | ||
|
576d1abc20 | ||
|
05b98c22f8 | ||
|
c59be019e9 | ||
|
40014d81f2 | ||
|
dd4339c540 | ||
|
709e3fe825 | ||
|
ed1701c6a5 | ||
|
a0762d9e34 | ||
|
ce7c817c85 | ||
|
2833c59b94 | ||
|
f4a06d1391 | ||
|
5d214a92ac | ||
|
1eb3fc0a0b |
8
.gdbinit
Normal file
8
.gdbinit
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# GDB may have ./.gdbinit loading disabled by default. In that case you can
|
||||||
|
# follow the instructions it prints. They boil down to adding the following to
|
||||||
|
# your home directory's ~/.gdbinit file:
|
||||||
|
#
|
||||||
|
# add-auto-load-safe-path /path/to/qemu/.gdbinit
|
||||||
|
|
||||||
|
# Load QEMU-specific sub-commands and settings
|
||||||
|
source scripts/qemu-gdb.py
|
17
.gitignore
vendored
17
.gitignore
vendored
@@ -50,6 +50,7 @@
|
|||||||
/qemu-version.h.tmp
|
/qemu-version.h.tmp
|
||||||
/module_block.h
|
/module_block.h
|
||||||
/vscclient
|
/vscclient
|
||||||
|
/vhost-user-scsi
|
||||||
/fsdev/virtfs-proxy-helper
|
/fsdev/virtfs-proxy-helper
|
||||||
*.[1-9]
|
*.[1-9]
|
||||||
*.a
|
*.a
|
||||||
@@ -99,14 +100,14 @@
|
|||||||
/pc-bios/optionrom/kvmvapic.img
|
/pc-bios/optionrom/kvmvapic.img
|
||||||
/pc-bios/s390-ccw/s390-ccw.elf
|
/pc-bios/s390-ccw/s390-ccw.elf
|
||||||
/pc-bios/s390-ccw/s390-ccw.img
|
/pc-bios/s390-ccw/s390-ccw.img
|
||||||
/docs/qemu-ga-qapi.texi
|
/docs/interop/qemu-ga-qapi.texi
|
||||||
/docs/qemu-ga-ref.html
|
/docs/interop/qemu-ga-ref.html
|
||||||
/docs/qemu-ga-ref.info*
|
/docs/interop/qemu-ga-ref.info*
|
||||||
/docs/qemu-ga-ref.txt
|
/docs/interop/qemu-ga-ref.txt
|
||||||
/docs/qemu-qmp-qapi.texi
|
/docs/interop/qemu-qmp-qapi.texi
|
||||||
/docs/qemu-qmp-ref.html
|
/docs/interop/qemu-qmp-ref.html
|
||||||
/docs/qemu-qmp-ref.info*
|
/docs/interop/qemu-qmp-ref.info*
|
||||||
/docs/qemu-qmp-ref.txt
|
/docs/interop/qemu-qmp-ref.txt
|
||||||
/docs/version.texi
|
/docs/version.texi
|
||||||
*.tps
|
*.tps
|
||||||
.stgit-*
|
.stgit-*
|
||||||
|
@@ -1,15 +1,22 @@
|
|||||||
language: c
|
language: c
|
||||||
|
git:
|
||||||
|
submodules: false
|
||||||
env:
|
env:
|
||||||
|
global:
|
||||||
|
- LC_ALL=C
|
||||||
matrix:
|
matrix:
|
||||||
- IMAGE=debian-armhf-cross
|
- IMAGE=debian-armhf-cross
|
||||||
TARGET_LIST=arm-softmmu,arm-linux-user
|
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
|
||||||
- IMAGE=debian-arm64-cross
|
- IMAGE=debian-arm64-cross
|
||||||
TARGET_LIST=aarch64-softmmu,aarch64-linux-user
|
TARGET_LIST=aarch64-softmmu,aarch64-linux-user
|
||||||
- IMAGE=debian-s390x-cross
|
- IMAGE=debian-s390x-cross
|
||||||
TARGET_LIST=s390x-softmmu,s390x-linux-user
|
TARGET_LIST=s390x-softmmu,s390x-linux-user
|
||||||
|
# mips64el-softmmu disabled due to libfdt problem
|
||||||
|
- IMAGE=debian-mipsel-cross
|
||||||
|
TARGET_LIST=mipsel-softmmu,mipsel-linux-user,mips64el-linux-user
|
||||||
build:
|
build:
|
||||||
pre_ci:
|
pre_ci:
|
||||||
- make docker-image-${IMAGE}
|
- make docker-image-${IMAGE} V=1
|
||||||
pre_ci_boot:
|
pre_ci_boot:
|
||||||
image_name: qemu
|
image_name: qemu
|
||||||
image_tag: ${IMAGE}
|
image_tag: ${IMAGE}
|
||||||
@@ -17,5 +24,13 @@ build:
|
|||||||
options: "-e HOME=/root"
|
options: "-e HOME=/root"
|
||||||
ci:
|
ci:
|
||||||
- unset CC
|
- unset CC
|
||||||
|
# some targets require newer up to date packages, for example TARGET_LIST matching
|
||||||
|
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||||
|
# see the configure script:
|
||||||
|
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
|
||||||
|
# " (1) Preferred: Install the DTC (libfdt) devel package"
|
||||||
|
# " (2) Fetch the DTC submodule, using:"
|
||||||
|
# " git submodule update --init dtc"
|
||||||
|
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
|
||||||
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||||
- make -j2
|
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||||
|
18
MAINTAINERS
18
MAINTAINERS
@@ -1005,6 +1005,14 @@ S: Supported
|
|||||||
F: hw/vfio/*
|
F: hw/vfio/*
|
||||||
F: include/hw/vfio/
|
F: include/hw/vfio/
|
||||||
|
|
||||||
|
vfio-ccw
|
||||||
|
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
|
S: Supported
|
||||||
|
F: hw/vfio/ccw.c
|
||||||
|
F: hw/s390x/s390-ccw.c
|
||||||
|
F: include/hw/s390x/s390-ccw.h
|
||||||
|
T: git git://github.com/cohuck/qemu.git s390-next
|
||||||
|
|
||||||
vhost
|
vhost
|
||||||
M: Michael S. Tsirkin <mst@redhat.com>
|
M: Michael S. Tsirkin <mst@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
@@ -1230,13 +1238,12 @@ M: Paolo Bonzini <pbonzini@redhat.com>
|
|||||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: chardev/
|
F: chardev/
|
||||||
F: backends/msmouse.c
|
F: include/chardev/
|
||||||
F: backends/testdev.c
|
|
||||||
|
|
||||||
Character Devices (Braille)
|
Character Devices (Braille)
|
||||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: backends/baum.c
|
F: chardev/baum.c
|
||||||
|
|
||||||
Command line option argument parsing
|
Command line option argument parsing
|
||||||
M: Markus Armbruster <armbru@redhat.com>
|
M: Markus Armbruster <armbru@redhat.com>
|
||||||
@@ -1404,8 +1411,7 @@ F: include/qapi/qmp/
|
|||||||
X: include/qapi/qmp/dispatch.h
|
X: include/qapi/qmp/dispatch.h
|
||||||
F: scripts/coccinelle/qobject.cocci
|
F: scripts/coccinelle/qobject.cocci
|
||||||
F: tests/check-qdict.c
|
F: tests/check-qdict.c
|
||||||
F: tests/check-qfloat.c
|
F: tests/check-qnum.c
|
||||||
F: tests/check-qint.c
|
|
||||||
F: tests/check-qjson.c
|
F: tests/check-qjson.c
|
||||||
F: tests/check-qlist.c
|
F: tests/check-qlist.c
|
||||||
F: tests/check-qstring.c
|
F: tests/check-qstring.c
|
||||||
@@ -1851,12 +1857,14 @@ Build and test automation
|
|||||||
-------------------------
|
-------------------------
|
||||||
M: Alex Bennée <alex.bennee@linaro.org>
|
M: Alex Bennée <alex.bennee@linaro.org>
|
||||||
M: Fam Zheng <famz@redhat.com>
|
M: Fam Zheng <famz@redhat.com>
|
||||||
|
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||||
L: qemu-devel@nongnu.org
|
L: qemu-devel@nongnu.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: .travis.yml
|
F: .travis.yml
|
||||||
F: .shippable.yml
|
F: .shippable.yml
|
||||||
F: tests/docker/
|
F: tests/docker/
|
||||||
W: https://travis-ci.org/qemu/qemu
|
W: https://travis-ci.org/qemu/qemu
|
||||||
|
W: https://app.shippable.com/github/qemu/qemu
|
||||||
W: http://patchew.org/QEMU/
|
W: http://patchew.org/QEMU/
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
|
77
Makefile
77
Makefile
@@ -207,8 +207,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
|||||||
|
|
||||||
ifdef BUILD_DOCS
|
ifdef BUILD_DOCS
|
||||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||||
DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7
|
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||||
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
|
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||||
endif
|
endif
|
||||||
@@ -269,6 +269,7 @@ dummy := $(call unnest-vars,, \
|
|||||||
ivshmem-client-obj-y \
|
ivshmem-client-obj-y \
|
||||||
ivshmem-server-obj-y \
|
ivshmem-server-obj-y \
|
||||||
libvhost-user-obj-y \
|
libvhost-user-obj-y \
|
||||||
|
vhost-user-scsi-obj-y \
|
||||||
qga-vss-dll-obj-y \
|
qga-vss-dll-obj-y \
|
||||||
block-obj-y \
|
block-obj-y \
|
||||||
block-obj-m \
|
block-obj-m \
|
||||||
@@ -473,6 +474,8 @@ ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
|||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
|
vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y)
|
||||||
|
$(call LINK, $^)
|
||||||
|
|
||||||
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
||||||
$(call quiet-command,$(PYTHON) $< $@ \
|
$(call quiet-command,$(PYTHON) $< $@ \
|
||||||
@@ -519,11 +522,12 @@ distclean: clean
|
|||||||
rm -f qemu-doc.vr qemu-doc.txt
|
rm -f qemu-doc.vr qemu-doc.txt
|
||||||
rm -f config.log
|
rm -f config.log
|
||||||
rm -f linux-headers/asm
|
rm -f linux-headers/asm
|
||||||
rm -f docs/qemu-ga-qapi.texi docs/qemu-qmp-qapi.texi docs/version.texi
|
rm -f docs/version.texi
|
||||||
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
|
rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
|
||||||
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
|
||||||
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||||
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||||
|
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
for d in $(TARGET_DIRS); do \
|
for d in $(TARGET_DIRS); do \
|
||||||
rm -rf $$d || exit 1 ; \
|
rm -rf $$d || exit 1 ; \
|
||||||
done
|
done
|
||||||
@@ -562,13 +566,13 @@ install-doc: $(DOCS)
|
|||||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
ifdef CONFIG_POSIX
|
ifdef CONFIG_POSIX
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
ifneq ($(TOOLS),)
|
ifneq ($(TOOLS),)
|
||||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||||
@@ -576,9 +580,9 @@ ifneq ($(TOOLS),)
|
|||||||
endif
|
endif
|
||||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
@@ -666,28 +670,27 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
|
|||||||
|
|
||||||
# documentation
|
# documentation
|
||||||
MAKEINFO=makeinfo
|
MAKEINFO=makeinfo
|
||||||
MAKEINFOFLAGS=--no-split --number-sections -I docs
|
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
|
||||||
TEXIFLAG=$(if $(V),,--quiet)
|
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
|
||||||
|
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
|
||||||
|
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
|
||||||
|
|
||||||
docs/version.texi: $(SRC_PATH)/VERSION
|
docs/version.texi: $(SRC_PATH)/VERSION
|
||||||
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
|
||||||
|
|
||||||
%.html: %.texi
|
%.html: %.texi docs/version.texi
|
||||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||||
--html $< -o $@,"GEN","$@")
|
--html $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.info: %.texi
|
%.info: %.texi docs/version.texi
|
||||||
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.txt: %.texi
|
%.txt: %.texi docs/version.texi
|
||||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||||
--plaintext $< -o $@,"GEN","$@")
|
--plaintext $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
%.pdf: %.texi
|
%.pdf: %.texi docs/version.texi
|
||||||
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I docs $< -o $@,"GEN","$@")
|
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.txt docs/qemu-ga-ref.pdf docs/qemu-ga-ref.7.pod: docs/version.texi
|
|
||||||
docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.pod: docs/version.texi
|
|
||||||
|
|
||||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||||
@@ -701,12 +704,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
|
|||||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||||
|
|
||||||
docs/qemu-qmp-qapi.texi docs/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
|
docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
|
||||||
|
|
||||||
docs/qemu-qmp-qapi.texi: $(qapi-modules)
|
docs/interop/qemu-qmp-qapi.texi: $(qapi-modules)
|
||||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||||
|
|
||||||
docs/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
|
docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
|
||||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||||
|
|
||||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||||
@@ -716,21 +719,25 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
|||||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||||
qemu-ga.8: qemu-ga.texi
|
qemu-ga.8: qemu-ga.texi
|
||||||
|
|
||||||
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
|
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||||
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||||
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||||
|
|
||||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||||
qemu-monitor-info.texi
|
qemu-monitor-info.texi
|
||||||
|
|
||||||
docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
|
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||||
docs/qemu-ga-ref.texi docs/qemu-ga-qapi.texi
|
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||||
|
docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7: \
|
||||||
|
docs/interop/qemu-ga-ref.texi docs/interop/qemu-ga-qapi.texi
|
||||||
|
|
||||||
docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
|
docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
||||||
docs/qemu-qmp-ref.texi docs/qemu-qmp-qapi.texi
|
docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \
|
||||||
|
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
||||||
|
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
||||||
|
|
||||||
|
|
||||||
ifdef CONFIG_WIN32
|
ifdef CONFIG_WIN32
|
||||||
@@ -791,9 +798,11 @@ endif # CONFIG_WIN
|
|||||||
|
|
||||||
# Add a dependency on the generated files, so that they are always
|
# Add a dependency on the generated files, so that they are always
|
||||||
# rebuilt before other object files
|
# rebuilt before other object files
|
||||||
|
ifneq ($(wildcard config-host.mak),)
|
||||||
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||||
Makefile: $(GENERATED_FILES)
|
Makefile: $(GENERATED_FILES)
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
||||||
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
||||||
|
@@ -50,11 +50,8 @@ common-obj-$(CONFIG_LINUX) += fsdev/
|
|||||||
|
|
||||||
common-obj-y += migration/
|
common-obj-y += migration/
|
||||||
|
|
||||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
|
||||||
|
|
||||||
common-obj-y += audio/
|
common-obj-y += audio/
|
||||||
common-obj-y += hw/
|
common-obj-y += hw/
|
||||||
common-obj-y += accel.o
|
|
||||||
|
|
||||||
common-obj-y += replay/
|
common-obj-y += replay/
|
||||||
|
|
||||||
@@ -70,6 +67,7 @@ common-obj-y += tpm.o
|
|||||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||||
|
|
||||||
common-obj-y += backends/
|
common-obj-y += backends/
|
||||||
|
common-obj-y += chardev/
|
||||||
|
|
||||||
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
||||||
|
|
||||||
@@ -112,6 +110,10 @@ qga-vss-dll-obj-y = qga/
|
|||||||
ivshmem-client-obj-y = contrib/ivshmem-client/
|
ivshmem-client-obj-y = contrib/ivshmem-client/
|
||||||
ivshmem-server-obj-y = contrib/ivshmem-server/
|
ivshmem-server-obj-y = contrib/ivshmem-server/
|
||||||
libvhost-user-obj-y = contrib/libvhost-user/
|
libvhost-user-obj-y = contrib/libvhost-user/
|
||||||
|
vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||||
|
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
|
||||||
|
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
|
||||||
|
vhost-user-scsi-obj-y += contrib/libvhost-user/libvhost-user.o
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
trace-events-subdirs =
|
trace-events-subdirs =
|
||||||
@@ -121,6 +123,7 @@ trace-events-subdirs += io
|
|||||||
trace-events-subdirs += migration
|
trace-events-subdirs += migration
|
||||||
trace-events-subdirs += block
|
trace-events-subdirs += block
|
||||||
trace-events-subdirs += backends
|
trace-events-subdirs += backends
|
||||||
|
trace-events-subdirs += chardev
|
||||||
trace-events-subdirs += hw/block
|
trace-events-subdirs += hw/block
|
||||||
trace-events-subdirs += hw/block/dataplane
|
trace-events-subdirs += hw/block/dataplane
|
||||||
trace-events-subdirs += hw/char
|
trace-events-subdirs += hw/char
|
||||||
@@ -163,6 +166,8 @@ trace-events-subdirs += target/ppc
|
|||||||
trace-events-subdirs += qom
|
trace-events-subdirs += qom
|
||||||
trace-events-subdirs += linux-user
|
trace-events-subdirs += linux-user
|
||||||
trace-events-subdirs += qapi
|
trace-events-subdirs += qapi
|
||||||
|
trace-events-subdirs += accel/tcg
|
||||||
|
trace-events-subdirs += accel/kvm
|
||||||
|
|
||||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||||
|
|
||||||
|
@@ -88,20 +88,17 @@ all: $(PROGS) stap
|
|||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# cpu emulator library
|
# cpu emulator library
|
||||||
obj-y = exec.o translate-all.o cpu-exec.o
|
obj-y += exec.o
|
||||||
obj-y += translate-common.o
|
obj-y += accel/
|
||||||
obj-y += cpu-exec-common.o
|
|
||||||
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
|
obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
|
||||||
obj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
obj-y += tcg/tcg-common.o tcg/tcg-runtime.o
|
||||||
obj-y += tcg/tcg-common.o
|
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
||||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||||
obj-y += fpu/softfloat.o
|
obj-y += fpu/softfloat.o
|
||||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||||
obj-y += disas.o
|
obj-y += disas.o
|
||||||
obj-y += tcg-runtime.o
|
|
||||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
||||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
|
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
|
||||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
|
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
|
||||||
@@ -142,11 +139,10 @@ ifdef CONFIG_SOFTMMU
|
|||||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||||
obj-y += qtest.o bootdevice.o
|
obj-y += qtest.o bootdevice.o
|
||||||
obj-y += hw/
|
obj-y += hw/
|
||||||
obj-$(CONFIG_KVM) += kvm-all.o
|
obj-y += memory.o
|
||||||
obj-y += memory.o cputlb.o
|
|
||||||
obj-y += memory_mapping.o
|
obj-y += memory_mapping.o
|
||||||
obj-y += dump.o
|
obj-y += dump.o
|
||||||
obj-y += migration/ram.o migration/savevm.o
|
obj-y += migration/ram.o
|
||||||
LIBS := $(libs_softmmu) $(LIBS)
|
LIBS := $(libs_softmmu) $(LIBS)
|
||||||
|
|
||||||
# Hardware support
|
# Hardware support
|
||||||
|
4
accel/Makefile.objs
Normal file
4
accel/Makefile.objs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||||
|
obj-y += kvm/
|
||||||
|
obj-y += tcg/
|
||||||
|
obj-y += stubs/
|
@@ -34,15 +34,6 @@
|
|||||||
#include "hw/xen/xen.h"
|
#include "hw/xen/xen.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
|
||||||
int tcg_tb_size;
|
|
||||||
static bool tcg_allowed = true;
|
|
||||||
|
|
||||||
static int tcg_init(MachineState *ms)
|
|
||||||
{
|
|
||||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo accel_type = {
|
static const TypeInfo accel_type = {
|
||||||
.name = TYPE_ACCEL,
|
.name = TYPE_ACCEL,
|
||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_OBJECT,
|
||||||
@@ -129,27 +120,9 @@ void configure_accelerator(MachineState *ms)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
|
||||||
{
|
|
||||||
AccelClass *ac = ACCEL_CLASS(oc);
|
|
||||||
ac->name = "tcg";
|
|
||||||
ac->init_machine = tcg_init;
|
|
||||||
ac->allowed = &tcg_allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
|
||||||
|
|
||||||
static const TypeInfo tcg_accel_type = {
|
|
||||||
.name = TYPE_TCG_ACCEL,
|
|
||||||
.parent = TYPE_ACCEL,
|
|
||||||
.class_init = tcg_accel_class_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void register_accel_types(void)
|
static void register_accel_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&accel_type);
|
type_register_static(&accel_type);
|
||||||
type_register_static(&tcg_accel_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(register_accel_types);
|
type_init(register_accel_types);
|
1
accel/kvm/Makefile.objs
Normal file
1
accel/kvm/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
obj-$(CONFIG_KVM) += kvm-all.o
|
@@ -23,6 +23,7 @@
|
|||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
#include "hw/pci/msix.h"
|
#include "hw/pci/msix.h"
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
#include "exec/ram_addr.h"
|
#include "exec/ram_addr.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
@@ -1144,6 +1145,7 @@ void kvm_irqchip_release_virq(KVMState *s, int virq)
|
|||||||
}
|
}
|
||||||
clear_gsi(s, virq);
|
clear_gsi(s, virq);
|
||||||
kvm_arch_release_virq_post(virq);
|
kvm_arch_release_virq_post(virq);
|
||||||
|
trace_kvm_irqchip_release_virq(virq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int kvm_hash_msi(uint32_t data)
|
static unsigned int kvm_hash_msi(uint32_t data)
|
||||||
@@ -1287,7 +1289,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_kvm_irqchip_add_msi_route(virq);
|
trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A",
|
||||||
|
vector, virq);
|
||||||
|
|
||||||
kvm_add_routing_entry(s, &kroute);
|
kvm_add_routing_entry(s, &kroute);
|
||||||
kvm_arch_add_msi_route_post(&kroute, vector, dev);
|
kvm_arch_add_msi_route_post(&kroute, vector, dev);
|
||||||
@@ -1746,6 +1749,8 @@ static int kvm_init(MachineState *ms)
|
|||||||
kvm_ioeventfd_any_length_allowed =
|
kvm_ioeventfd_any_length_allowed =
|
||||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD_ANY_LENGTH) > 0);
|
(kvm_check_extension(s, KVM_CAP_IOEVENTFD_ANY_LENGTH) > 0);
|
||||||
|
|
||||||
|
kvm_state = s;
|
||||||
|
|
||||||
ret = kvm_arch_init(ms, s);
|
ret = kvm_arch_init(ms, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
@@ -1755,8 +1760,6 @@ static int kvm_init(MachineState *ms)
|
|||||||
kvm_irqchip_create(ms, s);
|
kvm_irqchip_create(ms, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_state = s;
|
|
||||||
|
|
||||||
if (kvm_eventfds_allowed) {
|
if (kvm_eventfds_allowed) {
|
||||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||||
@@ -1896,6 +1899,16 @@ void kvm_cpu_synchronize_post_init(CPUState *cpu)
|
|||||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
|
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_kvm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg)
|
||||||
|
{
|
||||||
|
cpu->kvm_vcpu_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu)
|
||||||
|
{
|
||||||
|
run_on_cpu(cpu, do_kvm_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef KVM_HAVE_MCE_INJECTION
|
#ifdef KVM_HAVE_MCE_INJECTION
|
||||||
static __thread void *pending_sigbus_addr;
|
static __thread void *pending_sigbus_addr;
|
||||||
static __thread int pending_sigbus_code;
|
static __thread int pending_sigbus_code;
|
||||||
@@ -1964,6 +1977,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
cpu_exec_start(cpu);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
MemTxAttrs attrs;
|
MemTxAttrs attrs;
|
||||||
@@ -2052,7 +2066,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
|||||||
break;
|
break;
|
||||||
case KVM_EXIT_SHUTDOWN:
|
case KVM_EXIT_SHUTDOWN:
|
||||||
DPRINTF("shutdown\n");
|
DPRINTF("shutdown\n");
|
||||||
qemu_system_reset_request();
|
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||||
ret = EXCP_INTERRUPT;
|
ret = EXCP_INTERRUPT;
|
||||||
break;
|
break;
|
||||||
case KVM_EXIT_UNKNOWN:
|
case KVM_EXIT_UNKNOWN:
|
||||||
@@ -2066,11 +2080,11 @@ int kvm_cpu_exec(CPUState *cpu)
|
|||||||
case KVM_EXIT_SYSTEM_EVENT:
|
case KVM_EXIT_SYSTEM_EVENT:
|
||||||
switch (run->system_event.type) {
|
switch (run->system_event.type) {
|
||||||
case KVM_SYSTEM_EVENT_SHUTDOWN:
|
case KVM_SYSTEM_EVENT_SHUTDOWN:
|
||||||
qemu_system_shutdown_request();
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||||
ret = EXCP_INTERRUPT;
|
ret = EXCP_INTERRUPT;
|
||||||
break;
|
break;
|
||||||
case KVM_SYSTEM_EVENT_RESET:
|
case KVM_SYSTEM_EVENT_RESET:
|
||||||
qemu_system_reset_request();
|
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||||
ret = EXCP_INTERRUPT;
|
ret = EXCP_INTERRUPT;
|
||||||
break;
|
break;
|
||||||
case KVM_SYSTEM_EVENT_CRASH:
|
case KVM_SYSTEM_EVENT_CRASH:
|
||||||
@@ -2093,6 +2107,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
|||||||
}
|
}
|
||||||
} while (ret == 0);
|
} while (ret == 0);
|
||||||
|
|
||||||
|
cpu_exec_end(cpu);
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -2204,8 +2219,8 @@ int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr)
|
|||||||
return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1;
|
return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_device_access(int fd, int group, uint64_t attr,
|
int kvm_device_access(int fd, int group, uint64_t attr,
|
||||||
void *val, bool write)
|
void *val, bool write, Error **errp)
|
||||||
{
|
{
|
||||||
struct kvm_device_attr kvmattr;
|
struct kvm_device_attr kvmattr;
|
||||||
int err;
|
int err;
|
||||||
@@ -2219,11 +2234,12 @@ void kvm_device_access(int fd, int group, uint64_t attr,
|
|||||||
write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR,
|
write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR,
|
||||||
&kvmattr);
|
&kvmattr);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
error_report("KVM_%s_DEVICE_ATTR failed: %s",
|
error_setg_errno(errp, -err,
|
||||||
write ? "SET" : "GET", strerror(-err));
|
"KVM_%s_DEVICE_ATTR failed: Group %d "
|
||||||
error_printf("Group %d attr 0x%016" PRIx64 "\n", group, attr);
|
"attr 0x%016" PRIx64,
|
||||||
abort();
|
write ? "SET" : "GET", group, attr);
|
||||||
}
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 1 on success, 0 on failure */
|
/* Return 1 on success, 0 on failure */
|
15
accel/kvm/trace-events
Normal file
15
accel/kvm/trace-events
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Trace events for debugging and performance instrumentation
|
||||||
|
|
||||||
|
# kvm-all.c
|
||||||
|
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||||
|
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||||
|
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
|
||||||
|
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
||||||
|
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||||
|
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
|
||||||
|
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
|
||||||
|
kvm_irqchip_commit_routes(void) ""
|
||||||
|
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||||
|
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||||
|
kvm_irqchip_release_virq(int virq) "virq %d"
|
||||||
|
|
1
accel/stubs/Makefile.objs
Normal file
1
accel/stubs/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
3
accel/tcg/Makefile.objs
Normal file
3
accel/tcg/Makefile.objs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
obj-$(CONFIG_SOFTMMU) += tcg-all.o
|
||||||
|
obj-$(CONFIG_SOFTMMU) += cputlb.o
|
||||||
|
obj-y += cpu-exec.o cpu-exec-common.o translate-all.o translate-common.o
|
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "disas/disas.h"
|
#include "disas/disas.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
||||||
@@ -309,10 +309,8 @@ static bool tb_cmp(const void *p, const void *d)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TranslationBlock *tb_htable_lookup(CPUState *cpu,
|
TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||||
target_ulong pc,
|
target_ulong cs_base, uint32_t flags)
|
||||||
target_ulong cs_base,
|
|
||||||
uint32_t flags)
|
|
||||||
{
|
{
|
||||||
tb_page_addr_t phys_pc;
|
tb_page_addr_t phys_pc;
|
||||||
struct tb_desc desc;
|
struct tb_desc desc;
|
@@ -1,7 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* QEMU System Emulator
|
* QEMU System Emulator, accelerator interfaces
|
||||||
*
|
*
|
||||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
* Copyright (c) 2014 Red Hat Inc.
|
||||||
*
|
*
|
||||||
* 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
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,65 +24,38 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "sysemu/accel.h"
|
||||||
#include "tap_int.h"
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "qom/object.h"
|
||||||
|
|
||||||
int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
|
int tcg_tb_size;
|
||||||
int vnet_hdr_required, int mq_required, Error **errp)
|
static bool tcg_allowed = true;
|
||||||
{
|
|
||||||
error_setg(errp, "no tap on Haiku");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp)
|
static int tcg_init(MachineState *ms)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_probe_vnet_hdr(int fd)
|
|
||||||
{
|
{
|
||||||
|
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tap_probe_has_ufo(int fd)
|
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
return 0;
|
AccelClass *ac = ACCEL_CLASS(oc);
|
||||||
|
ac->name = "tcg";
|
||||||
|
ac->init_machine = tcg_init;
|
||||||
|
ac->allowed = &tcg_allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tap_probe_vnet_hdr_len(int fd, int len)
|
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||||
|
|
||||||
|
static const TypeInfo tcg_accel_type = {
|
||||||
|
.name = TYPE_TCG_ACCEL,
|
||||||
|
.parent = TYPE_ACCEL,
|
||||||
|
.class_init = tcg_accel_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_accel_types(void)
|
||||||
{
|
{
|
||||||
return 0;
|
type_register_static(&tcg_accel_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tap_fd_set_vnet_hdr_len(int fd, int len)
|
type_init(register_accel_types);
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_fd_set_vnet_le(int fd, int is_le)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_fd_set_vnet_be(int fd, int is_be)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tap_fd_set_offload(int fd, int csum, int tso4,
|
|
||||||
int tso6, int ecn, int ufo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_fd_enable(int fd)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_fd_disable(int fd)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tap_fd_get_ifname(int fd, char *ifname)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
10
accel/tcg/trace-events
Normal file
10
accel/tcg/trace-events
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Trace events for debugging and performance instrumentation
|
||||||
|
|
||||||
|
# TCG related tracing (mostly disabled by default)
|
||||||
|
# cpu-exec.c
|
||||||
|
disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||||
|
disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||||
|
disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=%x"
|
||||||
|
|
||||||
|
# translate-all.c
|
||||||
|
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
@@ -25,7 +25,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#define NO_CPU_IO_DEFS
|
#define NO_CPU_IO_DEFS
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "disas/disas.h"
|
#include "disas/disas.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
||||||
@@ -523,8 +523,6 @@ static inline PageDesc *page_find(tb_page_addr_t index)
|
|||||||
# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
|
# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
|
# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
|
||||||
#elif defined(__arm__)
|
|
||||||
# define MAX_CODE_GEN_BUFFER_SIZE (16u * 1024 * 1024)
|
|
||||||
#elif defined(__s390x__)
|
#elif defined(__s390x__)
|
||||||
/* We have a +- 4GB range on the branches; leave some slop. */
|
/* We have a +- 4GB range on the branches; leave some slop. */
|
||||||
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
|
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
|
||||||
@@ -781,12 +779,13 @@ static inline void code_gen_alloc(size_t tb_size)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estimate a good size for the number of TBs we can support. We
|
/* size this conservatively -- realloc later if needed */
|
||||||
still haven't deducted the prologue from the buffer size here,
|
tcg_ctx.tb_ctx.tbs_size =
|
||||||
but that's minimal and won't affect the estimate much. */
|
tcg_ctx.code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE / 8;
|
||||||
tcg_ctx.code_gen_max_blocks
|
if (unlikely(!tcg_ctx.tb_ctx.tbs_size)) {
|
||||||
= tcg_ctx.code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE;
|
tcg_ctx.tb_ctx.tbs_size = 64 * 1024;
|
||||||
tcg_ctx.tb_ctx.tbs = g_new(TranslationBlock, tcg_ctx.code_gen_max_blocks);
|
}
|
||||||
|
tcg_ctx.tb_ctx.tbs = g_new(TranslationBlock *, tcg_ctx.tb_ctx.tbs_size);
|
||||||
|
|
||||||
qemu_mutex_init(&tcg_ctx.tb_ctx.tb_lock);
|
qemu_mutex_init(&tcg_ctx.tb_ctx.tb_lock);
|
||||||
}
|
}
|
||||||
@@ -828,16 +827,20 @@ bool tcg_enabled(void)
|
|||||||
static TranslationBlock *tb_alloc(target_ulong pc)
|
static TranslationBlock *tb_alloc(target_ulong pc)
|
||||||
{
|
{
|
||||||
TranslationBlock *tb;
|
TranslationBlock *tb;
|
||||||
|
TBContext *ctx;
|
||||||
|
|
||||||
assert_tb_locked();
|
assert_tb_locked();
|
||||||
|
|
||||||
if (tcg_ctx.tb_ctx.nb_tbs >= tcg_ctx.code_gen_max_blocks) {
|
tb = tcg_tb_alloc(&tcg_ctx);
|
||||||
|
if (unlikely(tb == NULL)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
tb = &tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs++];
|
ctx = &tcg_ctx.tb_ctx;
|
||||||
tb->pc = pc;
|
if (unlikely(ctx->nb_tbs == ctx->tbs_size)) {
|
||||||
tb->cflags = 0;
|
ctx->tbs_size *= 2;
|
||||||
tb->invalid = false;
|
ctx->tbs = g_renew(TranslationBlock *, ctx->tbs, ctx->tbs_size);
|
||||||
|
}
|
||||||
|
ctx->tbs[ctx->nb_tbs++] = tb;
|
||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,8 +853,10 @@ void tb_free(TranslationBlock *tb)
|
|||||||
Ignore the hard cases and just back up if this TB happens to
|
Ignore the hard cases and just back up if this TB happens to
|
||||||
be the last one generated. */
|
be the last one generated. */
|
||||||
if (tcg_ctx.tb_ctx.nb_tbs > 0 &&
|
if (tcg_ctx.tb_ctx.nb_tbs > 0 &&
|
||||||
tb == &tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs - 1]) {
|
tb == tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs - 1]) {
|
||||||
tcg_ctx.code_gen_ptr = tb->tc_ptr;
|
size_t struct_size = ROUND_UP(sizeof(*tb), qemu_icache_linesize);
|
||||||
|
|
||||||
|
tcg_ctx.code_gen_ptr = tb->tc_ptr - struct_size;
|
||||||
tcg_ctx.tb_ctx.nb_tbs--;
|
tcg_ctx.tb_ctx.nb_tbs--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1279,9 +1284,11 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||||||
|
|
||||||
gen_code_buf = tcg_ctx.code_gen_ptr;
|
gen_code_buf = tcg_ctx.code_gen_ptr;
|
||||||
tb->tc_ptr = gen_code_buf;
|
tb->tc_ptr = gen_code_buf;
|
||||||
|
tb->pc = pc;
|
||||||
tb->cs_base = cs_base;
|
tb->cs_base = cs_base;
|
||||||
tb->flags = flags;
|
tb->flags = flags;
|
||||||
tb->cflags = cflags;
|
tb->cflags = cflags;
|
||||||
|
tb->invalid = false;
|
||||||
|
|
||||||
#ifdef CONFIG_PROFILER
|
#ifdef CONFIG_PROFILER
|
||||||
tcg_ctx.tb_count1++; /* includes aborted translations because of
|
tcg_ctx.tb_count1++; /* includes aborted translations because of
|
||||||
@@ -1666,7 +1673,7 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr)
|
|||||||
m_max = tcg_ctx.tb_ctx.nb_tbs - 1;
|
m_max = tcg_ctx.tb_ctx.nb_tbs - 1;
|
||||||
while (m_min <= m_max) {
|
while (m_min <= m_max) {
|
||||||
m = (m_min + m_max) >> 1;
|
m = (m_min + m_max) >> 1;
|
||||||
tb = &tcg_ctx.tb_ctx.tbs[m];
|
tb = tcg_ctx.tb_ctx.tbs[m];
|
||||||
v = (uintptr_t)tb->tc_ptr;
|
v = (uintptr_t)tb->tc_ptr;
|
||||||
if (v == tc_ptr) {
|
if (v == tc_ptr) {
|
||||||
return tb;
|
return tb;
|
||||||
@@ -1676,7 +1683,7 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr)
|
|||||||
m_min = m + 1;
|
m_min = m + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &tcg_ctx.tb_ctx.tbs[m_max];
|
return tcg_ctx.tb_ctx.tbs[m_max];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
@@ -1874,7 +1881,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
|||||||
direct_jmp_count = 0;
|
direct_jmp_count = 0;
|
||||||
direct_jmp2_count = 0;
|
direct_jmp2_count = 0;
|
||||||
for (i = 0; i < tcg_ctx.tb_ctx.nb_tbs; i++) {
|
for (i = 0; i < tcg_ctx.tb_ctx.nb_tbs; i++) {
|
||||||
tb = &tcg_ctx.tb_ctx.tbs[i];
|
tb = tcg_ctx.tb_ctx.tbs[i];
|
||||||
target_code_size += tb->size;
|
target_code_size += tb->size;
|
||||||
if (tb->size > max_target_code_size) {
|
if (tb->size > max_target_code_size) {
|
||||||
max_target_code_size = tb->size;
|
max_target_code_size = tb->size;
|
||||||
@@ -1894,8 +1901,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
|||||||
cpu_fprintf(f, "gen code size %td/%zd\n",
|
cpu_fprintf(f, "gen code size %td/%zd\n",
|
||||||
tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer,
|
tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer,
|
||||||
tcg_ctx.code_gen_highwater - tcg_ctx.code_gen_buffer);
|
tcg_ctx.code_gen_highwater - tcg_ctx.code_gen_buffer);
|
||||||
cpu_fprintf(f, "TB count %d/%d\n",
|
cpu_fprintf(f, "TB count %d\n", tcg_ctx.tb_ctx.nb_tbs);
|
||||||
tcg_ctx.tb_ctx.nb_tbs, tcg_ctx.code_gen_max_blocks);
|
|
||||||
cpu_fprintf(f, "TB avg target size %d max=%d bytes\n",
|
cpu_fprintf(f, "TB avg target size %d max=%d bytes\n",
|
||||||
tcg_ctx.tb_ctx.nb_tbs ? target_code_size /
|
tcg_ctx.tb_ctx.nb_tbs ? target_code_size /
|
||||||
tcg_ctx.tb_ctx.nb_tbs : 0,
|
tcg_ctx.tb_ctx.nb_tbs : 0,
|
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
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
common-obj-y += rng.o rng-egd.o
|
common-obj-y += rng.o rng-egd.o
|
||||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||||
|
|
||||||
common-obj-y += msmouse.o wctablet.o testdev.o
|
|
||||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
|
||||||
baum.o-cflags := $(SDL_CFLAGS)
|
|
||||||
|
|
||||||
common-obj-$(CONFIG_TPM) += tpm.o
|
common-obj-$(CONFIG_TPM) += tpm.o
|
||||||
|
|
||||||
common-obj-y += hostmem.o hostmem-ram.o
|
common-obj-y += hostmem.o hostmem-ram.o
|
||||||
|
@@ -222,7 +222,7 @@ cryptodev_backend_can_be_deleted(UserCreatable *uc, Error **errp)
|
|||||||
|
|
||||||
static void cryptodev_backend_instance_init(Object *obj)
|
static void cryptodev_backend_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
object_property_add(obj, "queues", "int",
|
object_property_add(obj, "queues", "uint32",
|
||||||
cryptodev_backend_get_queues,
|
cryptodev_backend_get_queues,
|
||||||
cryptodev_backend_set_queues,
|
cryptodev_backend_set_queues,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/rng.h"
|
#include "sysemu/rng.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char-fe.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ static void rng_egd_finalize(Object *obj)
|
|||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(obj);
|
RngEgd *s = RNG_EGD(obj);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&s->chr);
|
qemu_chr_fe_deinit(&s->chr, false);
|
||||||
g_free(s->chr_name);
|
g_free(s->chr_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
# See docs/tracing.txt for syntax documentation.
|
|
||||||
|
|
||||||
# backends/wctablet.c
|
|
||||||
wct_init(void) ""
|
|
||||||
wct_cmd_re(void) ""
|
|
||||||
wct_cmd_st(void) ""
|
|
||||||
wct_cmd_sp(void) ""
|
|
||||||
wct_cmd_ts(int input) "0x%02x"
|
|
||||||
wct_cmd_other(const char *cmd) "%s"
|
|
||||||
wct_speed(int speed) "%d"
|
|
||||||
|
60
block.c
60
block.c
@@ -163,11 +163,16 @@ void path_combine(char *dest, int dest_size,
|
|||||||
if (path_is_absolute(filename)) {
|
if (path_is_absolute(filename)) {
|
||||||
pstrcpy(dest, dest_size, filename);
|
pstrcpy(dest, dest_size, filename);
|
||||||
} else {
|
} else {
|
||||||
p = strchr(base_path, ':');
|
const char *protocol_stripped = NULL;
|
||||||
if (p)
|
|
||||||
p++;
|
if (path_has_protocol(base_path)) {
|
||||||
else
|
protocol_stripped = strchr(base_path, ':');
|
||||||
p = base_path;
|
if (protocol_stripped) {
|
||||||
|
protocol_stripped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = protocol_stripped ?: base_path;
|
||||||
|
|
||||||
p1 = strrchr(base_path, '/');
|
p1 = strrchr(base_path, '/');
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
@@ -192,6 +197,41 @@ void path_combine(char *dest, int dest_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function for bdrv_parse_filename() implementations to remove optional
|
||||||
|
* protocol prefixes (especially "file:") from a filename and for putting the
|
||||||
|
* stripped filename into the options QDict if there is such a prefix.
|
||||||
|
*/
|
||||||
|
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
|
||||||
|
QDict *options)
|
||||||
|
{
|
||||||
|
if (strstart(filename, prefix, &filename)) {
|
||||||
|
/* Stripping the explicit protocol prefix may result in a protocol
|
||||||
|
* prefix being (wrongly) detected (if the filename contains a colon) */
|
||||||
|
if (path_has_protocol(filename)) {
|
||||||
|
QString *fat_filename;
|
||||||
|
|
||||||
|
/* This means there is some colon before the first slash; therefore,
|
||||||
|
* this cannot be an absolute path */
|
||||||
|
assert(!path_is_absolute(filename));
|
||||||
|
|
||||||
|
/* And we can thus fix the protocol detection issue by prefixing it
|
||||||
|
* by "./" */
|
||||||
|
fat_filename = qstring_from_str("./");
|
||||||
|
qstring_append(fat_filename, filename);
|
||||||
|
|
||||||
|
assert(!path_has_protocol(qstring_get_str(fat_filename)));
|
||||||
|
|
||||||
|
qdict_put(options, "filename", fat_filename);
|
||||||
|
} else {
|
||||||
|
/* If no protocol prefix was detected, we can use the shortened
|
||||||
|
* filename as-is */
|
||||||
|
qdict_put_str(options, "filename", filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Returns whether the image file is opened as read-only. Note that this can
|
/* 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
|
* return false and writing to the image file is still not possible because the
|
||||||
* image is inactivated. */
|
* image is inactivated. */
|
||||||
@@ -280,6 +320,8 @@ BlockDriverState *bdrv_new(void)
|
|||||||
QLIST_INIT(&bs->op_blockers[i]);
|
QLIST_INIT(&bs->op_blockers[i]);
|
||||||
}
|
}
|
||||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||||
|
qemu_co_mutex_init(&bs->reqs_lock);
|
||||||
|
qemu_mutex_init(&bs->dirty_bitmap_mutex);
|
||||||
bs->refcnt = 1;
|
bs->refcnt = 1;
|
||||||
bs->aio_context = qemu_get_aio_context();
|
bs->aio_context = qemu_get_aio_context();
|
||||||
|
|
||||||
@@ -1260,7 +1302,9 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
|||||||
goto fail_opts;
|
goto fail_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
/* bdrv_new() and bdrv_close() make it so */
|
||||||
|
assert(atomic_read(&bs->copy_on_read) == 0);
|
||||||
|
|
||||||
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
|
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
|
||||||
if (!bs->read_only) {
|
if (!bs->read_only) {
|
||||||
bdrv_enable_copy_on_read(bs);
|
bdrv_enable_copy_on_read(bs);
|
||||||
@@ -3023,7 +3067,7 @@ static void bdrv_close(BlockDriverState *bs)
|
|||||||
|
|
||||||
g_free(bs->opaque);
|
g_free(bs->opaque);
|
||||||
bs->opaque = NULL;
|
bs->opaque = NULL;
|
||||||
bs->copy_on_read = 0;
|
atomic_set(&bs->copy_on_read, 0);
|
||||||
bs->backing_file[0] = '\0';
|
bs->backing_file[0] = '\0';
|
||||||
bs->backing_format[0] = '\0';
|
bs->backing_format[0] = '\0';
|
||||||
bs->total_sectors = 0;
|
bs->total_sectors = 0;
|
||||||
@@ -3382,7 +3426,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
|
|||||||
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);
|
||||||
bdrv_parent_cb_resize(bs);
|
bdrv_parent_cb_resize(bs);
|
||||||
++bs->write_gen;
|
atomic_inc(&bs->write_gen);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -32,23 +32,28 @@
|
|||||||
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
||||||
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
||||||
|
|
||||||
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
void block_acct_init(BlockAcctStats *stats)
|
||||||
bool account_failed)
|
|
||||||
{
|
{
|
||||||
stats->account_invalid = account_invalid;
|
qemu_mutex_init(&stats->lock);
|
||||||
stats->account_failed = account_failed;
|
|
||||||
|
|
||||||
if (qtest_enabled()) {
|
if (qtest_enabled()) {
|
||||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void block_acct_setup(BlockAcctStats *stats, bool account_invalid,
|
||||||
|
bool account_failed)
|
||||||
|
{
|
||||||
|
stats->account_invalid = account_invalid;
|
||||||
|
stats->account_failed = account_failed;
|
||||||
|
}
|
||||||
|
|
||||||
void block_acct_cleanup(BlockAcctStats *stats)
|
void block_acct_cleanup(BlockAcctStats *stats)
|
||||||
{
|
{
|
||||||
BlockAcctTimedStats *s, *next;
|
BlockAcctTimedStats *s, *next;
|
||||||
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
qemu_mutex_destroy(&stats->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||||
@@ -58,12 +63,15 @@ void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
|||||||
|
|
||||||
s = g_new0(BlockAcctTimedStats, 1);
|
s = g_new0(BlockAcctTimedStats, 1);
|
||||||
s->interval_length = interval_length;
|
s->interval_length = interval_length;
|
||||||
|
s->stats = stats;
|
||||||
|
qemu_mutex_lock(&stats->lock);
|
||||||
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
||||||
|
|
||||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||||
timed_average_init(&s->latency[i], clock_type,
|
timed_average_init(&s->latency[i], clock_type,
|
||||||
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&stats->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||||
@@ -86,7 +94,8 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
|||||||
cookie->type = type;
|
cookie->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||||
|
bool failed)
|
||||||
{
|
{
|
||||||
BlockAcctTimedStats *s;
|
BlockAcctTimedStats *s;
|
||||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||||
@@ -98,31 +107,16 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
|||||||
|
|
||||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||||
|
|
||||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
qemu_mutex_lock(&stats->lock);
|
||||||
stats->nr_ops[cookie->type]++;
|
|
||||||
stats->total_time_ns[cookie->type] += latency_ns;
|
|
||||||
stats->last_access_time_ns = time_ns;
|
|
||||||
|
|
||||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
if (failed) {
|
||||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
stats->failed_ops[cookie->type]++;
|
||||||
|
} else {
|
||||||
|
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||||
|
stats->nr_ops[cookie->type]++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
|
||||||
{
|
|
||||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
|
||||||
|
|
||||||
stats->failed_ops[cookie->type]++;
|
|
||||||
|
|
||||||
if (stats->account_failed) {
|
|
||||||
BlockAcctTimedStats *s;
|
|
||||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
|
||||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
|
||||||
|
|
||||||
if (qtest_enabled()) {
|
|
||||||
latency_ns = qtest_latency_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!failed || stats->account_failed) {
|
||||||
stats->total_time_ns[cookie->type] += latency_ns;
|
stats->total_time_ns[cookie->type] += latency_ns;
|
||||||
stats->last_access_time_ns = time_ns;
|
stats->last_access_time_ns = time_ns;
|
||||||
|
|
||||||
@@ -130,29 +124,45 @@ void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
|||||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_mutex_unlock(&stats->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||||
|
{
|
||||||
|
block_account_one_io(stats, cookie, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||||
|
{
|
||||||
|
block_account_one_io(stats, cookie, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
|
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
|
||||||
{
|
{
|
||||||
assert(type < BLOCK_MAX_IOTYPE);
|
assert(type < BLOCK_MAX_IOTYPE);
|
||||||
|
|
||||||
/* block_acct_done() and block_acct_failed() update
|
/* block_account_one_io() updates total_time_ns[], but this one does
|
||||||
* total_time_ns[], but this one does not. The reason is that
|
* not. The reason is that invalid requests are accounted during their
|
||||||
* invalid requests are accounted during their submission,
|
* submission, therefore there's no actual I/O involved.
|
||||||
* therefore there's no actual I/O involved. */
|
*/
|
||||||
|
qemu_mutex_lock(&stats->lock);
|
||||||
stats->invalid_ops[type]++;
|
stats->invalid_ops[type]++;
|
||||||
|
|
||||||
if (stats->account_invalid) {
|
if (stats->account_invalid) {
|
||||||
stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
|
stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&stats->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
|
||||||
int num_requests)
|
int num_requests)
|
||||||
{
|
{
|
||||||
assert(type < BLOCK_MAX_IOTYPE);
|
assert(type < BLOCK_MAX_IOTYPE);
|
||||||
|
|
||||||
|
qemu_mutex_lock(&stats->lock);
|
||||||
stats->merged[type] += num_requests;
|
stats->merged[type] += num_requests;
|
||||||
|
qemu_mutex_unlock(&stats->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
|
int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
|
||||||
@@ -167,7 +177,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats,
|
|||||||
|
|
||||||
assert(type < BLOCK_MAX_IOTYPE);
|
assert(type < BLOCK_MAX_IOTYPE);
|
||||||
|
|
||||||
|
qemu_mutex_lock(&stats->stats->lock);
|
||||||
sum = timed_average_sum(&stats->latency[type], &elapsed);
|
sum = timed_average_sum(&stats->latency[type], &elapsed);
|
||||||
|
qemu_mutex_unlock(&stats->stats->lock);
|
||||||
|
|
||||||
return (double) sum / elapsed;
|
return (double) sum / elapsed;
|
||||||
}
|
}
|
||||||
|
@@ -692,7 +692,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
if (job) {
|
if (job) {
|
||||||
backup_clean(&job->common);
|
backup_clean(&job->common);
|
||||||
block_job_unref(&job->common);
|
block_job_early_fail(&job->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@@ -31,7 +31,6 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "sysemu/qtest.h"
|
#include "sysemu/qtest.h"
|
||||||
|
|
||||||
|
@@ -168,7 +168,7 @@ static int blk_root_inactivate(BdrvChild *child)
|
|||||||
* this point because the VM is stopped) and unattached monitor-owned
|
* this point because the VM is stopped) and unattached monitor-owned
|
||||||
* BlockBackends. If there is still any other user like a block job, then
|
* BlockBackends. If there is still any other user like a block job, then
|
||||||
* we simply can't inactivate the image. */
|
* we simply can't inactivate the image. */
|
||||||
if (!blk->dev && !blk->name[0]) {
|
if (!blk->dev && !blk_name(blk)[0]) {
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,8 +216,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
|||||||
blk->shared_perm = shared_perm;
|
blk->shared_perm = shared_perm;
|
||||||
blk_set_enable_write_cache(blk, true);
|
blk_set_enable_write_cache(blk, true);
|
||||||
|
|
||||||
|
qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
|
||||||
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
|
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
|
||||||
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
|
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
|
||||||
|
block_acct_init(&blk->stats);
|
||||||
|
|
||||||
notifier_list_init(&blk->remove_bs_notifiers);
|
notifier_list_init(&blk->remove_bs_notifiers);
|
||||||
notifier_list_init(&blk->insert_bs_notifiers);
|
notifier_list_init(&blk->insert_bs_notifiers);
|
||||||
@@ -1953,7 +1955,7 @@ static void blk_root_drained_begin(BdrvChild *child)
|
|||||||
/* Note that blk->root may not be accessible here yet if we are just
|
/* Note that blk->root may not be accessible here yet if we are just
|
||||||
* attaching to a BlockDriverState that is drained. Use child instead. */
|
* attaching to a BlockDriverState that is drained. Use child instead. */
|
||||||
|
|
||||||
if (blk->public.io_limits_disabled++ == 0) {
|
if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
|
||||||
throttle_group_restart_blk(blk);
|
throttle_group_restart_blk(blk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1964,7 +1966,7 @@ static void blk_root_drained_end(BdrvChild *child)
|
|||||||
assert(blk->quiesce_counter);
|
assert(blk->quiesce_counter);
|
||||||
|
|
||||||
assert(blk->public.io_limits_disabled);
|
assert(blk->public.io_limits_disabled);
|
||||||
--blk->public.io_limits_disabled;
|
atomic_dec(&blk->public.io_limits_disabled);
|
||||||
|
|
||||||
if (--blk->quiesce_counter == 0) {
|
if (--blk->quiesce_counter == 0) {
|
||||||
if (blk->dev_ops && blk->dev_ops->drained_end) {
|
if (blk->dev_ops && blk->dev_ops->drained_end) {
|
||||||
|
@@ -89,6 +89,10 @@ static void commit_complete(BlockJob *job, void *opaque)
|
|||||||
int ret = data->ret;
|
int ret = data->ret;
|
||||||
bool remove_commit_top_bs = false;
|
bool remove_commit_top_bs = false;
|
||||||
|
|
||||||
|
/* Make sure overlay_bs and top stay around until bdrv_set_backing_hd() */
|
||||||
|
bdrv_ref(top);
|
||||||
|
bdrv_ref(overlay_bs);
|
||||||
|
|
||||||
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||||
* the normal backing chain can be restored. */
|
* the normal backing chain can be restored. */
|
||||||
blk_unref(s->base);
|
blk_unref(s->base);
|
||||||
@@ -124,6 +128,9 @@ static void commit_complete(BlockJob *job, void *opaque)
|
|||||||
if (remove_commit_top_bs) {
|
if (remove_commit_top_bs) {
|
||||||
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
|
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_unref(overlay_bs);
|
||||||
|
bdrv_unref(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn commit_run(void *opaque)
|
static void coroutine_fn commit_run(void *opaque)
|
||||||
@@ -426,7 +433,7 @@ fail:
|
|||||||
if (commit_top_bs) {
|
if (commit_top_bs) {
|
||||||
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
|
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
|
||||||
}
|
}
|
||||||
block_job_unref(&s->common);
|
block_job_early_fail(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||||
*/
|
*/
|
||||||
struct BdrvDirtyBitmap {
|
struct BdrvDirtyBitmap {
|
||||||
|
QemuMutex *mutex;
|
||||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||||
HBitmap *meta; /* Meta dirty bitmap */
|
HBitmap *meta; /* Meta dirty bitmap */
|
||||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||||
@@ -52,6 +53,27 @@ struct BdrvDirtyBitmapIter {
|
|||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline void bdrv_dirty_bitmaps_lock(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock(&bs->dirty_bitmap_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bdrv_dirty_bitmaps_unlock(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
qemu_mutex_unlock(&bs->dirty_bitmap_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called with BQL or dirty_bitmap lock taken. */
|
||||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm;
|
BdrvDirtyBitmap *bm;
|
||||||
@@ -65,6 +87,7 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
@@ -72,6 +95,7 @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
|||||||
bitmap->name = NULL;
|
bitmap->name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||||
uint32_t granularity,
|
uint32_t granularity,
|
||||||
const char *name,
|
const char *name,
|
||||||
@@ -96,11 +120,14 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||||
|
bitmap->mutex = &bs->dirty_bitmap_mutex;
|
||||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||||
bitmap->size = bitmap_size;
|
bitmap->size = bitmap_size;
|
||||||
bitmap->name = g_strdup(name);
|
bitmap->name = g_strdup(name);
|
||||||
bitmap->disabled = false;
|
bitmap->disabled = false;
|
||||||
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||||
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,20 +146,24 @@ void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
|||||||
int chunk_size)
|
int chunk_size)
|
||||||
{
|
{
|
||||||
assert(!bitmap->meta);
|
assert(!bitmap->meta);
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
bitmap->meta = hbitmap_create_meta(bitmap->bitmap,
|
bitmap->meta = hbitmap_create_meta(bitmap->bitmap,
|
||||||
chunk_size * BITS_PER_BYTE);
|
chunk_size * BITS_PER_BYTE);
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(bitmap->meta);
|
assert(bitmap->meta);
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
hbitmap_free_meta(bitmap->bitmap);
|
hbitmap_free_meta(bitmap->bitmap);
|
||||||
bitmap->meta = NULL;
|
bitmap->meta = NULL;
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
int bdrv_dirty_bitmap_get_meta_locked(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||||
int nb_sectors)
|
int nb_sectors)
|
||||||
{
|
{
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta);
|
int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta);
|
||||||
@@ -147,11 +178,26 @@ int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
|
||||||
|
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||||
|
int nb_sectors)
|
||||||
|
{
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
|
dirty = bdrv_dirty_bitmap_get_meta_locked(bs, bitmap, sector, nb_sectors);
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
|
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs,
|
void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *bitmap, int64_t sector,
|
BdrvDirtyBitmap *bitmap, int64_t sector,
|
||||||
int nb_sectors)
|
int nb_sectors)
|
||||||
{
|
{
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
hbitmap_reset(bitmap->meta, sector, nb_sectors);
|
hbitmap_reset(bitmap->meta, sector, nb_sectors);
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
|
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
|
||||||
@@ -164,16 +210,19 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
|
|||||||
return bitmap->name;
|
return bitmap->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->successor;
|
return bitmap->successor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return !(bitmap->disabled || bitmap->successor);
|
return !(bitmap->disabled || bitmap->successor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||||
@@ -188,6 +237,7 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
|||||||
/**
|
/**
|
||||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||||
* Requires that the bitmap is not frozen and has no successor.
|
* Requires that the bitmap is not frozen and has no successor.
|
||||||
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
@@ -220,6 +270,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|||||||
/**
|
/**
|
||||||
* For a bitmap with a successor, yield our name to the successor,
|
* For a bitmap with a successor, yield our name to the successor,
|
||||||
* delete the old bitmap, and return a handle to the new bitmap.
|
* delete the old bitmap, and return a handle to the new bitmap.
|
||||||
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *bitmap,
|
BdrvDirtyBitmap *bitmap,
|
||||||
@@ -247,6 +298,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
|||||||
* In cases of failure where we can no longer safely delete the parent,
|
* In cases of failure where we can no longer safely delete the parent,
|
||||||
* we may wish to re-join the parent and child/successor.
|
* we may wish to re-join the parent and child/successor.
|
||||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||||
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *parent,
|
BdrvDirtyBitmap *parent,
|
||||||
@@ -271,25 +323,30 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Truncates _all_ bitmaps attached to a BDS.
|
* Truncates _all_ bitmaps attached to a BDS.
|
||||||
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
uint64_t size = bdrv_nb_sectors(bs);
|
uint64_t size = bdrv_nb_sectors(bs);
|
||||||
|
|
||||||
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
assert(!bitmap->active_iterators);
|
assert(!bitmap->active_iterators);
|
||||||
hbitmap_truncate(bitmap->bitmap, size);
|
hbitmap_truncate(bitmap->bitmap, size);
|
||||||
bitmap->size = size;
|
bitmap->size = size;
|
||||||
}
|
}
|
||||||
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||||
BdrvDirtyBitmap *bitmap,
|
BdrvDirtyBitmap *bitmap,
|
||||||
bool only_named)
|
bool only_named)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm, *next;
|
BdrvDirtyBitmap *bm, *next;
|
||||||
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||||
assert(!bm->active_iterators);
|
assert(!bm->active_iterators);
|
||||||
@@ -301,15 +358,19 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
|||||||
g_free(bm);
|
g_free(bm);
|
||||||
|
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||||
@@ -318,18 +379,21 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
|||||||
/**
|
/**
|
||||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||||
* There must not be any frozen bitmaps attached.
|
* There must not be any frozen bitmaps attached.
|
||||||
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
bitmap->disabled = true;
|
bitmap->disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
@@ -342,6 +406,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
|||||||
BlockDirtyInfoList *list = NULL;
|
BlockDirtyInfoList *list = NULL;
|
||||||
BlockDirtyInfoList **plist = &list;
|
BlockDirtyInfoList **plist = &list;
|
||||||
|
|
||||||
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||||
@@ -354,12 +419,14 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
|||||||
*plist = entry;
|
*plist = entry;
|
||||||
plist = &entry->next;
|
plist = &entry->next;
|
||||||
}
|
}
|
||||||
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
int64_t sector)
|
int bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t sector)
|
||||||
{
|
{
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
return hbitmap_get(bitmap->bitmap, sector);
|
return hbitmap_get(bitmap->bitmap, sector);
|
||||||
@@ -432,23 +499,42 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
|||||||
return hbitmap_iter_next(&iter->hbi);
|
return hbitmap_iter_next(&iter->hbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
int64_t cur_sector, int64_t nr_sectors)
|
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t cur_sector, int64_t nr_sectors)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t cur_sector, int64_t nr_sectors)
|
int64_t cur_sector, int64_t nr_sectors)
|
||||||
|
{
|
||||||
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
|
bdrv_set_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors);
|
||||||
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
|
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t cur_sector, int64_t nr_sectors)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t cur_sector, int64_t nr_sectors)
|
||||||
|
{
|
||||||
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
|
bdrv_reset_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors);
|
||||||
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
hbitmap_reset_all(bitmap->bitmap);
|
hbitmap_reset_all(bitmap->bitmap);
|
||||||
} else {
|
} else {
|
||||||
@@ -457,6 +543,7 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
|||||||
hbitmap_granularity(backup));
|
hbitmap_granularity(backup));
|
||||||
*out = backup;
|
*out = backup;
|
||||||
}
|
}
|
||||||
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||||
@@ -508,12 +595,19 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
|||||||
int64_t nr_sectors)
|
int64_t nr_sectors)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
|
|
||||||
|
if (QLIST_EMPTY(&bs->dirty_bitmaps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
}
|
}
|
||||||
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -381,12 +381,7 @@ static void raw_parse_flags(int bdrv_flags, int *open_flags)
|
|||||||
static void raw_parse_filename(const char *filename, QDict *options,
|
static void raw_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* The filename does not have to be prefixed by the protocol name, since
|
bdrv_parse_filename_strip_prefix(filename, "file:", options);
|
||||||
* "file" is the default protocol; therefore, the return value of this
|
|
||||||
* function call can be ignored. */
|
|
||||||
strstart(filename, "file:", &filename);
|
|
||||||
|
|
||||||
qdict_put_str(options, "filename", filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList raw_runtime_opts = {
|
static QemuOptsList raw_runtime_opts = {
|
||||||
@@ -2395,10 +2390,7 @@ static int check_hdev_writable(BDRVRawState *s)
|
|||||||
static void hdev_parse_filename(const char *filename, QDict *options,
|
static void hdev_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* The prefix is optional, just as for "file". */
|
bdrv_parse_filename_strip_prefix(filename, "host_device:", options);
|
||||||
strstart(filename, "host_device:", &filename);
|
|
||||||
|
|
||||||
qdict_put_str(options, "filename", filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hdev_is_sg(BlockDriverState *bs)
|
static bool hdev_is_sg(BlockDriverState *bs)
|
||||||
@@ -2697,10 +2689,7 @@ static BlockDriver bdrv_host_device = {
|
|||||||
static void cdrom_parse_filename(const char *filename, QDict *options,
|
static void cdrom_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* The prefix is optional, just as for "file". */
|
bdrv_parse_filename_strip_prefix(filename, "host_cdrom:", options);
|
||||||
strstart(filename, "host_cdrom:", &filename);
|
|
||||||
|
|
||||||
qdict_put_str(options, "filename", filename);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -276,12 +276,7 @@ static void raw_parse_flags(int flags, bool use_aio, int *access_flags,
|
|||||||
static void raw_parse_filename(const char *filename, QDict *options,
|
static void raw_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* The filename does not have to be prefixed by the protocol name, since
|
bdrv_parse_filename_strip_prefix(filename, "file:", options);
|
||||||
* "file" is the default protocol; therefore, the return value of this
|
|
||||||
* function call can be ignored. */
|
|
||||||
strstart(filename, "file:", &filename);
|
|
||||||
|
|
||||||
qdict_put_str(options, "filename", filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList raw_runtime_opts = {
|
static QemuOptsList raw_runtime_opts = {
|
||||||
@@ -671,10 +666,7 @@ static int hdev_probe_device(const char *filename)
|
|||||||
static void hdev_parse_filename(const char *filename, QDict *options,
|
static void hdev_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* The prefix is optional, just as for "file". */
|
bdrv_parse_filename_strip_prefix(filename, "host_device:", options);
|
||||||
strstart(filename, "host_device:", &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,
|
||||||
|
@@ -493,8 +493,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
const char *ptr;
|
const char *ptr;
|
||||||
size_t num_servers;
|
int i, type, num_servers;
|
||||||
int i, type;
|
|
||||||
|
|
||||||
/* create opts info from runtime_json_opts list */
|
/* create opts info from runtime_json_opts list */
|
||||||
opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
|
||||||
@@ -964,29 +963,6 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
|
|||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
return acb.ret;
|
return acb.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool gluster_supports_zerofill(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
|
||||||
int64_t size)
|
|
||||||
{
|
|
||||||
return glfs_zerofill(fd, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
static inline bool gluster_supports_zerofill(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
|
||||||
int64_t size)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int qemu_gluster_create(const char *filename,
|
static int qemu_gluster_create(const char *filename,
|
||||||
@@ -996,9 +972,10 @@ static int qemu_gluster_create(const char *filename,
|
|||||||
struct glfs *glfs;
|
struct glfs *glfs;
|
||||||
struct glfs_fd *fd;
|
struct glfs_fd *fd;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int prealloc = 0;
|
PreallocMode prealloc;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
gconf = g_new0(BlockdevOptionsGluster, 1);
|
gconf = g_new0(BlockdevOptionsGluster, 1);
|
||||||
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
|
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
|
||||||
@@ -1026,13 +1003,12 @@ static int qemu_gluster_create(const char *filename,
|
|||||||
BDRV_SECTOR_SIZE);
|
BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||||
if (!tmp || !strcmp(tmp, "off")) {
|
prealloc = qapi_enum_parse(PreallocMode_lookup, tmp,
|
||||||
prealloc = 0;
|
PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
|
||||||
} else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) {
|
&local_err);
|
||||||
prealloc = 1;
|
g_free(tmp);
|
||||||
} else {
|
if (local_err) {
|
||||||
error_setg(errp, "Invalid preallocation mode: '%s'"
|
error_propagate(errp, local_err);
|
||||||
" or GlusterFS doesn't support zerofill API", tmp);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1041,21 +1017,48 @@ static int qemu_gluster_create(const char *filename,
|
|||||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
|
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
} else {
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (prealloc) {
|
||||||
|
#ifdef CONFIG_GLUSTERFS_FALLOCATE
|
||||||
|
case PREALLOC_MODE_FALLOC:
|
||||||
|
if (glfs_fallocate(fd, 0, 0, total_size)) {
|
||||||
|
error_setg(errp, "Could not preallocate data for the new file");
|
||||||
|
ret = -errno;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_GLUSTERFS_FALLOCATE */
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
case PREALLOC_MODE_FULL:
|
||||||
if (!glfs_ftruncate(fd, total_size)) {
|
if (!glfs_ftruncate(fd, total_size)) {
|
||||||
if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) {
|
if (glfs_zerofill(fd, 0, total_size)) {
|
||||||
|
error_setg(errp, "Could not zerofill the new file");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
error_setg(errp, "Could not resize file");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_GLUSTERFS_ZEROFILL */
|
||||||
|
case PREALLOC_MODE_OFF:
|
||||||
|
if (glfs_ftruncate(fd, total_size) != 0) {
|
||||||
|
ret = -errno;
|
||||||
|
error_setg(errp, "Could not resize file");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_setg(errp, "Unsupported preallocation mode: %s",
|
||||||
|
PreallocMode_lookup[prealloc]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (glfs_close(fd) != 0) {
|
if (glfs_close(fd) != 0) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
g_free(tmp);
|
|
||||||
qapi_free_BlockdevOptionsGluster(gconf);
|
qapi_free_BlockdevOptionsGluster(gconf);
|
||||||
glfs_clear_preopened(glfs);
|
glfs_clear_preopened(glfs);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1275,7 +1278,14 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
|||||||
if (offs < 0) {
|
if (offs < 0) {
|
||||||
return -errno; /* D3 or D4 */
|
return -errno; /* D3 or D4 */
|
||||||
}
|
}
|
||||||
assert(offs >= start);
|
|
||||||
|
if (offs < start) {
|
||||||
|
/* This is not a valid return by lseek(). We are safe to just return
|
||||||
|
* -EIO in this case, and we'll treat it like D4. Unfortunately some
|
||||||
|
* versions of gluster server will return offs < start, so an assert
|
||||||
|
* here will unnecessarily abort QEMU. */
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (offs > start) {
|
if (offs > start) {
|
||||||
/* D2: in hole, next data at offs */
|
/* D2: in hole, next data at offs */
|
||||||
@@ -1307,7 +1317,14 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
|||||||
if (offs < 0) {
|
if (offs < 0) {
|
||||||
return -errno; /* D1 and (H3 or H4) */
|
return -errno; /* D1 and (H3 or H4) */
|
||||||
}
|
}
|
||||||
assert(offs >= start);
|
|
||||||
|
if (offs < start) {
|
||||||
|
/* This is not a valid return by lseek(). We are safe to just return
|
||||||
|
* -EIO in this case, and we'll treat it like H4. Unfortunately some
|
||||||
|
* versions of gluster server will return offs < start, so an assert
|
||||||
|
* here will unnecessarily abort QEMU. */
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (offs > start) {
|
if (offs > start) {
|
||||||
/*
|
/*
|
||||||
|
70
block/io.c
70
block/io.c
@@ -26,6 +26,7 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "block/blockjob.h"
|
#include "block/blockjob.h"
|
||||||
|
#include "block/blockjob_int.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
@@ -129,13 +130,13 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
|||||||
*/
|
*/
|
||||||
void bdrv_enable_copy_on_read(BlockDriverState *bs)
|
void bdrv_enable_copy_on_read(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
bs->copy_on_read++;
|
atomic_inc(&bs->copy_on_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
assert(bs->copy_on_read > 0);
|
int old = atomic_fetch_dec(&bs->copy_on_read);
|
||||||
bs->copy_on_read--;
|
assert(old >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if any requests are in-flight (including throttled requests) */
|
/* Check if any requests are in-flight (including throttled requests) */
|
||||||
@@ -240,7 +241,7 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bs->quiesce_counter++) {
|
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
||||||
aio_disable_external(bdrv_get_aio_context(bs));
|
aio_disable_external(bdrv_get_aio_context(bs));
|
||||||
bdrv_parent_drained_begin(bs);
|
bdrv_parent_drained_begin(bs);
|
||||||
}
|
}
|
||||||
@@ -251,7 +252,7 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
|||||||
void bdrv_drained_end(BlockDriverState *bs)
|
void bdrv_drained_end(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
assert(bs->quiesce_counter > 0);
|
assert(bs->quiesce_counter > 0);
|
||||||
if (--bs->quiesce_counter > 0) {
|
if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,16 +302,9 @@ void bdrv_drain_all_begin(void)
|
|||||||
bool waited = true;
|
bool waited = true;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvNextIterator it;
|
BdrvNextIterator it;
|
||||||
BlockJob *job = NULL;
|
|
||||||
GSList *aio_ctxs = NULL, *ctx;
|
GSList *aio_ctxs = NULL, *ctx;
|
||||||
|
|
||||||
while ((job = block_job_next(job))) {
|
block_job_pause_all();
|
||||||
AioContext *aio_context = blk_get_aio_context(job->blk);
|
|
||||||
|
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
block_job_pause(job);
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||||
@@ -354,7 +348,6 @@ void bdrv_drain_all_end(void)
|
|||||||
{
|
{
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvNextIterator it;
|
BdrvNextIterator it;
|
||||||
BlockJob *job = NULL;
|
|
||||||
|
|
||||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||||
@@ -365,13 +358,7 @@ void bdrv_drain_all_end(void)
|
|||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((job = block_job_next(job))) {
|
block_job_resume_all();
|
||||||
AioContext *aio_context = blk_get_aio_context(job->blk);
|
|
||||||
|
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
block_job_resume(job);
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_drain_all(void)
|
void bdrv_drain_all(void)
|
||||||
@@ -388,11 +375,13 @@ void bdrv_drain_all(void)
|
|||||||
static void tracked_request_end(BdrvTrackedRequest *req)
|
static void tracked_request_end(BdrvTrackedRequest *req)
|
||||||
{
|
{
|
||||||
if (req->serialising) {
|
if (req->serialising) {
|
||||||
req->bs->serialising_in_flight--;
|
atomic_dec(&req->bs->serialising_in_flight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&req->bs->reqs_lock);
|
||||||
QLIST_REMOVE(req, list);
|
QLIST_REMOVE(req, list);
|
||||||
qemu_co_queue_restart_all(&req->wait_queue);
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
|
qemu_co_mutex_unlock(&req->bs->reqs_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -417,7 +406,9 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
|
|||||||
|
|
||||||
qemu_co_queue_init(&req->wait_queue);
|
qemu_co_queue_init(&req->wait_queue);
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||||
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
|
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
|
||||||
|
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||||
@@ -427,7 +418,7 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
|||||||
- overlap_offset;
|
- overlap_offset;
|
||||||
|
|
||||||
if (!req->serialising) {
|
if (!req->serialising) {
|
||||||
req->bs->serialising_in_flight++;
|
atomic_inc(&req->bs->serialising_in_flight);
|
||||||
req->serialising = true;
|
req->serialising = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,7 +505,8 @@ static void dummy_bh_cb(void *opaque)
|
|||||||
|
|
||||||
void bdrv_wakeup(BlockDriverState *bs)
|
void bdrv_wakeup(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
if (bs->wakeup) {
|
/* The barrier (or an atomic op) is in the caller. */
|
||||||
|
if (atomic_read(&bs->wakeup)) {
|
||||||
aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
|
aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,12 +524,13 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
|||||||
bool retry;
|
bool retry;
|
||||||
bool waited = false;
|
bool waited = false;
|
||||||
|
|
||||||
if (!bs->serialising_in_flight) {
|
if (!atomic_read(&bs->serialising_in_flight)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
retry = false;
|
retry = false;
|
||||||
|
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||||
QLIST_FOREACH(req, &bs->tracked_requests, list) {
|
QLIST_FOREACH(req, &bs->tracked_requests, list) {
|
||||||
if (req == self || (!req->serialising && !self->serialising)) {
|
if (req == self || (!req->serialising && !self->serialising)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -556,7 +549,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
|||||||
* (instead of producing a deadlock in the former case). */
|
* (instead of producing a deadlock in the former case). */
|
||||||
if (!req->waiting_for) {
|
if (!req->waiting_for) {
|
||||||
self->waiting_for = req;
|
self->waiting_for = req;
|
||||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
|
||||||
self->waiting_for = NULL;
|
self->waiting_for = NULL;
|
||||||
retry = true;
|
retry = true;
|
||||||
waited = true;
|
waited = true;
|
||||||
@@ -564,6 +557,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||||
} while (retry);
|
} while (retry);
|
||||||
|
|
||||||
return waited;
|
return waited;
|
||||||
@@ -1157,7 +1151,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
|
|||||||
bdrv_inc_in_flight(bs);
|
bdrv_inc_in_flight(bs);
|
||||||
|
|
||||||
/* Don't do copy-on-read if we read data before write operation */
|
/* Don't do copy-on-read if we read data before write operation */
|
||||||
if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
if (atomic_read(&bs->copy_on_read) && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||||
flags |= BDRV_REQ_COPY_ON_READ;
|
flags |= BDRV_REQ_COPY_ON_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1414,12 +1408,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
|
|||||||
}
|
}
|
||||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
|
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
|
||||||
|
|
||||||
++bs->write_gen;
|
atomic_inc(&bs->write_gen);
|
||||||
bdrv_set_dirty(bs, start_sector, end_sector - start_sector);
|
bdrv_set_dirty(bs, start_sector, end_sector - start_sector);
|
||||||
|
|
||||||
if (bs->wr_highest_offset < offset + bytes) {
|
stat64_max(&bs->wr_highest_offset, offset + bytes);
|
||||||
bs->wr_highest_offset = offset + bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
bs->total_sectors = MAX(bs->total_sectors, end_sector);
|
bs->total_sectors = MAX(bs->total_sectors, end_sector);
|
||||||
@@ -2305,14 +2297,17 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
|||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_gen = bs->write_gen;
|
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||||
|
current_gen = atomic_read(&bs->write_gen);
|
||||||
|
|
||||||
/* Wait until any previous flushes are completed */
|
/* Wait until any previous flushes are completed */
|
||||||
while (bs->active_flush_req) {
|
while (bs->active_flush_req) {
|
||||||
qemu_co_queue_wait(&bs->flush_queue, NULL);
|
qemu_co_queue_wait(&bs->flush_queue, &bs->reqs_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Flushes reach this point in nondecreasing current_gen order. */
|
||||||
bs->active_flush_req = true;
|
bs->active_flush_req = true;
|
||||||
|
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||||
|
|
||||||
/* Write back all layers by calling one driver function */
|
/* Write back all layers by calling one driver function */
|
||||||
if (bs->drv->bdrv_co_flush) {
|
if (bs->drv->bdrv_co_flush) {
|
||||||
@@ -2384,9 +2379,12 @@ out:
|
|||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
bs->flushed_gen = current_gen;
|
bs->flushed_gen = current_gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||||
bs->active_flush_req = false;
|
bs->active_flush_req = false;
|
||||||
/* Return value is ignored - it's ok if wait queue is empty */
|
/* Return value is ignored - it's ok if wait queue is empty */
|
||||||
qemu_co_queue_next(&bs->flush_queue);
|
qemu_co_queue_next(&bs->flush_queue);
|
||||||
|
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||||
|
|
||||||
early_exit:
|
early_exit:
|
||||||
bdrv_dec_in_flight(bs);
|
bdrv_dec_in_flight(bs);
|
||||||
@@ -2530,7 +2528,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset,
|
|||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
++bs->write_gen;
|
atomic_inc(&bs->write_gen);
|
||||||
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
|
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
|
||||||
req.bytes >> BDRV_SECTOR_BITS);
|
req.bytes >> BDRV_SECTOR_BITS);
|
||||||
tracked_request_end(&req);
|
tracked_request_end(&req);
|
||||||
@@ -2657,7 +2655,7 @@ void bdrv_io_plug(BlockDriverState *bs)
|
|||||||
bdrv_io_plug(child->bs);
|
bdrv_io_plug(child->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->io_plugged++ == 0) {
|
if (atomic_fetch_inc(&bs->io_plugged) == 0) {
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
if (drv && drv->bdrv_io_plug) {
|
if (drv && drv->bdrv_io_plug) {
|
||||||
drv->bdrv_io_plug(bs);
|
drv->bdrv_io_plug(bs);
|
||||||
@@ -2670,7 +2668,7 @@ void bdrv_io_unplug(BlockDriverState *bs)
|
|||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
|
|
||||||
assert(bs->io_plugged);
|
assert(bs->io_plugged);
|
||||||
if (--bs->io_plugged == 0) {
|
if (atomic_fetch_dec(&bs->io_plugged) == 1) {
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
if (drv && drv->bdrv_io_unplug) {
|
if (drv && drv->bdrv_io_unplug) {
|
||||||
drv->bdrv_io_unplug(bs);
|
drv->bdrv_io_unplug(bs);
|
||||||
|
@@ -1732,6 +1732,10 @@ static QemuOptsList runtime_opts = {
|
|||||||
.name = "timeout",
|
.name = "timeout",
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "filename",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1747,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
char *initiator_name = NULL;
|
char *initiator_name = NULL;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *transport_name, *portal, *target;
|
const char *transport_name, *portal, *target, *filename;
|
||||||
#if LIBISCSI_API_VERSION >= (20160603)
|
#if LIBISCSI_API_VERSION >= (20160603)
|
||||||
enum iscsi_transport_type transport;
|
enum iscsi_transport_type transport;
|
||||||
#endif
|
#endif
|
||||||
int i, ret = 0, timeout = 0, lun;
|
int i, ret = 0, timeout = 0, lun;
|
||||||
|
|
||||||
|
/* If we are given a filename, parse the filename, with precedence given to
|
||||||
|
* filename encoded options */
|
||||||
|
filename = qdict_get_try_str(options, "filename");
|
||||||
|
if (filename) {
|
||||||
|
error_report("Warning: 'filename' option specified. "
|
||||||
|
"This is an unsupported option, and may be deprecated "
|
||||||
|
"in the future");
|
||||||
|
iscsi_parse_filename(filename, options, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -1967,6 +1986,7 @@ out:
|
|||||||
}
|
}
|
||||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||||
}
|
}
|
||||||
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -342,6 +342,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
|
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
|
||||||
MAX_IO_SECTORS);
|
MAX_IO_SECTORS);
|
||||||
|
|
||||||
|
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||||
sector_num = bdrv_dirty_iter_next(s->dbi);
|
sector_num = bdrv_dirty_iter_next(s->dbi);
|
||||||
if (sector_num < 0) {
|
if (sector_num < 0) {
|
||||||
bdrv_set_dirty_iter(s->dbi, 0);
|
bdrv_set_dirty_iter(s->dbi, 0);
|
||||||
@@ -349,6 +350,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||||
assert(sector_num >= 0);
|
assert(sector_num >= 0);
|
||||||
}
|
}
|
||||||
|
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||||
|
|
||||||
first_chunk = sector_num / sectors_per_chunk;
|
first_chunk = sector_num / sectors_per_chunk;
|
||||||
while (test_bit(first_chunk, s->in_flight_bitmap)) {
|
while (test_bit(first_chunk, s->in_flight_bitmap)) {
|
||||||
@@ -360,12 +362,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
|
|
||||||
/* Find the number of consective dirty chunks following the first dirty
|
/* Find the number of consective dirty chunks following the first dirty
|
||||||
* one, and wait for in flight requests in them. */
|
* one, and wait for in flight requests in them. */
|
||||||
|
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||||
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||||
int64_t next_dirty;
|
int64_t next_dirty;
|
||||||
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||||
int64_t next_chunk = next_sector / sectors_per_chunk;
|
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||||
if (next_sector >= end ||
|
if (next_sector >= end ||
|
||||||
!bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
!bdrv_get_dirty_locked(source, s->dirty_bitmap, next_sector)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||||
@@ -386,8 +389,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
* calling bdrv_get_block_status_above could yield - if some blocks are
|
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||||
* marked dirty in this window, we need to know.
|
* marked dirty in this window, we need to know.
|
||||||
*/
|
*/
|
||||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num,
|
bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, sector_num,
|
||||||
nb_chunks * sectors_per_chunk);
|
nb_chunks * sectors_per_chunk);
|
||||||
|
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||||
|
|
||||||
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||||
while (nb_chunks > 0 && sector_num < end) {
|
while (nb_chunks > 0 && sector_num < end) {
|
||||||
int64_t ret;
|
int64_t ret;
|
||||||
@@ -506,6 +511,8 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
|||||||
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||||
|
|
||||||
/* Make sure that the source BDS doesn't go away before we called
|
/* Make sure that the source BDS doesn't go away before we called
|
||||||
* block_job_completed(). */
|
* block_job_completed(). */
|
||||||
bdrv_ref(src);
|
bdrv_ref(src);
|
||||||
@@ -514,7 +521,12 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
|||||||
|
|
||||||
/* Remove target parent that still uses BLK_PERM_WRITE/RESIZE before
|
/* Remove target parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||||
* inserting target_bs at s->to_replace, where we might not be able to get
|
* inserting target_bs at s->to_replace, where we might not be able to get
|
||||||
* these permissions. */
|
* these permissions.
|
||||||
|
*
|
||||||
|
* Note that blk_unref() alone doesn't necessarily drop permissions because
|
||||||
|
* we might be running nested inside mirror_drain(), which takes an extra
|
||||||
|
* reference, so use an explicit blk_set_perm() first. */
|
||||||
|
blk_set_perm(s->target, 0, BLK_PERM_ALL, &error_abort);
|
||||||
blk_unref(s->target);
|
blk_unref(s->target);
|
||||||
s->target = NULL;
|
s->target = NULL;
|
||||||
|
|
||||||
@@ -899,7 +911,6 @@ immediate_exit:
|
|||||||
g_free(s->cow_bitmap);
|
g_free(s->cow_bitmap);
|
||||||
g_free(s->in_flight_bitmap);
|
g_free(s->in_flight_bitmap);
|
||||||
bdrv_dirty_iter_free(s->dbi);
|
bdrv_dirty_iter_free(s->dbi);
|
||||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
|
||||||
|
|
||||||
data = g_malloc(sizeof(*data));
|
data = g_malloc(sizeof(*data));
|
||||||
data->ret = ret;
|
data->ret = ret;
|
||||||
@@ -1252,7 +1263,7 @@ fail:
|
|||||||
|
|
||||||
g_free(s->replaces);
|
g_free(s->replaces);
|
||||||
blk_unref(s->target);
|
blk_unref(s->target);
|
||||||
block_job_unref(&s->common);
|
block_job_early_fail(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "nbd-client.h"
|
#include "nbd-client.h"
|
||||||
|
|
||||||
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
|
||||||
@@ -70,10 +71,14 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
|||||||
NBDClientSession *s = opaque;
|
NBDClientSession *s = opaque;
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
int ret;
|
int ret;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
assert(s->reply.handle == 0);
|
assert(s->reply.handle == 0);
|
||||||
ret = nbd_receive_reply(s->ioc, &s->reply);
|
ret = nbd_receive_reply(s->ioc, &s->reply, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -114,6 +119,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
|||||||
int rc, ret, i;
|
int rc, ret, i;
|
||||||
|
|
||||||
qemu_co_mutex_lock(&s->send_mutex);
|
qemu_co_mutex_lock(&s->send_mutex);
|
||||||
|
while (s->in_flight == MAX_NBD_REQUESTS) {
|
||||||
|
qemu_co_queue_wait(&s->free_sema, &s->send_mutex);
|
||||||
|
}
|
||||||
|
s->in_flight++;
|
||||||
|
|
||||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||||
if (s->recv_coroutine[i] == NULL) {
|
if (s->recv_coroutine[i] == NULL) {
|
||||||
@@ -135,8 +144,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
|||||||
qio_channel_set_cork(s->ioc, true);
|
qio_channel_set_cork(s->ioc, true);
|
||||||
rc = nbd_send_request(s->ioc, request);
|
rc = nbd_send_request(s->ioc, request);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false,
|
||||||
false);
|
NULL);
|
||||||
if (ret != request->len) {
|
if (ret != request->len) {
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
}
|
}
|
||||||
@@ -164,8 +173,8 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
|||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
} else {
|
} else {
|
||||||
if (qiov && reply->error == 0) {
|
if (qiov && reply->error == 0) {
|
||||||
ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
|
ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true,
|
||||||
true);
|
NULL);
|
||||||
if (ret != request->len) {
|
if (ret != request->len) {
|
||||||
reply->error = EIO;
|
reply->error = EIO;
|
||||||
}
|
}
|
||||||
@@ -176,20 +185,6 @@ static void nbd_co_receive_reply(NBDClientSession *s,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nbd_coroutine_start(NBDClientSession *s,
|
|
||||||
NBDRequest *request)
|
|
||||||
{
|
|
||||||
/* Poor man semaphore. The free_sema is locked when no other request
|
|
||||||
* can be accepted, and unlocked after receiving one reply. */
|
|
||||||
if (s->in_flight == MAX_NBD_REQUESTS) {
|
|
||||||
qemu_co_queue_wait(&s->free_sema, NULL);
|
|
||||||
assert(s->in_flight < MAX_NBD_REQUESTS);
|
|
||||||
}
|
|
||||||
s->in_flight++;
|
|
||||||
|
|
||||||
/* s->recv_coroutine[i] is set as soon as we get the send_lock. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nbd_coroutine_end(BlockDriverState *bs,
|
static void nbd_coroutine_end(BlockDriverState *bs,
|
||||||
NBDRequest *request)
|
NBDRequest *request)
|
||||||
{
|
{
|
||||||
@@ -197,13 +192,16 @@ static void nbd_coroutine_end(BlockDriverState *bs,
|
|||||||
int i = HANDLE_TO_INDEX(s, request->handle);
|
int i = HANDLE_TO_INDEX(s, request->handle);
|
||||||
|
|
||||||
s->recv_coroutine[i] = NULL;
|
s->recv_coroutine[i] = NULL;
|
||||||
s->in_flight--;
|
|
||||||
qemu_co_queue_next(&s->free_sema);
|
|
||||||
|
|
||||||
/* Kick the read_reply_co to get the next reply. */
|
/* Kick the read_reply_co to get the next reply. */
|
||||||
if (s->read_reply_co) {
|
if (s->read_reply_co) {
|
||||||
aio_co_wake(s->read_reply_co);
|
aio_co_wake(s->read_reply_co);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&s->send_mutex);
|
||||||
|
s->in_flight--;
|
||||||
|
qemu_co_queue_next(&s->free_sema);
|
||||||
|
qemu_co_mutex_unlock(&s->send_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
@@ -221,7 +219,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|||||||
assert(bytes <= NBD_MAX_BUFFER_SIZE);
|
assert(bytes <= NBD_MAX_BUFFER_SIZE);
|
||||||
assert(!flags);
|
assert(!flags);
|
||||||
|
|
||||||
nbd_coroutine_start(client, &request);
|
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
@@ -251,7 +248,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
assert(bytes <= NBD_MAX_BUFFER_SIZE);
|
assert(bytes <= NBD_MAX_BUFFER_SIZE);
|
||||||
|
|
||||||
nbd_coroutine_start(client, &request);
|
|
||||||
ret = nbd_co_send_request(bs, &request, qiov);
|
ret = nbd_co_send_request(bs, &request, qiov);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
@@ -286,7 +282,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
|
|||||||
request.flags |= NBD_CMD_FLAG_NO_HOLE;
|
request.flags |= NBD_CMD_FLAG_NO_HOLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nbd_coroutine_start(client, &request);
|
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
@@ -311,7 +306,6 @@ int nbd_client_co_flush(BlockDriverState *bs)
|
|||||||
request.from = 0;
|
request.from = 0;
|
||||||
request.len = 0;
|
request.len = 0;
|
||||||
|
|
||||||
nbd_coroutine_start(client, &request);
|
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
@@ -337,7 +331,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nbd_coroutine_start(client, &request);
|
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
reply.error = -ret;
|
reply.error = -ret;
|
||||||
|
@@ -37,7 +37,6 @@
|
|||||||
#include "qapi/qobject-output-visitor.h"
|
#include "qapi/qobject-output-visitor.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
|
@@ -36,7 +36,6 @@
|
|||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
@@ -730,7 +729,9 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
|
|||||||
if (task->ret < 0) {
|
if (task->ret < 0) {
|
||||||
error_report("NFS Error: %s", nfs_get_error(nfs));
|
error_report("NFS Error: %s", nfs_get_error(nfs));
|
||||||
}
|
}
|
||||||
task->complete = 1;
|
|
||||||
|
/* Set task->complete before reading bs->wakeup. */
|
||||||
|
atomic_mb_set(&task->complete, 1);
|
||||||
bdrv_wakeup(task->bs);
|
bdrv_wakeup(task->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
block/qapi.c
15
block/qapi.c
@@ -441,7 +441,7 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
|
|||||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||||
}
|
}
|
||||||
|
|
||||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
|
||||||
|
|
||||||
if (bs->file) {
|
if (bs->file) {
|
||||||
s->has_parent = true;
|
s->has_parent = true;
|
||||||
@@ -595,9 +595,11 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
|||||||
int comp_indent, QObject *obj)
|
int comp_indent, QObject *obj)
|
||||||
{
|
{
|
||||||
switch (qobject_type(obj)) {
|
switch (qobject_type(obj)) {
|
||||||
case QTYPE_QINT: {
|
case QTYPE_QNUM: {
|
||||||
QInt *value = qobject_to_qint(obj);
|
QNum *value = qobject_to_qnum(obj);
|
||||||
func_fprintf(f, "%" PRId64, qint_get_int(value));
|
char *tmp = qnum_to_string(value);
|
||||||
|
func_fprintf(f, "%s", tmp);
|
||||||
|
g_free(tmp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QSTRING: {
|
case QTYPE_QSTRING: {
|
||||||
@@ -615,11 +617,6 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
|||||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_QFLOAT: {
|
|
||||||
QFloat *value = qobject_to_qfloat(obj);
|
|
||||||
func_fprintf(f, "%g", qfloat_get_double(value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QTYPE_QBOOL: {
|
case QTYPE_QBOOL: {
|
||||||
QBool *value = qobject_to_qbool(obj);
|
QBool *value = qobject_to_qbool(obj);
|
||||||
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
||||||
|
@@ -852,6 +852,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
header_size += backing_filename_len;
|
header_size += backing_filename_len;
|
||||||
} else {
|
} else {
|
||||||
/* special backing file for vvfat */
|
/* special backing file for vvfat */
|
||||||
|
g_free(backing_file);
|
||||||
backing_file = NULL;
|
backing_file = NULL;
|
||||||
}
|
}
|
||||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||||
|
@@ -1797,7 +1797,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (offset_into_cluster(s, offset)) {
|
if (offset_into_cluster(s, 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 " unaligned (L2 offset: %#"
|
||||||
PRIx64 ", L2 index: %#x)", offset,
|
PRIx64 ", L2 index: %#x)", offset,
|
||||||
l2_offset, j);
|
l2_offset, j);
|
||||||
|
@@ -3222,7 +3222,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
|||||||
|
|
||||||
if (s->refcount_bits != refcount_bits) {
|
if (s->refcount_bits != refcount_bits) {
|
||||||
int refcount_order = ctz32(refcount_bits);
|
int refcount_order = ctz32(refcount_bits);
|
||||||
Error *local_error = NULL;
|
|
||||||
|
|
||||||
if (new_version < 3 && refcount_bits != 16) {
|
if (new_version < 3 && refcount_bits != 16) {
|
||||||
error_report("Different refcount widths than 16 bits require "
|
error_report("Different refcount widths than 16 bits require "
|
||||||
@@ -3234,9 +3233,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
|||||||
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
|
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
|
||||||
ret = qcow2_change_refcount_order(bs, refcount_order,
|
ret = qcow2_change_refcount_order(bs, refcount_order,
|
||||||
&qcow2_amend_helper_cb,
|
&qcow2_amend_helper_cb,
|
||||||
&helper_cb_info, &local_error);
|
&helper_cb_info, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report_err(local_error);
|
error_report_err(local_err);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qed.h"
|
#include "qed.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "migration/migration.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
|
|
||||||
static const AIOCBInfo qed_aiocb_info = {
|
static const AIOCBInfo qed_aiocb_info = {
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
#include "qapi/qmp/qlist.h"
|
#include "qapi/qmp/qlist.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
22
block/rbd.c
22
block/rbd.c
@@ -340,6 +340,10 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Legacy rados key/value option parameters",
|
.help = "Legacy rados key/value option parameters",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "filename",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -541,12 +545,27 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
const char *pool, *snap, *conf, *user, *image_name, *keypairs;
|
const char *pool, *snap, *conf, *user, *image_name, *keypairs;
|
||||||
const char *secretid;
|
const char *secretid, *filename;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *mon_host = NULL;
|
char *mon_host = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
/* If we are given a filename, parse the filename, with precedence given to
|
||||||
|
* filename encoded options */
|
||||||
|
filename = qdict_get_try_str(options, "filename");
|
||||||
|
if (filename) {
|
||||||
|
error_report("Warning: 'filename' option specified. "
|
||||||
|
"This is an unsupported option, and may be deprecated "
|
||||||
|
"in the future");
|
||||||
|
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
r = -EINVAL;
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -665,6 +684,7 @@ failed_shutdown:
|
|||||||
failed_opts:
|
failed_opts:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
g_free(mon_host);
|
g_free(mon_host);
|
||||||
|
exit:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
#include "qemu/uri.h"
|
#include "qemu/uri.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
@@ -698,7 +697,8 @@ out:
|
|||||||
|
|
||||||
srco->co = NULL;
|
srco->co = NULL;
|
||||||
srco->ret = ret;
|
srco->ret = ret;
|
||||||
srco->finished = true;
|
/* Set srco->finished before reading bs->wakeup. */
|
||||||
|
atomic_mb_set(&srco->finished, true);
|
||||||
if (srco->bs) {
|
if (srco->bs) {
|
||||||
bdrv_wakeup(srco->bs);
|
bdrv_wakeup(srco->bs);
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,6 @@
|
|||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "qemu/uri.h"
|
#include "qemu/uri.h"
|
||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qapi/qmp/qint.h"
|
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
#include "qapi/qobject-output-visitor.h"
|
#include "qapi/qobject-output-visitor.h"
|
||||||
|
@@ -280,6 +280,6 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (orig_bs_flags != bdrv_get_flags(bs)) {
|
if (orig_bs_flags != bdrv_get_flags(bs)) {
|
||||||
bdrv_reopen(bs, s->bs_flags, NULL);
|
bdrv_reopen(bs, orig_bs_flags, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -240,7 +240,7 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
|
|||||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||||
bool must_wait;
|
bool must_wait;
|
||||||
|
|
||||||
if (blkp->io_limits_disabled) {
|
if (atomic_read(&blkp->io_limits_disabled)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +260,25 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
|
|||||||
return must_wait;
|
return must_wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Start the next pending I/O request for a BlockBackend. Return whether
|
||||||
|
* any request was actually pending.
|
||||||
|
*
|
||||||
|
* @blk: the current BlockBackend
|
||||||
|
* @is_write: the type of operation (read/write)
|
||||||
|
*/
|
||||||
|
static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
|
||||||
|
ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]);
|
||||||
|
qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Look for the next pending I/O request and schedule it.
|
/* Look for the next pending I/O request and schedule it.
|
||||||
*
|
*
|
||||||
* This assumes that tg->lock is held.
|
* This assumes that tg->lock is held.
|
||||||
@@ -287,12 +306,12 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
|
|||||||
if (!must_wait) {
|
if (!must_wait) {
|
||||||
/* Give preference to requests from the current blk */
|
/* Give preference to requests from the current blk */
|
||||||
if (qemu_in_coroutine() &&
|
if (qemu_in_coroutine() &&
|
||||||
qemu_co_queue_next(&blkp->throttled_reqs[is_write])) {
|
throttle_group_co_restart_queue(blk, is_write)) {
|
||||||
token = blk;
|
token = blk;
|
||||||
} else {
|
} else {
|
||||||
ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
|
ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
|
||||||
int64_t now = qemu_clock_get_ns(tt->clock_type);
|
int64_t now = qemu_clock_get_ns(tt->clock_type);
|
||||||
timer_mod(tt->timers[is_write], now + 1);
|
timer_mod(tt->timers[is_write], now);
|
||||||
tg->any_timer_armed[is_write] = true;
|
tg->any_timer_armed[is_write] = true;
|
||||||
}
|
}
|
||||||
tg->tokens[is_write] = token;
|
tg->tokens[is_write] = token;
|
||||||
@@ -326,7 +345,10 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
|||||||
if (must_wait || blkp->pending_reqs[is_write]) {
|
if (must_wait || blkp->pending_reqs[is_write]) {
|
||||||
blkp->pending_reqs[is_write]++;
|
blkp->pending_reqs[is_write]++;
|
||||||
qemu_mutex_unlock(&tg->lock);
|
qemu_mutex_unlock(&tg->lock);
|
||||||
qemu_co_queue_wait(&blkp->throttled_reqs[is_write], NULL);
|
qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
|
||||||
|
qemu_co_queue_wait(&blkp->throttled_reqs[is_write],
|
||||||
|
&blkp->throttled_reqs_lock);
|
||||||
|
qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
|
||||||
qemu_mutex_lock(&tg->lock);
|
qemu_mutex_lock(&tg->lock);
|
||||||
blkp->pending_reqs[is_write]--;
|
blkp->pending_reqs[is_write]--;
|
||||||
}
|
}
|
||||||
@@ -340,15 +362,50 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
|||||||
qemu_mutex_unlock(&tg->lock);
|
qemu_mutex_unlock(&tg->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BlockBackend *blk;
|
||||||
|
bool is_write;
|
||||||
|
} RestartData;
|
||||||
|
|
||||||
|
static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
|
||||||
|
{
|
||||||
|
RestartData *data = opaque;
|
||||||
|
BlockBackend *blk = data->blk;
|
||||||
|
bool is_write = data->is_write;
|
||||||
|
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||||
|
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||||
|
bool empty_queue;
|
||||||
|
|
||||||
|
empty_queue = !throttle_group_co_restart_queue(blk, is_write);
|
||||||
|
|
||||||
|
/* If the request queue was empty then we have to take care of
|
||||||
|
* scheduling the next one */
|
||||||
|
if (empty_queue) {
|
||||||
|
qemu_mutex_lock(&tg->lock);
|
||||||
|
schedule_next_request(blk, is_write);
|
||||||
|
qemu_mutex_unlock(&tg->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
|
||||||
|
{
|
||||||
|
Coroutine *co;
|
||||||
|
RestartData rd = {
|
||||||
|
.blk = blk,
|
||||||
|
.is_write = is_write
|
||||||
|
};
|
||||||
|
|
||||||
|
co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
|
||||||
|
aio_co_enter(blk_get_aio_context(blk), co);
|
||||||
|
}
|
||||||
|
|
||||||
void throttle_group_restart_blk(BlockBackend *blk)
|
void throttle_group_restart_blk(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 2; i++) {
|
if (blkp->throttle_state) {
|
||||||
while (qemu_co_enter_next(&blkp->throttled_reqs[i])) {
|
throttle_group_restart_queue(blk, 0);
|
||||||
;
|
throttle_group_restart_queue(blk, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,8 +433,7 @@ void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
|
|||||||
throttle_config(ts, tt, cfg);
|
throttle_config(ts, tt, cfg);
|
||||||
qemu_mutex_unlock(&tg->lock);
|
qemu_mutex_unlock(&tg->lock);
|
||||||
|
|
||||||
qemu_co_enter_next(&blkp->throttled_reqs[0]);
|
throttle_group_restart_blk(blk);
|
||||||
qemu_co_enter_next(&blkp->throttled_reqs[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the throttle configuration from a particular group. Similar to
|
/* Get the throttle configuration from a particular group. Similar to
|
||||||
@@ -408,7 +464,6 @@ static void timer_cb(BlockBackend *blk, bool is_write)
|
|||||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||||
ThrottleState *ts = blkp->throttle_state;
|
ThrottleState *ts = blkp->throttle_state;
|
||||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||||
bool empty_queue;
|
|
||||||
|
|
||||||
/* The timer has just been fired, so we can update the flag */
|
/* The timer has just been fired, so we can update the flag */
|
||||||
qemu_mutex_lock(&tg->lock);
|
qemu_mutex_lock(&tg->lock);
|
||||||
@@ -416,17 +471,7 @@ static void timer_cb(BlockBackend *blk, bool is_write)
|
|||||||
qemu_mutex_unlock(&tg->lock);
|
qemu_mutex_unlock(&tg->lock);
|
||||||
|
|
||||||
/* Run the request that was waiting for this timer */
|
/* Run the request that was waiting for this timer */
|
||||||
aio_context_acquire(blk_get_aio_context(blk));
|
throttle_group_restart_queue(blk, is_write);
|
||||||
empty_queue = !qemu_co_enter_next(&blkp->throttled_reqs[is_write]);
|
|
||||||
aio_context_release(blk_get_aio_context(blk));
|
|
||||||
|
|
||||||
/* If the request queue was empty then we have to take care of
|
|
||||||
* scheduling the next one */
|
|
||||||
if (empty_queue) {
|
|
||||||
qemu_mutex_lock(&tg->lock);
|
|
||||||
schedule_next_request(blk, is_write);
|
|
||||||
qemu_mutex_unlock(&tg->lock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_timer_cb(void *opaque)
|
static void read_timer_cb(void *opaque)
|
||||||
|
@@ -29,7 +29,6 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "migration/blocker.h"
|
#include "migration/blocker.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"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
@@ -27,6 +27,10 @@ typedef struct NBDServerData {
|
|||||||
|
|
||||||
static NBDServerData *nbd_server;
|
static NBDServerData *nbd_server;
|
||||||
|
|
||||||
|
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||||
|
{
|
||||||
|
nbd_client_put(client);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||||
gpointer opaque)
|
gpointer opaque)
|
||||||
@@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
|||||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||||
nbd_client_new(NULL, cioc,
|
nbd_client_new(NULL, cioc,
|
||||||
nbd_server->tlscreds, NULL,
|
nbd_server->tlscreds, NULL,
|
||||||
nbd_client_put);
|
nbd_blockdev_client_closed);
|
||||||
object_unref(OBJECT(cioc));
|
object_unref(OBJECT(cioc));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
58
blockdev.c
58
blockdev.c
@@ -334,8 +334,9 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case QTYPE_QINT: {
|
case QTYPE_QNUM: {
|
||||||
int64_t length = qint_get_int(qobject_to_qint(entry->value));
|
int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
|
||||||
|
|
||||||
if (length > 0 && length <= UINT_MAX) {
|
if (length > 0 && length <= UINT_MAX) {
|
||||||
block_acct_add_interval(stats, (unsigned) length);
|
block_acct_add_interval(stats, (unsigned) length);
|
||||||
} else {
|
} else {
|
||||||
@@ -595,7 +596,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
autostart = 0;
|
autostart = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_acct_init(blk_get_stats(blk), account_invalid, account_failed);
|
block_acct_setup(blk_get_stats(blk), account_invalid, account_failed);
|
||||||
|
|
||||||
if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) {
|
if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) {
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
@@ -1362,12 +1363,10 @@ out_aio_context:
|
|||||||
static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
||||||
const char *name,
|
const char *name,
|
||||||
BlockDriverState **pbs,
|
BlockDriverState **pbs,
|
||||||
AioContext **paio,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
AioContext *aio_context;
|
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
error_setg(errp, "Node cannot be NULL");
|
error_setg(errp, "Node cannot be NULL");
|
||||||
@@ -1383,29 +1382,17 @@ static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_context = bdrv_get_aio_context(bs);
|
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
|
|
||||||
bitmap = bdrv_find_dirty_bitmap(bs, name);
|
bitmap = bdrv_find_dirty_bitmap(bs, name);
|
||||||
if (!bitmap) {
|
if (!bitmap) {
|
||||||
error_setg(errp, "Dirty bitmap '%s' not found", name);
|
error_setg(errp, "Dirty bitmap '%s' not found", name);
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pbs) {
|
if (pbs) {
|
||||||
*pbs = bs;
|
*pbs = bs;
|
||||||
}
|
}
|
||||||
if (paio) {
|
|
||||||
*paio = aio_context;
|
|
||||||
} else {
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
|
|
||||||
fail:
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New and old BlockDriverState structs for atomic group operations */
|
/* New and old BlockDriverState structs for atomic group operations */
|
||||||
@@ -1791,7 +1778,7 @@ static void external_snapshot_commit(BlkActionState *common)
|
|||||||
/* We don't need (or want) to use the transactional
|
/* We don't need (or want) to use the transactional
|
||||||
* bdrv_reopen_multiple() across all the entries at once, because we
|
* bdrv_reopen_multiple() across all the entries at once, because we
|
||||||
* don't want to abort all of them if one of them fails the reopen */
|
* don't want to abort all of them if one of them fails the reopen */
|
||||||
if (!state->old_bs->copy_on_read) {
|
if (!atomic_read(&state->old_bs->copy_on_read)) {
|
||||||
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
|
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
@@ -1803,7 +1790,11 @@ static void external_snapshot_abort(BlkActionState *common)
|
|||||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||||
if (state->new_bs) {
|
if (state->new_bs) {
|
||||||
if (state->overlay_appended) {
|
if (state->overlay_appended) {
|
||||||
|
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
|
||||||
|
close state->old_bs; we need it */
|
||||||
|
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
|
||||||
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
|
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
|
||||||
|
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2021,7 +2012,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
|||||||
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
||||||
action->name,
|
action->name,
|
||||||
&state->bs,
|
&state->bs,
|
||||||
&state->aio_context,
|
|
||||||
errp);
|
errp);
|
||||||
if (!state->bitmap) {
|
if (!state->bitmap) {
|
||||||
return;
|
return;
|
||||||
@@ -2729,7 +2719,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
|||||||
bool has_granularity, uint32_t granularity,
|
bool has_granularity, uint32_t granularity,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
AioContext *aio_context;
|
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
|
||||||
if (!name || name[0] == '\0') {
|
if (!name || name[0] == '\0') {
|
||||||
@@ -2742,14 +2731,11 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_context = bdrv_get_aio_context(bs);
|
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
|
|
||||||
if (has_granularity) {
|
if (has_granularity) {
|
||||||
if (granularity < 512 || !is_power_of_2(granularity)) {
|
if (granularity < 512 || !is_power_of_2(granularity)) {
|
||||||
error_setg(errp, "Granularity must be power of 2 "
|
error_setg(errp, "Granularity must be power of 2 "
|
||||||
"and at least 512");
|
"and at least 512");
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Default to cluster size, if available: */
|
/* Default to cluster size, if available: */
|
||||||
@@ -2757,19 +2743,15 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
||||||
|
|
||||||
out:
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
AioContext *aio_context;
|
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
|
|
||||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
|
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||||
if (!bitmap || !bs) {
|
if (!bitmap || !bs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2778,13 +2760,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
|||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"Bitmap '%s' is currently frozen and cannot be removed",
|
"Bitmap '%s' is currently frozen and cannot be removed",
|
||||||
name);
|
name);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
bdrv_dirty_bitmap_make_anon(bitmap);
|
bdrv_dirty_bitmap_make_anon(bitmap);
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||||
|
|
||||||
out:
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2794,11 +2773,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
|||||||
void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
AioContext *aio_context;
|
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
|
||||||
bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
|
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
||||||
if (!bitmap || !bs) {
|
if (!bitmap || !bs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2807,18 +2785,15 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
|||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"Bitmap '%s' is currently frozen and cannot be modified",
|
"Bitmap '%s' is currently frozen and cannot be modified",
|
||||||
name);
|
name);
|
||||||
goto out;
|
return;
|
||||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"Bitmap '%s' is currently disabled and cannot be cleared",
|
"Bitmap '%s' is currently disabled and cannot be cleared",
|
||||||
name);
|
name);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_clear_dirty_bitmap(bitmap, NULL);
|
bdrv_clear_dirty_bitmap(bitmap, NULL);
|
||||||
|
|
||||||
out:
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
||||||
@@ -3715,7 +3690,6 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
trace_qmp_block_job_resume(job);
|
trace_qmp_block_job_resume(job);
|
||||||
block_job_iostatus_reset(job);
|
|
||||||
block_job_user_resume(job);
|
block_job_user_resume(job);
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
|
890
blockjob.c
890
blockjob.c
@@ -55,35 +55,20 @@ struct BlockJobTxn {
|
|||||||
|
|
||||||
static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
|
static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
|
||||||
|
|
||||||
static char *child_job_get_parent_desc(BdrvChild *c)
|
/*
|
||||||
{
|
* The block job API is composed of two categories of functions.
|
||||||
BlockJob *job = c->opaque;
|
*
|
||||||
return g_strdup_printf("%s job '%s'",
|
* The first includes functions used by the monitor. The monitor is
|
||||||
BlockJobType_lookup[job->driver->job_type],
|
* peculiar in that it accesses the block job list with block_job_get, and
|
||||||
job->id);
|
* therefore needs consistency across block_job_get and the actual operation
|
||||||
}
|
* (e.g. block_job_set_speed). The consistency is achieved with
|
||||||
|
* aio_context_acquire/release. These functions are declared in blockjob.h.
|
||||||
static const BdrvChildRole child_job = {
|
*
|
||||||
.get_parent_desc = child_job_get_parent_desc,
|
* The second includes functions used by the block job drivers and sometimes
|
||||||
.stay_at_node = true,
|
* by the core block layer. These do not care about locking, because the
|
||||||
};
|
* whole coroutine runs under the AioContext lock, and are declared in
|
||||||
|
* blockjob_int.h.
|
||||||
static void block_job_drained_begin(void *opaque)
|
*/
|
||||||
{
|
|
||||||
BlockJob *job = opaque;
|
|
||||||
block_job_pause(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_drained_end(void *opaque)
|
|
||||||
{
|
|
||||||
BlockJob *job = opaque;
|
|
||||||
block_job_resume(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const BlockDevOps block_job_dev_ops = {
|
|
||||||
.drained_begin = block_job_drained_begin,
|
|
||||||
.drained_end = block_job_drained_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
BlockJob *block_job_next(BlockJob *job)
|
BlockJob *block_job_next(BlockJob *job)
|
||||||
{
|
{
|
||||||
@@ -106,6 +91,80 @@ BlockJob *block_job_get(const char *id)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockJobTxn *block_job_txn_new(void)
|
||||||
|
{
|
||||||
|
BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
|
||||||
|
QLIST_INIT(&txn->jobs);
|
||||||
|
txn->refcnt = 1;
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_txn_ref(BlockJobTxn *txn)
|
||||||
|
{
|
||||||
|
txn->refcnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_txn_unref(BlockJobTxn *txn)
|
||||||
|
{
|
||||||
|
if (txn && --txn->refcnt == 0) {
|
||||||
|
g_free(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
|
||||||
|
{
|
||||||
|
if (!txn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!job->txn);
|
||||||
|
job->txn = txn;
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
|
||||||
|
block_job_txn_ref(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_pause(BlockJob *job)
|
||||||
|
{
|
||||||
|
job->pause_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_resume(BlockJob *job)
|
||||||
|
{
|
||||||
|
assert(job->pause_count > 0);
|
||||||
|
job->pause_count--;
|
||||||
|
if (job->pause_count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
block_job_enter(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_ref(BlockJob *job)
|
||||||
|
{
|
||||||
|
++job->refcnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_attached_aio_context(AioContext *new_context,
|
||||||
|
void *opaque);
|
||||||
|
static void block_job_detach_aio_context(void *opaque);
|
||||||
|
|
||||||
|
static void block_job_unref(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (--job->refcnt == 0) {
|
||||||
|
BlockDriverState *bs = blk_bs(job->blk);
|
||||||
|
bs->job = NULL;
|
||||||
|
block_job_remove_all_bdrv(job);
|
||||||
|
blk_remove_aio_context_notifier(job->blk,
|
||||||
|
block_job_attached_aio_context,
|
||||||
|
block_job_detach_aio_context, job);
|
||||||
|
blk_unref(job->blk);
|
||||||
|
error_free(job->blocker);
|
||||||
|
g_free(job->id);
|
||||||
|
QLIST_REMOVE(job, job_list);
|
||||||
|
g_free(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void block_job_attached_aio_context(AioContext *new_context,
|
static void block_job_attached_aio_context(AioContext *new_context,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
@@ -145,6 +204,36 @@ static void block_job_detach_aio_context(void *opaque)
|
|||||||
block_job_unref(job);
|
block_job_unref(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *child_job_get_parent_desc(BdrvChild *c)
|
||||||
|
{
|
||||||
|
BlockJob *job = c->opaque;
|
||||||
|
return g_strdup_printf("%s job '%s'",
|
||||||
|
BlockJobType_lookup[job->driver->job_type],
|
||||||
|
job->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const BdrvChildRole child_job = {
|
||||||
|
.get_parent_desc = child_job_get_parent_desc,
|
||||||
|
.stay_at_node = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void block_job_drained_begin(void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
block_job_pause(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_drained_end(void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
block_job_resume(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const BlockDevOps block_job_dev_ops = {
|
||||||
|
.drained_begin = block_job_drained_begin,
|
||||||
|
.drained_end = block_job_drained_end,
|
||||||
|
};
|
||||||
|
|
||||||
void block_job_remove_all_bdrv(BlockJob *job)
|
void block_job_remove_all_bdrv(BlockJob *job)
|
||||||
{
|
{
|
||||||
GSList *l;
|
GSList *l;
|
||||||
@@ -175,6 +264,350 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool block_job_is_internal(BlockJob *job)
|
||||||
|
{
|
||||||
|
return (job->id == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool block_job_started(BlockJob *job)
|
||||||
|
{
|
||||||
|
return job->co;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All jobs must allow a pause point before entering their job proper. This
|
||||||
|
* ensures that jobs can be paused prior to being started, then resumed later.
|
||||||
|
*/
|
||||||
|
static void coroutine_fn block_job_co_entry(void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
|
||||||
|
assert(job && job->driver && job->driver->start);
|
||||||
|
block_job_pause_point(job);
|
||||||
|
job->driver->start(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_start(BlockJob *job)
|
||||||
|
{
|
||||||
|
assert(job && !block_job_started(job) && job->paused &&
|
||||||
|
job->driver && job->driver->start);
|
||||||
|
job->co = qemu_coroutine_create(block_job_co_entry, job);
|
||||||
|
job->pause_count--;
|
||||||
|
job->busy = true;
|
||||||
|
job->paused = false;
|
||||||
|
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_completed_single(BlockJob *job)
|
||||||
|
{
|
||||||
|
assert(job->completed);
|
||||||
|
|
||||||
|
if (!job->ret) {
|
||||||
|
if (job->driver->commit) {
|
||||||
|
job->driver->commit(job);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (job->driver->abort) {
|
||||||
|
job->driver->abort(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (job->driver->clean) {
|
||||||
|
job->driver->clean(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job->cb) {
|
||||||
|
job->cb(job->opaque, job->ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit events only if we actually started */
|
||||||
|
if (block_job_started(job)) {
|
||||||
|
if (block_job_is_cancelled(job)) {
|
||||||
|
block_job_event_cancelled(job);
|
||||||
|
} else {
|
||||||
|
const char *msg = NULL;
|
||||||
|
if (job->ret < 0) {
|
||||||
|
msg = strerror(-job->ret);
|
||||||
|
}
|
||||||
|
block_job_event_completed(job, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job->txn) {
|
||||||
|
QLIST_REMOVE(job, txn_list);
|
||||||
|
block_job_txn_unref(job->txn);
|
||||||
|
}
|
||||||
|
block_job_unref(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_cancel_async(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
|
||||||
|
block_job_iostatus_reset(job);
|
||||||
|
}
|
||||||
|
if (job->user_paused) {
|
||||||
|
/* Do not call block_job_enter here, the caller will handle it. */
|
||||||
|
job->user_paused = false;
|
||||||
|
job->pause_count--;
|
||||||
|
}
|
||||||
|
job->cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int block_job_finish_sync(BlockJob *job,
|
||||||
|
void (*finish)(BlockJob *, Error **errp),
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
assert(blk_bs(job->blk)->job == job);
|
||||||
|
|
||||||
|
block_job_ref(job);
|
||||||
|
|
||||||
|
if (finish) {
|
||||||
|
finish(job, &local_err);
|
||||||
|
}
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
block_job_unref(job);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
/* block_job_drain calls block_job_enter, and it should be enough to
|
||||||
|
* induce progress until the job completes or moves to the main thread.
|
||||||
|
*/
|
||||||
|
while (!job->deferred_to_main_loop && !job->completed) {
|
||||||
|
block_job_drain(job);
|
||||||
|
}
|
||||||
|
while (!job->completed) {
|
||||||
|
aio_poll(qemu_get_aio_context(), true);
|
||||||
|
}
|
||||||
|
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
||||||
|
block_job_unref(job);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_completed_txn_abort(BlockJob *job)
|
||||||
|
{
|
||||||
|
AioContext *ctx;
|
||||||
|
BlockJobTxn *txn = job->txn;
|
||||||
|
BlockJob *other_job;
|
||||||
|
|
||||||
|
if (txn->aborting) {
|
||||||
|
/*
|
||||||
|
* We are cancelled by another job, which will handle everything.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
txn->aborting = true;
|
||||||
|
block_job_txn_ref(txn);
|
||||||
|
|
||||||
|
/* We are the first failed job. Cancel other jobs. */
|
||||||
|
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||||
|
ctx = blk_get_aio_context(other_job->blk);
|
||||||
|
aio_context_acquire(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Other jobs are effectively cancelled by us, set the status for
|
||||||
|
* them; this job, however, may or may not be cancelled, depending
|
||||||
|
* on the caller, so leave it. */
|
||||||
|
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||||
|
if (other_job != job) {
|
||||||
|
block_job_cancel_async(other_job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!QLIST_EMPTY(&txn->jobs)) {
|
||||||
|
other_job = QLIST_FIRST(&txn->jobs);
|
||||||
|
ctx = blk_get_aio_context(other_job->blk);
|
||||||
|
if (!other_job->completed) {
|
||||||
|
assert(other_job->cancelled);
|
||||||
|
block_job_finish_sync(other_job, NULL, NULL);
|
||||||
|
}
|
||||||
|
block_job_completed_single(other_job);
|
||||||
|
aio_context_release(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_job_txn_unref(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_completed_txn_success(BlockJob *job)
|
||||||
|
{
|
||||||
|
AioContext *ctx;
|
||||||
|
BlockJobTxn *txn = job->txn;
|
||||||
|
BlockJob *other_job, *next;
|
||||||
|
/*
|
||||||
|
* Successful completion, see if there are other running jobs in this
|
||||||
|
* txn.
|
||||||
|
*/
|
||||||
|
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||||
|
if (!other_job->completed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* We are the last completed job, commit the transaction. */
|
||||||
|
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||||
|
ctx = blk_get_aio_context(other_job->blk);
|
||||||
|
aio_context_acquire(ctx);
|
||||||
|
assert(other_job->ret == 0);
|
||||||
|
block_job_completed_single(other_job);
|
||||||
|
aio_context_release(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (!job->driver->set_speed) {
|
||||||
|
error_setg(errp, QERR_UNSUPPORTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
job->driver->set_speed(job, speed, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
job->speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_complete(BlockJob *job, Error **errp)
|
||||||
|
{
|
||||||
|
/* Should not be reachable via external interface for internal jobs */
|
||||||
|
assert(job->id);
|
||||||
|
if (job->pause_count || job->cancelled ||
|
||||||
|
!block_job_started(job) || !job->driver->complete) {
|
||||||
|
error_setg(errp, "The active block job '%s' cannot be completed",
|
||||||
|
job->id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
job->driver->complete(job, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_user_pause(BlockJob *job)
|
||||||
|
{
|
||||||
|
job->user_paused = true;
|
||||||
|
block_job_pause(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool block_job_user_paused(BlockJob *job)
|
||||||
|
{
|
||||||
|
return job->user_paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_user_resume(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (job && job->user_paused && job->pause_count > 0) {
|
||||||
|
block_job_iostatus_reset(job);
|
||||||
|
job->user_paused = false;
|
||||||
|
block_job_resume(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_cancel(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (block_job_started(job)) {
|
||||||
|
block_job_cancel_async(job);
|
||||||
|
block_job_enter(job);
|
||||||
|
} else {
|
||||||
|
block_job_completed(job, -ECANCELED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
||||||
|
* used with block_job_finish_sync() without the need for (rather nasty)
|
||||||
|
* function pointer casts there. */
|
||||||
|
static void block_job_cancel_err(BlockJob *job, Error **errp)
|
||||||
|
{
|
||||||
|
block_job_cancel(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
int block_job_cancel_sync(BlockJob *job)
|
||||||
|
{
|
||||||
|
return block_job_finish_sync(job, &block_job_cancel_err, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_cancel_sync_all(void)
|
||||||
|
{
|
||||||
|
BlockJob *job;
|
||||||
|
AioContext *aio_context;
|
||||||
|
|
||||||
|
while ((job = QLIST_FIRST(&block_jobs))) {
|
||||||
|
aio_context = blk_get_aio_context(job->blk);
|
||||||
|
aio_context_acquire(aio_context);
|
||||||
|
block_job_cancel_sync(job);
|
||||||
|
aio_context_release(aio_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int block_job_complete_sync(BlockJob *job, Error **errp)
|
||||||
|
{
|
||||||
|
return block_job_finish_sync(job, &block_job_complete, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
||||||
|
{
|
||||||
|
BlockJobInfo *info;
|
||||||
|
|
||||||
|
if (block_job_is_internal(job)) {
|
||||||
|
error_setg(errp, "Cannot query QEMU internal jobs");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
info = g_new0(BlockJobInfo, 1);
|
||||||
|
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
|
||||||
|
info->device = g_strdup(job->id);
|
||||||
|
info->len = job->len;
|
||||||
|
info->busy = job->busy;
|
||||||
|
info->paused = job->pause_count > 0;
|
||||||
|
info->offset = job->offset;
|
||||||
|
info->speed = job->speed;
|
||||||
|
info->io_status = job->iostatus;
|
||||||
|
info->ready = job->ready;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_iostatus_set_err(BlockJob *job, int error)
|
||||||
|
{
|
||||||
|
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||||
|
job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE :
|
||||||
|
BLOCK_DEVICE_IO_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_event_cancelled(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (block_job_is_internal(job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qapi_event_send_block_job_cancelled(job->driver->job_type,
|
||||||
|
job->id,
|
||||||
|
job->len,
|
||||||
|
job->offset,
|
||||||
|
job->speed,
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_event_completed(BlockJob *job, const char *msg)
|
||||||
|
{
|
||||||
|
if (block_job_is_internal(job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qapi_event_send_block_job_completed(job->driver->job_type,
|
||||||
|
job->id,
|
||||||
|
job->len,
|
||||||
|
job->offset,
|
||||||
|
job->speed,
|
||||||
|
!!msg,
|
||||||
|
msg,
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* API for block job drivers and the block layer. These functions are
|
||||||
|
* declared in blockjob_int.h.
|
||||||
|
*/
|
||||||
|
|
||||||
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||||
BlockDriverState *bs, uint64_t perm,
|
BlockDriverState *bs, uint64_t perm,
|
||||||
uint64_t shared_perm, int64_t speed, int flags,
|
uint64_t shared_perm, int64_t speed, int flags,
|
||||||
@@ -259,163 +692,23 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_job_is_internal(BlockJob *job)
|
void block_job_pause_all(void)
|
||||||
{
|
{
|
||||||
return (job->id == NULL);
|
BlockJob *job = NULL;
|
||||||
}
|
while ((job = block_job_next(job))) {
|
||||||
|
AioContext *aio_context = blk_get_aio_context(job->blk);
|
||||||
|
|
||||||
static bool block_job_started(BlockJob *job)
|
aio_context_acquire(aio_context);
|
||||||
{
|
block_job_pause(job);
|
||||||
return job->co;
|
aio_context_release(aio_context);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All jobs must allow a pause point before entering their job proper. This
|
|
||||||
* ensures that jobs can be paused prior to being started, then resumed later.
|
|
||||||
*/
|
|
||||||
static void coroutine_fn block_job_co_entry(void *opaque)
|
|
||||||
{
|
|
||||||
BlockJob *job = opaque;
|
|
||||||
|
|
||||||
assert(job && job->driver && job->driver->start);
|
|
||||||
block_job_pause_point(job);
|
|
||||||
job->driver->start(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_start(BlockJob *job)
|
|
||||||
{
|
|
||||||
assert(job && !block_job_started(job) && job->paused &&
|
|
||||||
job->driver && job->driver->start);
|
|
||||||
job->co = qemu_coroutine_create(block_job_co_entry, job);
|
|
||||||
job->pause_count--;
|
|
||||||
job->busy = true;
|
|
||||||
job->paused = false;
|
|
||||||
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_ref(BlockJob *job)
|
|
||||||
{
|
|
||||||
++job->refcnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_unref(BlockJob *job)
|
|
||||||
{
|
|
||||||
if (--job->refcnt == 0) {
|
|
||||||
BlockDriverState *bs = blk_bs(job->blk);
|
|
||||||
bs->job = NULL;
|
|
||||||
block_job_remove_all_bdrv(job);
|
|
||||||
blk_remove_aio_context_notifier(job->blk,
|
|
||||||
block_job_attached_aio_context,
|
|
||||||
block_job_detach_aio_context, job);
|
|
||||||
blk_unref(job->blk);
|
|
||||||
error_free(job->blocker);
|
|
||||||
g_free(job->id);
|
|
||||||
QLIST_REMOVE(job, job_list);
|
|
||||||
g_free(job);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_job_completed_single(BlockJob *job)
|
void block_job_early_fail(BlockJob *job)
|
||||||
{
|
{
|
||||||
if (!job->ret) {
|
|
||||||
if (job->driver->commit) {
|
|
||||||
job->driver->commit(job);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (job->driver->abort) {
|
|
||||||
job->driver->abort(job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (job->driver->clean) {
|
|
||||||
job->driver->clean(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job->cb) {
|
|
||||||
job->cb(job->opaque, job->ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Emit events only if we actually started */
|
|
||||||
if (block_job_started(job)) {
|
|
||||||
if (block_job_is_cancelled(job)) {
|
|
||||||
block_job_event_cancelled(job);
|
|
||||||
} else {
|
|
||||||
const char *msg = NULL;
|
|
||||||
if (job->ret < 0) {
|
|
||||||
msg = strerror(-job->ret);
|
|
||||||
}
|
|
||||||
block_job_event_completed(job, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job->txn) {
|
|
||||||
QLIST_REMOVE(job, txn_list);
|
|
||||||
block_job_txn_unref(job->txn);
|
|
||||||
}
|
|
||||||
block_job_unref(job);
|
block_job_unref(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_job_completed_txn_abort(BlockJob *job)
|
|
||||||
{
|
|
||||||
AioContext *ctx;
|
|
||||||
BlockJobTxn *txn = job->txn;
|
|
||||||
BlockJob *other_job, *next;
|
|
||||||
|
|
||||||
if (txn->aborting) {
|
|
||||||
/*
|
|
||||||
* We are cancelled by another job, which will handle everything.
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
txn->aborting = true;
|
|
||||||
/* We are the first failed job. Cancel other jobs. */
|
|
||||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
|
||||||
ctx = blk_get_aio_context(other_job->blk);
|
|
||||||
aio_context_acquire(ctx);
|
|
||||||
}
|
|
||||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
|
||||||
if (other_job == job || other_job->completed) {
|
|
||||||
/* Other jobs are "effectively" cancelled by us, set the status for
|
|
||||||
* them; this job, however, may or may not be cancelled, depending
|
|
||||||
* on the caller, so leave it. */
|
|
||||||
if (other_job != job) {
|
|
||||||
other_job->cancelled = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
block_job_cancel_sync(other_job);
|
|
||||||
assert(other_job->completed);
|
|
||||||
}
|
|
||||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
|
||||||
ctx = blk_get_aio_context(other_job->blk);
|
|
||||||
block_job_completed_single(other_job);
|
|
||||||
aio_context_release(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_completed_txn_success(BlockJob *job)
|
|
||||||
{
|
|
||||||
AioContext *ctx;
|
|
||||||
BlockJobTxn *txn = job->txn;
|
|
||||||
BlockJob *other_job, *next;
|
|
||||||
/*
|
|
||||||
* Successful completion, see if there are other running jobs in this
|
|
||||||
* txn.
|
|
||||||
*/
|
|
||||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
|
||||||
if (!other_job->completed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* We are the last completed job, commit the transaction. */
|
|
||||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
|
||||||
ctx = blk_get_aio_context(other_job->blk);
|
|
||||||
aio_context_acquire(ctx);
|
|
||||||
assert(other_job->ret == 0);
|
|
||||||
block_job_completed_single(other_job);
|
|
||||||
aio_context_release(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_completed(BlockJob *job, int ret)
|
void block_job_completed(BlockJob *job, int ret)
|
||||||
{
|
{
|
||||||
assert(blk_bs(job->blk)->job == job);
|
assert(blk_bs(job->blk)->job == job);
|
||||||
@@ -431,58 +724,11 @@ void block_job_completed(BlockJob *job, int ret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|
||||||
{
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
if (!job->driver->set_speed) {
|
|
||||||
error_setg(errp, QERR_UNSUPPORTED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
job->driver->set_speed(job, speed, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
job->speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_complete(BlockJob *job, Error **errp)
|
|
||||||
{
|
|
||||||
/* Should not be reachable via external interface for internal jobs */
|
|
||||||
assert(job->id);
|
|
||||||
if (job->pause_count || job->cancelled ||
|
|
||||||
!block_job_started(job) || !job->driver->complete) {
|
|
||||||
error_setg(errp, "The active block job '%s' cannot be completed",
|
|
||||||
job->id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
job->driver->complete(job, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_pause(BlockJob *job)
|
|
||||||
{
|
|
||||||
job->pause_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_user_pause(BlockJob *job)
|
|
||||||
{
|
|
||||||
job->user_paused = true;
|
|
||||||
block_job_pause(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool block_job_should_pause(BlockJob *job)
|
static bool block_job_should_pause(BlockJob *job)
|
||||||
{
|
{
|
||||||
return job->pause_count > 0;
|
return job->pause_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_job_user_paused(BlockJob *job)
|
|
||||||
{
|
|
||||||
return job ? job->user_paused : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void coroutine_fn block_job_pause_point(BlockJob *job)
|
void coroutine_fn block_job_pause_point(BlockJob *job)
|
||||||
{
|
{
|
||||||
assert(job && block_job_started(job));
|
assert(job && block_job_started(job));
|
||||||
@@ -511,39 +757,29 @@ void coroutine_fn block_job_pause_point(BlockJob *job)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_resume(BlockJob *job)
|
void block_job_resume_all(void)
|
||||||
{
|
{
|
||||||
assert(job->pause_count > 0);
|
BlockJob *job = NULL;
|
||||||
job->pause_count--;
|
while ((job = block_job_next(job))) {
|
||||||
if (job->pause_count) {
|
AioContext *aio_context = blk_get_aio_context(job->blk);
|
||||||
return;
|
|
||||||
}
|
|
||||||
block_job_enter(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_user_resume(BlockJob *job)
|
aio_context_acquire(aio_context);
|
||||||
{
|
|
||||||
if (job && job->user_paused && job->pause_count > 0) {
|
|
||||||
job->user_paused = false;
|
|
||||||
block_job_resume(job);
|
block_job_resume(job);
|
||||||
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_enter(BlockJob *job)
|
void block_job_enter(BlockJob *job)
|
||||||
{
|
{
|
||||||
if (job->co && !job->busy) {
|
if (!block_job_started(job)) {
|
||||||
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
return;
|
||||||
|
}
|
||||||
|
if (job->deferred_to_main_loop) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_cancel(BlockJob *job)
|
if (!job->busy) {
|
||||||
{
|
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
||||||
if (block_job_started(job)) {
|
|
||||||
job->cancelled = true;
|
|
||||||
block_job_iostatus_reset(job);
|
|
||||||
block_job_enter(job);
|
|
||||||
} else {
|
|
||||||
block_job_completed(job, -ECANCELED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,76 +788,6 @@ bool block_job_is_cancelled(BlockJob *job)
|
|||||||
return job->cancelled;
|
return job->cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_iostatus_reset(BlockJob *job)
|
|
||||||
{
|
|
||||||
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
|
||||||
if (job->driver->iostatus_reset) {
|
|
||||||
job->driver->iostatus_reset(job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int block_job_finish_sync(BlockJob *job,
|
|
||||||
void (*finish)(BlockJob *, Error **errp),
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
Error *local_err = NULL;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
assert(blk_bs(job->blk)->job == job);
|
|
||||||
|
|
||||||
block_job_ref(job);
|
|
||||||
|
|
||||||
finish(job, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
block_job_unref(job);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
/* block_job_drain calls block_job_enter, and it should be enough to
|
|
||||||
* induce progress until the job completes or moves to the main thread.
|
|
||||||
*/
|
|
||||||
while (!job->deferred_to_main_loop && !job->completed) {
|
|
||||||
block_job_drain(job);
|
|
||||||
}
|
|
||||||
while (!job->completed) {
|
|
||||||
aio_poll(qemu_get_aio_context(), true);
|
|
||||||
}
|
|
||||||
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
|
||||||
block_job_unref(job);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
|
||||||
* used with block_job_finish_sync() without the need for (rather nasty)
|
|
||||||
* function pointer casts there. */
|
|
||||||
static void block_job_cancel_err(BlockJob *job, Error **errp)
|
|
||||||
{
|
|
||||||
block_job_cancel(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
int block_job_cancel_sync(BlockJob *job)
|
|
||||||
{
|
|
||||||
return block_job_finish_sync(job, &block_job_cancel_err, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_cancel_sync_all(void)
|
|
||||||
{
|
|
||||||
BlockJob *job;
|
|
||||||
AioContext *aio_context;
|
|
||||||
|
|
||||||
while ((job = QLIST_FIRST(&block_jobs))) {
|
|
||||||
aio_context = blk_get_aio_context(job->blk);
|
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
block_job_cancel_sync(job);
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int block_job_complete_sync(BlockJob *job, Error **errp)
|
|
||||||
{
|
|
||||||
return block_job_finish_sync(job, &block_job_complete, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
||||||
{
|
{
|
||||||
assert(job->busy);
|
assert(job->busy);
|
||||||
@@ -658,63 +824,13 @@ void block_job_yield(BlockJob *job)
|
|||||||
block_job_pause_point(job);
|
block_job_pause_point(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
void block_job_iostatus_reset(BlockJob *job)
|
||||||
{
|
|
||||||
BlockJobInfo *info;
|
|
||||||
|
|
||||||
if (block_job_is_internal(job)) {
|
|
||||||
error_setg(errp, "Cannot query QEMU internal jobs");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
info = g_new0(BlockJobInfo, 1);
|
|
||||||
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
|
|
||||||
info->device = g_strdup(job->id);
|
|
||||||
info->len = job->len;
|
|
||||||
info->busy = job->busy;
|
|
||||||
info->paused = job->pause_count > 0;
|
|
||||||
info->offset = job->offset;
|
|
||||||
info->speed = job->speed;
|
|
||||||
info->io_status = job->iostatus;
|
|
||||||
info->ready = job->ready;
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_iostatus_set_err(BlockJob *job, int error)
|
|
||||||
{
|
{
|
||||||
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||||
job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE :
|
|
||||||
BLOCK_DEVICE_IO_STATUS_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_event_cancelled(BlockJob *job)
|
|
||||||
{
|
|
||||||
if (block_job_is_internal(job)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
assert(job->user_paused && job->pause_count > 0);
|
||||||
qapi_event_send_block_job_cancelled(job->driver->job_type,
|
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||||
job->id,
|
|
||||||
job->len,
|
|
||||||
job->offset,
|
|
||||||
job->speed,
|
|
||||||
&error_abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_event_completed(BlockJob *job, const char *msg)
|
|
||||||
{
|
|
||||||
if (block_job_is_internal(job)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qapi_event_send_block_job_completed(job->driver->job_type,
|
|
||||||
job->id,
|
|
||||||
job->len,
|
|
||||||
job->offset,
|
|
||||||
job->speed,
|
|
||||||
!!msg,
|
|
||||||
msg,
|
|
||||||
&error_abort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_event_ready(BlockJob *job)
|
void block_job_event_ready(BlockJob *job)
|
||||||
@@ -790,7 +906,6 @@ static void block_job_defer_to_main_loop_bh(void *opaque)
|
|||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->job->deferred_to_main_loop = false;
|
|
||||||
data->fn(data->job, data->opaque);
|
data->fn(data->job, data->opaque);
|
||||||
|
|
||||||
if (aio_context != data->aio_context) {
|
if (aio_context != data->aio_context) {
|
||||||
@@ -816,36 +931,3 @@ void block_job_defer_to_main_loop(BlockJob *job,
|
|||||||
aio_bh_schedule_oneshot(qemu_get_aio_context(),
|
aio_bh_schedule_oneshot(qemu_get_aio_context(),
|
||||||
block_job_defer_to_main_loop_bh, data);
|
block_job_defer_to_main_loop_bh, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockJobTxn *block_job_txn_new(void)
|
|
||||||
{
|
|
||||||
BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
|
|
||||||
QLIST_INIT(&txn->jobs);
|
|
||||||
txn->refcnt = 1;
|
|
||||||
return txn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_txn_ref(BlockJobTxn *txn)
|
|
||||||
{
|
|
||||||
txn->refcnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_txn_unref(BlockJobTxn *txn)
|
|
||||||
{
|
|
||||||
if (txn && --txn->refcnt == 0) {
|
|
||||||
g_free(txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
|
|
||||||
{
|
|
||||||
if (!txn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!job->txn);
|
|
||||||
job->txn = txn;
|
|
||||||
|
|
||||||
QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
|
|
||||||
block_job_txn_ref(txn);
|
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
chardev-obj-y += char.o
|
chardev-obj-y += char.o
|
||||||
chardev-obj-$(CONFIG_WIN32) += char-console.o
|
chardev-obj-$(CONFIG_WIN32) += char-console.o
|
||||||
chardev-obj-$(CONFIG_POSIX) += char-fd.o
|
chardev-obj-$(CONFIG_POSIX) += char-fd.o
|
||||||
|
chardev-obj-y += char-fe.o
|
||||||
chardev-obj-y += char-file.o
|
chardev-obj-y += char-file.o
|
||||||
chardev-obj-y += char-io.o
|
chardev-obj-y += char-io.o
|
||||||
chardev-obj-y += char-mux.o
|
chardev-obj-y += char-mux.o
|
||||||
@@ -15,3 +16,9 @@ chardev-obj-y += char-stdio.o
|
|||||||
chardev-obj-y += char-udp.o
|
chardev-obj-y += char-udp.o
|
||||||
chardev-obj-$(CONFIG_WIN32) += char-win.o
|
chardev-obj-$(CONFIG_WIN32) += char-win.o
|
||||||
chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
|
chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
|
||||||
|
|
||||||
|
common-obj-y += msmouse.o wctablet.o testdev.o
|
||||||
|
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||||
|
baum.o-cflags := $(SDL_CFLAGS)
|
||||||
|
|
||||||
|
common-obj-$(CONFIG_SPICE) += spice.o
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "hw/usb.h"
|
#include "hw/usb.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
@@ -22,14 +22,14 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
|
|
||||||
static void qemu_chr_open_win_con(Chardev *chr,
|
static void qemu_chr_open_win_con(Chardev *chr,
|
||||||
ChardevBackend *backend,
|
ChardevBackend *backend,
|
||||||
bool *be_opened,
|
bool *be_opened,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
|
win_chr_set_file(chr, GetStdHandle(STD_OUTPUT_HANDLE), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_console_class_init(ObjectClass *oc, void *data)
|
static void char_console_class_init(ObjectClass *oc, void *data)
|
||||||
|
@@ -25,11 +25,11 @@
|
|||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "io/channel-file.h"
|
#include "io/channel-file.h"
|
||||||
|
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#include "char-io.h"
|
#include "chardev/char-io.h"
|
||||||
|
|
||||||
/* Called with chr_write_lock held. */
|
/* Called with chr_write_lock held. */
|
||||||
static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||||
|
361
chardev/char-fe.c
Normal file
361
chardev/char-fe.c
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
* QEMU System Emulator
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi-visit.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
|
#include "chardev/char-fe.h"
|
||||||
|
#include "chardev/char-io.h"
|
||||||
|
#include "chardev/char-mux.h"
|
||||||
|
|
||||||
|
int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qemu_chr_write(s, buf, len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qemu_chr_write(s, buf, len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
int offset = 0, counter = 10;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
|
||||||
|
return replay_char_read_all_load(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (offset < len) {
|
||||||
|
retry:
|
||||||
|
res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
|
||||||
|
len - offset);
|
||||||
|
if (res == -1 && errno == EAGAIN) {
|
||||||
|
g_usleep(100);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
||||||
|
replay_char_read_all_save_error(res);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += res;
|
||||||
|
|
||||||
|
if (!counter--) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
||||||
|
replay_char_read_all_save_buf(buf, offset);
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
|
||||||
|
res = -ENOTSUP;
|
||||||
|
} else {
|
||||||
|
res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_get_msgfd(CharBackend *be)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
int fd;
|
||||||
|
int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
|
||||||
|
if (s && qemu_chr_replay(s)) {
|
||||||
|
error_report("Replay: get msgfd is not supported "
|
||||||
|
"for serial devices yet");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CHARDEV_GET_CLASS(s)->get_msgfds ?
|
||||||
|
CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CHARDEV_GET_CLASS(s)->set_msgfds ?
|
||||||
|
CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_accept_input(CharBackend *be)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
|
||||||
|
CHARDEV_GET_CLASS(s)->chr_accept_input(s);
|
||||||
|
}
|
||||||
|
qemu_notify_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[CHR_READ_BUF_LEN];
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
/* XXX this blocks entire thread. Rewrite to use
|
||||||
|
* qemu_chr_fe_write and background I/O callbacks */
|
||||||
|
qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chardev *qemu_chr_fe_get_driver(CharBackend *be)
|
||||||
|
{
|
||||||
|
return be->chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
|
||||||
|
{
|
||||||
|
int tag = 0;
|
||||||
|
|
||||||
|
if (CHARDEV_IS_MUX(s)) {
|
||||||
|
MuxChardev *d = MUX_CHARDEV(s);
|
||||||
|
|
||||||
|
if (d->mux_cnt >= MAX_MUX) {
|
||||||
|
goto unavailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->backends[d->mux_cnt] = b;
|
||||||
|
tag = d->mux_cnt++;
|
||||||
|
} else if (s->be) {
|
||||||
|
goto unavailable;
|
||||||
|
} else {
|
||||||
|
s->be = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
b->fe_open = false;
|
||||||
|
b->tag = tag;
|
||||||
|
b->chr = s;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
unavailable:
|
||||||
|
error_setg(errp, QERR_DEVICE_IN_USE, s->label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_deinit(CharBackend *b, bool del)
|
||||||
|
{
|
||||||
|
assert(b);
|
||||||
|
|
||||||
|
if (b->chr) {
|
||||||
|
qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
|
||||||
|
if (b->chr->be == b) {
|
||||||
|
b->chr->be = NULL;
|
||||||
|
}
|
||||||
|
if (CHARDEV_IS_MUX(b->chr)) {
|
||||||
|
MuxChardev *d = MUX_CHARDEV(b->chr);
|
||||||
|
d->backends[b->tag] = NULL;
|
||||||
|
}
|
||||||
|
if (del) {
|
||||||
|
object_unparent(OBJECT(b->chr));
|
||||||
|
}
|
||||||
|
b->chr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_set_handlers(CharBackend *b,
|
||||||
|
IOCanReadHandler *fd_can_read,
|
||||||
|
IOReadHandler *fd_read,
|
||||||
|
IOEventHandler *fd_event,
|
||||||
|
void *opaque,
|
||||||
|
GMainContext *context,
|
||||||
|
bool set_open)
|
||||||
|
{
|
||||||
|
Chardev *s;
|
||||||
|
ChardevClass *cc;
|
||||||
|
int fe_open;
|
||||||
|
|
||||||
|
s = b->chr;
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc = CHARDEV_GET_CLASS(s);
|
||||||
|
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
||||||
|
fe_open = 0;
|
||||||
|
remove_fd_in_watch(s);
|
||||||
|
} else {
|
||||||
|
fe_open = 1;
|
||||||
|
}
|
||||||
|
b->chr_can_read = fd_can_read;
|
||||||
|
b->chr_read = fd_read;
|
||||||
|
b->chr_event = fd_event;
|
||||||
|
b->opaque = opaque;
|
||||||
|
if (cc->chr_update_read_handler) {
|
||||||
|
cc->chr_update_read_handler(s, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_open) {
|
||||||
|
qemu_chr_fe_set_open(b, fe_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fe_open) {
|
||||||
|
qemu_chr_fe_take_focus(b);
|
||||||
|
/* We're connecting to an already opened device, so let's make sure we
|
||||||
|
also get the open event */
|
||||||
|
if (s->be_open) {
|
||||||
|
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CHARDEV_IS_MUX(s)) {
|
||||||
|
mux_chr_set_handlers(s, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_take_focus(CharBackend *b)
|
||||||
|
{
|
||||||
|
if (!b->chr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CHARDEV_IS_MUX(b->chr)) {
|
||||||
|
mux_set_focus(b->chr, b->tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
|
||||||
|
{
|
||||||
|
if (!be->chr) {
|
||||||
|
error_setg(errp, "missing associated backend");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qemu_chr_wait_connected(be->chr, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
|
||||||
|
{
|
||||||
|
Chardev *chr = be->chr;
|
||||||
|
|
||||||
|
if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
|
||||||
|
CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
|
||||||
|
{
|
||||||
|
Chardev *chr = be->chr;
|
||||||
|
|
||||||
|
if (!chr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (be->fe_open == fe_open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
be->fe_open = fe_open;
|
||||||
|
if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
|
||||||
|
CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
|
||||||
|
GIOFunc func, void *user_data)
|
||||||
|
{
|
||||||
|
Chardev *s = be->chr;
|
||||||
|
GSource *src;
|
||||||
|
guint tag;
|
||||||
|
|
||||||
|
if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
|
||||||
|
if (!src) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
|
||||||
|
tag = g_source_attach(src, NULL);
|
||||||
|
g_source_unref(src);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_chr_fe_disconnect(CharBackend *be)
|
||||||
|
{
|
||||||
|
Chardev *chr = be->chr;
|
||||||
|
|
||||||
|
if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
|
||||||
|
CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
|
||||||
|
}
|
||||||
|
}
|
@@ -24,12 +24,12 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
#else
|
#else
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void qmp_chardev_open_file(Chardev *chr,
|
static void qmp_chardev_open_file(Chardev *chr,
|
||||||
@@ -65,7 +65,7 @@ static void qmp_chardev_open_file(Chardev *chr,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_chr_open_win_file(chr, out);
|
win_chr_set_file(chr, out, false);
|
||||||
#else
|
#else
|
||||||
int flags, in = -1, out;
|
int flags, in = -1, out;
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "char-io.h"
|
#include "chardev/char-io.h"
|
||||||
|
|
||||||
typedef struct IOWatchPoll {
|
typedef struct IOWatchPoll {
|
||||||
GSource parent;
|
GSource parent;
|
||||||
|
@@ -24,9 +24,9 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "char-mux.h"
|
#include "chardev/char-mux.h"
|
||||||
|
|
||||||
/* MUX driver for serial I/O splitting */
|
/* MUX driver for serial I/O splitting */
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ static void char_mux_finalize(Object *obj)
|
|||||||
be->chr = NULL;
|
be->chr = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_chr_fe_deinit(&d->chr);
|
qemu_chr_fe_deinit(&d->chr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
|
void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
|
|
||||||
static void null_chr_open(Chardev *chr,
|
static void null_chr_open(Chardev *chr,
|
||||||
ChardevBackend *backend,
|
ChardevBackend *backend,
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#include "char-parallel.h"
|
#include "chardev/char-parallel.h"
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
|
||||||
|
@@ -23,12 +23,12 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
#else
|
#else
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -58,27 +58,27 @@ static int win_chr_pipe_init(Chardev *chr, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
|
openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
|
||||||
s->hcom = CreateNamedPipe(openname,
|
s->file = CreateNamedPipe(openname,
|
||||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
|
||||||
PIPE_WAIT,
|
PIPE_WAIT,
|
||||||
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
|
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
|
||||||
g_free(openname);
|
g_free(openname);
|
||||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
if (s->file == INVALID_HANDLE_VALUE) {
|
||||||
error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
|
error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
|
||||||
s->hcom = NULL;
|
s->file = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroMemory(&ov, sizeof(ov));
|
ZeroMemory(&ov, sizeof(ov));
|
||||||
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
ret = ConnectNamedPipe(s->hcom, &ov);
|
ret = ConnectNamedPipe(s->file, &ov);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_setg(errp, "Failed ConnectNamedPipe");
|
error_setg(errp, "Failed ConnectNamedPipe");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
|
ret = GetOverlappedResult(s->file, &ov, &size, TRUE);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
error_setg(errp, "Failed GetOverlappedResult");
|
error_setg(errp, "Failed GetOverlappedResult");
|
||||||
if (ov.hEvent) {
|
if (ov.hEvent) {
|
||||||
|
@@ -24,12 +24,12 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "io/channel-file.h"
|
#include "io/channel-file.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
#include "char-io.h"
|
#include "chardev/char-io.h"
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
#include "qemu/base64.h"
|
#include "qemu/base64.h"
|
||||||
|
|
||||||
|
@@ -27,14 +27,14 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
#else
|
#else
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "char-serial.h"
|
#include "chardev/char-serial.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ static void qmp_chardev_open_serial(Chardev *chr,
|
|||||||
{
|
{
|
||||||
ChardevHostdev *serial = backend->u.serial.data;
|
ChardevHostdev *serial = backend->u.serial.data;
|
||||||
|
|
||||||
win_chr_init(chr, serial->device, errp);
|
win_chr_serial_init(chr, serial->device, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||||
|
@@ -22,14 +22,14 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "io/channel-socket.h"
|
#include "io/channel-socket.h"
|
||||||
#include "io/channel-tls.h"
|
#include "io/channel-tls.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/clone-visitor.h"
|
#include "qapi/clone-visitor.h"
|
||||||
|
|
||||||
#include "char-io.h"
|
#include "chardev/char-io.h"
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* TCP Net console */
|
/* TCP Net console */
|
||||||
|
@@ -25,14 +25,14 @@
|
|||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
#include "char-win-stdio.h"
|
#include "chardev/char-win-stdio.h"
|
||||||
#else
|
#else
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include "char-fd.h"
|
#include "chardev/char-fd.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
@@ -22,11 +22,11 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "io/channel-socket.h"
|
#include "io/channel-socket.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#include "char-io.h"
|
#include "chardev/char-io.h"
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* UDP Net console */
|
/* UDP Net console */
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
#include "char-win-stdio.h"
|
#include "chardev/char-win-stdio.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Chardev parent;
|
Chardev parent;
|
||||||
|
@@ -24,23 +24,30 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "char-win.h"
|
#include "chardev/char-win.h"
|
||||||
|
|
||||||
static void win_chr_readfile(Chardev *chr)
|
static void win_chr_read(Chardev *chr, DWORD len)
|
||||||
{
|
{
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
WinChardev *s = WIN_CHARDEV(chr);
|
||||||
|
int max_size = qemu_chr_be_can_write(chr);
|
||||||
int ret, err;
|
int ret, err;
|
||||||
uint8_t buf[CHR_READ_BUF_LEN];
|
uint8_t buf[CHR_READ_BUF_LEN];
|
||||||
DWORD size;
|
DWORD size;
|
||||||
|
|
||||||
|
if (len > max_size) {
|
||||||
|
len = max_size;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ZeroMemory(&s->orecv, sizeof(s->orecv));
|
ZeroMemory(&s->orecv, sizeof(s->orecv));
|
||||||
s->orecv.hEvent = s->hrecv;
|
s->orecv.hEvent = s->hrecv;
|
||||||
ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
|
ret = ReadFile(s->file, buf, len, &size, &s->orecv);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err == ERROR_IO_PENDING) {
|
if (err == ERROR_IO_PENDING) {
|
||||||
ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
|
ret = GetOverlappedResult(s->file, &s->orecv, &size, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,46 +56,22 @@ static void win_chr_readfile(Chardev *chr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void win_chr_read(Chardev *chr)
|
static int win_chr_serial_poll(void *opaque)
|
||||||
{
|
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
|
||||||
|
|
||||||
if (s->len > s->max_size) {
|
|
||||||
s->len = s->max_size;
|
|
||||||
}
|
|
||||||
if (s->len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
win_chr_readfile(chr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int win_chr_read_poll(Chardev *chr)
|
|
||||||
{
|
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
|
||||||
|
|
||||||
s->max_size = qemu_chr_be_can_write(chr);
|
|
||||||
return s->max_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int win_chr_poll(void *opaque)
|
|
||||||
{
|
{
|
||||||
Chardev *chr = CHARDEV(opaque);
|
Chardev *chr = CHARDEV(opaque);
|
||||||
WinChardev *s = WIN_CHARDEV(opaque);
|
WinChardev *s = WIN_CHARDEV(opaque);
|
||||||
COMSTAT status;
|
COMSTAT status;
|
||||||
DWORD comerr;
|
DWORD comerr;
|
||||||
|
|
||||||
ClearCommError(s->hcom, &comerr, &status);
|
ClearCommError(s->file, &comerr, &status);
|
||||||
if (status.cbInQue > 0) {
|
if (status.cbInQue > 0) {
|
||||||
s->len = status.cbInQue;
|
win_chr_read(chr, status.cbInQue);
|
||||||
win_chr_read_poll(chr);
|
|
||||||
win_chr_read(chr);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int win_chr_init(Chardev *chr, const char *filename, Error **errp)
|
int win_chr_serial_init(Chardev *chr, const char *filename, Error **errp)
|
||||||
{
|
{
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
WinChardev *s = WIN_CHARDEV(chr);
|
||||||
COMMCONFIG comcfg;
|
COMMCONFIG comcfg;
|
||||||
@@ -108,15 +91,15 @@ int win_chr_init(Chardev *chr, const char *filename, Error **errp)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->hcom = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
s->file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
if (s->file == INVALID_HANDLE_VALUE) {
|
||||||
error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
|
error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
|
||||||
s->hcom = NULL;
|
s->file = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
|
if (!SetupComm(s->file, NRECVBUF, NSENDBUF)) {
|
||||||
error_setg(errp, "Failed SetupComm");
|
error_setg(errp, "Failed SetupComm");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -127,27 +110,27 @@ int win_chr_init(Chardev *chr, const char *filename, Error **errp)
|
|||||||
comcfg.dcb.DCBlength = sizeof(DCB);
|
comcfg.dcb.DCBlength = sizeof(DCB);
|
||||||
CommConfigDialog(filename, NULL, &comcfg);
|
CommConfigDialog(filename, NULL, &comcfg);
|
||||||
|
|
||||||
if (!SetCommState(s->hcom, &comcfg.dcb)) {
|
if (!SetCommState(s->file, &comcfg.dcb)) {
|
||||||
error_setg(errp, "Failed SetCommState");
|
error_setg(errp, "Failed SetCommState");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetCommMask(s->hcom, EV_ERR)) {
|
if (!SetCommMask(s->file, EV_ERR)) {
|
||||||
error_setg(errp, "Failed SetCommMask");
|
error_setg(errp, "Failed SetCommMask");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
cto.ReadIntervalTimeout = MAXDWORD;
|
cto.ReadIntervalTimeout = MAXDWORD;
|
||||||
if (!SetCommTimeouts(s->hcom, &cto)) {
|
if (!SetCommTimeouts(s->file, &cto)) {
|
||||||
error_setg(errp, "Failed SetCommTimeouts");
|
error_setg(errp, "Failed SetCommTimeouts");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ClearCommError(s->hcom, &err, &comstat)) {
|
if (!ClearCommError(s->file, &err, &comstat)) {
|
||||||
error_setg(errp, "Failed ClearCommError");
|
error_setg(errp, "Failed ClearCommError");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
qemu_add_polling_cb(win_chr_poll, chr);
|
qemu_add_polling_cb(win_chr_serial_poll, chr);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@@ -160,11 +143,9 @@ int win_chr_pipe_poll(void *opaque)
|
|||||||
WinChardev *s = WIN_CHARDEV(opaque);
|
WinChardev *s = WIN_CHARDEV(opaque);
|
||||||
DWORD size;
|
DWORD size;
|
||||||
|
|
||||||
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
|
PeekNamedPipe(s->file, NULL, 0, NULL, &size, NULL);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
s->len = size;
|
win_chr_read(chr, size);
|
||||||
win_chr_read_poll(chr);
|
|
||||||
win_chr_read(chr);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -181,14 +162,14 @@ static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
|
|||||||
s->osend.hEvent = s->hsend;
|
s->osend.hEvent = s->hsend;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
if (s->hsend) {
|
if (s->hsend) {
|
||||||
ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
|
ret = WriteFile(s->file, buf, len, &size, &s->osend);
|
||||||
} else {
|
} else {
|
||||||
ret = WriteFile(s->hcom, buf, len, &size, NULL);
|
ret = WriteFile(s->file, buf, len, &size, NULL);
|
||||||
}
|
}
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err == ERROR_IO_PENDING) {
|
if (err == ERROR_IO_PENDING) {
|
||||||
ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
|
ret = GetOverlappedResult(s->file, &s->osend, &size, TRUE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
buf += size;
|
buf += size;
|
||||||
len -= size;
|
len -= size;
|
||||||
@@ -211,34 +192,30 @@ static void char_win_finalize(Object *obj)
|
|||||||
Chardev *chr = CHARDEV(obj);
|
Chardev *chr = CHARDEV(obj);
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
WinChardev *s = WIN_CHARDEV(chr);
|
||||||
|
|
||||||
if (s->skip_free) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->hsend) {
|
if (s->hsend) {
|
||||||
CloseHandle(s->hsend);
|
CloseHandle(s->hsend);
|
||||||
}
|
}
|
||||||
if (s->hrecv) {
|
if (s->hrecv) {
|
||||||
CloseHandle(s->hrecv);
|
CloseHandle(s->hrecv);
|
||||||
}
|
}
|
||||||
if (s->hcom) {
|
if (!s->keep_open && s->file) {
|
||||||
CloseHandle(s->hcom);
|
CloseHandle(s->file);
|
||||||
}
|
}
|
||||||
if (s->fpipe) {
|
if (s->fpipe) {
|
||||||
qemu_del_polling_cb(win_chr_pipe_poll, chr);
|
qemu_del_polling_cb(win_chr_pipe_poll, chr);
|
||||||
} else {
|
} else {
|
||||||
qemu_del_polling_cb(win_chr_poll, chr);
|
qemu_del_polling_cb(win_chr_serial_poll, chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
|
void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open)
|
||||||
{
|
{
|
||||||
WinChardev *s = WIN_CHARDEV(chr);
|
WinChardev *s = WIN_CHARDEV(chr);
|
||||||
|
|
||||||
s->skip_free = true;
|
s->keep_open = keep_open;
|
||||||
s->hcom = fd_out;
|
s->file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_win_class_init(ObjectClass *oc, void *data)
|
static void char_win_class_init(ObjectClass *oc, void *data)
|
||||||
|
394
chardev/char.c
394
chardev/char.c
@@ -22,22 +22,18 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
#include "qemu/help_option.h"
|
#include "qemu/help_option.h"
|
||||||
|
|
||||||
#include "char-mux.h"
|
#include "chardev/char-mux.h"
|
||||||
#include "char-io.h"
|
|
||||||
#include "char-parallel.h"
|
|
||||||
#include "char-serial.h"
|
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* character device */
|
/* character device */
|
||||||
@@ -70,8 +66,7 @@ void qemu_chr_be_event(Chardev *s, int event)
|
|||||||
|
|
||||||
/* 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_write_log(Chardev *s, const uint8_t *buf, size_t len)
|
||||||
const uint8_t *buf, size_t len)
|
|
||||||
{
|
{
|
||||||
size_t done = 0;
|
size_t done = 0;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
@@ -95,8 +90,9 @@ static void qemu_chr_fe_write_log(Chardev *s,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_chr_fe_write_buffer(Chardev *s,
|
static int qemu_chr_write_buffer(Chardev *s,
|
||||||
const uint8_t *buf, int len, int *offset)
|
const uint8_t *buf, int len,
|
||||||
|
int *offset, bool write_all)
|
||||||
{
|
{
|
||||||
ChardevClass *cc = CHARDEV_GET_CLASS(s);
|
ChardevClass *cc = CHARDEV_GET_CLASS(s);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
@@ -106,7 +102,7 @@ static int qemu_chr_fe_write_buffer(Chardev *s,
|
|||||||
while (*offset < len) {
|
while (*offset < len) {
|
||||||
retry:
|
retry:
|
||||||
res = cc->chr_write(s, buf + *offset, len - *offset);
|
res = cc->chr_write(s, buf + *offset, len - *offset);
|
||||||
if (res < 0 && errno == EAGAIN) {
|
if (res < 0 && errno == EAGAIN && write_all) {
|
||||||
g_usleep(100);
|
g_usleep(100);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@@ -116,68 +112,31 @@ static int qemu_chr_fe_write_buffer(Chardev *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*offset += res;
|
*offset += res;
|
||||||
|
if (!write_all) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (*offset > 0) {
|
if (*offset > 0) {
|
||||||
qemu_chr_fe_write_log(s, buf, *offset);
|
qemu_chr_write_log(s, buf, *offset);
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(&s->chr_write_lock);
|
qemu_mutex_unlock(&s->chr_write_lock);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool qemu_chr_replay(Chardev *chr)
|
int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all)
|
||||||
{
|
{
|
||||||
return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
|
int offset = 0;
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
ChardevClass *cc;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
|
|
||||||
int offset;
|
|
||||||
replay_char_write_event_load(&ret, &offset);
|
|
||||||
assert(offset <= len);
|
|
||||||
qemu_chr_fe_write_buffer(s, buf, offset, &offset);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
cc = CHARDEV_GET_CLASS(s);
|
|
||||||
qemu_mutex_lock(&s->chr_write_lock);
|
|
||||||
ret = cc->chr_write(s, buf, len);
|
|
||||||
|
|
||||||
if (ret > 0) {
|
|
||||||
qemu_chr_fe_write_log(s, buf, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_mutex_unlock(&s->chr_write_lock);
|
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
|
||||||
replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
int offset;
|
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
|
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
|
||||||
replay_char_write_event_load(&res, &offset);
|
replay_char_write_event_load(&res, &offset);
|
||||||
assert(offset <= len);
|
assert(offset <= len);
|
||||||
qemu_chr_fe_write_buffer(s, buf, offset, &offset);
|
qemu_chr_write_buffer(s, buf, offset, &offset, true);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
|
res = qemu_chr_write_buffer(s, buf, len, &offset, write_all);
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
||||||
replay_char_write_event_save(res, offset);
|
replay_char_write_event_save(res, offset);
|
||||||
@@ -189,78 +148,6 @@ int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return qemu_chr_write_all(s, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
int offset = 0, counter = 10;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
|
|
||||||
return replay_char_read_all_load(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (offset < len) {
|
|
||||||
retry:
|
|
||||||
res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
|
|
||||||
len - offset);
|
|
||||||
if (res == -1 && errno == EAGAIN) {
|
|
||||||
g_usleep(100);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res < 0) {
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
|
||||||
replay_char_read_all_save_error(res);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += res;
|
|
||||||
|
|
||||||
if (!counter--) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
|
||||||
replay_char_read_all_save_buf(buf, offset);
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
|
|
||||||
res = -ENOTSUP;
|
|
||||||
} else {
|
|
||||||
res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_be_can_write(Chardev *s)
|
int qemu_chr_be_can_write(Chardev *s)
|
||||||
{
|
{
|
||||||
CharBackend *be = s->be;
|
CharBackend *be = s->be;
|
||||||
@@ -293,75 +180,12 @@ void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int qemu_chr_fe_get_msgfd(CharBackend *be)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
int fd;
|
|
||||||
int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
|
|
||||||
if (s && qemu_chr_replay(s)) {
|
|
||||||
error_report("Replay: get msgfd is not supported "
|
|
||||||
"for serial devices yet");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CHARDEV_GET_CLASS(s)->get_msgfds ?
|
|
||||||
CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CHARDEV_GET_CLASS(s)->set_msgfds ?
|
|
||||||
CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_add_client(Chardev *s, int fd)
|
int qemu_chr_add_client(Chardev *s, int fd)
|
||||||
{
|
{
|
||||||
return CHARDEV_GET_CLASS(s)->chr_add_client ?
|
return CHARDEV_GET_CLASS(s)->chr_add_client ?
|
||||||
CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
|
CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_fe_accept_input(CharBackend *be)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
|
|
||||||
if (!s) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
|
|
||||||
CHARDEV_GET_CLASS(s)->chr_accept_input(s);
|
|
||||||
}
|
|
||||||
qemu_notify_event();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char buf[CHR_READ_BUF_LEN];
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
||||||
/* XXX this blocks entire thread. Rewrite to use
|
|
||||||
* qemu_chr_fe_write and background I/O callbacks */
|
|
||||||
qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
|
static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
|
||||||
bool *be_opened, Error **errp)
|
bool *be_opened, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -473,40 +297,6 @@ static Notifier muxes_realize_notify = {
|
|||||||
.notify = muxes_realize_done,
|
.notify = muxes_realize_done,
|
||||||
};
|
};
|
||||||
|
|
||||||
Chardev *qemu_chr_fe_get_driver(CharBackend *be)
|
|
||||||
{
|
|
||||||
return be->chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
|
|
||||||
{
|
|
||||||
int tag = 0;
|
|
||||||
|
|
||||||
if (CHARDEV_IS_MUX(s)) {
|
|
||||||
MuxChardev *d = MUX_CHARDEV(s);
|
|
||||||
|
|
||||||
if (d->mux_cnt >= MAX_MUX) {
|
|
||||||
goto unavailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->backends[d->mux_cnt] = b;
|
|
||||||
tag = d->mux_cnt++;
|
|
||||||
} else if (s->be) {
|
|
||||||
goto unavailable;
|
|
||||||
} else {
|
|
||||||
s->be = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
b->fe_open = false;
|
|
||||||
b->tag = tag;
|
|
||||||
b->chr = s;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
unavailable:
|
|
||||||
error_setg(errp, QERR_DEVICE_IN_USE, s->label);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool qemu_chr_is_busy(Chardev *s)
|
static bool qemu_chr_is_busy(Chardev *s)
|
||||||
{
|
{
|
||||||
if (CHARDEV_IS_MUX(s)) {
|
if (CHARDEV_IS_MUX(s)) {
|
||||||
@@ -517,84 +307,6 @@ static bool qemu_chr_is_busy(Chardev *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_fe_deinit(CharBackend *b)
|
|
||||||
{
|
|
||||||
assert(b);
|
|
||||||
|
|
||||||
if (b->chr) {
|
|
||||||
qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
|
|
||||||
if (b->chr->be == b) {
|
|
||||||
b->chr->be = NULL;
|
|
||||||
}
|
|
||||||
if (CHARDEV_IS_MUX(b->chr)) {
|
|
||||||
MuxChardev *d = MUX_CHARDEV(b->chr);
|
|
||||||
d->backends[b->tag] = NULL;
|
|
||||||
}
|
|
||||||
b->chr = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_chr_fe_set_handlers(CharBackend *b,
|
|
||||||
IOCanReadHandler *fd_can_read,
|
|
||||||
IOReadHandler *fd_read,
|
|
||||||
IOEventHandler *fd_event,
|
|
||||||
void *opaque,
|
|
||||||
GMainContext *context,
|
|
||||||
bool set_open)
|
|
||||||
{
|
|
||||||
Chardev *s;
|
|
||||||
ChardevClass *cc;
|
|
||||||
int fe_open;
|
|
||||||
|
|
||||||
s = b->chr;
|
|
||||||
if (!s) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cc = CHARDEV_GET_CLASS(s);
|
|
||||||
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
|
||||||
fe_open = 0;
|
|
||||||
remove_fd_in_watch(s);
|
|
||||||
} else {
|
|
||||||
fe_open = 1;
|
|
||||||
}
|
|
||||||
b->chr_can_read = fd_can_read;
|
|
||||||
b->chr_read = fd_read;
|
|
||||||
b->chr_event = fd_event;
|
|
||||||
b->opaque = opaque;
|
|
||||||
if (cc->chr_update_read_handler) {
|
|
||||||
cc->chr_update_read_handler(s, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (set_open) {
|
|
||||||
qemu_chr_fe_set_open(b, fe_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fe_open) {
|
|
||||||
qemu_chr_fe_take_focus(b);
|
|
||||||
/* We're connecting to an already opened device, so let's make sure we
|
|
||||||
also get the open event */
|
|
||||||
if (s->be_open) {
|
|
||||||
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CHARDEV_IS_MUX(s)) {
|
|
||||||
mux_chr_set_handlers(s, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_chr_fe_take_focus(CharBackend *b)
|
|
||||||
{
|
|
||||||
if (!b->chr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CHARDEV_IS_MUX(b->chr)) {
|
|
||||||
mux_set_focus(b->chr, b->tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_chr_wait_connected(Chardev *chr, Error **errp)
|
int qemu_chr_wait_connected(Chardev *chr, Error **errp)
|
||||||
{
|
{
|
||||||
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
|
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
|
||||||
@@ -606,16 +318,6 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
|
|
||||||
{
|
|
||||||
if (!be->chr) {
|
|
||||||
error_setg(errp, "missing associated backend");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return qemu_chr_wait_connected(be->chr, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
||||||
{
|
{
|
||||||
char host[65], port[33], width[8], height[8];
|
char host[65], port[33], width[8], height[8];
|
||||||
@@ -748,12 +450,12 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
|||||||
}
|
}
|
||||||
if (strstart(filename, "/dev/parport", NULL) ||
|
if (strstart(filename, "/dev/parport", NULL) ||
|
||||||
strstart(filename, "/dev/ppi", NULL)) {
|
strstart(filename, "/dev/ppi", NULL)) {
|
||||||
qemu_opt_set(opts, "backend", "parport", &error_abort);
|
qemu_opt_set(opts, "backend", "parallel", &error_abort);
|
||||||
qemu_opt_set(opts, "path", filename, &error_abort);
|
qemu_opt_set(opts, "path", filename, &error_abort);
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
if (strstart(filename, "/dev/", NULL)) {
|
if (strstart(filename, "/dev/", NULL)) {
|
||||||
qemu_opt_set(opts, "backend", "tty", &error_abort);
|
qemu_opt_set(opts, "backend", "serial", &error_abort);
|
||||||
qemu_opt_set(opts, "path", filename, &error_abort);
|
qemu_opt_set(opts, "path", filename, &error_abort);
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
@@ -841,7 +543,7 @@ chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque)
|
|||||||
|
|
||||||
object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
|
object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
|
for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
|
||||||
fn(chardev_alias_table[i].alias, opaque);
|
fn(chardev_alias_table[i].alias, opaque);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -887,7 +589,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
|
for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
|
||||||
if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
|
if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
|
||||||
name = chardev_alias_table[i].typename;
|
name = chardev_alias_table[i].typename;
|
||||||
break;
|
break;
|
||||||
@@ -992,64 +694,6 @@ Chardev *qemu_chr_new(const char *label, const char *filename)
|
|||||||
return chr;
|
return chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
|
|
||||||
{
|
|
||||||
Chardev *chr = be->chr;
|
|
||||||
|
|
||||||
if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
|
|
||||||
CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
|
|
||||||
{
|
|
||||||
Chardev *chr = be->chr;
|
|
||||||
|
|
||||||
if (!chr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (be->fe_open == fe_open) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
be->fe_open = fe_open;
|
|
||||||
if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
|
|
||||||
CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
|
|
||||||
GIOFunc func, void *user_data)
|
|
||||||
{
|
|
||||||
Chardev *s = be->chr;
|
|
||||||
GSource *src;
|
|
||||||
guint tag;
|
|
||||||
|
|
||||||
if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
|
|
||||||
if (!src) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
|
|
||||||
tag = g_source_attach(src, NULL);
|
|
||||||
g_source_unref(src);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_chr_fe_disconnect(CharBackend *be)
|
|
||||||
{
|
|
||||||
Chardev *chr = be->chr;
|
|
||||||
|
|
||||||
if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
|
|
||||||
CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qmp_query_chardev_foreach(Object *obj, void *data)
|
static int qmp_query_chardev_foreach(Object *obj, void *data)
|
||||||
{
|
{
|
||||||
Chardev *chr = CHARDEV(obj);
|
Chardev *chr = CHARDEV(obj);
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "ui/input.h"
|
#include "ui/input.h"
|
||||||
|
|
@@ -1,7 +1,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "ui/qemu-spice.h"
|
#include "ui/qemu-spice.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include <spice.h>
|
#include <spice.h>
|
||||||
#include <spice/protocol.h>
|
#include <spice/protocol.h>
|
@@ -25,7 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char.h"
|
||||||
|
|
||||||
#define BUF_SIZE 32
|
#define BUF_SIZE 32
|
||||||
|
|
18
chardev/trace-events
Normal file
18
chardev/trace-events
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# See docs/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
|
# chardev/wctablet.c
|
||||||
|
wct_init(void) ""
|
||||||
|
wct_cmd_re(void) ""
|
||||||
|
wct_cmd_st(void) ""
|
||||||
|
wct_cmd_sp(void) ""
|
||||||
|
wct_cmd_ts(int input) "0x%02x"
|
||||||
|
wct_cmd_other(const char *cmd) "%s"
|
||||||
|
wct_speed(int speed) "%d"
|
||||||
|
|
||||||
|
# chardev/spice.c
|
||||||
|
spice_vmc_write(ssize_t out, int len) "spice wrote %zd of requested %d"
|
||||||
|
spice_vmc_read(int bytes, int len) "spice read %d of requested %d"
|
||||||
|
spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
|
||||||
|
spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"
|
||||||
|
spice_vmc_event(int event) "spice vmc event %d"
|
||||||
|
|
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "sysemu/char.h"
|
#include "chardev/char-serial.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "ui/input.h"
|
#include "ui/input.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
155
configure
vendored
155
configure
vendored
@@ -91,7 +91,8 @@ update_cxxflags() {
|
|||||||
# Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those
|
# Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those
|
||||||
# options which some versions of GCC's C++ compiler complain about
|
# options which some versions of GCC's C++ compiler complain about
|
||||||
# because they only make sense for C programs.
|
# because they only make sense for C programs.
|
||||||
QEMU_CXXFLAGS=
|
QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS"
|
||||||
|
|
||||||
for arg in $QEMU_CFLAGS; do
|
for arg in $QEMU_CFLAGS; do
|
||||||
case $arg in
|
case $arg in
|
||||||
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
|
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
|
||||||
@@ -300,6 +301,7 @@ seccomp=""
|
|||||||
glusterfs=""
|
glusterfs=""
|
||||||
glusterfs_xlator_opt="no"
|
glusterfs_xlator_opt="no"
|
||||||
glusterfs_discard="no"
|
glusterfs_discard="no"
|
||||||
|
glusterfs_fallocate="no"
|
||||||
glusterfs_zerofill="no"
|
glusterfs_zerofill="no"
|
||||||
gtk=""
|
gtk=""
|
||||||
gtkabi=""
|
gtkabi=""
|
||||||
@@ -316,6 +318,7 @@ vte=""
|
|||||||
virglrenderer=""
|
virglrenderer=""
|
||||||
tpm="yes"
|
tpm="yes"
|
||||||
libssh2=""
|
libssh2=""
|
||||||
|
live_block_migration="yes"
|
||||||
numa=""
|
numa=""
|
||||||
tcmalloc="no"
|
tcmalloc="no"
|
||||||
jemalloc="no"
|
jemalloc="no"
|
||||||
@@ -343,6 +346,9 @@ for opt do
|
|||||||
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
|
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
|
||||||
EXTRA_CFLAGS="$optarg"
|
EXTRA_CFLAGS="$optarg"
|
||||||
;;
|
;;
|
||||||
|
--extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg"
|
||||||
|
EXTRA_CXXFLAGS="$optarg"
|
||||||
|
;;
|
||||||
--extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg"
|
--extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg"
|
||||||
EXTRA_LDFLAGS="$optarg"
|
EXTRA_LDFLAGS="$optarg"
|
||||||
;;
|
;;
|
||||||
@@ -401,7 +407,7 @@ QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS"
|
|||||||
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
|
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
|
||||||
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
|
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
|
||||||
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
|
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
|
||||||
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/include"
|
QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/accel/tcg -I\$(SRC_PATH)/include"
|
||||||
if test "$debug_info" = "yes"; then
|
if test "$debug_info" = "yes"; then
|
||||||
CFLAGS="-g $CFLAGS"
|
CFLAGS="-g $CFLAGS"
|
||||||
LDFLAGS="-g $LDFLAGS"
|
LDFLAGS="-g $LDFLAGS"
|
||||||
@@ -786,6 +792,8 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--extra-cflags=*)
|
--extra-cflags=*)
|
||||||
;;
|
;;
|
||||||
|
--extra-cxxflags=*)
|
||||||
|
;;
|
||||||
--extra-ldflags=*)
|
--extra-ldflags=*)
|
||||||
;;
|
;;
|
||||||
--enable-debug-info)
|
--enable-debug-info)
|
||||||
@@ -1169,6 +1177,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--enable-libssh2) libssh2="yes"
|
--enable-libssh2) libssh2="yes"
|
||||||
;;
|
;;
|
||||||
|
--disable-live-block-migration) live_block_migration="no"
|
||||||
|
;;
|
||||||
|
--enable-live-block-migration) live_block_migration="yes"
|
||||||
|
;;
|
||||||
--disable-numa) numa="no"
|
--disable-numa) numa="no"
|
||||||
;;
|
;;
|
||||||
--enable-numa) numa="yes"
|
--enable-numa) numa="yes"
|
||||||
@@ -1207,12 +1219,12 @@ case "$cpu" in
|
|||||||
LDFLAGS="-m64 $LDFLAGS"
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
;;
|
;;
|
||||||
sparc)
|
sparc)
|
||||||
LDFLAGS="-m32 $LDFLAGS"
|
CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc"
|
||||||
CPU_CFLAGS="-m32 -mcpu=ultrasparc"
|
LDFLAGS="-m32 -mv8plus $LDFLAGS"
|
||||||
;;
|
;;
|
||||||
sparc64)
|
sparc64)
|
||||||
LDFLAGS="-m64 $LDFLAGS"
|
|
||||||
CPU_CFLAGS="-m64 -mcpu=ultrasparc"
|
CPU_CFLAGS="-m64 -mcpu=ultrasparc"
|
||||||
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
;;
|
;;
|
||||||
s390)
|
s390)
|
||||||
CPU_CFLAGS="-m31"
|
CPU_CFLAGS="-m31"
|
||||||
@@ -1299,6 +1311,7 @@ Advanced options (experts only):
|
|||||||
--cxx=CXX use C++ compiler CXX [$cxx]
|
--cxx=CXX use C++ compiler CXX [$cxx]
|
||||||
--objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
|
--objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
|
||||||
--extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
|
--extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
|
||||||
|
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS
|
||||||
--extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
|
--extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
|
||||||
--make=MAKE use specified make [$make]
|
--make=MAKE use specified make [$make]
|
||||||
--install=INSTALL use specified install [$install]
|
--install=INSTALL use specified install [$install]
|
||||||
@@ -1401,6 +1414,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
|||||||
libnfs nfs support
|
libnfs nfs support
|
||||||
smartcard smartcard support (libcacard)
|
smartcard smartcard support (libcacard)
|
||||||
libusb libusb (for usb passthrough)
|
libusb libusb (for usb passthrough)
|
||||||
|
live-block-migration Block migration in the main migration stream
|
||||||
usb-redir usb network redirection support
|
usb-redir usb network redirection support
|
||||||
lzo support of lzo compression library
|
lzo support of lzo compression library
|
||||||
snappy support of snappy compression library
|
snappy support of snappy compression library
|
||||||
@@ -1483,37 +1497,6 @@ if test "$bogus_os" = "yes"; then
|
|||||||
error_exit "Unrecognized host OS $targetos"
|
error_exit "Unrecognized host OS $targetos"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check that the C++ compiler exists and works with the C compiler
|
|
||||||
if has $cxx; then
|
|
||||||
cat > $TMPC <<EOF
|
|
||||||
int c_function(void);
|
|
||||||
int main(void) { return c_function(); }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
compile_object
|
|
||||||
|
|
||||||
cat > $TMPCXX <<EOF
|
|
||||||
extern "C" {
|
|
||||||
int c_function(void);
|
|
||||||
}
|
|
||||||
int c_function(void) { return 42; }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
update_cxxflags
|
|
||||||
|
|
||||||
if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then
|
|
||||||
# C++ compiler $cxx works ok with C compiler $cc
|
|
||||||
:
|
|
||||||
else
|
|
||||||
echo "C++ compiler $cxx does not work with C compiler $cc"
|
|
||||||
echo "Disabling C++ specific optional code"
|
|
||||||
cxx=
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "No C++ compiler available; disabling C++ specific optional code"
|
|
||||||
cxx=
|
|
||||||
fi
|
|
||||||
|
|
||||||
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
|
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
|
||||||
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
|
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
|
||||||
gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||||
@@ -2318,14 +2301,14 @@ fi
|
|||||||
# GTK probe
|
# GTK probe
|
||||||
|
|
||||||
if test "$gtkabi" = ""; then
|
if test "$gtkabi" = ""; then
|
||||||
# The GTK ABI was not specified explicitly, so try whether 2.0 is available.
|
# The GTK ABI was not specified explicitly, so try whether 3.0 is available.
|
||||||
# Use 3.0 as a fallback if that is available.
|
# Use 2.0 as a fallback if that is available.
|
||||||
if $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
if $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
||||||
gtkabi=2.0
|
|
||||||
elif $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
|
|
||||||
gtkabi=3.0
|
gtkabi=3.0
|
||||||
else
|
elif $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
|
||||||
gtkabi=2.0
|
gtkabi=2.0
|
||||||
|
else
|
||||||
|
gtkabi=3.0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -2348,7 +2331,7 @@ if test "$gtk" != "no"; then
|
|||||||
libs_softmmu="$gtk_libs $libs_softmmu"
|
libs_softmmu="$gtk_libs $libs_softmmu"
|
||||||
gtk="yes"
|
gtk="yes"
|
||||||
elif test "$gtk" = "yes"; then
|
elif test "$gtk" = "yes"; then
|
||||||
feature_not_found "gtk" "Install gtk2 or gtk3 devel"
|
feature_not_found "gtk" "Install gtk3-devel"
|
||||||
else
|
else
|
||||||
gtk="no"
|
gtk="no"
|
||||||
fi
|
fi
|
||||||
@@ -2615,12 +2598,12 @@ fi
|
|||||||
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
||||||
|
|
||||||
if test "$sdlabi" = ""; then
|
if test "$sdlabi" = ""; then
|
||||||
if $pkg_config --exists "sdl"; then
|
if $pkg_config --exists "sdl2"; then
|
||||||
sdlabi=1.2
|
|
||||||
elif $pkg_config --exists "sdl2"; then
|
|
||||||
sdlabi=2.0
|
sdlabi=2.0
|
||||||
else
|
elif $pkg_config --exists "sdl"; then
|
||||||
sdlabi=1.2
|
sdlabi=1.2
|
||||||
|
else
|
||||||
|
sdlabi=2.0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -2647,7 +2630,7 @@ elif has ${sdl_config}; then
|
|||||||
sdlversion=$($sdlconfig --version)
|
sdlversion=$($sdlconfig --version)
|
||||||
else
|
else
|
||||||
if test "$sdl" = "yes" ; then
|
if test "$sdl" = "yes" ; then
|
||||||
feature_not_found "sdl" "Install SDL devel"
|
feature_not_found "sdl" "Install SDL2-devel"
|
||||||
fi
|
fi
|
||||||
sdl=no
|
sdl=no
|
||||||
fi
|
fi
|
||||||
@@ -3035,14 +3018,13 @@ if test "$curses" != "no" ; then
|
|||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
int main(void) {
|
int main(void) {
|
||||||
const char *s = curses_version();
|
|
||||||
wchar_t wch = L'w';
|
wchar_t wch = L'w';
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
resize_term(0, 0);
|
resize_term(0, 0);
|
||||||
addwstr(L"wide chars\n");
|
addwstr(L"wide chars\n");
|
||||||
addnwstr(&wch, 1);
|
addnwstr(&wch, 1);
|
||||||
add_wch(WACS_DEGREE);
|
add_wch(WACS_DEGREE);
|
||||||
return s != 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
IFS=:
|
IFS=:
|
||||||
@@ -3577,6 +3559,7 @@ if test "$glusterfs" != "no" ; then
|
|||||||
glusterfs_discard="yes"
|
glusterfs_discard="yes"
|
||||||
fi
|
fi
|
||||||
if $pkg_config --atleast-version=6 glusterfs-api; then
|
if $pkg_config --atleast-version=6 glusterfs-api; then
|
||||||
|
glusterfs_fallocate="yes"
|
||||||
glusterfs_zerofill="yes"
|
glusterfs_zerofill="yes"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -3623,25 +3606,6 @@ if compile_prog "" "" ; then
|
|||||||
inotify1=yes
|
inotify1=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# check if utimensat and futimens are supported
|
|
||||||
utimens=no
|
|
||||||
cat > $TMPC << EOF
|
|
||||||
#define _ATFILE_SOURCE
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
utimensat(AT_FDCWD, "foo", NULL, 0);
|
|
||||||
futimens(0, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
if compile_prog "" "" ; then
|
|
||||||
utimens=yes
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if pipe2 is there
|
# check if pipe2 is there
|
||||||
pipe2=no
|
pipe2=no
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
@@ -5076,6 +5040,38 @@ EOF
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check that the C++ compiler exists and works with the C compiler.
|
||||||
|
# All the QEMU_CXXFLAGS are based on QEMU_CFLAGS. Keep this at the end to don't miss any other that could be added.
|
||||||
|
if has $cxx; then
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
int c_function(void);
|
||||||
|
int main(void) { return c_function(); }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
compile_object
|
||||||
|
|
||||||
|
cat > $TMPCXX <<EOF
|
||||||
|
extern "C" {
|
||||||
|
int c_function(void);
|
||||||
|
}
|
||||||
|
int c_function(void) { return 42; }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
update_cxxflags
|
||||||
|
|
||||||
|
if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then
|
||||||
|
# C++ compiler $cxx works ok with C compiler $cc
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "C++ compiler $cxx does not work with C compiler $cc"
|
||||||
|
echo "Disabling C++ specific optional code"
|
||||||
|
cxx=
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No C++ compiler available; disabling C++ specific optional code"
|
||||||
|
cxx=
|
||||||
|
fi
|
||||||
|
|
||||||
echo_version() {
|
echo_version() {
|
||||||
if test "$1" = "yes" ; then
|
if test "$1" = "yes" ; then
|
||||||
echo "($2)"
|
echo "($2)"
|
||||||
@@ -5216,6 +5212,7 @@ echo "TPM support $tpm"
|
|||||||
echo "libssh2 support $libssh2"
|
echo "libssh2 support $libssh2"
|
||||||
echo "TPM passthrough $tpm_passthrough"
|
echo "TPM passthrough $tpm_passthrough"
|
||||||
echo "QOM debugging $qom_cast_debug"
|
echo "QOM debugging $qom_cast_debug"
|
||||||
|
echo "Live block migration $live_block_migration"
|
||||||
echo "lzo support $lzo"
|
echo "lzo support $lzo"
|
||||||
echo "snappy support $snappy"
|
echo "snappy support $snappy"
|
||||||
echo "bzip2 support $bzip2"
|
echo "bzip2 support $bzip2"
|
||||||
@@ -5280,6 +5277,7 @@ if test "$mingw32" = "no" ; then
|
|||||||
fi
|
fi
|
||||||
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
|
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
|
||||||
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
|
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
|
||||||
|
echo "extra_cxxflags=$EXTRA_CXXFLAGS" >> $config_host_mak
|
||||||
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
|
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
|
||||||
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
|
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
|
||||||
echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
|
echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
|
||||||
@@ -5427,9 +5425,6 @@ fi
|
|||||||
if test "$curses" = "yes" ; then
|
if test "$curses" = "yes" ; then
|
||||||
echo "CONFIG_CURSES=y" >> $config_host_mak
|
echo "CONFIG_CURSES=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
if test "$utimens" = "yes" ; then
|
|
||||||
echo "CONFIG_UTIMENSAT=y" >> $config_host_mak
|
|
||||||
fi
|
|
||||||
if test "$pipe2" = "yes" ; then
|
if test "$pipe2" = "yes" ; then
|
||||||
echo "CONFIG_PIPE2=y" >> $config_host_mak
|
echo "CONFIG_PIPE2=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
@@ -5772,6 +5767,10 @@ if test "$glusterfs_discard" = "yes" ; then
|
|||||||
echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
|
echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$glusterfs_fallocate" = "yes" ; then
|
||||||
|
echo "CONFIG_GLUSTERFS_FALLOCATE=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$glusterfs_zerofill" = "yes" ; then
|
if test "$glusterfs_zerofill" = "yes" ; then
|
||||||
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
|
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
@@ -5782,6 +5781,10 @@ if test "$libssh2" = "yes" ; then
|
|||||||
echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
|
echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$live_block_migration" = "yes" ; then
|
||||||
|
echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
# USB host support
|
# USB host support
|
||||||
if test "$libusb" = "yes"; then
|
if test "$libusb" = "yes"; then
|
||||||
echo "HOST_USB=libusb legacy" >> $config_host_mak
|
echo "HOST_USB=libusb legacy" >> $config_host_mak
|
||||||
@@ -5917,6 +5920,7 @@ echo "WINDRES=$windres" >> $config_host_mak
|
|||||||
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
||||||
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
|
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
|
||||||
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
||||||
|
echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak
|
||||||
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
|
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
|
||||||
if test "$sparse" = "yes" ; then
|
if test "$sparse" = "yes" ; then
|
||||||
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
|
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
|
||||||
@@ -6033,11 +6037,11 @@ TARGET_ABI_DIR=""
|
|||||||
|
|
||||||
case "$target_name" in
|
case "$target_name" in
|
||||||
i386)
|
i386)
|
||||||
gdb_xml_files="i386-32bit-core.xml"
|
gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml"
|
||||||
;;
|
;;
|
||||||
x86_64)
|
x86_64)
|
||||||
TARGET_BASE_ARCH=i386
|
TARGET_BASE_ARCH=i386
|
||||||
gdb_xml_files="i386-64bit-core.xml"
|
gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml"
|
||||||
;;
|
;;
|
||||||
alpha)
|
alpha)
|
||||||
mttcg="yes"
|
mttcg="yes"
|
||||||
@@ -6370,7 +6374,7 @@ fi
|
|||||||
|
|
||||||
# build tree in object directory in case the source is not in the current directory
|
# build tree in object directory in case the source is not in the current directory
|
||||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
|
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
|
||||||
DIRS="$DIRS docs fsdev"
|
DIRS="$DIRS docs docs/interop fsdev"
|
||||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||||
DIRS="$DIRS qapi-generated"
|
DIRS="$DIRS qapi-generated"
|
||||||
@@ -6382,6 +6386,7 @@ FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
|||||||
FILES="$FILES pc-bios/s390-ccw/Makefile"
|
FILES="$FILES pc-bios/s390-ccw/Makefile"
|
||||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||||
FILES="$FILES pc-bios/qemu-icon.bmp"
|
FILES="$FILES pc-bios/qemu-icon.bmp"
|
||||||
|
FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit
|
||||||
for bios_file in \
|
for bios_file in \
|
||||||
$source_path/pc-bios/*.bin \
|
$source_path/pc-bios/*.bin \
|
||||||
$source_path/pc-bios/*.lid \
|
$source_path/pc-bios/*.lid \
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
#include <linux/vhost.h>
|
#include <linux/vhost.h>
|
||||||
#include "standard-headers/linux/virtio_ring.h"
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
@@ -192,11 +193,11 @@ typedef struct VuVirtq {
|
|||||||
} VuVirtq;
|
} VuVirtq;
|
||||||
|
|
||||||
enum VuWatchCondtion {
|
enum VuWatchCondtion {
|
||||||
VU_WATCH_IN = 1 << 0,
|
VU_WATCH_IN = POLLIN,
|
||||||
VU_WATCH_OUT = 1 << 1,
|
VU_WATCH_OUT = POLLOUT,
|
||||||
VU_WATCH_PRI = 1 << 2,
|
VU_WATCH_PRI = POLLPRI,
|
||||||
VU_WATCH_ERR = 1 << 3,
|
VU_WATCH_ERR = POLLERR,
|
||||||
VU_WATCH_HUP = 1 << 4,
|
VU_WATCH_HUP = POLLHUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
||||||
|
1
contrib/vhost-user-scsi/Makefile.objs
Normal file
1
contrib/vhost-user-scsi/Makefile.objs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vhost-user-scsi-obj-y = vhost-user-scsi.o
|
886
contrib/vhost-user-scsi/vhost-user-scsi.c
Normal file
886
contrib/vhost-user-scsi/vhost-user-scsi.c
Normal file
@@ -0,0 +1,886 @@
|
|||||||
|
/*
|
||||||
|
* vhost-user-scsi sample application
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Nutanix Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Felipe Franciosi <felipe@nutanix.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 only.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "contrib/libvhost-user/libvhost-user.h"
|
||||||
|
#include "hw/virtio/virtio-scsi.h"
|
||||||
|
#include "iscsi/iscsi.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
/* Small compat shim from glib 2.32 */
|
||||||
|
#ifndef G_SOURCE_CONTINUE
|
||||||
|
#define G_SOURCE_CONTINUE TRUE
|
||||||
|
#endif
|
||||||
|
#ifndef G_SOURCE_REMOVE
|
||||||
|
#define G_SOURCE_REMOVE FALSE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* #define VUS_DEBUG 1 */
|
||||||
|
|
||||||
|
/** Log helpers **/
|
||||||
|
|
||||||
|
#define PPRE \
|
||||||
|
struct timespec ts; \
|
||||||
|
char timebuf[64]; \
|
||||||
|
struct tm tm; \
|
||||||
|
(void)clock_gettime(CLOCK_REALTIME, &ts); \
|
||||||
|
(void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
|
||||||
|
|
||||||
|
#define PEXT(lvl, msg, ...) do { \
|
||||||
|
PPRE; \
|
||||||
|
fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
|
||||||
|
timebuf, ts.tv_nsec / 1000, \
|
||||||
|
__FILE__, __func__, __LINE__, ## __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define PNOR(lvl, msg, ...) do { \
|
||||||
|
PPRE; \
|
||||||
|
fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
|
||||||
|
timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef VUS_DEBUG
|
||||||
|
#define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
|
||||||
|
#define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
|
||||||
|
#define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PDBG(msg, ...) { }
|
||||||
|
#define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
|
||||||
|
#define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** vhost-user-scsi specific definitions **/
|
||||||
|
|
||||||
|
/* Only 1 LUN and device supported today */
|
||||||
|
#define VUS_MAX_LUNS 1
|
||||||
|
#define VUS_MAX_DEVS 1
|
||||||
|
|
||||||
|
#define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
|
||||||
|
|
||||||
|
typedef struct iscsi_lun {
|
||||||
|
struct iscsi_context *iscsi_ctx;
|
||||||
|
int iscsi_lun;
|
||||||
|
} iscsi_lun_t;
|
||||||
|
|
||||||
|
typedef struct vhost_scsi_dev {
|
||||||
|
VuDev vu_dev;
|
||||||
|
int server_sock;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GTree *fdmap; /* fd -> gsource context id */
|
||||||
|
iscsi_lun_t luns[VUS_MAX_LUNS];
|
||||||
|
} vhost_scsi_dev_t;
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
|
||||||
|
|
||||||
|
/** glib event loop integration for libvhost-user and misc callbacks **/
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
|
||||||
|
QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
|
||||||
|
|
||||||
|
typedef struct vus_gsrc {
|
||||||
|
GSource parent;
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
GPollFD gfd;
|
||||||
|
vu_watch_cb vu_cb;
|
||||||
|
} vus_gsrc_t;
|
||||||
|
|
||||||
|
static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
return (b > a) - (b < a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
|
||||||
|
{
|
||||||
|
assert(timeout);
|
||||||
|
|
||||||
|
*timeout = -1;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_check(GSource *src)
|
||||||
|
{
|
||||||
|
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||||
|
|
||||||
|
assert(vus_src);
|
||||||
|
|
||||||
|
return vus_src->gfd.revents & vus_src->gfd.events;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
|
||||||
|
|
||||||
|
assert(vus_src);
|
||||||
|
assert(!(vus_src->vu_cb && cb));
|
||||||
|
|
||||||
|
vdev_scsi = vus_src->vdev_scsi;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
return cb(data);
|
||||||
|
}
|
||||||
|
if (vus_src->vu_cb) {
|
||||||
|
vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs vus_gsrc_funcs = {
|
||||||
|
vus_gsrc_prepare,
|
||||||
|
vus_gsrc_check,
|
||||||
|
vus_gsrc_dispatch,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
|
||||||
|
vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
|
||||||
|
{
|
||||||
|
GSource *vus_gsrc;
|
||||||
|
vus_gsrc_t *vus_src;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(vu_cb || gsrc_cb);
|
||||||
|
assert(!(vu_cb && gsrc_cb));
|
||||||
|
|
||||||
|
vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
|
||||||
|
if (!vus_gsrc) {
|
||||||
|
PERR("Error creating GSource for new watch");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vus_src = (vus_gsrc_t *)vus_gsrc;
|
||||||
|
|
||||||
|
vus_src->vdev_scsi = vdev_scsi;
|
||||||
|
vus_src->gfd.fd = fd;
|
||||||
|
vus_src->gfd.events = cond;
|
||||||
|
vus_src->vu_cb = vu_cb;
|
||||||
|
|
||||||
|
g_source_add_poll(vus_gsrc, &vus_src->gfd);
|
||||||
|
g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
|
||||||
|
id = g_source_attach(vus_gsrc, NULL);
|
||||||
|
assert(id);
|
||||||
|
g_source_unref(vus_gsrc);
|
||||||
|
|
||||||
|
g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
|
||||||
|
(gpointer)(uintptr_t)id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* from libiscsi's scsi-lowlevel.h **
|
||||||
|
*
|
||||||
|
* nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
|
||||||
|
* QEMU's scsi.h also defines "SCSI_XFER_NONE".
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SCSI_CDB_MAX_SIZE 16
|
||||||
|
|
||||||
|
struct scsi_iovector {
|
||||||
|
struct scsi_iovec *iov;
|
||||||
|
int niov;
|
||||||
|
int nalloc;
|
||||||
|
size_t offset;
|
||||||
|
int consumed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_allocated_memory {
|
||||||
|
struct scsi_allocated_memory *next;
|
||||||
|
char buf[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_data {
|
||||||
|
int size;
|
||||||
|
unsigned char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum scsi_sense_key {
|
||||||
|
SCSI_SENSE_NO_SENSE = 0x00,
|
||||||
|
SCSI_SENSE_RECOVERED_ERROR = 0x01,
|
||||||
|
SCSI_SENSE_NOT_READY = 0x02,
|
||||||
|
SCSI_SENSE_MEDIUM_ERROR = 0x03,
|
||||||
|
SCSI_SENSE_HARDWARE_ERROR = 0x04,
|
||||||
|
SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
|
||||||
|
SCSI_SENSE_UNIT_ATTENTION = 0x06,
|
||||||
|
SCSI_SENSE_DATA_PROTECTION = 0x07,
|
||||||
|
SCSI_SENSE_BLANK_CHECK = 0x08,
|
||||||
|
SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
|
||||||
|
SCSI_SENSE_COPY_ABORTED = 0x0a,
|
||||||
|
SCSI_SENSE_COMMAND_ABORTED = 0x0b,
|
||||||
|
SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
|
||||||
|
SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
|
||||||
|
SCSI_SENSE_MISCOMPARE = 0x0e
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_sense {
|
||||||
|
unsigned char error_type;
|
||||||
|
enum scsi_sense_key key;
|
||||||
|
int ascq;
|
||||||
|
unsigned sense_specific:1;
|
||||||
|
unsigned ill_param_in_cdb:1;
|
||||||
|
unsigned bit_pointer_valid:1;
|
||||||
|
unsigned char bit_pointer;
|
||||||
|
uint16_t field_pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum scsi_residual {
|
||||||
|
SCSI_RESIDUAL_NO_RESIDUAL = 0,
|
||||||
|
SCSI_RESIDUAL_UNDERFLOW,
|
||||||
|
SCSI_RESIDUAL_OVERFLOW
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scsi_task {
|
||||||
|
int status;
|
||||||
|
int cdb_size;
|
||||||
|
int xfer_dir;
|
||||||
|
int expxferlen;
|
||||||
|
unsigned char cdb[SCSI_CDB_MAX_SIZE];
|
||||||
|
enum scsi_residual residual_status;
|
||||||
|
size_t residual;
|
||||||
|
struct scsi_sense sense;
|
||||||
|
struct scsi_data datain;
|
||||||
|
struct scsi_allocated_memory *mem;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
uint32_t itt;
|
||||||
|
uint32_t cmdsn;
|
||||||
|
uint32_t lun;
|
||||||
|
|
||||||
|
struct scsi_iovector iovector_in;
|
||||||
|
struct scsi_iovector iovector_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** libiscsi integration **/
|
||||||
|
|
||||||
|
static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
|
||||||
|
{
|
||||||
|
struct iscsi_url *iscsi_url;
|
||||||
|
struct iscsi_context *iscsi_ctx;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
assert(lun);
|
||||||
|
assert(iscsi_uri);
|
||||||
|
|
||||||
|
iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
|
||||||
|
if (!iscsi_ctx) {
|
||||||
|
PERR("Unable to create iSCSI context");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
|
||||||
|
if (!iscsi_url) {
|
||||||
|
PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
|
||||||
|
iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||||
|
if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
|
||||||
|
PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
lun->iscsi_ctx = iscsi_ctx;
|
||||||
|
lun->iscsi_lun = iscsi_url->lun;
|
||||||
|
|
||||||
|
PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (iscsi_url) {
|
||||||
|
iscsi_destroy_url(iscsi_url);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
(void)iscsi_destroy_context(iscsi_ctx);
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
|
||||||
|
int xfer_len) {
|
||||||
|
struct scsi_task *task;
|
||||||
|
|
||||||
|
assert(cdb_len > 0);
|
||||||
|
assert(cdb);
|
||||||
|
|
||||||
|
task = calloc(1, sizeof(struct scsi_task));
|
||||||
|
if (!task) {
|
||||||
|
PERR("Error allocating task: %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(task->cdb, cdb, cdb_len);
|
||||||
|
task->cdb_size = cdb_len;
|
||||||
|
task->xfer_dir = dir;
|
||||||
|
task->expxferlen = xfer_len;
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_cdb_len(uint8_t *cdb)
|
||||||
|
{
|
||||||
|
assert(cdb);
|
||||||
|
|
||||||
|
switch (cdb[0] >> 5) {
|
||||||
|
case 0: return 6;
|
||||||
|
case 1: /* fall through */
|
||||||
|
case 2: return 10;
|
||||||
|
case 4: return 16;
|
||||||
|
case 5: return 12;
|
||||||
|
}
|
||||||
|
PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_cmd_sync(struct iscsi_context *ctx,
|
||||||
|
VirtIOSCSICmdReq *req,
|
||||||
|
struct iovec *out, unsigned int out_len,
|
||||||
|
VirtIOSCSICmdResp *rsp,
|
||||||
|
struct iovec *in, unsigned int in_len) {
|
||||||
|
struct scsi_task *task;
|
||||||
|
uint32_t dir;
|
||||||
|
uint32_t len;
|
||||||
|
int cdb_len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(ctx);
|
||||||
|
assert(req);
|
||||||
|
assert(rsp);
|
||||||
|
|
||||||
|
if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
|
||||||
|
/* Ignore anything different than target=0, lun=0 */
|
||||||
|
PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
|
||||||
|
req->lun[1], req->lun[3]);
|
||||||
|
rsp->status = SCSI_STATUS_CHECK_CONDITION;
|
||||||
|
memset(rsp->sense, 0, sizeof(rsp->sense));
|
||||||
|
rsp->sense_len = 18;
|
||||||
|
rsp->sense[0] = 0x70;
|
||||||
|
rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
|
||||||
|
rsp->sense[7] = 10;
|
||||||
|
rsp->sense[12] = 0x24;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdb_len = get_cdb_len(req->cdb);
|
||||||
|
if (cdb_len == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
if (!out_len && !in_len) {
|
||||||
|
dir = SCSI_XFER_NONE;
|
||||||
|
} else if (out_len) {
|
||||||
|
dir = SCSI_XFER_TO_DEV;
|
||||||
|
for (i = 0; i < out_len; i++) {
|
||||||
|
len += out[i].iov_len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dir = SCSI_XFER_FROM_DEV;
|
||||||
|
for (i = 0; i < in_len; i++) {
|
||||||
|
len += in[i].iov_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task = scsi_task_new(cdb_len, req->cdb, dir, len);
|
||||||
|
if (!task) {
|
||||||
|
PERR("Unable to create iscsi task");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == SCSI_XFER_TO_DEV) {
|
||||||
|
task->iovector_out.iov = (struct scsi_iovec *)out;
|
||||||
|
task->iovector_out.niov = out_len;
|
||||||
|
} else if (dir == SCSI_XFER_FROM_DEV) {
|
||||||
|
task->iovector_in.iov = (struct scsi_iovec *)in;
|
||||||
|
task->iovector_in.niov = in_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
|
||||||
|
cdb_len, dir, task);
|
||||||
|
if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
|
||||||
|
PERR("Error serving SCSI command");
|
||||||
|
free(task);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(rsp, 0, sizeof(*rsp));
|
||||||
|
|
||||||
|
rsp->status = task->status;
|
||||||
|
rsp->resid = task->residual;
|
||||||
|
|
||||||
|
if (task->status == SCSI_STATUS_CHECK_CONDITION) {
|
||||||
|
rsp->response = VIRTIO_SCSI_S_FAILURE;
|
||||||
|
rsp->sense_len = task->datain.size - 2;
|
||||||
|
memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(task);
|
||||||
|
|
||||||
|
PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
|
||||||
|
rsp->status, rsp->resid, rsp->response, rsp->sense_len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** libvhost-user callbacks **/
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
|
||||||
|
|
||||||
|
static void vus_panic_cb(VuDev *vu_dev, const char *buf)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
PERR("vu_panic: %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi) {
|
||||||
|
assert(vdev_scsi->loop);
|
||||||
|
g_main_loop_quit(vdev_scsi->loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
|
||||||
|
void *pvt) {
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(cb);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||||
|
(gpointer)(uintptr_t)fd);
|
||||||
|
if (id) {
|
||||||
|
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||||
|
assert(vus_src);
|
||||||
|
g_source_destroy(vus_src);
|
||||||
|
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_del_watch_cb(VuDev *vu_dev, int fd)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
|
||||||
|
(gpointer)(uintptr_t)fd);
|
||||||
|
if (id) {
|
||||||
|
GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
|
||||||
|
assert(vus_src);
|
||||||
|
g_source_destroy(vus_src);
|
||||||
|
(void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_ctl(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
/* Control VQ not implemented */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_evt(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
/* Event VQ not implemented */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_proc_req(VuDev *vu_dev, int idx)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi;
|
||||||
|
VuVirtq *vq;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||||
|
PERR("VQ Index out of range: %d", idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vq = vu_get_queue(vu_dev, idx);
|
||||||
|
if (!vq) {
|
||||||
|
PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDBG("Got kicked on vq[%d]@%p", idx, vq);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
VuVirtqElement *elem;
|
||||||
|
VirtIOSCSICmdReq *req;
|
||||||
|
VirtIOSCSICmdResp *rsp;
|
||||||
|
|
||||||
|
elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
|
||||||
|
if (!elem) {
|
||||||
|
PDBG("No more elements pending on vq[%d]@%p", idx, vq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PDBG("Popped elem@%p", elem);
|
||||||
|
|
||||||
|
assert(!((elem->out_num > 1) && (elem->in_num > 1)));
|
||||||
|
assert((elem->out_num > 0) && (elem->in_num > 0));
|
||||||
|
|
||||||
|
if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
|
||||||
|
PERR("Invalid virtio-scsi req header");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
|
||||||
|
|
||||||
|
if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
|
||||||
|
PERR("Invalid virtio-scsi rsp header");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
|
||||||
|
|
||||||
|
if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
|
||||||
|
req, &elem->out_sg[1], elem->out_num - 1,
|
||||||
|
rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vu_queue_push(vu_dev, vq, elem, 0);
|
||||||
|
vu_queue_notify(vu_dev, vq);
|
||||||
|
|
||||||
|
free(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
|
||||||
|
{
|
||||||
|
VuVirtq *vq;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
|
||||||
|
PERR("VQ Index out of range: %d", idx);
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vq = vu_get_queue(vu_dev, idx);
|
||||||
|
|
||||||
|
switch (idx) {
|
||||||
|
case 0:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VuDevIface vus_iface = {
|
||||||
|
.queue_set_started = vus_queue_set_started,
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean vus_vhost_cb(gpointer data)
|
||||||
|
{
|
||||||
|
VuDev *vu_dev = (VuDev *)data;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
if (!vu_dispatch(vu_dev) != 0) {
|
||||||
|
PERR("Error processing vhost message");
|
||||||
|
vus_panic_cb(vu_dev, NULL);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** misc helpers **/
|
||||||
|
|
||||||
|
static int unix_sock_new(char *unix_fn)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
struct sockaddr_un un;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
assert(unix_fn);
|
||||||
|
|
||||||
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sock <= 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
(void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
|
||||||
|
len = sizeof(un.sun_family) + strlen(un.sun_path);
|
||||||
|
|
||||||
|
(void)unlink(unix_fn);
|
||||||
|
if (bind(sock, (struct sockaddr *)&un, len) < 0) {
|
||||||
|
perror("bind");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(sock, 1) < 0) {
|
||||||
|
perror("listen");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
(void)close(sock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** vhost-user-scsi **/
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(vu_dev);
|
||||||
|
|
||||||
|
for (i = 0; i < VUS_MAX_DEVS; i++) {
|
||||||
|
if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
|
||||||
|
return vhost_scsi_devs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PERR("Unknown VuDev %p", vu_dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
|
||||||
|
{
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi->server_sock >= 0) {
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
socklen_t sslen = sizeof(ss);
|
||||||
|
|
||||||
|
if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
|
||||||
|
&sslen) == 0) {
|
||||||
|
struct sockaddr_un *su = (struct sockaddr_un *)&ss;
|
||||||
|
(void)unlink(su->sun_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)close(vdev_scsi->server_sock);
|
||||||
|
vdev_scsi->server_sock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi->loop) {
|
||||||
|
g_main_loop_unref(vdev_scsi->loop);
|
||||||
|
vdev_scsi->loop = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||||
|
|
||||||
|
assert(unix_fn);
|
||||||
|
|
||||||
|
vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
PERR("calloc: %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->server_sock = unix_sock_new(unix_fn);
|
||||||
|
if (vdev_scsi->server_sock < 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
if (!vdev_scsi->loop) {
|
||||||
|
PERR("Error creating glib event loop");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
|
||||||
|
if (!vdev_scsi->fdmap) {
|
||||||
|
PERR("Error creating glib tree for fdmap");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vdev_scsi;
|
||||||
|
|
||||||
|
err:
|
||||||
|
vdev_scsi_deinit(vdev_scsi);
|
||||||
|
free(vdev_scsi);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
|
||||||
|
char *iscsi_uri, uint32_t lun) {
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(iscsi_uri);
|
||||||
|
assert(lun < VUS_MAX_LUNS);
|
||||||
|
|
||||||
|
if (vdev_scsi->luns[lun].iscsi_ctx) {
|
||||||
|
PERR("Lun %d already configured", lun);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
|
||||||
|
{
|
||||||
|
int cli_sock;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
assert(vdev_scsi);
|
||||||
|
assert(vdev_scsi->server_sock >= 0);
|
||||||
|
assert(vdev_scsi->loop);
|
||||||
|
|
||||||
|
cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
|
||||||
|
if (cli_sock < 0) {
|
||||||
|
perror("accept");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vu_init(&vdev_scsi->vu_dev,
|
||||||
|
cli_sock,
|
||||||
|
vus_panic_cb,
|
||||||
|
vus_add_watch_cb,
|
||||||
|
vus_del_watch_cb,
|
||||||
|
&vus_iface);
|
||||||
|
|
||||||
|
if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
|
||||||
|
&vdev_scsi->vu_dev)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_main_loop_run(vdev_scsi->loop);
|
||||||
|
|
||||||
|
out:
|
||||||
|
vu_deinit(&vdev_scsi->vu_dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
vhost_scsi_dev_t *vdev_scsi = NULL;
|
||||||
|
char *unix_fn = NULL;
|
||||||
|
char *iscsi_uri = NULL;
|
||||||
|
int opt, err = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "u:i:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
goto help;
|
||||||
|
case 'u':
|
||||||
|
unix_fn = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
iscsi_uri = strdup(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!unix_fn || !iscsi_uri) {
|
||||||
|
goto help;
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_scsi = vdev_scsi_new(unix_fn);
|
||||||
|
if (!vdev_scsi) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
vhost_scsi_devs[0] = vdev_scsi;
|
||||||
|
|
||||||
|
if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev_scsi_run(vdev_scsi) != 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (vdev_scsi) {
|
||||||
|
vdev_scsi_deinit(vdev_scsi);
|
||||||
|
free(vdev_scsi);
|
||||||
|
}
|
||||||
|
if (unix_fn) {
|
||||||
|
free(unix_fn);
|
||||||
|
}
|
||||||
|
if (iscsi_uri) {
|
||||||
|
free(iscsi_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err:
|
||||||
|
err = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
help:
|
||||||
|
fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
|
||||||
|
argv[0]);
|
||||||
|
fprintf(stderr, " -u path to unix socket\n");
|
||||||
|
fprintf(stderr, " -i iscsi uri for lun 0\n");
|
||||||
|
fprintf(stderr, " -h print help and quit\n");
|
||||||
|
|
||||||
|
goto err;
|
||||||
|
}
|
11
cpus.c
11
cpus.c
@@ -677,9 +677,9 @@ static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
|
|||||||
sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS);
|
sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS);
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
atomic_set(&cpu->throttle_thread_scheduled, 0);
|
|
||||||
g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */
|
g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
|
atomic_set(&cpu->throttle_thread_scheduled, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cpu_throttle_timer_tick(void *opaque)
|
static void cpu_throttle_timer_tick(void *opaque)
|
||||||
@@ -921,6 +921,15 @@ void cpu_synchronize_all_post_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cpu_synchronize_all_pre_loadvm(void)
|
||||||
|
{
|
||||||
|
CPUState *cpu;
|
||||||
|
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
cpu_synchronize_pre_loadvm(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int do_vm_stop(RunState state)
|
static int do_vm_stop(RunState state)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@@ -15,6 +15,7 @@ CONFIG_TWL92230=y
|
|||||||
CONFIG_TSC2005=y
|
CONFIG_TSC2005=y
|
||||||
CONFIG_LM832X=y
|
CONFIG_LM832X=y
|
||||||
CONFIG_TMP105=y
|
CONFIG_TMP105=y
|
||||||
|
CONFIG_TMP421=y
|
||||||
CONFIG_STELLARIS=y
|
CONFIG_STELLARIS=y
|
||||||
CONFIG_STELLARIS_INPUT=y
|
CONFIG_STELLARIS_INPUT=y
|
||||||
CONFIG_STELLARIS_ENET=y
|
CONFIG_STELLARIS_ENET=y
|
||||||
|
@@ -7,6 +7,7 @@ CONFIG_USB_UHCI=y
|
|||||||
CONFIG_USB_OHCI=y
|
CONFIG_USB_OHCI=y
|
||||||
CONFIG_USB_EHCI=y
|
CONFIG_USB_EHCI=y
|
||||||
CONFIG_USB_XHCI=y
|
CONFIG_USB_XHCI=y
|
||||||
|
CONFIG_USB_XHCI_NEC=y
|
||||||
CONFIG_NE2000_PCI=y
|
CONFIG_NE2000_PCI=y
|
||||||
CONFIG_EEPRO100_PCI=y
|
CONFIG_EEPRO100_PCI=y
|
||||||
CONFIG_PCNET_PCI=y
|
CONFIG_PCNET_PCI=y
|
||||||
@@ -42,3 +43,4 @@ CONFIG_VGA=y
|
|||||||
CONFIG_VGA_PCI=y
|
CONFIG_VGA_PCI=y
|
||||||
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
|
CONFIG_IVSHMEM=$(CONFIG_EVENTFD)
|
||||||
CONFIG_ROCKER=y
|
CONFIG_ROCKER=y
|
||||||
|
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
CONFIG_PCI=y
|
CONFIG_PCI=y
|
||||||
CONFIG_VIRTIO_PCI=y
|
CONFIG_VIRTIO_PCI=y
|
||||||
|
CONFIG_VHOST_USER_SCSI=$(CONFIG_LINUX)
|
||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
CONFIG_TERMINAL3270=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_VFIO_CCW=$(CONFIG_LINUX)
|
||||||
CONFIG_WDT_DIAG288=y
|
CONFIG_WDT_DIAG288=y
|
||||||
|
@@ -6,6 +6,9 @@ libvixl_OBJS = vixl/utils.o \
|
|||||||
|
|
||||||
# The -Wno-sign-compare is needed only for gcc 4.6, which complains about
|
# The -Wno-sign-compare is needed only for gcc 4.6, which complains about
|
||||||
# some signed-unsigned equality comparisons which later gcc versions do not.
|
# some signed-unsigned equality comparisons which later gcc versions do not.
|
||||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) -Wno-sign-compare
|
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CXXFLAGS) -Wno-sign-compare
|
||||||
|
# Ensure that C99 macros are defined regardless of the inclusion order of
|
||||||
|
# headers in vixl. This is required at least on NetBSD.
|
||||||
|
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
|
||||||
|
|
||||||
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
||||||
|
@@ -13,7 +13,7 @@ Let's assume we have a QEMU machine with two NICs (virtio, e1000) and two
|
|||||||
disks (IDE, virtio):
|
disks (IDE, virtio):
|
||||||
|
|
||||||
qemu -drive file=disk1.img,if=none,id=disk1
|
qemu -drive file=disk1.img,if=none,id=disk1
|
||||||
-device ide-drive,drive=disk1,bootindex=4
|
-device ide-hd,drive=disk1,bootindex=4
|
||||||
-drive file=disk2.img,if=none,id=disk2
|
-drive file=disk2.img,if=none,id=disk2
|
||||||
-device virtio-blk-pci,drive=disk2,bootindex=3
|
-device virtio-blk-pci,drive=disk2,bootindex=3
|
||||||
-netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2
|
-netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user