mirror of
git://sourceware.org/git/lvm2.git
synced 2026-01-07 12:32:49 +03:00
Compare commits
825 Commits
dev-agk-tm
...
dev-mcsont
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a297549e16 | ||
|
|
efa31825ba | ||
|
|
50c56ade2a | ||
|
|
61c321d110 | ||
|
|
26f94d9119 | ||
|
|
9e45ad04b0 | ||
|
|
245d7fcd59 | ||
|
|
fa4ce13e3f | ||
|
|
c3aa2988d1 | ||
|
|
7581cc20c9 | ||
|
|
b9926fb1be | ||
|
|
2f615e73de | ||
|
|
0e3e974bff | ||
|
|
bbdcdc12b2 | ||
|
|
e9b2148dab | ||
|
|
34bde8b6c7 | ||
|
|
f90c3d69ce | ||
|
|
67bdae0697 | ||
|
|
50fb24f5c2 | ||
|
|
ffd959d2c0 | ||
|
|
343d80a7fe | ||
|
|
a611f24751 | ||
|
|
4ae481bb30 | ||
|
|
2cd30c0d44 | ||
|
|
010062cf03 | ||
|
|
df00a62fd0 | ||
|
|
796efef15e | ||
|
|
50ab7b3ceb | ||
|
|
719ab4da05 | ||
|
|
2c46c60155 | ||
|
|
4b7bcf3ed4 | ||
|
|
cf426744ae | ||
|
|
f1be01c7ef | ||
|
|
8b741b3bba | ||
|
|
5ee3d6dbdf | ||
|
|
9a8bff003b | ||
|
|
963af243bf | ||
|
|
8be8b994c6 | ||
|
|
20d94e4072 | ||
|
|
bc200e0b16 | ||
|
|
36b8b9ed5c | ||
|
|
cd39b67b7e | ||
|
|
96fd312e85 | ||
|
|
f53cce1804 | ||
|
|
e298a87f71 | ||
|
|
c285314763 | ||
|
|
4e99308b7c | ||
|
|
c37aa7881d | ||
|
|
c65f3fd8df | ||
|
|
c24fa7b7b7 | ||
|
|
3fe2610cff | ||
|
|
889c88e9da | ||
|
|
f50af80199 | ||
|
|
219c4c2ce2 | ||
|
|
45ee63ede9 | ||
|
|
8632aa5fd3 | ||
|
|
a0b30eadd1 | ||
|
|
a087da2f51 | ||
|
|
1f970f590f | ||
|
|
61358d92cb | ||
|
|
5d6bf1efb2 | ||
|
|
2327f3997b | ||
|
|
9fcda0d975 | ||
|
|
f758d16b30 | ||
|
|
444dedea23 | ||
|
|
92f1c888e0 | ||
|
|
a57b92dec3 | ||
|
|
8d3e01ff4f | ||
|
|
0585754593 | ||
|
|
6cad4eba9b | ||
|
|
44b98d77ec | ||
|
|
1349b61bb7 | ||
|
|
2e51535b18 | ||
|
|
2caae8a572 | ||
|
|
2694f93bb4 | ||
|
|
e147786bac | ||
|
|
1c07c54829 | ||
|
|
6b12930860 | ||
|
|
402b41f58d | ||
|
|
560dd9c552 | ||
|
|
4e5761487e | ||
|
|
3954034b15 | ||
|
|
ad86cda4d7 | ||
|
|
36523a398d | ||
|
|
e4f72e6655 | ||
|
|
8bcd482cc5 | ||
|
|
93de9b59c2 | ||
|
|
e03bdd7556 | ||
|
|
c4a6b9ded0 | ||
|
|
4743c4900d | ||
|
|
c6e079cda3 | ||
|
|
f55b8e387f | ||
|
|
53fcd4fd8f | ||
|
|
ba629ceea1 | ||
|
|
e5cdb114a6 | ||
|
|
73d1646a00 | ||
|
|
881f49a767 | ||
|
|
474963573a | ||
|
|
9e438b4bc6 | ||
|
|
fdb014f02f | ||
|
|
4a3e1ac740 | ||
|
|
f39141ebc1 | ||
|
|
5ddd1ead2d | ||
|
|
b387d026bd | ||
|
|
a178ad0858 | ||
|
|
7550665ba4 | ||
|
|
3d980172b0 | ||
|
|
e3c8cebd87 | ||
|
|
2f846697fc | ||
|
|
cff9bff0af | ||
|
|
143aca25da | ||
|
|
1e9c409a3e | ||
|
|
e1cc409ae6 | ||
|
|
721a172edf | ||
|
|
23478d9d21 | ||
|
|
b0e1019add | ||
|
|
9aedf68c32 | ||
|
|
ea036ecfd3 | ||
|
|
1650c10438 | ||
|
|
b13ebfa4c2 | ||
|
|
b62c0787de | ||
|
|
ff2bf11360 | ||
|
|
7232458b6c | ||
|
|
8bea252a63 | ||
|
|
66665f5e42 | ||
|
|
5cf1c61152 | ||
|
|
2cd6cd3439 | ||
|
|
82e7426028 | ||
|
|
862899cc88 | ||
|
|
09aafb61e3 | ||
|
|
3b42cdad0c | ||
|
|
49813abc00 | ||
|
|
788d89c412 | ||
|
|
e0a176f49a | ||
|
|
24186cd9ce | ||
|
|
2806e6265f | ||
|
|
02a5921cdb | ||
|
|
b61c89b52b | ||
|
|
78c937552a | ||
|
|
a311684c4a | ||
|
|
f3a87a2c2e | ||
|
|
1727df7c98 | ||
|
|
29ae8b8ca5 | ||
|
|
e80ca65d30 | ||
|
|
d6bce03615 | ||
|
|
8623e33651 | ||
|
|
c31e6b0aca | ||
|
|
cb7766bc14 | ||
|
|
1308572a87 | ||
|
|
adf9bf80a3 | ||
|
|
f633d68c54 | ||
|
|
c733e96445 | ||
|
|
3669c33af4 | ||
|
|
e2ae1f2d71 | ||
|
|
3aeff09bd7 | ||
|
|
d5a5792b30 | ||
|
|
a397b69ce3 | ||
|
|
a8921be641 | ||
|
|
9d7afaaab8 | ||
|
|
71b9cb8e0f | ||
|
|
057d866889 | ||
|
|
e79e092f8b | ||
|
|
4729b4af0b | ||
|
|
f3be66c002 | ||
|
|
2047d405af | ||
|
|
1370277ea7 | ||
|
|
f38cfd09c4 | ||
|
|
515867bbad | ||
|
|
bcf1aa99e6 | ||
|
|
b41b112a4b | ||
|
|
559cf0cd1e | ||
|
|
228e331e27 | ||
|
|
3ead62e24b | ||
|
|
fa025cdd9a | ||
|
|
f5d1f4f086 | ||
|
|
699bf86090 | ||
|
|
03d6cfdd99 | ||
|
|
6064b9f1b2 | ||
|
|
6d4f36c2c7 | ||
|
|
63289b54c7 | ||
|
|
0a726a7e26 | ||
|
|
b79f1e176f | ||
|
|
81d954df4e | ||
|
|
7cbee7e9cf | ||
|
|
717957ddc5 | ||
|
|
9b04851fc5 | ||
|
|
dcf8f3111a | ||
|
|
ece0b131e5 | ||
|
|
519f4453a5 | ||
|
|
bc6ae7030a | ||
|
|
167aa34926 | ||
|
|
8fc64c5ee6 | ||
|
|
8d44cd3e47 | ||
|
|
40f57155a3 | ||
|
|
1bef4dfab3 | ||
|
|
e974f6866a | ||
|
|
a93699ece9 | ||
|
|
e4bb94a93e | ||
|
|
93ac80037a | ||
|
|
c9e5e6800c | ||
|
|
f0f68791f3 | ||
|
|
b39c26ddc3 | ||
|
|
d1ae1455b4 | ||
|
|
c115d92287 | ||
|
|
ece117ee10 | ||
|
|
590a1ebcf7 | ||
|
|
863a2e693e | ||
|
|
8dbfdb5b73 | ||
|
|
675b94a11b | ||
|
|
850e95f24a | ||
|
|
083f162e8e | ||
|
|
7f56908c2b | ||
|
|
427e8ba3e3 | ||
|
|
6a5575e959 | ||
|
|
57cde6063f | ||
|
|
d0cb672466 | ||
|
|
75886f59e4 | ||
|
|
1d2de5dd13 | ||
|
|
df0797db8c | ||
|
|
2d077286b9 | ||
|
|
f5ea02ffee | ||
|
|
262a42025f | ||
|
|
d5234e1b7e | ||
|
|
a188b1e513 | ||
|
|
9764ee0b3f | ||
|
|
322d4ed05e | ||
|
|
a01e1fec0f | ||
|
|
0e42ebd6d4 | ||
|
|
fe1cabfa34 | ||
|
|
cb5405ded8 | ||
|
|
f8ce9bf3bc | ||
|
|
9fda169077 | ||
|
|
9799c8da07 | ||
|
|
613466aa8f | ||
|
|
813a83b2d6 | ||
|
|
fa8d5e4e81 | ||
|
|
b93aded021 | ||
|
|
efa281685a | ||
|
|
ab27d5dc2a | ||
|
|
bd872064a2 | ||
|
|
d1b652143a | ||
|
|
e7bb508809 | ||
|
|
de2863739f | ||
|
|
c26bde42af | ||
|
|
0e03c68619 | ||
|
|
3374a59250 | ||
|
|
6afb911252 | ||
|
|
a8d59404f7 | ||
|
|
a1a89a453f | ||
|
|
ed749cdb5b | ||
|
|
5502f72e41 | ||
|
|
c527a0cbfc | ||
|
|
63d4983890 | ||
|
|
a991664dec | ||
|
|
ab1aa0a4fb | ||
|
|
d910f75d89 | ||
|
|
94362423c4 | ||
|
|
acf40f5587 | ||
|
|
227a0d7336 | ||
|
|
a41968c4b4 | ||
|
|
672b8c196b | ||
|
|
cc96eea029 | ||
|
|
5f648406b0 | ||
|
|
3ebc745f53 | ||
|
|
acd2c6f256 | ||
|
|
b10b462fde | ||
|
|
a75eb8d74c | ||
|
|
0569add94c | ||
|
|
12dfd0ed02 | ||
|
|
ad10d42671 | ||
|
|
f7645995da | ||
|
|
4ed9b07380 | ||
|
|
0174ba692c | ||
|
|
48594d007a | ||
|
|
50a603de6f | ||
|
|
e4fe0d1b8f | ||
|
|
951676a59e | ||
|
|
4456d9aa77 | ||
|
|
b394a9f63f | ||
|
|
9e296c9c6f | ||
|
|
5b87f5fb72 | ||
|
|
bb384f8488 | ||
|
|
82feb5f111 | ||
|
|
66990bc7c8 | ||
|
|
6fcb2ba440 | ||
|
|
b8a7f6ba3d | ||
|
|
0851ee5301 | ||
|
|
df8eef7096 | ||
|
|
c1dbb22ba4 | ||
|
|
99cddd67a9 | ||
|
|
814dd84e07 | ||
|
|
d5bcc56eef | ||
|
|
f7ffba204e | ||
|
|
90e419c645 | ||
|
|
49147cbaa7 | ||
|
|
69907e0780 | ||
|
|
b90d4b38e5 | ||
|
|
befdfc245b | ||
|
|
0d78e4c1e9 | ||
|
|
763c65314e | ||
|
|
24aee732a5 | ||
|
|
ba6ed5c90c | ||
|
|
e0c94d883a | ||
|
|
39e3b5d8ac | ||
|
|
39fc98d731 | ||
|
|
5503699c37 | ||
|
|
e0bfc946cb | ||
|
|
9546edeef9 | ||
|
|
716199334c | ||
|
|
4479228d32 | ||
|
|
4afb5971b9 | ||
|
|
dd075e93c1 | ||
|
|
d4fd39f64c | ||
|
|
acb784e2a8 | ||
|
|
8a0af1bec8 | ||
|
|
8bd9a89c14 | ||
|
|
a30e622279 | ||
|
|
76075ff55d | ||
|
|
bfb904af1c | ||
|
|
d88376ca78 | ||
|
|
6283f5ea3f | ||
|
|
43ce357ebc | ||
|
|
d136790bab | ||
|
|
214de62b5d | ||
|
|
e9c0a64fb5 | ||
|
|
7ac8e21f3c | ||
|
|
fdb362b998 | ||
|
|
06accf1395 | ||
|
|
d3dcca639c | ||
|
|
98eb9e5754 | ||
|
|
347c807f86 | ||
|
|
1e5f6887b1 | ||
|
|
81f07c3cca | ||
|
|
885eb2024f | ||
|
|
00f6a8466e | ||
|
|
06b2e5c176 | ||
|
|
caa600a409 | ||
|
|
b9c1cef817 | ||
|
|
4a01e4f389 | ||
|
|
08771bbbbf | ||
|
|
8d9d32b315 | ||
|
|
fdaa7e2e87 | ||
|
|
2beb3009bd | ||
|
|
214235367b | ||
|
|
c516321325 | ||
|
|
27495a3555 | ||
|
|
05ee83579b | ||
|
|
6cd0523337 | ||
|
|
063d065388 | ||
|
|
abba06fb3b | ||
|
|
3759a1f62b | ||
|
|
5c5e449dc5 | ||
|
|
a40d447a02 | ||
|
|
95cf127134 | ||
|
|
595196bc29 | ||
|
|
403c87c1aa | ||
|
|
948f2d9979 | ||
|
|
db8d3bdfa9 | ||
|
|
3a4fe54ca1 | ||
|
|
7f7ec769d9 | ||
|
|
cd369d8a7f | ||
|
|
0c1d3db8db | ||
|
|
6a44dceb48 | ||
|
|
5ac9f8d631 | ||
|
|
6d14d5d16b | ||
|
|
06c789eda1 | ||
|
|
1924426ad1 | ||
|
|
c2a8bbed3b | ||
|
|
9b41efae82 | ||
|
|
0181c77e3f | ||
|
|
033df741e2 | ||
|
|
28c8e95d19 | ||
|
|
9a730233c9 | ||
|
|
0ecf232194 | ||
|
|
3702f39ef3 | ||
|
|
d6f2445996 | ||
|
|
264077907e | ||
|
|
adae8ee1c2 | ||
|
|
7e85361c34 | ||
|
|
fab063cfcb | ||
|
|
9337ff48bc | ||
|
|
a90de76fd8 | ||
|
|
f865e1bf87 | ||
|
|
89f34eaf0c | ||
|
|
76a45424a7 | ||
|
|
c46dbfb14e | ||
|
|
4be1ec3da4 | ||
|
|
c35d3242a8 | ||
|
|
6cd798f556 | ||
|
|
b7fd8ac8eb | ||
|
|
87291a2832 | ||
|
|
61583281e5 | ||
|
|
a60416a13f | ||
|
|
3c9ed33f83 | ||
|
|
73ae68e1c4 | ||
|
|
6029d6d8d8 | ||
|
|
25a66737e3 | ||
|
|
a9f2c1e1f5 | ||
|
|
bc275bcddf | ||
|
|
0253f5a21d | ||
|
|
b2574c2f3a | ||
|
|
3bbdde808a | ||
|
|
fbf64fe730 | ||
|
|
43fb32e761 | ||
|
|
5b86b0e3dc | ||
|
|
f7435cd8c7 | ||
|
|
286c9c78b4 | ||
|
|
a39eaea27d | ||
|
|
5052970da3 | ||
|
|
7ee0a6e44d | ||
|
|
3417d6229d | ||
|
|
c6ca81a38d | ||
|
|
8c453e2e5e | ||
|
|
28d35e5c59 | ||
|
|
64dd656ef7 | ||
|
|
7c852c75c3 | ||
|
|
e296f784c9 | ||
|
|
df2acbbb97 | ||
|
|
ed799404f8 | ||
|
|
3bbc17a670 | ||
|
|
fb0aca86f8 | ||
|
|
99cd7108d3 | ||
|
|
f8745dc23e | ||
|
|
550380c1a4 | ||
|
|
3b3ee66b1f | ||
|
|
b5da4fdfce | ||
|
|
be154e30e8 | ||
|
|
ad756bb708 | ||
|
|
c1abcee142 | ||
|
|
889558fedb | ||
|
|
d25c135806 | ||
|
|
0217c53b24 | ||
|
|
2eba7c7755 | ||
|
|
11ceb77867 | ||
|
|
517d6cc418 | ||
|
|
0e56fa6892 | ||
|
|
0a5edc1f12 | ||
|
|
9640320aea | ||
|
|
ca87674ea4 | ||
|
|
edede1d20f | ||
|
|
093428b067 | ||
|
|
7b8b13c62b | ||
|
|
35ffc3f8eb | ||
|
|
67c02877a1 | ||
|
|
4c7565b65d | ||
|
|
fa8d0b5766 | ||
|
|
79b2961399 | ||
|
|
e2c766d37e | ||
|
|
ac768a9d2b | ||
|
|
83e362cd32 | ||
|
|
0b465d1543 | ||
|
|
d38a2d64f0 | ||
|
|
7616a7f46e | ||
|
|
cbe81a0b05 | ||
|
|
0221ebfd64 | ||
|
|
a7a23e7dd2 | ||
|
|
38b4354494 | ||
|
|
ec0f5c2bf6 | ||
|
|
86c8f0f01f | ||
|
|
7362ed68be | ||
|
|
f5da325d70 | ||
|
|
172d8fb355 | ||
|
|
0cadfdd69d | ||
|
|
842b3074b7 | ||
|
|
6f48741062 | ||
|
|
e2be14e2d5 | ||
|
|
6740c78e83 | ||
|
|
09fcc8eaa8 | ||
|
|
73578e36fa | ||
|
|
3e3cb22f2a | ||
|
|
5c9dcd99fd | ||
|
|
b5d9914628 | ||
|
|
ccab54677c | ||
|
|
bbb8040456 | ||
|
|
4362013872 | ||
|
|
228ed56455 | ||
|
|
413488edc6 | ||
|
|
30a4c7988e | ||
|
|
0a31fb4aa3 | ||
|
|
576dd1fc41 | ||
|
|
3b02b35c3e | ||
|
|
5f780813f2 | ||
|
|
9ad42e5f06 | ||
|
|
d974644db7 | ||
|
|
57bb46c5e7 | ||
|
|
39ce38eb88 | ||
|
|
3c0f5bdd08 | ||
|
|
ae50374811 | ||
|
|
67b80e2d9d | ||
|
|
2b96bb403c | ||
|
|
2ae4a04710 | ||
|
|
e649f71022 | ||
|
|
38f33251b1 | ||
|
|
9a5bd01b0c | ||
|
|
3600caa71d | ||
|
|
1c5c99afce | ||
|
|
2e1869b923 | ||
|
|
a2310e2de0 | ||
|
|
c9729022bf | ||
|
|
8bf92875f7 | ||
|
|
d2840b0ec1 | ||
|
|
bc50dc6e70 | ||
|
|
ed837e6971 | ||
|
|
f4a60fe004 | ||
|
|
822a8b62be | ||
|
|
c016b573ee | ||
|
|
a5e13f2eef | ||
|
|
88fe07ad0a | ||
|
|
49db9b5e0b | ||
|
|
ac18164a52 | ||
|
|
4ebfd8e8eb | ||
|
|
b393fbec00 | ||
|
|
2bb02e24bf | ||
|
|
52656c89fd | ||
|
|
9cab005797 | ||
|
|
dfc320f5b8 | ||
|
|
2688aafefb | ||
|
|
8b755f1e04 | ||
|
|
dc30d4b2f2 | ||
|
|
efad84ebc2 | ||
|
|
b3c41bce3d | ||
|
|
65912ce44d | ||
|
|
977d0a3613 | ||
|
|
90d0ff6636 | ||
|
|
8fd300f7df | ||
|
|
972b535220 | ||
|
|
9fe0be871c | ||
|
|
506ab29bfd | ||
|
|
6abc3f10ae | ||
|
|
11d9b0cae7 | ||
|
|
11436b00e0 | ||
|
|
24e7745d7a | ||
|
|
db0560c1b0 | ||
|
|
1553993ea1 | ||
|
|
39f05855c0 | ||
|
|
d709d8445f | ||
|
|
9687ee2a74 | ||
|
|
8dcc973bbb | ||
|
|
a418f88b76 | ||
|
|
3ea862bdfc | ||
|
|
bfc61a9543 | ||
|
|
de042fa13d | ||
|
|
61153d90e5 | ||
|
|
f564e78d98 | ||
|
|
c863c9581d | ||
|
|
7aba7fe68b | ||
|
|
f6459757af | ||
|
|
c1cd18f21e | ||
|
|
029a76b4f8 | ||
|
|
c365d7de4f | ||
|
|
89935ace29 | ||
|
|
39f24a169c | ||
|
|
ef79d639fe | ||
|
|
cca815d240 | ||
|
|
1b08797419 | ||
|
|
52ebad31ba | ||
|
|
1ddbbb67e0 | ||
|
|
bdf7479449 | ||
|
|
9384b2b5c5 | ||
|
|
2bc896f2a3 | ||
|
|
545ca59468 | ||
|
|
0a2b5d5748 | ||
|
|
65d6118e47 | ||
|
|
513e9e3264 | ||
|
|
475626fb6c | ||
|
|
865a9c5873 | ||
|
|
b904d6653d | ||
|
|
fade45b1d1 | ||
|
|
dd7ac793a0 | ||
|
|
877c2f2ffb | ||
|
|
0931067dc5 | ||
|
|
138225a3a8 | ||
|
|
ab63923d19 | ||
|
|
cdcea0bf55 | ||
|
|
5c878167a2 | ||
|
|
f8f6219513 | ||
|
|
54856b2965 | ||
|
|
e890c37704 | ||
|
|
8a14b8a733 | ||
|
|
5b6e62dc1f | ||
|
|
cdb8400de2 | ||
|
|
1c97fda425 | ||
|
|
ea34dad66f | ||
|
|
c7fdacbc50 | ||
|
|
0fe4f65f65 | ||
|
|
4670e9f698 | ||
|
|
47bfac21ca | ||
|
|
1fec86571f | ||
|
|
f8616ac2d8 | ||
|
|
28a9fcd94b | ||
|
|
dcb5434a7f | ||
|
|
fc3ed8856f | ||
|
|
c492fbb51c | ||
|
|
66f4f8c27f | ||
|
|
ae27461777 | ||
|
|
fcdac700f9 | ||
|
|
f2504257e4 | ||
|
|
1409c4a1c2 | ||
|
|
c42a18d372 | ||
|
|
aee27dc7ba | ||
|
|
7b0a8f47be | ||
|
|
aa833bdd8a | ||
|
|
9b6a62f944 | ||
|
|
c0973e70a5 | ||
|
|
89c65d4f71 | ||
|
|
45e5e702c1 | ||
|
|
6d05859862 | ||
|
|
ae21305ee7 | ||
|
|
a01a8d7172 | ||
|
|
a9b0aa5c17 | ||
|
|
e351f8bc66 | ||
|
|
7e33bd1335 | ||
|
|
ddb5de7a98 | ||
|
|
196579af1f | ||
|
|
44726ed9cb | ||
|
|
1717d4cb17 | ||
|
|
570c6239ee | ||
|
|
217f3f8741 | ||
|
|
da2b155a9d | ||
|
|
4331182964 | ||
|
|
21057676a1 | ||
|
|
e49b114f7e | ||
|
|
8065492046 | ||
|
|
8b26a007b1 | ||
|
|
0da296003d | ||
|
|
34fd818caf | ||
|
|
c2b10daf69 | ||
|
|
e7670d3338 | ||
|
|
b504bb809e | ||
|
|
ae093df3f1 | ||
|
|
d75aa55784 | ||
|
|
96a61337b0 | ||
|
|
28255e3eee | ||
|
|
f328532f05 | ||
|
|
7bce66c5e8 | ||
|
|
6e580465b5 | ||
|
|
37471bb477 | ||
|
|
e4f478d86d | ||
|
|
89f54a5094 | ||
|
|
c29899b910 | ||
|
|
a1e3398ffc | ||
|
|
9d2add1361 | ||
|
|
6c67c7557c | ||
|
|
4343280ebc | ||
|
|
f17c2cf7c6 | ||
|
|
29c6c17121 | ||
|
|
d9a77e8bb4 | ||
|
|
79c4971210 | ||
|
|
5f138f3604 | ||
|
|
e3e5beec74 | ||
|
|
9c71fa0214 | ||
|
|
098c843c50 | ||
|
|
d9ef9eb330 | ||
|
|
748f29b42a | ||
|
|
4507ba3596 | ||
|
|
a7cb76ae94 | ||
|
|
697fa7aa1d | ||
|
|
93fc937429 | ||
|
|
7be54bd687 | ||
|
|
d9e6298edb | ||
|
|
dc8034f5eb | ||
|
|
1cde30eba0 | ||
|
|
6a57ed17a2 | ||
|
|
467adfa082 | ||
|
|
8ae3b244fc | ||
|
|
b03e55a513 | ||
|
|
0d0fab3d2d | ||
|
|
19647d1cd4 | ||
|
|
1563b93691 | ||
|
|
c4c4acfd42 | ||
|
|
0f0eb04edb | ||
|
|
46867a45d2 | ||
|
|
cb2c4542a6 | ||
|
|
38d77898ae | ||
|
|
7a475bef32 | ||
|
|
da7e13ef88 | ||
|
|
acb42ec465 | ||
|
|
00f1b208a1 | ||
|
|
d51429254f | ||
|
|
ac18005de9 | ||
|
|
fa5ba7e42d | ||
|
|
037c234eaa | ||
|
|
73cda0437f | ||
|
|
9731d48691 | ||
|
|
d437bd86ff | ||
|
|
7323557379 | ||
|
|
e878c3fc32 | ||
|
|
27a1a0e5c0 | ||
|
|
1287edf626 | ||
|
|
d81e3f9b06 | ||
|
|
05f954ee9b | ||
|
|
79d214032b | ||
|
|
1693fef529 | ||
|
|
55d83f9f6e | ||
|
|
66400d003d | ||
|
|
a19456b868 | ||
|
|
91965af9b1 | ||
|
|
73189170f5 | ||
|
|
ff3ffe30e4 | ||
|
|
9068de011d | ||
|
|
a7d077b89b | ||
|
|
ace97c9f9c | ||
|
|
7a7b8a7778 | ||
|
|
99bfbbf229 | ||
|
|
bc286910ec | ||
|
|
3a48fb47b7 | ||
|
|
1507956383 | ||
|
|
397b7891ff | ||
|
|
410c992744 | ||
|
|
14abe1e87b | ||
|
|
cafcc5813a | ||
|
|
fe69731d31 | ||
|
|
30975a3328 | ||
|
|
8c02cc9e8f | ||
|
|
4e0c0417ce | ||
|
|
8d7ece126b | ||
|
|
08487a3098 | ||
|
|
e5b40e0488 | ||
|
|
9e7b00a3b9 | ||
|
|
c82ab92d04 | ||
|
|
5c40e81a7e | ||
|
|
f4383a70ba | ||
|
|
aa75e181be | ||
|
|
b4c69320fc | ||
|
|
f2d0eefa77 | ||
|
|
26c58027fb | ||
|
|
f331eb1c0d | ||
|
|
fd6661dfcf | ||
|
|
d727382275 | ||
|
|
67fbe980a7 | ||
|
|
689af32313 | ||
|
|
d68d71013f | ||
|
|
9553dc7761 | ||
|
|
f6f8f0c7fd | ||
|
|
bed869a8a0 | ||
|
|
750fc2e876 | ||
|
|
285413b502 | ||
|
|
d794444715 | ||
|
|
6365f011b0 | ||
|
|
043f58452a | ||
|
|
a082ce2613 | ||
|
|
4c925692f5 | ||
|
|
70ad633638 | ||
|
|
2b3b486a37 | ||
|
|
eae54b67d8 | ||
|
|
90512910e5 | ||
|
|
b1ace8ce19 | ||
|
|
e9cadbe105 | ||
|
|
49a8c786d5 | ||
|
|
06c1f71897 | ||
|
|
3f351466f7 | ||
|
|
7ac7cc0ac8 | ||
|
|
9476cf8cdc | ||
|
|
5f5db7cf41 | ||
|
|
f203d4e206 | ||
|
|
3b7834af17 | ||
|
|
29b2cfba06 | ||
|
|
1bd57b4c1d | ||
|
|
e095586d9e | ||
|
|
0edd89fadc | ||
|
|
a8a579b154 | ||
|
|
0646fd465e | ||
|
|
dd88a0f05c | ||
|
|
6cb2c35d16 | ||
|
|
ee37838b11 | ||
|
|
7421252edc | ||
|
|
a6fdb9d9d7 | ||
|
|
15b6793528 | ||
|
|
b05caca77e | ||
|
|
eb3597acb3 | ||
|
|
112846ce0b | ||
|
|
6134a71a90 | ||
|
|
f92b6f9930 | ||
|
|
73e93ef5e5 | ||
|
|
ca9cbd92c4 | ||
|
|
6481471c9d | ||
|
|
b6e7a0b490 | ||
|
|
f04abd1f8a | ||
|
|
23de09aeb8 | ||
|
|
b2f1254c14 | ||
|
|
ce199db848 | ||
|
|
9be086fbee | ||
|
|
406d6de651 | ||
|
|
16c209c613 | ||
|
|
e643de6e61 | ||
|
|
805bf6ec74 | ||
|
|
6ba94fdd81 | ||
|
|
cc4855acbe | ||
|
|
052f28746d | ||
|
|
b09ea3b6f7 | ||
|
|
749372caf3 | ||
|
|
bc1adc32cb | ||
|
|
6b48868cf0 | ||
|
|
261e6c3df6 | ||
|
|
9bfc8881cb | ||
|
|
32bcdd90ae | ||
|
|
8e5305f630 | ||
|
|
e7f1329cae | ||
|
|
c3bb2b29d4 | ||
|
|
e87fa7c9ce | ||
|
|
1671b83585 | ||
|
|
f5401fbd34 | ||
|
|
552e60b3a1 | ||
|
|
a2d2fe3a8c | ||
|
|
a1195aaa66 | ||
|
|
d67f160200 | ||
|
|
dd6fbcbb69 | ||
|
|
c3642957c5 | ||
|
|
0eb9daf602 | ||
|
|
32febed8d5 | ||
|
|
e40768ac32 | ||
|
|
27399755fd | ||
|
|
e113df129e | ||
|
|
6dff5dc653 | ||
|
|
d90a647802 | ||
|
|
12fba201be | ||
|
|
4f278324c7 | ||
|
|
7239a45b79 | ||
|
|
d94036f8ed | ||
|
|
60b61f2db3 | ||
|
|
afdbb28f72 | ||
|
|
34a9e3d3cd | ||
|
|
7cfe5ab9bc | ||
|
|
83258e3385 | ||
|
|
1b6d0346a3 |
101
.gitignore
vendored
101
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
*.5
|
||||
*.7
|
||||
*.8
|
||||
*.8_gen
|
||||
*.a
|
||||
*.d
|
||||
*.o
|
||||
@@ -30,3 +31,103 @@ make.tmpl
|
||||
/cscope.out
|
||||
/tags
|
||||
/tmp/
|
||||
|
||||
|
||||
tools/man-generator
|
||||
tools/man-generator.c
|
||||
|
||||
test/lib/lvchange
|
||||
test/lib/lvconvert
|
||||
test/lib/lvcreate
|
||||
test/lib/lvdisplay
|
||||
test/lib/lvextend
|
||||
test/lib/lvmconfig
|
||||
test/lib/lvmdiskscan
|
||||
test/lib/lvmsadc
|
||||
test/lib/lvmsar
|
||||
test/lib/lvreduce
|
||||
test/lib/lvremove
|
||||
test/lib/lvrename
|
||||
test/lib/lvresize
|
||||
test/lib/lvs
|
||||
test/lib/lvscan
|
||||
test/lib/pvchange
|
||||
test/lib/pvck
|
||||
test/lib/pvcreate
|
||||
test/lib/pvdisplay
|
||||
test/lib/pvmove
|
||||
test/lib/pvremove
|
||||
test/lib/pvresize
|
||||
test/lib/pvs
|
||||
test/lib/pvscan
|
||||
test/lib/vgcfgbackup
|
||||
test/lib/vgcfgrestore
|
||||
test/lib/vgchange
|
||||
test/lib/vgck
|
||||
test/lib/vgconvert
|
||||
test/lib/vgcreate
|
||||
test/lib/vgdisplay
|
||||
test/lib/vgexport
|
||||
test/lib/vgextend
|
||||
test/lib/vgimport
|
||||
test/lib/vgimportclone
|
||||
test/lib/vgmerge
|
||||
test/lib/vgmknodes
|
||||
test/lib/vgreduce
|
||||
test/lib/vgremove
|
||||
test/lib/vgrename
|
||||
test/lib/vgs
|
||||
test/lib/vgscan
|
||||
test/lib/vgsplit
|
||||
test/api/lvtest.t
|
||||
test/api/pe_start.t
|
||||
test/api/percent.t
|
||||
test/api/python_lvm_unit.py
|
||||
test/api/test
|
||||
test/api/thin_percent.t
|
||||
test/api/vglist.t
|
||||
test/api/vgtest.t
|
||||
test/lib/aux
|
||||
test/lib/check
|
||||
test/lib/clvmd
|
||||
test/lib/dm-version-expected
|
||||
test/lib/dmeventd
|
||||
test/lib/dmsetup
|
||||
test/lib/dmstats
|
||||
test/lib/fail
|
||||
test/lib/flavour-ndev-cluster
|
||||
test/lib/flavour-ndev-cluster-lvmpolld
|
||||
test/lib/flavour-ndev-lvmetad
|
||||
test/lib/flavour-ndev-lvmetad-lvmpolld
|
||||
test/lib/flavour-ndev-lvmpolld
|
||||
test/lib/flavour-ndev-vanilla
|
||||
test/lib/flavour-udev-cluster
|
||||
test/lib/flavour-udev-cluster-lvmpolld
|
||||
test/lib/flavour-udev-lvmetad
|
||||
test/lib/flavour-udev-lvmetad-lvmpolld
|
||||
test/lib/flavour-udev-lvmlockd-dlm
|
||||
test/lib/flavour-udev-lvmlockd-sanlock
|
||||
test/lib/flavour-udev-lvmlockd-test
|
||||
test/lib/flavour-udev-lvmpolld
|
||||
test/lib/flavour-udev-vanilla
|
||||
test/lib/fsadm
|
||||
test/lib/get
|
||||
test/lib/inittest
|
||||
test/lib/invalid
|
||||
test/lib/lvm
|
||||
test/lib/lvm-wrapper
|
||||
test/lib/lvmchange
|
||||
test/lib/lvmdbusd.profile
|
||||
test/lib/lvmetad
|
||||
test/lib/lvmpolld
|
||||
test/lib/not
|
||||
test/lib/paths
|
||||
test/lib/paths-common
|
||||
test/lib/runner
|
||||
test/lib/should
|
||||
test/lib/test
|
||||
test/lib/thin-performance.profile
|
||||
test/lib/utils
|
||||
test/lib/version-expected
|
||||
test/unit/dmraid_t.c
|
||||
test/unit/unit-test
|
||||
|
||||
25
COPYING.BSD
Normal file
25
COPYING.BSD
Normal file
@@ -0,0 +1,25 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2014, Red Hat, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
46
Makefile.in
46
Makefile.in
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -18,7 +18,7 @@ top_builddir = @top_builddir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
|
||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
|
||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts device_mapper tools
|
||||
|
||||
ifeq ("@UDEV_RULES@", "yes")
|
||||
SUBDIRS += udev
|
||||
@@ -43,8 +43,7 @@ endif
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = conf include man test scripts \
|
||||
libdaemon lib tools daemons libdm \
|
||||
udev po liblvm python \
|
||||
unit-tests/datastruct unit-tests/mm unit-tests/regex
|
||||
udev po liblvm python device_mapper
|
||||
tools.distclean: test.distclean
|
||||
endif
|
||||
DISTCLEAN_DIRS += lcov_reports*
|
||||
@@ -62,6 +61,9 @@ po: tools daemons
|
||||
man: tools
|
||||
all_man: tools
|
||||
scripts: liblvm libdm
|
||||
test: tools daemons
|
||||
unit-test: lib
|
||||
run-unit-test: unit-test
|
||||
|
||||
lib.device-mapper: include.device-mapper
|
||||
libdm.device-mapper: include.device-mapper
|
||||
@@ -97,7 +99,7 @@ endif
|
||||
DISTCLEAN_TARGETS += cscope.out
|
||||
CLEAN_DIRS += autom4te.cache
|
||||
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit-test run-unit-test: test
|
||||
$(MAKE) -C test $(@)
|
||||
|
||||
conf.generate man.generate: tools
|
||||
@@ -122,11 +124,11 @@ rpm: dist
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES
|
||||
DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\
|
||||
GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\
|
||||
DM_VER=$$(cut -d' ' -f1 $(top_srcdir)/VERSION_DM | cut -d- -f1);\
|
||||
GIT_VER=$$(cd $(top_srcdir); git describe | cut -s -d- --output-delimiter=. -f2,3);\
|
||||
sed -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \
|
||||
-e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \
|
||||
-e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \
|
||||
-e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $${GIT_VER:-"0"}," \
|
||||
$(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
|
||||
rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
||||
|
||||
@@ -146,7 +148,7 @@ install_system_dirs:
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
|
||||
$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
|
||||
|
||||
install_initscripts:
|
||||
install_initscripts:
|
||||
$(MAKE) -C scripts install_initscripts
|
||||
|
||||
install_systemd_generators:
|
||||
@@ -169,6 +171,7 @@ install_tmpfiles_configuration:
|
||||
|
||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
||||
libdaemon/client.info libdaemon/server.info \
|
||||
test/unit.info \
|
||||
daemons/clvmd.info \
|
||||
daemons/dmeventd.info \
|
||||
daemons/lvmetad.info \
|
||||
@@ -211,31 +214,6 @@ endif
|
||||
|
||||
endif
|
||||
|
||||
ifeq ("$(TESTING)", "yes")
|
||||
# testing and report generation
|
||||
RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
|
||||
|
||||
.PHONY: unit-test ruby-test test-programs
|
||||
|
||||
# FIXME: put dependencies on libdm and liblvm
|
||||
# FIXME: Should be handled by Makefiles in subdirs, not here at top level.
|
||||
test-programs:
|
||||
cd unit-tests/regex && $(MAKE)
|
||||
cd unit-tests/datastruct && $(MAKE)
|
||||
cd unit-tests/mm && $(MAKE)
|
||||
|
||||
unit-test: test-programs
|
||||
$(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
|
||||
$(RUBY) report-generators/title_page.rb
|
||||
|
||||
memcheck: test-programs
|
||||
$(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
|
||||
$(RUBY) report-generators/title_page.rb
|
||||
|
||||
ruby-test:
|
||||
$(RUBY) report-generators/test/ts.rb
|
||||
endif
|
||||
|
||||
ifneq ($(shell which ctags),)
|
||||
.PHONY: tags
|
||||
tags:
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.02.147-git (2017-12-18)
|
||||
1.02.166-git (2019-08-27)
|
||||
|
||||
172
WHATS_NEW
172
WHATS_NEW
@@ -1,18 +1,174 @@
|
||||
Version 2.02.178 -
|
||||
Version 2.02.187 -
|
||||
===================================
|
||||
Ensure minimum required region size on striped RaidLV creation.
|
||||
Fix resize of thin-pool with data and metadata of different segtype.
|
||||
Fix splitting mirror leg in cluster.
|
||||
Fix activation order when removing merged snapshot.
|
||||
Add support for DM_DEVICE_GET_TARGET_VERSION into device_mapper.
|
||||
Add lvextend-raid.sh to check on RaidLV extensions synchronization.
|
||||
Fix lvmetad shutdown and avoid lenghty timeouts when rebooting system.
|
||||
Prevent creating VGs with PVs with different logical block sizes.
|
||||
Pvmove runs in exlusively activating mode for exclusively active LVs.
|
||||
Activate thin-pool layered volume as 'read-only' device.
|
||||
Ignore crypto devices with UUID signature CRYPT-SUBDEV.
|
||||
Enhance validation for thin and cache pool conversion and swapping.
|
||||
Fixed activation on boot - lvm2 no longer activates incomplete VGs.
|
||||
|
||||
Version 2.02.186 - 27th August 2019
|
||||
===================================
|
||||
Improve internal removal of cached devices.
|
||||
Synchronize with udev when dropping snapshot.
|
||||
Add missing device synchronization point before removing pvmove node.
|
||||
Correctly set read_ahead for LVs when pvmove is finished.
|
||||
Fix metadata writes from corrupting with large physical block size.
|
||||
Report no_discard_passdown for cache LVs with lvs -o+kernel_discards.
|
||||
Prevent shared active mirror LVs with lvmlockd.
|
||||
|
||||
Version 2.02.185 - 13th May 2019
|
||||
================================
|
||||
Fix change of monitoring in clustered volumes.
|
||||
Improve -lXXX%VG modifier which improves cache segment estimation.
|
||||
Add synchronization with udev before removing cached devices.
|
||||
Fix missing growth of _pmspare volume when extending _tmeta volume.
|
||||
Automatically grow thin metadata, when thin data gets too big.
|
||||
Add support for vgsplit with cached devices.
|
||||
Fix signal delivery checking race in libdaemon (lvmetad).
|
||||
Add missing Before=shutdown.target to LVM2 services to fix shutdown ordering.
|
||||
|
||||
Version 2.02.184 - 22nd March 2019
|
||||
==================================
|
||||
Fix (de)activation of RaidLVs with visible SubLVs
|
||||
Change scan_lvs default to 0 so LVs are not scanned for PVs.
|
||||
Add scan_lvs config setting to control if lvm scans LVs for PVs.
|
||||
Fix missing proper initialization of pv_list struct when adding pv.
|
||||
|
||||
Version 2.02.183 - 07th December 2018
|
||||
=====================================
|
||||
Add devices/use_aio, aio_max, aio_memory to configure AIO limits.
|
||||
Support asynchronous I/O when scanning devices.
|
||||
Detect asynchronous I/O capability in configure or accept --disable-aio.
|
||||
Add AIO_SUPPORTED_CODE_PATH to indicate whether AIO may be used.
|
||||
Avoid disabling lvmetad when repair does nothing.
|
||||
Fix component detection for md version 0.90.
|
||||
Use sync io if async io_setup fails, or use_aio=0 is set in config.
|
||||
Avoid opening devices to get block size by using existing open fd.
|
||||
|
||||
Version 2.02.182 - 30th October 2018
|
||||
====================================
|
||||
Fix possible write race between last metadata block and the first extent.
|
||||
Fix filtering of md 1.0 devices so they are not seen as duplicate PVs.
|
||||
Fix lvconvert striped/raid0/raid0_meta -> raid6 regression.
|
||||
Add After=rbdmap.service to {lvm2-activation-net,blk-availability}.service.
|
||||
Fix pvs with lvmetad to avoid too many open files from filter reads.
|
||||
Fix pvscan --cache to avoid too many open files from filter reads.
|
||||
Reduce max concurrent aios to avoid EMFILE with many devices.
|
||||
Fix lvconvert conversion attempts to linear.
|
||||
Fix lvconvert raid0/raid0_meta -> striped regression.
|
||||
Fix lvconvert --splitmirror for mirror type (2.02.178).
|
||||
Do not pair cache policy and cache metadata format.
|
||||
Fix mirrors honoring read_only_volume_list.
|
||||
|
||||
Version 2.02.181 - 01 August 2018
|
||||
=================================
|
||||
Reject conversions on raid1 LVs with split tracked SubLVs.
|
||||
Reject conversions on raid1 split tracked SubLVs.
|
||||
Fix dmstats list failing when no regions exist.
|
||||
Reject conversions of LVs under snapshot.
|
||||
Limit suggested options on incorrect option for lvconvert subcommand.
|
||||
|
||||
Version 2.02.180 - 19th July 2018
|
||||
=================================
|
||||
Never send any discard ioctl with test mode.
|
||||
Fix thin-pool alloc which needs same PV for data and metadata.
|
||||
Extend list of non-memlocked areas with newly linked libs.
|
||||
Enhance vgcfgrestore to check for active LVs in restored VG.
|
||||
lvconvert: provide possible layouts between linear and striped/raid
|
||||
Fix unmonitoring of merging snapshots.
|
||||
Add missing -l description in fsadm man page.
|
||||
Cache can uses metadata format 2 with cleaner policy.
|
||||
Avoid showing internal error in lvs output or pvmoved LVs.
|
||||
Fix check if resized PV can also fit metadata area.
|
||||
Reopen devices RDWR only before writing to avoid udev issues.
|
||||
Change pvresize output confusing when no resize took place.
|
||||
Fix lvmetad hanging on shutdown.
|
||||
Fix mem leak in clvmd and more coverity issues.
|
||||
|
||||
Version 2.02.179 - 18th June 2018
|
||||
=================================
|
||||
Allow forced vgchange to lock type none on clustered VG.
|
||||
Add the report field "shared".
|
||||
Enable automatic metadata consistency repair on a shared VG.
|
||||
Fix pvremove force on a PV with a shared VG.
|
||||
Fixed vgimportclone of a PV with a shared VG.
|
||||
Enable previously disallowed thin/cache commands in shared VGs.
|
||||
Enable metadata-related changes on LVs active with shared lock.
|
||||
Do not continue trying to use a device that cannot be opened.
|
||||
Fix problems opening a device that fails and returns.
|
||||
Use versionsort to fix archive file expiry beyond 100000 files.
|
||||
|
||||
Version 2.02.178 - 13th June 2018
|
||||
=================================
|
||||
|
||||
Version 2.02.178-rc1 - 24th May 2018
|
||||
====================================
|
||||
Add libaio dependency for build.
|
||||
Remove lvm1 and pool format handling and add filter to ignore them.
|
||||
Move some filter checks to after disks are read.
|
||||
Rework disk scanning and when it is used.
|
||||
Add new io layer and shift code to using it.
|
||||
Fix lvconvert's return code on degraded -m raid1 conversion.
|
||||
--enable-testing switch for ./configure has been removed.
|
||||
--with-snapshots switch for ./configure has been removed.
|
||||
--with-mirrors switch for ./configure has been removed.
|
||||
--with-raid switch for ./configure has been removed.
|
||||
--with-thin switch for ./configure has been removed.
|
||||
--with-cache switch for ./configure has been removed.
|
||||
Include new unit-test framework and unit tests.
|
||||
Extend validation of region_size for mirror segment.
|
||||
Reload whole device stack when reinitilizing mirror log.
|
||||
Mirrors without monitoring are WARNING and not blocking on error.
|
||||
Detect too big region_size with clustered mirrors.
|
||||
Fix evaluation of maximal region size for mirror log.
|
||||
Enhance mirror log size estimation and use smaller size when possible.
|
||||
Fix incorrect mirror log size calculation on 32bit arch.
|
||||
Enhance preloading tree creating.
|
||||
Fix regression on acceptance of any LV on lvconvert.
|
||||
Restore usability of thin LV to be again external origin for another thin.
|
||||
Keep systemd vars on change event in 69-dm-lvm-metad.rules for systemd reload.
|
||||
Write systemd and non-systemd rule in 69-dm-lvm-metad.rules, GOTO active one.
|
||||
Add test for activation/volume_list (Sub)LV remnants.
|
||||
Disallow usage of cache format 2 with mq cache policy.
|
||||
Again accept striped LV as COW LV with lvconvert -s (2.02.169).
|
||||
Fix raid target version testing for supported features.
|
||||
Allow activation of pools when thin/cache_check tool is missing.
|
||||
Remove RaidLV on creation failure when rmeta devices can't be activated.
|
||||
Add prioritized_section() to restore cookie boundaries (2.02.177).
|
||||
Enhance error messages when read error happens.
|
||||
Enhance mirror log initialization for old mirror target.
|
||||
Skip private crypto and stratis devices.
|
||||
Skip frozen raid devices from scanning.
|
||||
Activate RAID SubLVs on read_only_volume_list readwrite.
|
||||
Offer convenience type raid5_n converting to raid10.
|
||||
Automatically avoid reading invalid snapshots during device scan.
|
||||
Ensure COW device is writable even for read-only thick snapshots.
|
||||
Support activation of component LVs in read-only mode.
|
||||
Extend internal library to recognize and work with component LV.
|
||||
Skip duplicate check for active LV when prompting for its removal.
|
||||
Activate correct lock holding LV when it is cached.
|
||||
Do not modify archived metadata when removing striped raid.
|
||||
Fix memleak on error path when obtaining lv_raid_data_offset.
|
||||
Fix compatibility size test of extended external origin.
|
||||
Add external_origin visiting in for_each_sub_lv().
|
||||
Ensure cluster commands drop their device cache before locking VG.
|
||||
Do not report LV as remotely active when it's locally exclusive in cluster.
|
||||
Add deprecate messages for usage of mirrors with mirrorlog.
|
||||
Separate reporting of monitoring status and error status.
|
||||
Improve validation of created strings in vgimportclone.
|
||||
Add missing initialisation of mem pool in systemd generator.
|
||||
Do not reopen output streams for multithreaded users of liblvm.
|
||||
Configure ensures /usr/bin dir is checked for dmpd tools.
|
||||
Restore pvmove support for wide-clustered active volumes (2.02.177).
|
||||
Avoid non-exclusive activation of exclusive segment types.
|
||||
Fix trimming sibling PVs when doing a pvmove of raid subLVs.
|
||||
Preserve exclusive activation during thin snaphost merge.
|
||||
Suppress some repeated reads of the same disk data at the device layer.
|
||||
Avoid exceeding array bounds in allocation tag processing.
|
||||
Refactor metadata reading code to use callback functions.
|
||||
Move memory allocation for the key dev_reads into the device layer.
|
||||
Add --lockopt to common options and add option to skip selected locks.
|
||||
|
||||
Version 2.02.177 - 18th December 2017
|
||||
=====================================
|
||||
|
||||
50
WHATS_NEW_DM
50
WHATS_NEW_DM
@@ -1,5 +1,53 @@
|
||||
Version 1.02.147 -
|
||||
Version 1.02.166 -
|
||||
===================================
|
||||
Add support for DM_DEVICE_GET_TARGET_VERSION.
|
||||
|
||||
Version 1.02.164 - 27th August 2019
|
||||
===================================
|
||||
Add debug of dmsetup udevcomplete with hexa print DM_COOKIE_COMPLETED.
|
||||
Fix versioning of dm_stats_create_region and dm_stats_create_region.
|
||||
Parsing of cache status understand no_discard_passdown.
|
||||
|
||||
Version 1.02.158 - 13th May 2019
|
||||
================================
|
||||
|
||||
Version 1.02.156 - 22nd March 2019
|
||||
==================================
|
||||
Ensure migration_threshold for cache is at least 8 chunks.
|
||||
Enhance ioctl flattening and add parameters only when needed.
|
||||
Add DM_DEVICE_ARM_POLL for API completness matching kernel.
|
||||
|
||||
Version 1.02.154 - 07th December 2018
|
||||
=====================================
|
||||
Do not add parameters for RESUME with DM_DEVICE_CREATE dm task.
|
||||
Fix dmstats report printing no output.
|
||||
|
||||
Version 1.02.152 - 30th October 2018
|
||||
====================================
|
||||
Add hot fix to avoiding locking collision when monitoring thin-pools.
|
||||
|
||||
Version 1.02.150 - 01 August 2018
|
||||
=================================
|
||||
Add vdo plugin for monitoring VDO devices.
|
||||
|
||||
Version 1.02.149 - 19th July 2018
|
||||
=================================
|
||||
|
||||
Version 1.02.148 - 18th June 2018
|
||||
=================================
|
||||
|
||||
Version 1.02.147 - 13th June 2018
|
||||
=================================
|
||||
|
||||
Version 1.02.147-rc1 - 24th May 2018
|
||||
====================================
|
||||
Reuse uname() result for mirror target.
|
||||
Recognize also mounted btrfs through dm_device_has_mounted_fs().
|
||||
Add missing log_error() into dm_stats_populate() returning 0.
|
||||
Avoid calling dm_stats_populat() for DM devices without any stats regions.
|
||||
Support DM_DEBUG_WITH_LINE_NUMBERS envvar for debug msg with source:line.
|
||||
Configured command for thin pool threshold handling gets whole environment.
|
||||
Fix tests for failing dm_snprintf() in stats code.
|
||||
Parsing mirror status accepts 'userspace' keyword in status.
|
||||
Introduce dm_malloc_aligned for page alignment of buffers.
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ AC_DEFUN([AC_TRY_LDFLAGS],
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 3
|
||||
serial 3
|
||||
|
||||
AC_DEFUN([AX_GCC_BUILTIN], [
|
||||
AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
|
||||
|
||||
210
aclocal.m4
vendored
210
aclocal.m4
vendored
@@ -69,32 +69,63 @@ AC_DEFUN([AX_PYTHON_MODULE],[
|
||||
fi
|
||||
])
|
||||
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 1 (pkg-config-0.24)
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
dnl serial 11 (pkg-config-0.29)
|
||||
dnl
|
||||
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
||||
dnl
|
||||
dnl This program is free software; you can redistribute it and/or modify
|
||||
dnl it under the terms of the GNU General Public License as published by
|
||||
dnl the Free Software Foundation; either version 2 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl This program is distributed in the hope that it will be useful, but
|
||||
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
dnl General Public License for more details.
|
||||
dnl
|
||||
dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program; if not, write to the Free Software
|
||||
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
dnl 02111-1307, USA.
|
||||
dnl
|
||||
dnl As a special exception to the GNU General Public License, if you
|
||||
dnl distribute this file as part of a program that contains a
|
||||
dnl configuration script generated by Autoconf, you may include it under
|
||||
dnl the same distribution terms that you use for the rest of that
|
||||
dnl program.
|
||||
|
||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
# ----------------------------------
|
||||
dnl PKG_PREREQ(MIN-VERSION)
|
||||
dnl -----------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Verify that the version of the pkg-config macros are at least
|
||||
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
|
||||
dnl installed version of pkg-config, this checks the developer's version
|
||||
dnl of pkg.m4 when generating configure.
|
||||
dnl
|
||||
dnl To ensure that this macro is defined, also add:
|
||||
dnl m4_ifndef([PKG_PREREQ],
|
||||
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
|
||||
dnl
|
||||
dnl See the "Since" comment for each macro you use to see what version
|
||||
dnl of the macros you require.
|
||||
m4_defun([PKG_PREREQ],
|
||||
[m4_define([PKG_MACROS_VERSION], [0.29])
|
||||
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||
])dnl PKG_PREREQ
|
||||
|
||||
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
dnl ----------------------------------
|
||||
dnl Since: 0.16
|
||||
dnl
|
||||
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
|
||||
dnl first found in the path. Checks that the version of pkg-config found
|
||||
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
|
||||
dnl used since that's the first version where most current features of
|
||||
dnl pkg-config existed.
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
@@ -116,18 +147,19 @@ if test -n "$PKG_CONFIG"; then
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
fi[]dnl
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
])dnl PKG_PROG_PKG_CONFIG
|
||||
|
||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
# only at the first occurence in configure.ac, so if the first place
|
||||
# it's called might be skipped (such as if it is within an "if", you
|
||||
# have to call PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------------------------------
|
||||
dnl Since: 0.18
|
||||
dnl
|
||||
dnl Check to see whether a particular set of modules exists. Similar to
|
||||
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
dnl
|
||||
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
dnl only at the first occurence in configure.ac, so if the first place
|
||||
dnl it's called might be skipped (such as if it is within an "if", you
|
||||
dnl have to call PKG_CHECK_EXISTS manually
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
@@ -137,8 +169,10 @@ m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
dnl ---------------------------------------------
|
||||
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
|
||||
dnl pkg_failed based on the result.
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
@@ -150,10 +184,11 @@ m4_define([_PKG_CONFIG],
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])# _PKG_CONFIG
|
||||
])dnl _PKG_CONFIG
|
||||
|
||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
# -----------------------------
|
||||
dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
dnl ---------------------------
|
||||
dnl Internal check to see if pkg-config supports short errors.
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
@@ -161,19 +196,17 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
])dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
# [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
#
|
||||
# Note that if there is a possibility the first call to
|
||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl --------------------------------------------------------------
|
||||
dnl Since: 0.4.0
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
@@ -227,16 +260,40 @@ else
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
fi[]dnl
|
||||
])# PKG_CHECK_MODULES
|
||||
])dnl PKG_CHECK_MODULES
|
||||
|
||||
|
||||
# PKG_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable pkgconfigdir as the location where a module
|
||||
# should install pkg-config .pc files. By default the directory is
|
||||
# $libdir/pkgconfig, but the default can be changed by passing
|
||||
# DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
# parameter.
|
||||
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl ---------------------------------------------------------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Checks for existence of MODULES and gathers its build flags with
|
||||
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
|
||||
dnl and VARIABLE-PREFIX_LIBS from --libs.
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
|
||||
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
|
||||
dnl configure.ac.
|
||||
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
_save_PKG_CONFIG=$PKG_CONFIG
|
||||
PKG_CONFIG="$PKG_CONFIG --static"
|
||||
PKG_CHECK_MODULES($@)
|
||||
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
|
||||
])dnl PKG_CHECK_MODULES_STATIC
|
||||
|
||||
|
||||
dnl PKG_INSTALLDIR([DIRECTORY])
|
||||
dnl -------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable pkgconfigdir as the location where a module
|
||||
dnl should install pkg-config .pc files. By default the directory is
|
||||
dnl $libdir/pkgconfig, but the default can be changed by passing
|
||||
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
dnl parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
@@ -247,16 +304,18 @@ AC_ARG_WITH([pkgconfigdir],
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_INSTALLDIR
|
||||
])dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
# module should install arch-independent pkg-config .pc files. By
|
||||
# default the directory is $datadir/pkgconfig, but the default can be
|
||||
# changed by passing DIRECTORY. The user can override through the
|
||||
# --with-noarch-pkgconfigdir parameter.
|
||||
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
|
||||
dnl --------------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
dnl module should install arch-independent pkg-config .pc files. By
|
||||
dnl default the directory is $datadir/pkgconfig, but the default can be
|
||||
dnl changed by passing DIRECTORY. The user can override through the
|
||||
dnl --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
@@ -267,13 +326,15 @@ AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_NOARCH_INSTALLDIR
|
||||
])dnl PKG_NOARCH_INSTALLDIR
|
||||
|
||||
|
||||
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# -------------------------------------------
|
||||
# Retrieves the value of the pkg-config variable for the given module.
|
||||
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------
|
||||
dnl Since: 0.28
|
||||
dnl
|
||||
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
@@ -282,7 +343,7 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])# PKG_CHECK_VAR
|
||||
])dnl PKG_CHECK_VAR
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
@@ -536,5 +597,4 @@ AC_DEFUN([AM_RUN_LOG],
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
||||
(exit $ac_status); }])
|
||||
|
||||
|
||||
m4_include([acinclude.m4])
|
||||
|
||||
862
base/data-struct/radix-tree.c
Normal file
862
base/data-struct/radix-tree.c
Normal file
@@ -0,0 +1,862 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#include "radix-tree.h"
|
||||
|
||||
#include "base/memory/container_of.h"
|
||||
#include "base/memory/zalloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
enum node_type {
|
||||
UNSET = 0,
|
||||
VALUE,
|
||||
VALUE_CHAIN,
|
||||
PREFIX_CHAIN,
|
||||
NODE4,
|
||||
NODE16,
|
||||
NODE48,
|
||||
NODE256
|
||||
};
|
||||
|
||||
struct value {
|
||||
enum node_type type;
|
||||
union radix_value value;
|
||||
};
|
||||
|
||||
// This is used for entries that have a key which is a prefix of another key.
|
||||
struct value_chain {
|
||||
union radix_value value;
|
||||
struct value child;
|
||||
};
|
||||
|
||||
struct prefix_chain {
|
||||
struct value child;
|
||||
unsigned len;
|
||||
uint8_t prefix[0];
|
||||
};
|
||||
|
||||
struct node4 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[4];
|
||||
struct value values[4];
|
||||
};
|
||||
|
||||
struct node16 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[16];
|
||||
struct value values[16];
|
||||
};
|
||||
|
||||
struct node48 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[256];
|
||||
struct value values[48];
|
||||
};
|
||||
|
||||
struct node256 {
|
||||
uint32_t nr_entries;
|
||||
struct value values[256];
|
||||
};
|
||||
|
||||
struct radix_tree {
|
||||
unsigned nr_entries;
|
||||
struct value root;
|
||||
radix_value_dtr dtr;
|
||||
void *dtr_context;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context)
|
||||
{
|
||||
struct radix_tree *rt = malloc(sizeof(*rt));
|
||||
|
||||
if (rt) {
|
||||
rt->nr_entries = 0;
|
||||
rt->root.type = UNSET;
|
||||
rt->dtr = dtr;
|
||||
rt->dtr_context = dtr_context;
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
static inline void _dtr(struct radix_tree *rt, union radix_value v)
|
||||
{
|
||||
if (rt->dtr)
|
||||
rt->dtr(rt->dtr_context, v);
|
||||
}
|
||||
|
||||
// Returns the number of values removed
|
||||
static unsigned _free_node(struct radix_tree *rt, struct value v)
|
||||
{
|
||||
unsigned i, nr = 0;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
switch (v.type) {
|
||||
case UNSET:
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
_dtr(rt, v.value);
|
||||
nr = 1;
|
||||
break;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = v.value.ptr;
|
||||
_dtr(rt, vc->value);
|
||||
nr = 1 + _free_node(rt, vc->child);
|
||||
free(vc);
|
||||
break;
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = v.value.ptr;
|
||||
nr = _free_node(rt, pc->child);
|
||||
free(pc);
|
||||
break;
|
||||
|
||||
case NODE4:
|
||||
n4 = (struct node4 *) v.value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++)
|
||||
nr += _free_node(rt, n4->values[i]);
|
||||
free(n4);
|
||||
break;
|
||||
|
||||
case NODE16:
|
||||
n16 = (struct node16 *) v.value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++)
|
||||
nr += _free_node(rt, n16->values[i]);
|
||||
free(n16);
|
||||
break;
|
||||
|
||||
case NODE48:
|
||||
n48 = (struct node48 *) v.value.ptr;
|
||||
for (i = 0; i < n48->nr_entries; i++)
|
||||
nr += _free_node(rt, n48->values[i]);
|
||||
free(n48);
|
||||
break;
|
||||
|
||||
case NODE256:
|
||||
n256 = (struct node256 *) v.value.ptr;
|
||||
for (i = 0; i < 256; i++)
|
||||
nr += _free_node(rt, n256->values[i]);
|
||||
free(n256);
|
||||
break;
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
void radix_tree_destroy(struct radix_tree *rt)
|
||||
{
|
||||
_free_node(rt, rt->root);
|
||||
free(rt);
|
||||
}
|
||||
|
||||
unsigned radix_tree_size(struct radix_tree *rt)
|
||||
{
|
||||
return rt->nr_entries;
|
||||
}
|
||||
|
||||
static bool _insert(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv);
|
||||
|
||||
static bool _insert_unset(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
unsigned len = ke - kb;
|
||||
|
||||
if (!len) {
|
||||
// value
|
||||
v->type = VALUE;
|
||||
v->value = rv;
|
||||
rt->nr_entries++;
|
||||
} else {
|
||||
// prefix -> value
|
||||
struct prefix_chain *pc = zalloc(sizeof(*pc) + len);
|
||||
if (!pc)
|
||||
return false;
|
||||
|
||||
pc->child.type = VALUE;
|
||||
pc->child.value = rv;
|
||||
pc->len = len;
|
||||
memcpy(pc->prefix, kb, len);
|
||||
v->type = PREFIX_CHAIN;
|
||||
v->value.ptr = pc;
|
||||
rt->nr_entries++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_value(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
unsigned len = ke - kb;
|
||||
|
||||
if (!len)
|
||||
// overwrite
|
||||
v->value = rv;
|
||||
|
||||
else {
|
||||
// value_chain -> value
|
||||
struct value_chain *vc = zalloc(sizeof(*vc));
|
||||
if (!vc)
|
||||
return false;
|
||||
|
||||
vc->value = v->value;
|
||||
if (!_insert(rt, &vc->child, kb, ke, rv)) {
|
||||
free(vc);
|
||||
return false;
|
||||
}
|
||||
|
||||
v->type = VALUE_CHAIN;
|
||||
v->value.ptr = vc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_value_chain(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct value_chain *vc = v->value.ptr;
|
||||
return _insert(rt, &vc->child, kb, ke, rv);
|
||||
}
|
||||
|
||||
static unsigned min(unsigned lhs, unsigned rhs)
|
||||
{
|
||||
if (lhs <= rhs)
|
||||
return lhs;
|
||||
else
|
||||
return rhs;
|
||||
}
|
||||
|
||||
static bool _insert_prefix_chain(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct prefix_chain *pc = v->value.ptr;
|
||||
|
||||
if (*kb == pc->prefix[0]) {
|
||||
// There's a common prefix let's split the chain into two and
|
||||
// recurse.
|
||||
struct prefix_chain *pc2;
|
||||
unsigned i, len = min(pc->len, ke - kb);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (kb[i] != pc->prefix[i])
|
||||
break;
|
||||
|
||||
pc2 = zalloc(sizeof(*pc2) + pc->len - i);
|
||||
pc2->len = pc->len - i;
|
||||
memmove(pc2->prefix, pc->prefix + i, pc2->len);
|
||||
pc2->child = pc->child;
|
||||
|
||||
// FIXME: this trashes pc so we can't back out
|
||||
pc->child.type = PREFIX_CHAIN;
|
||||
pc->child.value.ptr = pc2;
|
||||
pc->len = i;
|
||||
|
||||
if (!_insert(rt, &pc->child, kb + i, ke, rv)) {
|
||||
free(pc2);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Stick an n4 in front.
|
||||
struct node4 *n4 = zalloc(sizeof(*n4));
|
||||
if (!n4)
|
||||
return false;
|
||||
|
||||
n4->keys[0] = *kb;
|
||||
if (!_insert(rt, n4->values, kb + 1, ke, rv)) {
|
||||
free(n4);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pc->len) {
|
||||
n4->keys[1] = pc->prefix[0];
|
||||
if (pc->len == 1) {
|
||||
n4->values[1] = pc->child;
|
||||
free(pc);
|
||||
} else {
|
||||
memmove(pc->prefix, pc->prefix + 1, pc->len - 1);
|
||||
pc->len--;
|
||||
n4->values[1] = *v;
|
||||
}
|
||||
n4->nr_entries = 2;
|
||||
} else
|
||||
n4->nr_entries = 1;
|
||||
|
||||
v->type = NODE4;
|
||||
v->value.ptr = n4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node4(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node4 *n4 = v->value.ptr;
|
||||
if (n4->nr_entries == 4) {
|
||||
struct node16 *n16 = zalloc(sizeof(*n16));
|
||||
if (!n16)
|
||||
return false;
|
||||
|
||||
n16->nr_entries = 5;
|
||||
memcpy(n16->keys, n4->keys, sizeof(n4->keys));
|
||||
memcpy(n16->values, n4->values, sizeof(n4->values));
|
||||
|
||||
n16->keys[4] = *kb;
|
||||
if (!_insert(rt, n16->values + 4, kb + 1, ke, rv)) {
|
||||
free(n16);
|
||||
return false;
|
||||
}
|
||||
free(n4);
|
||||
v->type = NODE16;
|
||||
v->value.ptr = n16;
|
||||
} else {
|
||||
n4 = v->value.ptr;
|
||||
if (!_insert(rt, n4->values + n4->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
|
||||
n4->keys[n4->nr_entries] = *kb;
|
||||
n4->nr_entries++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node16(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node16 *n16 = v->value.ptr;
|
||||
|
||||
if (n16->nr_entries == 16) {
|
||||
unsigned i;
|
||||
struct node48 *n48 = zalloc(sizeof(*n48));
|
||||
|
||||
if (!n48)
|
||||
return false;
|
||||
|
||||
n48->nr_entries = 17;
|
||||
memset(n48->keys, 48, sizeof(n48->keys));
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
n48->keys[n16->keys[i]] = i;
|
||||
n48->values[i] = n16->values[i];
|
||||
}
|
||||
|
||||
n48->keys[*kb] = 16;
|
||||
if (!_insert(rt, n48->values + 16, kb + 1, ke, rv)) {
|
||||
free(n48);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(n16);
|
||||
v->type = NODE48;
|
||||
v->value.ptr = n48;
|
||||
} else {
|
||||
if (!_insert(rt, n16->values + n16->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
n16->keys[n16->nr_entries] = *kb;
|
||||
n16->nr_entries++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node48(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node48 *n48 = v->value.ptr;
|
||||
if (n48->nr_entries == 48) {
|
||||
unsigned i;
|
||||
struct node256 *n256 = zalloc(sizeof(*n256));
|
||||
if (!n256)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (n48->keys[i] >= 48)
|
||||
continue;
|
||||
|
||||
n256->values[i] = n48->values[n48->keys[i]];
|
||||
}
|
||||
|
||||
if (!_insert(rt, n256->values + *kb, kb + 1, ke, rv)) {
|
||||
free(n256);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(n48);
|
||||
v->type = NODE256;
|
||||
v->value.ptr = n256;
|
||||
|
||||
} else {
|
||||
if (!_insert(rt, n48->values + n48->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
|
||||
n48->keys[*kb] = n48->nr_entries;
|
||||
n48->nr_entries++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node256(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node256 *n256 = v->value.ptr;
|
||||
bool was_unset = n256->values[*kb].type == UNSET;
|
||||
|
||||
if (!_insert(rt, n256->values + *kb, kb + 1, ke, rv))
|
||||
return false;
|
||||
|
||||
if (was_unset)
|
||||
n256->nr_entries++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: the tree should not be touched if insert fails (eg, OOM)
|
||||
static bool _insert(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
if (kb == ke) {
|
||||
if (v->type == UNSET) {
|
||||
v->type = VALUE;
|
||||
v->value = rv;
|
||||
rt->nr_entries++;
|
||||
|
||||
} else if (v->type == VALUE) {
|
||||
v->value = rv;
|
||||
|
||||
} else {
|
||||
struct value_chain *vc = zalloc(sizeof(*vc));
|
||||
if (!vc)
|
||||
return false;
|
||||
|
||||
vc->value = rv;
|
||||
vc->child = *v;
|
||||
v->type = VALUE_CHAIN;
|
||||
v->value.ptr = vc;
|
||||
rt->nr_entries++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (v->type) {
|
||||
case UNSET:
|
||||
return _insert_unset(rt, v, kb, ke, rv);
|
||||
|
||||
case VALUE:
|
||||
return _insert_value(rt, v, kb, ke, rv);
|
||||
|
||||
case VALUE_CHAIN:
|
||||
return _insert_value_chain(rt, v, kb, ke, rv);
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
return _insert_prefix_chain(rt, v, kb, ke, rv);
|
||||
|
||||
case NODE4:
|
||||
return _insert_node4(rt, v, kb, ke, rv);
|
||||
|
||||
case NODE16:
|
||||
return _insert_node16(rt, v, kb, ke, rv);
|
||||
|
||||
case NODE48:
|
||||
return _insert_node48(rt, v, kb, ke, rv);
|
||||
|
||||
case NODE256:
|
||||
return _insert_node256(rt, v, kb, ke, rv);
|
||||
}
|
||||
|
||||
// can't get here
|
||||
return false;
|
||||
}
|
||||
|
||||
struct lookup_result {
|
||||
struct value *v;
|
||||
uint8_t *kb;
|
||||
};
|
||||
|
||||
static struct lookup_result _lookup_prefix(struct value *v, uint8_t *kb, uint8_t *ke)
|
||||
{
|
||||
unsigned i;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
if (kb == ke)
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
switch (v->type) {
|
||||
case UNSET:
|
||||
case VALUE:
|
||||
break;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = v->value.ptr;
|
||||
return _lookup_prefix(&vc->child, kb, ke);
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = v->value.ptr;
|
||||
if (ke - kb < pc->len)
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
for (i = 0; i < pc->len; i++)
|
||||
if (kb[i] != pc->prefix[i])
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
return _lookup_prefix(&pc->child, kb + pc->len, ke);
|
||||
|
||||
case NODE4:
|
||||
n4 = v->value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++)
|
||||
if (n4->keys[i] == *kb)
|
||||
return _lookup_prefix(n4->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE16:
|
||||
// FIXME: use binary search or simd?
|
||||
n16 = v->value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++)
|
||||
if (n16->keys[i] == *kb)
|
||||
return _lookup_prefix(n16->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE48:
|
||||
n48 = v->value.ptr;
|
||||
i = n48->keys[*kb];
|
||||
if (i < 48)
|
||||
return _lookup_prefix(n48->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE256:
|
||||
n256 = v->value.ptr;
|
||||
return _lookup_prefix(n256->values + *kb, kb + 1, ke);
|
||||
}
|
||||
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
}
|
||||
|
||||
bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
return _insert(rt, lr.v, lr.kb, ke, rv);
|
||||
}
|
||||
|
||||
// Note the degrade functions also free the original node.
|
||||
static void _degrade_to_n4(struct node16 *n16, struct value *result)
|
||||
{
|
||||
struct node4 *n4 = zalloc(sizeof(*n4));
|
||||
|
||||
n4->nr_entries = n16->nr_entries;
|
||||
memcpy(n4->keys, n16->keys, n16->nr_entries * sizeof(*n4->keys));
|
||||
memcpy(n4->values, n16->values, n16->nr_entries * sizeof(*n4->values));
|
||||
free(n16);
|
||||
|
||||
result->type = NODE4;
|
||||
result->value.ptr = n4;
|
||||
}
|
||||
|
||||
static void _degrade_to_n16(struct node48 *n48, struct value *result)
|
||||
{
|
||||
struct node4 *n16 = zalloc(sizeof(*n16));
|
||||
|
||||
n16->nr_entries = n48->nr_entries;
|
||||
memcpy(n16->keys, n48->keys, n48->nr_entries * sizeof(*n16->keys));
|
||||
memcpy(n16->values, n48->values, n48->nr_entries * sizeof(*n16->values));
|
||||
free(n48);
|
||||
|
||||
result->type = NODE16;
|
||||
result->value.ptr = n16;
|
||||
}
|
||||
|
||||
static void _degrade_to_n48(struct node256 *n256, struct value *result)
|
||||
{
|
||||
unsigned i, count = 0;
|
||||
struct node4 *n48 = zalloc(sizeof(*n48));
|
||||
|
||||
n48->nr_entries = n256->nr_entries;
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (n256->values[i].type == UNSET)
|
||||
continue;
|
||||
|
||||
n48->keys[count] = i;
|
||||
n48->values[count] = n256->values[i];
|
||||
count++;
|
||||
}
|
||||
free(n256);
|
||||
|
||||
result->type = NODE48;
|
||||
result->value.ptr = n48;
|
||||
}
|
||||
|
||||
static bool _remove(struct radix_tree *rt, struct value *root, uint8_t *kb, uint8_t *ke)
|
||||
{
|
||||
bool r;
|
||||
unsigned i;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
if (kb == ke) {
|
||||
if (root->type == VALUE) {
|
||||
root->type = UNSET;
|
||||
_dtr(rt, root->value);
|
||||
return true;
|
||||
|
||||
} else if (root->type == VALUE_CHAIN) {
|
||||
vc = root->value.ptr;
|
||||
_dtr(rt, vc->value);
|
||||
memcpy(root, &vc->child, sizeof(*root));
|
||||
free(vc);
|
||||
return true;
|
||||
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (root->type) {
|
||||
case UNSET:
|
||||
case VALUE:
|
||||
// this is a value for a prefix of the key
|
||||
return false;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = root->value.ptr;
|
||||
r = _remove(rt, &vc->child, kb, ke);
|
||||
if (r && (vc->child.type == UNSET)) {
|
||||
memcpy(root, &vc->child, sizeof(*root));
|
||||
free(vc);
|
||||
}
|
||||
return r;
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = root->value.ptr;
|
||||
if (ke - kb < pc->len)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < pc->len; i++)
|
||||
if (kb[i] != pc->prefix[i])
|
||||
return false;
|
||||
|
||||
return _remove(rt, &pc->child, kb + pc->len, ke);
|
||||
|
||||
case NODE4:
|
||||
n4 = root->value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++) {
|
||||
if (n4->keys[i] == *kb) {
|
||||
r = _remove(rt, n4->values + i, kb + 1, ke);
|
||||
if (r && n4->values[i].type == UNSET) {
|
||||
n4->nr_entries--;
|
||||
if (i < n4->nr_entries)
|
||||
// slide the entries down
|
||||
memmove(n4->keys + i, n4->keys + i + 1,
|
||||
sizeof(*n4->keys) * (n4->nr_entries - i));
|
||||
if (!n4->nr_entries)
|
||||
root->type = UNSET;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case NODE16:
|
||||
n16 = root->value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++) {
|
||||
if (n16->keys[i] == *kb) {
|
||||
r = _remove(rt, n16->values + i, kb + 1, ke);
|
||||
if (r && n16->values[i].type == UNSET) {
|
||||
n16->nr_entries--;
|
||||
if (i < n16->nr_entries)
|
||||
// slide the entries down
|
||||
memmove(n16->keys + i, n16->keys + i + 1,
|
||||
sizeof(*n16->keys) * (n16->nr_entries - i));
|
||||
if (n16->nr_entries <= 4)
|
||||
_degrade_to_n4(n16, root);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case NODE48:
|
||||
n48 = root->value.ptr;
|
||||
i = n48->keys[*kb];
|
||||
if (i < 48) {
|
||||
r = _remove(rt, n48->values + i, kb + 1, ke);
|
||||
if (r && n48->values[i].type == UNSET) {
|
||||
n48->keys[*kb] = 48;
|
||||
n48->nr_entries--;
|
||||
if (n48->nr_entries <= 16)
|
||||
_degrade_to_n16(n48, root);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return false;
|
||||
|
||||
case NODE256:
|
||||
n256 = root->value.ptr;
|
||||
r = _remove(rt, n256->values + (*kb), kb + 1, ke);
|
||||
if (r && n256->values[*kb].type == UNSET) {
|
||||
n256->nr_entries--;
|
||||
if (n256->nr_entries <= 48)
|
||||
_degrade_to_n48(n256, root);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool radix_tree_remove(struct radix_tree *rt, uint8_t *key_begin, uint8_t *key_end)
|
||||
{
|
||||
if (_remove(rt, &rt->root, key_begin, key_end)) {
|
||||
rt->nr_entries--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _prefix_chain_matches(struct lookup_result *lr, uint8_t *ke)
|
||||
{
|
||||
// It's possible the top node is a prefix chain, and
|
||||
// the remaining key matches part of it.
|
||||
if (lr->v->type == PREFIX_CHAIN) {
|
||||
unsigned i, rlen = ke - lr->kb;
|
||||
struct prefix_chain *pc = lr->v->value.ptr;
|
||||
if (rlen < pc->len) {
|
||||
for (i = 0; i < rlen; i++)
|
||||
if (pc->prefix[i] != lr->kb[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned radix_tree_remove_prefix(struct radix_tree *rt, uint8_t *kb, uint8_t *ke)
|
||||
{
|
||||
unsigned count = 0;
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
if (lr.kb == ke || _prefix_chain_matches(&lr, ke)) {
|
||||
count = _free_node(rt, *lr.v);
|
||||
lr.v->type = UNSET;
|
||||
}
|
||||
|
||||
rt->nr_entries -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
bool radix_tree_lookup(struct radix_tree *rt,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value *result)
|
||||
{
|
||||
struct value_chain *vc;
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
if (lr.kb == ke) {
|
||||
switch (lr.v->type) {
|
||||
case VALUE:
|
||||
*result = lr.v->value;
|
||||
return true;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = lr.v->value.ptr;
|
||||
*result = vc->value;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: build up the keys too
|
||||
static bool _iterate(struct value *v, struct radix_tree_iterator *it)
|
||||
{
|
||||
unsigned i;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
switch (v->type) {
|
||||
case UNSET:
|
||||
// can't happen
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
return it->visit(it, NULL, NULL, v->value);
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = v->value.ptr;
|
||||
return it->visit(it, NULL, NULL, vc->value) && _iterate(&vc->child, it);
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = v->value.ptr;
|
||||
return _iterate(&pc->child, it);
|
||||
|
||||
case NODE4:
|
||||
n4 = (struct node4 *) v->value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++)
|
||||
if (!_iterate(n4->values + i, it))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case NODE16:
|
||||
n16 = (struct node16 *) v->value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++)
|
||||
if (!_iterate(n16->values + i, it))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case NODE48:
|
||||
n48 = (struct node48 *) v->value.ptr;
|
||||
for (i = 0; i < n48->nr_entries; i++)
|
||||
if (!_iterate(n48->values + i, it))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case NODE256:
|
||||
n256 = (struct node256 *) v->value.ptr;
|
||||
for (i = 0; i < 256; i++)
|
||||
if (n256->values[i].type != UNSET && !_iterate(n256->values + i, it))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// can't get here
|
||||
return false;
|
||||
}
|
||||
|
||||
void radix_tree_iterate(struct radix_tree *rt, uint8_t *kb, uint8_t *ke,
|
||||
struct radix_tree_iterator *it)
|
||||
{
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
if (lr.kb == ke || _prefix_chain_matches(&lr, ke))
|
||||
_iterate(lr.v, it);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
58
base/data-struct/radix-tree.h
Normal file
58
base/data-struct/radix-tree.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
#define BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct radix_tree;
|
||||
|
||||
union radix_value {
|
||||
void *ptr;
|
||||
uint64_t n;
|
||||
};
|
||||
|
||||
typedef void (*radix_value_dtr)(void *context, union radix_value v);
|
||||
|
||||
// dtr will be called on any deleted entries. dtr may be NULL.
|
||||
struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context);
|
||||
void radix_tree_destroy(struct radix_tree *rt);
|
||||
|
||||
unsigned radix_tree_size(struct radix_tree *rt);
|
||||
bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value v);
|
||||
bool radix_tree_remove(struct radix_tree *rt, uint8_t *kb, uint8_t *ke);
|
||||
|
||||
// Returns the number of values removed
|
||||
unsigned radix_tree_remove_prefix(struct radix_tree *rt, uint8_t *prefix_b, uint8_t *prefix_e);
|
||||
|
||||
bool radix_tree_lookup(struct radix_tree *rt,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value *result);
|
||||
|
||||
// The radix tree stores entries in lexicographical order. Which means
|
||||
// we can iterate entries, in order. Or iterate entries with a particular
|
||||
// prefix.
|
||||
struct radix_tree_iterator {
|
||||
// Returns false if the iteration should end.
|
||||
bool (*visit)(struct radix_tree_iterator *it,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value v);
|
||||
};
|
||||
|
||||
void radix_tree_iterate(struct radix_tree *rt, uint8_t *kb, uint8_t *ke,
|
||||
struct radix_tree_iterator *it);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
23
base/memory/container_of.h
Normal file
23
base/memory/container_of.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_CONTAINER_OF_H
|
||||
#define BASE_MEMORY_CONTAINER_OF_H
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define container_of(v, t, head) \
|
||||
((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
31
base/memory/zalloc.h
Normal file
31
base/memory/zalloc.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_ZALLOC_H
|
||||
#define BASE_MEMORY_ZALLOC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static inline void *zalloc(size_t len)
|
||||
{
|
||||
void *ptr = malloc(len);
|
||||
if (ptr)
|
||||
memset(ptr, 0, len);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -59,22 +59,6 @@ devices {
|
||||
# This configuration option is advanced.
|
||||
scan = [ "/dev" ]
|
||||
|
||||
# Configuration option devices/use_aio.
|
||||
# Use linux asynchronous I/O for parallel device access where possible.
|
||||
# This configuration option has an automatic default value.
|
||||
# use_aio = 1
|
||||
|
||||
# Configuration option devices/aio_max.
|
||||
# Maximum number of asynchronous I/Os to issue concurrently.
|
||||
# This configuration option has an automatic default value.
|
||||
# aio_max = 128
|
||||
|
||||
# Configuration option devices/aio_memory.
|
||||
# Approximate maximum total amount of memory (in MB) used
|
||||
# for asynchronous I/O buffers.
|
||||
# This configuration option has an automatic default value.
|
||||
# aio_memory = 10
|
||||
|
||||
# Configuration option devices/obtain_device_list_from_udev.
|
||||
# Obtain the list of available devices from udev.
|
||||
# This avoids opening or using any inapplicable non-block devices or
|
||||
@@ -201,6 +185,20 @@ devices {
|
||||
# present on the system. sysfs must be part of the kernel and mounted.)
|
||||
sysfs_scan = 1
|
||||
|
||||
# Configuration option devices/scan_lvs.
|
||||
# Scan LVM LVs for layered PVs, allowing LVs to be used as PVs.
|
||||
# When 1, LVM will detect PVs layered on LVs, and caution must be
|
||||
# taken to avoid a host accessing a layered VG that may not belong
|
||||
# to it, e.g. from a guest image. This generally requires excluding
|
||||
# the LVs with device filters. Also, when this setting is enabled,
|
||||
# every LVM command will scan every active LV on the system (unless
|
||||
# filtered), which can cause performance problems on systems with
|
||||
# many active LVs. When this setting is 0, LVM will not detect or
|
||||
# use PVs that exist on LVs, and will not allow a PV to be created on
|
||||
# an LV. The LVs are ignored using a built in device filter that
|
||||
# identifies and excludes LVs.
|
||||
scan_lvs = 0
|
||||
|
||||
# Configuration option devices/multipath_component_detection.
|
||||
# Ignore devices that are components of DM multipath devices.
|
||||
multipath_component_detection = 1
|
||||
@@ -718,29 +716,17 @@ global {
|
||||
activation = 1
|
||||
|
||||
# Configuration option global/fallback_to_lvm1.
|
||||
# Try running LVM1 tools if LVM cannot communicate with DM.
|
||||
# This option only applies to 2.4 kernels and is provided to help
|
||||
# switch between device-mapper kernels and LVM1 kernels. The LVM1
|
||||
# tools need to be installed with .lvm1 suffices, e.g. vgscan.lvm1.
|
||||
# They will stop working once the lvm2 on-disk metadata format is used.
|
||||
# This setting is no longer used.
|
||||
# This configuration option has an automatic default value.
|
||||
# fallback_to_lvm1 = @DEFAULT_FALLBACK_TO_LVM1@
|
||||
# fallback_to_lvm1 = 0
|
||||
|
||||
# Configuration option global/format.
|
||||
# The default metadata format that commands should use.
|
||||
# The -M 1|2 option overrides this setting.
|
||||
#
|
||||
# Accepted values:
|
||||
# lvm1
|
||||
# lvm2
|
||||
#
|
||||
# This setting is no longer used.
|
||||
# This configuration option has an automatic default value.
|
||||
# format = "lvm2"
|
||||
|
||||
# Configuration option global/format_libraries.
|
||||
# Shared libraries that process different metadata formats.
|
||||
# If support for LVM1 metadata was compiled as a shared library use
|
||||
# format_libraries = "liblvm2format1.so"
|
||||
# This setting is no longer used.
|
||||
# This configuration option does not have a default value defined.
|
||||
|
||||
# Configuration option global/segment_libraries.
|
||||
@@ -837,13 +823,6 @@ global {
|
||||
# encountered the internal error. Please only enable for debugging.
|
||||
abort_on_internal_errors = 0
|
||||
|
||||
# Configuration option global/detect_internal_vg_cache_corruption.
|
||||
# Internal verification of VG structures.
|
||||
# Check if CRC matches when a parsed VG is used multiple times. This
|
||||
# is useful to catch unexpected changes to cached VG structures.
|
||||
# Please only enable for debugging.
|
||||
detect_internal_vg_cache_corruption = 0
|
||||
|
||||
# Configuration option global/metadata_read_only.
|
||||
# No operations that change on-disk metadata are permitted.
|
||||
# Additionally, read-only commands that encounter metadata in need of
|
||||
@@ -926,6 +905,11 @@ global {
|
||||
# This configuration option has an automatic default value.
|
||||
# lvdisplay_shows_full_device_path = 0
|
||||
|
||||
# Configuration option global/use_aio.
|
||||
# Use async I/O when reading and writing devices.
|
||||
# This configuration option has an automatic default value.
|
||||
# use_aio = 1
|
||||
|
||||
# Configuration option global/use_lvmetad.
|
||||
# Use lvmetad to cache metadata and reduce disk scanning.
|
||||
# When enabled (and running), lvmetad provides LVM commands with VG
|
||||
@@ -1143,6 +1127,16 @@ global {
|
||||
# When enabled, an LVM command that changes PVs, changes VG metadata,
|
||||
# or changes the activation state of an LV will send a notification.
|
||||
notify_dbus = 1
|
||||
|
||||
# Configuration option global/io_memory_size.
|
||||
# The amount of memory in KiB that LVM allocates to perform disk io.
|
||||
# LVM performance may benefit from more io memory when there are many
|
||||
# disks or VG metadata is large. Increasing this size may be necessary
|
||||
# when a single copy of VG metadata is larger than the current setting.
|
||||
# This value should usually not be decreased from the default; setting
|
||||
# it too low can result in lvm failing to read VGs.
|
||||
# This configuration option has an automatic default value.
|
||||
# io_memory_size = 8192
|
||||
}
|
||||
|
||||
# Configuration section activation.
|
||||
|
||||
340
configure
vendored
340
configure
vendored
@@ -653,7 +653,6 @@ UDEV_RULES
|
||||
UDEV_PC
|
||||
THIN
|
||||
TESTSUITE_DATA
|
||||
TESTING
|
||||
STATIC_LINK
|
||||
STATICDIR
|
||||
SNAPSHOTS
|
||||
@@ -664,7 +663,6 @@ SBINDIR
|
||||
REPLICATORS
|
||||
READLINE_LIBS
|
||||
RT_LIBS
|
||||
RAID
|
||||
PYTHON3DIR
|
||||
PYTHON2DIR
|
||||
PYTHON3_LIBDIRS
|
||||
@@ -677,7 +675,6 @@ PYTHON_BINDINGS
|
||||
PYTHON3
|
||||
PTHREAD_LIBS
|
||||
M_LIBS
|
||||
POOL
|
||||
PKGCONFIG
|
||||
ODIRECT
|
||||
OCFDIR
|
||||
@@ -692,8 +689,6 @@ LVM_MINOR
|
||||
LVM_MAJOR
|
||||
LVM_LIBAPI
|
||||
LVM_VERSION
|
||||
LVM1_FALLBACK
|
||||
LVM1
|
||||
LIB_SUFFIX
|
||||
LDDEPS
|
||||
JOBS
|
||||
@@ -707,9 +702,7 @@ FSADM
|
||||
ELDFLAGS
|
||||
DM_LIB_PATCHLEVEL
|
||||
DMEVENTD_PATH
|
||||
AIO_LIBS
|
||||
DL_LIBS
|
||||
AIO
|
||||
DEVMAPPER
|
||||
DEFAULT_USE_LVMLOCKD
|
||||
DEFAULT_USE_LVMPOLLD
|
||||
@@ -723,7 +716,6 @@ DEFAULT_RAID10_SEGTYPE
|
||||
DEFAULT_PROFILE_SUBDIR
|
||||
DEFAULT_PID_DIR
|
||||
DEFAULT_MIRROR_SEGTYPE
|
||||
DEFAULT_FALLBACK_TO_LVM1
|
||||
DEFAULT_LOCK_DIR
|
||||
DEFAULT_DM_RUN_DIR
|
||||
DEFAULT_DATA_ALIGNMENT
|
||||
@@ -783,8 +775,6 @@ LOCKD_SANLOCK_LIBS
|
||||
LOCKD_SANLOCK_CFLAGS
|
||||
VALGRIND_LIBS
|
||||
VALGRIND_CFLAGS
|
||||
CUNIT_LIBS
|
||||
CUNIT_CFLAGS
|
||||
GENPNG
|
||||
GENHTML
|
||||
LCOV
|
||||
@@ -886,6 +876,7 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@@ -917,13 +908,9 @@ with_device_gid
|
||||
with_device_mode
|
||||
with_device_nodes_on
|
||||
with_default_name_mangling
|
||||
enable_lvm1_fallback
|
||||
with_lvm1
|
||||
with_pool
|
||||
with_cluster
|
||||
with_snapshots
|
||||
with_mirrors
|
||||
with_raid
|
||||
with_default_mirror_segtype
|
||||
with_default_raid10_segtype
|
||||
with_default_sparse_segtype
|
||||
@@ -953,10 +940,8 @@ with_cmirrord_pidfile
|
||||
enable_debug
|
||||
with_optimisation
|
||||
enable_profiling
|
||||
enable_testing
|
||||
enable_valgrind_pool
|
||||
enable_devmapper
|
||||
enable_aio
|
||||
enable_lvmetad
|
||||
enable_lvmpolld
|
||||
enable_lvmlockd_sanlock
|
||||
@@ -1046,8 +1031,6 @@ DLM_CFLAGS
|
||||
DLM_LIBS
|
||||
SACKPT_CFLAGS
|
||||
SACKPT_LIBS
|
||||
CUNIT_CFLAGS
|
||||
CUNIT_LIBS
|
||||
VALGRIND_CFLAGS
|
||||
VALGRIND_LIBS
|
||||
LOCKD_SANLOCK_CFLAGS
|
||||
@@ -1101,6 +1084,7 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE}'
|
||||
@@ -1353,6 +1337,15 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@@ -1490,7 +1483,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir
|
||||
libdir localedir mandir runstatedir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@@ -1643,6 +1636,7 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@@ -1679,8 +1673,6 @@ Optional Features:
|
||||
speeds up one-time build.
|
||||
--enable-static_link use this to link the tools to their libraries
|
||||
statically (default is dynamic linking
|
||||
--enable-lvm1_fallback use this to fall back and use LVM1 binaries if
|
||||
device-mapper is missing from the kernel
|
||||
--disable-thin_check_needs_check
|
||||
required if thin_check version is < 0.3.0
|
||||
--disable-cache_check_needs_check
|
||||
@@ -1692,10 +1684,8 @@ Optional Features:
|
||||
--enable-cmirrord enable the cluster mirror log daemon
|
||||
--enable-debug enable debugging
|
||||
--enable-profiling gather gcov profiling data
|
||||
--enable-testing enable testing targets in the makefile
|
||||
--enable-valgrind-pool enable valgrind awareness of pools
|
||||
--disable-devmapper disable LVM2 device-mapper interaction
|
||||
--disable-aio disable asynchronous I/O
|
||||
--enable-lvmetad enable the LVM Metadata Daemon
|
||||
--enable-lvmpolld enable the LVM Polling Daemon
|
||||
--enable-lvmlockd-sanlock
|
||||
@@ -1749,15 +1739,10 @@ Optional Packages:
|
||||
create nodes on resume or create [ON=resume]
|
||||
--with-default-name-mangling=MANGLING
|
||||
default name mangling: auto/none/hex [auto]
|
||||
--with-lvm1=TYPE LVM1 metadata support: internal/shared/none
|
||||
[internal]
|
||||
--with-pool=TYPE GFS pool read-only support: internal/shared/none
|
||||
[internal]
|
||||
--with-cluster=TYPE cluster LVM locking support: internal/shared/none
|
||||
[internal]
|
||||
--with-snapshots=TYPE snapshot support: internal/shared/none [internal]
|
||||
--with-mirrors=TYPE mirror support: internal/shared/none [internal]
|
||||
--with-raid=TYPE raid support: internal/shared/none [internal]
|
||||
--with-default-mirror-segtype=TYPE
|
||||
default mirror segtype: raid1/mirror [raid1]
|
||||
--with-default-raid10-segtype=TYPE
|
||||
@@ -1882,9 +1867,6 @@ Some influential environment variables:
|
||||
SACKPT_CFLAGS
|
||||
C compiler flags for SACKPT, overriding pkg-config
|
||||
SACKPT_LIBS linker flags for SACKPT, overriding pkg-config
|
||||
CUNIT_CFLAGS
|
||||
C compiler flags for CUNIT, overriding pkg-config
|
||||
CUNIT_LIBS linker flags for CUNIT, overriding pkg-config
|
||||
VALGRIND_CFLAGS
|
||||
C compiler flags for VALGRIND, overriding pkg-config
|
||||
VALGRIND_LIBS
|
||||
@@ -3183,7 +3165,6 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
AIO=yes
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
@@ -3203,7 +3184,6 @@ case "$host_os" in
|
||||
CLDNOWHOLEARCHIVE=
|
||||
LIB_SUFFIX=dylib
|
||||
DEVMAPPER=yes
|
||||
AIO=no
|
||||
ODIRECT=no
|
||||
DM_IOCTLS=no
|
||||
SELINUX=no
|
||||
@@ -6161,7 +6141,7 @@ fi
|
||||
|
||||
|
||||
for ac_header in assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
getopt.h inttypes.h langinfo.h libgen.h limits.h locale.h paths.h \
|
||||
getopt.h inttypes.h langinfo.h libaio.h libgen.h limits.h locale.h paths.h \
|
||||
signal.h stdarg.h stddef.h stdio.h stdlib.h string.h sys/file.h \
|
||||
sys/ioctl.h syslog.h sys/mman.h sys/param.h sys/resource.h sys/stat.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
@@ -6692,7 +6672,7 @@ fi
|
||||
|
||||
################################################################################
|
||||
for ac_func in ftruncate gethostname getpagesize gettimeofday localtime_r \
|
||||
memchr memset mkdir mkfifo munmap nl_langinfo realpath rmdir setenv \
|
||||
memchr memset mkdir mkfifo munmap nl_langinfo pselect realpath rmdir setenv \
|
||||
setlocale strcasecmp strchr strcspn strdup strerror strncasecmp strndup \
|
||||
strrchr strspn strstr strtol strtoul uname
|
||||
do :
|
||||
@@ -8391,78 +8371,6 @@ cat >>confdefs.h <<_ACEOF
|
||||
_ACEOF
|
||||
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable lvm1 fallback" >&5
|
||||
$as_echo_n "checking whether to enable lvm1 fallback... " >&6; }
|
||||
# Check whether --enable-lvm1_fallback was given.
|
||||
if test "${enable_lvm1_fallback+set}" = set; then :
|
||||
enableval=$enable_lvm1_fallback; LVM1_FALLBACK=$enableval
|
||||
else
|
||||
LVM1_FALLBACK=no
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LVM1_FALLBACK" >&5
|
||||
$as_echo "$LVM1_FALLBACK" >&6; }
|
||||
|
||||
if test "$LVM1_FALLBACK" = yes; then
|
||||
DEFAULT_FALLBACK_TO_LVM1=1
|
||||
|
||||
$as_echo "#define LVM1_FALLBACK 1" >>confdefs.h
|
||||
|
||||
else
|
||||
DEFAULT_FALLBACK_TO_LVM1=0
|
||||
fi
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define DEFAULT_FALLBACK_TO_LVM1 $DEFAULT_FALLBACK_TO_LVM1
|
||||
_ACEOF
|
||||
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for lvm1 metadata" >&5
|
||||
$as_echo_n "checking whether to include support for lvm1 metadata... " >&6; }
|
||||
|
||||
# Check whether --with-lvm1 was given.
|
||||
if test "${with_lvm1+set}" = set; then :
|
||||
withval=$with_lvm1; LVM1=$withval
|
||||
else
|
||||
LVM1=internal
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LVM1" >&5
|
||||
$as_echo "$LVM1" >&6; }
|
||||
|
||||
case "$LVM1" in
|
||||
none|shared) ;;
|
||||
internal)
|
||||
$as_echo "#define LVM1_INTERNAL 1" >>confdefs.h
|
||||
;;
|
||||
*) as_fn_error $? "--with-lvm1 parameter invalid" "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for GFS pool metadata" >&5
|
||||
$as_echo_n "checking whether to include support for GFS pool metadata... " >&6; }
|
||||
|
||||
# Check whether --with-pool was given.
|
||||
if test "${with_pool+set}" = set; then :
|
||||
withval=$with_pool; POOL=$withval
|
||||
else
|
||||
POOL=internal
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $POOL" >&5
|
||||
$as_echo "$POOL" >&6; }
|
||||
|
||||
case "$POOL" in
|
||||
none|shared) ;;
|
||||
internal)
|
||||
$as_echo "#define POOL_INTERNAL 1" >>confdefs.h
|
||||
;;
|
||||
*) as_fn_error $? "--with-pool parameter invalid" "$LINENO" 5
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for cluster locking" >&5
|
||||
$as_echo_n "checking whether to include support for cluster locking... " >&6; }
|
||||
@@ -8528,19 +8436,6 @@ $as_echo "#define MIRRORED_INTERNAL 1" >>confdefs.h
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include raid" >&5
|
||||
$as_echo_n "checking whether to include raid... " >&6; }
|
||||
|
||||
# Check whether --with-raid was given.
|
||||
if test "${with_raid+set}" = set; then :
|
||||
withval=$with_raid; RAID=$withval
|
||||
else
|
||||
RAID=internal
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RAID" >&5
|
||||
$as_echo "$RAID" >&6; }
|
||||
|
||||
|
||||
# Check whether --with-default-mirror-segtype was given.
|
||||
if test "${with_default_mirror_segtype+set}" = set; then :
|
||||
@@ -8557,15 +8452,10 @@ else
|
||||
DEFAULT_RAID10_SEGTYPE="raid10"
|
||||
fi
|
||||
|
||||
case "$RAID" in
|
||||
none) test "$DEFAULT_MIRROR_SEGTYPE" = "raid1" && DEFAULT_MIRROR_SEGTYPE="mirror"
|
||||
test "$DEFAULT_RAID10_SEGTYPE" = "raid10" && DEFAULT_RAID10_SEGTYPE="mirror" ;;
|
||||
shared) ;;
|
||||
internal)
|
||||
|
||||
|
||||
$as_echo "#define RAID_INTERNAL 1" >>confdefs.h
|
||||
;;
|
||||
*) as_fn_error $? "--with-raid parameter invalid" "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -11598,114 +11488,6 @@ $as_echo "$as_me: WARNING: GD.pm perl module is not installed" >&2;}
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable unit testing" >&5
|
||||
$as_echo_n "checking whether to enable unit testing... " >&6; }
|
||||
# Check whether --enable-testing was given.
|
||||
if test "${enable_testing+set}" = set; then :
|
||||
enableval=$enable_testing; TESTING=$enableval
|
||||
else
|
||||
TESTING=no
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TESTING" >&5
|
||||
$as_echo "$TESTING" >&6; }
|
||||
|
||||
if test "$TESTING" = yes; then
|
||||
pkg_config_init
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5
|
||||
$as_echo_n "checking for CUNIT... " >&6; }
|
||||
|
||||
if test -n "$CUNIT_CFLAGS"; then
|
||||
pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
if test -n "$CUNIT_LIBS"; then
|
||||
pkg_cv_CUNIT_LIBS="$CUNIT_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.0" 2>&1`
|
||||
else
|
||||
CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.0" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$CUNIT_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (cunit >= 2.0) were not met:
|
||||
|
||||
$CUNIT_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
Alternatively, you may set the environment variables CUNIT_CFLAGS
|
||||
and CUNIT_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details." "$LINENO" 5
|
||||
elif test $pkg_failed = untried; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
Alternatively, you may set the environment variables CUNIT_CFLAGS
|
||||
and CUNIT_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
|
||||
See \`config.log' for more details" "$LINENO" 5; }
|
||||
else
|
||||
CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS
|
||||
CUNIT_LIBS=$pkg_cv_CUNIT_LIBS
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
TESTSUITE_DATA='${datarootdir}/lvm2-testsuite'
|
||||
# double eval needed ${datarootdir} -> ${prefix}/share -> real path
|
||||
@@ -11832,67 +11614,6 @@ $as_echo "#define DEVMAPPER_SUPPORT 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use asynchronous I/O" >&5
|
||||
$as_echo_n "checking whether to asynchronous I/O... " >&6; }
|
||||
# Check whether --enable-aio was given.
|
||||
if test "${enable_aio+set}" = set; then :
|
||||
enableval=$enable_aio; AIO=$enableval
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AIO" >&5
|
||||
$as_echo "$AIO" >&6; }
|
||||
|
||||
if test "$AIO" = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for io_setup in -laio" >&5
|
||||
$as_echo_n "checking for io_setup in -laio... " >&6; }
|
||||
if ${ac_cv_lib_aio_io_setup+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-laio $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char io_setup ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return io_setup ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_aio_io_setup=yes
|
||||
else
|
||||
ac_cv_lib_aio_io_setup=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aio_io_setup" >&5
|
||||
$as_echo "$ac_cv_lib_aio_io_setup" >&6; }
|
||||
if test "x$ac_cv_lib_aio_io_setup" = xyes; then :
|
||||
|
||||
$as_echo "#define AIO_SUPPORT 1" >>confdefs.h
|
||||
|
||||
AIO_LIBS="-laio"
|
||||
AIO_SUPPORT=yes
|
||||
else
|
||||
AIO_LIBS=
|
||||
AIO_SUPPORT=no
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build LVMetaD" >&5
|
||||
$as_echo_n "checking whether to build LVMetaD... " >&6; }
|
||||
@@ -14141,8 +13862,6 @@ fi
|
||||
|
||||
################################################################################
|
||||
if [ \( "$LVM1" = shared -o "$POOL" = shared -o "$CLUSTER" = shared \
|
||||
-o "$SNAPSHOTS" = shared -o "$MIRRORS" = shared \
|
||||
-o "$RAID" = shared -o "$CACHE" = shared \
|
||||
\) -a "$STATIC_LINK" = yes ]; then
|
||||
as_fn_error $? "Features cannot be 'shared' when building statically" "$LINENO" 5
|
||||
fi
|
||||
@@ -15830,14 +15549,6 @@ _ACEOF
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15848,7 +15559,7 @@ _ACEOF
|
||||
|
||||
|
||||
################################################################################
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmeventd/plugins/vdo/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile device_mapper/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/locking/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/api/python_lvm_unit.py test/unit/Makefile tools/Makefile udev/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@@ -16556,6 +16267,7 @@ do
|
||||
"daemons/dmeventd/plugins/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/mirror/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/snapshot/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/thin/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/thin/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/vdo/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/vdo/Makefile" ;;
|
||||
"daemons/dmfilemapd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmfilemapd/Makefile" ;;
|
||||
"daemons/lvmdbusd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/Makefile" ;;
|
||||
"daemons/lvmdbusd/lvmdbusd") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/lvmdbusd" ;;
|
||||
@@ -16565,6 +16277,7 @@ do
|
||||
"daemons/lvmetad/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmetad/Makefile" ;;
|
||||
"daemons/lvmpolld/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmpolld/Makefile" ;;
|
||||
"daemons/lvmlockd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmlockd/Makefile" ;;
|
||||
"device_mapper/Makefile") CONFIG_FILES="$CONFIG_FILES device_mapper/Makefile" ;;
|
||||
"conf/Makefile") CONFIG_FILES="$CONFIG_FILES conf/Makefile" ;;
|
||||
"conf/example.conf") CONFIG_FILES="$CONFIG_FILES conf/example.conf" ;;
|
||||
"conf/lvmlocal.conf") CONFIG_FILES="$CONFIG_FILES conf/lvmlocal.conf" ;;
|
||||
@@ -16573,15 +16286,8 @@ do
|
||||
"include/.symlinks") CONFIG_FILES="$CONFIG_FILES include/.symlinks" ;;
|
||||
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
||||
"lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
|
||||
"lib/format1/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format1/Makefile" ;;
|
||||
"lib/format_pool/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format_pool/Makefile" ;;
|
||||
"lib/locking/Makefile") CONFIG_FILES="$CONFIG_FILES lib/locking/Makefile" ;;
|
||||
"lib/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES lib/mirror/Makefile" ;;
|
||||
"include/lvm-version.h") CONFIG_FILES="$CONFIG_FILES include/lvm-version.h" ;;
|
||||
"lib/raid/Makefile") CONFIG_FILES="$CONFIG_FILES lib/raid/Makefile" ;;
|
||||
"lib/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES lib/snapshot/Makefile" ;;
|
||||
"lib/thin/Makefile") CONFIG_FILES="$CONFIG_FILES lib/thin/Makefile" ;;
|
||||
"lib/cache_segtype/Makefile") CONFIG_FILES="$CONFIG_FILES lib/cache_segtype/Makefile" ;;
|
||||
"libdaemon/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/Makefile" ;;
|
||||
"libdaemon/client/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/client/Makefile" ;;
|
||||
"libdaemon/server/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/server/Makefile" ;;
|
||||
@@ -16622,12 +16328,10 @@ do
|
||||
"scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
|
||||
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
|
||||
"test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;;
|
||||
"test/api/python_lvm_unit.py") CONFIG_FILES="$CONFIG_FILES test/api/python_lvm_unit.py" ;;
|
||||
"test/unit/Makefile") CONFIG_FILES="$CONFIG_FILES test/unit/Makefile" ;;
|
||||
"tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
|
||||
"udev/Makefile") CONFIG_FILES="$CONFIG_FILES udev/Makefile" ;;
|
||||
"unit-tests/datastruct/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/datastruct/Makefile" ;;
|
||||
"unit-tests/regex/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/regex/Makefile" ;;
|
||||
"unit-tests/mm/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/mm/Makefile" ;;
|
||||
|
||||
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
||||
esac
|
||||
|
||||
@@ -39,7 +39,6 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
AIO=yes
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
@@ -59,7 +58,6 @@ case "$host_os" in
|
||||
CLDNOWHOLEARCHIVE=
|
||||
LIB_SUFFIX=dylib
|
||||
DEVMAPPER=yes
|
||||
AIO=no
|
||||
ODIRECT=no
|
||||
DM_IOCTLS=no
|
||||
SELINUX=no
|
||||
@@ -105,7 +103,7 @@ AC_HEADER_SYS_WAIT
|
||||
AC_HEADER_TIME
|
||||
|
||||
AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
getopt.h inttypes.h langinfo.h libgen.h limits.h locale.h paths.h \
|
||||
getopt.h inttypes.h langinfo.h libaio.h libgen.h limits.h locale.h paths.h \
|
||||
signal.h stdarg.h stddef.h stdio.h stdlib.h string.h sys/file.h \
|
||||
sys/ioctl.h syslog.h sys/mman.h sys/param.h sys/resource.h sys/stat.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
@@ -148,7 +146,7 @@ AX_GCC_BUILTIN([__builtin_clz])
|
||||
################################################################################
|
||||
dnl -- Check for functions
|
||||
AC_CHECK_FUNCS([ftruncate gethostname getpagesize gettimeofday localtime_r \
|
||||
memchr memset mkdir mkfifo munmap nl_langinfo realpath rmdir setenv \
|
||||
memchr memset mkdir mkfifo munmap nl_langinfo pselect realpath rmdir setenv \
|
||||
setlocale strcasecmp strchr strcspn strdup strerror strncasecmp strndup \
|
||||
strrchr strspn strstr strtol strtoul uname], , [AC_MSG_ERROR(bailing out)])
|
||||
AC_FUNC_ALLOCA
|
||||
@@ -284,58 +282,6 @@ esac
|
||||
AC_MSG_RESULT($MANGLING)
|
||||
AC_DEFINE_UNQUOTED([DEFAULT_DM_NAME_MANGLING], $mangling, [Define default name mangling behaviour])
|
||||
|
||||
################################################################################
|
||||
dnl -- LVM1 tool fallback option
|
||||
AC_MSG_CHECKING(whether to enable lvm1 fallback)
|
||||
AC_ARG_ENABLE(lvm1_fallback,
|
||||
AC_HELP_STRING([--enable-lvm1_fallback],
|
||||
[use this to fall back and use LVM1 binaries if
|
||||
device-mapper is missing from the kernel]),
|
||||
LVM1_FALLBACK=$enableval, LVM1_FALLBACK=no)
|
||||
AC_MSG_RESULT($LVM1_FALLBACK)
|
||||
|
||||
if test "$LVM1_FALLBACK" = yes; then
|
||||
DEFAULT_FALLBACK_TO_LVM1=1
|
||||
AC_DEFINE([LVM1_FALLBACK], 1, [Define to 1 if 'lvm' should fall back to using LVM1 binaries if device-mapper is missing from the kernel])
|
||||
else
|
||||
DEFAULT_FALLBACK_TO_LVM1=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_FALLBACK_TO_LVM1, [$DEFAULT_FALLBACK_TO_LVM1],
|
||||
[Fall back to LVM1 by default if device-mapper is missing from the kernel.])
|
||||
|
||||
################################################################################
|
||||
dnl -- format1 inclusion type
|
||||
AC_MSG_CHECKING(whether to include support for lvm1 metadata)
|
||||
AC_ARG_WITH(lvm1,
|
||||
AC_HELP_STRING([--with-lvm1=TYPE],
|
||||
[LVM1 metadata support: internal/shared/none [internal]]),
|
||||
LVM1=$withval, LVM1=internal)
|
||||
|
||||
AC_MSG_RESULT($LVM1)
|
||||
|
||||
case "$LVM1" in
|
||||
none|shared) ;;
|
||||
internal) AC_DEFINE([LVM1_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for LVM1 metadata.]) ;;
|
||||
*) AC_MSG_ERROR([--with-lvm1 parameter invalid]) ;;
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
dnl -- format_pool inclusion type
|
||||
AC_MSG_CHECKING(whether to include support for GFS pool metadata)
|
||||
AC_ARG_WITH(pool,
|
||||
AC_HELP_STRING([--with-pool=TYPE],
|
||||
[GFS pool read-only support: internal/shared/none [internal]]),
|
||||
POOL=$withval, POOL=internal)
|
||||
AC_MSG_RESULT($POOL)
|
||||
|
||||
case "$POOL" in
|
||||
none|shared) ;;
|
||||
internal) AC_DEFINE([POOL_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for GFS pool metadata.]) ;;
|
||||
*) AC_MSG_ERROR([--with-pool parameter invalid])
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
dnl -- cluster_locking inclusion type
|
||||
AC_MSG_CHECKING(whether to include support for cluster locking)
|
||||
@@ -386,13 +332,6 @@ esac
|
||||
|
||||
################################################################################
|
||||
dnl -- raid inclusion type
|
||||
AC_MSG_CHECKING(whether to include raid)
|
||||
AC_ARG_WITH(raid,
|
||||
AC_HELP_STRING([--with-raid=TYPE],
|
||||
[raid support: internal/shared/none [internal]]),
|
||||
RAID=$withval, RAID=internal)
|
||||
AC_MSG_RESULT($RAID)
|
||||
|
||||
AC_ARG_WITH(default-mirror-segtype,
|
||||
AC_HELP_STRING([--with-default-mirror-segtype=TYPE],
|
||||
[default mirror segtype: raid1/mirror [raid1]]),
|
||||
@@ -401,14 +340,9 @@ AC_ARG_WITH(default-raid10-segtype,
|
||||
AC_HELP_STRING([--with-default-raid10-segtype=TYPE],
|
||||
[default mirror segtype: raid10/mirror [raid10]]),
|
||||
DEFAULT_RAID10_SEGTYPE=$withval, DEFAULT_RAID10_SEGTYPE="raid10")
|
||||
case "$RAID" in
|
||||
none) test "$DEFAULT_MIRROR_SEGTYPE" = "raid1" && DEFAULT_MIRROR_SEGTYPE="mirror"
|
||||
test "$DEFAULT_RAID10_SEGTYPE" = "raid10" && DEFAULT_RAID10_SEGTYPE="mirror" ;;
|
||||
shared) ;;
|
||||
internal) AC_DEFINE([RAID_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for raid.]) ;;
|
||||
*) AC_MSG_ERROR([--with-raid parameter invalid]) ;;
|
||||
esac
|
||||
|
||||
AC_DEFINE([RAID_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for raid.])
|
||||
|
||||
AC_DEFINE_UNQUOTED([DEFAULT_MIRROR_SEGTYPE], ["$DEFAULT_MIRROR_SEGTYPE"],
|
||||
[Default segtype used for mirror volumes.])
|
||||
@@ -1069,20 +1003,6 @@ if test "$PROFILING" = yes; then
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable testing
|
||||
AC_MSG_CHECKING(whether to enable unit testing)
|
||||
AC_ARG_ENABLE(testing,
|
||||
AC_HELP_STRING([--enable-testing],
|
||||
[enable testing targets in the makefile]),
|
||||
TESTING=$enableval, TESTING=no)
|
||||
AC_MSG_RESULT($TESTING)
|
||||
|
||||
if test "$TESTING" = yes; then
|
||||
pkg_config_init
|
||||
PKG_CHECK_MODULES(CUNIT, cunit >= 2.0)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Set LVM2 testsuite data
|
||||
TESTSUITE_DATA='${datarootdir}/lvm2-testsuite'
|
||||
@@ -1124,24 +1044,6 @@ if test "$DEVMAPPER" = yes; then
|
||||
AC_DEFINE([DEVMAPPER_SUPPORT], 1, [Define to 1 to enable LVM2 device-mapper interaction.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Disable aio
|
||||
AC_MSG_CHECKING(whether to use asynchronous I/O)
|
||||
AC_ARG_ENABLE(aio,
|
||||
AC_HELP_STRING([--disable-aio],
|
||||
[disable asynchronous I/O]),
|
||||
AIO=$enableval)
|
||||
AC_MSG_RESULT($AIO)
|
||||
|
||||
if test "$AIO" = yes; then
|
||||
AC_CHECK_LIB(aio, io_setup,
|
||||
[AC_DEFINE([AIO_SUPPORT], 1, [Define to 1 if aio is available.])
|
||||
AIO_LIBS="-laio"
|
||||
AIO_SUPPORT=yes],
|
||||
[AIO_LIBS=
|
||||
AIO_SUPPORT=no ])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmetad
|
||||
AC_MSG_CHECKING(whether to build LVMetaD)
|
||||
@@ -1637,8 +1539,6 @@ AC_CHECK_LIB(dl, dlopen,
|
||||
################################################################################
|
||||
dnl -- Check for shared/static conflicts
|
||||
if [[ \( "$LVM1" = shared -o "$POOL" = shared -o "$CLUSTER" = shared \
|
||||
-o "$SNAPSHOTS" = shared -o "$MIRRORS" = shared \
|
||||
-o "$RAID" = shared -o "$CACHE" = shared \
|
||||
\) -a "$STATIC_LINK" = yes ]]; then
|
||||
AC_MSG_ERROR([Features cannot be 'shared' when building statically])
|
||||
fi
|
||||
@@ -2067,7 +1967,6 @@ AC_SUBST(DEFAULT_CACHE_SUBDIR)
|
||||
AC_SUBST(DEFAULT_DATA_ALIGNMENT)
|
||||
AC_SUBST(DEFAULT_DM_RUN_DIR)
|
||||
AC_SUBST(DEFAULT_LOCK_DIR)
|
||||
AC_SUBST(DEFAULT_FALLBACK_TO_LVM1)
|
||||
AC_SUBST(DEFAULT_MIRROR_SEGTYPE)
|
||||
AC_SUBST(DEFAULT_PID_DIR)
|
||||
AC_SUBST(DEFAULT_PROFILE_SUBDIR)
|
||||
@@ -2081,11 +1980,9 @@ AC_SUBST(DEFAULT_USE_LVMETAD)
|
||||
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
||||
AC_SUBST(DEFAULT_USE_LVMLOCKD)
|
||||
AC_SUBST(DEVMAPPER)
|
||||
AC_SUBST(AIO)
|
||||
AC_SUBST(DLM_CFLAGS)
|
||||
AC_SUBST(DLM_LIBS)
|
||||
AC_SUBST(DL_LIBS)
|
||||
AC_SUBST(AIO_LIBS)
|
||||
AC_SUBST(DMEVENTD_PATH)
|
||||
AC_SUBST(DM_LIB_PATCHLEVEL)
|
||||
AC_SUBST(ELDFLAGS)
|
||||
@@ -2100,8 +1997,6 @@ AC_SUBST(JOBS)
|
||||
AC_SUBST(LDDEPS)
|
||||
AC_SUBST(LIBS)
|
||||
AC_SUBST(LIB_SUFFIX)
|
||||
AC_SUBST(LVM1)
|
||||
AC_SUBST(LVM1_FALLBACK)
|
||||
AC_SUBST(LVM_VERSION)
|
||||
AC_SUBST(LVM_LIBAPI)
|
||||
AC_SUBST(LVM_MAJOR)
|
||||
@@ -2118,7 +2013,6 @@ AC_SUBST(OCF)
|
||||
AC_SUBST(OCFDIR)
|
||||
AC_SUBST(ODIRECT)
|
||||
AC_SUBST(PKGCONFIG)
|
||||
AC_SUBST(POOL)
|
||||
AC_SUBST(M_LIBS)
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PYTHON2)
|
||||
@@ -2134,7 +2028,6 @@ AC_SUBST(PYTHON2DIR)
|
||||
AC_SUBST(PYTHON3DIR)
|
||||
AC_SUBST(QUORUM_CFLAGS)
|
||||
AC_SUBST(QUORUM_LIBS)
|
||||
AC_SUBST(RAID)
|
||||
AC_SUBST(RT_LIBS)
|
||||
AC_SUBST(READLINE_LIBS)
|
||||
AC_SUBST(REPLICATORS)
|
||||
@@ -2150,7 +2043,6 @@ AC_SUBST(SYSTEMD_LIBS)
|
||||
AC_SUBST(SNAPSHOTS)
|
||||
AC_SUBST(STATICDIR)
|
||||
AC_SUBST(STATIC_LINK)
|
||||
AC_SUBST(TESTING)
|
||||
AC_SUBST(TESTSUITE_DATA)
|
||||
AC_SUBST(THIN)
|
||||
AC_SUBST(THIN_CHECK_CMD)
|
||||
@@ -2207,6 +2099,7 @@ daemons/dmeventd/plugins/raid/Makefile
|
||||
daemons/dmeventd/plugins/mirror/Makefile
|
||||
daemons/dmeventd/plugins/snapshot/Makefile
|
||||
daemons/dmeventd/plugins/thin/Makefile
|
||||
daemons/dmeventd/plugins/vdo/Makefile
|
||||
daemons/dmfilemapd/Makefile
|
||||
daemons/lvmdbusd/Makefile
|
||||
daemons/lvmdbusd/lvmdbusd
|
||||
@@ -2216,6 +2109,7 @@ daemons/lvmdbusd/path.py
|
||||
daemons/lvmetad/Makefile
|
||||
daemons/lvmpolld/Makefile
|
||||
daemons/lvmlockd/Makefile
|
||||
device_mapper/Makefile
|
||||
conf/Makefile
|
||||
conf/example.conf
|
||||
conf/lvmlocal.conf
|
||||
@@ -2224,15 +2118,8 @@ conf/metadata_profile_template.profile
|
||||
include/.symlinks
|
||||
include/Makefile
|
||||
lib/Makefile
|
||||
lib/format1/Makefile
|
||||
lib/format_pool/Makefile
|
||||
lib/locking/Makefile
|
||||
lib/mirror/Makefile
|
||||
include/lvm-version.h
|
||||
lib/raid/Makefile
|
||||
lib/snapshot/Makefile
|
||||
lib/thin/Makefile
|
||||
lib/cache_segtype/Makefile
|
||||
libdaemon/Makefile
|
||||
libdaemon/client/Makefile
|
||||
libdaemon/server/Makefile
|
||||
@@ -2273,12 +2160,10 @@ scripts/lvmdump.sh
|
||||
scripts/Makefile
|
||||
test/Makefile
|
||||
test/api/Makefile
|
||||
test/api/python_lvm_unit.py
|
||||
test/unit/Makefile
|
||||
tools/Makefile
|
||||
udev/Makefile
|
||||
unit-tests/datastruct/Makefile
|
||||
unit-tests/regex/Makefile
|
||||
unit-tests/mm/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -74,7 +74,7 @@ TARGETS = \
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS) -laio
|
||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
INSTALL_TARGETS = \
|
||||
|
||||
@@ -108,7 +108,6 @@ int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
|
||||
lock_flags = args[1];
|
||||
lockname = &args[2];
|
||||
/* Check to see if the VG is in use by LVM1 */
|
||||
status = do_check_lvm1(lockname);
|
||||
do_lock_vg(lock_cmd, lock_flags, lockname);
|
||||
break;
|
||||
|
||||
|
||||
@@ -887,7 +887,8 @@ static void main_loop(int cmd_timeout)
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGINT);
|
||||
sigaddset(&ss, SIGTERM);
|
||||
pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
|
||||
if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL))
|
||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
||||
/* Main loop */
|
||||
while (!quit) {
|
||||
fd_set in;
|
||||
@@ -1731,11 +1732,12 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
||||
SIGUSR2 (kills subthreads) */
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGUSR1);
|
||||
pthread_sigmask(SIG_BLOCK, &ss, NULL);
|
||||
|
||||
if (pthread_sigmask(SIG_BLOCK, &ss, NULL))
|
||||
log_warn("WARNING: Failed to block SIGUSR1.");
|
||||
sigdelset(&ss, SIGUSR1);
|
||||
sigaddset(&ss, SIGUSR2);
|
||||
pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
|
||||
if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL))
|
||||
log_warn("WARNING: Failed to unblock SIGUSR2.");
|
||||
|
||||
/* Loop around doing PRE and POST functions until the client goes away */
|
||||
while (!client->bits.localsock.finished) {
|
||||
@@ -1999,6 +2001,9 @@ static int send_message(void *buf, int msglen, const char *csid, int fd,
|
||||
return clops->cluster_send_message(buf, msglen, csid, errtext);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
/* Make sure it all goes */
|
||||
for (ptr = 0; ptr < msglen;) {
|
||||
if ((len = write(fd, (char*)buf + ptr, msglen - ptr)) <= 0) {
|
||||
|
||||
@@ -639,16 +639,6 @@ int post_lock_lv(unsigned char command, unsigned char lock_flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if a VG is in use by LVM1 so we don't stomp on it */
|
||||
int do_check_lvm1(const char *vgname)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = check_lvm1_vg_inactive(cmd, vgname);
|
||||
|
||||
return status == 1 ? 0 : EBUSY;
|
||||
}
|
||||
|
||||
int do_refresh_cache(void)
|
||||
{
|
||||
DEBUGLOG("Refreshing context\n");
|
||||
@@ -661,10 +651,9 @@ int do_refresh_cache(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_full_scan_done(0);
|
||||
init_ignore_suspended_devices(1);
|
||||
lvmcache_force_next_label_scan();
|
||||
lvmcache_label_scan(cmd);
|
||||
label_scan_destroy(cmd); /* destroys bcache (to close devs), keeps lvmcache */
|
||||
dm_pool_empty(cmd->mem);
|
||||
|
||||
pthread_mutex_unlock(&lvm_lock);
|
||||
@@ -807,8 +796,7 @@ static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
|
||||
if (level != _LOG_ERR && level != _LOG_FATAL)
|
||||
return;
|
||||
|
||||
strncpy(last_error, message, sizeof(last_error));
|
||||
last_error[sizeof(last_error)-1] = '\0';
|
||||
(void) dm_strncpy(last_error, message, sizeof(last_error));
|
||||
}
|
||||
|
||||
/* This checks some basic cluster-LVM configuration stuff */
|
||||
@@ -844,7 +832,7 @@ void lvm_do_backup(const char *vgname)
|
||||
|
||||
pthread_mutex_lock(&lvm_lock);
|
||||
|
||||
vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, WARN_PV_READ, &consistent);
|
||||
vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, 0, 0, WARN_PV_READ, &consistent);
|
||||
|
||||
if (vg && consistent)
|
||||
check_current_backup(vg);
|
||||
|
||||
@@ -25,7 +25,6 @@ extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
|
||||
extern const char *do_lock_query(char *resource);
|
||||
extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
|
||||
char *resource);
|
||||
extern int do_check_lvm1(const char *vgname);
|
||||
extern int do_refresh_cache(void);
|
||||
extern int init_clvm(struct dm_hash_table *excl_uuid);
|
||||
extern void destroy_lvm(void);
|
||||
|
||||
@@ -166,6 +166,9 @@ int cluster_send(struct clog_request *rq)
|
||||
{
|
||||
int r;
|
||||
int found = 0;
|
||||
#if CMIRROR_HAS_CHECKPOINT
|
||||
int count = 0;
|
||||
#endif
|
||||
struct iovec iov;
|
||||
struct clog_cpg *entry;
|
||||
|
||||
@@ -203,8 +206,6 @@ int cluster_send(struct clog_request *rq)
|
||||
|
||||
#if CMIRROR_HAS_CHECKPOINT
|
||||
do {
|
||||
int count = 0;
|
||||
|
||||
r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1);
|
||||
if (r != SA_AIS_ERR_TRY_AGAIN)
|
||||
break;
|
||||
@@ -1630,7 +1631,7 @@ int create_cluster_cpg(char *uuid, uint64_t luid)
|
||||
|
||||
size = ((strlen(uuid) + 1) > CPG_MAX_NAME_LENGTH) ?
|
||||
CPG_MAX_NAME_LENGTH : (strlen(uuid) + 1);
|
||||
strncpy(new->name.value, uuid, size);
|
||||
(void) dm_strncpy(new->name.value, uuid, size);
|
||||
new->name.length = (uint32_t)size;
|
||||
new->luid = luid;
|
||||
|
||||
|
||||
@@ -451,15 +451,19 @@ static int _clog_ctr(char *uuid, uint64_t luid,
|
||||
lc->skip_bit_warning = region_count;
|
||||
lc->disk_fd = -1;
|
||||
lc->log_dev_failed = 0;
|
||||
strncpy(lc->uuid, uuid, DM_UUID_LEN);
|
||||
if (!dm_strncpy(lc->uuid, uuid, DM_UUID_LEN)) {
|
||||
LOG_ERROR("Cannot use too long UUID %s.", uuid);
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
lc->luid = luid;
|
||||
|
||||
if (get_log(lc->uuid, lc->luid) ||
|
||||
get_pending_log(lc->uuid, lc->luid)) {
|
||||
LOG_ERROR("[%s/%" PRIu64 "u] Log already exists, unable to create.",
|
||||
SHORT_UUID(lc->uuid), lc->luid);
|
||||
dm_free(lc);
|
||||
return -EINVAL;
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dm_list_init(&lc->mark_list);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#define _LVM_CLOG_LOGGING_H
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "configure.h"
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -645,6 +645,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh)
|
||||
uuid = dm_task_get_uuid(dmt);
|
||||
|
||||
if (!strstr(dmevh->dso, "libdevmapper-event-lvm2thin.so") &&
|
||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2vdo.so") &&
|
||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") &&
|
||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") &&
|
||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so"))
|
||||
@@ -754,11 +755,10 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
|
||||
uuid = dm_task_get_uuid(dmt);
|
||||
|
||||
/* FIXME Distinguish errors connecting to daemon */
|
||||
if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
|
||||
&msg, dmevh->dso, uuid, dmevh->mask, 0)) {
|
||||
if ((ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
|
||||
&msg, dmevh->dso, uuid, dmevh->mask, 0))) {
|
||||
log_debug("%s: device not registered.", dm_task_get_name(dmt));
|
||||
ret = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2005, 2011 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -16,27 +16,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SUBDIRS += lvm2
|
||||
|
||||
ifneq ("@MIRRORS@", "none")
|
||||
SUBDIRS += mirror
|
||||
endif
|
||||
|
||||
ifneq ("@SNAPSHOTS@", "none")
|
||||
SUBDIRS += snapshot
|
||||
endif
|
||||
|
||||
ifneq ("@RAID@", "none")
|
||||
SUBDIRS += raid
|
||||
endif
|
||||
|
||||
ifneq ("@THIN@", "none")
|
||||
SUBDIRS += thin
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = lvm2 mirror snapshot raid thin
|
||||
endif
|
||||
SUBDIRS += lvm2 snapshot raid thin mirror vdo
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
@@ -44,3 +24,4 @@ snapshot: lvm2
|
||||
mirror: lvm2
|
||||
raid: lvm2
|
||||
thin: lvm2
|
||||
vdo: lvm2
|
||||
|
||||
@@ -31,6 +31,13 @@ static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int _register_count = 0;
|
||||
static struct dm_pool *_mem_pool = NULL;
|
||||
static void *_lvm_handle = NULL;
|
||||
static DM_LIST_INIT(_env_registry);
|
||||
|
||||
struct env_data {
|
||||
struct dm_list list;
|
||||
const char *cmd;
|
||||
const char *data;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("#lvm")
|
||||
|
||||
@@ -100,6 +107,7 @@ void dmeventd_lvm2_exit(void)
|
||||
lvm2_run(_lvm_handle, "_memlock_dec");
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
dm_list_init(&_env_registry);
|
||||
lvm2_exit(_lvm_handle);
|
||||
_lvm_handle = NULL;
|
||||
log_debug("lvm plugin exited.");
|
||||
@@ -124,6 +132,8 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
static char _internal_prefix[] = "_dmeventd_";
|
||||
char *vg = NULL, *lv = NULL, *layer;
|
||||
int r;
|
||||
struct env_data *env_data;
|
||||
const char *env = NULL;
|
||||
|
||||
if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) {
|
||||
log_error("Unable to determine VG name from %s.",
|
||||
@@ -137,18 +147,36 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
*layer = '\0';
|
||||
|
||||
if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) {
|
||||
dmeventd_lvm2_lock();
|
||||
/* output of internal command passed via env var */
|
||||
if (!dmeventd_lvm2_run(cmd))
|
||||
cmd = NULL;
|
||||
else if ((cmd = getenv(cmd)))
|
||||
cmd = dm_pool_strdup(mem, cmd); /* copy with lock */
|
||||
dmeventd_lvm2_unlock();
|
||||
/* check if ENVVAR wasn't already resolved */
|
||||
dm_list_iterate_items(env_data, &_env_registry)
|
||||
if (!strcmp(cmd, env_data->cmd)) {
|
||||
env = env_data->data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cmd) {
|
||||
log_error("Unable to find configured command.");
|
||||
return 0;
|
||||
if (!env) {
|
||||
/* run lvm2 command to find out setting value */
|
||||
dmeventd_lvm2_lock();
|
||||
if (!dmeventd_lvm2_run(cmd) ||
|
||||
!(env = getenv(cmd))) {
|
||||
dmeventd_lvm2_unlock();
|
||||
log_error("Unable to find configured command.");
|
||||
return 0;
|
||||
}
|
||||
/* output of internal command passed via env var */
|
||||
env = dm_pool_strdup(_mem_pool, env); /* copy with lock */
|
||||
dmeventd_lvm2_unlock();
|
||||
if (!env ||
|
||||
!(env_data = dm_pool_zalloc(_mem_pool, sizeof(*env_data))) ||
|
||||
!(env_data->cmd = dm_pool_strdup(_mem_pool, cmd))) {
|
||||
log_error("Unable to allocate env memory.");
|
||||
return 0;
|
||||
}
|
||||
env_data->data = env;
|
||||
/* add to ENVVAR registry */
|
||||
dm_list_add(&_env_registry, &env_data->list);
|
||||
}
|
||||
cmd = env;
|
||||
}
|
||||
|
||||
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
||||
|
||||
@@ -76,14 +76,17 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
if (status->insync_regions < status->total_regions) {
|
||||
if (!state->warned) {
|
||||
state->warned = 1;
|
||||
log_warn("WARNING: waiting for resynchronization to finish "
|
||||
"before initiating repair on RAID device %s.", device);
|
||||
}
|
||||
|
||||
goto out; /* Not yet done syncing with accessible devices */
|
||||
/*
|
||||
* Use the first event to run a repair ignoring any additonal ones.
|
||||
*
|
||||
* We presume lvconvert to do pre-repair
|
||||
* checks to avoid bloat in this plugin.
|
||||
*/
|
||||
if (!state->warned && status->insync_regions < status->total_regions) {
|
||||
state->warned = 1;
|
||||
log_warn("WARNING: waiting for resynchronization to finish "
|
||||
"before initiating repair on RAID device %s.", device);
|
||||
/* Fall through to allow lvconvert to run. */
|
||||
}
|
||||
|
||||
if (state->failed)
|
||||
|
||||
@@ -64,23 +64,23 @@ DM_EVENT_LOG_FN("thin")
|
||||
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
char val[3][36];
|
||||
char *env[] = { val[0], val[1], val[2], NULL };
|
||||
char val[16];
|
||||
int i;
|
||||
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1");
|
||||
(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1);
|
||||
|
||||
if (state->data_percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d",
|
||||
state->data_percent / DM_PERCENT_1);
|
||||
(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d",
|
||||
state->metadata_percent / DM_PERCENT_1);
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->data_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_DATA", val, 1);
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->metadata_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_METADATA", val, 1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
env[1] = NULL;
|
||||
log_debug("Error event processing.");
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ static int _run_command(struct dso_state *state)
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execve(state->argv[0], state->argv, env);
|
||||
execvp(state->argv[0], state->argv);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
|
||||
3
daemons/dmeventd/plugins/vdo/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/vdo/.exported_symbols
Normal file
@@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
||||
@@ -1,6 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -16,18 +15,22 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES =\
|
||||
disk-rep.c \
|
||||
format1.c \
|
||||
import-export.c \
|
||||
import-extents.c \
|
||||
layout.c \
|
||||
lvm1-label.c \
|
||||
vg_number.c
|
||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
|
||||
LIB_SHARED = liblvm2format1.$(LIB_SUFFIX)
|
||||
SOURCES = dmeventd_vdo.c
|
||||
|
||||
LIB_NAME = libdevmapper-event-lvm2vdo
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
CFLOW_LIST = $(SOURCES)
|
||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install: install_lvm2_plugin
|
||||
LIBS += -ldevmapper-event-lvm2 $(INTERNAL_LIBS)
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
||||
419
daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
Normal file
419
daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
Normal file
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "dmeventd_lvm.h"
|
||||
#include "libdevmapper-event.h"
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* First warning when VDO pool is 80% full. */
|
||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||
/* Run a check every 5%. */
|
||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||
/* Do not bother checking VDO pool is less than 50% full. */
|
||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
||||
|
||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
||||
|
||||
#define VDO_DEBUG 0
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
int percent_check;
|
||||
int percent;
|
||||
uint64_t known_data_size;
|
||||
unsigned fails;
|
||||
unsigned max_fails;
|
||||
int restore_sigset;
|
||||
sigset_t old_sigset;
|
||||
pid_t pid;
|
||||
char *argv[3];
|
||||
const char *cmd_str;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct vdo_status {
|
||||
uint64_t used_blocks;
|
||||
uint64_t total_blocks;
|
||||
};
|
||||
|
||||
static int _vdo_status_parse(const char *params, struct vdo_status *status)
|
||||
{
|
||||
if (sscanf(params, "%*s %*s %*s %*s %*s %" PRIu64 " %" PRIu64,
|
||||
&status->used_blocks,
|
||||
&status->total_blocks) < 2) {
|
||||
log_error("Failed to parse vdo params: %s.", params);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
DM_EVENT_LOG_FN("vdo")
|
||||
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
char val[16];
|
||||
int i;
|
||||
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1);
|
||||
|
||||
if (state->percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_VDO_POOL", val, 1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
log_debug("Error event processing.");
|
||||
}
|
||||
|
||||
log_verbose("Executing command: %s", state->cmd_str);
|
||||
|
||||
/* TODO:
|
||||
* Support parallel run of 'task' and it's waitpid maintainence
|
||||
* ATM we can't handle signaling of SIGALRM
|
||||
* as signalling is not allowed while 'process_event()' is running
|
||||
*/
|
||||
if (!(state->pid = fork())) {
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execvp(state->argv[0], state->argv);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
{
|
||||
#if VDO_DEBUG
|
||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
||||
#endif
|
||||
if (state->argv[0])
|
||||
return _run_command(state);
|
||||
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->fails = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if executed command has finished
|
||||
* Only 1 command may run */
|
||||
static int _wait_for_pid(struct dso_state *state)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (state->pid == -1)
|
||||
return 1;
|
||||
|
||||
if (!waitpid(state->pid, &status, WNOHANG))
|
||||
return 0;
|
||||
|
||||
/* Wait for finish */
|
||||
if (WIFEXITED(status)) {
|
||||
log_verbose("Child %d exited with status %d.",
|
||||
state->pid, WEXITSTATUS(status));
|
||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
||||
} else {
|
||||
if (WIFSIGNALED(status))
|
||||
log_verbose("Child %d was terminated with status %d.",
|
||||
state->pid, WTERMSIG(status));
|
||||
state->fails = 1;
|
||||
}
|
||||
|
||||
state->pid = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask event __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
struct dso_state *state = *user;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
int needs_policy = 0;
|
||||
struct dm_task *new_dmt = NULL;
|
||||
struct vdo_status status;
|
||||
|
||||
#if VDO_DEBUG
|
||||
log_debug("Watch for VDO %s:%.2f%%.", state->name,
|
||||
dm_percent_to_round_float(state->percent_check, 2));
|
||||
#endif
|
||||
if (!_wait_for_pid(state)) {
|
||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
||||
state->pid, state->cmd_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event & DM_EVENT_DEVICE_ERROR) {
|
||||
#if VDO_DEBUG
|
||||
log_debug("VDO event error.");
|
||||
#endif
|
||||
/* Error -> no need to check and do instant resize */
|
||||
state->percent = 0;
|
||||
if (_use_policy(dmt, state))
|
||||
goto out;
|
||||
|
||||
stack;
|
||||
|
||||
if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt)))
|
||||
goto_out;
|
||||
|
||||
/* Non-blocking status read */
|
||||
if (!dm_task_no_flush(new_dmt))
|
||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
||||
|
||||
if (!dm_task_run(new_dmt))
|
||||
goto_out;
|
||||
|
||||
dmt = new_dmt;
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
|
||||
if (!target_type || (strcmp(target_type, "vdo") != 0)) {
|
||||
log_error("Invalid target type.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_vdo_status_parse(params, &status)) {
|
||||
log_error("Failed to parse status.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
state->percent = dm_make_percent(status.used_blocks,
|
||||
status.total_blocks);
|
||||
|
||||
#if VDO_DEBUG
|
||||
log_debug("VDO %s status %.2f%% " FMTu64 "/" FMTu64 ".",
|
||||
state->name, dm_percent_to_round_float(state->percent, 2),
|
||||
status.used_blocks, status.total_blocks);
|
||||
#endif
|
||||
|
||||
/* VDO pool size had changed. Clear the threshold. */
|
||||
if (state->known_data_size != status.total_blocks) {
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
state->known_data_size = status.total_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger action when threshold boundary is exceeded.
|
||||
* Report 80% threshold warning when it's used above 80%.
|
||||
* Only 100% is exception as it cannot be surpased so policy
|
||||
* action is called for: >50%, >55% ... >95%, 100%
|
||||
*/
|
||||
if ((state->percent > WARNING_THRESH) &&
|
||||
(state->percent > state->percent_check))
|
||||
log_warn("WARNING: VDO %s %s is now %.2f%% full.",
|
||||
state->name, device,
|
||||
dm_percent_to_round_float(state->percent, 2));
|
||||
if (state->percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->percent > state->percent_check)
|
||||
needs_policy = 1;
|
||||
state->percent_check = (state->percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->percent_check == DM_PERCENT_100)
|
||||
state->percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
|
||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
||||
*/
|
||||
if (state->fails) {
|
||||
if (state->fails++ <= state->max_fails) {
|
||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
||||
state->fails - 1, state->max_fails);
|
||||
return;
|
||||
}
|
||||
if (state->max_fails < MAX_FAILS)
|
||||
state->max_fails <<= 1;
|
||||
state->fails = needs_policy = 1; /* Retry failing command */
|
||||
} else
|
||||
state->max_fails = 1; /* Reset on success */
|
||||
|
||||
/* FIXME: ATM nothing can be done, drop 0, once it becomes useful */
|
||||
if (0 && needs_policy)
|
||||
_use_policy(dmt, state);
|
||||
out:
|
||||
if (new_dmt)
|
||||
dm_task_destroy(new_dmt);
|
||||
}
|
||||
|
||||
/* Handle SIGCHLD for a thread */
|
||||
static void _sig_child(int signum __attribute__((unused)))
|
||||
{
|
||||
/* empty SIG_IGN */;
|
||||
}
|
||||
|
||||
/* Setup handler for SIGCHLD when executing external command
|
||||
* to get quick 'waitpid()' reaction
|
||||
* It will interrupt syscall just like SIGALRM and
|
||||
* invoke process_event().
|
||||
*/
|
||||
static void _init_thread_signals(struct dso_state *state)
|
||||
{
|
||||
struct sigaction act = { .sa_handler = _sig_child };
|
||||
sigset_t my_sigset;
|
||||
|
||||
sigemptyset(&my_sigset);
|
||||
|
||||
if (sigaction(SIGCHLD, &act, NULL))
|
||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
||||
else
|
||||
state->restore_sigset = 1;
|
||||
}
|
||||
|
||||
static void _restore_thread_signals(struct dso_state *state)
|
||||
{
|
||||
if (state->restore_sigset &&
|
||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid,
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
const char *cmd;
|
||||
char *str;
|
||||
char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
|
||||
const char *name = "pool";
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("vdo_pool_state", state))
|
||||
goto_bad;
|
||||
|
||||
state->cmd_str = "";
|
||||
|
||||
/* Search for command for LVM- prefixed devices only */
|
||||
cmd = (strncmp(uuid, "LVM-", 4) == 0) ? "_dmeventd_vdo_command" : "";
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), cmd, device))
|
||||
goto_bad;
|
||||
|
||||
if (strncmp(cmd_str, "lvm ", 4) == 0) {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
|
||||
log_error("Failed to copy lvm VDO command.");
|
||||
goto bad;
|
||||
}
|
||||
} else if (cmd_str[0] == '/') {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
|
||||
log_error("Failed to copy VDO command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Find last space before 'vg/lv' */
|
||||
if (!(str = strrchr(state->cmd_str, ' ')))
|
||||
goto inval;
|
||||
|
||||
if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
|
||||
str - state->cmd_str))) {
|
||||
log_error("Failed to copy command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
state->argv[1] = str + 1; /* 1 argument - vg/lv */
|
||||
_init_thread_signals(state);
|
||||
} else if (cmd[0] == 0) {
|
||||
state->name = "volume"; /* What to use with 'others?' */
|
||||
} else/* Unuspported command format */
|
||||
goto inval;
|
||||
|
||||
state->pid = -1;
|
||||
state->name = name;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring VDO %s %s.", name, device);
|
||||
|
||||
return 1;
|
||||
inval:
|
||||
log_error("Invalid command for monitoring: %s.", cmd_str);
|
||||
bad:
|
||||
log_error("Failed to monitor VDO %s %s.", name, device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
const char *name = state->name;
|
||||
int i;
|
||||
|
||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
||||
if (i == 0)
|
||||
/* Give it 2 seconds, then try to terminate & kill it */
|
||||
log_verbose("Child %d still not finished (%s) waiting.",
|
||||
state->pid, state->cmd_str);
|
||||
else if (i == 3) {
|
||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
||||
kill(state->pid, SIGINT);
|
||||
kill(state->pid, SIGTERM);
|
||||
} else if (i == 5) {
|
||||
log_warn("WARNING: Killing child %d.", state->pid);
|
||||
kill(state->pid, SIGKILL);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (state->pid != -1)
|
||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
||||
|
||||
_restore_thread_signals(state);
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring VDO %s %s.", name, device);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
#else
|
||||
# define MAJOR(x) major((x))
|
||||
# define MINOR(x) minor((x))
|
||||
# define MKDEV(x,y) makedev((x),(y))
|
||||
# define MKDEV(x,y) makedev((dev_t)(x),(dev_t)(y))
|
||||
#endif
|
||||
|
||||
/* limit to two updates/sec */
|
||||
@@ -629,10 +629,10 @@ check_unlinked:
|
||||
|
||||
static int _daemonise(struct filemap_monitor *fm)
|
||||
{
|
||||
pid_t pid = 0, sid;
|
||||
pid_t pid = 0;
|
||||
int fd;
|
||||
|
||||
if (!(sid = setsid())) {
|
||||
if (!setsid()) {
|
||||
_early_log("setsid failed.");
|
||||
return 0;
|
||||
}
|
||||
@@ -802,7 +802,7 @@ bad:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char * _mode_names[] = {
|
||||
static const char * const _mode_names[] = {
|
||||
"inode",
|
||||
"path"
|
||||
};
|
||||
@@ -827,8 +827,10 @@ int main(int argc, char **argv)
|
||||
"mode=%s, path=%s", fm.fd, fm.group_id,
|
||||
_mode_names[fm.mode], fm.path);
|
||||
|
||||
if (!_foreground && !_daemonise(&fm))
|
||||
if (!_foreground && !_daemonise(&fm)) {
|
||||
dm_free(fm.path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return _dmfilemapd(&fm);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ LVMDBUS_BUILDDIR_FILES = \
|
||||
|
||||
LVMDBUSD = lvmdbusd
|
||||
|
||||
CLEAN_DIRS += __pycache__
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
.PHONY: install_lvmdbusd
|
||||
|
||||
@@ -497,7 +497,7 @@ class Lv(LvCommon):
|
||||
# it is a thin lv
|
||||
if not dbo.IsThinVolume:
|
||||
if optional_size == 0:
|
||||
space = dbo.SizeBytes / 80
|
||||
space = dbo.SizeBytes // 80
|
||||
remainder = space % 512
|
||||
optional_size = space + 512 - remainder
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#define LVMETAD_TOKEN_UPDATE_IN_PROGRESS "update in progress"
|
||||
|
||||
#define LVMETAD_DISABLE_REASON_DIRECT "DIRECT"
|
||||
#define LVMETAD_DISABLE_REASON_LVM1 "LVM1"
|
||||
#define LVMETAD_DISABLE_REASON_DUPLICATES "DUPLICATES"
|
||||
#define LVMETAD_DISABLE_REASON_VGRESTORE "VGRESTORE"
|
||||
#define LVMETAD_DISABLE_REASON_REPAIR "REPAIR"
|
||||
|
||||
@@ -200,12 +200,12 @@ struct vg_info {
|
||||
#define GLFL_INVALID 0x00000001
|
||||
#define GLFL_DISABLE 0x00000002
|
||||
#define GLFL_DISABLE_REASON_DIRECT 0x00000004
|
||||
#define GLFL_DISABLE_REASON_LVM1 0x00000008
|
||||
/* 0x00000008 */
|
||||
#define GLFL_DISABLE_REASON_DUPLICATES 0x00000010
|
||||
#define GLFL_DISABLE_REASON_VGRESTORE 0x00000020
|
||||
#define GLFL_DISABLE_REASON_REPAIR 0x00000040
|
||||
|
||||
#define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_REPAIR | GLFL_DISABLE_REASON_LVM1 | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE)
|
||||
#define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_REPAIR | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE)
|
||||
|
||||
#define VGFL_INVALID 0x00000001
|
||||
|
||||
@@ -2369,8 +2369,6 @@ static response set_global_info(lvmetad_state *s, request r)
|
||||
reason_flags |= GLFL_DISABLE_REASON_DIRECT;
|
||||
if (strstr(reason, LVMETAD_DISABLE_REASON_REPAIR))
|
||||
reason_flags |= GLFL_DISABLE_REASON_REPAIR;
|
||||
if (strstr(reason, LVMETAD_DISABLE_REASON_LVM1))
|
||||
reason_flags |= GLFL_DISABLE_REASON_LVM1;
|
||||
if (strstr(reason, LVMETAD_DISABLE_REASON_DUPLICATES))
|
||||
reason_flags |= GLFL_DISABLE_REASON_DUPLICATES;
|
||||
if (strstr(reason, LVMETAD_DISABLE_REASON_VGRESTORE))
|
||||
@@ -2421,21 +2419,17 @@ static response set_global_info(lvmetad_state *s, request r)
|
||||
|
||||
static response get_global_info(lvmetad_state *s, request r)
|
||||
{
|
||||
char reason[REASON_BUF_SIZE];
|
||||
/* This buffer should be large enough to hold all the possible reasons. */
|
||||
char reason[REASON_BUF_SIZE] = { 0 };
|
||||
char flag_str[64];
|
||||
int pid;
|
||||
|
||||
/* This buffer should be large enough to hold all the possible reasons. */
|
||||
|
||||
memset(reason, 0, sizeof(reason));
|
||||
|
||||
pid = (int)daemon_request_int(r, "pid", 0);
|
||||
|
||||
if (s->flags & GLFL_DISABLE) {
|
||||
snprintf(reason, REASON_BUF_SIZE - 1, "%s%s%s%s%s",
|
||||
snprintf(reason, REASON_BUF_SIZE, "%s%s%s%s",
|
||||
(s->flags & GLFL_DISABLE_REASON_DIRECT) ? LVMETAD_DISABLE_REASON_DIRECT "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_REPAIR) ? LVMETAD_DISABLE_REASON_REPAIR "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_LVM1) ? LVMETAD_DISABLE_REASON_LVM1 "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_DUPLICATES) ? LVMETAD_DISABLE_REASON_DUPLICATES "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_VGRESTORE) ? LVMETAD_DISABLE_REASON_VGRESTORE "," : "");
|
||||
}
|
||||
@@ -2531,10 +2525,8 @@ inval:
|
||||
|
||||
info = dm_hash_lookup(s->vgid_to_info, uuid);
|
||||
if (!info) {
|
||||
info = malloc(sizeof(struct vg_info));
|
||||
if (!info)
|
||||
if (!(info = dm_zalloc(sizeof(struct vg_info))))
|
||||
goto bad;
|
||||
memset(info, 0, sizeof(struct vg_info));
|
||||
if (!dm_hash_insert(s->vgid_to_info, uuid, (void*)info))
|
||||
goto bad;
|
||||
}
|
||||
@@ -2677,6 +2669,7 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
int pid;
|
||||
int cache_lock = 0;
|
||||
int info_lock = 0;
|
||||
uint64_t timegap = 0;
|
||||
|
||||
rq = daemon_request_str(r, "request", "NONE");
|
||||
token = daemon_request_str(r, "token", "NONE");
|
||||
@@ -2705,9 +2698,8 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
if (!prev_in_progress && this_in_progress) {
|
||||
/* New update is starting (filter token is replaced by update token) */
|
||||
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
state->update_begin = _monotonic_seconds();
|
||||
state->update_timeout = update_timeout;
|
||||
state->update_pid = pid;
|
||||
@@ -2720,23 +2712,32 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
state->update_cmd);
|
||||
|
||||
} else if (prev_in_progress && this_in_progress) {
|
||||
timegap = _monotonic_seconds() - state->update_begin;
|
||||
if (timegap < state->update_timeout) {
|
||||
pthread_mutex_unlock(&state->token_lock);
|
||||
return daemon_reply_simple("token_updating",
|
||||
"expected = %s", state->token,
|
||||
"update_pid = " FMTd64, (int64_t)state->update_pid,
|
||||
"reason = %s", "another command has populated the cache",
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Current update is cancelled and replaced by a new update */
|
||||
|
||||
DEBUGLOG(state, "token_update replacing pid %d begin %llu len %d cmd %s",
|
||||
WARN(state, "token_update replacing pid %d begin %llu len %d cmd %s",
|
||||
state->update_pid,
|
||||
(unsigned long long)state->update_begin,
|
||||
(int)(_monotonic_seconds() - state->update_begin),
|
||||
(int)(timegap),
|
||||
state->update_cmd);
|
||||
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
state->update_begin = _monotonic_seconds();
|
||||
state->update_timeout = update_timeout;
|
||||
state->update_pid = pid;
|
||||
strncpy(state->update_cmd, cmd, CMD_NAME_SIZE - 1);
|
||||
|
||||
DEBUGLOG(state, "token_update begin %llu timeout %d pid %d cmd %s",
|
||||
WARN(state, "token_update begin %llu timeout %d pid %d cmd %s",
|
||||
(unsigned long long)state->update_begin,
|
||||
state->update_timeout,
|
||||
state->update_pid,
|
||||
@@ -2747,7 +2748,7 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
|
||||
if (state->update_pid != pid) {
|
||||
/* If a pid doing update was cancelled, ignore its token update at the end. */
|
||||
DEBUGLOG(state, "token_update ignored from cancelled update pid %d", pid);
|
||||
WARN(state, "token_update ignored from cancelled update pid %d", pid);
|
||||
pthread_mutex_unlock(&state->token_lock);
|
||||
|
||||
return daemon_reply_simple("token_mismatch",
|
||||
@@ -2758,13 +2759,12 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
NULL);
|
||||
}
|
||||
|
||||
DEBUGLOG(state, "token_update end len %d pid %d new token %s",
|
||||
WARN(state, "token_update end len %d pid %d new token %s",
|
||||
(int)(_monotonic_seconds() - state->update_begin),
|
||||
state->update_pid, token);
|
||||
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
state->update_begin = 0;
|
||||
state->update_timeout = 0;
|
||||
state->update_pid = 0;
|
||||
@@ -2964,7 +2964,7 @@ static void usage(const char *prog, FILE *file)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signed char opt;
|
||||
struct timeval timeout;
|
||||
struct timespec timeout;
|
||||
daemon_idle di = { .ptimeout = &timeout };
|
||||
lvmetad_state ls = { .log_config = "" };
|
||||
daemon_state s = {
|
||||
|
||||
@@ -27,6 +27,8 @@ ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||
LOCK_LIBS += -ldlm_lt
|
||||
endif
|
||||
|
||||
SOURCES2 = lvmlockctl.c
|
||||
|
||||
TARGETS = lvmlockd lvmlockctl
|
||||
|
||||
.PHONY: install_lvmlockd
|
||||
|
||||
@@ -1009,6 +1009,8 @@ static void add_work_action(struct action *act)
|
||||
pthread_mutex_unlock(&worker_mutex);
|
||||
}
|
||||
|
||||
#define ERR_LVMETAD_NOT_RUNNING -200
|
||||
|
||||
static daemon_reply send_lvmetad(const char *id, ...)
|
||||
{
|
||||
daemon_reply reply;
|
||||
@@ -1029,9 +1031,9 @@ retry:
|
||||
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0) {
|
||||
err = lvmetad_handle.error ?: lvmetad_handle.socket_fd;
|
||||
pthread_mutex_unlock(&lvmetad_mutex);
|
||||
log_error("lvmetad_open reconnect error %d", err);
|
||||
log_debug("lvmetad_open reconnect error %d", err);
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
reply.error = err;
|
||||
reply.error = ERR_LVMETAD_NOT_RUNNING;
|
||||
va_end(ap);
|
||||
return reply;
|
||||
} else {
|
||||
@@ -1265,6 +1267,15 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
* caches, and tell lvmetad to set global invalid to 0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* lvmetad not running:
|
||||
* Even if we have not previously found lvmetad running,
|
||||
* we attempt to connect and invalidate in case it has
|
||||
* been started while lvmlockd is running. We don't
|
||||
* want to allow lvmetad to be used with invalid data if
|
||||
* it happens to be enabled and started after lvmlockd.
|
||||
*/
|
||||
|
||||
if (inval_meta && (r->type == LD_RT_VG)) {
|
||||
daemon_reply reply;
|
||||
char *uuid;
|
||||
@@ -1284,8 +1295,10 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
"version = " FMTd64, (int64_t)new_version,
|
||||
NULL);
|
||||
|
||||
if (reply.error || strcmp(daemon_reply_str(reply, "response", ""), "OK"))
|
||||
log_error("set_vg_info in lvmetad failed %d", reply.error);
|
||||
if (reply.error || strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
|
||||
if (reply.error != ERR_LVMETAD_NOT_RUNNING)
|
||||
log_error("set_vg_info in lvmetad failed %d", reply.error);
|
||||
}
|
||||
daemon_reply_destroy(reply);
|
||||
}
|
||||
|
||||
@@ -1300,8 +1313,10 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
"global_invalid = " FMTd64, INT64_C(1),
|
||||
NULL);
|
||||
|
||||
if (reply.error || strcmp(daemon_reply_str(reply, "response", ""), "OK"))
|
||||
log_error("set_global_info in lvmetad failed %d", reply.error);
|
||||
if (reply.error || strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
|
||||
if (reply.error != ERR_LVMETAD_NOT_RUNNING)
|
||||
log_error("set_global_info in lvmetad failed %d", reply.error);
|
||||
}
|
||||
daemon_reply_destroy(reply);
|
||||
}
|
||||
|
||||
@@ -2800,6 +2815,9 @@ static int add_lockspace_thread(const char *ls_name,
|
||||
if (ls2->thread_stop) {
|
||||
log_debug("add_lockspace_thread %s exists and stopping", ls->name);
|
||||
rv = -EAGAIN;
|
||||
} else if (!ls2->create_fail && !ls2->create_done) {
|
||||
log_debug("add_lockspace_thread %s exists and starting", ls->name);
|
||||
rv = -ESTARTING;
|
||||
} else {
|
||||
log_debug("add_lockspace_thread %s exists", ls->name);
|
||||
rv = -EEXIST;
|
||||
@@ -3041,7 +3059,7 @@ static int count_lockspace_starting(uint32_t client_id)
|
||||
|
||||
pthread_mutex_lock(&lockspaces_mutex);
|
||||
list_for_each_entry(ls, &lockspaces, list) {
|
||||
if (ls->start_client_id != client_id)
|
||||
if (client_id && (ls->start_client_id != client_id))
|
||||
continue;
|
||||
|
||||
if (!ls->create_done && !ls->create_fail) {
|
||||
@@ -3442,7 +3460,7 @@ static void *worker_thread_main(void *arg_in)
|
||||
add_client_result(act);
|
||||
|
||||
} else if (act->op == LD_OP_START_WAIT) {
|
||||
act->result = count_lockspace_starting(act->client_id);
|
||||
act->result = count_lockspace_starting(0);
|
||||
if (!act->result)
|
||||
add_client_result(act);
|
||||
else
|
||||
@@ -3476,7 +3494,7 @@ static void *worker_thread_main(void *arg_in)
|
||||
list_for_each_entry_safe(act, safe, &delayed_list, list) {
|
||||
if (act->op == LD_OP_START_WAIT) {
|
||||
log_debug("work delayed start_wait for client %u", act->client_id);
|
||||
act->result = count_lockspace_starting(act->client_id);
|
||||
act->result = count_lockspace_starting(0);
|
||||
if (!act->result) {
|
||||
list_del(&act->list);
|
||||
add_client_result(act);
|
||||
@@ -5848,7 +5866,7 @@ static int main_loop(daemon_state *ds_arg)
|
||||
pthread_mutex_init(&lvmetad_mutex, NULL);
|
||||
lvmetad_handle = lvmetad_open(NULL);
|
||||
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0)
|
||||
log_error("lvmetad_open error %d", lvmetad_handle.error);
|
||||
log_debug("lvmetad_open error %d", lvmetad_handle.error);
|
||||
else
|
||||
lvmetad_connected = 1;
|
||||
|
||||
@@ -5856,8 +5874,13 @@ static int main_loop(daemon_state *ds_arg)
|
||||
* Attempt to rejoin lockspaces and adopt locks from a previous
|
||||
* instance of lvmlockd that left behind lockspaces/locks.
|
||||
*/
|
||||
if (adopt_opt)
|
||||
adopt_locks();
|
||||
if (adopt_opt) {
|
||||
/* FIXME: implement this without lvmetad */
|
||||
if (!lvmetad_connected)
|
||||
log_error("Cannot adopt locks without lvmetad running.");
|
||||
else
|
||||
adopt_locks();
|
||||
}
|
||||
|
||||
while (1) {
|
||||
rv = poll(pollfd, pollfd_maxi + 1, -1);
|
||||
|
||||
@@ -699,7 +699,7 @@ int lm_hosts_dlm(struct lockspace *ls, int notify)
|
||||
return 0;
|
||||
|
||||
memset(ls_nodes_path, 0, sizeof(ls_nodes_path));
|
||||
snprintf(ls_nodes_path, PATH_MAX-1, "%s/%s/nodes",
|
||||
snprintf(ls_nodes_path, PATH_MAX, "%s/%s/nodes",
|
||||
DLM_LOCKSPACES_PATH, ls->name);
|
||||
|
||||
if (!(ls_dir = opendir(ls_nodes_path)))
|
||||
|
||||
@@ -294,6 +294,36 @@ out:
|
||||
return host_id;
|
||||
}
|
||||
|
||||
/* Prepare valid /dev/mapper/vgname-lvname with all the mangling */
|
||||
static int build_dm_path(char *path, size_t path_len,
|
||||
const char *vg_name, const char *lv_name)
|
||||
{
|
||||
struct dm_pool *mem;
|
||||
char *dm_name;
|
||||
int rv = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("namepool", 1024))) {
|
||||
log_error("Failed to create mempool.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!(dm_name = dm_build_dm_name(mem, vg_name, lv_name, NULL))) {
|
||||
log_error("Failed to build dm name for %s/%s.", vg_name, lv_name);
|
||||
rv = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((dm_snprintf(path, path_len, "%s/%s", dm_dir(), dm_name) < 0)) {
|
||||
log_error("Failed to create path %s/%s.", dm_dir(), dm_name);
|
||||
rv = -EINVAL;
|
||||
}
|
||||
|
||||
fail:
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* vgcreate
|
||||
*
|
||||
@@ -336,7 +366,8 @@ int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_ar
|
||||
if (strlen(lock_lv_name) + strlen(lock_args_version) + 2 > MAX_ARGS)
|
||||
return -EARGS;
|
||||
|
||||
snprintf(disk.path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
if ((rv = build_dm_path(disk.path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
|
||||
log_debug("S %s init_vg_san path %s", ls_name, disk.path);
|
||||
|
||||
@@ -513,7 +544,8 @@ int lm_init_lv_sanlock(char *ls_name, char *vg_name, char *lv_name,
|
||||
|
||||
strncpy(rd.rs.lockspace_name, ls_name, SANLK_NAME_LEN);
|
||||
rd.rs.num_disks = 1;
|
||||
snprintf(rd.rs.disks[0].path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
if ((rv = build_dm_path(rd.rs.disks[0].path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
|
||||
align_size = sanlock_align(&rd.rs.disks[0]);
|
||||
if (align_size <= 0) {
|
||||
@@ -612,7 +644,8 @@ int lm_rename_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_
|
||||
return rv;
|
||||
}
|
||||
|
||||
snprintf(disk.path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
if ((rv = build_dm_path(disk.path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
|
||||
log_debug("S %s rename_vg_san path %s", ls_name, disk.path);
|
||||
|
||||
@@ -1069,10 +1102,10 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
|
||||
* and appending "lockctl" to get /path/to/lvmlockctl.
|
||||
*/
|
||||
memset(killpath, 0, sizeof(killpath));
|
||||
snprintf(killpath, SANLK_PATH_LEN - 1, "%slockctl", LVM_PATH);
|
||||
snprintf(killpath, SANLK_PATH_LEN, "%slockctl", LVM_PATH);
|
||||
|
||||
memset(killargs, 0, sizeof(killargs));
|
||||
snprintf(killargs, SANLK_PATH_LEN - 1, "--kill %s", ls->vg_name);
|
||||
snprintf(killargs, SANLK_PATH_LEN, "--kill %s", ls->vg_name);
|
||||
|
||||
rv = check_args_version(ls->vg_args, VG_LOCK_ARGS_MAJOR);
|
||||
if (rv < 0) {
|
||||
@@ -1088,8 +1121,8 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(disk_path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s",
|
||||
ls->vg_name, lock_lv_name);
|
||||
if ((ret = build_dm_path(disk_path, SANLK_PATH_LEN, ls->vg_name, lock_lv_name)))
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* When a vg is started, the internal sanlock lv should be
|
||||
|
||||
@@ -915,7 +915,7 @@ int main(int argc, char *argv[])
|
||||
int option_index = 0;
|
||||
int client = 0, server = 0;
|
||||
unsigned action = ACTION_MAX;
|
||||
struct timeval timeout;
|
||||
struct timespec timeout;
|
||||
daemon_idle di = { .ptimeout = &timeout };
|
||||
struct lvmpolld_state ls = { .log_config = "" };
|
||||
daemon_state s = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -14,11 +14,10 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = cache.c
|
||||
|
||||
LIB_SHARED = liblvm2cache.$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
SOURCES=\
|
||||
vdo/status.c
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install: install_lvm2_plugin
|
||||
LIB_NAME = libdevicemapper
|
||||
LIB_STATIC = $(LIB_NAME).a
|
||||
248
device_mapper/vdo/status.c
Normal file
248
device_mapper/vdo/status.c
Normal file
@@ -0,0 +1,248 @@
|
||||
#include "target.h"
|
||||
|
||||
// For DM_ARRAY_SIZE!
|
||||
#include "libdevmapper.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static char *_tok_cpy(const char *b, const char *e)
|
||||
{
|
||||
char *new = malloc((e - b) + 1);
|
||||
char *ptr = new;
|
||||
|
||||
if (new) {
|
||||
while (b != e)
|
||||
*ptr++ = *b++;
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static bool _tok_eq(const char *b, const char *e, const char *str)
|
||||
{
|
||||
while (b != e) {
|
||||
if (!*str || *b != *str)
|
||||
return false;
|
||||
|
||||
b++;
|
||||
str++;
|
||||
}
|
||||
|
||||
return !*str;
|
||||
}
|
||||
|
||||
static bool _parse_operating_mode(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_operating_mode mode;
|
||||
} _table[] = {
|
||||
{"recovering", VDO_MODE_RECOVERING},
|
||||
{"read-only", VDO_MODE_READ_ONLY},
|
||||
{"normal", VDO_MODE_NORMAL}
|
||||
};
|
||||
|
||||
enum vdo_operating_mode *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_compression_state(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_compression_state state;
|
||||
} _table[] = {
|
||||
{"online", VDO_COMPRESSION_ONLINE},
|
||||
{"offline", VDO_COMPRESSION_OFFLINE}
|
||||
};
|
||||
|
||||
enum vdo_compression_state *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_recovering(const char *b, const char *e, void *context)
|
||||
{
|
||||
bool *r = context;
|
||||
|
||||
if (_tok_eq(b, e, "recovering"))
|
||||
*r = true;
|
||||
|
||||
else if (_tok_eq(b, e, "-"))
|
||||
*r = false;
|
||||
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _parse_index_state(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_index_state state;
|
||||
} _table[] = {
|
||||
{"error", VDO_INDEX_ERROR},
|
||||
{"closed", VDO_INDEX_CLOSED},
|
||||
{"opening", VDO_INDEX_OPENING},
|
||||
{"closing", VDO_INDEX_CLOSING},
|
||||
{"offline", VDO_INDEX_OFFLINE},
|
||||
{"online", VDO_INDEX_ONLINE},
|
||||
{"unknown", VDO_INDEX_UNKNOWN}
|
||||
};
|
||||
|
||||
enum vdo_index_state *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_uint64(const char *b, const char *e, void *context)
|
||||
{
|
||||
uint64_t *r = context, n;
|
||||
|
||||
n = 0;
|
||||
while (b != e) {
|
||||
if (!isdigit(*b))
|
||||
return false;
|
||||
|
||||
n = (n * 10) + (*b - '0');
|
||||
b++;
|
||||
}
|
||||
|
||||
*r = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *_eat_space(const char *b, const char *e)
|
||||
{
|
||||
while (b != e && isspace(*b))
|
||||
b++;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static const char *_next_tok(const char *b, const char *e)
|
||||
{
|
||||
const char *te = b;
|
||||
while (te != e && !isspace(*te))
|
||||
te++;
|
||||
|
||||
return te == b ? NULL : te;
|
||||
}
|
||||
|
||||
static void _set_error(struct vdo_status_parse_result *result, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
static void _set_error(struct vdo_status_parse_result *result, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(result->error, sizeof(result->error), fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static bool _parse_field(const char **b, const char *e,
|
||||
bool (*p_fn)(const char *, const char *, void *),
|
||||
void *field, const char *field_name,
|
||||
struct vdo_status_parse_result *result)
|
||||
{
|
||||
const char *te;
|
||||
|
||||
te = _next_tok(*b, e);
|
||||
if (!te) {
|
||||
_set_error(result, "couldn't get token for '%s'", field_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p_fn(*b, te, field)) {
|
||||
_set_error(result, "couldn't parse '%s'", field_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
*b = _eat_space(te, e);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool vdo_status_parse(const char *input, struct vdo_status_parse_result *result)
|
||||
{
|
||||
const char *b = b = input;
|
||||
const char *e = input + strlen(input);
|
||||
const char *te;
|
||||
struct vdo_status *s = malloc(sizeof(*s));
|
||||
|
||||
if (!s) {
|
||||
_set_error(result, "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
b = _eat_space(b, e);
|
||||
te = _next_tok(b, e);
|
||||
if (!te) {
|
||||
_set_error(result, "couldn't get token for device");
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
s->device = _tok_cpy(b, te);
|
||||
if (!s->device) {
|
||||
_set_error(result, "out of memory");
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
b = _eat_space(te, e);
|
||||
|
||||
#define XX(p, f, fn) if (!_parse_field(&b, e, p, f, fn, result)) goto bad;
|
||||
XX(_parse_operating_mode, &s->operating_mode, "operating mode");
|
||||
XX(_parse_recovering, &s->recovering, "recovering");
|
||||
XX(_parse_index_state, &s->index_state, "index state");
|
||||
XX(_parse_compression_state, &s->compression_state, "compression state");
|
||||
XX(_parse_uint64, &s->used_blocks, "used blocks");
|
||||
XX(_parse_uint64, &s->total_blocks, "total blocks");
|
||||
#undef XX
|
||||
|
||||
if (b != e) {
|
||||
_set_error(result, "too many tokens");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
result->status = s;
|
||||
return true;
|
||||
|
||||
bad:
|
||||
free(s->device);
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
68
device_mapper/vdo/target.h
Normal file
68
device_mapper/vdo/target.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_MAPPER_VDO_TARGET_H
|
||||
#define DEVICE_MAPPER_VDO_TARGET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
enum vdo_operating_mode {
|
||||
VDO_MODE_RECOVERING,
|
||||
VDO_MODE_READ_ONLY,
|
||||
VDO_MODE_NORMAL
|
||||
};
|
||||
|
||||
enum vdo_compression_state {
|
||||
VDO_COMPRESSION_ONLINE,
|
||||
VDO_COMPRESSION_OFFLINE
|
||||
};
|
||||
|
||||
enum vdo_index_state {
|
||||
VDO_INDEX_ERROR,
|
||||
VDO_INDEX_CLOSED,
|
||||
VDO_INDEX_OPENING,
|
||||
VDO_INDEX_CLOSING,
|
||||
VDO_INDEX_OFFLINE,
|
||||
VDO_INDEX_ONLINE,
|
||||
VDO_INDEX_UNKNOWN
|
||||
};
|
||||
|
||||
struct vdo_status {
|
||||
char *device;
|
||||
enum vdo_operating_mode operating_mode;
|
||||
bool recovering;
|
||||
enum vdo_index_state index_state;
|
||||
enum vdo_compression_state compression_state;
|
||||
uint64_t used_blocks;
|
||||
uint64_t total_blocks;
|
||||
};
|
||||
|
||||
void vdo_status_destroy(struct vdo_status *s);
|
||||
|
||||
#define VDO_MAX_ERROR 256
|
||||
|
||||
struct vdo_status_parse_result {
|
||||
char error[VDO_MAX_ERROR];
|
||||
struct vdo_status *status;
|
||||
};
|
||||
|
||||
// Parses the status line from the kernel target.
|
||||
bool vdo_status_parse(const char *input, struct vdo_status_parse_result *result);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -1,215 +0,0 @@
|
||||
Introducing asynchronous I/O to LVM
|
||||
===================================
|
||||
|
||||
Issuing I/O asynchronously means instructing the kernel to perform specific
|
||||
I/O and return immediately without waiting for it to complete. The data
|
||||
is collected from the kernel later.
|
||||
|
||||
Advantages
|
||||
----------
|
||||
|
||||
A1. While waiting for the I/O to happen, the program could perform other
|
||||
operations.
|
||||
|
||||
A2. When LVM is searching for its Physical Volumes, it issues a small amount of
|
||||
I/O to a large number of disks. If this was issued in parallel the overall
|
||||
runtime might be shorter while there should be little effect on the cpu time.
|
||||
|
||||
A3. If more than one timeout occurs when accessing any devices, these can be
|
||||
taken in parallel, again reducing the runtime. This applies globally,
|
||||
not just while the code is searching for Physical Volumes, so reading,
|
||||
writing and committing the metadata may occasionally benefit too to some
|
||||
extent and there are probably maintenance advantages in using the same
|
||||
method of I/O throughout the main body of the code.
|
||||
|
||||
A4. By introducing a simple callback function mechanism, the conversion can be
|
||||
performed largely incrementally by first refactoring and continuing to
|
||||
use synchronous I/O with the callbacks performed immediately. This allows the
|
||||
callbacks to be introduced without changing the running sequence of the code
|
||||
initially. Future projects could refactor some of the calling sites to
|
||||
simplify the code structure and even eliminate some of the nesting.
|
||||
This allows each part of what might ultimately amount to a large change to be
|
||||
introduced and tested independently.
|
||||
|
||||
|
||||
Disadvantages
|
||||
-------------
|
||||
|
||||
D1. The resulting code may be more complex with more failure modes to
|
||||
handle. Mitigate by thorough auditing and testing, rolling out
|
||||
gradually, and offering a simple switch to revert to the old behaviour.
|
||||
|
||||
D2. The linux asynchronous I/O implementation is less mature than
|
||||
its synchronous I/O implementation and might show up problems that
|
||||
depend on the version of the kernel or library used. Fixes or
|
||||
workarounds for some of these might require kernel changes. For
|
||||
example, there are suggestions that despite being supposedly async,
|
||||
there are still cases where system calls can block. There might be
|
||||
resource dependencies on other processes running on the system that make
|
||||
it unsuitable for use while any devices are suspended. Mitigation
|
||||
as for D1.
|
||||
|
||||
D3. The error handling within callbacks becomes more complicated.
|
||||
However we know that existing call paths can already sometimes discard
|
||||
errors, sometimes deliberately, sometimes not, so this aspect is in need
|
||||
of a complete review anyway and the new approach will make the error
|
||||
handling more transparent. Aim initially for overall behaviour that is
|
||||
no worse than that of the existing code, then work on improving it
|
||||
later.
|
||||
|
||||
D4. The work will take a few weeks to code and test. This leads to a
|
||||
significant opportunity cost when compared against other enhancements
|
||||
that could be achieved in that time. However, the proof-of-concept work
|
||||
performed while writing this design has satisfied me that the work could
|
||||
proceed and be committed incrementally as a background task.
|
||||
|
||||
|
||||
Observations regarding LVM's I/O Architecture
|
||||
---------------------------------------------
|
||||
|
||||
H1. All device, metadata and config file I/O is constrained to pass through a
|
||||
single route in lib/device.
|
||||
|
||||
H2. The first step of the analysis was to instrument this code path with
|
||||
log_debug messages. I/O is split into the following categories:
|
||||
|
||||
"dev signatures",
|
||||
"PV labels",
|
||||
"VG metadata header",
|
||||
"VG metadata content",
|
||||
"extra VG metadata header",
|
||||
"extra VG metadata content",
|
||||
"LVM1 metadata",
|
||||
"pool metadata",
|
||||
"LV content",
|
||||
"logging",
|
||||
|
||||
H3. A bounce buffer is used for most I/O.
|
||||
|
||||
H4. Most callers finish using the supplied data before any further I/O is
|
||||
issued. The few that don't could be converted trivially to do so.
|
||||
|
||||
H5. There is one stream of I/O per metadata area on each device.
|
||||
|
||||
H6. Some reads fall at offsets close to immediately preceding reads, so it's
|
||||
possible to avoid these by caching one "block" per metadata area I/O stream.
|
||||
|
||||
H7. Simple analysis suggests a minimum aligned read size of 8k would deliver
|
||||
immediate gains from this caching. A larger size might perform worse because
|
||||
almost all the time the extra data read would not be used, but this can be
|
||||
re-examined and tuned after the code is in place.
|
||||
|
||||
|
||||
Proposal
|
||||
--------
|
||||
|
||||
P1. Retain the "single I/O path" but offer an asynchronous option.
|
||||
|
||||
P2. Eliminate the bounce buffer in most cases by improving alignment.
|
||||
|
||||
P3. Reduce the number of reads by always reading a minimum of an aligned
|
||||
8k block.
|
||||
|
||||
P4. Eliminate repeated reads by caching the last block read and changing
|
||||
the lib/device interface to return a pointer to read-only data within
|
||||
this block.
|
||||
|
||||
P5. Only perform these interface changes for code on the critical path
|
||||
for now by converting other code sites to use wrappers around the new
|
||||
interface.
|
||||
|
||||
P6. Treat asynchronous I/O as the interface of choice and optimise only
|
||||
for this case.
|
||||
|
||||
P7. Convert the callers on the critical path to pass callback functions
|
||||
to the device layer. These functions will be called later with the
|
||||
read-only data, a context pointer and a success/failure indicator.
|
||||
Where an existing function performs a sequence of I/O, this has the
|
||||
advantage of breaking up the large function into smaller ones and
|
||||
wrapping the parameters used into structures. While this might look
|
||||
rather messy and ad-hoc in the short-term, it's a first step towards
|
||||
breaking up confusingly long functions into component parts and wrapping
|
||||
the existing long parameter lists into more appropriate structures and
|
||||
refactoring these parts of the code.
|
||||
|
||||
P8. Limit the resources used by the asynchronous I/O by using two
|
||||
tunable parameters, one limiting the number of outstanding I/Os issued
|
||||
and another limiting the total amount of memory used.
|
||||
|
||||
P9. Provide a fallback option if asynchronous I/O is unavailable by
|
||||
sharing the code paths but issuing the I/O synchronously and calling the
|
||||
callback immediately.
|
||||
|
||||
P10. Only allocate the buffer for the I/O at the point where the I/O is
|
||||
about to be issued.
|
||||
|
||||
P11. If the thresholds are exceeded, add the request to a simple queue,
|
||||
and process it later after some I/O has completed.
|
||||
|
||||
|
||||
Future work
|
||||
-----------
|
||||
F1. Perform a complete review of the error tracking so that device
|
||||
failures are handled and reported more cleanly, extending the existing
|
||||
basic error counting mechanism.
|
||||
|
||||
F2. Consider whether some of the nested callbacks can be eliminated,
|
||||
which would allow for additional simplifications.
|
||||
|
||||
F3. Adjust the contents of the adhoc context structs into more logical
|
||||
arrangements and use them more widely.
|
||||
|
||||
F4. Perform wider refactoring of these areas of code.
|
||||
|
||||
|
||||
Testing considerations
|
||||
----------------------
|
||||
T1. The changes touch code on the device path, so a thorough re-test of
|
||||
the device layer is required. The new code needs a full audit down
|
||||
through the library layer into the kernel to check that all the error
|
||||
conditions that are currently implemented (such as EAGAIN) are handled
|
||||
sensibly. (LVM's I/O layer needs to remain as solid as we can make it.)
|
||||
|
||||
T2. The current test suite provides a reasonably broad range of coverage
|
||||
of this area but is far from comprehensive.
|
||||
|
||||
|
||||
Acceptance criteria
|
||||
-------------------
|
||||
A1. The current test suite should pass to the same extent as before the
|
||||
changes.
|
||||
|
||||
A2. When all debugging and logging is disabled, strace -c must show
|
||||
improvements e.g. the expected fewer number of reads.
|
||||
|
||||
A3. Running a range of commands under valgrind must not reveal any
|
||||
new leaks due to the changes.
|
||||
|
||||
A4. All new coverity reports from the change must be addressed.
|
||||
|
||||
A5. CPU time should be similar to that before, as the same work
|
||||
is being done overall, just in a different order.
|
||||
|
||||
A6. Tests need to show improved behaviour in targetted areas. For example,
|
||||
if several devices are slow and time out, the delays should occur
|
||||
in parallel and the elapsed time should be less than before.
|
||||
|
||||
|
||||
Release considerations
|
||||
----------------------
|
||||
R1. Async I/O should be widely available and largely reliable on linux
|
||||
nowadays (even though parts of its interface and implementation remain a
|
||||
matter of controversy) so we should try to make its use the default
|
||||
whereever it is supported. If certain types of systems have problems we
|
||||
should try to detect those cases and disable it automatically there.
|
||||
|
||||
R2. Because the implications of an unexpected problem in the new code
|
||||
could be severe for the people affected, the roll out needs to be gentle
|
||||
without a deadline to allow us plenty of time to gain confidence in the
|
||||
new code. Our own testing will only be able to cover a tiny fraction of
|
||||
the different setups our users have, so we need to look out for problems
|
||||
caused by this proactively and encourage people to test it on their own
|
||||
systems and report back. It must go into the tree near the start of a
|
||||
release cycle rather than at the end to provide time for our confidence
|
||||
in it to grow.
|
||||
|
||||
338
doc/lvm-disk-reading.txt
Normal file
338
doc/lvm-disk-reading.txt
Normal file
@@ -0,0 +1,338 @@
|
||||
LVM disk reading
|
||||
|
||||
Reading disks happens in two phases. The first is a discovery phase,
|
||||
which determines what's on the disks. The second is a working phase,
|
||||
which does a particular job for the command.
|
||||
|
||||
|
||||
Phase 1: Discovery
|
||||
------------------
|
||||
|
||||
Read all the disks on the system to find out:
|
||||
- What are the LVM devices?
|
||||
- What VG's exist on those devices?
|
||||
|
||||
This phase is called "label scan" (although it reads and scans everything,
|
||||
not just the label.) It stores the information it discovers (what LVM
|
||||
devices exist, and what VGs exist on them) in lvmcache. The devs/VGs info
|
||||
in lvmcache is the starting point for phase two.
|
||||
|
||||
|
||||
Phase 1 in outline:
|
||||
|
||||
For each device:
|
||||
|
||||
a. Read the first <N> KB of the device. (N is configurable.)
|
||||
|
||||
b. Look for the lvm label_header in the first four sectors,
|
||||
if none exists, it's not an lvm device, so quit looking at it.
|
||||
(By default, label_header is in the second sector.)
|
||||
|
||||
c. Look at the pv_header, which follows the label_header.
|
||||
This tells us the location of VG metadata on the device.
|
||||
There can be 0, 1 or 2 copies of VG metadata. The first
|
||||
is always at the start of the device, the second (if used)
|
||||
is at the end.
|
||||
|
||||
d. Look at the first mda_header (location came from pv_header
|
||||
in the previous step). This is by default in sector 8,
|
||||
4096 bytes from the start of the device. This tells us the
|
||||
location of the actual VG metadata text.
|
||||
|
||||
e. Look at the first copy of the text VG metadata (location came
|
||||
from mda_header in the previous step). This is by default
|
||||
in sector 9, 4608 bytes from the start of the device.
|
||||
The VG metadata is only partially analyzed to create a basic
|
||||
summary of the VG.
|
||||
|
||||
f. Store an "info" entry in lvmcache for this device,
|
||||
indicating that it is an lvm device, and store a "vginfo"
|
||||
entry in lvmcache indicating the name of the VG seen
|
||||
in the metadata in step e.
|
||||
|
||||
g. If the pv_header in step c shows a second mda_header
|
||||
location at the end of the device, then read that as
|
||||
in step d, and repeat steps e-f for it.
|
||||
|
||||
At the end of phase 1, lvmcache will have a list of devices
|
||||
that belong to LVM, and a list of VG names that exist on
|
||||
those devices. Each device (info struct) is associated
|
||||
with the VG (vginfo struct) it is used in.
|
||||
|
||||
|
||||
Phase 1 in code:
|
||||
|
||||
The most relevant functions are listed for each step in the outline.
|
||||
|
||||
lvmcache_label_scan()
|
||||
label_scan()
|
||||
|
||||
. dev_cache_scan()
|
||||
choose which devices on the system to look at
|
||||
|
||||
. for each dev in dev_cache: bcache prefetch/read
|
||||
|
||||
. _process_block() to process data from bcache
|
||||
_find_lvm_header() checks if this is an lvm dev by looking at label_header
|
||||
_text_read() via ops->read() looks at mda/pv/vg data to populate lvmcache
|
||||
|
||||
. _read_mda_header_and_metadata()
|
||||
raw_read_mda_header()
|
||||
|
||||
. _read_mda_header_and_metadata()
|
||||
read_metadata_location()
|
||||
text_read_metadata_summary()
|
||||
config_file_read_fd()
|
||||
_read_vgsummary() via ops->read_vgsummary()
|
||||
|
||||
. _text_read(): lvmcache_add()
|
||||
[adds this device to list of lvm devices]
|
||||
_read_mda_header_and_metadata(): lvmcache_update_vgname_and_id()
|
||||
[adds the VG name to list of VGs]
|
||||
|
||||
|
||||
Phase 2: Work
|
||||
-------------
|
||||
|
||||
This phase carries out the operation requested by the command that was
|
||||
run.
|
||||
|
||||
Whereas the first phase is based on iterating through each device on the
|
||||
system, this phase is based on iterating through each VG name. The list
|
||||
of VG names comes from phase 1, which stored the list in lvmcache to be
|
||||
used by phase 2.
|
||||
|
||||
Some commands may need to iterate through all VG names, while others may
|
||||
need to iterate through just one or two.
|
||||
|
||||
This phase includes locking each VG as work is done on it, so that two
|
||||
commands do not interfere with each other.
|
||||
|
||||
|
||||
Phase 2 in outline:
|
||||
|
||||
For each VG name:
|
||||
|
||||
a. Lock the VG.
|
||||
|
||||
b. Repeat the phase 1 scan steps for each device in this VG.
|
||||
The phase 1 information in lvmcache may have changed because no VG lock
|
||||
was held during phase 1. So, repeat the phase 1 steps, but only for the
|
||||
devices in this VG. N.B. for commands that are just reporting data,
|
||||
we skip this step if the data from phase 1 was complete and consistent.
|
||||
|
||||
c. Get the list of on-disk metadata locations for this VG.
|
||||
Phase 1 created this list in lvmcache to be used here. At this
|
||||
point we copy it out of lvmcache. In the simple/common case,
|
||||
this is a list of devices in the VG. But, some devices may
|
||||
have 0 or 2 metadata locations instead of the default 1, so it
|
||||
is not always equal to the list of devices. We want to read
|
||||
every copy of the metadata for this VG.
|
||||
|
||||
d. For each metadata location on each device in the VG
|
||||
(the list from the previous step):
|
||||
|
||||
1) Look at the mda_header. The location of the mda_header was saved
|
||||
in the lvmcache info struct by phase 1 (where it came from the
|
||||
pv_header.) The mda_header tells us where the text VG metadata is
|
||||
located.
|
||||
|
||||
2) Look at the text VG metadata. The location came from mda_header
|
||||
in the previous step. The VG metadata is fully analyzed and used
|
||||
to create an in-memory 'struct volume_group'.
|
||||
|
||||
e. Compare the copies of VG metadata that were found in each location.
|
||||
If some copies are older, choose the newest one to use, and update
|
||||
any older copies.
|
||||
|
||||
f. Update details about the devices/VG in lvmcache.
|
||||
|
||||
g. Pass the 'vg' struct to the command-specific code to work with.
|
||||
|
||||
|
||||
Phase 2 in code:
|
||||
|
||||
The most relevant functions are listed for each step in the outline.
|
||||
|
||||
For each VG name:
|
||||
process_each_vg()
|
||||
|
||||
. vg_read()
|
||||
lock_vol()
|
||||
|
||||
. vg_read()
|
||||
lvmcache_label_rescan_vg() (if needed)
|
||||
[insert phase 1 steps for scanning devs, but only devs in this vg]
|
||||
|
||||
. vg_read()
|
||||
create_instance()
|
||||
_text_create_text_instance()
|
||||
_create_vg_text_instance()
|
||||
lvmcache_fid_add_mdas_vg()
|
||||
[Copies mda locations from info->mdas where it was saved
|
||||
by phase 1, into fid->metadata_areas_in_use. This is
|
||||
the key connection between phase 1 and phase 2.]
|
||||
|
||||
. dm_list_iterate_items(mda, &fid->metadata_areas_in_use)
|
||||
|
||||
. _vg_read_raw() via ops->vg_read()
|
||||
raw_read_mda_header()
|
||||
|
||||
. _vg_read_raw()
|
||||
text_read_metadata()
|
||||
config_file_read_fd()
|
||||
_read_vg() via ops->read_vg()
|
||||
|
||||
. return the 'vg' struct from vg_read() and use it to do
|
||||
command-specific work
|
||||
|
||||
|
||||
|
||||
Filter i/o
|
||||
----------
|
||||
|
||||
Some filters must be applied before reading a device, and other filters
|
||||
must be applied after reading a device. In all cases, the filters must be
|
||||
applied before lvm processes the device, i.e. before it looks for an lvm
|
||||
label.
|
||||
|
||||
1. Some filters need to be applied prior to reading any devices
|
||||
because the purpose of the filter is to avoid submitting any
|
||||
io on the excluded devices. The regex filter is the primary
|
||||
example. Other filters benefit from being applied prior to
|
||||
reading devices because they can tell which devices to
|
||||
exclude without doing io to the device. An example of this
|
||||
is the mpath filter.
|
||||
|
||||
2. Some filters need to be applied after reading a device because
|
||||
they are based on data/signatures seen on the device.
|
||||
The partitioned filter is an example of this; lvm needs to
|
||||
read a device to see if it has a partition table before it can
|
||||
know whether to exclude the device from further processing.
|
||||
|
||||
We apply filters from 1 before reading devices, and we apply filters from
|
||||
2 after populating bcache, but before processing the device (i.e. before
|
||||
checking for an lvm label, which is the first step in processing.)
|
||||
|
||||
The current implementation of this makes filters return -EAGAIN if they
|
||||
want to read the device, but bcache data is not yet available. This will
|
||||
happen when filtering runs prior to populating bcache. In this case the
|
||||
device is flagged. After bcache is populated, the filters are reapplied
|
||||
to the flagged devices. The filters which need to look at device content
|
||||
are now able to get it from bcache. Devices that do not pass filters at
|
||||
this point are excluded just like devices which were excluded earlier.
|
||||
|
||||
(Some filters from 2 can be skipped by consulting udev for the information
|
||||
instead of reading the device. This is not entirely reliable, so it is
|
||||
disabled by default with the config setting external_device_info_source.
|
||||
It may be worthwhile to change the filters to use the udev info as a hint,
|
||||
or only use udev info for filtering in reporting commands where
|
||||
inaccuracies are not a big problem.)
|
||||
|
||||
|
||||
|
||||
I/O Performance
|
||||
---------------
|
||||
|
||||
. 400 loop devices used as PVs
|
||||
. 40 VGs each with 10 PVs
|
||||
. each VG has one active LV
|
||||
. each of the 10 PVs in vg0 has an artificial 100 ms read delay
|
||||
. read/write/io_submit are system call counts using strace
|
||||
. old is lvm 2.2.175
|
||||
. new is lvm 2.2.178 (shortly before)
|
||||
|
||||
|
||||
Command: pvs
|
||||
------------
|
||||
old: 0m17.422s
|
||||
new: 0m0.331s
|
||||
|
||||
old: read 7773 write 497
|
||||
new: read 2807 write 495 io_submit 448
|
||||
|
||||
|
||||
Command: vgs
|
||||
------------
|
||||
old: 0m20.383s
|
||||
new: 0m0.325s
|
||||
|
||||
old: read 10684 write 129
|
||||
new: read 2807 write 129 io_submit 448
|
||||
|
||||
|
||||
Command: vgck vg0
|
||||
-----------------
|
||||
old: 0m16.212s
|
||||
new: 0m1.290s
|
||||
|
||||
old: read 6372 write 4
|
||||
new: read 2807 write 4 io_submit 458
|
||||
|
||||
|
||||
Command: lvcreate -n test -l1 -an vg0
|
||||
-------------------------------------
|
||||
old: 0m29.271s
|
||||
new: 0m1.351s
|
||||
|
||||
old: read 6503 write 39
|
||||
new: read 2808 write 9 io_submit 488
|
||||
|
||||
|
||||
Command: lvremove vg0/test
|
||||
--------------------------
|
||||
old: 0m29.262s
|
||||
new: 0m1.348s
|
||||
|
||||
old: read 6502 write 36
|
||||
new: read 2807 write 6 io_submit 488
|
||||
|
||||
|
||||
io_submit sources
|
||||
-----------------
|
||||
|
||||
vgs:
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 8 for other devs on the system
|
||||
|
||||
vgck vg0:
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 10 for each PV in vg0 (rescan)
|
||||
- 8 for other devs on the system
|
||||
|
||||
lvcreate -n test -l1 -an vg0
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 10 for each PV in vg0 (rescan)
|
||||
- 8 for other devs on the system
|
||||
writes:
|
||||
- 10 for metadata on each PV in vg0
|
||||
- 10 for precommit on each PV in vg0
|
||||
- 10 for commit on each PV in vg0
|
||||
|
||||
|
||||
|
||||
With lvmetad
|
||||
------------
|
||||
|
||||
Command: pvs
|
||||
------------
|
||||
old: 0m5.405s
|
||||
new: 0m1.404s
|
||||
|
||||
Command: vgs
|
||||
------------
|
||||
old: 0m0.222s
|
||||
new: 0m0.223s
|
||||
|
||||
Command: lvcreate -n test -l1 -an vg0
|
||||
-------------------------------------
|
||||
old: 0m10.128s
|
||||
new: 0m1.137s
|
||||
|
||||
|
||||
158
doc/refactoring.txt
Normal file
158
doc/refactoring.txt
Normal file
@@ -0,0 +1,158 @@
|
||||
Over time, I'd like to refactor the LVM code into these high level modules.
|
||||
|
||||
|
||||
+-------------------------------------------+
|
||||
| |
|
||||
| User Interface |
|
||||
| |
|
||||
| |
|
||||
+-------------------+-----------------------+
|
||||
|
|
||||
+--------------------v-----------------------+
|
||||
| |
|
||||
| LVM Core |
|
||||
| |
|
||||
| |
|
||||
+----+----------------+-----------------+----+
|
||||
| | |
|
||||
+-----v-----+ +-----v------+ +------v----+
|
||||
| | | | | |
|
||||
| Device | | Metadata | | System |
|
||||
| Mapper | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
+-----------+ +------------+ +-----------+
|
||||
|
||||
+---------------------------------------------------------+
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| |
|
||||
| Base |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+------------------------------------+
|
||||
|
||||
Going from the bottom up we have:
|
||||
|
||||
Base
|
||||
----
|
||||
|
||||
This holds all our general purpose code such as data structures, regex engine,
|
||||
memory allocators. In fact pretty much everything in libdevmapper apart from
|
||||
the dm code and config.
|
||||
|
||||
This can be used by any code in the system, which is why I've drawn a line
|
||||
between it and the code above rather than using arrows.
|
||||
|
||||
If anyone can come up with a better name please do. I'm trying to stay away
|
||||
from 'utils'.
|
||||
|
||||
|
||||
Device mapper
|
||||
-------------
|
||||
|
||||
As well as the low level dm-ioctl driving code we need to have all our dm 'best
|
||||
practise' stuff in here. For instance this is the code that decides to use the
|
||||
mirror target to move some data around; that knows to suspend a thin volume
|
||||
before taking a snapshot of it. This module is going to have a lot more code
|
||||
in it than the current libdevmapper.
|
||||
|
||||
It should not know anything about the LVM abstractions or metadata (no PVs, LVs
|
||||
or VGs). It just knows about the dm world.
|
||||
|
||||
Code in here is only allowed to use base.
|
||||
|
||||
|
||||
Metadata model
|
||||
--------------
|
||||
|
||||
Here we have all the format handling, labelling, config parsing etc. We try
|
||||
and put *everything* to do with LVM in here that doesn't actually require dm.
|
||||
|
||||
|
||||
System
|
||||
------
|
||||
|
||||
Code that interfaces with the system (udev etc).
|
||||
|
||||
|
||||
LVM Core
|
||||
--------
|
||||
|
||||
[terrible name]
|
||||
|
||||
This ties together the last 3 units. It should just be glue. We need to be
|
||||
strict about pushing code down from here to keep this as small as possible.
|
||||
|
||||
|
||||
User interface
|
||||
--------------
|
||||
|
||||
Self explanatory.
|
||||
|
||||
|
||||
Headers
|
||||
-------
|
||||
|
||||
Headers will be included using sub directories to make it clearer where they
|
||||
are in the tree.
|
||||
|
||||
eg,
|
||||
#include "base/mm/pool.h"
|
||||
#include "base/data-struct/list.h"
|
||||
#include "dm/thin-provisioning.h"
|
||||
#include "core/pvmove.h"
|
||||
|
||||
|
||||
Getting there
|
||||
=============
|
||||
|
||||
+-------------------------------------------+
|
||||
| |
|
||||
| |
|
||||
| Tools |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+---------+------------------------------+--+
|
||||
| |
|
||||
| +---------------v---------------------------+
|
||||
| | |
|
||||
| | |
|
||||
| | Lib |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +----------------+--------------------------+
|
||||
| |
|
||||
| |
|
||||
+-----v-------------------------------v-----+
|
||||
| |
|
||||
| |
|
||||
| libdevmapper |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-------------------------------------------+
|
||||
|
||||
This is where I see us now.
|
||||
|
||||
'base' should be easy to factor out, it's just the non-dm part of libdevmapper
|
||||
(ie. the bulk of it). But we have the problem that libdevmapper is a public
|
||||
interface to get round.
|
||||
|
||||
'lib' is where the bulk of our code currently is. Dependency-wise the code is
|
||||
a bit like a ball of string. So splitting it up is going to take time. We can
|
||||
probably pull code pretty quickly into the 'metadata model' dir. But factoring
|
||||
out the dm best practises stuff is going to require splitting at least
|
||||
files, and probably functions. Certainly not something that can be done in one
|
||||
go. System should just be a question of cherry picking functions.
|
||||
|
||||
I'm not too familiar with the tools dir. Hopefully it just corresponds with
|
||||
the User Interface module and doesn't contain any business logic.
|
||||
53
doc/release-notes/2.02.178
Normal file
53
doc/release-notes/2.02.178
Normal file
@@ -0,0 +1,53 @@
|
||||
Version 2.02.178
|
||||
================
|
||||
|
||||
There are going to be some large changes to the lvm2 codebase
|
||||
over the next year or so. Starting with this release. These
|
||||
changes should be internal rather than having a big effect on
|
||||
the command line. Inevitably these changes will increase the
|
||||
chance of bugs, so please be on the alert.
|
||||
|
||||
|
||||
Remove support for obsolete metadata formats
|
||||
--------------------------------------------
|
||||
|
||||
Support for the GFS pool format, and format used by the
|
||||
original 1990's version of LVM1 have been removed.
|
||||
|
||||
Use asynchronous IO
|
||||
-------------------
|
||||
|
||||
Almost all IO uses libaio now.
|
||||
|
||||
Rewrite label scanning
|
||||
----------------------
|
||||
|
||||
Dave Teigland has reworked the label scanning and metadata reading
|
||||
logic to minimise the amount of IOs issued. Combined with the aio changes
|
||||
this can greatly improve scanning speed for some systems.
|
||||
|
||||
./configure options
|
||||
-------------------
|
||||
|
||||
We're going to try and remove as many options from ./configure as we
|
||||
can. Each option multiplies the number of possible configurations
|
||||
that we should test (this testing is currently not occurring).
|
||||
|
||||
The first batch to be removed are:
|
||||
|
||||
--enable-testing
|
||||
--with-snapshots
|
||||
--with-mirrors
|
||||
--with-raid
|
||||
--with-thin
|
||||
--with-cache
|
||||
|
||||
Stable targets that are in the upstream kernel will just be supported.
|
||||
|
||||
In future optional target flags will be given in two situations:
|
||||
|
||||
1) The target is experimental, or not upstream at all (eg, vdo).
|
||||
2) The target is deprecated and support will be removed at some future date.
|
||||
|
||||
This decision could well be contentious, so could distro maintainers feel
|
||||
free to comment.
|
||||
257
doc/unit-tests.txt
Normal file
257
doc/unit-tests.txt
Normal file
@@ -0,0 +1,257 @@
|
||||
Building unit tests
|
||||
===================
|
||||
|
||||
make unit-unit/unit-test
|
||||
|
||||
|
||||
Running unit tests
|
||||
==================
|
||||
|
||||
The tests leave no artifacts at the moment, so you can just run
|
||||
unit-test/unit-test from wherever you want.
|
||||
|
||||
./unit-test <list|run> [pattern]
|
||||
|
||||
Listing tests
|
||||
-------------
|
||||
|
||||
Every test has a symbolic path associated with it. Just like file paths they
|
||||
are split into components separated by '/'s. The 'list' command will show you
|
||||
a tree of these tests, along with some description text.
|
||||
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list
|
||||
base
|
||||
data-struct
|
||||
bitset
|
||||
and ................................................. and all bits
|
||||
equal ............................................... equality
|
||||
get_next ............................................ get next set bit
|
||||
list
|
||||
splice .............................................. joining lists together
|
||||
string
|
||||
asprint ............................................. tests asprint
|
||||
strncpy ............................................. tests string copying
|
||||
device
|
||||
bcache
|
||||
block-size-multiple-page ............................ block size must be a multiple of page size
|
||||
block-size-positive ................................. block size must be positive
|
||||
blocks-get-evicted .................................. block get evicted with many reads
|
||||
cache-blocks-positive ............................... nr cache blocks must be positive
|
||||
create-destroy ...................................... simple create/destroy
|
||||
flush-waits ......................................... flush waits for all dirty
|
||||
get-reads ........................................... bcache_get() triggers read
|
||||
prefetch-never-waits ................................ too many prefetches does not trigger a wait
|
||||
prefetch-reads ...................................... prefetch issues a read
|
||||
read-multiple-files ................................. read from multiple files
|
||||
reads-cached ........................................ repeated reads are cached
|
||||
writeback-occurs .................................... dirty data gets written back
|
||||
zero-flag-dirties ................................... zeroed data counts as dirty
|
||||
formatting
|
||||
percent
|
||||
0 ................................................... Pretty printing of percentages near 0%
|
||||
100 ................................................. Pretty printing of percentages near 100%
|
||||
regex
|
||||
fingerprints .......................................... not sure
|
||||
matching .............................................. test the matcher with a variety of regexes
|
||||
dm
|
||||
target
|
||||
mirror
|
||||
status .............................................. parsing mirror status
|
||||
metadata
|
||||
config
|
||||
cascade ............................................... cascade
|
||||
clone ................................................. duplicating a config tree
|
||||
parse ................................................. parsing various
|
||||
|
||||
|
||||
An optional 'pattern' argument may be specified to select subsets of tests.
|
||||
This pattern is a posix regex and does a substring match, so you will need to
|
||||
use anchors if you particularly want the match at the beginning or end of the
|
||||
string.
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list data-struct
|
||||
base
|
||||
data-struct
|
||||
bitset
|
||||
and ................................................. and all bits
|
||||
equal ............................................... equality
|
||||
get_next ............................................ get next set bit
|
||||
list
|
||||
splice .............................................. joining lists together
|
||||
string
|
||||
asprint ............................................. tests asprint
|
||||
strncpy ............................................. tests string copying
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list s$
|
||||
base
|
||||
device
|
||||
bcache
|
||||
flush-waits ......................................... flush waits for all dirty
|
||||
get-reads ........................................... bcache_get() triggers read
|
||||
prefetch-never-waits ................................ too many prefetches does not trigger a wait
|
||||
prefetch-reads ...................................... prefetch issues a read
|
||||
read-multiple-files ................................. read from multiple files
|
||||
writeback-occurs .................................... dirty data gets written back
|
||||
zero-flag-dirties ................................... zeroed data counts as dirty
|
||||
regex
|
||||
fingerprints .......................................... not sure
|
||||
dm
|
||||
target
|
||||
mirror
|
||||
status .............................................. parsing mirror status
|
||||
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
'make run-unit-test' from the top level will run all unit tests. But I tend to
|
||||
run it by hand to I can select just the tests I'm working on.
|
||||
|
||||
Use the 'run' command to run the tests. Currently all logging goes to stderr,
|
||||
so the test runner prints a line at the start of the test and a line
|
||||
indicating success or failure at the end.
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run bcache/block-size
|
||||
[RUN ] /base/device/bcache/block-size-multiple-page
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
[ OK] /base/device/bcache/block-size-multiple-page
|
||||
|
||||
[RUN ] /base/device/bcache/block-size-positive
|
||||
bcache must have a non zero block size
|
||||
[ OK] /base/device/bcache/block-size-positive
|
||||
|
||||
|
||||
2/2 tests passed
|
||||
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run data-struct
|
||||
[RUN ] /base/data-struct/bitset/and
|
||||
[ OK] /base/data-struct/bitset/and
|
||||
|
||||
[RUN ] /base/data-struct/bitset/equal
|
||||
[ OK] /base/data-struct/bitset/equal
|
||||
|
||||
[RUN ] /base/data-struct/bitset/get_next
|
||||
[ OK] /base/data-struct/bitset/get_next
|
||||
|
||||
[RUN ] /base/data-struct/list/splice
|
||||
[ OK] /base/data-struct/list/splice
|
||||
|
||||
[RUN ] /base/data-struct/string/asprint
|
||||
[ OK] /base/data-struct/string/asprint
|
||||
|
||||
[RUN ] /base/data-struct/string/strncpy
|
||||
[ OK] /base/data-struct/string/strncpy
|
||||
|
||||
|
||||
6/6 tests passed
|
||||
|
||||
|
||||
Writing tests
|
||||
=============
|
||||
|
||||
[See unit-test/framework.h and unit-test/units.h for the details]
|
||||
|
||||
Tests are grouped together into 'suites', all tests in a suite share a
|
||||
'fixture'. A fixture is a void * to any object you want; use it to set up any
|
||||
common environment that you need for the tests to run (eg, creating a dm_pool).
|
||||
|
||||
Test suites have nothing to do with the test paths, you can have tests from
|
||||
different suites with similar paths, the runner sorts things for you.
|
||||
|
||||
Put your tests in a file in unit-test/, with '_t' at the end of the name
|
||||
(convention only, nothing relies on this).
|
||||
|
||||
#include "units.h"
|
||||
|
||||
Then write any fixtures you need:
|
||||
|
||||
eg,
|
||||
static void *_mem_init(void) {
|
||||
struct dm_pool *mem = dm_pool_create("bitset test", 1024);
|
||||
if (!mem) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void _mem_exit(void *mem)
|
||||
{
|
||||
dm_pool_destroy(mem);
|
||||
}
|
||||
|
||||
Then write your tests, which should take the void * that was returned by your
|
||||
fixture. Use the T_ASSERT* macros to indicate failure.
|
||||
|
||||
eg,
|
||||
static void test_equal(void *fixture)
|
||||
{
|
||||
struct dm_pool *mem = fixture;
|
||||
dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
|
||||
dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
|
||||
|
||||
int i, j;
|
||||
for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
|
||||
dm_bit_set(bs1, i);
|
||||
dm_bit_set(bs2, i);
|
||||
}
|
||||
|
||||
T_ASSERT(dm_bitset_equal(bs1, bs2));
|
||||
T_ASSERT(dm_bitset_equal(bs2, bs1));
|
||||
|
||||
for (i = 0; i < NR_BITS; i++) {
|
||||
bit_flip(bs1, i);
|
||||
T_ASSERT(!dm_bitset_equal(bs1, bs2));
|
||||
T_ASSERT(!dm_bitset_equal(bs2, bs1));
|
||||
|
||||
T_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */
|
||||
bit_flip(bs1, i);
|
||||
}
|
||||
}
|
||||
|
||||
At the end of your test file you should write a function that builds one or
|
||||
more test suites and adds them to the list of all suites that is passed in. I
|
||||
tend to write a little macro (T) to save typing the same test path repeatedly.
|
||||
|
||||
eg,
|
||||
#define T(path, desc, fn) register_test(ts, "/base/data-struct/bitset/" path, desc, fn)
|
||||
|
||||
void bitset_tests(struct dm_list *all_tests)
|
||||
{
|
||||
struct test_suite *ts = test_suite_create(_mem_init, _mem_exit);
|
||||
if (!ts) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T("get_next", "get next set bit", test_get_next);
|
||||
T("equal", "equality", test_equal);
|
||||
T("and", "and all bits", test_and);
|
||||
|
||||
dm_list_add(all_tests, &ts->list);
|
||||
}
|
||||
|
||||
Then you need to declare your registration function and call it in units.h.
|
||||
|
||||
|
||||
// Declare the function that adds tests suites here ...
|
||||
...
|
||||
void bitset_tests(struct dm_list *suites);
|
||||
...
|
||||
|
||||
// ... and call it in here.
|
||||
static inline void register_all_tests(struct dm_list *suites)
|
||||
{
|
||||
...
|
||||
bitset_tests(suites);
|
||||
...
|
||||
}
|
||||
|
||||
Finally add your test file to the Makefile.in and rerun configure.
|
||||
|
||||
59
doc/vdo.md
59
doc/vdo.md
@@ -22,17 +22,25 @@ Usual limitations apply:
|
||||
- Never layer LUKS over another LUKS - it makes no sense.
|
||||
- LUKS is better over the raids, than under.
|
||||
|
||||
Devices which are not best suitable as backing device:
|
||||
|
||||
- thin volumes - at the moment it is not possible to take snapshot of active VDO volume on top of thin volume.
|
||||
|
||||
### Using VDO as a PV:
|
||||
|
||||
1. under tpool
|
||||
1. under tdata
|
||||
- The best fit - it will deduplicate additional redundancies among all
|
||||
snapshots and will reduce the footprint.
|
||||
- Risks: Resize! dmevent will not be able to handle resizing of tpool ATM.
|
||||
2. under corig
|
||||
- Cache fits better under VDO device - it will reduce amount of data, and
|
||||
deduplicate, so there should be more hits.
|
||||
- This is useful to keep the most frequently used data in cache
|
||||
uncompressed (if that happens to be a bottleneck.)
|
||||
uncompressed or without deduplication if that happens to be a bottleneck.
|
||||
- Cache may fit better under VDO device, depending on compressibility and
|
||||
amount of duplicates, as
|
||||
- compression will reduce amount of data, thus effectively increasing
|
||||
size of cache,
|
||||
- and deduplication may emphasize hotspots.
|
||||
- Performance testing of your particular workload is strongly recommended.
|
||||
3. under (multiple) linear LVs - e.g. used for VMs.
|
||||
|
||||
### And where VDO does not fit:
|
||||
@@ -50,36 +58,47 @@ Usual limitations apply:
|
||||
|
||||
- under snapshot CoW device - when there are multiple of those it could deduplicate
|
||||
|
||||
## Development
|
||||
|
||||
### Things to decide
|
||||
|
||||
- under integrity devices - it should work - mostly for data
|
||||
- hash is not compressible and unique - it makes sense to have separate imeta and idata volumes for integrity devices
|
||||
- under integrity devices
|
||||
- VDO should work well for data blocks,
|
||||
- but hashes are mostly unique and not compressible - were it possible it
|
||||
would make sense to have separate imeta and idata volumes for integrity
|
||||
devices.
|
||||
|
||||
### Future Integration of VDO into LVM:
|
||||
|
||||
One issue is using both LUKS and RAID under VDO. We have two options:
|
||||
|
||||
- use mdadm x LUKS x VDO+LV
|
||||
- use LV RAID x LUKS x VDO+LV - still requiring recursive LVs.
|
||||
- use LV RAID x LUKS x VDO+LV
|
||||
|
||||
Another issue is duality of VDO - it is a top level LV but it can be seen as a "pool" for multiple devices.
|
||||
In both cases dmeventd will not be able to resize the volume at the moment.
|
||||
|
||||
- This is one usecase which could not be handled by LVM at the moment.
|
||||
- Size of the VDO is its physical size and virtual size - just like tpool.
|
||||
- same problems with virtual vs physical size - it can get full, without exposing it fo a FS
|
||||
Another issue is duality of VDO - it can be used as a top level LV (with a
|
||||
filesystem on top) but it can be used as "pool" for multiple devices too.
|
||||
|
||||
Another possible RFE is to split data and metadata:
|
||||
This will be solved in similar way thin pools allow multiple volumes.
|
||||
|
||||
- e.g. keep data on HDD and metadata on SSD
|
||||
Also VDO, has two sizes - its physical size and virtual size - and when
|
||||
overprovisioning, just like tpool, we face same problems - VDO can get full,
|
||||
without exposing it to a FS. dmeventd monitoring will be needed.
|
||||
|
||||
Another possible RFE is to split data and metadata - keep data on HDD and metadata on SSD.
|
||||
|
||||
## Issues / Testing
|
||||
|
||||
- fstrim/discard pass down - does it work with VDO?
|
||||
- VDO can run in synchronous vs. asynchronous mode
|
||||
- synchronous for devices where write is safe after it is confirmed. Some devices are lying.
|
||||
- asynchronous for devices requiring flush
|
||||
- multiple devices under VDO - need to find common options
|
||||
- pvmove - changing characteristics of underlying device
|
||||
- autoactivation during boot
|
||||
- Q: can we use VDO for RootFS?
|
||||
- VDO can run in synchronous vs. asynchronous mode:
|
||||
- synchronous for devices where write is safe after it is confirmed. Some
|
||||
devices are lying.
|
||||
- asynchronous for devices requiring flush.
|
||||
- Multiple devices under VDO - need to find and expose common properties, or
|
||||
not allow grouping them together. (This is same for all volumes with more
|
||||
physical devices below.)
|
||||
- pvmove changing characteristics of underlying device.
|
||||
- autoactivation during boot?
|
||||
- Q: can we use VDO for RootFS? Dracut!
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
@top_srcdir@/lib/config/defaults.h
|
||||
@top_srcdir@/lib/datastruct/btree.h
|
||||
@top_srcdir@/lib/datastruct/str_list.h
|
||||
@top_srcdir@/lib/device/bcache.h
|
||||
@top_srcdir@/lib/device/dev-cache.h
|
||||
@top_srcdir@/lib/device/dev-ext-udev-constants.h
|
||||
@top_srcdir@/lib/device/dev-type.h
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
/* include/configure.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to 1 if aio is available. */
|
||||
#undef AIO_SUPPORT
|
||||
/* include/configure.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 to use libblkid detection of signatures when wiping. */
|
||||
#undef BLKID_WIPING_SUPPORT
|
||||
@@ -75,10 +72,6 @@
|
||||
/* Default system configuration directory. */
|
||||
#undef DEFAULT_ETC_DIR
|
||||
|
||||
/* Fall back to LVM1 by default if device-mapper is missing from the kernel.
|
||||
*/
|
||||
#undef DEFAULT_FALLBACK_TO_LVM1
|
||||
|
||||
/* Name of default locking directory. */
|
||||
#undef DEFAULT_LOCK_DIR
|
||||
|
||||
@@ -252,6 +245,9 @@
|
||||
/* Define to 1 if you have the <langinfo.h> header file. */
|
||||
#undef HAVE_LANGINFO_H
|
||||
|
||||
/* Define to 1 if you have the <libaio.h> header file. */
|
||||
#undef HAVE_LIBAIO_H
|
||||
|
||||
/* Define to 1 if you have the <libcman.h> header file. */
|
||||
#undef HAVE_LIBCMAN_H
|
||||
|
||||
@@ -344,15 +340,15 @@
|
||||
/* Define to 1 if you have the <paths.h> header file. */
|
||||
#undef HAVE_PATHS_H
|
||||
|
||||
/* Define to 1 if you have the `pselect' function. */
|
||||
#undef HAVE_PSELECT
|
||||
|
||||
/* Define to 1 if you have the <pthread.h> header file. */
|
||||
#undef HAVE_PTHREAD_H
|
||||
|
||||
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
||||
#undef HAVE_PTRDIFF_T
|
||||
|
||||
/* Define to 1 if the compiler has the `__builtin_clz` builtin. */
|
||||
#undef HAVE___BUILTIN_CLZ
|
||||
|
||||
/* Define to 1 if you have the <readline/history.h> header file. */
|
||||
#undef HAVE_READLINE_HISTORY_H
|
||||
|
||||
@@ -481,9 +477,16 @@
|
||||
/* Define to 1 if you have the `strtoull' function. */
|
||||
#undef HAVE_STRTOULL
|
||||
|
||||
/* Define to 1 if `st_blocks' is a member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if `st_rdev' is a member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_RDEV
|
||||
|
||||
/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
|
||||
`HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
|
||||
#undef HAVE_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if you have the <syslog.h> header file. */
|
||||
#undef HAVE_SYSLOG_H
|
||||
|
||||
@@ -555,6 +558,9 @@
|
||||
/* Define to 1 if you have the <sys/utsname.h> header file. */
|
||||
#undef HAVE_SYS_UTSNAME_H
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#undef HAVE_SYS_WAIT_H
|
||||
|
||||
@@ -594,6 +600,9 @@
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Define to 1 if the system has the `__builtin_clz' built-in function */
|
||||
#undef HAVE___BUILTIN_CLZ
|
||||
|
||||
/* Internalization package */
|
||||
#undef INTL_PACKAGE
|
||||
|
||||
@@ -610,13 +619,6 @@
|
||||
slash. */
|
||||
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
|
||||
|
||||
/* Define to 1 if 'lvm' should fall back to using LVM1 binaries if
|
||||
device-mapper is missing from the kernel */
|
||||
#undef LVM1_FALLBACK
|
||||
|
||||
/* Define to 1 to include built-in support for LVM1 metadata. */
|
||||
#undef LVM1_INTERNAL
|
||||
|
||||
/* Path to lvmetad pidfile. */
|
||||
#undef LVMETAD_PIDFILE
|
||||
|
||||
@@ -679,9 +681,6 @@
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 to include built-in support for GFS pool metadata. */
|
||||
#undef POOL_INTERNAL
|
||||
|
||||
/* Define to 1 to include built-in support for raid. */
|
||||
#undef RAID_INTERNAL
|
||||
|
||||
|
||||
@@ -16,34 +16,6 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
ifeq ("@LVM1@", "shared")
|
||||
SUBDIRS = format1
|
||||
endif
|
||||
|
||||
ifeq ("@POOL@", "shared")
|
||||
SUBDIRS += format_pool
|
||||
endif
|
||||
|
||||
ifeq ("@SNAPSHOTS@", "shared")
|
||||
SUBDIRS += snapshot
|
||||
endif
|
||||
|
||||
ifeq ("@MIRRORS@", "shared")
|
||||
SUBDIRS += mirror
|
||||
endif
|
||||
|
||||
ifeq ("@RAID@", "shared")
|
||||
SUBDIRS += raid
|
||||
endif
|
||||
|
||||
ifeq ("@THIN@", "shared")
|
||||
SUBDIRS += thin
|
||||
endif
|
||||
|
||||
ifeq ("@CACHE@", "shared")
|
||||
SUBDIRS += cache_segtype
|
||||
endif
|
||||
|
||||
ifeq ("@CLUSTER@", "shared")
|
||||
SUBDIRS += locking
|
||||
endif
|
||||
@@ -51,10 +23,13 @@ endif
|
||||
SOURCES =\
|
||||
activate/activate.c \
|
||||
cache/lvmcache.c \
|
||||
cache_segtype/cache.c \
|
||||
commands/toolcontext.c \
|
||||
config/config.c \
|
||||
datastruct/btree.c \
|
||||
datastruct/str_list.c \
|
||||
device/bcache.c \
|
||||
device/bcache-utils.c \
|
||||
device/dev-cache.c \
|
||||
device/dev-ext.c \
|
||||
device/dev-io.c \
|
||||
@@ -63,6 +38,7 @@ SOURCES =\
|
||||
device/dev-type.c \
|
||||
device/dev-luks.c \
|
||||
device/dev-dasd.c \
|
||||
device/dev-lvm1-pool.c \
|
||||
display/display.c \
|
||||
error/errseg.c \
|
||||
unknown/unknown.c \
|
||||
@@ -77,6 +53,7 @@ SOURCES =\
|
||||
filters/filter-type.c \
|
||||
filters/filter-usable.c \
|
||||
filters/filter-internal.c \
|
||||
filters/filter-signature.c \
|
||||
format_text/archive.c \
|
||||
format_text/archiver.c \
|
||||
format_text/export.c \
|
||||
@@ -107,6 +84,7 @@ SOURCES =\
|
||||
metadata/snapshot_manip.c \
|
||||
metadata/thin_manip.c \
|
||||
metadata/vg.c \
|
||||
mirror/mirrored.c \
|
||||
misc/crc.c \
|
||||
misc/lvm-exec.c \
|
||||
misc/lvm-file.c \
|
||||
@@ -120,55 +98,19 @@ SOURCES =\
|
||||
mm/memlock.c \
|
||||
notify/lvmnotify.c \
|
||||
properties/prop_common.c \
|
||||
raid/raid.c \
|
||||
report/properties.c \
|
||||
report/report.c \
|
||||
snapshot/snapshot.c \
|
||||
striped/striped.c \
|
||||
thin/thin.c \
|
||||
uuid/uuid.c \
|
||||
zero/zero.c
|
||||
|
||||
ifeq ("@LVM1@", "internal")
|
||||
SOURCES +=\
|
||||
format1/disk-rep.c \
|
||||
format1/format1.c \
|
||||
format1/import-export.c \
|
||||
format1/import-extents.c \
|
||||
format1/layout.c \
|
||||
format1/lvm1-label.c \
|
||||
format1/vg_number.c
|
||||
endif
|
||||
|
||||
ifeq ("@POOL@", "internal")
|
||||
SOURCES +=\
|
||||
format_pool/disk_rep.c \
|
||||
format_pool/format_pool.c \
|
||||
format_pool/import_export.c \
|
||||
format_pool/pool_label.c
|
||||
endif
|
||||
|
||||
ifeq ("@CLUSTER@", "internal")
|
||||
SOURCES += locking/cluster_locking.c
|
||||
endif
|
||||
|
||||
ifeq ("@SNAPSHOTS@", "internal")
|
||||
SOURCES += snapshot/snapshot.c
|
||||
endif
|
||||
|
||||
ifeq ("@MIRRORS@", "internal")
|
||||
SOURCES += mirror/mirrored.c
|
||||
endif
|
||||
|
||||
ifeq ("@RAID@", "internal")
|
||||
SOURCES += raid/raid.c
|
||||
endif
|
||||
|
||||
ifeq ("@THIN@", "internal")
|
||||
SOURCES += thin/thin.c
|
||||
endif
|
||||
|
||||
ifeq ("@CACHE@", "internal")
|
||||
SOURCES += cache_segtype/cache.c
|
||||
endif
|
||||
|
||||
ifeq ("@DEVMAPPER@", "yes")
|
||||
SOURCES +=\
|
||||
activate/dev_manager.c \
|
||||
@@ -201,14 +143,7 @@ LIB_STATIC = $(LIB_NAME).a
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS =\
|
||||
format1 \
|
||||
format_pool \
|
||||
snapshot \
|
||||
mirror \
|
||||
notify \
|
||||
raid \
|
||||
thin \
|
||||
cache_segtype \
|
||||
locking
|
||||
endif
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "config.h"
|
||||
#include "segtype.h"
|
||||
#include "sharedlib.h"
|
||||
#include "lvmcache.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
@@ -35,19 +37,6 @@
|
||||
|
||||
#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args)
|
||||
|
||||
int lvm1_present(struct cmd_context *cmd)
|
||||
{
|
||||
static char path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/lvm/global", cmd->proc_dir)
|
||||
< 0) {
|
||||
log_error("LVM1 proc global snprintf failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (path_exists(path)) ? 1 : 0;
|
||||
}
|
||||
|
||||
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
|
||||
struct dm_list *modules)
|
||||
{
|
||||
@@ -606,7 +595,7 @@ int module_present(struct cmd_context *cmd, const char *target_name)
|
||||
#endif
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
int i = dm_snprintf(path, (sizeof(path) - 1), "%smodule/dm_%s",
|
||||
int i = dm_snprintf(path, sizeof(path), "%smodule/dm_%s",
|
||||
dm_sysfs_dir(), target_name);
|
||||
|
||||
if (i > 0) {
|
||||
@@ -669,7 +658,7 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
int use_layer, struct lvinfo *info,
|
||||
const struct lv_segment *seg,
|
||||
struct lv_seg_status *seg_status,
|
||||
int with_open_count, int with_read_ahead)
|
||||
int with_open_count, int with_read_ahead, int with_name_check)
|
||||
{
|
||||
struct dm_info dminfo;
|
||||
|
||||
@@ -689,7 +678,7 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
/* New thin-pool has no layer, but -tpool suffix needs to be queried */
|
||||
if (!use_layer && lv_is_new_thin_pool(lv)) {
|
||||
/* Check if there isn't existing old thin pool mapping in the table */
|
||||
if (!dev_manager_info(cmd, lv, NULL, 0, 0, &dminfo, NULL, NULL))
|
||||
if (!dev_manager_info(cmd, lv, NULL, 0, 0, 0, &dminfo, NULL, NULL))
|
||||
return_0;
|
||||
if (!dminfo.exists)
|
||||
use_layer = 1;
|
||||
@@ -702,8 +691,9 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
|
||||
if (!dev_manager_info(cmd, lv,
|
||||
(use_layer) ? lv_layer(lv) : NULL,
|
||||
with_open_count, with_read_ahead,
|
||||
&dminfo, (info) ? &info->read_ahead : NULL,
|
||||
with_open_count, with_read_ahead, with_name_check,
|
||||
&dminfo,
|
||||
(info) ? &info->read_ahead : NULL,
|
||||
seg_status))
|
||||
return_0;
|
||||
|
||||
@@ -732,7 +722,7 @@ int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, int use_la
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
return _lv_info(cmd, lv, use_layer, info, NULL, NULL, with_open_count, with_read_ahead);
|
||||
return _lv_info(cmd, lv, use_layer, info, NULL, NULL, with_open_count, with_read_ahead, 0);
|
||||
}
|
||||
|
||||
int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
||||
@@ -750,6 +740,15 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_info_with_name_check(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
int use_layer, struct lvinfo *info)
|
||||
{
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
return _lv_info(cmd, lv, use_layer, info, NULL, NULL, 0, 0, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if lv_with_info_and_seg_status info structure populated,
|
||||
* else 0 on failure or if device not active locally.
|
||||
@@ -777,16 +776,16 @@ int lv_info_with_seg_status(struct cmd_context *cmd,
|
||||
* STATUS is collected from cache LV */
|
||||
if (!(lv_seg = get_only_segment_using_this_lv(lv)))
|
||||
return_0;
|
||||
(void) _lv_info(cmd, lv_seg->lv, 1, NULL, lv_seg, &status->seg_status, 0, 0);
|
||||
(void) _lv_info(cmd, lv_seg->lv, 1, NULL, lv_seg, &status->seg_status, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lv_is_thin_pool(lv)) {
|
||||
/* Always collect status for '-tpool' */
|
||||
if (_lv_info(cmd, lv, 1, &status->info, lv_seg, &status->seg_status, 0, 0) &&
|
||||
if (_lv_info(cmd, lv, 1, &status->info, lv_seg, &status->seg_status, 0, 0, 0) &&
|
||||
(status->seg_status.type == SEG_STATUS_THIN_POOL)) {
|
||||
/* There is -tpool device, but query 'active' state of 'fake' thin-pool */
|
||||
if (!_lv_info(cmd, lv, 0, NULL, NULL, NULL, 0, 0) &&
|
||||
if (!_lv_info(cmd, lv, 0, NULL, NULL, NULL, 0, 0, 0) &&
|
||||
!status->seg_status.thin_pool->needs_check)
|
||||
status->info.exists = 0; /* So pool LV is not active */
|
||||
}
|
||||
@@ -795,10 +794,10 @@ int lv_info_with_seg_status(struct cmd_context *cmd,
|
||||
|
||||
if (lv_is_external_origin(lv)) {
|
||||
if (!_lv_info(cmd, lv, 0, &status->info, NULL, NULL,
|
||||
with_open_count, with_read_ahead))
|
||||
with_open_count, with_read_ahead, 0))
|
||||
return_0;
|
||||
|
||||
(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0);
|
||||
(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -811,13 +810,13 @@ int lv_info_with_seg_status(struct cmd_context *cmd,
|
||||
/* Show INFO for actual origin and grab status for merging origin */
|
||||
if (!_lv_info(cmd, lv, 0, &status->info, lv_seg,
|
||||
lv_is_merging_origin(lv) ? &status->seg_status : NULL,
|
||||
with_open_count, with_read_ahead))
|
||||
with_open_count, with_read_ahead, 0))
|
||||
return_0;
|
||||
|
||||
if (status->info.exists &&
|
||||
(status->seg_status.type != SEG_STATUS_SNAPSHOT)) /* Not merging */
|
||||
/* Grab STATUS from layered -real */
|
||||
(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0);
|
||||
(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -826,10 +825,11 @@ int lv_info_with_seg_status(struct cmd_context *cmd,
|
||||
olv = origin_from_cow(lv);
|
||||
|
||||
if (!_lv_info(cmd, olv, 0, &status->info, first_seg(olv), &status->seg_status,
|
||||
with_open_count, with_read_ahead))
|
||||
with_open_count, with_read_ahead, 0))
|
||||
return_0;
|
||||
|
||||
if (status->seg_status.type == SEG_STATUS_SNAPSHOT) {
|
||||
if (status->seg_status.type == SEG_STATUS_SNAPSHOT ||
|
||||
(lv_is_thin_volume(olv) && (status->seg_status.type == SEG_STATUS_THIN))) {
|
||||
log_debug_activation("Snapshot merge is in progress, querying status of %s instead.",
|
||||
display_lvname(lv));
|
||||
/*
|
||||
@@ -846,7 +846,7 @@ int lv_info_with_seg_status(struct cmd_context *cmd,
|
||||
}
|
||||
|
||||
return _lv_info(cmd, lv, 0, &status->info, lv_seg, &status->seg_status,
|
||||
with_open_count, with_read_ahead);
|
||||
with_open_count, with_read_ahead, 0);
|
||||
}
|
||||
|
||||
#define OPEN_COUNT_CHECK_RETRIES 25
|
||||
@@ -1006,8 +1006,10 @@ int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset)
|
||||
if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1)))
|
||||
return_0;
|
||||
|
||||
if (!(r = dev_manager_raid_status(dm, lv, &status)))
|
||||
stack;
|
||||
if (!(r = dev_manager_raid_status(dm, lv, &status))) {
|
||||
dev_manager_destroy(dm);
|
||||
return_0;
|
||||
}
|
||||
|
||||
*data_offset = status->data_offset;
|
||||
|
||||
@@ -1543,8 +1545,11 @@ static int _lv_is_active(const struct logical_volume *lv,
|
||||
if (skip_cluster_query)
|
||||
goto out;
|
||||
|
||||
if ((r = cluster_lock_held(lv->lvid.s, "", &e)) >= 0)
|
||||
if ((r = cluster_lock_held(lv->lvid.s, "", &e)) >= 0) {
|
||||
if (l && e)
|
||||
r = 0; /* exclusive locally */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If lock query is not supported (due to interfacing with old
|
||||
@@ -1662,7 +1667,10 @@ static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd
|
||||
if (!(dmevh = dm_event_handler_create()))
|
||||
return_NULL;
|
||||
|
||||
if (dm_event_handler_set_dmeventd_path(dmevh, find_config_tree_str(cmd, dmeventd_executable_CFG, NULL)))
|
||||
if (!cmd->default_settings.dmeventd_executable)
|
||||
cmd->default_settings.dmeventd_executable = find_config_tree_str(cmd, dmeventd_executable_CFG, NULL);
|
||||
|
||||
if (dm_event_handler_set_dmeventd_path(dmevh, cmd->default_settings.dmeventd_executable))
|
||||
goto_bad;
|
||||
|
||||
if (dso && dm_event_handler_set_dso(dmevh, dso))
|
||||
@@ -1678,21 +1686,18 @@ static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd
|
||||
|
||||
bad:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath)
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, int id)
|
||||
{
|
||||
char *path;
|
||||
const char *libpath = find_config_tree_str(cmd, id, NULL);
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
|
||||
log_error("Failed to allocate dmeventd library path.");
|
||||
return NULL;
|
||||
}
|
||||
get_shared_library_path(cmd, libpath, path, sizeof(path));
|
||||
|
||||
get_shared_library_path(cmd, libpath, path, PATH_MAX);
|
||||
|
||||
return path;
|
||||
return dm_strdup(path);
|
||||
}
|
||||
|
||||
static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
@@ -1709,13 +1714,18 @@ static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_vo
|
||||
return build_dm_uuid(cmd->mem, lv, layer);
|
||||
}
|
||||
|
||||
static int _device_registered_with_dmeventd(struct cmd_context *cmd, const struct logical_volume *lv, int *pending, const char **dso)
|
||||
static int _device_registered_with_dmeventd(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv,
|
||||
const char **dso,
|
||||
int *pending, int *monitored)
|
||||
{
|
||||
char *uuid;
|
||||
enum dm_event_mask evmask = 0;
|
||||
enum dm_event_mask evmask;
|
||||
struct dm_event_handler *dmevh;
|
||||
int r;
|
||||
|
||||
*pending = 0;
|
||||
*monitored = 0;
|
||||
|
||||
if (!(uuid = _build_target_uuid(cmd, lv)))
|
||||
return_0;
|
||||
@@ -1723,9 +1733,20 @@ static int _device_registered_with_dmeventd(struct cmd_context *cmd, const struc
|
||||
if (!(dmevh = _create_dm_event_handler(cmd, uuid, NULL, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
if ((r = dm_event_get_registered_device(dmevh, 0))) {
|
||||
if (r == -ENOENT) {
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/* FIXME: why do we care which 'dso' is monitoring? */
|
||||
if (dso && (*dso = dm_event_handler_get_dso(dmevh)) &&
|
||||
!(*dso = dm_pool_strdup(cmd->mem, *dso))) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
@@ -1734,21 +1755,25 @@ static int _device_registered_with_dmeventd(struct cmd_context *cmd, const struc
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
if (dso && (*dso = dm_event_handler_get_dso(dmevh)) && !(*dso = dm_pool_strdup(cmd->mem, *dso)))
|
||||
log_error("Failed to duplicate dso name.");
|
||||
|
||||
*monitored = evmask;
|
||||
r = 1;
|
||||
out:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return evmask;
|
||||
return r;
|
||||
}
|
||||
|
||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
const struct logical_volume *lv, int *pending)
|
||||
const struct logical_volume *lv,
|
||||
int *pending, int *monitored)
|
||||
{
|
||||
char *uuid;
|
||||
enum dm_event_mask evmask = 0;
|
||||
enum dm_event_mask evmask;
|
||||
struct dm_event_handler *dmevh;
|
||||
int r;
|
||||
|
||||
*pending = 0;
|
||||
*monitored = 0;
|
||||
|
||||
if (!dso)
|
||||
return_0;
|
||||
@@ -1759,9 +1784,13 @@ int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
if ((r = dm_event_get_registered_device(dmevh, 0))) {
|
||||
if (r == -ENOENT) {
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
@@ -1770,9 +1799,12 @@ int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
*monitored = evmask;
|
||||
r = 1;
|
||||
out:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return evmask;
|
||||
return r;
|
||||
}
|
||||
|
||||
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
||||
@@ -1800,7 +1832,7 @@ int target_register_events(struct cmd_context *cmd, const char *dso, const struc
|
||||
if (!r)
|
||||
return_0;
|
||||
|
||||
log_very_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
log_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1815,7 +1847,7 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
const struct lv_activate_opts *laopts, int monitor)
|
||||
{
|
||||
#ifdef DMEVENTD
|
||||
int i, pending = 0, monitored;
|
||||
int i, pending = 0, monitored = 0;
|
||||
int r = 1;
|
||||
struct dm_list *snh, *snht;
|
||||
struct lv_segment *seg;
|
||||
@@ -1823,12 +1855,15 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
int (*monitor_fn) (struct lv_segment *s, int e);
|
||||
uint32_t s;
|
||||
static const struct lv_activate_opts zlaopts = { 0 };
|
||||
struct lv_activate_opts mirr_laopts = { .origin_only = 1 };
|
||||
struct lvinfo info;
|
||||
const char *dso = NULL;
|
||||
int new_unmonitor;
|
||||
|
||||
if (!laopts)
|
||||
laopts = &zlaopts;
|
||||
else
|
||||
mirr_laopts.read_only = laopts->read_only;
|
||||
|
||||
/* skip dmeventd code altogether */
|
||||
if (dmeventd_monitor_mode() == DMEVENTD_MONITOR_IGNORE)
|
||||
@@ -1885,7 +1920,8 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
* In case of a snapshot device, we monitor lv->snapshot->lv,
|
||||
* not the actual LV itself.
|
||||
*/
|
||||
if (lv_is_cow(lv) && (laopts->no_merging || !lv_is_merging_cow(lv))) {
|
||||
if (lv_is_cow(lv) && (laopts->no_merging || !lv_is_merging_cow(lv) ||
|
||||
lv_has_target_type(lv->vg->cmd->mem, lv, NULL, TARGET_NAME_SNAPSHOT))) {
|
||||
if (!(r = monitor_dev_for_events(cmd, lv->snapshot->lv, NULL, monitor)))
|
||||
stack;
|
||||
return r;
|
||||
@@ -1924,9 +1960,7 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
continue;
|
||||
if (!monitor_dev_for_events(cmd, seg_lv(seg, s), NULL,
|
||||
monitor)) {
|
||||
log_error("Failed to %smonitor %s",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg_lv(seg, s)));
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
@@ -1961,11 +1995,21 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
!seg->segtype->ops->target_monitored) /* doesn't support registration */
|
||||
continue;
|
||||
|
||||
if (!monitor)
|
||||
if (!monitor) {
|
||||
/* When unmonitoring, obtain existing dso being used. */
|
||||
monitored = _device_registered_with_dmeventd(cmd, seg_is_snapshot(seg) ? seg->cow : seg->lv, &pending, &dso);
|
||||
else
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
if (!_device_registered_with_dmeventd(cmd, seg_is_snapshot(seg) ? seg->cow : seg->lv,
|
||||
&dso, &pending, &monitored)) {
|
||||
log_warn("WARNING: Failed to %smonitor %s.",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg_is_snapshot(seg) ? seg->cow : seg->lv));
|
||||
return 0;
|
||||
}
|
||||
} else if (!seg->segtype->ops->target_monitored(seg, &pending, &monitored)) {
|
||||
log_warn("WARNING: Failed to %smonitor %s.",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg->lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: We should really try again if pending */
|
||||
monitored = (pending) ? 0 : monitored;
|
||||
@@ -1977,7 +2021,9 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
if (monitored)
|
||||
log_verbose("%s already monitored.", display_lvname(lv));
|
||||
else if (seg->segtype->ops->target_monitor_events) {
|
||||
log_verbose("Monitoring %s%s", display_lvname(lv), test_mode() ? " [Test mode: skipping this]" : "");
|
||||
log_very_verbose("Monitoring %s with %s.%s", display_lvname(lv),
|
||||
seg->segtype->dso,
|
||||
test_mode() ? " [Test mode: skipping this]" : "");
|
||||
monitor_fn = seg->segtype->ops->target_monitor_events;
|
||||
}
|
||||
} else {
|
||||
@@ -1999,26 +2045,42 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
|
||||
if (new_unmonitor) {
|
||||
if (!target_register_events(cmd, dso, seg_is_snapshot(seg) ? seg->cow : lv, 0, 0, 10)) {
|
||||
log_error("%s: segment unmonitoring failed.",
|
||||
display_lvname(lv));
|
||||
|
||||
log_warn("WARNING: %s: segment unmonitoring failed.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
} else if (monitor_fn) {
|
||||
/* FIXME specify events */
|
||||
if (!monitor_fn(seg, 0)) {
|
||||
log_error("%s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), lvseg_name(seg));
|
||||
log_warn("WARNING: %s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), lvseg_name(seg));
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (!cmd->is_clvmd && !vg_write_lock_held() && lv_is_mirror(lv)) {
|
||||
/*
|
||||
* Commands vgchange and lvchange do use read-only lock when changing
|
||||
* monitoring (--monitor y|n). All other use cases hold 'write-lock'
|
||||
* so they skip this dm mirror table refreshing step.
|
||||
* Shortcut is also not applied with clvmd.
|
||||
*/
|
||||
if (!_lv_activate_lv(lv, &mirr_laopts)) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check [un]monitor results */
|
||||
/* Try a couple times if pending, but not forever... */
|
||||
for (i = 0;; i++) {
|
||||
pending = 0;
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
if (!seg->segtype->ops->target_monitored(seg, &pending, &monitored)) {
|
||||
stack;
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
if (!pending || i >= 40)
|
||||
break;
|
||||
log_very_verbose("%s %smonitoring still pending: waiting...",
|
||||
@@ -2031,8 +2093,8 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
}
|
||||
|
||||
if (!r && !error_message_produced())
|
||||
log_error("%sonitoring %s failed.", monitor ? "M" : "Not m",
|
||||
display_lvname(lv));
|
||||
log_warn("WARNING: %sonitoring %s failed.", monitor ? "M" : "Not m",
|
||||
display_lvname(lv));
|
||||
return r;
|
||||
#else
|
||||
return 1;
|
||||
@@ -2061,6 +2123,17 @@ static int _preload_detached_lv(struct logical_volume *lv, void *data)
|
||||
!lv_is_raid_metadata(lv_pre) && lv_is_active(lv) &&
|
||||
!_lv_preload(lv_pre, detached->laopts, detached->flush_required))
|
||||
return_0;
|
||||
} else if (lv_is_mirror_image(lv)) {
|
||||
if ((lv_pre = find_lv_in_vg_by_lvid(detached->lv_pre->vg, &lv->lvid)) &&
|
||||
!lv_is_mirror_image(lv_pre) && lv_is_active(lv) &&
|
||||
!_lv_preload(lv_pre, detached->laopts, detached->flush_required))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!lv_is_visible(lv) && (lv_pre = find_lv(detached->lv_pre->vg, lv->name)) &&
|
||||
lv_is_visible(lv_pre)) {
|
||||
if (!_lv_preload(lv_pre, detached->laopts, detached->flush_required))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* FIXME: condition here should be far more limiting to really
|
||||
@@ -2092,11 +2165,67 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
struct dm_pool *mem = NULL;
|
||||
struct dm_list suspend_lvs;
|
||||
struct lv_list *lvl;
|
||||
const union lvid *lvid = (const union lvid *) lvid_s;
|
||||
const char *vgid = (const char *)lvid->id[0].uuid;
|
||||
struct volume_group *vg;
|
||||
struct volume_group *vg_pre;
|
||||
int found;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
|
||||
if (!cmd->is_clvmd)
|
||||
goto skip_read;
|
||||
|
||||
if (lv && lv_pre)
|
||||
goto skip_read;
|
||||
|
||||
if (!(vg = lvmcache_get_saved_vg(vgid, 0))) {
|
||||
log_debug("lv_suspend did not find saved_vg %.8s so reading", vgid);
|
||||
if (!(vg = vg_read_by_vgid(cmd, vgid, 0))) {
|
||||
log_error("lv_suspend could not read vgid %.8s", vgid);
|
||||
goto out;
|
||||
}
|
||||
log_debug("lv_suspend using read vg %s %d %p", vg->name, vg->seqno, vg);
|
||||
} else {
|
||||
log_debug("lv_suspend using saved_vg %s %d %p", vg->name, vg->seqno, vg);
|
||||
}
|
||||
|
||||
if (!(vg_pre = lvmcache_get_saved_vg(vgid, 1))) {
|
||||
log_debug("lv_suspend did not find pre saved_vg %.8s so reading", vgid);
|
||||
if (!(vg_pre = vg_read_by_vgid(cmd, vgid, 1))) {
|
||||
log_error("lv_suspend could not read pre vgid %.8s", vgid);
|
||||
goto out;
|
||||
}
|
||||
log_debug("lv_suspend using pre read vg %s %d %p", vg_pre->name, vg_pre->seqno, vg_pre);
|
||||
} else {
|
||||
log_debug("lv_suspend using pre saved_vg %s %d %p", vg_pre->name, vg_pre->seqno, vg_pre);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that vg and vg_pre returned by vg_read_by_vgid will
|
||||
* not be the same as saved_vg_old/saved_vg_new that would
|
||||
* be returned by lvmcache_get_saved_vg() because the saved_vg's
|
||||
* are copies of the vg struct that is created by _vg_read.
|
||||
* (Should we grab and use the saved_vg to use here instead of
|
||||
* the vg returned by vg_read_by_vgid?)
|
||||
*/
|
||||
|
||||
if ((vg->status & EXPORTED_VG) || (vg_pre->status & EXPORTED_VG)) {
|
||||
log_error("Volume group \"%s\" is exported", vg->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lv = lv_to_free = find_lv_in_vg_by_lvid(vg, lvid);
|
||||
lv_pre = lv_pre_to_free = find_lv_in_vg_by_lvid(vg_pre, lvid);
|
||||
|
||||
if (!lv || !lv_pre) {
|
||||
log_error("lv_suspend could not find lv %p lv_pre %p vg %p vg_pre %p vgid %s",
|
||||
lv, lv_pre, vg, vg_pre, vgid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
skip_read:
|
||||
/* lv comes from committed metadata */
|
||||
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
goto_out;
|
||||
@@ -2121,6 +2250,19 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
if (!lv_info(cmd, lv, laopts->origin_only, &info, 0, 0))
|
||||
goto_out;
|
||||
|
||||
/*
|
||||
* Save old and new (current and precommitted) versions of the
|
||||
* VG metadata for lv_resume() to use, since lv_resume can't
|
||||
* read metadata given that devices are suspended. lv_resume()
|
||||
* will resume LVs using the old/current metadata if the vg_commit
|
||||
* did happen (or failed), and it will resume LVs using the
|
||||
* new/precommitted metadata if the vg_commit succeeded.
|
||||
*/
|
||||
if (cmd->is_clvmd) {
|
||||
lvmcache_save_vg(lv->vg, 0);
|
||||
lvmcache_save_vg(lv_pre->vg, 1);
|
||||
}
|
||||
|
||||
if (!info.exists || info.suspended) {
|
||||
if (!error_if_not_suspended) {
|
||||
r = 1;
|
||||
@@ -2327,16 +2469,60 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
struct lv_activate_opts *laopts, int error_if_not_active,
|
||||
const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *lv_to_free = NULL;
|
||||
struct dm_list *snh;
|
||||
struct volume_group *vg = NULL;
|
||||
struct logical_volume *lv_found = NULL;
|
||||
const union lvid *lvid;
|
||||
const char *vgid;
|
||||
struct lvinfo info;
|
||||
int r = 0;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
|
||||
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
goto_out;
|
||||
/*
|
||||
* When called in clvmd, lvid_s is set and lv is not. We need to
|
||||
* get the VG metadata without reading disks because devs are
|
||||
* suspended. lv_suspend() saved old and new VG metadata for us
|
||||
* to use here. If vg_commit() happened, lvmcache_get_saved_vg_latest
|
||||
* will return the new metadata for us to use in resuming LVs.
|
||||
* If vg_commit() did not happen, lvmcache_get_saved_vg_latest
|
||||
* returns the old metadata which we use to resume LVs.
|
||||
*/
|
||||
if (!lv) {
|
||||
if (!lvid_s) {
|
||||
log_error(INTERNAL_ERROR "Requested resume of unindentified resource!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvid = (const union lvid *) lvid_s;
|
||||
vgid = (const char *)lvid->id[0].uuid;
|
||||
|
||||
if ((vg = lvmcache_get_saved_vg_latest(vgid))) {
|
||||
log_debug_activation("Resuming LVID %s found saved vg seqno %d %s", lvid_s, vg->seqno, vg->name);
|
||||
if ((lv_found = find_lv_in_vg_by_lvid(vg, lvid))) {
|
||||
log_debug_activation("Resuming LVID %s found saved LV %s", lvid_s, display_lvname(lv_found));
|
||||
lv = lv_found;
|
||||
} else
|
||||
log_debug_activation("Resuming LVID %s did not find saved LV", lvid_s);
|
||||
} else
|
||||
log_debug_activation("Resuming LVID %s did not find saved VG", lvid_s);
|
||||
|
||||
/*
|
||||
* resume must have been called without a preceding suspend,
|
||||
* so we need to read the vg.
|
||||
*/
|
||||
|
||||
if (!lv) {
|
||||
log_debug_activation("Resuming LVID %s reading VG", lvid_s);
|
||||
if (!(lv_found = lv_from_lvid(cmd, lvid_s, 0))) {
|
||||
log_debug_activation("Resuming LVID %s failed to read VG", lvid_s);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lv = lv_found;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lv_is_origin(lv) && !lv_is_thin_volume(lv) && !lv_is_thin_pool(lv))
|
||||
laopts->origin_only = 0;
|
||||
@@ -2397,9 +2583,6 @@ needs_resume:
|
||||
|
||||
r = 1;
|
||||
out:
|
||||
if (lv_to_free)
|
||||
release_vg(lv_to_free->vg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2622,13 +2805,24 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lv_raid_has_visible_sublvs(lv)) {
|
||||
log_error("Refusing activation of RAID LV %s with "
|
||||
"visible SubLVs.", display_lvname(lv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_mode()) {
|
||||
_skip("Activating %s.", display_lvname(lv));
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (filter)
|
||||
/* Component LV activation is enforced to be 'read-only' */
|
||||
/* TODO: should not apply for LVs in maintenance mode */
|
||||
if (!lv_is_visible(lv) && lv_is_component(lv)) {
|
||||
laopts->read_only = 1;
|
||||
laopts->component_lv = lv;
|
||||
} else if (filter)
|
||||
laopts->read_only = _passes_readonly_filter(cmd, lv);
|
||||
|
||||
log_debug_activation("Activating %s%s%s%s%s.", display_lvname(lv),
|
||||
@@ -2637,14 +2831,14 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
laopts->noscan ? " noscan" : "",
|
||||
laopts->temporary ? " temporary" : "");
|
||||
|
||||
if (!lv_info(cmd, lv, 0, &info, 0, 0))
|
||||
if (!lv_info_with_name_check(cmd, lv, 0, &info))
|
||||
goto_out;
|
||||
|
||||
/*
|
||||
* Nothing to do?
|
||||
*/
|
||||
if (info.exists && !info.suspended && info.live_table &&
|
||||
(info.read_only == read_only_lv(lv, laopts))) {
|
||||
(info.read_only == read_only_lv(lv, laopts, NULL))) {
|
||||
r = 1;
|
||||
log_debug_activation("LV %s is already active.", display_lvname(lv));
|
||||
goto out;
|
||||
@@ -2815,3 +3009,112 @@ void activation_exit(void)
|
||||
dev_manager_exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _component_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
struct logical_volume **component_lv = (struct logical_volume **) data;
|
||||
|
||||
if (lv_is_locked(lv) || lv_is_pvmove(lv) ||/* ignoring */
|
||||
/* thin-pool is special and it's using layered device */
|
||||
(lv_is_thin_pool(lv) && pool_is_active(lv)))
|
||||
return -1;
|
||||
|
||||
if (lv_is_active(lv)) {
|
||||
if (!lv_is_component(lv) || lv_is_visible(lv))
|
||||
return -1; /* skip whole subtree */
|
||||
|
||||
log_debug_activation("Found active component LV %s.", display_lvname(lv));
|
||||
*component_lv = lv;
|
||||
return 0; /* break any further processing */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds out for any LV if any of its component LVs are active.
|
||||
* Function first checks if an existing LV is visible and active eventually
|
||||
* it's lock holding LV is already active. In such case sub LV cannot be
|
||||
* actived alone and no further checking is needed.
|
||||
*
|
||||
* Returns active component LV if there is such.
|
||||
*/
|
||||
const struct logical_volume *lv_component_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *component_lv = NULL;
|
||||
const struct logical_volume *holder_lv = lv_lock_holder(lv);
|
||||
|
||||
if ((holder_lv != lv) && lv_is_active(holder_lv))
|
||||
return NULL; /* Lock holding LV is active, do not check components */
|
||||
|
||||
if (_component_cb((struct logical_volume *) lv, &holder_lv) == 1)
|
||||
(void) for_each_sub_lv((struct logical_volume *) lv, _component_cb,
|
||||
(void*) &component_lv);
|
||||
|
||||
return component_lv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds out if any LV above is active, as stacked device tree can be composed of
|
||||
* chained set of LVs.
|
||||
*
|
||||
* Returns active holder LV if there is such.
|
||||
*/
|
||||
const struct logical_volume *lv_holder_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *holder;
|
||||
const struct seg_list *sl;
|
||||
|
||||
if (lv_is_locked(lv) || lv_is_pvmove(lv))
|
||||
return NULL; /* Skip pvmove/locked LV tracking */
|
||||
|
||||
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
|
||||
/* Recursive call for upper-stack holder */
|
||||
if ((holder = lv_holder_is_active(sl->seg->lv)))
|
||||
return holder;
|
||||
|
||||
if (lv_is_active(sl->seg->lv)) {
|
||||
log_debug_activation("Found active holder LV %s.", display_lvname(sl->seg->lv));
|
||||
return sl->seg->lv;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _deactivate_sub_lv_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
struct logical_volume **slv = data;
|
||||
|
||||
if (lv_is_thin_pool(lv) || lv_is_external_origin(lv))
|
||||
return -1;
|
||||
|
||||
if (!deactivate_lv(lv->vg->cmd, lv)) {
|
||||
*slv = lv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivates LV toghether with explicit deactivation call made also for all its component LVs.
|
||||
*/
|
||||
int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
|
||||
{
|
||||
struct logical_volume *flv;
|
||||
|
||||
if (!deactivate_lv(lv->vg->cmd, lv)) {
|
||||
log_error("Cannot deactivate logical volume %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!for_each_sub_lv((struct logical_volume *)lv, _deactivate_sub_lv_cb, &flv)) {
|
||||
log_error("Cannot deactivate subvolume %s of logical volume %s.",
|
||||
display_lvname(flv), display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -83,6 +83,7 @@ struct lv_activate_opts {
|
||||
* flags are persistent in udev db for any spurious event
|
||||
* that follows. */
|
||||
unsigned resuming; /* Set when resuming after a suspend. */
|
||||
const struct logical_volume *component_lv;
|
||||
};
|
||||
|
||||
void set_activation(int activation, int silent);
|
||||
@@ -90,7 +91,6 @@ int activation(void);
|
||||
|
||||
int driver_version(char *version, size_t size);
|
||||
int library_version(char *version, size_t size);
|
||||
int lvm1_present(struct cmd_context *cmd);
|
||||
|
||||
int module_present(struct cmd_context *cmd, const char *target_name);
|
||||
int target_present_version(struct cmd_context *cmd, const char *target_name,
|
||||
@@ -134,6 +134,8 @@ int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, int use_la
|
||||
struct lvinfo *info, int with_open_count, int with_read_ahead);
|
||||
int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
||||
struct lvinfo *info, int with_open_count, int with_read_ahead);
|
||||
int lv_info_with_name_check(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
int use_layer, struct lvinfo *info);
|
||||
|
||||
/*
|
||||
* Returns 1 if lv_info_and_seg_status structure has been populated,
|
||||
@@ -198,6 +200,11 @@ int lv_is_active_exclusive(const struct logical_volume *lv);
|
||||
int lv_is_active_exclusive_locally(const struct logical_volume *lv);
|
||||
int lv_is_active_exclusive_remotely(const struct logical_volume *lv);
|
||||
|
||||
/* Check is any component LV is active */
|
||||
const struct logical_volume *lv_component_is_active(const struct logical_volume *lv);
|
||||
const struct logical_volume *lv_holder_is_active(const struct logical_volume *lv);
|
||||
int deactivate_lv_with_sub_lv(const struct logical_volume *lv);
|
||||
|
||||
int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
const char *layer, const char *target_type);
|
||||
|
||||
@@ -206,9 +213,9 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
|
||||
#ifdef DMEVENTD
|
||||
# include "libdevmapper-event.h"
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, int id);
|
||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
const struct logical_volume *lv, int *pending);
|
||||
const struct logical_volume *lv, int *pending, int *monitored);
|
||||
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
||||
int evmask __attribute__((unused)), int set, int timeout);
|
||||
#endif
|
||||
@@ -229,6 +236,7 @@ struct dev_usable_check_params {
|
||||
unsigned int check_suspended:1;
|
||||
unsigned int check_error_target:1;
|
||||
unsigned int check_reserved:1;
|
||||
unsigned int check_lv:1;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -32,6 +32,9 @@
|
||||
|
||||
#define MAX_TARGET_PARAMSIZE 50000
|
||||
#define LVM_UDEV_NOSCAN_FLAG DM_SUBSYSTEM_UDEV_FLAG0
|
||||
#define CRYPT_TEMP "CRYPT-TEMP"
|
||||
#define CRYPT_SUBDEV "CRYPT-SUBDEV"
|
||||
#define STRATIS "stratis-"
|
||||
|
||||
typedef enum {
|
||||
PRELOAD,
|
||||
@@ -62,7 +65,6 @@ struct dev_manager {
|
||||
int activation; /* building activation tree */
|
||||
int suspend; /* building suspend tree */
|
||||
unsigned track_external_lv_deps;
|
||||
struct dm_list pending_delete; /* str_list of dlid(s) with pending delete */
|
||||
unsigned track_pending_delete;
|
||||
unsigned track_pvmove_deps;
|
||||
|
||||
@@ -72,10 +74,22 @@ struct dev_manager {
|
||||
struct lv_layer {
|
||||
const struct logical_volume *lv;
|
||||
const char *old_name;
|
||||
int visible_component;
|
||||
};
|
||||
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts)
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts, const char *layer)
|
||||
{
|
||||
if (layer && lv_is_cow(lv))
|
||||
return 0; /* Keep snapshot's COW volume writable */
|
||||
|
||||
if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv))
|
||||
return 0; /* Keep RAID SubLvs writable */
|
||||
|
||||
if (!layer) {
|
||||
if (lv_is_thin_pool(lv))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (laopts->read_only || !(lv->status & LVM_WRITE));
|
||||
}
|
||||
|
||||
@@ -169,7 +183,8 @@ static int _get_segment_status_from_target_params(const char *target_name,
|
||||
}
|
||||
|
||||
/* Validate target_name segtype from DM table with lvm2 metadata segtype */
|
||||
if (strcmp(segtype->name, target_name) &&
|
||||
if (!lv_is_locked(seg->lv) &&
|
||||
strcmp(segtype->name, target_name) &&
|
||||
/* If kernel's type isn't an exact match is it compatible? */
|
||||
(!segtype->ops->target_status_compatible ||
|
||||
!segtype->ops->target_status_compatible(target_name))) {
|
||||
@@ -225,6 +240,7 @@ static uint32_t _seg_len(const struct lv_segment *seg)
|
||||
static int _info_run(const char *dlid, struct dm_info *dminfo,
|
||||
uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status,
|
||||
const char *name_check,
|
||||
int with_open_count, int with_read_ahead,
|
||||
uint32_t major, uint32_t minor)
|
||||
{
|
||||
@@ -235,6 +251,7 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
||||
void *target = NULL;
|
||||
uint64_t target_start, target_length, start, length;
|
||||
char *target_name, *target_params;
|
||||
const char *devname;
|
||||
|
||||
if (seg_status) {
|
||||
dmtask = DM_DEVICE_STATUS;
|
||||
@@ -248,7 +265,12 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
||||
with_open_count, with_flush, 0)))
|
||||
return_0;
|
||||
|
||||
if (with_read_ahead && dminfo->exists) {
|
||||
if (name_check && dminfo->exists &&
|
||||
(devname = dm_task_get_name(dmt)) &&
|
||||
(strcmp(name_check, devname) != 0))
|
||||
dminfo->exists = 0; /* mismatching name -> device does not exist */
|
||||
|
||||
if (with_read_ahead && read_ahead && dminfo->exists) {
|
||||
if (!dm_task_get_read_ahead(dmt, read_ahead))
|
||||
goto_out;
|
||||
} else if (read_ahead)
|
||||
@@ -356,7 +378,7 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
if (!(tmp_dev = dev_create_file(buf, NULL, NULL, 0)))
|
||||
goto_out;
|
||||
|
||||
tmp_dev->dev = MKDEV((dev_t)sm->logs[0].major, (dev_t)sm->logs[0].minor);
|
||||
tmp_dev->dev = MKDEV(sm->logs[0].major, sm->logs[0].minor);
|
||||
if (device_is_usable(tmp_dev, (struct dev_usable_check_params)
|
||||
{ .check_empty = 1,
|
||||
.check_blocked = 1,
|
||||
@@ -441,15 +463,21 @@ static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!target_type || !strcmp(target_type, TARGET_NAME_SNAPSHOT)) {
|
||||
|
||||
if (!target_type)
|
||||
continue;
|
||||
|
||||
if (!strcmp(target_type, TARGET_NAME_SNAPSHOT)) {
|
||||
if (!params || sscanf(params, "%d:%d %d:%d", &major1, &minor1, &major2, &minor2) != 4) {
|
||||
log_error("Incorrect snapshot table found.");
|
||||
log_warn("WARNING: Incorrect snapshot table found for %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
goto out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1) || _device_is_suspended(major2, minor2);
|
||||
} else if (!strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) {
|
||||
if (!params || sscanf(params, "%d:%d", &major1, &minor1) != 2) {
|
||||
log_error("Incorrect snapshot-origin table found.");
|
||||
log_warn("WARNING: Incorrect snapshot-origin table found for %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
goto out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1);
|
||||
@@ -484,7 +512,7 @@ static int _ignore_unusable_thins(struct device *dev)
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!params || sscanf(params, "%d:%d", &major, &minor) != 2) {
|
||||
log_error("Failed to get thin-pool major:minor for thin device %d:%d.",
|
||||
log_warn("WARNING: Cannot get thin-pool major:minor for thin device %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
goto out;
|
||||
}
|
||||
@@ -514,6 +542,47 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _ignore_invalid_snapshot(const char *params)
|
||||
{
|
||||
struct dm_status_snapshot *s;
|
||||
struct dm_pool *mem;
|
||||
int r = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("invalid snapshots", 128)))
|
||||
return_0;
|
||||
|
||||
if (!dm_get_status_snapshot(mem, params, &s))
|
||||
stack;
|
||||
else
|
||||
r = s->invalid;
|
||||
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _ignore_frozen_raid(struct device *dev, const char *params)
|
||||
{
|
||||
struct dm_status_raid *s;
|
||||
struct dm_pool *mem;
|
||||
int r = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("frozen raid", 128)))
|
||||
return_0;
|
||||
|
||||
if (!dm_get_status_raid(mem, params, &s))
|
||||
stack;
|
||||
else if (s->sync_action && !strcmp(s->sync_action, "frozen")) {
|
||||
log_warn("WARNING: %s frozen raid device (%d:%d) needs inspection.",
|
||||
dev_name(dev), (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
r = 1;
|
||||
}
|
||||
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* device_is_usable
|
||||
* @dev
|
||||
@@ -583,12 +652,32 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
}
|
||||
}
|
||||
|
||||
if (check.check_lv && uuid && !strncmp(uuid, "LVM-", 4)) {
|
||||
/* Skip LVs */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (check.check_reserved && uuid &&
|
||||
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
|
||||
!strncmp(uuid, CRYPT_SUBDEV, sizeof(CRYPT_SUBDEV) - 1) ||
|
||||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
|
||||
/* Skip private crypto devices */
|
||||
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
|
||||
dev_name(dev), uuid,
|
||||
uuid[0] == 'C' ? "crypto" : "stratis",
|
||||
name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME Also check for mpath no paths */
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
if (check.check_blocked && target_type && !strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
if (!target_type)
|
||||
continue;
|
||||
|
||||
if (check.check_blocked && !strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
if (ignore_lvm_mirrors()) {
|
||||
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
|
||||
goto out;
|
||||
@@ -622,21 +711,33 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
* correctly, not just snapshots but any cobimnation possible
|
||||
* in a stack - use proper dm tree to check this instead.
|
||||
*/
|
||||
if (check.check_suspended && target_type &&
|
||||
if (check.check_suspended &&
|
||||
(!strcmp(target_type, TARGET_NAME_SNAPSHOT) || !strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) &&
|
||||
_ignore_suspended_snapshot_component(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strcmp(target_type, TARGET_NAME_SNAPSHOT) &&
|
||||
_ignore_invalid_snapshot(params)) {
|
||||
log_debug_activation("%s: Invalid %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strncmp(target_type, TARGET_NAME_RAID, 4) && _ignore_frozen_raid(dev, params)) {
|
||||
log_debug_activation("%s: Frozen %s device %s not usable.",
|
||||
dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: extend check struct ? */
|
||||
if (target_type && !strcmp(target_type, TARGET_NAME_THIN) &&
|
||||
if (!strcmp(target_type, TARGET_NAME_THIN) &&
|
||||
!_ignore_unusable_thins(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (target_type && strcmp(target_type, TARGET_NAME_ERROR))
|
||||
if (strcmp(target_type, TARGET_NAME_ERROR))
|
||||
only_error_target = 0;
|
||||
} while (next);
|
||||
|
||||
@@ -685,18 +786,19 @@ static int _original_uuid_format_check_required(struct cmd_context *cmd)
|
||||
|
||||
static int _info(struct cmd_context *cmd,
|
||||
const char *name, const char *dlid,
|
||||
int with_open_count, int with_read_ahead,
|
||||
int with_open_count, int with_read_ahead, int with_name_check,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status)
|
||||
{
|
||||
char old_style_dlid[sizeof(UUID_PREFIX) + 2 * ID_LEN];
|
||||
const char *suffix, *suffix_position;
|
||||
const char *name_check = (with_name_check) ? name : NULL;
|
||||
unsigned i = 0;
|
||||
|
||||
log_debug_activation("Getting device info for %s [%s].", name, dlid);
|
||||
|
||||
/* Check for dlid */
|
||||
if (!_info_run(dlid, dminfo, read_ahead, seg_status,
|
||||
if (!_info_run(dlid, dminfo, read_ahead, seg_status, name_check,
|
||||
with_open_count, with_read_ahead, 0, 0))
|
||||
return_0;
|
||||
|
||||
@@ -712,7 +814,8 @@ static int _info(struct cmd_context *cmd,
|
||||
(void) strncpy(old_style_dlid, dlid, sizeof(old_style_dlid));
|
||||
old_style_dlid[sizeof(old_style_dlid) - 1] = '\0';
|
||||
if (!_info_run(old_style_dlid, dminfo, read_ahead, seg_status,
|
||||
with_open_count, with_read_ahead, 0, 0))
|
||||
name_check, with_open_count, with_read_ahead,
|
||||
0, 0))
|
||||
return_0;
|
||||
if (dminfo->exists)
|
||||
return 1;
|
||||
@@ -725,7 +828,7 @@ static int _info(struct cmd_context *cmd,
|
||||
|
||||
/* Check for dlid before UUID_PREFIX was added */
|
||||
if (!_info_run(dlid + sizeof(UUID_PREFIX) - 1, dminfo, read_ahead, seg_status,
|
||||
with_open_count, with_read_ahead, 0, 0))
|
||||
name_check, with_open_count, with_read_ahead, 0, 0))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
@@ -733,12 +836,12 @@ static int _info(struct cmd_context *cmd,
|
||||
|
||||
static int _info_by_dev(uint32_t major, uint32_t minor, struct dm_info *info)
|
||||
{
|
||||
return _info_run(NULL, info, NULL, 0, 0, 0, major, minor);
|
||||
return _info_run(NULL, info, NULL, NULL, NULL, 0, 0, major, minor);
|
||||
}
|
||||
|
||||
int dev_manager_info(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv, const char *layer,
|
||||
int with_open_count, int with_read_ahead,
|
||||
int with_open_count, int with_read_ahead, int with_name_check,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status)
|
||||
{
|
||||
@@ -751,7 +854,8 @@ int dev_manager_info(struct cmd_context *cmd,
|
||||
if (!(dlid = build_dm_uuid(cmd->mem, lv, layer)))
|
||||
goto_out;
|
||||
|
||||
if (!(r = _info(cmd, name, dlid, with_open_count, with_read_ahead,
|
||||
if (!(r = _info(cmd, name, dlid,
|
||||
with_open_count, with_read_ahead, with_name_check,
|
||||
dminfo, read_ahead, seg_status)))
|
||||
stack;
|
||||
out:
|
||||
@@ -1163,8 +1267,6 @@ struct dev_manager *dev_manager_create(struct cmd_context *cmd,
|
||||
|
||||
dm_udev_set_sync_support(cmd->current_settings.udev_sync);
|
||||
|
||||
dm_list_init(&dm->pending_delete);
|
||||
|
||||
return dm;
|
||||
|
||||
bad:
|
||||
@@ -1502,6 +1604,9 @@ int dev_manager_thin_percent(struct dev_manager *dm,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Explore state of running DM table to obtain currently used deviceId
|
||||
*/
|
||||
int dev_manager_thin_device_id(struct dev_manager *dm,
|
||||
const struct logical_volume *lv,
|
||||
uint32_t *device_id)
|
||||
@@ -1511,10 +1616,16 @@ int dev_manager_thin_device_id(struct dev_manager *dm,
|
||||
struct dm_info info;
|
||||
uint64_t start, length;
|
||||
char *params, *target_type = NULL;
|
||||
const char *layer = lv_layer(lv);
|
||||
int r = 0;
|
||||
|
||||
if (lv_is_merging_origin(lv) && !lv_info(lv->vg->cmd, lv, 1, NULL, 0, 0))
|
||||
/* If the merge has already happened, that table
|
||||
* can already be using correct LV without -real layer */
|
||||
layer = NULL;
|
||||
|
||||
/* Build dlid for the thin layer */
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv))))
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task_run(DM_DEVICE_TABLE, &info, NULL, dlid, 0, 0, 0, 0, 1, 0)))
|
||||
@@ -1590,7 +1701,8 @@ int dev_manager_mknodes(const struct logical_volume *lv)
|
||||
return_0;
|
||||
|
||||
if (dminfo.exists) {
|
||||
if (_lv_has_mknode(lv))
|
||||
/* read-only component LV is also made visible */
|
||||
if (_lv_has_mknode(lv) || (dminfo.read_only && lv_is_component(lv)))
|
||||
r = _dev_manager_lv_mknodes(lv);
|
||||
} else
|
||||
r = _dev_manager_lv_rmnodes(lv);
|
||||
@@ -1652,7 +1764,8 @@ static int _check_udev_fallback(struct cmd_context *cmd)
|
||||
#endif /* UDEV_SYNC_SUPPORT */
|
||||
|
||||
static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
const char *layer, int noscan, int temporary)
|
||||
const char *layer, int noscan, int temporary,
|
||||
int visible_component)
|
||||
{
|
||||
uint16_t udev_flags = 0;
|
||||
|
||||
@@ -1668,7 +1781,7 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_vol
|
||||
* If not, create just the /dev/mapper content.
|
||||
*/
|
||||
/* FIXME: add target's method for this */
|
||||
if (lv_is_new_thin_pool(lv))
|
||||
if (lv_is_new_thin_pool(lv) || visible_component)
|
||||
/* New thin-pool is regular LV with -tpool UUID suffix. */
|
||||
udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG |
|
||||
DM_UDEV_DISABLE_OTHER_RULES_FLAG;
|
||||
@@ -1719,10 +1832,20 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_vol
|
||||
|
||||
static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
const struct logical_volume *lv, int origin_only);
|
||||
|
||||
static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts,
|
||||
const char *layer);
|
||||
/*
|
||||
* Check for device holders (ATM used only for removed pvmove targets)
|
||||
* and add them into dtree structures.
|
||||
* When 'laopts != NULL' add them as new nodes - which also corrects READ_AHEAD.
|
||||
* Note: correct table are already explicitelly PRELOADED.
|
||||
*/
|
||||
static int _check_holder(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
const struct logical_volume *lv, uint32_t major,
|
||||
const char *d_name)
|
||||
const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts,
|
||||
uint32_t major, const char *d_name)
|
||||
{
|
||||
const char *default_uuid_prefix = dm_uuid_prefix();
|
||||
const size_t default_uuid_prefix_len = strlen(default_uuid_prefix);
|
||||
@@ -1773,8 +1896,11 @@ static int _check_holder(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
log_debug_activation("Found holder %s of %s.",
|
||||
display_lvname(lv_det),
|
||||
display_lvname(lv));
|
||||
if (!_add_lv_to_dtree(dm, dtree, lv_det, 0))
|
||||
goto_out;
|
||||
if (!laopts) {
|
||||
if (!_add_lv_to_dtree(dm, dtree, lv_det, 0))
|
||||
goto_out;
|
||||
} else if (!_add_new_lv_to_dtree(dm, dtree, lv_det, laopts, 0))
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1791,7 +1917,9 @@ out:
|
||||
* i.e. PVMOVE is being finished and final table is going to be resumed.
|
||||
*/
|
||||
static int _add_holders_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
const struct logical_volume *lv, struct dm_info *info)
|
||||
const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts,
|
||||
const struct dm_info *info)
|
||||
{
|
||||
const char *sysfs_dir = dm_sysfs_dir();
|
||||
char sysfs_path[PATH_MAX];
|
||||
@@ -1814,7 +1942,7 @@ static int _add_holders_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
while ((dirent = readdir(d)))
|
||||
/* Expects minor is added to 'dm-' prefix */
|
||||
if (!strncmp(dirent->d_name, "dm-", 3) &&
|
||||
!_check_holder(dm, dtree, lv, info->major, dirent->d_name))
|
||||
!_check_holder(dm, dtree, lv, laopts, info->major, dirent->d_name))
|
||||
goto_out;
|
||||
|
||||
r = 1;
|
||||
@@ -1834,10 +1962,10 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
|
||||
if (!(dlid = build_dm_uuid(dm->track_pending_delete ? dm->cmd->pending_delete_mem : dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, &info, NULL, NULL))
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, 0, &info, NULL, NULL))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
@@ -1866,7 +1994,8 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
}
|
||||
|
||||
if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor,
|
||||
_get_udev_flags(dm, lv, layer, 0, 0))) {
|
||||
_get_udev_flags(dm, lv, layer,
|
||||
0, 0, 0))) {
|
||||
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree.",
|
||||
info.major, info.minor);
|
||||
return 0;
|
||||
@@ -1875,7 +2004,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (info.exists && dm->track_pending_delete) {
|
||||
log_debug_activation("Tracking pending delete for %s (%s).",
|
||||
display_lvname(lv), dlid);
|
||||
if (!str_list_add(dm->mem, &dm->pending_delete, dlid))
|
||||
if (!str_list_add(dm->cmd->pending_delete_mem, &dm->cmd->pending_delete, dlid))
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -1885,7 +2014,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
*/
|
||||
if (info.exists && !lv_is_pvmove(lv) &&
|
||||
!strchr(lv->name, '_') && !strncmp(lv->name, "pvmove", 6))
|
||||
if (!_add_holders_to_dtree(dm, dtree, lv, &info))
|
||||
if (!_add_holders_to_dtree(dm, dtree, lv, NULL, &info))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
@@ -1898,9 +2027,56 @@ struct pool_cb_data {
|
||||
int skip_zero; /* to skip zeroed device header (check first 64B) */
|
||||
int exec; /* which binary to call */
|
||||
int opts;
|
||||
struct {
|
||||
unsigned maj;
|
||||
unsigned min;
|
||||
unsigned patch;
|
||||
} version;
|
||||
const char *global;
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple version of check function calling 'tool -V'
|
||||
*
|
||||
* Returns 1 if the tool's version is equal or better to given.
|
||||
* Otherwise it returns 0.
|
||||
*/
|
||||
static int _check_tool_version(struct cmd_context *cmd, const char *tool,
|
||||
unsigned maj, unsigned min, unsigned patch)
|
||||
{
|
||||
const char *argv[] = { tool, "-V", NULL };
|
||||
struct pipe_data pdata;
|
||||
FILE *f;
|
||||
char buf[128] = { 0 };
|
||||
char *nl;
|
||||
unsigned v_maj, v_min, v_patch;
|
||||
int ret = 0;
|
||||
|
||||
if (!(f = pipe_open(cmd, argv, 0, &pdata))) {
|
||||
log_warn("WARNING: Cannot read output from %s.", argv[0]);
|
||||
} else {
|
||||
if (fgets(buf, sizeof(buf) - 1, f) &&
|
||||
(sscanf(buf, "%u.%u.%u", &v_maj, &v_min, &v_patch) == 3)) {
|
||||
if ((v_maj > maj) ||
|
||||
((v_maj == maj) &&
|
||||
((v_min > min) ||
|
||||
(v_min == min && v_patch >= patch))))
|
||||
ret = 1;
|
||||
|
||||
if ((nl = strchr(buf, '\n')))
|
||||
nl[0] = 0; /* cut newline away */
|
||||
|
||||
log_verbose("Found version of %s %s is %s then requested %u.%u.%u.",
|
||||
argv[0], buf, ret ? "better" : "older", maj, min, patch);
|
||||
} else
|
||||
log_warn("WARNING: Cannot parse output '%s' from %s.", buf, argv[0]);
|
||||
|
||||
(void) pipe_close(&pdata);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _pool_callback(struct dm_tree_node *node,
|
||||
dm_node_callback_t type, void *cb_data)
|
||||
{
|
||||
@@ -1976,6 +2152,19 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
|
||||
if (!(ret = exec_cmd(pool_lv->vg->cmd, (const char * const *)argv,
|
||||
&status, 0))) {
|
||||
if (status == ENOENT) {
|
||||
log_warn("WARNING: Check is skipped, please install recommended missing binary %s!",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((data->version.maj || data->version.min || data->version.patch) &&
|
||||
!_check_tool_version(pool_lv->vg->cmd, argv[0],
|
||||
data->version.maj, data->version.min, data->version.patch)) {
|
||||
log_warn("WARNING: Check is skipped, please upgrade installed version of %s!",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
switch (type) {
|
||||
case DM_NODE_CALLBACK_PRELOADED:
|
||||
log_err_once("Check of pool %s failed (status:%d). "
|
||||
@@ -2033,6 +2222,10 @@ static int _pool_register_callback(struct dev_manager *dm,
|
||||
data->exec = global_cache_check_executable_CFG;
|
||||
data->opts = global_cache_check_options_CFG;
|
||||
data->global = "cache";
|
||||
if (first_seg(first_seg(lv)->pool_lv)->cache_metadata_format > 1) {
|
||||
data->version.maj = 0;
|
||||
data->version.min = 7;
|
||||
}
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "Registering unsupported pool callback.");
|
||||
return 0;
|
||||
@@ -2298,7 +2491,7 @@ static char *_add_error_or_zero_device(struct dev_manager *dm, struct dm_tree *d
|
||||
seg->lv->name, errid)))
|
||||
return_NULL;
|
||||
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, &info, NULL, NULL))
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, 0, &info, NULL, NULL))
|
||||
return_NULL;
|
||||
|
||||
if (!info.exists) {
|
||||
@@ -2564,11 +2757,6 @@ static int _add_target_to_dtree(struct dev_manager *dm,
|
||||
&dm->pvmove_mirror_count);
|
||||
}
|
||||
|
||||
static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts,
|
||||
const char *layer);
|
||||
|
||||
static int _add_new_external_lv_to_dtree(struct dev_manager *dm,
|
||||
struct dm_tree *dtree,
|
||||
struct logical_volume *external_lv,
|
||||
@@ -2806,6 +2994,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
}
|
||||
|
||||
lvlayer->lv = lv;
|
||||
lvlayer->visible_component = (laopts->component_lv == lv) ? 1 : 0;
|
||||
|
||||
/*
|
||||
* Add LV to dtree.
|
||||
@@ -2819,10 +3008,11 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!(dnode = dm_tree_add_new_dev_with_udev_flags(dtree, name, dlid,
|
||||
layer ? UINT32_C(0) : (uint32_t) lv->major,
|
||||
layer ? UINT32_C(0) : (uint32_t) lv->minor,
|
||||
read_only_lv(lv, laopts),
|
||||
read_only_lv(lv, laopts, layer),
|
||||
((lv->vg->status & PRECOMMITTED) | laopts->revert) ? 1 : 0,
|
||||
lvlayer,
|
||||
_get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary))))
|
||||
_get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary,
|
||||
lvlayer->visible_component))))
|
||||
return_0;
|
||||
|
||||
/* Store existing name so we can do rename later */
|
||||
@@ -2901,6 +3091,17 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
!_pool_register_callback(dm, dnode, lv))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* Update tables for ANY PVMOVE holders for active LV where the name starts with 'pvmove',
|
||||
* but it's not anymore PVMOVE LV and also it's not a PVMOVE _mimage LV.
|
||||
* When resume happens, tables MUST be already preloaded with correct entries!
|
||||
* (since we can't preload different table while devices are suspended)
|
||||
*/
|
||||
if (!lv_is_pvmove(lv) && !strncmp(lv->name, "pvmove", 6) && !strchr(lv->name, '_') &&
|
||||
(dinfo = _cached_dm_info(dm->mem, dtree, lv, NULL)))
|
||||
if (!_add_holders_to_dtree(dm, dtree, lv, laopts, dinfo))
|
||||
return_0;
|
||||
|
||||
if (read_ahead == DM_READ_AHEAD_AUTO) {
|
||||
/* we need RA at least twice a whole stripe - see the comment in md/raid0.c */
|
||||
read_ahead = max_stripe_size * 2;
|
||||
@@ -2966,7 +3167,7 @@ static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root
|
||||
r = 0;
|
||||
continue;
|
||||
}
|
||||
if (_lv_has_mknode(lvlayer->lv)) {
|
||||
if (_lv_has_mknode(lvlayer->lv) || lvlayer->visible_component) {
|
||||
if (!_dev_manager_lv_mknodes(lvlayer->lv))
|
||||
r = 0;
|
||||
continue;
|
||||
@@ -3020,13 +3221,6 @@ static int _clean_tree(struct dev_manager *dm, struct dm_tree_node *root, const
|
||||
const char *name, *uuid;
|
||||
struct dm_str_list *dl;
|
||||
|
||||
/* Deactivate any tracked pending delete nodes */
|
||||
dm_list_iterate_items(dl, &dm->pending_delete) {
|
||||
log_debug_activation("Deleting tracked UUID %s.", dl->str);
|
||||
if (!dm_tree_deactivate_children(root, dl->str, strlen(dl->str)))
|
||||
return_0;
|
||||
}
|
||||
|
||||
while ((child = dm_tree_next_child(&handle, root, 0))) {
|
||||
if (!(name = dm_tree_node_get_name(child)))
|
||||
continue;
|
||||
@@ -3047,10 +3241,28 @@ static int _clean_tree(struct dev_manager *dm, struct dm_tree_node *root, const
|
||||
if (non_toplevel_tree_dlid && !strcmp(non_toplevel_tree_dlid, uuid))
|
||||
continue;
|
||||
|
||||
if (!dm_tree_deactivate_children(root, uuid, strlen(uuid)))
|
||||
if (!(uuid = dm_pool_strdup(dm->cmd->pending_delete_mem, uuid))) {
|
||||
log_error("_clean_tree: Failed to duplicate uuid.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!str_list_add(dm->cmd->pending_delete_mem, &dm->cmd->pending_delete, uuid))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* Deactivate any tracked pending delete nodes */
|
||||
if (!dm_list_empty(&dm->cmd->pending_delete) && !dm_get_suspended_counter()) {
|
||||
fs_unlock();
|
||||
dm_tree_set_cookie(root, fs_get_cookie());
|
||||
dm_list_iterate_items(dl, &dm->cmd->pending_delete) {
|
||||
log_debug_activation("Deleting tracked UUID %s.", dl->str);
|
||||
if (!dm_tree_deactivate_children(root, dl->str, strlen(dl->str)))
|
||||
return_0;
|
||||
}
|
||||
dm_list_init(&dm->cmd->pending_delete);
|
||||
dm_pool_empty(dm->cmd->pending_delete_mem);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ struct dm_info;
|
||||
struct device;
|
||||
struct lv_seg_status;
|
||||
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts);
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts, const char *layer);
|
||||
|
||||
/*
|
||||
* Constructor and destructor.
|
||||
@@ -47,7 +47,7 @@ void dev_manager_exit(void);
|
||||
*/
|
||||
int dev_manager_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
const char *layer,
|
||||
int with_open_count, int with_read_ahead,
|
||||
int with_open_count, int with_read_ahead, int with_name_check,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status);
|
||||
|
||||
|
||||
@@ -441,7 +441,7 @@ static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
|
||||
const char *lv_name, const char *dev, const char *old_lv_name,
|
||||
int check_udev)
|
||||
{
|
||||
if (critical_section()) {
|
||||
if (prioritized_section()) {
|
||||
if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev,
|
||||
old_lv_name, check_udev))
|
||||
return_0;
|
||||
@@ -487,7 +487,8 @@ int fs_rename_lv(const struct logical_volume *lv, const char *dev,
|
||||
|
||||
void fs_unlock(void)
|
||||
{
|
||||
if (!critical_section()) {
|
||||
/* Do not allow syncing device name with suspended devices */
|
||||
if (!dm_get_suspended_counter()) {
|
||||
log_debug_activation("Syncing device names");
|
||||
/* Wait for all processed udev devices */
|
||||
if (!dm_udev_wait(_fs_cookie))
|
||||
|
||||
1544
lib/cache/lvmcache.c
vendored
1544
lib/cache/lvmcache.c
vendored
File diff suppressed because it is too large
Load Diff
53
lib/cache/lvmcache.h
vendored
53
lib/cache/lvmcache.h
vendored
@@ -59,21 +59,17 @@ struct lvmcache_vgsummary {
|
||||
const char *lock_type;
|
||||
uint32_t mda_checksum;
|
||||
size_t mda_size;
|
||||
int zero_offset;
|
||||
int seqno;
|
||||
};
|
||||
|
||||
int lvmcache_init(void);
|
||||
int lvmcache_init(struct cmd_context *cmd);
|
||||
void lvmcache_allow_reads_with_lvmetad(void);
|
||||
|
||||
void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset);
|
||||
|
||||
/*
|
||||
* lvmcache_label_scan() will scan labels the first time it's
|
||||
* called, but not on subsequent calls, unless
|
||||
* lvmcache_force_next_label_scan() is called first
|
||||
* to force the next lvmcache_label_scan() to scan again.
|
||||
*/
|
||||
void lvmcache_force_next_label_scan(void);
|
||||
int lvmcache_label_scan(struct cmd_context *cmd);
|
||||
int lvmcache_label_rescan_vg(struct cmd_context *cmd, const char *vgname, const char *vgid, int open_rw);
|
||||
|
||||
/* Add/delete a device */
|
||||
struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
@@ -82,10 +78,11 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
uint32_t vgstatus);
|
||||
int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
|
||||
void lvmcache_del(struct lvmcache_info *info);
|
||||
void lvmcache_del_dev(struct device *dev);
|
||||
|
||||
/* Update things */
|
||||
int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
|
||||
const struct lvmcache_vgsummary *vgsummary);
|
||||
struct lvmcache_vgsummary *vgsummary);
|
||||
int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted);
|
||||
|
||||
void lvmcache_lock_vgname(const char *vgname, int read_only);
|
||||
@@ -105,10 +102,8 @@ struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid);
|
||||
struct lvmcache_info *lvmcache_info_from_pvid(const char *pvid, struct device *dev, int valid_only);
|
||||
const char *lvmcache_vgname_from_vgid(struct dm_pool *mem, const char *vgid);
|
||||
const char *lvmcache_vgid_from_vgname(struct cmd_context *cmd, const char *vgname);
|
||||
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
|
||||
unsigned *scan_done_once, uint64_t *label_sector);
|
||||
const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
const char *devname);
|
||||
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid, uint64_t *label_sector);
|
||||
const char *lvmcache_pvid_from_devname(struct cmd_context *cmd, const char *devname);
|
||||
char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
|
||||
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
|
||||
const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
|
||||
@@ -134,20 +129,16 @@ int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
|
||||
struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
|
||||
const char *vgid);
|
||||
|
||||
/* Returns cached volume group metadata. */
|
||||
struct volume_group *lvmcache_get_vg(struct cmd_context *cmd, const char *vgname,
|
||||
const char *vgid, unsigned precommitted);
|
||||
void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
|
||||
void lvmcache_commit_metadata(const char *vgname);
|
||||
|
||||
int lvmcache_pvid_is_locked(const char *pvid);
|
||||
int lvmcache_fid_add_mdas(struct lvmcache_info *info, struct format_instance *fid,
|
||||
const char *id, int id_len);
|
||||
int lvmcache_fid_add_mdas_pv(struct lvmcache_info *info, struct format_instance *fid);
|
||||
int lvmcache_fid_add_mdas_vg(struct lvmcache_vginfo *vginfo, struct format_instance *fid);
|
||||
int lvmcache_populate_pv_fields(struct lvmcache_info *info,
|
||||
struct physical_volume *pv,
|
||||
int scan_label_only);
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv);
|
||||
int lvmcache_check_format(struct lvmcache_info *info, const struct format_type *fmt);
|
||||
void lvmcache_del_mdas(struct lvmcache_info *info);
|
||||
void lvmcache_del_das(struct lvmcache_info *info);
|
||||
@@ -164,6 +155,8 @@ uint32_t lvmcache_ext_flags(struct lvmcache_info *info);
|
||||
|
||||
const struct format_type *lvmcache_fmt(struct lvmcache_info *info);
|
||||
struct label *lvmcache_get_label(struct lvmcache_info *info);
|
||||
struct label *lvmcache_get_dev_label(struct device *dev);
|
||||
int lvmcache_has_dev_info(struct device *dev);
|
||||
|
||||
void lvmcache_update_pv(struct lvmcache_info *info, struct physical_volume *pv,
|
||||
const struct format_type *fmt);
|
||||
@@ -187,7 +180,6 @@ int lvmcache_foreach_pv(struct lvmcache_vginfo *vginfo,
|
||||
uint64_t lvmcache_device_size(struct lvmcache_info *info);
|
||||
void lvmcache_set_device_size(struct lvmcache_info *info, uint64_t size);
|
||||
struct device *lvmcache_device(struct lvmcache_info *info);
|
||||
void lvmcache_make_valid(struct lvmcache_info *info);
|
||||
int lvmcache_is_orphan(struct lvmcache_info *info);
|
||||
int lvmcache_uncertain_ownership(struct lvmcache_info *info);
|
||||
unsigned lvmcache_mda_count(struct lvmcache_info *info);
|
||||
@@ -196,6 +188,8 @@ uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info);
|
||||
|
||||
int lvmcache_found_duplicate_pvs(void);
|
||||
|
||||
void lvmcache_pvscan_duplicate_check(struct cmd_context *cmd);
|
||||
|
||||
int lvmcache_get_unused_duplicate_devs(struct cmd_context *cmd, struct dm_list *head);
|
||||
|
||||
int vg_has_duplicate_pvs(struct volume_group *vg);
|
||||
@@ -215,4 +209,23 @@ void lvmcache_remove_unchosen_duplicate(struct device *dev);
|
||||
|
||||
int lvmcache_pvid_in_unchosen_duplicates(const char *pvid);
|
||||
|
||||
int lvmcache_get_vg_devs(struct cmd_context *cmd,
|
||||
struct lvmcache_vginfo *vginfo,
|
||||
struct dm_list *devs);
|
||||
void lvmcache_set_independent_location(const char *vgname);
|
||||
|
||||
int lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid);
|
||||
|
||||
/*
|
||||
* These are clvmd-specific functions and are not related to lvmcache.
|
||||
* FIXME: rename these with a clvm_ prefix in place of lvmcache_
|
||||
*/
|
||||
void lvmcache_save_vg(struct volume_group *vg, int precommitted);
|
||||
struct volume_group *lvmcache_get_saved_vg(const char *vgid, int precommitted);
|
||||
struct volume_group *lvmcache_get_saved_vg_latest(const char *vgid);
|
||||
void lvmcache_drop_saved_vgid(const char *vgid);
|
||||
|
||||
uint64_t lvmcache_max_metadata_size(void);
|
||||
void lvmcache_save_metadata_size(uint64_t val);
|
||||
|
||||
#endif
|
||||
|
||||
652
lib/cache/lvmetad.c
vendored
652
lib/cache/lvmetad.c
vendored
File diff suppressed because it is too large
Load Diff
78
lib/cache/lvmetad.h
vendored
78
lib/cache/lvmetad.h
vendored
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "config-util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct volume_group;
|
||||
struct cmd_context;
|
||||
struct dm_config_tree;
|
||||
@@ -149,7 +151,7 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames);
|
||||
|
||||
int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait);
|
||||
int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait, struct dm_list *found_vgnames);
|
||||
|
||||
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
||||
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
|
||||
@@ -163,38 +165,48 @@ void lvmetad_clear_disabled(struct cmd_context *cmd);
|
||||
|
||||
# else /* LVMETAD_SUPPORT */
|
||||
|
||||
# define lvmetad_disconnect() do { } while (0)
|
||||
# define lvmetad_connect(cmd) (0)
|
||||
# define lvmetad_make_unused(cmd) do { } while (0)
|
||||
# define lvmetad_used() (0)
|
||||
# define lvmetad_set_socket(a) do { } while (0)
|
||||
# define lvmetad_socket_present() (0)
|
||||
# define lvmetad_pidfile_present() (0)
|
||||
# define lvmetad_set_token(a) do { } while (0)
|
||||
# define lvmetad_release_token() do { } while (0)
|
||||
# define lvmetad_vg_update(vg) (1)
|
||||
# define lvmetad_vg_update_pending(vg) (1)
|
||||
# define lvmetad_vg_update_finish(vg) (1)
|
||||
# define lvmetad_vg_remove_pending(vg) (1)
|
||||
# define lvmetad_vg_remove_finish(vg) (1)
|
||||
# define lvmetad_pv_found(cmd, pvid, dev, fmt, label_sector, vg, found_vgnames, changed_vgnames) (1)
|
||||
# define lvmetad_pv_gone(devno, pv_name) (1)
|
||||
# define lvmetad_pv_gone_by_dev(dev) (1)
|
||||
# define lvmetad_pv_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_pv_lookup(cmd, pvid, found) (0)
|
||||
# define lvmetad_pv_lookup_by_dev(cmd, dev, found) (0)
|
||||
# define lvmetad_vg_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_get_vgnameids(cmd, vgnameids) do { } while (0)
|
||||
# define lvmetad_vg_lookup(cmd, vgname, vgid) (NULL)
|
||||
# define lvmetad_pvscan_single(cmd, dev, found_vgnames, changed_vgnames) (0)
|
||||
# define lvmetad_pvscan_all_devs(cmd, do_wait) (0)
|
||||
# define lvmetad_vg_clear_outdated_pvs(vg) do { } while (0)
|
||||
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
|
||||
# define lvmetad_vg_is_foreign(cmd, vgname, vgid) (0)
|
||||
# define lvmetad_token_matches(cmd) (1)
|
||||
# define lvmetad_is_disabled(cmd, reason) (0)
|
||||
# define lvmetad_set_disabled(cmd, reason) do { } while (0)
|
||||
# define lvmetad_clear_disabled(cmd) do { } while (0)
|
||||
static inline int lvmetad_connect(struct cmd_context *cmd) {return 0;}
|
||||
static inline void lvmetad_disconnect(void) {}
|
||||
static inline void lvmetad_make_unused(struct cmd_context *cmd) {}
|
||||
static inline int lvmetad_used(void) {return 0;}
|
||||
static inline void lvmetad_set_socket(const char *thing) {}
|
||||
static inline int lvmetad_socket_present(void) {return 0;}
|
||||
static inline int lvmetad_pidfile_present(void) {return 0;}
|
||||
static inline void lvmetad_set_token(const struct dm_config_value *filter) {}
|
||||
static inline void lvmetad_release_token(void) {}
|
||||
static inline int lvmetad_vg_update_pending(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_update_finish(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_remove_pending(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_remove_finish(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct device *dev,
|
||||
const struct format_type *fmt, uint64_t label_sector,
|
||||
struct volume_group *vg,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames) {return 1;}
|
||||
static inline int lvmetad_pv_gone(dev_t devno, const char *pv_name) {return 1;}
|
||||
static inline int lvmetad_pv_gone_by_dev(struct device *dev) {return 1;}
|
||||
static inline int lvmetad_pv_list_to_lvmcache(struct cmd_context *cmd) {return 1;}
|
||||
static inline int lvmetad_pv_lookup(struct cmd_context *cmd, struct id pvid, int *found) {return 0;}
|
||||
static inline int lvmetad_pv_lookup_by_dev(struct cmd_context *cmd, struct device *dev, int *found) {return 0;}
|
||||
static inline int lvmetad_vg_list_to_lvmcache(struct cmd_context *cmd) {return 1;}
|
||||
static inline int lvmetad_get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids) {return 0;}
|
||||
static inline struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd,
|
||||
const char *vgname, const char *vgid) {return NULL;}
|
||||
static inline int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames) {return 0;}
|
||||
|
||||
static inline int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait, struct dm_list *found_vgnames) {return 0;}
|
||||
|
||||
static inline int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg) {return 0;}
|
||||
static inline void lvmetad_validate_global_cache(struct cmd_context *cmd, int force) {}
|
||||
static inline int lvmetad_token_matches(struct cmd_context *cmd) {return 1;}
|
||||
|
||||
static inline int lvmetad_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const char *vgid) {return 0;}
|
||||
|
||||
static inline int lvmetad_is_disabled(struct cmd_context *cmd, const char **reason) {return 0;}
|
||||
static inline void lvmetad_set_disabled(struct cmd_context *cmd, const char *reason) {}
|
||||
static inline void lvmetad_clear_disabled(struct cmd_context *cmd) {}
|
||||
|
||||
# endif /* LVMETAD_SUPPORT */
|
||||
|
||||
|
||||
@@ -36,16 +36,9 @@
|
||||
#include "sharedlib.h"
|
||||
#endif
|
||||
|
||||
#ifdef LVM1_INTERNAL
|
||||
#include "format1.h"
|
||||
#endif
|
||||
|
||||
#ifdef POOL_INTERNAL
|
||||
#include "format_pool.h"
|
||||
#endif
|
||||
|
||||
#include <locale.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
@@ -340,6 +333,8 @@ static void _init_logging(struct cmd_context *cmd)
|
||||
find_config_tree_bool(cmd, global_test_CFG, NULL);
|
||||
init_test(cmd->default_settings.test);
|
||||
|
||||
init_use_aio(find_config_tree_bool(cmd, global_use_aio_CFG, NULL));
|
||||
|
||||
/* Settings for logging to file */
|
||||
if (find_config_tree_bool(cmd, log_overwrite_CFG, NULL))
|
||||
append = 0;
|
||||
@@ -635,16 +630,6 @@ static int _process_config(struct cmd_context *cmd)
|
||||
*/
|
||||
cmd->default_settings.udev_fallback = udev_disabled ? 1 : -1;
|
||||
|
||||
#ifdef AIO_SUPPORT
|
||||
cmd->use_aio = find_config_tree_bool(cmd, devices_use_aio_CFG, NULL);
|
||||
#else
|
||||
cmd->use_aio = 0;
|
||||
#endif
|
||||
if (cmd->use_aio && !dev_async_setup(cmd))
|
||||
cmd->use_aio = 0;
|
||||
|
||||
log_debug_io("%ssing asynchronous I/O.", cmd->use_aio ? "U" : "Not u");
|
||||
|
||||
init_retry_deactivation(find_config_tree_bool(cmd, activation_retry_deactivation_CFG, NULL));
|
||||
|
||||
init_activation_checks(find_config_tree_bool(cmd, activation_checks_CFG, NULL));
|
||||
@@ -697,12 +682,11 @@ static int _process_config(struct cmd_context *cmd)
|
||||
if (find_config_tree_bool(cmd, report_two_word_unknown_device_CFG, NULL))
|
||||
init_unknown_device_name("unknown device");
|
||||
|
||||
init_detect_internal_vg_cache_corruption
|
||||
(find_config_tree_bool(cmd, global_detect_internal_vg_cache_corruption_CFG, NULL));
|
||||
|
||||
if (!_init_system_id(cmd))
|
||||
return_0;
|
||||
|
||||
init_io_memory_size(find_config_tree_int(cmd, global_io_memory_size_CFG, NULL));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1073,7 +1057,7 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_FILTERS 9
|
||||
#define MAX_FILTERS 10
|
||||
|
||||
static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
{
|
||||
@@ -1133,7 +1117,7 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
nr_filt++;
|
||||
|
||||
/* usable device filter. Required. */
|
||||
if (!(filters[nr_filt] = usable_filter_create(cmd->dev_types,
|
||||
if (!(filters[nr_filt] = usable_filter_create(cmd, cmd->dev_types,
|
||||
lvmetad_used() ? FILTER_MODE_PRE_LVMETAD
|
||||
: FILTER_MODE_NO_LVMETAD))) {
|
||||
log_error("Failed to create usabled device filter");
|
||||
@@ -1154,10 +1138,17 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/* signature filter. Required. */
|
||||
if (!(filters[nr_filt] = signature_filter_create(cmd->dev_types))) {
|
||||
log_error("Failed to create signature device filter");
|
||||
goto bad;
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/* md component filter. Optional, non-critical. */
|
||||
if (find_config_tree_bool(cmd, devices_md_component_detection_CFG, NULL)) {
|
||||
init_md_filtering(1);
|
||||
if ((filters[nr_filt] = md_filter_create(cmd->dev_types)))
|
||||
if ((filters[nr_filt] = md_filter_create(cmd, cmd->dev_types)))
|
||||
nr_filt++;
|
||||
}
|
||||
|
||||
@@ -1246,7 +1237,7 @@ int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
}
|
||||
nr_filt++;
|
||||
}
|
||||
if (!(filter_components[nr_filt] = usable_filter_create(cmd->dev_types, FILTER_MODE_POST_LVMETAD))) {
|
||||
if (!(filter_components[nr_filt] = usable_filter_create(cmd, cmd->dev_types, FILTER_MODE_POST_LVMETAD))) {
|
||||
log_verbose("Failed to create usable device filter.");
|
||||
goto bad;
|
||||
}
|
||||
@@ -1297,7 +1288,7 @@ int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
lvm_stat_ctim(&ts, &st);
|
||||
cts = config_file_timestamp(cmd->cft);
|
||||
if (timespeccmp(&ts, &cts, >) &&
|
||||
!persistent_filter_load(cmd->mem, cmd->filter, NULL))
|
||||
!persistent_filter_load(cmd->filter, NULL))
|
||||
log_verbose("Failed to load existing device cache from %s",
|
||||
dev_cache);
|
||||
}
|
||||
@@ -1353,20 +1344,6 @@ static int _init_formats(struct cmd_context *cmd)
|
||||
const struct dm_config_node *cn;
|
||||
#endif
|
||||
|
||||
#ifdef LVM1_INTERNAL
|
||||
if (!(fmt = init_lvm1_format(cmd)))
|
||||
return 0;
|
||||
fmt->library = NULL;
|
||||
dm_list_add(&cmd->formats, &fmt->list);
|
||||
#endif
|
||||
|
||||
#ifdef POOL_INTERNAL
|
||||
if (!(fmt = init_pool_format(cmd)))
|
||||
return 0;
|
||||
fmt->library = NULL;
|
||||
dm_list_add(&cmd->formats, &fmt->list);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDL
|
||||
/* Load any formats in shared libs if not static */
|
||||
if (!is_static() &&
|
||||
@@ -1489,6 +1466,7 @@ static int _init_segtypes(struct cmd_context *cmd)
|
||||
struct segment_type *segtype;
|
||||
struct segtype_library seglib = { .cmd = cmd, .lib = NULL };
|
||||
struct segment_type *(*init_segtype_array[])(struct cmd_context *cmd) = {
|
||||
init_linear_segtype,
|
||||
init_striped_segtype,
|
||||
init_zero_segtype,
|
||||
init_error_segtype,
|
||||
@@ -1660,7 +1638,6 @@ static void _init_rand(struct cmd_context *cmd)
|
||||
|
||||
static void _init_globals(struct cmd_context *cmd)
|
||||
{
|
||||
init_full_scan_done(0);
|
||||
init_mirror_in_sync(0);
|
||||
}
|
||||
|
||||
@@ -1784,6 +1761,8 @@ void destroy_config_context(struct cmd_context *cmd)
|
||||
dm_pool_destroy(cmd->mem);
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
if (cmd->pending_delete_mem)
|
||||
dm_pool_destroy(cmd->pending_delete_mem);
|
||||
|
||||
dm_free(cmd);
|
||||
}
|
||||
@@ -1791,6 +1770,8 @@ void destroy_config_context(struct cmd_context *cmd)
|
||||
/*
|
||||
* A "config context" is a very light weight toolcontext that
|
||||
* is only used for reading config settings from lvm.conf.
|
||||
*
|
||||
* FIXME: this needs to go back to parametrized create_toolcontext()
|
||||
*/
|
||||
struct cmd_context *create_config_context(void)
|
||||
{
|
||||
@@ -1807,6 +1788,12 @@ struct cmd_context *create_config_context(void)
|
||||
if (!(cmd->libmem = dm_pool_create("library", 4 * 1024)))
|
||||
goto_out;
|
||||
|
||||
if (!(cmd->mem = dm_pool_create("command", 4 * 1024)))
|
||||
goto out;
|
||||
|
||||
if (!(cmd->pending_delete_mem = dm_pool_create("pending_delete", 1024)))
|
||||
goto_out;
|
||||
|
||||
dm_list_init(&cmd->config_files);
|
||||
dm_list_init(&cmd->tags);
|
||||
|
||||
@@ -1837,7 +1824,7 @@ out:
|
||||
}
|
||||
|
||||
/* Entry point */
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded,
|
||||
@@ -1864,7 +1851,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
log_error("Failed to allocate command context");
|
||||
return NULL;
|
||||
}
|
||||
cmd->is_long_lived = is_long_lived;
|
||||
cmd->is_long_lived = is_clvmd;
|
||||
cmd->is_clvmd = is_clvmd;
|
||||
cmd->threaded = threaded ? 1 : 0;
|
||||
cmd->handles_missing_pvs = 0;
|
||||
cmd->handles_unknown_segments = 0;
|
||||
@@ -1883,7 +1871,13 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
|
||||
#ifndef VALGRIND_POOL
|
||||
/* Set in/out stream buffering before glibc */
|
||||
if (set_buffering) {
|
||||
if (set_buffering
|
||||
#ifdef SYS_gettid
|
||||
/* For threaded programs no changes of streams */
|
||||
/* On linux gettid() is implemented only via syscall */
|
||||
&& (syscall(SYS_gettid) == getpid())
|
||||
#endif
|
||||
) {
|
||||
/* Allocate 2 buffers */
|
||||
if (!(cmd->linebuffer = dm_malloc(2 * _linebuffer_size))) {
|
||||
log_error("Failed to allocate line buffer.");
|
||||
@@ -1914,7 +1908,7 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
}
|
||||
}
|
||||
/* Buffers are used for lines without '\n' */
|
||||
} else
|
||||
} else if (!set_buffering)
|
||||
/* Without buffering, must not use stdin/stdout */
|
||||
init_silent(1);
|
||||
#endif
|
||||
@@ -1948,6 +1942,9 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(cmd->pending_delete_mem = dm_pool_create("pending_delete", 1024)))
|
||||
goto_out;
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
goto_out;
|
||||
|
||||
@@ -1987,10 +1984,12 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
if (!_init_formats(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
if (!lvmcache_init(cmd))
|
||||
goto_out;
|
||||
|
||||
dm_list_init(&cmd->unused_duplicate_devs);
|
||||
/* FIXME: move into lvmcache_init */
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!_init_segtypes(cmd))
|
||||
goto_out;
|
||||
@@ -2008,10 +2007,11 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
if (set_filters && !init_filters(cmd, 1))
|
||||
goto_out;
|
||||
|
||||
cmd->default_settings.cache_vgmetadata = 1;
|
||||
cmd->current_settings = cmd->default_settings;
|
||||
|
||||
cmd->initialized.config = 1;
|
||||
|
||||
dm_list_init(&cmd->pending_delete);
|
||||
out:
|
||||
if (!cmd->initialized.config) {
|
||||
destroy_toolcontext(cmd);
|
||||
@@ -2122,6 +2122,7 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
activation_release();
|
||||
lvmcache_destroy(cmd, 0, 0);
|
||||
label_scan_destroy(cmd);
|
||||
label_exit();
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
@@ -2148,8 +2149,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
cmd->lib_dir = NULL;
|
||||
|
||||
label_init();
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -2210,6 +2209,9 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
if (!_init_formats(cmd))
|
||||
return_0;
|
||||
|
||||
if (!lvmcache_init(cmd))
|
||||
return_0;
|
||||
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -2221,6 +2223,12 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
cmd->initialized.config = 1;
|
||||
|
||||
if (!dm_list_empty(&cmd->pending_delete)) {
|
||||
log_debug(INTERNAL_ERROR "Unprocessed pending delete for %d devices.",
|
||||
dm_list_size(&cmd->pending_delete));
|
||||
dm_list_init(&cmd->pending_delete);
|
||||
}
|
||||
|
||||
if (cmd->initialized.connections && !init_connections(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -2237,12 +2245,13 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
int flags;
|
||||
|
||||
if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
|
||||
!cmd->filter->dump(cmd->filter, cmd->mem, 1))
|
||||
!cmd->filter->dump(cmd->filter, 1))
|
||||
stack;
|
||||
|
||||
archive_exit(cmd);
|
||||
backup_exit(cmd);
|
||||
lvmcache_destroy(cmd, 0, 0);
|
||||
label_scan_destroy(cmd);
|
||||
label_exit();
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
@@ -2263,6 +2272,8 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
|
||||
if (cmd->pending_delete_mem)
|
||||
dm_pool_destroy(cmd->pending_delete_mem);
|
||||
#ifndef VALGRIND_POOL
|
||||
if (cmd->linebuffer) {
|
||||
/* Reset stream buffering to defaults */
|
||||
|
||||
@@ -42,6 +42,7 @@ struct config_info {
|
||||
int cache_vgmetadata;
|
||||
const char *msg_prefix;
|
||||
const char *fmt_name;
|
||||
const char *dmeventd_executable;
|
||||
uint64_t unit_factor;
|
||||
int cmd_name; /* Show command name? */
|
||||
mode_t umask;
|
||||
@@ -94,6 +95,7 @@ struct cmd_context {
|
||||
char **argv;
|
||||
struct arg_values *opt_arg_values;
|
||||
struct dm_list arg_value_groups;
|
||||
int opt_count; /* total number of options (beginning with - or --) */
|
||||
|
||||
/*
|
||||
* Position args remaining after command name
|
||||
@@ -153,6 +155,8 @@ struct cmd_context {
|
||||
unsigned include_shared_vgs:1; /* report/display cmds can reveal lockd VGs */
|
||||
unsigned include_active_foreign_vgs:1; /* cmd should process foreign VGs with active LVs */
|
||||
unsigned vg_read_print_access_error:1; /* print access errors from vg_read */
|
||||
unsigned allow_mixed_block_sizes:1;
|
||||
unsigned force_access_clustered:1;
|
||||
unsigned lockd_gl_disable:1;
|
||||
unsigned lockd_vg_disable:1;
|
||||
unsigned lockd_lv_disable:1;
|
||||
@@ -160,11 +164,16 @@ struct cmd_context {
|
||||
unsigned lockd_vg_rescan:1;
|
||||
unsigned lockd_vg_default_sh:1;
|
||||
unsigned lockd_vg_enforce_sh:1;
|
||||
unsigned lockd_lv_sh:1;
|
||||
unsigned vg_notify:1;
|
||||
unsigned lv_notify:1;
|
||||
unsigned pv_notify:1;
|
||||
unsigned use_aio:1;
|
||||
unsigned activate_component:1; /* command activates component LV */
|
||||
unsigned process_component_lvs:1; /* command processes also component LVs */
|
||||
unsigned mirror_warn_printed:1; /* command already printed warning about non-monitored mirrors */
|
||||
unsigned pvscan_cache_single:1;
|
||||
unsigned can_use_one_scan:1;
|
||||
unsigned is_clvmd:1;
|
||||
unsigned use_full_md_check:1;
|
||||
|
||||
/*
|
||||
* Filtering.
|
||||
@@ -223,14 +232,15 @@ struct cmd_context {
|
||||
const char *report_list_item_separator;
|
||||
const char *time_format;
|
||||
unsigned rand_seed;
|
||||
struct dm_list unused_duplicate_devs; /* save preferences between lvmcache instances */
|
||||
struct dm_list pending_delete; /* list of LVs for removal */
|
||||
struct dm_pool *pending_delete_mem; /* memory pool for pending deletes */
|
||||
};
|
||||
|
||||
/*
|
||||
* system_dir may be NULL to use the default value.
|
||||
* The environment variable LVM_SYSTEM_DIR always takes precedence.
|
||||
*/
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "toolcontext.h"
|
||||
#include "lvm-file.h"
|
||||
#include "memlock.h"
|
||||
#include "label.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -279,7 +280,7 @@ struct dm_config_tree *config_file_open_and_read(const char *config_file,
|
||||
}
|
||||
|
||||
log_very_verbose("Loading config file: %s", config_file);
|
||||
if (!config_file_read(cmd->mem, cft)) {
|
||||
if (!config_file_read(cft)) {
|
||||
log_error("Failed to load config file %s", config_file);
|
||||
goto bad;
|
||||
}
|
||||
@@ -489,102 +490,32 @@ int override_config_tree_from_profile(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct process_config_file_params {
|
||||
struct dm_config_tree *cft;
|
||||
struct device *dev;
|
||||
off_t offset;
|
||||
size_t size;
|
||||
off_t offset2;
|
||||
size_t size2;
|
||||
checksum_fn_t checksum_fn;
|
||||
uint32_t checksum;
|
||||
int checksum_only;
|
||||
int no_dup_node_check;
|
||||
lvm_callback_fn_t config_file_read_fd_callback;
|
||||
void *config_file_read_fd_context;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void _process_config_file_buffer(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct process_config_file_params *pcfp = context;
|
||||
const char *fb = data, *fe;
|
||||
|
||||
if (failed) {
|
||||
pcfp->ret = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (pcfp->checksum_fn && pcfp->checksum !=
|
||||
(pcfp->checksum_fn(pcfp->checksum_fn(INITIAL_CRC, (const uint8_t *)fb, pcfp->size),
|
||||
(const uint8_t *)(fb + pcfp->size), pcfp->size2))) {
|
||||
log_error("%s: Checksum error at offset %" PRIu64, dev_name(pcfp->dev), (uint64_t) pcfp->offset);
|
||||
pcfp->ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pcfp->checksum_only) {
|
||||
fe = fb + pcfp->size + pcfp->size2;
|
||||
if (pcfp->no_dup_node_check) {
|
||||
if (!dm_config_parse_without_dup_node_check(pcfp->cft, fb, fe))
|
||||
pcfp->ret = 0;
|
||||
} else if (!dm_config_parse(pcfp->cft, fb, fe))
|
||||
pcfp->ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (pcfp->config_file_read_fd_callback)
|
||||
pcfp->config_file_read_fd_callback(!pcfp->ret, ioflags, pcfp->config_file_read_fd_context, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* When checksum_only is set, the checksum of buffer is only matched
|
||||
* and function avoids parsing of mda into config tree which
|
||||
* remains unmodified and should not be used.
|
||||
*/
|
||||
int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int checksum_only, int no_dup_node_check, unsigned ioflags,
|
||||
lvm_callback_fn_t config_file_read_fd_callback, void *config_file_read_fd_context)
|
||||
int checksum_only, int no_dup_node_check)
|
||||
{
|
||||
char *fb;
|
||||
char *fb, *fe;
|
||||
int r = 0;
|
||||
off_t mmap_offset = 0;
|
||||
int use_mmap = 1;
|
||||
const char *buf = NULL;
|
||||
unsigned circular = size2 ? 1 : 0; /* Wrapped around end of disk metadata buffer? */
|
||||
off_t mmap_offset = 0;
|
||||
char *buf = NULL;
|
||||
struct config_source *cs = dm_config_get_custom(cft);
|
||||
struct process_config_file_params *pcfp;
|
||||
|
||||
if (!_is_file_based_config_source(cs->type)) {
|
||||
log_error(INTERNAL_ERROR "config_file_read_fd: expected file, special file "
|
||||
"or profile config source, found %s config source.",
|
||||
_config_source_names[cs->type]);
|
||||
goto bad;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pcfp = dm_pool_zalloc(mem, sizeof(*pcfp)))) {
|
||||
log_debug("config_file_read_fd: process_config_file_params struct allocation failed");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pcfp->cft = cft;
|
||||
pcfp->dev = dev;
|
||||
pcfp->offset = offset;
|
||||
pcfp->size = size;
|
||||
pcfp->offset2 = offset2;
|
||||
pcfp->size2 = size2;
|
||||
pcfp->checksum_fn = checksum_fn;
|
||||
pcfp->checksum = checksum;
|
||||
pcfp->checksum_only = checksum_only;
|
||||
pcfp->no_dup_node_check = no_dup_node_check;
|
||||
pcfp->config_file_read_fd_callback = config_file_read_fd_callback;
|
||||
pcfp->config_file_read_fd_context = config_file_read_fd_context;
|
||||
pcfp->ret = 1;
|
||||
|
||||
/* Only use mmap with regular files */
|
||||
if (!(dev->flags & DEV_REGULAR) || circular)
|
||||
if (!(dev->flags & DEV_REGULAR) || size2)
|
||||
use_mmap = 0;
|
||||
|
||||
if (use_mmap) {
|
||||
@@ -594,40 +525,67 @@ int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct
|
||||
MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
|
||||
if (fb == (caddr_t) (-1)) {
|
||||
log_sys_error("mmap", dev_name(dev));
|
||||
goto bad;
|
||||
goto out;
|
||||
}
|
||||
_process_config_file_buffer(0, ioflags, pcfp, fb + mmap_offset);
|
||||
r = pcfp->ret;
|
||||
fb = fb + mmap_offset;
|
||||
} else {
|
||||
if (!(buf = dm_malloc(size + size2))) {
|
||||
log_error("Failed to allocate circular buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_read_bytes(dev, offset, size, buf))
|
||||
goto out;
|
||||
|
||||
if (size2) {
|
||||
if (!dev_read_bytes(dev, offset2, size2, buf + size))
|
||||
goto out;
|
||||
}
|
||||
|
||||
fb = buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* The checksum passed in is the checksum from the mda_header
|
||||
* preceding this metadata. They should always match.
|
||||
* FIXME: handle case where mda_header checksum is bad,
|
||||
* but the checksum calculated here is correct.
|
||||
*/
|
||||
if (checksum_fn && checksum !=
|
||||
(checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)fb, size),
|
||||
(const uint8_t *)(fb + size), size2))) {
|
||||
log_error("%s: Checksum error at offset %" PRIu64, dev_name(dev), (uint64_t) offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!checksum_only) {
|
||||
fe = fb + size + size2;
|
||||
if (no_dup_node_check) {
|
||||
if (!dm_config_parse_without_dup_node_check(cft, fb, fe))
|
||||
goto_out;
|
||||
} else {
|
||||
if (!dm_config_parse(cft, fb, fe))
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (!use_mmap)
|
||||
dm_free(buf);
|
||||
else {
|
||||
/* unmap the file */
|
||||
if (munmap(fb, size + mmap_offset)) {
|
||||
if (munmap(fb - mmap_offset, size + mmap_offset)) {
|
||||
log_sys_error("munmap", dev_name(dev));
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
if (circular) {
|
||||
if (!(buf = dev_read_circular(dev, (uint64_t) offset, size, (uint64_t) offset2, size2, reason)))
|
||||
goto_out;
|
||||
_process_config_file_buffer(0, ioflags, pcfp, buf);
|
||||
dm_free((void *)buf);
|
||||
} else {
|
||||
dev_read_callback(dev, (uint64_t) offset, size, reason, ioflags, _process_config_file_buffer, pcfp);
|
||||
if (config_file_read_fd_callback)
|
||||
return 1;
|
||||
}
|
||||
r = pcfp->ret;
|
||||
}
|
||||
|
||||
out:
|
||||
return r;
|
||||
|
||||
bad:
|
||||
if (config_file_read_fd_callback)
|
||||
config_file_read_fd_callback(1, ioflags, config_file_read_fd_context, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_file_read(struct dm_pool *mem, struct dm_config_tree *cft)
|
||||
int config_file_read(struct dm_config_tree *cft)
|
||||
{
|
||||
const char *filename = NULL;
|
||||
struct config_source *cs = dm_config_get_custom(cft);
|
||||
@@ -655,8 +613,8 @@ int config_file_read(struct dm_pool *mem, struct dm_config_tree *cft)
|
||||
}
|
||||
}
|
||||
|
||||
r = config_file_read_fd(mem, cft, cf->dev, DEV_IO_MDA_CONTENT, 0, (size_t) info.st_size, 0, 0,
|
||||
(checksum_fn_t) NULL, 0, 0, 0, 0, NULL, NULL);
|
||||
r = config_file_read_fd(cft, cf->dev, DEV_IO_MDA_CONTENT, 0, (size_t) info.st_size, 0, 0,
|
||||
(checksum_fn_t) NULL, 0, 0, 0);
|
||||
|
||||
if (!cf->keep_open) {
|
||||
if (!dev_close(cf->dev))
|
||||
|
||||
@@ -239,13 +239,11 @@ config_source_t config_get_source_type(struct dm_config_tree *cft);
|
||||
typedef uint32_t (*checksum_fn_t) (uint32_t initial, const uint8_t *buf, uint32_t size);
|
||||
|
||||
struct dm_config_tree *config_open(config_source_t source, const char *filename, int keep_open);
|
||||
int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int skip_parse, int no_dup_node_check, unsigned ioflags,
|
||||
lvm_callback_fn_t config_file_read_fd_callback, void *config_file_read_fd_context);
|
||||
|
||||
int config_file_read(struct dm_pool *mem, struct dm_config_tree *cft);
|
||||
int skip_parse, int no_dup_node_check);
|
||||
int config_file_read(struct dm_config_tree *cft);
|
||||
struct dm_config_tree *config_file_open_and_read(const char *config_file, config_source_t source,
|
||||
struct cmd_context *cmd);
|
||||
int config_write(struct dm_config_tree *cft, struct config_def_tree_spec *tree_spec,
|
||||
|
||||
@@ -226,16 +226,6 @@ cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING,
|
||||
cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Directories containing device nodes to use with LVM.\n")
|
||||
|
||||
cfg(devices_use_aio_CFG, "use_aio", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_USE_AIO, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Use linux asynchronous I/O for parallel device access where possible.\n")
|
||||
|
||||
cfg(devices_aio_max_CFG, "aio_max", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MAX, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Maximum number of asynchronous I/Os to issue concurrently.\n")
|
||||
|
||||
cfg(devices_aio_memory_CFG, "aio_memory", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MEMORY, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Approximate maximum total amount of memory (in MB) used\n"
|
||||
"for asynchronous I/O buffers.\n")
|
||||
|
||||
cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
cfg(devices_obtain_device_list_from_udev_CFG, "obtain_device_list_from_udev", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV, vsn(2, 2, 85), NULL, 0, NULL,
|
||||
@@ -355,6 +345,19 @@ cfg(devices_sysfs_scan_CFG, "sysfs_scan", devices_CFG_SECTION, 0, CFG_TYPE_BOOL,
|
||||
"This is a quick way of filtering out block devices that are not\n"
|
||||
"present on the system. sysfs must be part of the kernel and mounted.)\n")
|
||||
|
||||
cfg(devices_scan_lvs_CFG, "scan_lvs", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SCAN_LVS, vsn(2, 2, 182), NULL, 0, NULL,
|
||||
"Scan LVM LVs for layered PVs, allowing LVs to be used as PVs.\n"
|
||||
"When 1, LVM will detect PVs layered on LVs, and caution must be\n"
|
||||
"taken to avoid a host accessing a layered VG that may not belong\n"
|
||||
"to it, e.g. from a guest image. This generally requires excluding\n"
|
||||
"the LVs with device filters. Also, when this setting is enabled,\n"
|
||||
"every LVM command will scan every active LV on the system (unless\n"
|
||||
"filtered), which can cause performance problems on systems with\n"
|
||||
"many active LVs. When this setting is 0, LVM will not detect or\n"
|
||||
"use PVs that exist on LVs, and will not allow a PV to be created on\n"
|
||||
"an LV. The LVs are ignored using a built in device filter that\n"
|
||||
"identifies and excludes LVs.\n")
|
||||
|
||||
cfg(devices_multipath_component_detection_CFG, "multipath_component_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MULTIPATH_COMPONENT_DETECTION, vsn(2, 2, 89), NULL, 0, NULL,
|
||||
"Ignore devices that are components of DM multipath devices.\n")
|
||||
|
||||
@@ -467,6 +470,11 @@ cfg(devices_allow_changes_with_duplicate_pvs_CFG, "allow_changes_with_duplicate_
|
||||
"Enabling this setting allows the VG to be used as usual even with\n"
|
||||
"uncertain devices.\n")
|
||||
|
||||
cfg(devices_allow_mixed_block_sizes_CFG, "allow_mixed_block_sizes", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 187), NULL, 0, NULL,
|
||||
"Allow PVs in the same VG with different logical block sizes.\n"
|
||||
"When allowed, the user is responsible to ensure that an LV is\n"
|
||||
"using PVs with matching block sizes when necessary.\n")
|
||||
|
||||
cfg_array(allocation_cling_tag_list_CFG, "cling_tag_list", allocation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 77), NULL, 0, NULL,
|
||||
"Advise LVM which PVs to use when searching for new space.\n"
|
||||
"When searching for free space to extend an LV, the 'cling' allocation\n"
|
||||
@@ -777,26 +785,14 @@ cfg(global_activation_CFG, "activation", global_CFG_SECTION, 0, CFG_TYPE_BOOL, D
|
||||
"is not present in the kernel, disabling this should suppress\n"
|
||||
"the error messages.\n")
|
||||
|
||||
cfg(global_fallback_to_lvm1_CFG, "fallback_to_lvm1", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_LVM1, vsn(1, 0, 18), "@DEFAULT_FALLBACK_TO_LVM1@", 0, NULL,
|
||||
"Try running LVM1 tools if LVM cannot communicate with DM.\n"
|
||||
"This option only applies to 2.4 kernels and is provided to help\n"
|
||||
"switch between device-mapper kernels and LVM1 kernels. The LVM1\n"
|
||||
"tools need to be installed with .lvm1 suffices, e.g. vgscan.lvm1.\n"
|
||||
"They will stop working once the lvm2 on-disk metadata format is used.\n")
|
||||
cfg(global_fallback_to_lvm1_CFG, "fallback_to_lvm1", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(1, 0, 18), NULL, 0, NULL,
|
||||
"This setting is no longer used.\n")
|
||||
|
||||
cfg(global_format_CFG, "format", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_FORMAT, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"The default metadata format that commands should use.\n"
|
||||
"The -M 1|2 option overrides this setting.\n"
|
||||
"#\n"
|
||||
"Accepted values:\n"
|
||||
" lvm1\n"
|
||||
" lvm2\n"
|
||||
"#\n")
|
||||
"This setting is no longer used.\n")
|
||||
|
||||
cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Shared libraries that process different metadata formats.\n"
|
||||
"If support for LVM1 metadata was compiled as a shared library use\n"
|
||||
"format_libraries = \"liblvm2format1.so\"\n")
|
||||
"This setting is no longer used.")
|
||||
|
||||
cfg_array(global_segment_libraries_CFG, "segment_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL, 0, NULL, NULL)
|
||||
|
||||
@@ -878,11 +874,8 @@ cfg(global_abort_on_internal_errors_CFG, "abort_on_internal_errors", global_CFG_
|
||||
"Treat any internal errors as fatal errors, aborting the process that\n"
|
||||
"encountered the internal error. Please only enable for debugging.\n")
|
||||
|
||||
cfg(global_detect_internal_vg_cache_corruption_CFG, "detect_internal_vg_cache_corruption", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION, vsn(2, 2, 96), NULL, 0, NULL,
|
||||
"Internal verification of VG structures.\n"
|
||||
"Check if CRC matches when a parsed VG is used multiple times. This\n"
|
||||
"is useful to catch unexpected changes to cached VG structures.\n"
|
||||
"Please only enable for debugging.\n")
|
||||
cfg(global_detect_internal_vg_cache_corruption_CFG, "detect_internal_vg_cache_corruption", global_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2, 2, 96), NULL, vsn(2, 2, 174), NULL,
|
||||
"No longer used.\n")
|
||||
|
||||
cfg(global_metadata_read_only_CFG, "metadata_read_only", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_METADATA_READ_ONLY, vsn(2, 2, 75), NULL, 0, NULL,
|
||||
"No operations that change on-disk metadata are permitted.\n"
|
||||
@@ -960,6 +953,9 @@ cfg(global_lvdisplay_shows_full_device_path_CFG, "lvdisplay_shows_full_device_pa
|
||||
"Previously this was always shown as /dev/vgname/lvname even when that\n"
|
||||
"was never a valid path in the /dev filesystem.\n")
|
||||
|
||||
cfg(global_use_aio_CFG, "use_aio", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_USE_AIO, vsn(2, 2, 183), NULL, 0, NULL,
|
||||
"Use async I/O when reading and writing devices.\n")
|
||||
|
||||
cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_USE_LVMETAD, vsn(2, 2, 93), "@DEFAULT_USE_LVMETAD@", 0, NULL,
|
||||
"Use lvmetad to cache metadata and reduce disk scanning.\n"
|
||||
"When enabled (and running), lvmetad provides LVM commands with VG\n"
|
||||
@@ -1148,6 +1144,14 @@ cfg(global_notify_dbus_CFG, "notify_dbus", global_CFG_SECTION, 0, CFG_TYPE_BOOL,
|
||||
"When enabled, an LVM command that changes PVs, changes VG metadata,\n"
|
||||
"or changes the activation state of an LV will send a notification.\n")
|
||||
|
||||
cfg(global_io_memory_size_CFG, "io_memory_size", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_IO_MEMORY_SIZE_KB, vsn(2, 2, 184), NULL, 0, NULL,
|
||||
"The amount of memory in KiB that LVM allocates to perform disk io.\n"
|
||||
"LVM performance may benefit from more io memory when there are many\n"
|
||||
"disks or VG metadata is large. Increasing this size may be necessary\n"
|
||||
"when a single copy of VG metadata is larger than the current setting.\n"
|
||||
"This value should usually not be decreased from the default; setting\n"
|
||||
"it too low can result in lvm failing to read VGs.\n")
|
||||
|
||||
cfg(activation_udev_sync_CFG, "udev_sync", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_UDEV_SYNC, vsn(2, 2, 51), NULL, 0, NULL,
|
||||
"Use udev notifications to synchronize udev and LVM.\n"
|
||||
"The --nodevsync option overrides this setting.\n"
|
||||
|
||||
@@ -32,9 +32,6 @@
|
||||
#define DEFAULT_SYSTEM_ID_SOURCE "none"
|
||||
#define DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV 1
|
||||
#define DEFAULT_EXTERNAL_DEVICE_INFO_SOURCE "none"
|
||||
#define DEFAULT_USE_AIO 1
|
||||
#define DEFAULT_AIO_MAX 128
|
||||
#define DEFAULT_AIO_MEMORY 10
|
||||
#define DEFAULT_SYSFS_SCAN 1
|
||||
#define DEFAULT_MD_COMPONENT_DETECTION 1
|
||||
#define DEFAULT_FW_RAID_COMPONENT_DETECTION 0
|
||||
@@ -62,6 +59,7 @@
|
||||
#define DEFAULT_METADATA_READ_ONLY 0
|
||||
#define DEFAULT_LVDISPLAY_SHOWS_FULL_DEVICE_PATH 0
|
||||
#define DEFAULT_UNKNOWN_DEVICE_NAME "[unknown]"
|
||||
#define DEFAULT_USE_AIO 1
|
||||
|
||||
#define DEFAULT_SANLOCK_LV_EXTEND_MB 256
|
||||
|
||||
@@ -113,6 +111,8 @@
|
||||
#define DEFAULT_THIN_POOL_CHUNK_SIZE_POLICY "generic"
|
||||
#define DEFAULT_THIN_POOL_CHUNK_SIZE 64 /* KB */
|
||||
#define DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE 512 /* KB */
|
||||
/* Chunk size big enough it no longer needs jump by power-of-2 */
|
||||
#define DEFAULT_THIN_POOL_CHUNK_SIZE_ALIGNED 1024 /* KB */
|
||||
#define DEFAULT_THIN_POOL_DISCARDS "passdown"
|
||||
#define DEFAULT_THIN_POOL_ZERO 1
|
||||
#define DEFAULT_POOL_METADATA_SPARE 1 /* thin + cache */
|
||||
@@ -182,7 +182,6 @@
|
||||
#define DEFAULT_LOGLEVEL 0
|
||||
#define DEFAULT_INDENT 1
|
||||
#define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0
|
||||
#define DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION 0
|
||||
#define DEFAULT_UNITS "r"
|
||||
#define DEFAULT_SUFFIX 1
|
||||
#define DEFAULT_HOSTTAGS 0
|
||||
@@ -270,4 +269,8 @@
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD 100
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT 20
|
||||
|
||||
#define DEFAULT_SCAN_LVS 0
|
||||
|
||||
#define DEFAULT_IO_MEMORY_SIZE_KB 8192
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
||||
272
lib/device/bcache-utils.c
Normal file
272
lib/device/bcache-utils.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "bcache.h"
|
||||
|
||||
// FIXME: need to define this in a common place (that doesn't pull in deps)
|
||||
#ifndef SECTOR_SHIFT
|
||||
#define SECTOR_SHIFT 9
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void byte_range_to_block_range(struct bcache *cache, uint64_t start, size_t len,
|
||||
block_address *bb, block_address *be)
|
||||
{
|
||||
block_address block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
*bb = start / block_size;
|
||||
*be = (start + len + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
static uint64_t _min(uint64_t lhs, uint64_t rhs)
|
||||
{
|
||||
if (rhs < lhs)
|
||||
return rhs;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
block_address bb, be;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
while (bb < be) {
|
||||
bcache_prefetch(cache, fd, bb);
|
||||
bb++;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
|
||||
bcache_prefetch_bytes(cache, fd, start, len);
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(cache, fd, bb, 0, &b))
|
||||
return false;
|
||||
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memcpy(data, ((unsigned char *) b->data) + block_offset, blen);
|
||||
bcache_put(b);
|
||||
|
||||
block_offset = 0;
|
||||
len -= blen;
|
||||
data = ((unsigned char *) data) + blen;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// Writing bytes and zeroing bytes are very similar, so we factor out
|
||||
// this common code.
|
||||
|
||||
struct updater;
|
||||
|
||||
typedef bool (*partial_update_fn)(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len);
|
||||
typedef bool (*whole_update_fn)(struct updater *u, int fd, block_address bb, block_address be);
|
||||
|
||||
struct updater {
|
||||
struct bcache *cache;
|
||||
partial_update_fn partial_fn;
|
||||
whole_update_fn whole_fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static bool _update_bytes(struct updater *u, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct bcache *cache = u->cache;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
uint64_t nr_whole;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
// If the last block is partial, we will require a read, so let's
|
||||
// prefetch it.
|
||||
if ((start + len) % block_size)
|
||||
bcache_prefetch(cache, fd, (start + len) / block_size);
|
||||
|
||||
// First block may be partial
|
||||
if (block_offset) {
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
if (!u->partial_fn(u, fd, bb, block_offset, blen))
|
||||
return false;
|
||||
|
||||
len -= blen;
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
bb++;
|
||||
}
|
||||
|
||||
// Now we write out a set of whole blocks
|
||||
nr_whole = len / block_size;
|
||||
if (!u->whole_fn(u, fd, bb, bb + nr_whole))
|
||||
return false;
|
||||
|
||||
bb += nr_whole;
|
||||
len -= nr_whole * block_size;
|
||||
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
// Finally we write a partial end block
|
||||
return u->partial_fn(u, fd, bb, 0, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _write_partial(struct updater *u, int fd, block_address bb,
|
||||
uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memcpy(((unsigned char *) b->data) + offset, u->data, len);
|
||||
u->data = ((unsigned char *) u->data) + len;
|
||||
|
||||
bcache_put(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _write_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
uint64_t block_size = bcache_block_sectors(u->cache) << SECTOR_SHIFT;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
// We don't need to read the block since we are overwriting
|
||||
// it completely.
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
memcpy(b->data, u->data, block_size);
|
||||
u->data = ((unsigned char *) u->data) + block_size;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _write_partial;
|
||||
u.whole_fn = _write_whole;
|
||||
u.data = data;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _zero_partial(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memset(((unsigned char *) b->data) + offset, 0, len);
|
||||
bcache_put(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _zero_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _zero_partial;
|
||||
u.whole_fn = _zero_whole;
|
||||
u.data = NULL;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _set_partial(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
uint8_t val = *((uint8_t *) u->data);
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memset(((unsigned char *) b->data) + offset, val, len);
|
||||
bcache_put(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _set_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
uint8_t val = *((uint8_t *) u->data);
|
||||
uint64_t len = bcache_block_sectors(u->cache) * 512;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
memset((unsigned char *) b->data, val, len);
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _set_partial;
|
||||
u.whole_fn = _set_whole;
|
||||
u.data = &val;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
1389
lib/device/bcache.c
Normal file
1389
lib/device/bcache.c
Normal file
File diff suppressed because it is too large
Load Diff
166
lib/device/bcache.h
Normal file
166
lib/device/bcache.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef BCACHE_H
|
||||
#define BCACHE_H
|
||||
|
||||
#include "libdevmapper.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
// FIXME: move somewhere more sensible
|
||||
#define container_of(v, t, head) \
|
||||
((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
enum dir {
|
||||
DIR_READ,
|
||||
DIR_WRITE
|
||||
};
|
||||
|
||||
typedef uint64_t block_address;
|
||||
typedef uint64_t sector_t;
|
||||
|
||||
typedef void io_complete_fn(void *context, int io_error);
|
||||
|
||||
struct io_engine {
|
||||
void (*destroy)(struct io_engine *e);
|
||||
bool (*issue)(struct io_engine *e, enum dir d, int fd,
|
||||
sector_t sb, sector_t se, void *data, void *context);
|
||||
bool (*wait)(struct io_engine *e, io_complete_fn fn);
|
||||
unsigned (*max_io)(struct io_engine *e);
|
||||
};
|
||||
|
||||
struct io_engine *create_async_io_engine(void);
|
||||
struct io_engine *create_sync_io_engine(void);
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
struct bcache;
|
||||
struct block {
|
||||
/* clients may only access these three fields */
|
||||
int fd;
|
||||
uint64_t index;
|
||||
void *data;
|
||||
|
||||
struct bcache *cache;
|
||||
struct dm_list list;
|
||||
struct dm_list hash;
|
||||
|
||||
unsigned flags;
|
||||
unsigned ref_count;
|
||||
int error;
|
||||
enum dir io_dir;
|
||||
};
|
||||
|
||||
/*
|
||||
* Ownership of engine passes. Engine will be destroyed even if this fails.
|
||||
*/
|
||||
struct bcache *bcache_create(sector_t block_size, unsigned nr_cache_blocks,
|
||||
struct io_engine *engine);
|
||||
void bcache_destroy(struct bcache *cache);
|
||||
|
||||
enum bcache_get_flags {
|
||||
/*
|
||||
* The block will be zeroed before get_block returns it. This
|
||||
* potentially avoids a read if the block is not already in the cache.
|
||||
* GF_DIRTY is implicit.
|
||||
*/
|
||||
GF_ZERO = (1 << 0),
|
||||
|
||||
/*
|
||||
* Indicates the caller is intending to change the data in the block, a
|
||||
* writeback will occur after the block is released.
|
||||
*/
|
||||
GF_DIRTY = (1 << 1)
|
||||
};
|
||||
|
||||
sector_t bcache_block_sectors(struct bcache *cache);
|
||||
unsigned bcache_nr_cache_blocks(struct bcache *cache);
|
||||
unsigned bcache_max_prefetches(struct bcache *cache);
|
||||
|
||||
/*
|
||||
* Use the prefetch method to take advantage of asynchronous IO. For example,
|
||||
* if you wanted to read a block from many devices concurrently you'd do
|
||||
* something like this:
|
||||
*
|
||||
* dm_list_iterate_items (dev, &devices)
|
||||
* bcache_prefetch(cache, dev->fd, block);
|
||||
*
|
||||
* dm_list_iterate_items (dev, &devices) {
|
||||
* if (!bcache_get(cache, dev->fd, block, &b))
|
||||
* fail();
|
||||
*
|
||||
* process_block(b);
|
||||
* }
|
||||
*
|
||||
* It's slightly sub optimal, since you may not run the gets in the order that
|
||||
* they complete. But we're talking a very small difference, and it's worth it
|
||||
* to keep callbacks out of this interface.
|
||||
*/
|
||||
void bcache_prefetch(struct bcache *cache, int fd, block_address index);
|
||||
|
||||
/*
|
||||
* Returns true on success.
|
||||
*/
|
||||
bool bcache_get(struct bcache *cache, int fd, block_address index,
|
||||
unsigned flags, struct block **result);
|
||||
void bcache_put(struct block *b);
|
||||
|
||||
/*
|
||||
* flush() does not attempt to writeback locked blocks. flush will fail
|
||||
* (return false), if any unlocked dirty data cannot be written back.
|
||||
*/
|
||||
bool bcache_flush(struct bcache *cache);
|
||||
|
||||
/*
|
||||
* Removes a block from the cache.
|
||||
*
|
||||
* If the block is dirty it will be written back first. If the writeback fails
|
||||
* false will be returned.
|
||||
*
|
||||
* If the block is currently held false will be returned.
|
||||
*/
|
||||
bool bcache_invalidate(struct bcache *cache, int fd, block_address index);
|
||||
|
||||
/*
|
||||
* Invalidates all blocks on the given descriptor. Call this before closing
|
||||
* the descriptor to make sure everything is written back.
|
||||
*/
|
||||
bool bcache_invalidate_fd(struct bcache *cache, int fd);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// The next four functions are utilities written in terms of the above api.
|
||||
|
||||
// Prefetches the blocks neccessary to satisfy a byte range.
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
|
||||
// Reads, writes and zeroes bytes. Returns false if errors occur.
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val);
|
||||
|
||||
void bcache_set_last_byte(struct bcache *cache, int fd, uint64_t offset, int sector_size);
|
||||
void bcache_unset_last_byte(struct bcache *cache, int fd);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -73,7 +73,6 @@ static void _dev_init(struct device *dev, int max_error_count)
|
||||
dev->ext.src = DEV_EXT_NONE;
|
||||
|
||||
dm_list_init(&dev->aliases);
|
||||
dm_list_init(&dev->open_list);
|
||||
}
|
||||
|
||||
void dev_destroy_file(struct device *dev)
|
||||
@@ -275,10 +274,8 @@ static int _compare_paths(const char *path0, const char *path1)
|
||||
if (slash1 < slash0)
|
||||
return 1;
|
||||
|
||||
strncpy(p0, path0, sizeof(p0) - 1);
|
||||
p0[sizeof(p0) - 1] = '\0';
|
||||
strncpy(p1, path1, sizeof(p1) - 1);
|
||||
p1[sizeof(p1) - 1] = '\0';
|
||||
(void) dm_strncpy(p0, path0, sizeof(p0));
|
||||
(void) dm_strncpy(p1, path1, sizeof(p1));
|
||||
s0 = p0 + 1;
|
||||
s1 = p1 + 1;
|
||||
|
||||
@@ -336,10 +333,8 @@ static int _add_alias(struct device *dev, const char *path)
|
||||
|
||||
/* Is name already there? */
|
||||
dm_list_iterate_items(strl, &dev->aliases) {
|
||||
if (!strcmp(strl->str, path)) {
|
||||
log_debug_devs("%s: Already in device cache", path);
|
||||
if (!strcmp(strl->str, path))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
sl->str = path;
|
||||
@@ -347,13 +342,7 @@ static int _add_alias(struct device *dev, const char *path)
|
||||
if (!dm_list_empty(&dev->aliases)) {
|
||||
oldpath = dm_list_item(dev->aliases.n, struct dm_str_list)->str;
|
||||
prefer_old = _compare_paths(path, oldpath);
|
||||
log_debug_devs("%s: Aliased to %s in device cache%s (%d:%d)",
|
||||
path, oldpath, prefer_old ? "" : " (preferred name)",
|
||||
(int) MAJOR(dev->dev), (int) MINOR(dev->dev));
|
||||
|
||||
} else
|
||||
log_debug_devs("%s: Added to device cache (%d:%d)", path,
|
||||
(int) MAJOR(dev->dev), (int) MINOR(dev->dev));
|
||||
}
|
||||
|
||||
if (prefer_old)
|
||||
dm_list_add(&dev->aliases, &sl->list);
|
||||
@@ -491,7 +480,7 @@ static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *dev
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devno = MKDEV((dev_t)major, (dev_t)minor);
|
||||
devno = MKDEV(major, minor);
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno))) {
|
||||
/*
|
||||
* If we get here, it means the device is referenced in sysfs, but it's not yet in /dev.
|
||||
@@ -669,6 +658,28 @@ struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid)
|
||||
return dm_hash_lookup(_cache.lvid_index, lvid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scanning code calls this when it fails to open a device using
|
||||
* this path. The path is dropped from dev-cache. In the next
|
||||
* dev_cache_scan it may be added again, but it could be for a
|
||||
* different device.
|
||||
*/
|
||||
|
||||
void dev_cache_failed_path(struct device *dev, const char *path)
|
||||
{
|
||||
struct dm_str_list *strl;
|
||||
|
||||
if (dm_hash_lookup(_cache.names, path))
|
||||
dm_hash_remove(_cache.names, path);
|
||||
|
||||
dm_list_iterate_items(strl, &dev->aliases) {
|
||||
if (!strcmp(strl->str, path)) {
|
||||
dm_list_del(&strl->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Either creates a new dev, or adds an alias to
|
||||
* an existing dev.
|
||||
@@ -676,6 +687,8 @@ struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid)
|
||||
static int _insert_dev(const char *path, dev_t d)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device *dev_by_devt;
|
||||
struct device *dev_by_path;
|
||||
static dev_t loopfile_count = 0;
|
||||
int loopfile = 0;
|
||||
char *path_copy;
|
||||
@@ -688,8 +701,26 @@ static int _insert_dev(const char *path, dev_t d)
|
||||
loopfile = 1;
|
||||
}
|
||||
|
||||
/* is this device already registered ? */
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) d))) {
|
||||
dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) d);
|
||||
dev_by_path = (struct device *) dm_hash_lookup(_cache.names, path);
|
||||
dev = dev_by_devt;
|
||||
|
||||
/*
|
||||
* Existing device, existing path points to the same device.
|
||||
*/
|
||||
if (dev_by_devt && dev_by_path && (dev_by_devt == dev_by_path)) {
|
||||
log_debug_devs("Found dev %d:%d %s - exists. %.8s",
|
||||
(int)MAJOR(d), (int)MINOR(d), path, dev->pvid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* No device or path found, add devt to cache.devices, add name to cache.names.
|
||||
*/
|
||||
if (!dev_by_devt && !dev_by_path) {
|
||||
log_debug_devs("Found dev %d:%d %s - new.",
|
||||
(int)MAJOR(d), (int)MINOR(d), path);
|
||||
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) d))) {
|
||||
/* create new device */
|
||||
if (loopfile) {
|
||||
@@ -704,30 +735,126 @@ static int _insert_dev(const char *path, dev_t d)
|
||||
_free(dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (dm_hash_lookup(_cache.names, path) == dev) {
|
||||
/* Hash already has matching entry present */
|
||||
log_debug("%s: Path already cached.", path);
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("Failed to duplicate path string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!loopfile && !_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_hash_insert(_cache.names, path_copy, dev)) {
|
||||
log_error("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("Failed to duplicate path string.");
|
||||
return 0;
|
||||
/*
|
||||
* Existing device, path is new, add path as a new alias for the device.
|
||||
*/
|
||||
if (dev_by_devt && !dev_by_path) {
|
||||
log_debug_devs("Found dev %d:%d %s - new alias.",
|
||||
(int)MAJOR(d), (int)MINOR(d), path);
|
||||
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("Failed to duplicate path string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!loopfile && !_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_hash_insert(_cache.names, path_copy, dev)) {
|
||||
log_error("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!loopfile && !_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
/*
|
||||
* No existing device, but path exists and previously pointed
|
||||
* to a different device.
|
||||
*/
|
||||
if (!dev_by_devt && dev_by_path) {
|
||||
log_debug_devs("Found dev %d:%d %s - new device, path was previously %d:%d.",
|
||||
(int)MAJOR(d), (int)MINOR(d), path,
|
||||
(int)MAJOR(dev_by_path->dev), (int)MINOR(dev_by_path->dev));
|
||||
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) d))) {
|
||||
/* create new device */
|
||||
if (loopfile) {
|
||||
if (!(dev = dev_create_file(path, NULL, NULL, 0)))
|
||||
return_0;
|
||||
} else if (!(dev = _dev_create(d)))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
|
||||
log_error("Couldn't insert device into binary tree.");
|
||||
_free(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("Failed to duplicate path string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!loopfile && !_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_hash_remove(_cache.names, path);
|
||||
|
||||
if (!dm_hash_insert(_cache.names, path_copy, dev)) {
|
||||
log_error("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
if (!dm_hash_insert(_cache.names, path_copy, dev)) {
|
||||
log_error("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
/*
|
||||
* Existing device, and path exists and previously pointed to
|
||||
* a different device.
|
||||
*/
|
||||
if (dev_by_devt && dev_by_path) {
|
||||
log_debug_devs("Found dev %d:%d %s - existing device, path was previously %d:%d.",
|
||||
(int)MAJOR(d), (int)MINOR(d), path,
|
||||
(int)MAJOR(dev_by_path->dev), (int)MINOR(dev_by_path->dev));
|
||||
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("Failed to duplicate path string.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!loopfile && !_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_hash_remove(_cache.names, path);
|
||||
|
||||
if (!dm_hash_insert(_cache.names, path_copy, dev)) {
|
||||
log_error("Couldn't add name to hash in dev cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
log_error("Found dev %d:%d %s - failed to use.", (int)MAJOR(d), (int)MINOR(d), path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *_join(const char *dir, const char *name)
|
||||
@@ -857,7 +984,7 @@ static int _dev_cache_iterate_sysfs_for_index(const char *path)
|
||||
continue;
|
||||
}
|
||||
|
||||
devno = MKDEV((dev_t)major, (dev_t)minor);
|
||||
devno = MKDEV(major, minor);
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno)) &&
|
||||
!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) devno))) {
|
||||
if (!dm_device_get_name(major, minor, 1, devname, sizeof(devname)) ||
|
||||
@@ -1067,10 +1194,8 @@ static int _insert(const char *path, const struct stat *info,
|
||||
if (rec && !_insert_dir(path))
|
||||
return_0;
|
||||
} else { /* add a device */
|
||||
if (!S_ISBLK(info->st_mode)) {
|
||||
log_debug_devs("%s: Not a block device", path);
|
||||
if (!S_ISBLK(info->st_mode))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!_insert_dev(path, info->st_rdev))
|
||||
return_0;
|
||||
@@ -1079,12 +1204,13 @@ static int _insert(const char *path, const struct stat *info,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _full_scan(int dev_scan)
|
||||
void dev_cache_scan(void)
|
||||
{
|
||||
struct dir_list *dl;
|
||||
|
||||
log_debug_devs("Creating list of system devices.");
|
||||
|
||||
if (_cache.has_scanned && !dev_scan)
|
||||
return;
|
||||
_cache.has_scanned = 1;
|
||||
|
||||
_insert_dirs(&_cache.dirs);
|
||||
|
||||
@@ -1092,9 +1218,6 @@ static void _full_scan(int dev_scan)
|
||||
|
||||
dm_list_iterate_items(dl, &_cache.files)
|
||||
_insert_file(dl->dir);
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
init_full_scan_done(1);
|
||||
}
|
||||
|
||||
int dev_cache_has_scanned(void)
|
||||
@@ -1102,14 +1225,6 @@ int dev_cache_has_scanned(void)
|
||||
return _cache.has_scanned;
|
||||
}
|
||||
|
||||
void dev_cache_scan(int do_scan)
|
||||
{
|
||||
if (!do_scan)
|
||||
_cache.has_scanned = 1;
|
||||
else
|
||||
_full_scan(1);
|
||||
}
|
||||
|
||||
static int _init_preferred_names(struct cmd_context *cmd)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
@@ -1173,7 +1288,6 @@ out:
|
||||
int dev_cache_init(struct cmd_context *cmd)
|
||||
{
|
||||
_cache.names = NULL;
|
||||
_cache.has_scanned = 0;
|
||||
|
||||
if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
|
||||
return_0;
|
||||
@@ -1230,7 +1344,8 @@ static int _check_for_open_devices(int close_immediate)
|
||||
dev_name(dev), dev->open_count);
|
||||
num_open++;
|
||||
if (close_immediate)
|
||||
dev_close_immediate(dev);
|
||||
if (!dev_close_immediate(dev))
|
||||
stack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1247,24 +1362,12 @@ int dev_cache_check_for_open_devices(void)
|
||||
|
||||
int dev_cache_exit(void)
|
||||
{
|
||||
struct btree_iter *b;
|
||||
int num_open = 0;
|
||||
|
||||
dev_async_exit();
|
||||
|
||||
if (_cache.names)
|
||||
if ((num_open = _check_for_open_devices(1)) > 0)
|
||||
log_error(INTERNAL_ERROR "%d device(s) were left open and have been closed.", num_open);
|
||||
|
||||
if (_cache.devices) {
|
||||
/* FIXME Replace with structured devbuf cache */
|
||||
b = btree_first(_cache.devices);
|
||||
while (b) {
|
||||
devbufs_release(btree_get_data(b));
|
||||
b = btree_next(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (_cache.mem)
|
||||
dm_pool_destroy(_cache.mem);
|
||||
|
||||
@@ -1405,6 +1508,7 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
struct stat buf;
|
||||
struct device *d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
int info_available = 0;
|
||||
int ret = 1;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
@@ -1427,15 +1531,30 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
_insert(name, info_available ? &buf : NULL, 0, obtain_device_list_from_udev());
|
||||
d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
if (!d) {
|
||||
_full_scan(0);
|
||||
dev_cache_scan();
|
||||
d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!d || (f && !(d->flags & DEV_REGULAR) && !(f->passes_filter(f, d))))
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR)) {
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by name defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR) && !ret)
|
||||
return NULL;
|
||||
|
||||
log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev));
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1462,6 +1581,7 @@ struct device *dev_cache_get_by_devt(dev_t dev, struct dev_filter *f)
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
struct device *d = _dev_cache_seek_devt(dev);
|
||||
int ret;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
@@ -1470,38 +1590,47 @@ struct device *dev_cache_get_by_devt(dev_t dev, struct dev_filter *f)
|
||||
sysfs_dir = dm_sysfs_dir();
|
||||
if (sysfs_dir && *sysfs_dir) {
|
||||
/* First check if dev is sysfs to avoid useless scan */
|
||||
if (dm_snprintf(path, sizeof(path), "%s/dev/block/%d:%d",
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d",
|
||||
sysfs_dir, (int)MAJOR(dev), (int)MINOR(dev)) < 0) {
|
||||
log_error("dm_snprintf partition failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lstat(path, &info)) {
|
||||
log_debug("No sysfs entry for %d:%d.",
|
||||
(int)MAJOR(dev), (int)MINOR(dev));
|
||||
log_debug("No sysfs entry for %d:%d errno %d at %s.",
|
||||
(int)MAJOR(dev), (int)MINOR(dev), errno, path);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_full_scan(0);
|
||||
dev_cache_scan();
|
||||
d = _dev_cache_seek_devt(dev);
|
||||
}
|
||||
|
||||
return (d && (!f || (d->flags & DEV_REGULAR) ||
|
||||
f->passes_filter(f, d))) ? d : NULL;
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
if (d->flags & DEV_REGULAR)
|
||||
return d;
|
||||
|
||||
if (!f)
|
||||
return d;
|
||||
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by number defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return d;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dev_cache_full_scan(struct dev_filter *f)
|
||||
{
|
||||
if (f && f->wipe) {
|
||||
f->wipe(f); /* might call _full_scan(1) */
|
||||
if (!full_scan_done())
|
||||
_full_scan(1);
|
||||
} else
|
||||
_full_scan(1);
|
||||
}
|
||||
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused)
|
||||
{
|
||||
struct dev_iter *di = dm_malloc(sizeof(*di));
|
||||
|
||||
@@ -1510,13 +1639,6 @@ struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dev_scan && !trust_cache()) {
|
||||
/* Flag gets reset between each command */
|
||||
if (!full_scan_done())
|
||||
dev_cache_full_scan(f);
|
||||
} else
|
||||
_full_scan(0);
|
||||
|
||||
di->current = btree_first(_cache.devices);
|
||||
di->filter = f;
|
||||
if (di->filter)
|
||||
@@ -1541,13 +1663,27 @@ static struct device *_iter_next(struct dev_iter *iter)
|
||||
|
||||
struct device *dev_iter_get(struct dev_iter *iter)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
int ret;
|
||||
|
||||
while (iter->current) {
|
||||
struct device *d = _iter_next(iter);
|
||||
if (!iter->filter || (d->flags & DEV_REGULAR) ||
|
||||
iter->filter->passes_filter(iter->filter, d)) {
|
||||
log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev));
|
||||
return d;
|
||||
ret = 1;
|
||||
|
||||
f = iter->filter;
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR)) {
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by iter defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!f || (d->flags & DEV_REGULAR) || ret)
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
* predicate for devices.
|
||||
*/
|
||||
struct dev_filter {
|
||||
int (*passes_filter) (struct dev_filter *f, struct device *dev);
|
||||
void (*destroy) (struct dev_filter *f);
|
||||
void (*wipe) (struct dev_filter *f);
|
||||
int (*dump) (struct dev_filter *f, struct dm_pool *mem, int merge_existing);
|
||||
int (*passes_filter) (struct dev_filter * f, struct device * dev);
|
||||
void (*destroy) (struct dev_filter * f);
|
||||
void (*wipe) (struct dev_filter * f);
|
||||
int (*dump) (struct dev_filter * f, int merge_existing);
|
||||
void *private;
|
||||
unsigned use_count;
|
||||
};
|
||||
@@ -46,10 +46,8 @@ int dev_cache_exit(void);
|
||||
*/
|
||||
int dev_cache_check_for_open_devices(void);
|
||||
|
||||
/* Trigger(1) or avoid(0) a scan */
|
||||
void dev_cache_scan(int do_scan);
|
||||
void dev_cache_scan(void);
|
||||
int dev_cache_has_scanned(void);
|
||||
void dev_cache_full_scan(struct dev_filter *f);
|
||||
|
||||
int dev_cache_add_dir(const char *path);
|
||||
int dev_cache_add_loopfile(const char *path);
|
||||
@@ -66,10 +64,12 @@ void dev_set_preferred_name(struct dm_str_list *sl, struct device *dev);
|
||||
* Object for iterating through the cache.
|
||||
*/
|
||||
struct dev_iter;
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan);
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused);
|
||||
void dev_iter_destroy(struct dev_iter *iter);
|
||||
struct device *dev_iter_get(struct dev_iter *iter);
|
||||
|
||||
void dev_reset_error_count(struct cmd_context *cmd);
|
||||
|
||||
void dev_cache_failed_path(struct device *dev, const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,27 +18,22 @@
|
||||
#define LUKS_SIGNATURE "LUKS\xba\xbe"
|
||||
#define LUKS_SIGNATURE_SIZE 6
|
||||
|
||||
int dev_is_luks(struct device *dev, uint64_t *offset_found)
|
||||
int dev_is_luks(struct device *dev, uint64_t *offset_found, int full)
|
||||
{
|
||||
char buf[LUKS_SIGNATURE_SIZE];
|
||||
int ret = -1;
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (offset_found)
|
||||
*offset_found = 0;
|
||||
|
||||
if (!dev_read_buf(dev, 0, LUKS_SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf))
|
||||
if (!dev_read_bytes(dev, 0, LUKS_SIGNATURE_SIZE, buf))
|
||||
goto_out;
|
||||
|
||||
ret = memcmp(buf, LUKS_SIGNATURE, LUKS_SIGNATURE_SIZE) ? 0 : 1;
|
||||
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
174
lib/device/dev-lvm1-pool.c
Normal file
174
lib/device/dev-lvm1-pool.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "dev-type.h"
|
||||
#include "xlate.h"
|
||||
|
||||
/*
|
||||
* These lvm1 structs just used NAME_LEN in the previous format1 lvm2 code, but
|
||||
* NAME_LEN was defined as 128 in generic lvm2 code that was not lvm1-specific
|
||||
* and not disk-format-specific.
|
||||
*/
|
||||
|
||||
#define LVM1_NAME_LEN 128
|
||||
|
||||
struct data_area {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pv_disk {
|
||||
int8_t id[2];
|
||||
uint16_t version; /* lvm version */
|
||||
struct data_area pv_on_disk;
|
||||
struct data_area vg_on_disk;
|
||||
struct data_area pv_uuidlist_on_disk;
|
||||
struct data_area lv_on_disk;
|
||||
struct data_area pe_on_disk;
|
||||
int8_t pv_uuid[LVM1_NAME_LEN];
|
||||
int8_t vg_name[LVM1_NAME_LEN];
|
||||
int8_t system_id[LVM1_NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* only present on version == 2 pv's */
|
||||
uint32_t pe_start;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
int dev_is_lvm1(struct device *dev, char *buf, int buflen)
|
||||
{
|
||||
struct pv_disk *pvd = (struct pv_disk *) buf;
|
||||
uint32_t version;
|
||||
int ret;
|
||||
|
||||
version = xlate16(pvd->version);
|
||||
|
||||
if (pvd->id[0] == 'H' && pvd->id[1] == 'M' &&
|
||||
(version == 1 || version == 2))
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define POOL_MAGIC 0x011670
|
||||
#define POOL_NAME_SIZE 256
|
||||
|
||||
#define NSPMajorVersion 4
|
||||
#define NSPMinorVersion 1
|
||||
#define NSPUpdateLevel 3
|
||||
|
||||
/* When checking for version matching, the first two numbers **
|
||||
** are important for metadata formats, a.k.a pool labels. **
|
||||
** All the numbers are important when checking if the user **
|
||||
** space tools match up with the kernel module............. */
|
||||
|
||||
#define POOL_VERSION (NSPMajorVersion << 16 | \
|
||||
NSPMinorVersion << 8 | \
|
||||
NSPUpdateLevel)
|
||||
|
||||
struct pool_disk {
|
||||
uint64_t pl_magic; /* Pool magic number */
|
||||
uint64_t pl_pool_id; /* Unique pool identifier */
|
||||
char pl_pool_name[POOL_NAME_SIZE]; /* Name of pool */
|
||||
uint32_t pl_version; /* Pool version */
|
||||
uint32_t pl_subpools; /* Number of subpools in this pool */
|
||||
uint32_t pl_sp_id; /* Subpool number within pool */
|
||||
uint32_t pl_sp_devs; /* Number of data partitions in this subpool */
|
||||
uint32_t pl_sp_devid; /* Partition number within subpool */
|
||||
uint32_t pl_sp_type; /* Partition type */
|
||||
uint64_t pl_blocks; /* Number of blocks in this partition */
|
||||
uint32_t pl_striping; /* Striping size within subpool */
|
||||
/*
|
||||
* If the number of DMEP devices is zero, then the next field **
|
||||
* ** (pl_sp_dmepid) becomes the subpool ID for redirection. In **
|
||||
* ** other words, if this subpool does not have the capability **
|
||||
* ** to do DMEP, then it must specify which subpool will do it **
|
||||
* ** in it's place
|
||||
*/
|
||||
|
||||
/*
|
||||
* While the next 3 field are no longer used, they must stay to keep **
|
||||
* ** backward compatibility...........................................
|
||||
*/
|
||||
uint32_t pl_sp_dmepdevs;/* Number of dmep devices in this subpool */
|
||||
uint32_t pl_sp_dmepid; /* Dmep device number within subpool */
|
||||
uint32_t pl_sp_weight; /* if dmep dev, pref to using it */
|
||||
|
||||
uint32_t pl_minor; /* the pool minor number */
|
||||
uint32_t pl_padding; /* reminder - think about alignment */
|
||||
|
||||
/*
|
||||
* Even though we're zeroing out 8k at the front of the disk before
|
||||
* writing the label, putting this in
|
||||
*/
|
||||
char pl_reserve[184]; /* bump the structure size out to 512 bytes */
|
||||
};
|
||||
|
||||
#define CPIN_8(x, y, z) {memcpy((x), (y), (z));}
|
||||
#define CPIN_16(x, y) {(x) = xlate16_be((y));}
|
||||
#define CPIN_32(x, y) {(x) = xlate32_be((y));}
|
||||
#define CPIN_64(x, y) {(x) = xlate64_be((y));}
|
||||
|
||||
static void pool_label_in(struct pool_disk *pl, void *buf)
|
||||
{
|
||||
struct pool_disk *bufpl = (struct pool_disk *) buf;
|
||||
|
||||
CPIN_64(pl->pl_magic, bufpl->pl_magic);
|
||||
CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id);
|
||||
CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
|
||||
CPIN_32(pl->pl_version, bufpl->pl_version);
|
||||
CPIN_32(pl->pl_subpools, bufpl->pl_subpools);
|
||||
CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id);
|
||||
CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
|
||||
CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
|
||||
CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type);
|
||||
CPIN_64(pl->pl_blocks, bufpl->pl_blocks);
|
||||
CPIN_32(pl->pl_striping, bufpl->pl_striping);
|
||||
CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
|
||||
CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
|
||||
CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
|
||||
CPIN_32(pl->pl_minor, bufpl->pl_minor);
|
||||
CPIN_32(pl->pl_padding, bufpl->pl_padding);
|
||||
CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184);
|
||||
}
|
||||
|
||||
int dev_is_pool(struct device *dev, char *buf, int buflen)
|
||||
{
|
||||
struct pool_disk pd;
|
||||
int ret;
|
||||
|
||||
pool_label_in(&pd, buf);
|
||||
|
||||
/* can ignore 8 rightmost bits for ondisk format check */
|
||||
if ((pd.pl_magic == POOL_MAGIC) &&
|
||||
(pd.pl_version >> 8 == POOL_VERSION >> 8))
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,12 @@ static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
|
||||
uint32_t md_magic;
|
||||
|
||||
/* Version 1 is little endian; version 0.90.0 is machine endian */
|
||||
if (dev_read_buf(dev, sb_offset, sizeof(uint32_t), DEV_IO_SIGNATURES, &md_magic) &&
|
||||
((md_magic == MD_SB_MAGIC) ||
|
||||
((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC)))))
|
||||
|
||||
if (!dev_read_bytes(dev, sb_offset, sizeof(uint32_t), &md_magic))
|
||||
return_0;
|
||||
|
||||
if ((md_magic == MD_SB_MAGIC) ||
|
||||
((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -109,11 +112,14 @@ static int _udev_dev_is_md(struct device *dev)
|
||||
/*
|
||||
* Returns -1 on error
|
||||
*/
|
||||
static int _native_dev_is_md(struct device *dev, uint64_t *offset_found)
|
||||
static int _native_dev_is_md(struct device *dev, uint64_t *offset_found, int full)
|
||||
{
|
||||
int ret = 1;
|
||||
md_minor_version_t minor;
|
||||
uint64_t size, sb_offset;
|
||||
int ret;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
stack;
|
||||
@@ -123,47 +129,85 @@ static int _native_dev_is_md(struct device *dev, uint64_t *offset_found)
|
||||
if (size < MD_RESERVED_SECTORS * 2)
|
||||
return 0;
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
return -1;
|
||||
/*
|
||||
* Old md versions locate the magic number at the end of the device.
|
||||
* Those checks can't be satisfied with the initial bcache data, and
|
||||
* would require an extra read i/o at the end of every device. Issuing
|
||||
* an extra read to every device in every command, just to check for
|
||||
* the old md format is a bad tradeoff.
|
||||
*
|
||||
* When "full" is set, we check a the start and end of the device for
|
||||
* md magic numbers. When "full" is not set, we only check at the
|
||||
* start of the device for the magic numbers. We decide for each
|
||||
* command if it should do a full check (cmd->use_full_md_check),
|
||||
* and set it for commands that could possibly write to an md dev
|
||||
* (pvcreate/vgcreate/vgextend).
|
||||
*/
|
||||
if (!full) {
|
||||
sb_offset = 0;
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
log_debug_devs("Found md magic number at offset 0 of %s.", dev_name(dev));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sb_offset = 8 << SECTOR_SHIFT;
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
log_debug_devs("Found md magic number at offset %d of %s.", (int)sb_offset, dev_name(dev));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if it is an md component device. */
|
||||
/* Version 0.90.0 */
|
||||
sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
|
||||
if (_dev_has_md_magic(dev, sb_offset))
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
minor = MD_MINOR_VERSION_MIN;
|
||||
/* Version 1, try v1.0 -> v1.2 */
|
||||
do {
|
||||
sb_offset = _v1_sb_offset(size, minor);
|
||||
if (_dev_has_md_magic(dev, sb_offset))
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
} while (++minor <= MD_MINOR_VERSION_MAX);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
if (ret && offset_found)
|
||||
*offset_found = sb_offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dev_is_md(struct device *dev, uint64_t *offset_found)
|
||||
int dev_is_md(struct device *dev, uint64_t *offset_found, int full)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If non-native device status source is selected, use it
|
||||
* only if offset_found is not requested as this
|
||||
* information is not in udev db.
|
||||
*/
|
||||
if ((dev->ext.src == DEV_EXT_NONE) || offset_found)
|
||||
return _native_dev_is_md(dev, offset_found);
|
||||
if ((dev->ext.src == DEV_EXT_NONE) || offset_found) {
|
||||
ret = _native_dev_is_md(dev, offset_found, full);
|
||||
|
||||
if (!full) {
|
||||
if (!ret || (ret == -EAGAIN)) {
|
||||
if (udev_dev_is_md_component(dev))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev->ext.src == DEV_EXT_UDEV)
|
||||
return _udev_dev_is_md(dev);
|
||||
@@ -373,6 +417,26 @@ unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev)
|
||||
return stripe_width_sectors;
|
||||
}
|
||||
|
||||
int dev_is_md_with_end_superblock(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
char version_string[MD_MAX_SYSFS_SIZE];
|
||||
const char *attribute = "metadata_version";
|
||||
|
||||
if (MAJOR(dev->dev) != dt->md_major)
|
||||
return 0;
|
||||
|
||||
if (_md_sysfs_attribute_scanf(dt, dev, attribute,
|
||||
"%s", &version_string) != 1)
|
||||
return -1;
|
||||
|
||||
log_very_verbose("Device %s %s is %s.",
|
||||
dev_name(dev), attribute, version_string);
|
||||
|
||||
if (!strcmp(version_string, "1.0") || !strcmp(version_string, "0.90"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int dev_is_md(struct device *dev __attribute__((unused)),
|
||||
|
||||
@@ -35,19 +35,17 @@ static int _swap_detect_signature(const char *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dev_is_swap(struct device *dev, uint64_t *offset_found)
|
||||
int dev_is_swap(struct device *dev, uint64_t *offset_found, int full)
|
||||
{
|
||||
char buf[10];
|
||||
uint64_t size;
|
||||
unsigned page;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
@@ -60,7 +58,7 @@ int dev_is_swap(struct device *dev, uint64_t *offset_found)
|
||||
continue;
|
||||
if (size < (page >> SECTOR_SHIFT))
|
||||
break;
|
||||
if (!dev_read_buf(dev, page - SIGNATURE_SIZE, SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf)) {
|
||||
if (!dev_read_bytes(dev, page - SIGNATURE_SIZE, SIGNATURE_SIZE, buf)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
@@ -72,9 +70,6 @@ int dev_is_swap(struct device *dev, uint64_t *offset_found)
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "xlate.h"
|
||||
#include "config.h"
|
||||
#include "metadata.h"
|
||||
#include "label.h"
|
||||
|
||||
#include <libgen.h>
|
||||
#include <ctype.h>
|
||||
@@ -213,6 +214,9 @@ int dev_subsystem_part_major(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->device_mapper_major)
|
||||
return 1;
|
||||
|
||||
if (MAJOR(dev->dev) == dt->md_major)
|
||||
return 1;
|
||||
|
||||
if (MAJOR(dev->dev) == dt->drbd_major)
|
||||
return 1;
|
||||
|
||||
@@ -363,7 +367,7 @@ static int _has_partition_table(struct device *dev)
|
||||
uint16_t magic;
|
||||
} __attribute__((packed)) buf; /* sizeof() == SECTOR_SIZE */
|
||||
|
||||
if (!dev_read_buf(dev, UINT64_C(0), sizeof(buf), DEV_IO_SIGNATURES, &buf))
|
||||
if (!dev_read_bytes(dev, UINT64_C(0), sizeof(buf), &buf))
|
||||
return_0;
|
||||
|
||||
/* FIXME Check for other types of partition table too */
|
||||
@@ -432,6 +436,9 @@ static int _native_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!_is_partitionable(dt, dev))
|
||||
return 0;
|
||||
|
||||
@@ -439,17 +446,8 @@ static int _native_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
if ((MAJOR(dev->dev) == dt->dasd_major) && dasd_is_cdl_formatted(dev))
|
||||
return 1;
|
||||
|
||||
if (!dev_open_readonly_quiet(dev)) {
|
||||
log_debug_devs("%s: failed to open device, considering device "
|
||||
"is partitioned", dev_name(dev));
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = _has_partition_table(dev);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -506,7 +504,7 @@ int dev_get_primary_dev(struct dev_types *dt, struct device *dev, dev_t *result)
|
||||
*/
|
||||
if ((parts = dt->dev_type_array[major].max_partitions) > 1) {
|
||||
if ((residue = minor % parts)) {
|
||||
*result = MKDEV((dev_t)major, (dev_t)(minor - residue));
|
||||
*result = MKDEV(major, (minor - residue));
|
||||
ret = 2;
|
||||
} else {
|
||||
*result = dev->dev;
|
||||
@@ -576,7 +574,7 @@ int dev_get_primary_dev(struct dev_types *dt, struct device *dev, dev_t *result)
|
||||
path, buffer);
|
||||
goto out;
|
||||
}
|
||||
*result = MKDEV((dev_t)major, (dev_t)minor);
|
||||
*result = MKDEV(major, minor);
|
||||
ret = 2;
|
||||
out:
|
||||
if (fp && fclose(fp))
|
||||
@@ -675,7 +673,7 @@ static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name,
|
||||
} else
|
||||
log_verbose(_msg_wiping, type, name);
|
||||
|
||||
if (!dev_set(dev, offset_value, len, DEV_IO_SIGNATURES, 0)) {
|
||||
if (!dev_write_zeros(dev, offset_value, len)) {
|
||||
log_error("Failed to wipe %s signature on %s.", type, name);
|
||||
return 0;
|
||||
}
|
||||
@@ -748,12 +746,12 @@ out:
|
||||
|
||||
static int _wipe_signature(struct device *dev, const char *type, const char *name,
|
||||
int wipe_len, int yes, force_t force, int *wiped,
|
||||
int (*signature_detection_fn)(struct device *dev, uint64_t *offset_found))
|
||||
int (*signature_detection_fn)(struct device *dev, uint64_t *offset_found, int full))
|
||||
{
|
||||
int wipe;
|
||||
uint64_t offset_found;
|
||||
|
||||
wipe = signature_detection_fn(dev, &offset_found);
|
||||
wipe = signature_detection_fn(dev, &offset_found, 1);
|
||||
if (wipe == -1) {
|
||||
log_error("Fatal error while trying to detect %s on %s.",
|
||||
type, name);
|
||||
@@ -772,7 +770,7 @@ static int _wipe_signature(struct device *dev, const char *type, const char *nam
|
||||
}
|
||||
|
||||
log_print_unless_silent("Wiping %s on %s.", type, name);
|
||||
if (!dev_set(dev, offset_found, wipe_len, DEV_IO_SIGNATURES, 0)) {
|
||||
if (!dev_write_zeros(dev, offset_found, wipe_len)) {
|
||||
log_error("Failed to wipe %s on %s.", type, name);
|
||||
return 0;
|
||||
}
|
||||
@@ -1005,25 +1003,23 @@ int dev_is_rotational(struct dev_types *dt, struct device *dev)
|
||||
* failed already due to timeout in udev - in both cases the
|
||||
* udev_device_get_is_initialized returns 0.
|
||||
*/
|
||||
#define UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT 100
|
||||
#define UDEV_DEV_IS_MPATH_COMPONENT_USLEEP 100000
|
||||
#define UDEV_DEV_IS_COMPONENT_ITERATION_COUNT 100
|
||||
#define UDEV_DEV_IS_COMPONENT_USLEEP 100000
|
||||
|
||||
int udev_dev_is_mpath_component(struct device *dev)
|
||||
static struct udev_device *_udev_get_dev(struct device *dev)
|
||||
{
|
||||
struct udev *udev_context = udev_get_library_context();
|
||||
struct udev_device *udev_device = NULL;
|
||||
const char *value;
|
||||
int initialized = 0;
|
||||
unsigned i = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!udev_context) {
|
||||
log_warn("WARNING: No udev context available to check if device %s is multipath component.", dev_name(dev));
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (i >= UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT)
|
||||
if (i >= UDEV_DEV_IS_COMPONENT_ITERATION_COUNT)
|
||||
break;
|
||||
|
||||
if (udev_device)
|
||||
@@ -1031,7 +1027,7 @@ int udev_dev_is_mpath_component(struct device *dev)
|
||||
|
||||
if (!(udev_device = udev_device_new_from_devnum(udev_context, 'b', dev->dev))) {
|
||||
log_warn("WARNING: Failed to get udev device handler for device %s.", dev_name(dev));
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBUDEV_UDEV_DEVICE_GET_IS_INITIALIZED
|
||||
@@ -1043,19 +1039,35 @@ int udev_dev_is_mpath_component(struct device *dev)
|
||||
#endif
|
||||
|
||||
log_debug("Device %s not initialized in udev database (%u/%u, %u microseconds).", dev_name(dev),
|
||||
i + 1, UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT,
|
||||
i * UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
i + 1, UDEV_DEV_IS_COMPONENT_ITERATION_COUNT,
|
||||
i * UDEV_DEV_IS_COMPONENT_USLEEP);
|
||||
|
||||
usleep(UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
usleep(UDEV_DEV_IS_COMPONENT_USLEEP);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
log_warn("WARNING: Device %s not initialized in udev database even after waiting %u microseconds.",
|
||||
dev_name(dev), i * UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
dev_name(dev), i * UDEV_DEV_IS_COMPONENT_USLEEP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return udev_device;
|
||||
}
|
||||
|
||||
int udev_dev_is_mpath_component(struct device *dev)
|
||||
{
|
||||
struct udev_device *udev_device;
|
||||
const char *value;
|
||||
int ret = 0;
|
||||
|
||||
if (!obtain_device_list_from_udev())
|
||||
return 0;
|
||||
|
||||
if (!(udev_device = _udev_get_dev(dev)))
|
||||
return 0;
|
||||
|
||||
value = udev_device_get_property_value(udev_device, DEV_EXT_UDEV_BLKID_TYPE);
|
||||
if (value && !strcmp(value, DEV_EXT_UDEV_BLKID_TYPE_MPATH)) {
|
||||
log_debug("Device %s is multipath component based on blkid variable in udev db (%s=\"%s\").",
|
||||
@@ -1075,6 +1087,31 @@ out:
|
||||
udev_device_unref(udev_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int udev_dev_is_md_component(struct device *dev)
|
||||
{
|
||||
struct udev_device *udev_device;
|
||||
const char *value;
|
||||
int ret = 0;
|
||||
|
||||
if (!obtain_device_list_from_udev())
|
||||
return 0;
|
||||
|
||||
if (!(udev_device = _udev_get_dev(dev)))
|
||||
return 0;
|
||||
|
||||
value = udev_device_get_property_value(udev_device, DEV_EXT_UDEV_BLKID_TYPE);
|
||||
if (value && !strcmp(value, DEV_EXT_UDEV_BLKID_TYPE_SW_RAID)) {
|
||||
log_debug("Device %s is md raid component based on blkid variable in udev db (%s=\"%s\").",
|
||||
dev_name(dev), DEV_EXT_UDEV_BLKID_TYPE, value);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
udev_device_unref(udev_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int udev_dev_is_mpath_component(struct device *dev)
|
||||
@@ -1082,4 +1119,9 @@ int udev_dev_is_mpath_component(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_dev_is_md_component(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "device.h"
|
||||
#include "display.h"
|
||||
#include "label.h"
|
||||
|
||||
#define NUMBER_OF_MAJORS 4096
|
||||
|
||||
@@ -25,7 +26,7 @@
|
||||
#else
|
||||
# define MAJOR(x) major((x))
|
||||
# define MINOR(x) minor((x))
|
||||
# define MKDEV(x,y) makedev((x),(y))
|
||||
# define MKDEV(x,y) makedev((dev_t)(x),(dev_t)(y))
|
||||
#endif
|
||||
|
||||
#define PARTITION_SCSI_DEVICE (1 << 0)
|
||||
@@ -56,11 +57,15 @@ const char *dev_subsystem_name(struct dev_types *dt, struct device *dev);
|
||||
int major_is_scsi_device(struct dev_types *dt, int major);
|
||||
|
||||
/* Signature/superblock recognition with position returned where found. */
|
||||
int dev_is_md(struct device *dev, uint64_t *sb);
|
||||
int dev_is_swap(struct device *dev, uint64_t *signature);
|
||||
int dev_is_luks(struct device *dev, uint64_t *signature);
|
||||
int dev_is_md(struct device *dev, uint64_t *sb, int full);
|
||||
int dev_is_swap(struct device *dev, uint64_t *signature, int full);
|
||||
int dev_is_luks(struct device *dev, uint64_t *signature, int full);
|
||||
int dasd_is_cdl_formatted(struct device *dev);
|
||||
int udev_dev_is_mpath_component(struct device *dev);
|
||||
int udev_dev_is_md_component(struct device *dev);
|
||||
|
||||
int dev_is_lvm1(struct device *dev, char *buf, int buflen);
|
||||
int dev_is_pool(struct device *dev, char *buf, int buflen);
|
||||
|
||||
/* Signature wiping. */
|
||||
#define TYPE_LVM1_MEMBER 0x001
|
||||
@@ -72,6 +77,7 @@ int wipe_known_signatures(struct cmd_context *cmd, struct device *dev, const cha
|
||||
|
||||
/* Type-specific device properties */
|
||||
unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev);
|
||||
int dev_is_md_with_end_superblock(struct dev_types *dt, struct device *dev);
|
||||
|
||||
/* Partitioning */
|
||||
int major_max_partitions(struct dev_types *dt, int major);
|
||||
|
||||
@@ -31,18 +31,11 @@
|
||||
#define DEV_USED_FOR_LV 0x00000100 /* Is device used for an LV */
|
||||
#define DEV_ASSUMED_FOR_LV 0x00000200 /* Is device assumed for an LV */
|
||||
#define DEV_NOT_O_NOATIME 0x00000400 /* Don't use O_NOATIME */
|
||||
|
||||
/* ioflags */
|
||||
#define AIO_SUPPORTED_CODE_PATH 0x00000001 /* Set if the code path supports AIO */
|
||||
|
||||
#define aio_supported_code_path(ioflags) (((ioflags) & AIO_SUPPORTED_CODE_PATH) ? 1 : 0)
|
||||
|
||||
/*
|
||||
* Standard format for callback functions.
|
||||
* When provided, callback functions are called exactly once.
|
||||
* If failed is set, data cannot be accessed.
|
||||
*/
|
||||
typedef void (*lvm_callback_fn_t)(int failed, unsigned ioflags, void *context, const void *data);
|
||||
#define DEV_IN_BCACHE 0x00000800 /* dev fd is open and used in bcache */
|
||||
#define DEV_BCACHE_EXCL 0x00001000 /* bcache_fd should be open EXCL */
|
||||
#define DEV_FILTER_AFTER_SCAN 0x00002000 /* apply filter after bcache has data */
|
||||
#define DEV_FILTER_OUT_SCAN 0x00004000 /* filtered out during label scan */
|
||||
#define DEV_BCACHE_WRITE 0x00008000 /* bcache_fd is open with RDWR */
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
@@ -61,6 +54,39 @@ struct dev_ext {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct dm_list aliases; /* struct dm_str_list */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
int fd;
|
||||
int open_count;
|
||||
int error_count;
|
||||
int max_error_count;
|
||||
int phys_block_size; /* From either BLKPBSZGET or BLKSSZGET, don't use */
|
||||
int block_size; /* From BLKBSZGET, returns bdev->bd_block_size, likely set by fs, probably don't use */
|
||||
int physical_block_size; /* From BLKPBSZGET: lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations */
|
||||
int logical_block_size; /* From BLKSSZGET: lowest possible block size that the storage device can address */
|
||||
int read_ahead;
|
||||
int bcache_fd;
|
||||
uint32_t flags;
|
||||
unsigned size_seqno;
|
||||
uint64_t size;
|
||||
uint64_t end;
|
||||
struct dev_ext ext;
|
||||
const char *duplicate_prefer_reason;
|
||||
|
||||
const char *vgid; /* if device is an LV */
|
||||
const char *lvid; /* if device is an LV */
|
||||
|
||||
char pvid[ID_LEN + 1]; /* if device is a PV */
|
||||
char _padding[7];
|
||||
};
|
||||
|
||||
/*
|
||||
* All I/O is annotated with the reason it is performed.
|
||||
*/
|
||||
@@ -77,12 +103,10 @@ typedef enum dev_io_reason {
|
||||
DEV_IO_LOG /* Logging messages */
|
||||
} dev_io_reason_t;
|
||||
|
||||
/*
|
||||
* Is this I/O for a device's extra metadata area?
|
||||
*/
|
||||
#define EXTRA_IO(reason) ((reason) == DEV_IO_MDA_EXTRA_HEADER || (reason) == DEV_IO_MDA_EXTRA_CONTENT)
|
||||
#define DEV_DEVBUF(dev, reason) (EXTRA_IO((reason)) ? &(dev)->last_extra_devbuf : &(dev)->last_devbuf)
|
||||
#define DEV_DEVBUF_DATA(dev, reason) ((char *) DEV_DEVBUF((dev), (reason))->buf + DEV_DEVBUF((dev), (reason))->data_offset)
|
||||
struct device_list {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct device_area {
|
||||
struct device *dev;
|
||||
@@ -90,56 +114,6 @@ struct device_area {
|
||||
uint64_t size; /* Bytes */
|
||||
};
|
||||
|
||||
struct device_buffer {
|
||||
uint64_t data_offset; /* Offset to start of requested data within buf */
|
||||
void *malloc_address; /* Start of allocated memory */
|
||||
void *buf; /* Aligned buffer that contains data within it */
|
||||
struct device_area where; /* Location of buf */
|
||||
dev_io_reason_t reason;
|
||||
unsigned write:1; /* 1 if write; 0 if read */
|
||||
|
||||
lvm_callback_fn_t dev_read_callback_fn;
|
||||
void *dev_read_callback_context;
|
||||
struct dm_list aio_queued; /* Queue of async I/O waiting to be issued */
|
||||
};
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct dm_list aliases; /* struct dm_str_list */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
int fd;
|
||||
int open_count;
|
||||
int error_count;
|
||||
int max_error_count;
|
||||
int phys_block_size;
|
||||
int block_size;
|
||||
int read_ahead;
|
||||
uint32_t flags;
|
||||
unsigned size_seqno;
|
||||
uint64_t size;
|
||||
uint64_t end;
|
||||
struct dm_list open_list;
|
||||
struct dev_ext ext;
|
||||
struct device_buffer last_devbuf; /* Last data buffer read from the device */
|
||||
struct device_buffer last_extra_devbuf; /* Last data buffer read from the device for extra metadata area */
|
||||
|
||||
const char *vgid; /* if device is an LV */
|
||||
const char *lvid; /* if device is an LV */
|
||||
|
||||
char pvid[ID_LEN + 1]; /* if device is a PV */
|
||||
char _padding[7];
|
||||
};
|
||||
|
||||
struct device_list {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
*/
|
||||
@@ -160,6 +134,8 @@ void dev_size_seqno_inc(void);
|
||||
* All io should use these routines.
|
||||
*/
|
||||
int dev_get_block_size(struct device *dev, unsigned int *phys_block_size, unsigned int *block_size);
|
||||
int dev_get_direct_block_sizes(struct device *dev, unsigned int *physical_block_size,
|
||||
unsigned int *logical_block_size);
|
||||
int dev_get_size(struct device *dev, uint64_t *size);
|
||||
int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead);
|
||||
int dev_discard_blocks(struct device *dev, uint64_t offset_bytes, uint64_t size_bytes);
|
||||
@@ -173,25 +149,14 @@ int dev_open_readonly_buffered(struct device *dev);
|
||||
int dev_open_readonly_quiet(struct device *dev);
|
||||
int dev_close(struct device *dev);
|
||||
int dev_close_immediate(struct device *dev);
|
||||
void dev_close_all(void);
|
||||
int dev_test_excl(struct device *dev);
|
||||
|
||||
int dev_fd(struct device *dev);
|
||||
const char *dev_name(const struct device *dev);
|
||||
|
||||
/* Returns a read-only buffer */
|
||||
const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason);
|
||||
const char *dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason);
|
||||
|
||||
/* Passes the data (or error) to dev_read_callback_fn */
|
||||
void dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
|
||||
unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context);
|
||||
|
||||
/* Read data and copy it into a supplied private buffer. */
|
||||
/* Only use for tiny reads or on unimportant code paths. */
|
||||
int dev_read_buf(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *retbuf);
|
||||
|
||||
int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer);
|
||||
int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason, char *buf);
|
||||
int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer);
|
||||
int dev_append(struct device *dev, size_t len, dev_io_reason_t reason, char *buffer);
|
||||
int dev_set(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, int value);
|
||||
@@ -201,15 +166,7 @@ struct device *dev_create_file(const char *filename, struct device *dev,
|
||||
struct dm_str_list *alias, int use_malloc);
|
||||
void dev_destroy_file(struct device *dev);
|
||||
|
||||
void devbufs_release(struct device *dev);
|
||||
|
||||
/* Return a valid device name from the alias list; NULL otherwise */
|
||||
const char *dev_name_confirmed(struct device *dev, int quiet);
|
||||
|
||||
struct cmd_context;
|
||||
int dev_async_getevents(void);
|
||||
int dev_async_setup(struct cmd_context *cmd);
|
||||
void dev_async_exit(void);
|
||||
int dev_async_reset(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -705,13 +705,10 @@ void vgdisplay_full(const struct volume_group *vg)
|
||||
|
||||
log_print("--- Volume group ---");
|
||||
log_print("VG Name %s", vg->name);
|
||||
log_print("System ID %s", (vg->system_id && *vg->system_id) ? vg->system_id : vg->lvm1_system_id ? : "");
|
||||
log_print("System ID %s", (vg->system_id && *vg->system_id) ? vg->system_id : "");
|
||||
log_print("Format %s", vg->fid->fmt->name);
|
||||
if (vg->fid->fmt->features & FMT_MDAS) {
|
||||
log_print("Metadata Areas %d",
|
||||
vg_mda_count(vg));
|
||||
log_print("Metadata Sequence No %d", vg->seqno);
|
||||
}
|
||||
log_print("Metadata Areas %d", vg_mda_count(vg));
|
||||
log_print("Metadata Sequence No %d", vg->seqno);
|
||||
access_str = vg->status & (LVM_READ | LVM_WRITE);
|
||||
log_print("VG Access %s%s%s%s",
|
||||
access_str == (LVM_READ | LVM_WRITE) ? "read/write" : "",
|
||||
|
||||
@@ -15,14 +15,19 @@
|
||||
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
#include "device.h"
|
||||
|
||||
static int _and_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct dev_filter **filters;
|
||||
int ret;
|
||||
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters)
|
||||
if (!(*filters)->passes_filter(*filters, dev))
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters) {
|
||||
ret = (*filters)->passes_filter(*filters, dev);
|
||||
|
||||
if (!ret)
|
||||
return 0; /* No 'stack': a filter, not an error. */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -52,13 +57,13 @@ static void _composite_destroy(struct dev_filter *f)
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
static int _dump(struct dev_filter *f, struct dm_pool *mem, int merge_existing)
|
||||
static int _dump(struct dev_filter *f, int merge_existing)
|
||||
{
|
||||
struct dev_filter **filters;
|
||||
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters)
|
||||
if ((*filters)->dump &&
|
||||
!(*filters)->dump(*filters, mem, merge_existing))
|
||||
!(*filters)->dump(*filters, merge_existing))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -16,21 +16,98 @@
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
|
||||
/* See label.c comment about this hack. */
|
||||
extern int use_full_md_check;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#define MSG_SKIPPING "%s: Skipping md component device"
|
||||
|
||||
static int _ignore_md(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
/*
|
||||
* The purpose of these functions is to ignore md component devices,
|
||||
* e.g. if /dev/md0 is a raid1 composed of /dev/loop0 and /dev/loop1,
|
||||
* lvm wants to deal with md0 and ignore loop0 and loop1. md0 should
|
||||
* pass the filter, and loop0,loop1 should not pass the filter so lvm
|
||||
* will ignore them.
|
||||
*
|
||||
* (This is assuming lvm.conf md_component_detection=1.)
|
||||
*
|
||||
* If lvm does *not* ignore the components, then lvm may read lvm
|
||||
* labels from the component devs and potentially the md dev,
|
||||
* which can trigger duplicate detection, and/or cause lvm to display
|
||||
* md components as PVs rather than ignoring them.
|
||||
*
|
||||
* If scanning md componenents causes duplicates to be seen, then
|
||||
* the lvm duplicate resolution will exclude the components.
|
||||
*
|
||||
* The lvm md filter has three modes:
|
||||
*
|
||||
* 1. look for md superblock at the start of the device
|
||||
* 2. look for md superblock at the start and end of the device
|
||||
* 3. use udev to detect components
|
||||
*
|
||||
* mode 1 will not detect and exclude components of md devices
|
||||
* that use superblock version 0.9 or 1.0 which is at the end of the device.
|
||||
*
|
||||
* mode 2 will detect these, but mode 2 doubles the i/o done by label
|
||||
* scan, since there's a read at both the start and end of every device.
|
||||
*
|
||||
* mode 3 is used when external_device_info_source="udev". It does
|
||||
* not require any io from lvm, but this mode is not used by default
|
||||
* because there have been problems getting reliable info from udev.
|
||||
*
|
||||
* lvm uses mode 2 when:
|
||||
*
|
||||
* - the command is pvcreate/vgcreate/vgextend, which format new
|
||||
* devices, and if the user ran these commands on a component
|
||||
* device of an md device 0.9 or 1.0, then it would cause problems.
|
||||
* FIXME: this would only really need to scan the end of the
|
||||
* devices being formatted, not all devices.
|
||||
*
|
||||
* - it sees an md device on the system using version 0.9 or 1.0.
|
||||
* The point of this is just to avoid displaying md components
|
||||
* from the 'pvs' command.
|
||||
* FIXME: the cost (double i/o) may not be worth the benefit
|
||||
* (not showing md components).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Returns 0 if:
|
||||
* the device is an md component and it should be ignored.
|
||||
*
|
||||
* Returns 1 if:
|
||||
* the device is not md component and should not be ignored.
|
||||
*
|
||||
* The actual md device will pass this filter and should be used,
|
||||
* it is the md component devices that we are trying to exclude
|
||||
* that will not pass.
|
||||
*/
|
||||
|
||||
static int _passes_md_filter(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
/*
|
||||
* When md_component_dectection=0, don't even try to skip md
|
||||
* components.
|
||||
*/
|
||||
if (!md_filtering())
|
||||
return 1;
|
||||
|
||||
ret = dev_is_md(dev, NULL);
|
||||
|
||||
ret = dev_is_md(dev, NULL, use_full_md_check);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
/* let pass, call again after scan */
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
log_debug_devs("filter md deferred %s", dev_name(dev));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return 1;
|
||||
|
||||
if (ret == 1) {
|
||||
log_debug_devs("md filter full %d excluding md component %s", use_full_md_check, dev_name(dev));
|
||||
if (dev->ext.src == DEV_EXT_NONE)
|
||||
log_debug_devs(MSG_SKIPPING, dev_name(dev));
|
||||
else
|
||||
@@ -56,7 +133,7 @@ static void _destroy(struct dev_filter *f)
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *md_filter_create(struct dev_types *dt)
|
||||
struct dev_filter *md_filter_create(struct cmd_context *cmd, struct dev_types *dt)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
@@ -65,7 +142,7 @@ struct dev_filter *md_filter_create(struct dev_types *dt)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = _ignore_md;
|
||||
f->passes_filter = _passes_md_filter;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
f->private = dt;
|
||||
|
||||
@@ -21,8 +21,18 @@
|
||||
static int _passes_partitioned_filter(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct dev_types *dt = (struct dev_types *) f->private;
|
||||
int ret;
|
||||
|
||||
if (dev_is_partitioned(dt, dev)) {
|
||||
ret = dev_is_partitioned(dt, dev);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
/* let pass, call again after scan */
|
||||
log_debug_devs("filter partitioned deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (dev->ext.src == DEV_EXT_NONE)
|
||||
log_debug_devs(MSG_SKIPPING, dev_name(dev));
|
||||
else
|
||||
|
||||
@@ -26,12 +26,39 @@ struct pfilter {
|
||||
struct dev_types *dt;
|
||||
};
|
||||
|
||||
/*
|
||||
* The persistent filter is filter layer that sits above the other filters and
|
||||
* caches the final result of those other filters. When a device is first
|
||||
* checked against filters, it will not be in this cache, so this filter will
|
||||
* pass the device down to the other filters to check it. The other filters
|
||||
* will run and either include the device (good/pass) or exclude the device
|
||||
* (bad/fail). That good or bad result propagates up through this filter which
|
||||
* saves the result. The next time some code checks the filters against the
|
||||
* device, this persistent/cache filter is checked first. This filter finds
|
||||
* the previous result in its cache and returns it without reevaluating the
|
||||
* other real filters.
|
||||
*
|
||||
* FIXME: a cache like this should not be needed. The fact it's needed is a
|
||||
* symptom of code that should be fixed to not reevaluate filters multiple
|
||||
* times. A device should be checked against the filter once, and then not
|
||||
* need to be checked again. With scanning now controlled, we could probably
|
||||
* do this.
|
||||
*
|
||||
* FIXME: "persistent" isn't a great name for this caching filter. This filter
|
||||
* at one time saved its cache results to a file, which is how it got the name.
|
||||
* That .cache file does not work well, causes problems, and is no longer used
|
||||
* by default. The old code for it should be removed.
|
||||
*/
|
||||
|
||||
static const char* _good_device = "good";
|
||||
static const char* _bad_device = "bad";
|
||||
|
||||
/*
|
||||
* The hash table holds one of these two states
|
||||
* against each entry.
|
||||
*/
|
||||
#define PF_BAD_DEVICE ((void *) 1)
|
||||
#define PF_GOOD_DEVICE ((void *) 2)
|
||||
#define PF_BAD_DEVICE ((void *) &_good_device)
|
||||
#define PF_GOOD_DEVICE ((void *) &_bad_device)
|
||||
|
||||
static int _init_hash(struct pfilter *pf)
|
||||
{
|
||||
@@ -48,11 +75,7 @@ static void _persistent_filter_wipe(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
log_verbose("Wiping cache of LVM-capable devices");
|
||||
dm_hash_wipe(pf->devices);
|
||||
|
||||
/* Trigger complete device scan */
|
||||
dev_cache_scan(1);
|
||||
}
|
||||
|
||||
static int _read_array(struct pfilter *pf, struct dm_config_tree *cft,
|
||||
@@ -87,7 +110,7 @@ static int _read_array(struct pfilter *pf, struct dm_config_tree *cft,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_config_tree **cft_out)
|
||||
int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
struct dm_config_tree *cft;
|
||||
@@ -116,7 +139,7 @@ int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_
|
||||
if (!(cft = config_open(CONFIG_FILE_SPECIAL, pf->file, 1)))
|
||||
return_0;
|
||||
|
||||
if (!config_file_read(mem, cft))
|
||||
if (!config_file_read(cft))
|
||||
goto_out;
|
||||
|
||||
log_debug_devs("Loading persistent filter cache from %s", pf->file);
|
||||
@@ -126,15 +149,6 @@ int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_
|
||||
/* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
|
||||
PF_BAD_DEVICE); */
|
||||
|
||||
/* Did we find anything? */
|
||||
if (dm_hash_get_num_entries(pf->devices)) {
|
||||
/* We populated dev_cache ourselves */
|
||||
dev_cache_scan(0);
|
||||
if (!dev_cache_index_devs())
|
||||
stack;
|
||||
r = 1;
|
||||
}
|
||||
|
||||
log_very_verbose("Loaded persistent filter cache from %s", pf->file);
|
||||
|
||||
out:
|
||||
@@ -175,7 +189,7 @@ static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
|
||||
fprintf(fp, "\n\t]\n");
|
||||
}
|
||||
|
||||
static int _persistent_filter_dump(struct dev_filter *f, struct dm_pool *mem, int merge_existing)
|
||||
static int _persistent_filter_dump(struct dev_filter *f, int merge_existing)
|
||||
{
|
||||
struct pfilter *pf;
|
||||
char *tmp_file;
|
||||
@@ -234,7 +248,7 @@ static int _persistent_filter_dump(struct dev_filter *f, struct dm_pool *mem, in
|
||||
lvm_stat_ctim(&ts, &info);
|
||||
if (merge_existing && timespeccmp(&ts, &pf->ctime, !=))
|
||||
/* Keep cft open to avoid losing lock */
|
||||
persistent_filter_load(mem, f, &cft);
|
||||
persistent_filter_load(f, &cft);
|
||||
|
||||
tmp_file = alloca(strlen(pf->file) + 5);
|
||||
sprintf(tmp_file, "%s.tmp", pf->file);
|
||||
@@ -275,29 +289,61 @@ out:
|
||||
static int _lookup_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
void *l = dm_hash_lookup(pf->devices, dev_name(dev));
|
||||
void *l;
|
||||
struct dm_str_list *sl;
|
||||
int pass = 1;
|
||||
|
||||
/* Cached BAD? */
|
||||
if (l == PF_BAD_DEVICE) {
|
||||
log_debug_devs("%s: Skipping (cached)", dev_name(dev));
|
||||
if (dm_list_empty(&dev->aliases)) {
|
||||
log_debug_devs("%d:%d: filter cache skipping (no name)",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test dm devices every time, so cache them as GOOD. */
|
||||
if (MAJOR(dev->dev) == pf->dt->device_mapper_major) {
|
||||
if (!l)
|
||||
dm_list_iterate_items(sl, &dev->aliases)
|
||||
if (!dm_hash_insert(pf->devices, sl->str, PF_GOOD_DEVICE)) {
|
||||
log_error("Failed to hash device to filter.");
|
||||
return 0;
|
||||
}
|
||||
return pf->real->passes_filter(pf->real, dev);
|
||||
l = dm_hash_lookup(pf->devices, dev_name(dev));
|
||||
|
||||
/* Cached bad, skip dev */
|
||||
if (l == PF_BAD_DEVICE) {
|
||||
log_debug_devs("%s: filter cache skipping (cached bad)", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Uncached */
|
||||
/* Cached good, use dev */
|
||||
if (l == PF_GOOD_DEVICE) {
|
||||
log_debug_devs("%s: filter cache using (cached good)", dev_name(dev));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Uncached, check filters and cache the result */
|
||||
if (!l) {
|
||||
l = pf->real->passes_filter(pf->real, dev) ? PF_GOOD_DEVICE : PF_BAD_DEVICE;
|
||||
dev->flags &= ~DEV_FILTER_AFTER_SCAN;
|
||||
|
||||
pass = pf->real->passes_filter(pf->real, dev);
|
||||
|
||||
if (!pass) {
|
||||
/*
|
||||
* A device that does not pass one filter is excluded
|
||||
* even if the result of another filter is deferred,
|
||||
* because the deferred result won't change the exclude.
|
||||
*/
|
||||
l = PF_BAD_DEVICE;
|
||||
|
||||
} else if ((pass == -EAGAIN) || (dev->flags & DEV_FILTER_AFTER_SCAN)) {
|
||||
/*
|
||||
* When the filter result is deferred, we let the device
|
||||
* pass for now, but do not cache the result. We need to
|
||||
* rerun the filters later. At that point the final result
|
||||
* will be cached.
|
||||
*/
|
||||
log_debug_devs("filter cache deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
pass = 1;
|
||||
goto out;
|
||||
|
||||
} else if (pass) {
|
||||
l = PF_GOOD_DEVICE;
|
||||
}
|
||||
|
||||
log_debug_devs("filter caching %s %s", pass ? "good" : "bad", dev_name(dev));
|
||||
|
||||
dm_list_iterate_items(sl, &dev->aliases)
|
||||
if (!dm_hash_insert(pf->devices, sl->str, l)) {
|
||||
@@ -305,8 +351,8 @@ static int _lookup_p(struct dev_filter *f, struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (l == PF_BAD_DEVICE) ? 0 : 1;
|
||||
out:
|
||||
return pass;
|
||||
}
|
||||
|
||||
static void _persistent_destroy(struct dev_filter *f)
|
||||
|
||||
96
lib/filters/filter-signature.c
Normal file
96
lib/filters/filter-signature.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Luca Berra
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
static int _ignore_signature(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
int ret = 0;
|
||||
|
||||
if (!scan_bcache) {
|
||||
/* let pass, call again after scan */
|
||||
log_debug_devs("filter signature deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, BUFSIZE);
|
||||
|
||||
if (!dev_read_bytes(dev, 0, BUFSIZE, buf)) {
|
||||
log_debug_devs("%s: Skipping: error in signature detection",
|
||||
dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev_is_lvm1(dev, buf, BUFSIZE)) {
|
||||
log_debug_devs("%s: Skipping lvm1 device", dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev_is_pool(dev, buf, BUFSIZE)) {
|
||||
log_debug_devs("%s: Skipping gfs-pool device", dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
if (f->use_count)
|
||||
log_error(INTERNAL_ERROR "Destroying signature filter while in use %u times.", f->use_count);
|
||||
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dm_zalloc(sizeof(*f)))) {
|
||||
log_error("md filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = _ignore_signature;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
f->private = dt;
|
||||
|
||||
log_debug_devs("signature filter initialised.");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,11 @@
|
||||
#include "dev-ext-udev-constants.h"
|
||||
#endif
|
||||
|
||||
struct filter_data {
|
||||
filter_mode_t mode;
|
||||
int skip_lvs;
|
||||
};
|
||||
|
||||
static const char *_too_small_to_hold_pv_msg = "Too small to hold a PV";
|
||||
|
||||
static int _native_check_pv_min_size(struct device *dev)
|
||||
@@ -27,12 +32,6 @@ static int _native_check_pv_min_size(struct device *dev)
|
||||
uint64_t size;
|
||||
int ret = 0;
|
||||
|
||||
/* Check it's accessible */
|
||||
if (!dev_open_readonly_quiet(dev)) {
|
||||
log_debug_devs("%s: Skipping: open failed", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check it's not too small */
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
log_debug_devs("%s: Skipping: dev_get_size failed", dev_name(dev));
|
||||
@@ -47,9 +46,6 @@ static int _native_check_pv_min_size(struct device *dev)
|
||||
|
||||
ret = 1;
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -110,7 +106,9 @@ static int _check_pv_min_size(struct device *dev)
|
||||
|
||||
static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
filter_mode_t mode = *((filter_mode_t *) f->private);
|
||||
struct filter_data *data = f->private;
|
||||
filter_mode_t mode = data->mode;
|
||||
int skip_lvs = data->skip_lvs;
|
||||
struct dev_usable_check_params ucp = {0};
|
||||
int r = 1;
|
||||
|
||||
@@ -123,6 +121,7 @@ static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
||||
ucp.check_suspended = ignore_suspended_devices();
|
||||
ucp.check_error_target = 1;
|
||||
ucp.check_reserved = 1;
|
||||
ucp.check_lv = skip_lvs;
|
||||
break;
|
||||
case FILTER_MODE_PRE_LVMETAD:
|
||||
ucp.check_empty = 1;
|
||||
@@ -130,6 +129,7 @@ static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
||||
ucp.check_suspended = 0;
|
||||
ucp.check_error_target = 1;
|
||||
ucp.check_reserved = 1;
|
||||
ucp.check_lv = skip_lvs;
|
||||
break;
|
||||
case FILTER_MODE_POST_LVMETAD:
|
||||
ucp.check_empty = 0;
|
||||
@@ -137,6 +137,7 @@ static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
||||
ucp.check_suspended = ignore_suspended_devices();
|
||||
ucp.check_error_target = 0;
|
||||
ucp.check_reserved = 0;
|
||||
ucp.check_lv = skip_lvs;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -170,8 +171,9 @@ static void _usable_filter_destroy(struct dev_filter *f)
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *usable_filter_create(struct dev_types *dt __attribute__((unused)), filter_mode_t mode)
|
||||
struct dev_filter *usable_filter_create(struct cmd_context *cmd, struct dev_types *dt __attribute__((unused)), filter_mode_t mode)
|
||||
{
|
||||
struct filter_data *data;
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dm_zalloc(sizeof(struct dev_filter)))) {
|
||||
@@ -182,14 +184,20 @@ struct dev_filter *usable_filter_create(struct dev_types *dt __attribute__((unus
|
||||
f->passes_filter = _passes_usable_filter;
|
||||
f->destroy = _usable_filter_destroy;
|
||||
f->use_count = 0;
|
||||
if (!(f->private = dm_zalloc(sizeof(filter_mode_t)))) {
|
||||
|
||||
if (!(data = dm_zalloc(sizeof(struct filter_data)))) {
|
||||
log_error("Usable device filter mode allocation failed");
|
||||
dm_free(f);
|
||||
return NULL;
|
||||
}
|
||||
*((filter_mode_t *) f->private) = mode;
|
||||
|
||||
log_debug_devs("Usable device filter initialised.");
|
||||
data->mode = mode;
|
||||
|
||||
data->skip_lvs = !find_config_tree_bool(cmd, devices_scan_lvs_CFG, NULL);
|
||||
|
||||
f->private = data;
|
||||
|
||||
log_debug_devs("Usable device filter initialised (scan_lvs %d).", !data->skip_lvs);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
struct dev_filter *composite_filter_create(int n, int use_dev_ext_info, struct dev_filter **filters);
|
||||
|
||||
struct dev_filter *lvm_type_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *md_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *md_filter_create(struct cmd_context *cmd, struct dev_types *dt);
|
||||
struct dev_filter *fwraid_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *mpath_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *partitioned_filter_create(struct dev_types *dt);
|
||||
@@ -31,6 +31,7 @@ struct dev_filter *persistent_filter_create(struct dev_types *dt,
|
||||
struct dev_filter *f,
|
||||
const char *file);
|
||||
struct dev_filter *sysfs_filter_create(void);
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt);
|
||||
|
||||
struct dev_filter *internal_filter_create(void);
|
||||
int internal_filter_allow(struct dm_pool *mem, struct device *dev);
|
||||
@@ -51,8 +52,8 @@ typedef enum {
|
||||
FILTER_MODE_PRE_LVMETAD,
|
||||
FILTER_MODE_POST_LVMETAD
|
||||
} filter_mode_t;
|
||||
struct dev_filter *usable_filter_create(struct dev_types *dt, filter_mode_t mode);
|
||||
struct dev_filter *usable_filter_create(struct cmd_context *cmd, struct dev_types *dt, filter_mode_t mode);
|
||||
|
||||
int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_config_tree **cft_out);
|
||||
int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out);
|
||||
|
||||
#endif /* _LVM_FILTER_H */
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
init_format
|
||||
@@ -1,762 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "disk-rep.h"
|
||||
#include "xlate.h"
|
||||
#include "lvmcache.h"
|
||||
#include "metadata-exported.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define xx16(v) disk->v = xlate16(disk->v)
|
||||
#define xx32(v) disk->v = xlate32(disk->v)
|
||||
#define xx64(v) disk->v = xlate64(disk->v)
|
||||
|
||||
/*
|
||||
* Functions to perform the endian conversion
|
||||
* between disk and core. The same code works
|
||||
* both ways of course.
|
||||
*/
|
||||
static void _xlate_pvd(struct pv_disk *disk)
|
||||
{
|
||||
xx16(version);
|
||||
|
||||
xx32(pv_on_disk.base);
|
||||
xx32(pv_on_disk.size);
|
||||
xx32(vg_on_disk.base);
|
||||
xx32(vg_on_disk.size);
|
||||
xx32(pv_uuidlist_on_disk.base);
|
||||
xx32(pv_uuidlist_on_disk.size);
|
||||
xx32(lv_on_disk.base);
|
||||
xx32(lv_on_disk.size);
|
||||
xx32(pe_on_disk.base);
|
||||
xx32(pe_on_disk.size);
|
||||
|
||||
xx32(pv_major);
|
||||
xx32(pv_number);
|
||||
xx32(pv_status);
|
||||
xx32(pv_allocatable);
|
||||
xx32(pv_size);
|
||||
xx32(lv_cur);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pe_start);
|
||||
}
|
||||
|
||||
static void _xlate_lvd(struct lv_disk *disk)
|
||||
{
|
||||
xx32(lv_access);
|
||||
xx32(lv_status);
|
||||
xx32(lv_open);
|
||||
xx32(lv_dev);
|
||||
xx32(lv_number);
|
||||
xx32(lv_mirror_copies);
|
||||
xx32(lv_recovery);
|
||||
xx32(lv_schedule);
|
||||
xx32(lv_size);
|
||||
xx32(lv_snapshot_minor);
|
||||
xx16(lv_chunk_size);
|
||||
xx16(dummy);
|
||||
xx32(lv_allocated_le);
|
||||
xx32(lv_stripes);
|
||||
xx32(lv_stripesize);
|
||||
xx32(lv_badblock);
|
||||
xx32(lv_allocation);
|
||||
xx32(lv_io_timeout);
|
||||
xx32(lv_read_ahead);
|
||||
}
|
||||
|
||||
static void _xlate_vgd(struct vg_disk *disk)
|
||||
{
|
||||
xx32(vg_number);
|
||||
xx32(vg_access);
|
||||
xx32(vg_status);
|
||||
xx32(lv_max);
|
||||
xx32(lv_cur);
|
||||
xx32(lv_open);
|
||||
xx32(pv_max);
|
||||
xx32(pv_cur);
|
||||
xx32(pv_act);
|
||||
xx32(dummy);
|
||||
xx32(vgda);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pvg_total);
|
||||
}
|
||||
|
||||
static void _xlate_extents(struct pe_disk *extents, uint32_t count)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
extents[i].lv_num = xlate16(extents[i].lv_num);
|
||||
extents[i].le_num = xlate16(extents[i].le_num);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle both minor metadata formats.
|
||||
*/
|
||||
static int _munge_formats(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start;
|
||||
unsigned b, e;
|
||||
|
||||
switch (pvd->version) {
|
||||
case 1:
|
||||
pvd->pe_start = ((pvd->pe_on_disk.base +
|
||||
pvd->pe_on_disk.size) >> SECTOR_SHIFT);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pvd->version = 1;
|
||||
pe_start = pvd->pe_start << SECTOR_SHIFT;
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* UUID too long? */
|
||||
if (pvd->pv_uuid[ID_LEN]) {
|
||||
/* Retain ID_LEN chars from end */
|
||||
for (e = ID_LEN; e < sizeof(pvd->pv_uuid); e++) {
|
||||
if (!pvd->pv_uuid[e]) {
|
||||
e--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (b = 0; b < ID_LEN; b++) {
|
||||
pvd->pv_uuid[b] = pvd->pv_uuid[++e - ID_LEN];
|
||||
/* FIXME Remove all invalid chars */
|
||||
if (pvd->pv_uuid[b] == '/')
|
||||
pvd->pv_uuid[b] = '#';
|
||||
}
|
||||
memset(&pvd->pv_uuid[ID_LEN], 0, sizeof(pvd->pv_uuid) - ID_LEN);
|
||||
}
|
||||
|
||||
/* If UUID is missing, create one */
|
||||
if (pvd->pv_uuid[0] == '\0') {
|
||||
uuid_from_num((char *)pvd->pv_uuid, pvd->pv_number);
|
||||
pvd->pv_uuid[ID_LEN] = '\0';
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If exported, remove "PV_EXP" from end of VG name
|
||||
*/
|
||||
static void _munge_exported_vg(struct pv_disk *pvd)
|
||||
{
|
||||
int l;
|
||||
size_t s;
|
||||
|
||||
/* Return if PV not in a VG */
|
||||
if ((!*pvd->vg_name))
|
||||
return;
|
||||
/* FIXME also check vgd->status & VG_EXPORTED? */
|
||||
|
||||
l = strlen((char *)pvd->vg_name);
|
||||
s = sizeof(EXPORTED_TAG);
|
||||
if (!strncmp((char *)pvd->vg_name + l - s + 1, EXPORTED_TAG, s)) {
|
||||
pvd->vg_name[l - s + 1] = '\0';
|
||||
pvd->pv_status |= VG_EXPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
int munge_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
{
|
||||
_xlate_pvd(pvd);
|
||||
|
||||
if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
|
||||
log_very_verbose("%s does not have a valid LVM1 PV identifier",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_munge_formats(pvd)) {
|
||||
log_very_verbose("format1: Unknown metadata version %d "
|
||||
"found on %s", pvd->version, dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If VG is exported, set VG name back to the real name */
|
||||
_munge_exported_vg(pvd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
{
|
||||
if (!dev_read_buf(dev, UINT64_C(0), sizeof(*pvd), DEV_IO_FMT1, pvd)) {
|
||||
log_very_verbose("Failed to read PV data from %s",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return munge_pvd(dev, pvd);
|
||||
}
|
||||
|
||||
static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
|
||||
{
|
||||
if (!dev_read_buf(dev, pos, sizeof(*disk), DEV_IO_FMT1, disk))
|
||||
return_0;
|
||||
|
||||
_xlate_lvd(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd)
|
||||
{
|
||||
uint64_t pos = pvd->vg_on_disk.base;
|
||||
|
||||
if (!dev_read_buf(dev, pos, sizeof(*vgd), DEV_IO_FMT1, vgd))
|
||||
return_0;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
|
||||
if ((vgd->lv_max > MAX_LV) || (vgd->pv_max > MAX_PV))
|
||||
return_0;
|
||||
|
||||
/* If UUID is missing, create one */
|
||||
if (vgd->vg_uuid[0] == '\0')
|
||||
uuid_from_num((char *)vgd->vg_uuid, vgd->vg_number);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_uuids(struct disk_list *data)
|
||||
{
|
||||
unsigned num_read = 0;
|
||||
struct uuid_list *ul;
|
||||
char buffer[NAME_LEN] __attribute__((aligned(8)));
|
||||
uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
|
||||
uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
|
||||
|
||||
while (pos < end && num_read < data->vgd.pv_cur) {
|
||||
if (!dev_read_buf(data->dev, pos, sizeof(buffer), DEV_IO_FMT1, buffer))
|
||||
return_0;
|
||||
|
||||
if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul))))
|
||||
return_0;
|
||||
|
||||
memcpy(ul->uuid, buffer, NAME_LEN);
|
||||
ul->uuid[NAME_LEN - 1] = '\0';
|
||||
|
||||
dm_list_add(&data->uuids, &ul->list);
|
||||
|
||||
pos += NAME_LEN;
|
||||
num_read++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_lvd(struct lv_disk *lvd)
|
||||
{
|
||||
return !(lvd->lv_name[0] == '\0');
|
||||
}
|
||||
|
||||
static int _read_lvs(struct disk_list *data)
|
||||
{
|
||||
unsigned int i, lvs_read = 0;
|
||||
uint64_t pos;
|
||||
struct lvd_list *ll;
|
||||
struct vg_disk *vgd = &data->vgd;
|
||||
|
||||
for (i = 0; (i < vgd->lv_max) && (lvs_read < vgd->lv_cur); i++) {
|
||||
pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
|
||||
ll = dm_pool_alloc(data->mem, sizeof(*ll));
|
||||
|
||||
if (!ll)
|
||||
return_0;
|
||||
|
||||
if (!_read_lvd(data->dev, pos, &ll->lvd))
|
||||
return_0;
|
||||
|
||||
if (!_check_lvd(&ll->lvd))
|
||||
continue;
|
||||
|
||||
lvs_read++;
|
||||
dm_list_add(&data->lvds, &ll->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
|
||||
struct pe_disk *extents = dm_pool_alloc(data->mem, len);
|
||||
uint64_t pos = data->pvd.pe_on_disk.base;
|
||||
|
||||
if (!extents)
|
||||
return_0;
|
||||
|
||||
if (!dev_read_buf(data->dev, pos, len, DEV_IO_FMT1, extents))
|
||||
return_0;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
data->extents = extents;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __update_lvmcache(const struct format_type *fmt,
|
||||
struct disk_list *dl,
|
||||
struct device *dev, const char *vgid,
|
||||
unsigned exported)
|
||||
{
|
||||
struct lvmcache_info *info;
|
||||
const char *vgname = *((char *)dl->pvd.vg_name) ?
|
||||
(char *)dl->pvd.vg_name : fmt->orphan_vg_name;
|
||||
|
||||
if (!(info = lvmcache_add(fmt->labeller, (char *)dl->pvd.pv_uuid, dev,
|
||||
vgname, vgid, exported ? EXPORTED_VG : 0))) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32(dl->pvd.pv_size)) << SECTOR_SHIFT);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_make_valid(info);
|
||||
}
|
||||
|
||||
static struct disk_list *__read_disk(const struct format_type *fmt,
|
||||
struct device *dev, struct dm_pool *mem,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct disk_list *dl = dm_pool_zalloc(mem, sizeof(*dl));
|
||||
const char *name = dev_name(dev);
|
||||
|
||||
if (!dl)
|
||||
return_NULL;
|
||||
|
||||
dl->dev = dev;
|
||||
dl->mem = mem;
|
||||
dm_list_init(&dl->uuids);
|
||||
dm_list_init(&dl->lvds);
|
||||
|
||||
if (!_read_pvd(dev, &dl->pvd))
|
||||
goto_bad;
|
||||
|
||||
/*
|
||||
* is it an orphan ?
|
||||
*/
|
||||
if (!*dl->pvd.vg_name) {
|
||||
log_very_verbose("%s is not a member of any format1 VG", name);
|
||||
|
||||
__update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
|
||||
return (vg_name) ? NULL : dl;
|
||||
}
|
||||
|
||||
if (!read_vgd(dl->dev, &dl->vgd, &dl->pvd)) {
|
||||
log_error("Failed to read VG data from PV (%s)", name);
|
||||
__update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (vg_name && strcmp(vg_name, (char *)dl->pvd.vg_name)) {
|
||||
log_very_verbose("%s is not a member of the VG %s",
|
||||
name, vg_name);
|
||||
__update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
__update_lvmcache(fmt, dl, dev, (char *)dl->vgd.vg_uuid,
|
||||
dl->vgd.vg_status & VG_EXPORTED);
|
||||
|
||||
if (!_read_uuids(dl)) {
|
||||
log_error("Failed to read PV uuid list from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_lvs(dl)) {
|
||||
log_error("Failed to read LV's from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_extents(dl)) {
|
||||
log_error("Failed to read extents from %s", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
log_very_verbose("Found %s in %sVG %s", name,
|
||||
(dl->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
|
||||
dl->pvd.vg_name);
|
||||
|
||||
return dl;
|
||||
|
||||
bad:
|
||||
dm_pool_free(dl->mem, dl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
|
||||
struct dm_pool *mem, const char *vg_name)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
|
||||
if (!dev_open_readonly(dev))
|
||||
return_NULL;
|
||||
|
||||
dl = __read_disk(fmt, dev, mem, vg_name);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return dl;
|
||||
}
|
||||
|
||||
static void _add_pv_to_list(struct cmd_context *cmd, struct dm_list *head, struct disk_list *data)
|
||||
{
|
||||
struct pv_disk *pvd;
|
||||
struct disk_list *diskl;
|
||||
|
||||
dm_list_iterate_items(diskl, head) {
|
||||
pvd = &diskl->pvd;
|
||||
if (!strncmp((char *)data->pvd.pv_uuid, (char *)pvd->pv_uuid,
|
||||
sizeof(pvd->pv_uuid))) {
|
||||
if (!dev_subsystem_part_major(cmd->dev_types, data->dev)) {
|
||||
log_very_verbose("Ignoring duplicate PV %s on "
|
||||
"%s", pvd->pv_uuid,
|
||||
dev_name(data->dev));
|
||||
return;
|
||||
}
|
||||
log_very_verbose("Duplicate PV %s - using %s %s",
|
||||
pvd->pv_uuid, dev_subsystem_name(cmd->dev_types, data->dev),
|
||||
dev_name(data->dev));
|
||||
dm_list_del(&diskl->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dm_list_add(head, &data->list);
|
||||
}
|
||||
|
||||
struct _read_pvs_in_vg_baton {
|
||||
const char *vg_name;
|
||||
struct dm_list *head;
|
||||
struct disk_list *data;
|
||||
struct dm_pool *mem;
|
||||
int empty;
|
||||
};
|
||||
|
||||
static int _read_pv_in_vg(struct lvmcache_info *info, void *baton)
|
||||
{
|
||||
struct _read_pvs_in_vg_baton *b = baton;
|
||||
|
||||
b->empty = 0;
|
||||
|
||||
if (!lvmcache_device(info) ||
|
||||
!(b->data = read_disk(lvmcache_fmt(info), lvmcache_device(info), b->mem, b->vg_name)))
|
||||
return 0; /* stop here */
|
||||
|
||||
_add_pv_to_list(lvmcache_fmt(info)->cmd, b->head, b->data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a list of pv_d's structures, allocated from mem.
|
||||
* We keep track of the first object allocated from the pool
|
||||
* so we can free off all the memory if something goes wrong.
|
||||
*/
|
||||
int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
|
||||
struct dev_filter *filter, struct dm_pool *mem,
|
||||
struct dm_list *head)
|
||||
{
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
struct _read_pvs_in_vg_baton baton;
|
||||
|
||||
baton.head = head;
|
||||
baton.empty = 1;
|
||||
baton.data = NULL;
|
||||
baton.mem = mem;
|
||||
baton.vg_name = vg_name;
|
||||
|
||||
/* Fast path if we already saw this VG and cached the list of PVs */
|
||||
if (vg_name && (vginfo = lvmcache_vginfo_from_vgname(vg_name, NULL))) {
|
||||
|
||||
lvmcache_foreach_pv(vginfo, _read_pv_in_vg, &baton);
|
||||
|
||||
if (!baton.empty) {
|
||||
/* Did we find the whole VG? */
|
||||
if (!vg_name || is_orphan_vg(vg_name) ||
|
||||
(baton.data && *baton.data->pvd.vg_name &&
|
||||
dm_list_size(head) == baton.data->vgd.pv_cur))
|
||||
return 1;
|
||||
|
||||
/* Failed */
|
||||
dm_list_init(head);
|
||||
/* vgcache_del(vg_name); */
|
||||
}
|
||||
}
|
||||
|
||||
if (!(iter = dev_iter_create(filter, 1))) {
|
||||
log_error("read_pvs_in_vg: dev_iter_create failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise do a complete scan */
|
||||
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
|
||||
if ((baton.data = read_disk(fmt, dev, mem, vg_name))) {
|
||||
_add_pv_to_list(fmt->cmd, head, baton.data);
|
||||
}
|
||||
}
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
if (dm_list_empty(head))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_vgd(struct disk_list *data)
|
||||
{
|
||||
struct vg_disk *vgd = &data->vgd;
|
||||
uint64_t pos = data->pvd.vg_on_disk.base;
|
||||
|
||||
log_debug_metadata("Writing %s VG metadata to %s at %" PRIu64 " len %" PRIsize_t,
|
||||
data->pvd.vg_name, dev_name(data->dev), pos, sizeof(*vgd));
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
if (!dev_write(data->dev, pos, sizeof(*vgd), DEV_IO_FMT1, vgd))
|
||||
return_0;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_uuids(struct disk_list *data)
|
||||
{
|
||||
struct uuid_list *ul;
|
||||
uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
|
||||
uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
|
||||
|
||||
dm_list_iterate_items(ul, &data->uuids) {
|
||||
if (pos >= end) {
|
||||
log_error("Too many uuids to fit on %s",
|
||||
dev_name(data->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug_metadata("Writing %s uuidlist to %s at %" PRIu64 " len %d",
|
||||
data->pvd.vg_name, dev_name(data->dev),
|
||||
pos, NAME_LEN);
|
||||
|
||||
if (!dev_write(data->dev, pos, NAME_LEN, DEV_IO_FMT1, ul->uuid))
|
||||
return_0;
|
||||
|
||||
pos += NAME_LEN;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
|
||||
{
|
||||
log_debug_metadata("Writing %s LV %s metadata to %s at %" PRIu64 " len %"
|
||||
PRIsize_t, disk->vg_name, disk->lv_name, dev_name(dev),
|
||||
pos, sizeof(*disk));
|
||||
|
||||
_xlate_lvd(disk);
|
||||
if (!dev_write(dev, pos, sizeof(*disk), DEV_IO_FMT1, disk))
|
||||
return_0;
|
||||
|
||||
_xlate_lvd(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lvs(struct disk_list *data)
|
||||
{
|
||||
struct lvd_list *ll;
|
||||
uint64_t pos, offset;
|
||||
|
||||
pos = data->pvd.lv_on_disk.base;
|
||||
|
||||
if (!dev_set(data->dev, pos, data->pvd.lv_on_disk.size, DEV_IO_FMT1, 0)) {
|
||||
log_error("Couldn't zero lv area on device '%s'",
|
||||
dev_name(data->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(ll, &data->lvds) {
|
||||
offset = sizeof(struct lv_disk) * ll->lvd.lv_number;
|
||||
if (offset + sizeof(struct lv_disk) > data->pvd.lv_on_disk.size) {
|
||||
log_error("lv_number %d too large", ll->lvd.lv_number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_lvd(data->dev, pos + offset, &ll->lvd))
|
||||
return_0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
|
||||
struct pe_disk *extents = data->extents;
|
||||
uint64_t pos = data->pvd.pe_on_disk.base;
|
||||
|
||||
log_debug_metadata("Writing %s extents metadata to %s at %" PRIu64 " len %"
|
||||
PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
|
||||
pos, len);
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
if (!dev_write(data->dev, pos, len, DEV_IO_FMT1, extents))
|
||||
return_0;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_pvd(struct disk_list *data)
|
||||
{
|
||||
char *buf;
|
||||
uint64_t pos = data->pvd.pv_on_disk.base;
|
||||
size_t size = data->pvd.pv_on_disk.size;
|
||||
|
||||
if (size < sizeof(struct pv_disk)) {
|
||||
log_error("Invalid PV structure size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure that the gap between the PV structure and
|
||||
the next one is zeroed in order to make non LVM tools
|
||||
happy (idea from AED) */
|
||||
buf = dm_zalloc(size);
|
||||
if (!buf) {
|
||||
log_error("Couldn't allocate temporary PV buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buf, &data->pvd, sizeof(struct pv_disk));
|
||||
|
||||
log_debug_metadata("Writing %s PV metadata to %s at %" PRIu64 " len %"
|
||||
PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
|
||||
pos, size);
|
||||
|
||||
_xlate_pvd((struct pv_disk *) buf);
|
||||
if (!dev_write(data->dev, pos, size, DEV_IO_FMT1, buf)) {
|
||||
dm_free(buf);
|
||||
return_0;
|
||||
}
|
||||
|
||||
dm_free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* assumes the device has been opened.
|
||||
*/
|
||||
static int __write_all_pvd(const struct format_type *fmt __attribute__((unused)),
|
||||
struct disk_list *data, int write_vg_metadata)
|
||||
{
|
||||
const char *pv_name = dev_name(data->dev);
|
||||
|
||||
if (!_write_pvd(data)) {
|
||||
log_error("Failed to write PV structure onto %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, fmt); */
|
||||
/*
|
||||
* Stop here for orphan PVs or if VG metadata write not requested.
|
||||
*/
|
||||
if ((data->pvd.vg_name[0] == '\0') || !write_vg_metadata) {
|
||||
/* if (!test_mode())
|
||||
vgcache_add(data->pvd.vg_name, NULL, data->dev, fmt); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if (!test_mode())
|
||||
vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev,
|
||||
fmt); */
|
||||
|
||||
if (!_write_vgd(data)) {
|
||||
log_error("Failed to write VG data to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_uuids(data)) {
|
||||
log_error("Failed to write PV uuid list to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_lvs(data)) {
|
||||
log_error("Failed to write LV's to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_extents(data)) {
|
||||
log_error("Failed to write extents to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* opens the device and hands to the above fn.
|
||||
*/
|
||||
static int _write_all_pvd(const struct format_type *fmt, struct disk_list *data, int write_vg_metadata)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!data->dev)
|
||||
return_0;
|
||||
|
||||
if (!dev_open(data->dev))
|
||||
return_0;
|
||||
|
||||
r = __write_all_pvd(fmt, data, write_vg_metadata);
|
||||
|
||||
if (!dev_close(data->dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes all the given pv's to disk. Does very
|
||||
* little sanity checking, so make sure correct
|
||||
* data is passed to here.
|
||||
*/
|
||||
int write_disks(const struct format_type *fmt, struct dm_list *pvs, int write_vg_metadata)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
|
||||
dm_list_iterate_items(dl, pvs) {
|
||||
if (!(_write_all_pvd(fmt, dl, write_vg_metadata)))
|
||||
return_0;
|
||||
|
||||
log_very_verbose("Successfully wrote data to %s",
|
||||
dev_name(dl->dev));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DISK_REP_FORMAT1_H
|
||||
#define DISK_REP_FORMAT1_H
|
||||
|
||||
#include "metadata.h"
|
||||
#include "toolcontext.h"
|
||||
|
||||
#define MAX_PV 256
|
||||
#define MAX_LV 256
|
||||
#define MAX_VG 99
|
||||
|
||||
#define LVM_BLK_MAJOR 58
|
||||
|
||||
#define MAX_PV_SIZE ((uint32_t) -1) /* 2TB in sectors - 1 */
|
||||
#define PE_SIZE_PV_SIZE_REL 5 /* PV size must be at least 5 times PE size */
|
||||
#define MAX_LE_TOTAL 65534 /* 2^16 - 2 */
|
||||
#define MAX_PE_TOTAL ((uint32_t) -2)
|
||||
|
||||
#define UNMAPPED_EXTENT 0
|
||||
|
||||
/* volume group */
|
||||
#define VG_ACTIVE 0x01 /* vg_status */
|
||||
#define VG_EXPORTED 0x02 /* " */
|
||||
#define VG_EXTENDABLE 0x04 /* " */
|
||||
|
||||
#define VG_READ 0x01 /* vg_access */
|
||||
#define VG_WRITE 0x02 /* " */
|
||||
#define VG_CLUSTERED 0x04 /* " */
|
||||
#define VG_SHARED 0x08 /* " */
|
||||
|
||||
/* logical volume */
|
||||
#define LV_ACTIVE 0x01 /* lv_status */
|
||||
#define LV_SPINDOWN 0x02 /* " */
|
||||
#define LV_PERSISTENT_MINOR 0x04 /* " */
|
||||
|
||||
#define LV_READ 0x01 /* lv_access */
|
||||
#define LV_WRITE 0x02 /* " */
|
||||
#define LV_SNAPSHOT 0x04 /* " */
|
||||
#define LV_SNAPSHOT_ORG 0x08 /* " */
|
||||
|
||||
#define LV_BADBLOCK_ON 0x01 /* lv_badblock */
|
||||
|
||||
#define LV_STRICT 0x01 /* lv_allocation */
|
||||
#define LV_CONTIGUOUS 0x02 /* " */
|
||||
|
||||
/* physical volume */
|
||||
#define PV_ACTIVE 0x01 /* pv_status */
|
||||
#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
|
||||
|
||||
#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */
|
||||
#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */
|
||||
|
||||
struct data_area {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pv_disk {
|
||||
int8_t id[2];
|
||||
uint16_t version; /* lvm version */
|
||||
struct data_area pv_on_disk;
|
||||
struct data_area vg_on_disk;
|
||||
struct data_area pv_uuidlist_on_disk;
|
||||
struct data_area lv_on_disk;
|
||||
struct data_area pe_on_disk;
|
||||
int8_t pv_uuid[NAME_LEN];
|
||||
int8_t vg_name[NAME_LEN];
|
||||
int8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* only present on version == 2 pv's */
|
||||
uint32_t pe_start;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct lv_disk {
|
||||
int8_t lv_name[NAME_LEN];
|
||||
int8_t vg_name[NAME_LEN];
|
||||
uint32_t lv_access;
|
||||
uint32_t lv_status;
|
||||
uint32_t lv_open;
|
||||
uint32_t lv_dev;
|
||||
uint32_t lv_number;
|
||||
uint32_t lv_mirror_copies; /* for future use */
|
||||
uint32_t lv_recovery; /* " */
|
||||
uint32_t lv_schedule; /* " */
|
||||
uint32_t lv_size;
|
||||
uint32_t lv_snapshot_minor; /* minor number of original */
|
||||
uint16_t lv_chunk_size; /* chunk size of snapshot */
|
||||
uint16_t dummy;
|
||||
uint32_t lv_allocated_le;
|
||||
uint32_t lv_stripes;
|
||||
uint32_t lv_stripesize;
|
||||
uint32_t lv_badblock; /* for future use */
|
||||
uint32_t lv_allocation;
|
||||
uint32_t lv_io_timeout; /* for future use */
|
||||
uint32_t lv_read_ahead;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct vg_disk {
|
||||
int8_t vg_uuid[ID_LEN]; /* volume group UUID */
|
||||
int8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */
|
||||
uint32_t vg_number; /* volume group number */
|
||||
uint32_t vg_access; /* read/write */
|
||||
uint32_t vg_status; /* active or not */
|
||||
uint32_t lv_max; /* maximum logical volumes */
|
||||
uint32_t lv_cur; /* current logical volumes */
|
||||
uint32_t lv_open; /* open logical volumes */
|
||||
uint32_t pv_max; /* maximum physical volumes */
|
||||
uint32_t pv_cur; /* current physical volumes FU */
|
||||
uint32_t pv_act; /* active physical volumes */
|
||||
uint32_t dummy;
|
||||
uint32_t vgda; /* volume group descriptor arrays FU */
|
||||
uint32_t pe_size; /* physical extent size in sectors */
|
||||
uint32_t pe_total; /* total of physical extents */
|
||||
uint32_t pe_allocated; /* allocated physical extents */
|
||||
uint32_t pvg_total; /* physical volume groups FU */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pe_disk {
|
||||
uint16_t lv_num;
|
||||
uint16_t le_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct uuid_list {
|
||||
struct dm_list list;
|
||||
char uuid[NAME_LEN] __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
struct lvd_list {
|
||||
struct dm_list list;
|
||||
struct lv_disk lvd;
|
||||
};
|
||||
|
||||
struct disk_list {
|
||||
struct dm_list list;
|
||||
struct dm_pool *mem;
|
||||
struct device *dev;
|
||||
|
||||
struct pv_disk pvd __attribute__((aligned(8)));
|
||||
struct vg_disk vgd __attribute__((aligned(8)));
|
||||
struct dm_list uuids __attribute__((aligned(8)));
|
||||
struct dm_list lvds __attribute__((aligned(8)));
|
||||
struct pe_disk *extents __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
/*
|
||||
* Layout constants.
|
||||
*/
|
||||
#define METADATA_ALIGN 4096UL
|
||||
#define LVM1_PE_ALIGN (65536UL >> SECTOR_SHIFT) /* PE alignment */
|
||||
|
||||
#define METADATA_BASE 0UL
|
||||
#define PV_SIZE 1024UL
|
||||
#define VG_SIZE 4096UL
|
||||
|
||||
/*
|
||||
* Functions to calculate layout info.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl);
|
||||
int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
|
||||
uint32_t max_extent_count, uint64_t pe_start);
|
||||
|
||||
/*
|
||||
* Low level io routines which read/write
|
||||
* disk_lists.
|
||||
*/
|
||||
|
||||
struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
|
||||
struct dm_pool *mem, const char *vg_name);
|
||||
|
||||
int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
|
||||
struct dev_filter *filter,
|
||||
struct dm_pool *mem, struct dm_list *results);
|
||||
|
||||
int write_disks(const struct format_type *fmt, struct dm_list *pvds,
|
||||
int write_vg_metadata);
|
||||
|
||||
/*
|
||||
* Functions to translate to between disk and in
|
||||
* core structures.
|
||||
*/
|
||||
int import_pv(const struct format_type *fmt, struct dm_pool *mem,
|
||||
struct device *dev, struct volume_group *vg,
|
||||
struct physical_volume *pv, struct pv_disk *pvd,
|
||||
struct vg_disk *vgd);
|
||||
int export_pv(struct cmd_context *cmd, struct dm_pool *mem,
|
||||
struct volume_group *vg,
|
||||
struct pv_disk *pvd, struct physical_volume *pv);
|
||||
|
||||
int import_vg(struct dm_pool *mem,
|
||||
struct volume_group *vg, struct disk_list *dl);
|
||||
int export_vg(struct vg_disk *vgd, struct volume_group *vg);
|
||||
|
||||
int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
|
||||
struct logical_volume *lv, struct lv_disk *lvd);
|
||||
|
||||
int import_extents(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct dm_list *pvds);
|
||||
int export_extents(struct disk_list *dl, uint32_t lv_num,
|
||||
struct logical_volume *lv, struct physical_volume *pv);
|
||||
|
||||
int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
|
||||
struct volume_group *vg, struct dm_list *pvds);
|
||||
|
||||
int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds);
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *dev_dir);
|
||||
|
||||
int import_snapshots(struct dm_pool *mem, struct volume_group *vg,
|
||||
struct dm_list *pvds);
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg);
|
||||
|
||||
void export_numbers(struct dm_list *pvds, struct volume_group *vg);
|
||||
|
||||
void export_pv_act(struct dm_list *pvds);
|
||||
int munge_pvd(struct device *dev, struct pv_disk *pvd);
|
||||
int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd);
|
||||
|
||||
/* blech */
|
||||
int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter,
|
||||
const char *candidate_vg, int *result);
|
||||
int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
|
||||
const char *vg_name, struct dev_filter *filter);
|
||||
|
||||
int generate_lvm1_system_id(struct cmd_context *cmd, char *s, const char *prefix);
|
||||
|
||||
#endif
|
||||
@@ -1,631 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "disk-rep.h"
|
||||
#include "limits.h"
|
||||
#include "display.h"
|
||||
#include "toolcontext.h"
|
||||
#include "lvm1-label.h"
|
||||
#include "format1.h"
|
||||
#include "segtype.h"
|
||||
#include "pv_alloc.h"
|
||||
|
||||
/* VG consistency checks */
|
||||
static int _check_vgs(struct dm_list *pvs, struct volume_group *vg)
|
||||
{
|
||||
struct dm_list *pvh, *t;
|
||||
struct disk_list *dl = NULL;
|
||||
struct disk_list *first = NULL;
|
||||
|
||||
uint32_t pv_count = 0;
|
||||
uint32_t exported = 0;
|
||||
int first_time = 1;
|
||||
|
||||
/*
|
||||
* If there are exported and unexported PVs, ignore exported ones.
|
||||
* This means an active VG won't be affected if disks are inserted
|
||||
* bearing an exported VG with the same name.
|
||||
*/
|
||||
dm_list_iterate_items(dl, pvs) {
|
||||
if (first_time) {
|
||||
exported = dl->pvd.pv_status & VG_EXPORTED;
|
||||
first_time = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
|
||||
/* Remove exported PVs */
|
||||
dm_list_iterate_safe(pvh, t, pvs) {
|
||||
dl = dm_list_item(pvh, struct disk_list);
|
||||
if (dl->pvd.pv_status & VG_EXPORTED)
|
||||
dm_list_del(pvh);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any PVs with VG structs that differ from the first */
|
||||
dm_list_iterate_safe(pvh, t, pvs) {
|
||||
dl = dm_list_item(pvh, struct disk_list);
|
||||
|
||||
if (!first)
|
||||
first = dl;
|
||||
|
||||
else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
|
||||
log_error("VG data differs between PVs %s and %s",
|
||||
dev_name(first->dev), dev_name(dl->dev));
|
||||
log_debug_metadata("VG data on %s: %s %s %" PRIu32 " %" PRIu32
|
||||
" %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
|
||||
PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
|
||||
" %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
|
||||
PRIu32 " %" PRIu32 " %" PRIu32,
|
||||
dev_name(first->dev), first->vgd.vg_uuid,
|
||||
first->vgd.vg_name_dummy,
|
||||
first->vgd.vg_number, first->vgd.vg_access,
|
||||
first->vgd.vg_status, first->vgd.lv_max,
|
||||
first->vgd.lv_cur, first->vgd.lv_open,
|
||||
first->vgd.pv_max, first->vgd.pv_cur,
|
||||
first->vgd.pv_act, first->vgd.dummy,
|
||||
first->vgd.vgda, first->vgd.pe_size,
|
||||
first->vgd.pe_total, first->vgd.pe_allocated,
|
||||
first->vgd.pvg_total);
|
||||
log_debug_metadata("VG data on %s: %s %s %" PRIu32 " %" PRIu32
|
||||
" %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
|
||||
PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
|
||||
" %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
|
||||
PRIu32 " %" PRIu32 " %" PRIu32,
|
||||
dev_name(dl->dev), dl->vgd.vg_uuid,
|
||||
dl->vgd.vg_name_dummy, dl->vgd.vg_number,
|
||||
dl->vgd.vg_access, dl->vgd.vg_status,
|
||||
dl->vgd.lv_max, dl->vgd.lv_cur,
|
||||
dl->vgd.lv_open, dl->vgd.pv_max,
|
||||
dl->vgd.pv_cur, dl->vgd.pv_act, dl->vgd.dummy,
|
||||
dl->vgd.vgda, dl->vgd.pe_size,
|
||||
dl->vgd.pe_total, dl->vgd.pe_allocated,
|
||||
dl->vgd.pvg_total);
|
||||
dm_list_del(pvh);
|
||||
return 0;
|
||||
}
|
||||
pv_count++;
|
||||
}
|
||||
|
||||
/* On entry to fn, list known to be non-empty */
|
||||
if (pv_count != first->vgd.pv_cur) {
|
||||
log_error("%d PV(s) found for VG %s: expected %d",
|
||||
pv_count, first->pvd.vg_name, first->vgd.pv_cur);
|
||||
vg->status |= PARTIAL_VG;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _fix_partial_vg(struct volume_group *vg, struct dm_list *pvs)
|
||||
{
|
||||
uint32_t extent_count = 0;
|
||||
struct disk_list *dl;
|
||||
struct dm_list *pvh;
|
||||
struct pv_list *pvl;
|
||||
struct lv_list *ll;
|
||||
struct lv_segment *seg;
|
||||
|
||||
/*
|
||||
* FIXME: code should remap missing segments to error segment.
|
||||
* Also current mapping code allocates 1 segment per missing extent.
|
||||
* For now bail out completely - allocated structures are not complete
|
||||
*/
|
||||
dm_list_iterate_items(ll, &vg->lvs)
|
||||
dm_list_iterate_items(seg, &ll->lv->segments) {
|
||||
|
||||
/* area_count is always 1 here, s == 0 */
|
||||
if (seg_type(seg, 0) != AREA_PV)
|
||||
continue;
|
||||
|
||||
if (seg_pv(seg, 0))
|
||||
continue;
|
||||
|
||||
log_error("Partial mode support for missing lvm1 PVs and "
|
||||
"partially available LVs is currently not implemented.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_iterate(pvh, pvs) {
|
||||
dl = dm_list_item(pvh, struct disk_list);
|
||||
extent_count += dl->pvd.pe_total;
|
||||
}
|
||||
|
||||
/* FIXME: move this to one place to pv_manip */
|
||||
if (!(pvl = dm_pool_zalloc(vg->vgmem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = dm_pool_zalloc(vg->vgmem, sizeof(*pvl->pv))))
|
||||
return_0;
|
||||
|
||||
/* Use vg uuid with replaced first chars to "missing" as missing PV UUID */
|
||||
memcpy(&pvl->pv->id.uuid, vg->id.uuid, sizeof(pvl->pv->id.uuid));
|
||||
memcpy(&pvl->pv->id.uuid, "missing", 7);
|
||||
|
||||
if (!(pvl->pv->vg_name = dm_pool_strdup(vg->vgmem, vg->name)))
|
||||
goto_out;
|
||||
memcpy(&pvl->pv->vgid, &vg->id, sizeof(vg->id));
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
dm_list_init(&pvl->pv->tags);
|
||||
dm_list_init(&pvl->pv->segments);
|
||||
|
||||
pvl->pv->pe_size = vg->extent_size;
|
||||
pvl->pv->pe_count = vg->extent_count - extent_count;
|
||||
if (!alloc_pv_segment_whole_pv(vg->vgmem, pvl->pv))
|
||||
goto_out;
|
||||
|
||||
add_pvl_to_vgs(vg, pvl);
|
||||
log_debug_metadata("%s: partial VG, allocated missing PV using %d extents.",
|
||||
vg->name, pvl->pv->pe_count);
|
||||
|
||||
return 1;
|
||||
out:
|
||||
dm_pool_free(vg->vgmem, pvl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct volume_group *_format1_vg_read(struct format_instance *fid,
|
||||
const char *vg_name,
|
||||
struct metadata_area *mda __attribute__((unused)),
|
||||
struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)),
|
||||
unsigned *use_previous_vg __attribute__((unused)),
|
||||
int single_device __attribute__((unused)), unsigned ioflags)
|
||||
{
|
||||
struct volume_group *vg;
|
||||
struct disk_list *dl;
|
||||
DM_LIST_INIT(pvs);
|
||||
|
||||
/* Strip dev_dir if present */
|
||||
if (vg_name)
|
||||
vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
|
||||
|
||||
if (!(vg = alloc_vg("format1_vg_read", fid->fmt->cmd, NULL)))
|
||||
return_NULL;
|
||||
|
||||
if (!read_pvs_in_vg(fid->fmt, vg_name, fid->fmt->cmd->filter,
|
||||
vg->vgmem, &pvs))
|
||||
goto_bad;
|
||||
|
||||
if (dm_list_empty(&pvs))
|
||||
goto_bad;
|
||||
|
||||
if (!_check_vgs(&pvs, vg))
|
||||
goto_bad;
|
||||
|
||||
dl = dm_list_item(pvs.n, struct disk_list);
|
||||
|
||||
if (!import_vg(vg->vgmem, vg, dl))
|
||||
goto_bad;
|
||||
|
||||
if (!import_pvs(fid->fmt, vg->vgmem, vg, &pvs))
|
||||
goto_bad;
|
||||
|
||||
if (!import_lvs(vg->vgmem, vg, &pvs))
|
||||
goto_bad;
|
||||
|
||||
if (!import_extents(fid->fmt->cmd, vg, &pvs))
|
||||
goto_bad;
|
||||
|
||||
/* FIXME: workaround - temporary assignment of fid */
|
||||
vg->fid = fid;
|
||||
if (!import_snapshots(vg->vgmem, vg, &pvs)) {
|
||||
vg->fid = NULL;
|
||||
goto_bad;
|
||||
}
|
||||
vg->fid = NULL;
|
||||
|
||||
/* Fix extents counts by adding missing PV if partial VG */
|
||||
if ((vg->status & PARTIAL_VG) && !_fix_partial_vg(vg, &pvs))
|
||||
goto_bad;
|
||||
|
||||
vg_set_fid(vg, fid);
|
||||
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
release_vg(vg);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct disk_list *_flatten_pv(struct format_instance *fid,
|
||||
struct dm_pool *mem, struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
const char *dev_dir)
|
||||
{
|
||||
struct disk_list *dl = dm_pool_alloc(mem, sizeof(*dl));
|
||||
|
||||
if (!dl)
|
||||
return_NULL;
|
||||
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
|
||||
dm_list_init(&dl->uuids);
|
||||
dm_list_init(&dl->lvds);
|
||||
|
||||
if (!export_pv(fid->fmt->cmd, mem, vg, &dl->pvd, pv) ||
|
||||
!export_vg(&dl->vgd, vg) ||
|
||||
!export_uuids(dl, vg) ||
|
||||
!export_lvs(dl, vg, pv, dev_dir) || !calculate_layout(dl)) {
|
||||
dm_pool_free(mem, dl);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
return dl;
|
||||
}
|
||||
|
||||
static int _flatten_vg(struct format_instance *fid, struct dm_pool *mem,
|
||||
struct volume_group *vg,
|
||||
struct dm_list *pvds, const char *dev_dir,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
struct disk_list *data;
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (!(data = _flatten_pv(fid, mem, vg, pvl->pv, dev_dir)))
|
||||
return_0;
|
||||
|
||||
dm_list_add(pvds, &data->list);
|
||||
}
|
||||
|
||||
export_numbers(pvds, vg);
|
||||
export_pv_act(pvds);
|
||||
|
||||
if (!export_vg_number(fid, pvds, vg->name, filter))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg,
|
||||
struct metadata_area *mda __attribute__((unused)))
|
||||
{
|
||||
struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK);
|
||||
struct dm_list pvds;
|
||||
int r = 0;
|
||||
|
||||
if (!mem)
|
||||
return_0;
|
||||
|
||||
dm_list_init(&pvds);
|
||||
|
||||
r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir,
|
||||
fid->fmt->cmd->filter) &&
|
||||
write_disks(fid->fmt, &pvds, 1));
|
||||
|
||||
lvmcache_update_vg(vg, 0);
|
||||
dm_pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _format1_pv_read(const struct format_type *fmt, const char *pv_name,
|
||||
struct physical_volume *pv, int scan_label_only __attribute__((unused)))
|
||||
{
|
||||
struct dm_pool *mem = dm_pool_create("lvm1 pv_read", 1024);
|
||||
struct disk_list *dl;
|
||||
struct device *dev;
|
||||
int r = 0;
|
||||
|
||||
log_very_verbose("Reading physical volume data %s from disk", pv_name);
|
||||
|
||||
if (!mem)
|
||||
return_0;
|
||||
|
||||
if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
|
||||
goto_out;
|
||||
|
||||
if (!(dl = read_disk(fmt, dev, mem, NULL)))
|
||||
goto_out;
|
||||
|
||||
if (!import_pv(fmt, fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd, &dl->vgd))
|
||||
goto_out;
|
||||
|
||||
pv->fmt = fmt;
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
dm_pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _format1_pv_initialise(const struct format_type * fmt,
|
||||
struct pv_create_args *pva,
|
||||
struct physical_volume * pv)
|
||||
{
|
||||
if (pv->size > MAX_PV_SIZE)
|
||||
pv->size--;
|
||||
if (pv->size > MAX_PV_SIZE) {
|
||||
log_error("Physical volumes cannot be bigger than %s",
|
||||
display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nothing more to do if extent size isn't provided */
|
||||
if (!pva->extent_size)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This works out pe_start and pe_count.
|
||||
*/
|
||||
if (!calculate_extent_count(pv, pva->extent_size, pva->extent_count, pva->pe_start))
|
||||
return_0;
|
||||
|
||||
/* Retain existing extent locations exactly */
|
||||
if (((pva->pe_start || pva->extent_count) && (pva->pe_start != pv->pe_start)) ||
|
||||
(pva->extent_count && (pva->extent_count != pv->pe_count))) {
|
||||
log_error("Metadata would overwrite physical extents");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _format1_pv_setup(const struct format_type *fmt,
|
||||
struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
struct pv_create_args pva = { .id = {{0}},
|
||||
.idp = NULL,
|
||||
.ba_start = 0,
|
||||
.ba_size = 0,
|
||||
.pe_start = 0,
|
||||
.extent_count = 0,
|
||||
.extent_size = vg->extent_size};
|
||||
|
||||
return _format1_pv_initialise(fmt, &pva, pv);
|
||||
}
|
||||
|
||||
static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv)
|
||||
{
|
||||
uint64_t max_size = UINT_MAX;
|
||||
|
||||
if (!*lv->lvid.s)
|
||||
lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
|
||||
|
||||
if (lv->le_count > MAX_LE_TOTAL) {
|
||||
log_error("logical volumes cannot contain more than "
|
||||
"%d extents.", MAX_LE_TOTAL);
|
||||
return 0;
|
||||
}
|
||||
if (lv->size > max_size) {
|
||||
log_error("logical volumes cannot be larger than %s",
|
||||
display_size(fid->fmt->cmd, max_size));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _format1_pv_write(const struct format_type *fmt, struct physical_volume *pv)
|
||||
{
|
||||
struct dm_pool *mem;
|
||||
struct disk_list *dl;
|
||||
struct dm_list pvs;
|
||||
struct lvmcache_info *info;
|
||||
int pe_count, pe_size, pe_start;
|
||||
int r = 1;
|
||||
|
||||
if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
|
||||
pv->vg_name, NULL, 0)))
|
||||
return_0;
|
||||
|
||||
lvmcache_update_pv(info, pv, fmt);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_das(info);
|
||||
lvmcache_del_bas(info);
|
||||
|
||||
dm_list_init(&pvs);
|
||||
|
||||
pe_count = pv->pe_count;
|
||||
pe_size = pv->pe_size;
|
||||
pe_start = pv->pe_start;
|
||||
|
||||
/* Ensure any residual PE structure is gone */
|
||||
pv->pe_size = pv->pe_count = 0;
|
||||
pv->pe_start = LVM1_PE_ALIGN;
|
||||
|
||||
if (!(mem = dm_pool_create("lvm1 pv_write", 1024)))
|
||||
return_0;
|
||||
|
||||
if (!(dl = dm_pool_alloc(mem, sizeof(*dl))))
|
||||
goto_bad;
|
||||
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
dm_list_init(&dl->uuids);
|
||||
dm_list_init(&dl->lvds);
|
||||
|
||||
if (!export_pv(fmt->cmd, mem, NULL, &dl->pvd, pv))
|
||||
goto_bad;
|
||||
|
||||
/* must be set to be able to zero gap after PV structure in
|
||||
dev_write in order to make other disk tools happy */
|
||||
dl->pvd.pv_on_disk.base = METADATA_BASE;
|
||||
dl->pvd.pv_on_disk.size = PV_SIZE;
|
||||
dl->pvd.pe_on_disk.base = LVM1_PE_ALIGN << SECTOR_SHIFT;
|
||||
|
||||
dm_list_add(&pvs, &dl->list);
|
||||
if (!write_disks(fmt, &pvs, 0))
|
||||
goto_bad;
|
||||
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
r = 0;
|
||||
|
||||
out:
|
||||
pv->pe_size = pe_size;
|
||||
pv->pe_count = pe_count;
|
||||
pv->pe_start = pe_start;
|
||||
|
||||
dm_pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _format1_vg_setup(struct format_instance *fid, struct volume_group *vg)
|
||||
{
|
||||
/* just check max_pv and max_lv */
|
||||
if (!vg->max_lv || vg->max_lv >= MAX_LV)
|
||||
vg->max_lv = MAX_LV - 1;
|
||||
|
||||
if (!vg->max_pv || vg->max_pv >= MAX_PV)
|
||||
vg->max_pv = MAX_PV - 1;
|
||||
|
||||
if (!vg_check_new_extent_size(vg->fid->fmt, vg->extent_size))
|
||||
return_0;
|
||||
|
||||
/* Generate lvm1_system_id if not yet set */
|
||||
if (!*vg->lvm1_system_id &&
|
||||
!generate_lvm1_system_id(vg->cmd, vg->lvm1_system_id, ""))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _format1_segtype_supported(struct format_instance *fid __attribute__((unused)),
|
||||
const struct segment_type *segtype)
|
||||
{
|
||||
if (!(segtype->flags & SEG_FORMAT1_SUPPORT))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct metadata_area_ops _metadata_format1_ops = {
|
||||
.vg_read = _format1_vg_read,
|
||||
.vg_write = _format1_vg_write,
|
||||
};
|
||||
|
||||
static struct format_instance *_format1_create_instance(const struct format_type *fmt,
|
||||
const struct format_instance_ctx *fic)
|
||||
{
|
||||
struct format_instance *fid;
|
||||
struct metadata_area *mda;
|
||||
|
||||
if (!(fid = alloc_fid(fmt, fic)))
|
||||
return_NULL;
|
||||
|
||||
/* Define a NULL metadata area */
|
||||
if (!(mda = dm_pool_zalloc(fid->mem, sizeof(*mda)))) {
|
||||
log_error("Unable to allocate metadata area structure "
|
||||
"for lvm1 format");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
mda->ops = &_metadata_format1_ops;
|
||||
mda->metadata_locn = NULL;
|
||||
mda->status = 0;
|
||||
dm_list_add(&fid->metadata_areas_in_use, &mda->list);
|
||||
|
||||
return fid;
|
||||
|
||||
bad:
|
||||
dm_pool_destroy(fid->mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _format1_destroy_instance(struct format_instance *fid)
|
||||
{
|
||||
if (--fid->ref_count <= 1)
|
||||
dm_pool_destroy(fid->mem);
|
||||
}
|
||||
|
||||
static void _format1_destroy(struct format_type *fmt)
|
||||
{
|
||||
if (fmt->orphan_vg)
|
||||
free_orphan_vg(fmt->orphan_vg);
|
||||
|
||||
dm_free(fmt);
|
||||
}
|
||||
|
||||
static struct format_handler _format1_ops = {
|
||||
.pv_read = _format1_pv_read,
|
||||
.pv_initialise = _format1_pv_initialise,
|
||||
.pv_setup = _format1_pv_setup,
|
||||
.pv_write = _format1_pv_write,
|
||||
.lv_setup = _format1_lv_setup,
|
||||
.vg_setup = _format1_vg_setup,
|
||||
.segtype_supported = _format1_segtype_supported,
|
||||
.create_instance = _format1_create_instance,
|
||||
.destroy_instance = _format1_destroy_instance,
|
||||
.destroy = _format1_destroy,
|
||||
};
|
||||
|
||||
#ifdef LVM1_INTERNAL
|
||||
struct format_type *init_lvm1_format(struct cmd_context *cmd)
|
||||
#else /* Shared */
|
||||
struct format_type *init_format(struct cmd_context *cmd);
|
||||
struct format_type *init_format(struct cmd_context *cmd)
|
||||
#endif
|
||||
{
|
||||
struct format_type *fmt = dm_malloc(sizeof(*fmt));
|
||||
struct format_instance_ctx fic;
|
||||
struct format_instance *fid;
|
||||
|
||||
if (!fmt) {
|
||||
log_error("Failed to allocate format1 format type structure.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fmt->cmd = cmd;
|
||||
fmt->ops = &_format1_ops;
|
||||
fmt->name = FMT_LVM1_NAME;
|
||||
fmt->alias = NULL;
|
||||
fmt->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME;
|
||||
fmt->features = FMT_RESTRICTED_LVIDS | FMT_ORPHAN_ALLOCATABLE |
|
||||
FMT_RESTRICTED_READAHEAD | FMT_OBSOLETE |
|
||||
FMT_SYSTEMID_ON_PVS;
|
||||
fmt->private = NULL;
|
||||
|
||||
dm_list_init(&fmt->mda_ops);
|
||||
|
||||
if (!(fmt->labeller = lvm1_labeller_create(fmt))) {
|
||||
log_error("Couldn't create lvm1 label handler.");
|
||||
dm_free(fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(label_register_handler(fmt->labeller))) {
|
||||
log_error("Couldn't register lvm1 label handler.");
|
||||
fmt->labeller->ops->destroy(fmt->labeller);
|
||||
dm_free(fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(fmt->orphan_vg = alloc_vg("format1_orphan", cmd, fmt->orphan_vg_name))) {
|
||||
log_error("Couldn't create lvm1 orphan VG.");
|
||||
dm_free(fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fic.type = FMT_INSTANCE_AUX_MDAS;
|
||||
fic.context.vg_ref.vg_name = fmt->orphan_vg_name;
|
||||
fic.context.vg_ref.vg_id = NULL;
|
||||
|
||||
if (!(fid = _format1_create_instance(fmt, &fic))) {
|
||||
_format1_destroy(fmt);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
vg_set_fid(fmt->orphan_vg, fid);
|
||||
|
||||
log_very_verbose("Initialised format: %s", fmt->name);
|
||||
|
||||
return fmt;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FORMAT1_H
|
||||
#define _LVM_FORMAT1_H
|
||||
|
||||
#include "metadata.h"
|
||||
#include "lvmcache.h"
|
||||
|
||||
#define FMT_LVM1_NAME "lvm1"
|
||||
#define FMT_LVM1_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_LVM1_NAME)
|
||||
|
||||
#ifdef LVM1_INTERNAL
|
||||
struct format_type *init_lvm1_format(struct cmd_context *cmd);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,680 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Translates between disk and in-core formats.
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "disk-rep.h"
|
||||
#include "lvm-string.h"
|
||||
#include "toolcontext.h"
|
||||
#include "segtype.h"
|
||||
#include "pv_alloc.h"
|
||||
#include "display.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static int _check_vg_name(const char *name)
|
||||
{
|
||||
return strlen(name) < NAME_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts the last part of a path.
|
||||
*/
|
||||
static char *_create_lv_name(struct dm_pool *mem, const char *full_name)
|
||||
{
|
||||
const char *ptr = strrchr(full_name, '/');
|
||||
|
||||
if (!ptr)
|
||||
ptr = full_name;
|
||||
else
|
||||
ptr++;
|
||||
|
||||
return dm_pool_strdup(mem, ptr);
|
||||
}
|
||||
|
||||
int import_pv(const struct format_type *fmt, struct dm_pool *mem,
|
||||
struct device *dev, struct volume_group *vg,
|
||||
struct physical_volume *pv, struct pv_disk *pvd,
|
||||
struct vg_disk *vgd)
|
||||
{
|
||||
uint64_t size;
|
||||
|
||||
memset(pv, 0, sizeof(*pv));
|
||||
memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
|
||||
|
||||
pv->dev = dev;
|
||||
if (!*pvd->vg_name)
|
||||
pv->vg_name = fmt->orphan_vg_name;
|
||||
else if (!(pv->vg_name = dm_pool_strdup(mem, (char *)pvd->vg_name))) {
|
||||
log_error("Volume Group name allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&pv->vgid, vgd->vg_uuid, sizeof(vg->id));
|
||||
|
||||
/* Store system_id from first PV if PV belongs to a VG */
|
||||
if (vg && !*vg->lvm1_system_id)
|
||||
strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN);
|
||||
|
||||
if (vg &&
|
||||
strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id)))
|
||||
log_very_verbose("System ID %s on %s differs from %s for "
|
||||
"volume group", pvd->system_id,
|
||||
pv_dev_name(pv), vg->lvm1_system_id);
|
||||
|
||||
/*
|
||||
* If exported, we still need to flag in pv->status too because
|
||||
* we don't always have a struct volume_group when we need this.
|
||||
*/
|
||||
if (pvd->pv_status & VG_EXPORTED)
|
||||
pv->status |= EXPORTED_VG;
|
||||
|
||||
if (pvd->pv_allocatable)
|
||||
pv->status |= ALLOCATABLE_PV;
|
||||
|
||||
pv->size = pvd->pv_size;
|
||||
pv->pe_size = pvd->pe_size;
|
||||
pv->pe_start = pvd->pe_start;
|
||||
pv->pe_count = pvd->pe_total;
|
||||
pv->pe_alloc_count = 0;
|
||||
pv->pe_align = 0;
|
||||
pv->is_labelled = 0; /* format1 PVs have no label */
|
||||
pv->label_sector = 0;
|
||||
|
||||
/* Fix up pv size if missing or impossibly large */
|
||||
if (!pv->size || pv->size > (1ULL << 62)) {
|
||||
if (!dev_get_size(dev, &pv->size)) {
|
||||
log_error("%s: Couldn't get size.", pv_dev_name(pv));
|
||||
return 0;
|
||||
}
|
||||
log_verbose("Fixing up missing format1 size (%s) "
|
||||
"for PV %s", display_size(fmt->cmd, pv->size),
|
||||
pv_dev_name(pv));
|
||||
if (vg) {
|
||||
size = pv->pe_count * (uint64_t) vg->extent_size +
|
||||
pv->pe_start;
|
||||
if (size > pv->size)
|
||||
log_warn("WARNING: Physical Volume %s is too "
|
||||
"large for underlying device",
|
||||
pv_dev_name(pv));
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_init(&pv->tags);
|
||||
dm_list_init(&pv->segments);
|
||||
|
||||
if (!alloc_pv_segment_whole_pv(mem, pv))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int generate_lvm1_system_id(struct cmd_context *cmd, char *s, const char *prefix)
|
||||
{
|
||||
|
||||
if (dm_snprintf(s, NAME_LEN, "%s%s" FMTu64,
|
||||
prefix, cmd->hostname, (uint64_t)time(NULL)) < 0) {
|
||||
log_error("Generated LVM1 format system_id too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused)),
|
||||
struct volume_group *vg,
|
||||
struct pv_disk *pvd, struct physical_volume *pv)
|
||||
{
|
||||
memset(pvd, 0, sizeof(*pvd));
|
||||
|
||||
pvd->id[0] = 'H';
|
||||
pvd->id[1] = 'M';
|
||||
pvd->version = 1;
|
||||
|
||||
memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
|
||||
|
||||
if (pv->vg_name && !is_orphan(pv) && !(pv->status & UNLABELLED_PV)) {
|
||||
if (!_check_vg_name(pv->vg_name))
|
||||
return_0;
|
||||
strncpy((char *)pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
|
||||
}
|
||||
|
||||
/* Preserve existing system_id if it exists */
|
||||
if (vg && vg->lvm1_system_id && *vg->lvm1_system_id)
|
||||
strncpy((char *)pvd->system_id, vg->lvm1_system_id, sizeof(pvd->system_id));
|
||||
else if (vg && vg->system_id && *vg->system_id)
|
||||
strncpy((char *)pvd->system_id, vg->system_id, sizeof(pvd->system_id));
|
||||
|
||||
/* Is VG already exported or being exported? */
|
||||
if (vg && vg_is_exported(vg)) {
|
||||
/* Does system_id need setting? */
|
||||
if (!vg->lvm1_system_id || !*vg->lvm1_system_id ||
|
||||
strncmp(vg->lvm1_system_id, EXPORTED_TAG,
|
||||
sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG))
|
||||
return_0;
|
||||
}
|
||||
if (strlen((char *)pvd->vg_name) + sizeof(EXPORTED_TAG) >
|
||||
sizeof(pvd->vg_name)) {
|
||||
log_error("Volume group name %s too long to export",
|
||||
pvd->vg_name);
|
||||
return 0;
|
||||
}
|
||||
strcat((char *)pvd->vg_name, EXPORTED_TAG);
|
||||
}
|
||||
|
||||
/* Is VG being imported? */
|
||||
if (vg && !vg_is_exported(vg) && vg->lvm1_system_id && *vg->lvm1_system_id &&
|
||||
!strncmp(vg->lvm1_system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, IMPORTED_TAG))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* Generate system_id if PV is in VG */
|
||||
if (!pvd->system_id[0])
|
||||
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, ""))
|
||||
return_0;
|
||||
|
||||
/* Update internal system_id if we changed it */
|
||||
if (vg && vg->lvm1_system_id &&
|
||||
(!*vg->lvm1_system_id ||
|
||||
strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id))))
|
||||
strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN);
|
||||
|
||||
//pvd->pv_major = MAJOR(pv->dev);
|
||||
|
||||
if (pv->status & ALLOCATABLE_PV)
|
||||
pvd->pv_allocatable = PV_ALLOCATABLE;
|
||||
|
||||
pvd->pv_size = pv->size;
|
||||
pvd->lv_cur = 0; /* this is set when exporting the lv list */
|
||||
if (vg)
|
||||
pvd->pe_size = vg->extent_size;
|
||||
else
|
||||
pvd->pe_size = pv->pe_size;
|
||||
pvd->pe_total = pv->pe_count;
|
||||
pvd->pe_allocated = pv->pe_alloc_count;
|
||||
pvd->pe_start = pv->pe_start;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_vg(struct dm_pool *mem,
|
||||
struct volume_group *vg, struct disk_list *dl)
|
||||
{
|
||||
struct vg_disk *vgd = &dl->vgd;
|
||||
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
|
||||
|
||||
if (!_check_vg_name((char *)dl->pvd.vg_name))
|
||||
return_0;
|
||||
|
||||
if (!(vg->name = dm_pool_strdup(mem, (char *)dl->pvd.vg_name)))
|
||||
return_0;
|
||||
|
||||
if (!(vg->lvm1_system_id = dm_pool_zalloc(mem, NAME_LEN + 1)))
|
||||
return_0;
|
||||
|
||||
if (vgd->vg_status & VG_EXPORTED)
|
||||
vg->status |= EXPORTED_VG;
|
||||
|
||||
if (vgd->vg_status & VG_EXTENDABLE)
|
||||
vg->status |= RESIZEABLE_VG;
|
||||
|
||||
if (vgd->vg_access & VG_READ)
|
||||
vg->status |= LVM_READ;
|
||||
|
||||
if (vgd->vg_access & VG_WRITE)
|
||||
vg->status |= LVM_WRITE;
|
||||
|
||||
if (vgd->vg_access & VG_CLUSTERED)
|
||||
vg->status |= CLUSTERED;
|
||||
|
||||
if (vgd->vg_access & VG_SHARED)
|
||||
vg->status |= SHARED;
|
||||
|
||||
vg->extent_size = vgd->pe_size;
|
||||
vg->extent_count = vgd->pe_total;
|
||||
vg->free_count = vgd->pe_total;
|
||||
vg->max_lv = vgd->lv_max;
|
||||
vg->max_pv = vgd->pv_max;
|
||||
vg->alloc = ALLOC_NORMAL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_vg(struct vg_disk *vgd, struct volume_group *vg)
|
||||
{
|
||||
memset(vgd, 0, sizeof(*vgd));
|
||||
memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
|
||||
|
||||
if (vg->status & LVM_READ)
|
||||
vgd->vg_access |= VG_READ;
|
||||
|
||||
if (vg->status & LVM_WRITE)
|
||||
vgd->vg_access |= VG_WRITE;
|
||||
|
||||
if (vg_is_clustered(vg))
|
||||
vgd->vg_access |= VG_CLUSTERED;
|
||||
|
||||
if (vg->status & SHARED)
|
||||
vgd->vg_access |= VG_SHARED;
|
||||
|
||||
if (vg_is_exported(vg))
|
||||
vgd->vg_status |= VG_EXPORTED;
|
||||
|
||||
if (vg_is_resizeable(vg))
|
||||
vgd->vg_status |= VG_EXTENDABLE;
|
||||
|
||||
vgd->lv_max = vg->max_lv;
|
||||
vgd->lv_cur = vg_visible_lvs(vg) + snapshot_count(vg);
|
||||
|
||||
vgd->pv_max = vg->max_pv;
|
||||
vgd->pv_cur = vg->pv_count;
|
||||
|
||||
vgd->pe_size = vg->extent_size;
|
||||
vgd->pe_total = vg->extent_count;
|
||||
vgd->pe_allocated = vg->extent_count - vg->free_count;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
|
||||
struct logical_volume *lv, struct lv_disk *lvd)
|
||||
{
|
||||
if (!(lv->name = _create_lv_name(mem, (char *)lvd->lv_name)))
|
||||
return_0;
|
||||
|
||||
lv->status |= VISIBLE_LV;
|
||||
|
||||
if (lvd->lv_status & LV_SPINDOWN)
|
||||
lv->status |= SPINDOWN_LV;
|
||||
|
||||
if (lvd->lv_status & LV_PERSISTENT_MINOR) {
|
||||
lv->status |= FIXED_MINOR;
|
||||
lv->minor = MINOR(lvd->lv_dev);
|
||||
lv->major = MAJOR(lvd->lv_dev);
|
||||
} else {
|
||||
lv->major = -1;
|
||||
lv->minor = -1;
|
||||
}
|
||||
|
||||
if (lvd->lv_access & LV_READ)
|
||||
lv->status |= LVM_READ;
|
||||
|
||||
if (lvd->lv_access & LV_WRITE)
|
||||
lv->status |= LVM_WRITE;
|
||||
|
||||
if (lvd->lv_badblock)
|
||||
lv->status |= BADBLOCK_ON;
|
||||
|
||||
/* Drop the unused LV_STRICT here */
|
||||
if (lvd->lv_allocation & LV_CONTIGUOUS)
|
||||
lv->alloc = ALLOC_CONTIGUOUS;
|
||||
else
|
||||
lv->alloc = ALLOC_NORMAL;
|
||||
|
||||
if (!lvd->lv_read_ahead)
|
||||
lv->read_ahead = cmd->default_settings.read_ahead;
|
||||
else
|
||||
lv->read_ahead = lvd->lv_read_ahead;
|
||||
|
||||
lv->size = lvd->lv_size;
|
||||
lv->le_count = lvd->lv_allocated_le;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _export_lv(struct lv_disk *lvd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *dev_dir)
|
||||
{
|
||||
memset(lvd, 0, sizeof(*lvd));
|
||||
snprintf((char *)lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s",
|
||||
dev_dir, vg->name, lv->name);
|
||||
|
||||
(void) dm_strncpy((char *)lvd->vg_name, vg->name, sizeof(lvd->vg_name));
|
||||
|
||||
if (lv->status & LVM_READ)
|
||||
lvd->lv_access |= LV_READ;
|
||||
|
||||
if (lv->status & LVM_WRITE)
|
||||
lvd->lv_access |= LV_WRITE;
|
||||
|
||||
if (lv->status & SPINDOWN_LV)
|
||||
lvd->lv_status |= LV_SPINDOWN;
|
||||
|
||||
if (lv->status & FIXED_MINOR) {
|
||||
lvd->lv_status |= LV_PERSISTENT_MINOR;
|
||||
lvd->lv_dev = MKDEV(lv->major, lv->minor);
|
||||
} else {
|
||||
lvd->lv_dev = MKDEV(LVM_BLK_MAJOR, lvnum_from_lvid(&lv->lvid));
|
||||
}
|
||||
|
||||
if (lv->read_ahead == DM_READ_AHEAD_AUTO ||
|
||||
lv->read_ahead == DM_READ_AHEAD_NONE)
|
||||
lvd->lv_read_ahead = 0;
|
||||
else
|
||||
lvd->lv_read_ahead = lv->read_ahead;
|
||||
|
||||
lvd->lv_stripes =
|
||||
dm_list_item(lv->segments.n, struct lv_segment)->area_count;
|
||||
lvd->lv_stripesize =
|
||||
dm_list_item(lv->segments.n, struct lv_segment)->stripe_size;
|
||||
|
||||
lvd->lv_size = lv->size;
|
||||
lvd->lv_allocated_le = lv->le_count;
|
||||
|
||||
if (lv->status & BADBLOCK_ON)
|
||||
lvd->lv_badblock = LV_BADBLOCK_ON;
|
||||
|
||||
if (lv->alloc == ALLOC_CONTIGUOUS)
|
||||
lvd->lv_allocation |= LV_CONTIGUOUS;
|
||||
}
|
||||
|
||||
int export_extents(struct disk_list *dl, uint32_t lv_num,
|
||||
struct logical_volume *lv, struct physical_volume *pv)
|
||||
{
|
||||
struct pe_disk *ped;
|
||||
struct lv_segment *seg;
|
||||
uint32_t pe, s;
|
||||
|
||||
dm_list_iterate_items(seg, &lv->segments) {
|
||||
for (s = 0; s < seg->area_count; s++) {
|
||||
if (!(seg->segtype->flags & SEG_FORMAT1_SUPPORT)) {
|
||||
log_error("Segment type %s in LV %s: "
|
||||
"unsupported by format1",
|
||||
lvseg_name(seg), lv->name);
|
||||
return 0;
|
||||
}
|
||||
if (seg_type(seg, s) != AREA_PV) {
|
||||
log_error("Non-PV stripe found in LV %s: "
|
||||
"unsupported by format1", lv->name);
|
||||
return 0;
|
||||
}
|
||||
if (seg_pv(seg, s) != pv)
|
||||
continue; /* not our pv */
|
||||
|
||||
for (pe = 0; pe < (seg->len / seg->area_count); pe++) {
|
||||
ped = &dl->extents[pe + seg_pe(seg, s)];
|
||||
ped->lv_num = lv_num;
|
||||
ped->le_num = (seg->le / seg->area_count) + pe +
|
||||
s * (lv->le_count / seg->area_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
|
||||
struct volume_group *vg, struct dm_list *pvds)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
struct pv_list *pvl;
|
||||
|
||||
vg->pv_count = 0;
|
||||
dm_list_iterate_items(dl, pvds) {
|
||||
if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = dm_pool_alloc(mem, sizeof(*pvl->pv))))
|
||||
return_0;
|
||||
|
||||
if (!import_pv(fmt, mem, dl->dev, vg, pvl->pv, &dl->pvd, &dl->vgd))
|
||||
return_0;
|
||||
|
||||
pvl->pv->fmt = fmt;
|
||||
add_pvl_to_vgs(vg, pvl);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct logical_volume *_add_lv(struct dm_pool *mem,
|
||||
struct volume_group *vg,
|
||||
struct lv_disk *lvd)
|
||||
{
|
||||
struct logical_volume *lv;
|
||||
|
||||
if (!(lv = alloc_lv(mem)))
|
||||
return_NULL;
|
||||
|
||||
lvid_from_lvnum(&lv->lvid, &vg->id, lvd->lv_number);
|
||||
|
||||
if (!import_lv(vg->cmd, mem, lv, lvd))
|
||||
goto_bad;
|
||||
|
||||
if (!link_lv_to_vg(vg, lv))
|
||||
goto_bad;
|
||||
|
||||
return lv;
|
||||
bad:
|
||||
dm_pool_free(mem, lv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
struct lvd_list *ll;
|
||||
struct lv_disk *lvd;
|
||||
|
||||
dm_list_iterate_items(dl, pvds) {
|
||||
dm_list_iterate_items(ll, &dl->lvds) {
|
||||
lvd = &ll->lvd;
|
||||
|
||||
if (!find_lv(vg, (char *)lvd->lv_name) &&
|
||||
!_add_lv(mem, vg, lvd))
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: tidy */
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *dev_dir)
|
||||
{
|
||||
int r = 0;
|
||||
struct lv_list *ll;
|
||||
struct lvd_list *lvdl;
|
||||
size_t len;
|
||||
uint32_t lv_num;
|
||||
struct dm_hash_table *lvd_hash;
|
||||
|
||||
if (!_check_vg_name(vg->name))
|
||||
return_0;
|
||||
|
||||
if (!(lvd_hash = dm_hash_create(32)))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* setup the pv's extents array
|
||||
*/
|
||||
len = sizeof(struct pe_disk) * dl->pvd.pe_total;
|
||||
if (!(dl->extents = dm_pool_zalloc(dl->mem, len)))
|
||||
goto_out;
|
||||
|
||||
dm_list_iterate_items(ll, &vg->lvs) {
|
||||
if (lv_is_snapshot(ll->lv))
|
||||
continue;
|
||||
|
||||
if (!(lvdl = dm_pool_alloc(dl->mem, sizeof(*lvdl))))
|
||||
goto_out;
|
||||
|
||||
_export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
|
||||
|
||||
lv_num = lvnum_from_lvid(&ll->lv->lvid);
|
||||
lvdl->lvd.lv_number = lv_num;
|
||||
|
||||
if (!dm_hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd))
|
||||
goto_out;
|
||||
|
||||
if (!export_extents(dl, lv_num + 1, ll->lv, pv))
|
||||
goto_out;
|
||||
|
||||
if (lv_is_origin(ll->lv))
|
||||
lvdl->lvd.lv_access |= LV_SNAPSHOT_ORG;
|
||||
|
||||
if (lv_is_cow(ll->lv)) {
|
||||
lvdl->lvd.lv_access |= LV_SNAPSHOT;
|
||||
lvdl->lvd.lv_chunk_size = ll->lv->snapshot->chunk_size;
|
||||
lvdl->lvd.lv_snapshot_minor =
|
||||
lvnum_from_lvid(&ll->lv->snapshot->origin->lvid);
|
||||
}
|
||||
|
||||
dm_list_add(&dl->lvds, &lvdl->list);
|
||||
dl->pvd.lv_cur++;
|
||||
}
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
dm_hash_destroy(lvd_hash);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: More inefficient code.
|
||||
*/
|
||||
int import_snapshots(struct dm_pool *mem __attribute__((unused)), struct volume_group *vg,
|
||||
struct dm_list *pvds)
|
||||
{
|
||||
struct logical_volume *lvs[MAX_LV] = { 0 };
|
||||
struct disk_list *dl;
|
||||
struct lvd_list *ll;
|
||||
struct lv_disk *lvd;
|
||||
int lvnum;
|
||||
struct logical_volume *org, *cow;
|
||||
|
||||
/* build an index of lv numbers */
|
||||
dm_list_iterate_items(dl, pvds) {
|
||||
dm_list_iterate_items(ll, &dl->lvds) {
|
||||
lvd = &ll->lvd;
|
||||
|
||||
lvnum = lvd->lv_number;
|
||||
|
||||
if (lvnum >= MAX_LV) {
|
||||
log_error("Logical volume number "
|
||||
"out of bounds.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lvs[lvnum] &&
|
||||
!(lvs[lvnum] = find_lv(vg, (char *)lvd->lv_name))) {
|
||||
log_error("Couldn't find logical volume '%s'.",
|
||||
lvd->lv_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now iterate through yet again adding the snapshots.
|
||||
*/
|
||||
dm_list_iterate_items(dl, pvds) {
|
||||
dm_list_iterate_items(ll, &dl->lvds) {
|
||||
lvd = &ll->lvd;
|
||||
|
||||
if (!(lvd->lv_access & LV_SNAPSHOT))
|
||||
continue;
|
||||
|
||||
lvnum = lvd->lv_number;
|
||||
cow = lvs[lvnum];
|
||||
if (!(org = lvs[lvd->lv_snapshot_minor])) {
|
||||
log_error("Couldn't find origin logical volume "
|
||||
"for snapshot '%s'.", lvd->lv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we may have already added this snapshot */
|
||||
if (lv_is_cow(cow))
|
||||
continue;
|
||||
|
||||
/* insert the snapshot */
|
||||
if (!vg_add_snapshot(org, cow, NULL,
|
||||
org->le_count,
|
||||
lvd->lv_chunk_size)) {
|
||||
log_error("Couldn't add snapshot.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg)
|
||||
{
|
||||
struct uuid_list *ul;
|
||||
struct pv_list *pvl;
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (!(ul = dm_pool_alloc(dl->mem, sizeof(*ul))))
|
||||
return_0;
|
||||
|
||||
memset(ul->uuid, 0, sizeof(ul->uuid));
|
||||
memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
|
||||
|
||||
dm_list_add(&dl->uuids, &ul->list);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This calculates the nasty pv_number field
|
||||
* used by LVM1.
|
||||
*/
|
||||
void export_numbers(struct dm_list *pvds, struct volume_group *vg __attribute__((unused)))
|
||||
{
|
||||
struct disk_list *dl;
|
||||
int pv_num = 1;
|
||||
|
||||
dm_list_iterate_items(dl, pvds)
|
||||
dl->pvd.pv_number = pv_num++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate vg_disk->pv_act.
|
||||
*/
|
||||
void export_pv_act(struct dm_list *pvds)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
int act = 0;
|
||||
|
||||
dm_list_iterate_items(dl, pvds)
|
||||
if (dl->pvd.pv_status & PV_ACTIVE)
|
||||
act++;
|
||||
|
||||
dm_list_iterate_items(dl, pvds)
|
||||
dl->vgd.pv_act = act;
|
||||
}
|
||||
|
||||
int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
|
||||
const char *vg_name, struct dev_filter *filter)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
int vg_num;
|
||||
|
||||
if (!get_free_vg_number(fid, filter, vg_name, &vg_num))
|
||||
return_0;
|
||||
|
||||
dm_list_iterate_items(dl, pvds)
|
||||
dl->vgd.vg_number = vg_num;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "metadata.h"
|
||||
#include "disk-rep.h"
|
||||
#include "lv_alloc.h"
|
||||
#include "display.h"
|
||||
#include "segtype.h"
|
||||
|
||||
/*
|
||||
* After much thought I have decided it is easier,
|
||||
* and probably no less efficient, to convert the
|
||||
* pe->le map to a full le->pe map, and then
|
||||
* process this to get the segments form that
|
||||
* we're after. Any code which goes directly from
|
||||
* the pe->le map to segments would be gladly
|
||||
* accepted, if it is less complicated than this
|
||||
* file.
|
||||
*/
|
||||
struct pe_specifier {
|
||||
struct physical_volume *pv;
|
||||
uint32_t pe;
|
||||
};
|
||||
|
||||
struct lv_map {
|
||||
struct logical_volume *lv;
|
||||
uint32_t stripes;
|
||||
uint32_t stripe_size;
|
||||
struct pe_specifier *map;
|
||||
};
|
||||
|
||||
static struct dm_hash_table *_create_lv_maps(struct dm_pool *mem,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
struct dm_hash_table *maps = dm_hash_create(32);
|
||||
struct lv_list *ll;
|
||||
struct lv_map *lvm;
|
||||
|
||||
if (!maps) {
|
||||
log_error("Unable to create hash table for holding "
|
||||
"extent maps.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(ll, &vg->lvs) {
|
||||
if (lv_is_snapshot(ll->lv))
|
||||
continue;
|
||||
|
||||
if (!(lvm = dm_pool_alloc(mem, sizeof(*lvm))))
|
||||
goto_bad;
|
||||
|
||||
lvm->lv = ll->lv;
|
||||
/*
|
||||
* Alloc 1 extra element, so the loop in _area_length() and
|
||||
* _check_stripe() finds the last map member as noncontinuous.
|
||||
*/
|
||||
if (!(lvm->map = dm_pool_zalloc(mem, sizeof(*lvm->map)
|
||||
* (ll->lv->le_count + 1))))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_hash_insert(maps, ll->lv->name, lvm))
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
return maps;
|
||||
|
||||
bad:
|
||||
dm_hash_destroy(maps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _fill_lv_array(struct lv_map **lvs,
|
||||
struct dm_hash_table *maps, struct disk_list *dl)
|
||||
{
|
||||
struct lvd_list *ll;
|
||||
struct lv_map *lvm;
|
||||
|
||||
memset(lvs, 0, sizeof(*lvs) * MAX_LV);
|
||||
|
||||
dm_list_iterate_items(ll, &dl->lvds) {
|
||||
if (!(lvm = dm_hash_lookup(maps, strrchr((char *)ll->lvd.lv_name, '/')
|
||||
+ 1))) {
|
||||
log_error("Physical volume (%s) contains an "
|
||||
"unknown logical volume (%s).",
|
||||
dev_name(dl->dev), ll->lvd.lv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvm->stripes = ll->lvd.lv_stripes;
|
||||
lvm->stripe_size = ll->lvd.lv_stripesize;
|
||||
|
||||
lvs[ll->lvd.lv_number] = lvm;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _fill_maps(struct dm_hash_table *maps, struct volume_group *vg,
|
||||
struct dm_list *pvds)
|
||||
{
|
||||
struct disk_list *dl;
|
||||
struct physical_volume *pv;
|
||||
struct lv_map *lvms[MAX_LV], *lvm;
|
||||
struct pe_disk *e;
|
||||
uint32_t i, lv_num, le;
|
||||
|
||||
dm_list_iterate_items(dl, pvds) {
|
||||
if (!(pv = find_pv(vg, dl->dev))) {
|
||||
log_error("PV %s not found.", dl->dev->pvid);
|
||||
return 0;
|
||||
}
|
||||
e = dl->extents;
|
||||
|
||||
/* build an array of lv's for this pv */
|
||||
if (!_fill_lv_array(lvms, maps, dl))
|
||||
return_0;
|
||||
|
||||
for (i = 0; i < dl->pvd.pe_total; i++) {
|
||||
lv_num = e[i].lv_num;
|
||||
|
||||
if (lv_num == UNMAPPED_EXTENT)
|
||||
continue;
|
||||
|
||||
lv_num--;
|
||||
lvm = lvms[lv_num];
|
||||
|
||||
if (!lvm) {
|
||||
log_error("Invalid LV in extent map "
|
||||
"(PV %s, PE %" PRIu32
|
||||
", LV %" PRIu32
|
||||
", LE %" PRIu32 ")",
|
||||
dev_name(pv->dev), i,
|
||||
lv_num, e[i].le_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
le = e[i].le_num;
|
||||
|
||||
if (le >= lvm->lv->le_count) {
|
||||
log_error("logical extent number "
|
||||
"out of bounds");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lvm->map[le].pv) {
|
||||
log_error("logical extent (%u) "
|
||||
"already mapped.", le);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvm->map[le].pv = pv;
|
||||
lvm->map[le].pe = i;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_single_map(struct lv_map *lvm)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lvm->lv->le_count; i++) {
|
||||
if (!lvm->map[i].pv) {
|
||||
log_error("Logical volume (%s) contains an incomplete "
|
||||
"mapping table.", lvm->lv->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_maps_are_complete(struct dm_hash_table *maps)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
struct lv_map *lvm;
|
||||
|
||||
for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
|
||||
lvm = (struct lv_map *) dm_hash_get_data(maps, n);
|
||||
|
||||
if (!_check_single_map(lvm))
|
||||
return_0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t _area_length(struct lv_map *lvm, uint32_t le)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
|
||||
do
|
||||
len++;
|
||||
while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
|
||||
(lvm->map[le].pv &&
|
||||
lvm->map[le + len].pe == lvm->map[le].pe + len));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t le = 0, len;
|
||||
struct lv_segment *seg;
|
||||
struct segment_type *segtype;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
|
||||
return_0;
|
||||
|
||||
while (le < lvm->lv->le_count) {
|
||||
len = _area_length(lvm, le);
|
||||
|
||||
if (!(seg = alloc_lv_segment(segtype, lvm->lv, le, len, 0, 0, 0,
|
||||
NULL, 1, len, 0, 0, 0, 0, NULL))) {
|
||||
log_error("Failed to allocate linear segment.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv,
|
||||
lvm->map[le].pe))
|
||||
return_0;
|
||||
|
||||
dm_list_add(&lvm->lv->segments, &seg->list);
|
||||
|
||||
le += seg->len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
|
||||
uint32_t area_len, uint32_t base_le,
|
||||
uint32_t total_area_len)
|
||||
{
|
||||
uint32_t st;
|
||||
|
||||
/*
|
||||
* Is the next physical extent in every stripe adjacent to the last?
|
||||
*/
|
||||
for (st = 0; st < area_count; st++)
|
||||
if ((lvm->map[base_le + st * total_area_len + area_len].pv !=
|
||||
lvm->map[base_le + st * total_area_len].pv) ||
|
||||
(lvm->map[base_le + st * total_area_len].pv &&
|
||||
lvm->map[base_le + st * total_area_len + area_len].pe !=
|
||||
lvm->map[base_le + st * total_area_len].pe + area_len))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t st, first_area_le = 0, total_area_len;
|
||||
uint32_t area_len;
|
||||
struct lv_segment *seg;
|
||||
struct segment_type *segtype;
|
||||
|
||||
/*
|
||||
* Work out overall striped length
|
||||
*/
|
||||
if (lvm->lv->le_count % lvm->stripes) {
|
||||
log_error("Number of stripes (%u) incompatible "
|
||||
"with logical extent count (%u) for %s",
|
||||
lvm->stripes, lvm->lv->le_count, lvm->lv->name);
|
||||
}
|
||||
|
||||
total_area_len = lvm->lv->le_count / lvm->stripes;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
|
||||
return_0;
|
||||
|
||||
while (first_area_le < total_area_len) {
|
||||
area_len = 1;
|
||||
|
||||
/*
|
||||
* Find how many extents are contiguous in all stripes
|
||||
* and so can form part of this segment
|
||||
*/
|
||||
while (_check_stripe(lvm, lvm->stripes,
|
||||
area_len, first_area_le, total_area_len))
|
||||
area_len++;
|
||||
|
||||
if (!(seg = alloc_lv_segment(segtype, lvm->lv,
|
||||
lvm->stripes * first_area_le,
|
||||
lvm->stripes * area_len, 0,
|
||||
0, lvm->stripe_size, NULL,
|
||||
lvm->stripes,
|
||||
area_len, 0, 0, 0, 0, NULL))) {
|
||||
log_error("Failed to allocate striped segment.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up start positions of each stripe in this segment
|
||||
*/
|
||||
for (st = 0; st < seg->area_count; st++)
|
||||
if (!set_lv_segment_area_pv(seg, st,
|
||||
lvm->map[first_area_le + st * total_area_len].pv,
|
||||
lvm->map[first_area_le + st * total_area_len].pe))
|
||||
return_0;
|
||||
|
||||
dm_list_add(&lvm->lv->segments, &seg->list);
|
||||
|
||||
first_area_le += area_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _build_segments(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
{
|
||||
return (lvm->stripes > 1 ? _read_stripes(cmd, lvm) :
|
||||
_read_linear(cmd, lvm));
|
||||
}
|
||||
|
||||
static int _build_all_segments(struct cmd_context *cmd, struct dm_hash_table *maps)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
struct lv_map *lvm;
|
||||
|
||||
for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
|
||||
lvm = (struct lv_map *) dm_hash_get_data(maps, n);
|
||||
if (!_build_segments(cmd, lvm))
|
||||
return_0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_extents(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct dm_list *pvds)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_pool *scratch = dm_pool_create("lvm1 import_extents", 10 * 1024);
|
||||
struct dm_hash_table *maps;
|
||||
|
||||
if (!scratch)
|
||||
return_0;
|
||||
|
||||
if (!(maps = _create_lv_maps(scratch, vg))) {
|
||||
log_error("Couldn't allocate logical volume maps.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_fill_maps(maps, vg, pvds)) {
|
||||
log_error("Couldn't fill logical volume maps.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG))
|
||||
goto_out;
|
||||
|
||||
if (!_build_all_segments(cmd, maps)) {
|
||||
log_error("Couldn't build extent segments.");
|
||||
goto out;
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (maps)
|
||||
dm_hash_destroy(maps);
|
||||
dm_pool_destroy(scratch);
|
||||
return r;
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "disk-rep.h"
|
||||
|
||||
/*
|
||||
* Only works with powers of 2.
|
||||
*/
|
||||
static uint32_t _round_up(uint32_t n, uint32_t size)
|
||||
{
|
||||
size--;
|
||||
return (n + size) & ~size;
|
||||
}
|
||||
|
||||
/* Unused.
|
||||
static uint32_t _div_up(uint32_t n, uint32_t size)
|
||||
{
|
||||
return _round_up(n, size) / size;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* Each chunk of metadata should be aligned to
|
||||
* METADATA_ALIGN.
|
||||
*/
|
||||
static uint32_t _next_base(struct data_area *area)
|
||||
{
|
||||
return _round_up(area->base + area->size, METADATA_ALIGN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick calculation based on pe_start.
|
||||
*/
|
||||
static int _adjust_pe_on_disk(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start = pvd->pe_start << SECTOR_SHIFT;
|
||||
|
||||
if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
|
||||
return 0;
|
||||
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _calc_simple_layout(struct pv_disk *pvd)
|
||||
{
|
||||
pvd->pv_on_disk.base = METADATA_BASE;
|
||||
pvd->pv_on_disk.size = PV_SIZE;
|
||||
|
||||
pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
|
||||
pvd->vg_on_disk.size = VG_SIZE;
|
||||
|
||||
pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
|
||||
pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
|
||||
|
||||
pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
|
||||
pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
|
||||
|
||||
pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
|
||||
pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
|
||||
}
|
||||
|
||||
static int _check_vg_limits(struct disk_list *dl)
|
||||
{
|
||||
if (dl->vgd.lv_max > MAX_LV) {
|
||||
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
|
||||
dl->pvd.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dl->vgd.pv_max > MAX_PV) {
|
||||
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
|
||||
dl->pvd.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This assumes pe_count and pe_start have already
|
||||
* been calculated correctly.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl)
|
||||
{
|
||||
struct pv_disk *pvd = &dl->pvd;
|
||||
|
||||
_calc_simple_layout(pvd);
|
||||
if (!_adjust_pe_on_disk(pvd)) {
|
||||
log_error("Insufficient space for metadata and PE's.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_check_vg_limits(dl))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of extents that can fit on a disk is metadata format dependant.
|
||||
* pe_start is any existing value for pe_start
|
||||
*/
|
||||
int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
|
||||
uint32_t max_extent_count, uint64_t pe_start)
|
||||
{
|
||||
struct pv_disk *pvd = dm_malloc(sizeof(*pvd));
|
||||
uint32_t end;
|
||||
|
||||
if (!pvd)
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* Guess how many extents will fit, bearing in mind that
|
||||
* one is going to be knocked off at the start of the
|
||||
* next loop.
|
||||
*/
|
||||
if (max_extent_count)
|
||||
pvd->pe_total = max_extent_count + 1;
|
||||
else
|
||||
pvd->pe_total = (pv->size / extent_size);
|
||||
|
||||
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
|
||||
log_error("Too few extents on %s. Try smaller extent size.",
|
||||
pv_dev_name(pv));
|
||||
dm_free(pvd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
pvd->pe_total--;
|
||||
_calc_simple_layout(pvd);
|
||||
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size +
|
||||
SECTOR_SIZE - 1) >> SECTOR_SHIFT);
|
||||
|
||||
if (pe_start && end < pe_start)
|
||||
end = pe_start;
|
||||
|
||||
pvd->pe_start = _round_up(end, LVM1_PE_ALIGN);
|
||||
|
||||
} while ((pvd->pe_start + ((uint64_t)pvd->pe_total * extent_size))
|
||||
> pv->size);
|
||||
|
||||
if (pvd->pe_total > MAX_PE_TOTAL) {
|
||||
log_error("Metadata extent limit (%u) exceeded for %s - "
|
||||
"%u required", MAX_PE_TOTAL, pv_dev_name(pv),
|
||||
pvd->pe_total);
|
||||
dm_free(pvd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pv->pe_count = pvd->pe_total;
|
||||
pv->pe_start = pvd->pe_start;
|
||||
/* We can't set pe_size here without breaking LVM1 compatibility */
|
||||
dm_free(pvd);
|
||||
return 1;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "lvm1-label.h"
|
||||
#include "disk-rep.h"
|
||||
#include "label.h"
|
||||
#include "metadata.h"
|
||||
#include "xlate.h"
|
||||
#include "format1.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static void _not_supported(const char *op)
|
||||
{
|
||||
log_error("The '%s' operation is not supported for the lvm1 labeller.",
|
||||
op);
|
||||
}
|
||||
|
||||
static int _lvm1_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector)
|
||||
{
|
||||
struct pv_disk *pvd = (struct pv_disk *) buf;
|
||||
uint32_t version;
|
||||
|
||||
/* LVM1 label must always be in first sector */
|
||||
if (sector)
|
||||
return 0;
|
||||
|
||||
version = xlate16(pvd->version);
|
||||
|
||||
if (pvd->id[0] == 'H' && pvd->id[1] == 'M' &&
|
||||
(version == 1 || version == 2))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _lvm1_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused)))
|
||||
{
|
||||
_not_supported("write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _lvm1_read(struct labeller *l, struct device *dev, void *buf, unsigned ioflags,
|
||||
lvm_callback_fn_t read_label_callback_fn, void *read_label_callback_context)
|
||||
{
|
||||
struct pv_disk *pvd = (struct pv_disk *) buf;
|
||||
struct vg_disk vgd;
|
||||
struct lvmcache_info *info;
|
||||
struct label *label = NULL;
|
||||
const char *vgid = FMT_LVM1_ORPHAN_VG_NAME;
|
||||
const char *vgname = FMT_LVM1_ORPHAN_VG_NAME;
|
||||
unsigned exported = 0;
|
||||
int r = 0;
|
||||
|
||||
munge_pvd(dev, pvd);
|
||||
|
||||
if (*pvd->vg_name) {
|
||||
if (!read_vgd(dev, &vgd, pvd))
|
||||
return_0;
|
||||
vgid = (char *) vgd.vg_uuid;
|
||||
vgname = (char *) pvd->vg_name;
|
||||
exported = pvd->pv_status & VG_EXPORTED;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_add(l, (char *)pvd->pv_uuid, dev, vgname, vgid,
|
||||
exported)))
|
||||
goto_out;
|
||||
|
||||
label = lvmcache_get_label(info);
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32(pvd->pv_size)) << SECTOR_SHIFT);
|
||||
lvmcache_set_ext_version(info, 0);
|
||||
lvmcache_set_ext_flags(info, 0);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_bas(info);
|
||||
lvmcache_make_valid(info);
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (read_label_callback_fn)
|
||||
read_label_callback_fn(!r, 0, read_label_callback_context, label);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _lvm1_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
|
||||
{
|
||||
strcpy(label->type, "LVM1");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _lvm1_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused)))
|
||||
{
|
||||
}
|
||||
|
||||
static void _lvm1_destroy(struct labeller *l)
|
||||
{
|
||||
dm_free(l);
|
||||
}
|
||||
|
||||
struct label_ops _lvm1_ops = {
|
||||
.can_handle = _lvm1_can_handle,
|
||||
.write = _lvm1_write,
|
||||
.read = _lvm1_read,
|
||||
.initialise_label = _lvm1_initialise_label,
|
||||
.destroy_label = _lvm1_destroy_label,
|
||||
.destroy = _lvm1_destroy,
|
||||
};
|
||||
|
||||
struct labeller *lvm1_labeller_create(struct format_type *fmt)
|
||||
{
|
||||
struct labeller *l;
|
||||
|
||||
if (!(l = dm_malloc(sizeof(*l)))) {
|
||||
log_error("Couldn't allocate labeller object.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l->ops = &_lvm1_ops;
|
||||
l->fmt = fmt;
|
||||
|
||||
return l;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVM1_LABEL_H
|
||||
#define _LVM_LVM1_LABEL_H
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
struct labeller *lvm1_labeller_create(struct format_type *fmt);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user