Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2004 Luca Berra
3 : : * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
4 : : *
5 : : * This file is part of LVM2.
6 : : *
7 : : * This copyrighted material is made available to anyone wishing to use,
8 : : * modify, copy, or redistribute it subject to the terms and conditions
9 : : * of the GNU Lesser General Public License v.2.1.
10 : : *
11 : : * You should have received a copy of the GNU Lesser General Public License
12 : : * along with this program; if not, write to the Free Software Foundation,
13 : : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : : */
15 : :
16 : : #include "lib.h"
17 : : #include "metadata.h"
18 : : #include "xlate.h"
19 : : #include "filter.h"
20 : :
21 : : #ifdef linux
22 : :
23 : : /* Lifted from <linux/raid/md_p.h> because of difficulty including it */
24 : :
25 : : #define MD_SB_MAGIC 0xa92b4efc
26 : : #define MD_RESERVED_BYTES (64 * 1024ULL)
27 : : #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512)
28 : : #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \
29 : : - MD_RESERVED_SECTORS)
30 : :
31 : 0 : static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
32 : : {
33 : : uint32_t md_magic;
34 : :
35 : : /* Version 1 is little endian; version 0.90.0 is machine endian */
36 [ # # ][ # # ]: 0 : if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
[ # # ]
37 : 0 : ((md_magic == xlate32(MD_SB_MAGIC)) ||
38 : 0 : (md_magic == MD_SB_MAGIC)))
39 : 0 : return 1;
40 : :
41 : 0 : return 0;
42 : : }
43 : :
44 : : /*
45 : : * Calculate the position of the superblock.
46 : : * It is always aligned to a 4K boundary and
47 : : * depending on minor_version, it can be:
48 : : * 0: At least 8K, but less than 12K, from end of device
49 : : * 1: At start of device
50 : : * 2: 4K from start of device.
51 : : */
52 : : typedef enum {
53 : : MD_MINOR_VERSION_MIN,
54 : : MD_MINOR_V0 = MD_MINOR_VERSION_MIN,
55 : : MD_MINOR_V1,
56 : : MD_MINOR_V2,
57 : : MD_MINOR_VERSION_MAX = MD_MINOR_V2
58 : : } md_minor_version_t;
59 : :
60 : 0 : static uint64_t _v1_sb_offset(uint64_t size, md_minor_version_t minor_version)
61 : : {
62 : 0 : uint64_t uninitialized_var(sb_offset);
63 : :
64 [ # # # # ]: 0 : switch(minor_version) {
65 : : case MD_MINOR_V0:
66 : 0 : sb_offset = (size - 8 * 2) & ~(4 * 2 - 1ULL);
67 : 0 : break;
68 : : case MD_MINOR_V1:
69 : 0 : sb_offset = 0;
70 : 0 : break;
71 : : case MD_MINOR_V2:
72 : 0 : sb_offset = 4 * 2;
73 : : break;
74 : : }
75 : 0 : sb_offset <<= SECTOR_SHIFT;
76 : :
77 : 0 : return sb_offset;
78 : : }
79 : :
80 : : /*
81 : : * Returns -1 on error
82 : : */
83 : 0 : int dev_is_md(struct device *dev, uint64_t *sb)
84 : : {
85 : 0 : int ret = 1;
86 : : md_minor_version_t minor;
87 : : uint64_t size, sb_offset;
88 : :
89 [ # # ]: 0 : if (!dev_get_size(dev, &size)) {
90 : 0 : stack;
91 : 0 : return -1;
92 : : }
93 : :
94 [ # # ]: 0 : if (size < MD_RESERVED_SECTORS * 2)
95 : 0 : return 0;
96 : :
97 [ # # ]: 0 : if (!dev_open(dev)) {
98 : 0 : stack;
99 : 0 : return -1;
100 : : }
101 : :
102 : : /* Check if it is an md component device. */
103 : : /* Version 0.90.0 */
104 : 0 : sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
105 [ # # ]: 0 : if (_dev_has_md_magic(dev, sb_offset))
106 : 0 : goto out;
107 : :
108 : 0 : minor = MD_MINOR_VERSION_MIN;
109 : : /* Version 1, try v1.0 -> v1.2 */
110 : : do {
111 : 0 : sb_offset = _v1_sb_offset(size, minor);
112 [ # # ]: 0 : if (_dev_has_md_magic(dev, sb_offset))
113 : 0 : goto out;
114 [ # # ]: 0 : } while (++minor <= MD_MINOR_VERSION_MAX);
115 : :
116 : 0 : ret = 0;
117 : :
118 : : out:
119 [ # # ]: 0 : if (!dev_close(dev))
120 : 0 : stack;
121 : :
122 [ # # ][ # # ]: 0 : if (ret && sb)
123 : 0 : *sb = sb_offset;
124 : :
125 : 0 : return ret;
126 : : }
127 : :
128 : 0 : static int _md_sysfs_attribute_snprintf(char *path, size_t size,
129 : : const char *sysfs_dir,
130 : : struct device *blkdev,
131 : : const char *attribute)
132 : : {
133 : : struct stat info;
134 : 0 : dev_t dev = blkdev->dev;
135 : 0 : int ret = -1;
136 : :
137 [ # # ][ # # ]: 0 : if (!sysfs_dir || !*sysfs_dir)
138 : 0 : return ret;
139 : :
140 [ # # ]: 0 : if (MAJOR(dev) == blkext_major()) {
141 : : /* lookup parent MD device from blkext partition */
142 [ # # ]: 0 : if (!get_primary_dev(sysfs_dir, blkdev, &dev))
143 : 0 : return ret;
144 : : }
145 : :
146 [ # # ]: 0 : if (MAJOR(dev) != md_major())
147 : 0 : return ret;
148 : :
149 : 0 : ret = dm_snprintf(path, size, "%s/dev/block/%d:%d/md/%s", sysfs_dir,
150 : 0 : (int)MAJOR(dev), (int)MINOR(dev), attribute);
151 [ # # ]: 0 : if (ret < 0) {
152 : 0 : log_error("dm_snprintf md %s failed", attribute);
153 : 0 : return ret;
154 : : }
155 : :
156 [ # # ]: 0 : if (stat(path, &info) == -1) {
157 [ # # ]: 0 : if (errno != ENOENT) {
158 : 0 : log_sys_error("stat", path);
159 : 0 : return ret;
160 : : }
161 : : /* old sysfs structure */
162 : 0 : ret = dm_snprintf(path, size, "%s/block/md%d/md/%s",
163 : 0 : sysfs_dir, (int)MINOR(dev), attribute);
164 [ # # ]: 0 : if (ret < 0) {
165 : 0 : log_error("dm_snprintf old md %s failed", attribute);
166 : 0 : return ret;
167 : : }
168 : : }
169 : :
170 : 0 : return ret;
171 : : }
172 : :
173 : 0 : static int _md_sysfs_attribute_scanf(const char *sysfs_dir,
174 : : struct device *dev,
175 : : const char *attribute_name,
176 : : const char *attribute_fmt,
177 : : void *attribute_value)
178 : : {
179 : : char path[PATH_MAX+1], buffer[64];
180 : : FILE *fp;
181 : 0 : int ret = 0;
182 : :
183 [ # # ]: 0 : if (_md_sysfs_attribute_snprintf(path, PATH_MAX, sysfs_dir,
184 : : dev, attribute_name) < 0)
185 : 0 : return ret;
186 : :
187 [ # # ]: 0 : if (!(fp = fopen(path, "r"))) {
188 : 0 : log_sys_error("fopen", path);
189 : 0 : return ret;
190 : : }
191 : :
192 [ # # ]: 0 : if (!fgets(buffer, sizeof(buffer), fp)) {
193 : 0 : log_sys_error("fgets", path);
194 : 0 : goto out;
195 : : }
196 : :
197 [ # # ]: 0 : if ((ret = sscanf(buffer, attribute_fmt, attribute_value)) != 1) {
198 : 0 : log_error("%s sysfs attr %s not in expected format: %s",
199 : : dev_name(dev), attribute_name, buffer);
200 : : goto out;
201 : : }
202 : :
203 : : out:
204 [ # # ]: 0 : if (fclose(fp))
205 : 0 : log_sys_error("fclose", path);
206 : :
207 : 0 : return ret;
208 : : }
209 : :
210 : : /*
211 : : * Retrieve chunk size from md device using sysfs.
212 : : */
213 : 0 : static unsigned long dev_md_chunk_size(const char *sysfs_dir,
214 : : struct device *dev)
215 : : {
216 : 0 : const char *attribute = "chunk_size";
217 : 0 : unsigned long chunk_size_bytes = 0UL;
218 : :
219 [ # # ]: 0 : if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
220 : : "%lu", &chunk_size_bytes) != 1)
221 : 0 : return 0;
222 : :
223 : 0 : log_very_verbose("Device %s %s is %lu bytes.",
224 : : dev_name(dev), attribute, chunk_size_bytes);
225 : :
226 : 0 : return chunk_size_bytes >> SECTOR_SHIFT;
227 : : }
228 : :
229 : : /*
230 : : * Retrieve level from md device using sysfs.
231 : : */
232 : 0 : static int dev_md_level(const char *sysfs_dir, struct device *dev)
233 : : {
234 : 0 : const char *attribute = "level";
235 : 0 : int level = -1;
236 : :
237 [ # # ]: 0 : if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
238 : : "raid%d", &level) != 1)
239 : 0 : return -1;
240 : :
241 : 0 : log_very_verbose("Device %s %s is raid%d.",
242 : : dev_name(dev), attribute, level);
243 : :
244 : 0 : return level;
245 : : }
246 : :
247 : : /*
248 : : * Retrieve raid_disks from md device using sysfs.
249 : : */
250 : 0 : static int dev_md_raid_disks(const char *sysfs_dir, struct device *dev)
251 : : {
252 : 0 : const char *attribute = "raid_disks";
253 : 0 : int raid_disks = 0;
254 : :
255 [ # # ]: 0 : if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
256 : : "%d", &raid_disks) != 1)
257 : 0 : return 0;
258 : :
259 : 0 : log_very_verbose("Device %s %s is %d.",
260 : : dev_name(dev), attribute, raid_disks);
261 : :
262 : 0 : return raid_disks;
263 : : }
264 : :
265 : : /*
266 : : * Calculate stripe width of md device using its sysfs files.
267 : : */
268 : 0 : unsigned long dev_md_stripe_width(const char *sysfs_dir, struct device *dev)
269 : : {
270 : 0 : unsigned long chunk_size_sectors = 0UL;
271 : 0 : unsigned long stripe_width_sectors = 0UL;
272 : : int level, raid_disks, data_disks;
273 : :
274 : 0 : chunk_size_sectors = dev_md_chunk_size(sysfs_dir, dev);
275 [ # # ]: 0 : if (!chunk_size_sectors)
276 : 0 : return 0;
277 : :
278 : 0 : level = dev_md_level(sysfs_dir, dev);
279 [ # # ]: 0 : if (level < 0)
280 : 0 : return 0;
281 : :
282 : 0 : raid_disks = dev_md_raid_disks(sysfs_dir, dev);
283 [ # # ]: 0 : if (!raid_disks)
284 : 0 : return 0;
285 : :
286 : : /* The raid level governs the number of data disks. */
287 [ # # # # : 0 : switch (level) {
# ]
288 : : case 0:
289 : : /* striped md does not have any parity disks */
290 : 0 : data_disks = raid_disks;
291 : 0 : break;
292 : : case 1:
293 : : case 10:
294 : : /* mirrored md effectively has 1 data disk */
295 : 0 : data_disks = 1;
296 : 0 : break;
297 : : case 4:
298 : : case 5:
299 : : /* both raid 4 and 5 have a single parity disk */
300 : 0 : data_disks = raid_disks - 1;
301 : 0 : break;
302 : : case 6:
303 : : /* raid 6 has 2 parity disks */
304 : 0 : data_disks = raid_disks - 2;
305 : 0 : break;
306 : : default:
307 : 0 : log_error("Device %s has an unknown md raid level: %d",
308 : : dev_name(dev), level);
309 : 0 : return 0;
310 : : }
311 : :
312 : 0 : stripe_width_sectors = chunk_size_sectors * data_disks;
313 : :
314 : 0 : log_very_verbose("Device %s stripe-width is %lu bytes.",
315 : : dev_name(dev),
316 : : stripe_width_sectors << SECTOR_SHIFT);
317 : :
318 : 0 : return stripe_width_sectors;
319 : : }
320 : :
321 : : #else
322 : :
323 : : int dev_is_md(struct device *dev __attribute((unused)),
324 : : uint64_t *sb __attribute((unused)))
325 : : {
326 : : return 0;
327 : : }
328 : :
329 : : unsigned long dev_md_stripe_width(const char *sysfs_dir __attribute((unused)),
330 : : struct device *dev __attribute((unused)))
331 : : {
332 : : return 0UL;
333 : : }
334 : :
335 : : #endif
|