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