]> git.ozlabs.org Git - petitboot/blob - discover/devmapper.c
discover/devmapper: Retry dm-device remove if busy
[petitboot] / discover / devmapper.c
1 #include <talloc/talloc.h>
2 #include <types/types.h>
3 #include <log/log.h>
4 #include <errno.h>
5 #include <inttypes.h>
6 #include <string.h>
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11
12 #include "libdevmapper.h"
13 #include "devmapper.h"
14 #include "platform.h"
15
16 #define MERGE_INTERVAL_US       200000
17
18 struct target {
19         uint64_t        start_sector;
20         uint64_t        end_sector;
21         char            *ttype;
22         char            *params;
23 };
24
25 static unsigned long read_param_uint(struct discover_device *device,
26                                 const char *param)
27 {
28         unsigned long value = 0;
29         const char *tmp;
30
31         tmp = discover_device_get_param(device, param);
32         if (!tmp) {
33                 pb_debug("Could not retrieve parameter '%s' for %s\n",
34                          param, device->device_path);
35                 errno = EINVAL;
36         } else {
37                 errno = 0;
38                 value = strtoul(tmp, NULL, 0);
39         }
40
41         /* Return errno and result directly */
42         return value;
43 }
44
45 /* Return the number of sectors on a block device. Zero represents an error */
46 static uint64_t get_block_sectors(struct discover_device *device)
47 {
48         unsigned long major, minor, sectors = 0;
49         char *attr, *buf = NULL;
50         struct stat sb;
51         int fd = -1;
52         ssize_t sz;
53
54         sectors = read_param_uint(device, "ID_PART_ENTRY_SIZE");
55         if (!errno)
56                 return (uint64_t)sectors;
57         else
58                 pb_debug("Error reading sector count for %s: %m\n",
59                        device->device_path);
60
61         /* Either the udev property is missing or we failed to parse it.
62          * Instead try to directly read the size attribute out of sysfs */
63         major = read_param_uint(device, "MAJOR");
64         if (errno) {
65                 pb_debug("Error reading %s major number\n", device->device_path);
66                 return 0;
67         }
68         minor = read_param_uint(device, "MINOR");
69         if (errno) {
70                 pb_debug("Error reading %s minor number\n", device->device_path);
71                 return 0;
72         }
73
74         attr = talloc_asprintf(device, "/sys/dev/block/%lu:%lu/size",
75                                major, minor);
76         if (stat(attr, &sb)) {
77                 pb_debug("Failed to stat %s, %m\n", attr);
78                 goto out;
79         }
80
81         fd = open(attr, O_RDONLY);
82         if (fd < 0) {
83                 pb_debug("Failed to open sysfs attribute for %s\n",
84                          device->device_path);
85                 goto out;
86         }
87
88         buf = talloc_array(device, char, sb.st_size);
89         if (!buf) {
90                 pb_debug("Failed to allocate space for attr\n");
91                 goto out;
92         }
93
94         sz = read(fd, buf, sb.st_size);
95         if (sz <= 0) {
96                 pb_debug("Failed to read sysfs attr: %m\n");
97                 goto out;
98         }
99
100         sectors = strtoul(buf, NULL, 0);
101         if (errno) {
102                 pb_debug("Failed to read sectors from sysfs: %m\n");
103                 sectors = 0;
104         }
105
106 out:
107         close(fd);
108         talloc_free(buf);
109         talloc_free(attr);
110         return (uint64_t)sectors;
111 }
112
113 /*
114  * The system's libdm may or may not have udev sync support. Tell libdm
115  * to manage the creation of device nodes itself rather than waiting on udev
116  * to do it
117  */
118 static inline int set_cookie(struct dm_task *task, uint32_t *cookie)
119 {
120         uint16_t udev_rules = 0;
121         *cookie = 0;
122
123         dm_udev_set_sync_support(0);
124         udev_rules |= DM_UDEV_DISABLE_DM_RULES_FLAG |
125                 DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
126
127         return dm_task_set_cookie(task, cookie, udev_rules);
128 }
129
130 static bool snapshot_merge_complete(const char *dm_name)
131 {
132         uint64_t sectors, meta_sectors;
133         char *params = NULL,  *target_type = NULL;
134         uint64_t start, length;
135         struct dm_task *task;
136         bool result = true;
137         int n;
138
139         task = dm_task_create(DM_DEVICE_STATUS);
140         if (!task) {
141                 pb_log("%s: Error creating task\n", __func__);
142                 return result;
143         }
144
145         if (!dm_task_set_name(task, dm_name)) {
146                 pb_log("No dm-device named '%s'\n", dm_name);
147                 goto out;
148         }
149
150         if (!dm_task_run(task)) {
151                 pb_log("Unable to retrieve status for '%s'\n", dm_name);
152                 goto out;
153         }
154
155         dm_get_next_target(task, NULL, &start, &length, &target_type, &params);
156
157         if (!params) {
158                 pb_log("Unable to retrieve params for '%s'\n", dm_name);
159                 goto out;
160         }
161
162         if (!strncmp(params, "Invalid", strlen("Invalid"))) {
163                 pb_log("dm-device %s has become invalid\n", dm_name);
164                 goto out;
165         }
166
167         /* Merge is complete when metadata sectors are the only sectors
168          * allocated - see Documentation/device-mapper/snapshot.txt */
169         n = sscanf(params, "%" SCNu64 "/%*u %" SCNu64,
170                         &sectors, &meta_sectors);
171         if (n != 2) {
172                 pb_log("%s unexpected status: '%s'\n", dm_name, params);
173                 goto out;
174         }
175         result = sectors == meta_sectors;
176
177         pb_debug("%s merging; %" PRIu64 " sectors, %" PRIu64
178                         " metadata sectors\n",
179                  dm_name, sectors, meta_sectors);
180
181 out:
182         /* In case of error or an invalid snapshot return true so callers will
183          * move on and catch the error */
184         dm_task_destroy(task);
185         return result;
186 }
187
188 /* Resume or suspend dm device */
189 static int set_device_active(const char *dm_name, bool active)
190 {
191         struct dm_task *task;
192         uint32_t cookie;
193         int rc = -1;
194
195         if (active)
196                 task = dm_task_create(DM_DEVICE_RESUME);
197         else
198                 task = dm_task_create(DM_DEVICE_SUSPEND);
199
200         if (!task) {
201                 pb_log("%s: Could not create dm_task\n", __func__);
202                 return rc;
203         }
204
205         if (!dm_task_set_name(task, dm_name)) {
206                 pb_log("No dm-device named '%s'\n", dm_name);
207                 goto out;
208         }
209
210         if (!set_cookie(task, &cookie))
211                 goto out;
212
213         if (!dm_task_run(task)) {
214                 pb_log("Unable to %s device '%s'\n",
215                        active ? "resume" : "suspend", dm_name);
216                 goto out;
217         }
218
219         rc = 0;
220
221         /* Wait for /dev/mapper/ entries to be updated */
222         dm_udev_wait(cookie);
223
224 out:
225         dm_task_destroy(task);
226         return rc;
227 }
228
229 /* Run a DM_DEVICE_CREATE task with provided table (ttype and params) */
230 static int run_create_task(const char *dm_name, const struct target *target)
231 {
232         struct dm_task *task;
233         uint32_t cookie;
234
235         pb_debug("%s: %" PRIu64 " %" PRIu64 " '%s' '%s'\n", __func__,
236                  target->start_sector, target->end_sector,
237                  target->ttype, target->params);
238
239         task = dm_task_create(DM_DEVICE_CREATE);
240         if (!task) {
241                 pb_log("Error creating new dm-task\n");
242                 return -1;
243         }
244
245         if (!dm_task_set_name(task, dm_name))
246                 return -1;
247
248         if (!dm_task_add_target(task, target->start_sector, target->end_sector,
249                                 target->ttype, target->params))
250                 return -1;
251
252         if (!dm_task_set_add_node(task, DM_ADD_NODE_ON_CREATE))
253                 return -1;
254
255         if (!set_cookie(task, &cookie))
256                 return -1;
257
258         if (!dm_task_run(task)) {
259                 pb_log("Error executing dm-task\n");
260                 return -1;
261         }
262
263         /* Wait for /dev/mapper/ entry to appear */
264         dm_udev_wait(cookie);
265
266         dm_task_destroy(task);
267         return 0;
268 }
269
270 static int create_base(struct discover_device *device)
271 {
272         struct target target;
273         char *name = NULL;
274         int rc = -1;
275
276         if (!device->ramdisk)
277                 return rc;
278
279         target.start_sector = 0;
280         target.end_sector = device->ramdisk->sectors;
281
282         target.ttype = talloc_asprintf(device,  "linear");
283         target.params = talloc_asprintf(device, "%s 0", device->device_path);
284         if (!target.ttype || !target.params) {
285                 pb_log("Failed to allocate map parameters\n");
286                 goto out;
287         }
288
289         name = talloc_asprintf(device, "pb-%s-base", device->device->id);
290         if (!name || run_create_task(name, &target))
291                 goto out;
292
293         device->ramdisk->base = talloc_asprintf(device, "/dev/mapper/pb-%s-base",
294                                         device->device->id);
295         if (!device->ramdisk->base) {
296                 pb_log("Failed to track new device /dev/mapper/%s-base\n",
297                         device->device->id);
298                 goto out;
299         }
300
301         rc = 0;
302
303 out:
304         talloc_free(name);
305         talloc_free(target.params);
306         talloc_free(target.ttype);
307         return rc;
308 }
309
310 static int create_origin(struct discover_device *device)
311 {
312         struct target target;
313         char *name = NULL;
314         int rc = -1;
315
316         if (!device->ramdisk || !device->ramdisk->base)
317                 return -1;
318
319         target.start_sector = 0;
320         target.end_sector = device->ramdisk->sectors;
321
322         target.ttype = talloc_asprintf(device,  "snapshot-origin");
323         target.params = talloc_asprintf(device, "%s", device->ramdisk->base);
324         if (!target.ttype || !target.params) {
325                 pb_log("Failed to allocate map parameters\n");
326                 goto out;
327         }
328
329         name = talloc_asprintf(device, "pb-%s-origin", device->device->id);
330         if (!name || run_create_task(name, &target))
331                 goto out;
332
333         device->ramdisk->origin = talloc_asprintf(device,
334                                         "/dev/mapper/pb-%s-origin",
335                                         device->device->id);
336         if (!device->ramdisk->origin) {
337                 pb_log("Failed to track new device /dev/mapper/%s-origin\n",
338                        device->device->id);
339                 goto out;
340         }
341
342         rc = 0;
343
344 out:
345         talloc_free(name);
346         talloc_free(target.params);
347         talloc_free(target.ttype);
348         return rc;
349 }
350
351 static int create_snapshot(struct discover_device *device)
352 {
353         struct target target;
354         char *name = NULL;
355         int rc = -1;
356
357         if (!device->ramdisk || !device->ramdisk->base ||
358             !device->ramdisk->origin)
359                 return -1;
360
361         target.start_sector = 0;
362         target.end_sector = device->ramdisk->sectors;
363
364         target.ttype = talloc_asprintf(device,  "snapshot");
365         target.params = talloc_asprintf(device, "%s %s P 8",
366                  device->ramdisk->base, device->ramdisk->path);
367         if (!target.ttype || !target.params) {
368                 pb_log("Failed to allocate snapshot parameters\n");
369                 goto out;
370         }
371
372         name = talloc_asprintf(device, "pb-%s", device->device->id);
373         if (!name || run_create_task(name, &target))
374                 goto out;
375
376         device->ramdisk->snapshot = talloc_asprintf(device, "/dev/mapper/pb-%s",
377                                                 device->device->id);
378         if (!device->ramdisk->snapshot) {
379                 pb_log("Failed to track new device /dev/mapper/%s\n",
380                        device->device->id);
381                 goto out;
382         }
383
384         rc = 0;
385
386 out:
387         talloc_free(name);
388         talloc_free(target.params);
389         talloc_free(target.ttype);
390         return rc;
391 }
392
393 int devmapper_init_snapshot(struct device_handler *handler,
394                      struct discover_device *device)
395 {
396         struct ramdisk_device *ramdisk;
397
398         if (config_get()->disable_snapshots)
399                 return 0;
400
401         ramdisk = device_handler_get_ramdisk(handler);
402         if (!ramdisk) {
403                 pb_log("No ramdisk available for snapshot %s\n",
404                        device->device->id);
405                 return -1;
406         }
407
408         ramdisk->sectors = get_block_sectors(device);
409         if (!ramdisk->sectors) {
410                 pb_log("Error retreiving sectors for %s\n",
411                        device->device->id);
412                 return -1;
413         }
414
415         device->ramdisk = ramdisk;
416
417         /* Create linear map */
418         if (create_base(device)) {
419                 pb_log("Error creating linear base\n");
420                 goto err;
421         }
422
423         /* Create snapshot-origin */
424         if (create_origin(device)) {
425                 pb_log("Error creating snapshot-origin\n");
426                 goto err;
427         }
428
429         if (set_device_active(device->ramdisk->origin, false)) {
430                 pb_log("Failed to suspend origin\n");
431                 goto err;
432         }
433
434         /* Create snapshot */
435         if (create_snapshot(device)) {
436                 pb_log("Error creating snapshot\n");
437                 goto err;
438         }
439
440         if (set_device_active(device->ramdisk->origin, true)) {
441                 pb_log("Failed to resume origin\n");
442                 goto err;
443         }
444
445         pb_log("Snapshot successfully created for %s\n", device->device->id);
446
447         return 0;
448
449 err:
450         pb_log("Error creating snapshot devices for %s\n", device->device->id);
451         devmapper_destroy_snapshot(device);
452         return -1;
453 }
454
455 /* Destroy specific dm device */
456 static int destroy_device(const char *dm_name)
457 {
458         struct dm_task *task;
459         uint32_t cookie;
460         int rc, retries = 0;
461
462 retry:
463         task = dm_task_create(DM_DEVICE_REMOVE);
464         if (!task) {
465                 pb_debug("%s: could not create dm_task\n", __func__);
466                 return -1;
467         }
468
469         if (!dm_task_set_name(task, dm_name)) {
470                 rc = dm_task_get_errno(task);
471                 pb_debug("%s: Couldn't set name for %s, %s (%d)\n",
472                                 __func__, dm_name, strerror(rc), rc);
473                 goto out;
474         }
475
476         if (!set_cookie(task, &cookie)) {
477                 rc = dm_task_get_errno(task);
478                 pb_debug("%s: set_cookie failed, %s (%d)\n",
479                                 __func__, strerror(rc), rc);
480                 goto out;
481         }
482
483         if (!dm_task_run(task)) {
484                 rc = dm_task_get_errno(task);
485                 pb_debug("%s: Unable to remove device '%s', %s (%d)\n",
486                                 __func__, dm_name, strerror(rc), rc);
487                 goto out;
488         }
489
490         rc = 0;
491
492         /* Wait for /dev/mapper/ entries to be removed */
493         dm_udev_wait(cookie);
494
495 out:
496         dm_task_destroy(task);
497         if (rc == EBUSY && retries < 5) {
498                 pb_log("%s: Device busy, retry %d..\n", __func__, retries);
499                 usleep(100000);
500                 retries++;
501                 goto retry;
502         }
503         return rc;
504 }
505
506 /* Destroy all dm devices related to a discover_device's snapshot */
507 int devmapper_destroy_snapshot(struct discover_device *device)
508 {
509         int rc = -1;
510
511         if (!device->ramdisk)
512                 return 0;
513
514         if (device->mounted) {
515                 pb_log("Can not remove snapshot: %s is mounted\n",
516                        device->device->id);
517                 return -1;
518         }
519
520         /* Clean up dm devices in order */
521         if (device->ramdisk->snapshot)
522                 if (destroy_device(device->ramdisk->snapshot))
523                         goto out;
524
525         if (device->ramdisk->origin)
526                 if (destroy_device(device->ramdisk->origin))
527                         goto out;
528
529         if (device->ramdisk->base)
530                 if (destroy_device(device->ramdisk->base))
531                         goto out;
532
533         rc = 0;
534 out:
535         if (rc)
536                 pb_log("Warning: %s snapshot not cleanly removed\n",
537                        device->device->id);
538         device_handler_release_ramdisk(device);
539         return rc;
540 }
541
542 static int reload_snapshot(struct discover_device *device, bool merge)
543 {
544         struct target target;
545         struct dm_task *task;
546         int rc = -1;
547
548         target.start_sector = 0;
549         target.end_sector = device->ramdisk->sectors;
550
551         if (merge) {
552                 target.ttype = talloc_asprintf(device,  "snapshot-merge");
553                 target.params = talloc_asprintf(device, "%s %s P 8",
554                          device->ramdisk->base, device->ramdisk->path);
555         } else {
556                 target.ttype = talloc_asprintf(device,  "snapshot-origin");
557                 target.params = talloc_asprintf(device, "%s",
558                          device->ramdisk->base);
559         }
560         if (!target.ttype || !target.params) {
561                 pb_log("%s: failed to allocate parameters\n", __func__);
562                 goto err1;
563         }
564
565         task = dm_task_create(DM_DEVICE_RELOAD);
566         if (!task) {
567                 pb_log("%s: Error creating task\n", __func__);
568                 goto err1;
569         }
570
571         if (!dm_task_set_name(task, device->ramdisk->origin)) {
572                 pb_log("No dm-device named '%s'\n", device->ramdisk->origin);
573                 goto err2;
574         }
575
576         if (!dm_task_add_target(task, target.start_sector, target.end_sector,
577                                 target.ttype, target.params)) {
578                 pb_log("%s: Failed to set target\n", __func__);
579                 goto err2;
580         }
581
582         if (!dm_task_run(task)) {
583                 pb_log("Failed to reload %s\n", device->ramdisk->origin);
584                 goto err2;
585         }
586
587         rc = 0;
588 err2:
589         dm_task_destroy(task);
590 err1:
591         talloc_free(target.ttype);
592         talloc_free(target.params);
593         return rc;
594 }
595
596 int devmapper_merge_snapshot(struct discover_device *device)
597 {
598         if (device->mounted) {
599                 pb_log("%s: %s still mounted\n", __func__, device->device->id);
600                 return -1;
601         }
602
603         /* Suspend origin device */
604         if (set_device_active(device->ramdisk->origin, false)) {
605                 pb_log("%s: failed to suspend %s\n",
606                        __func__, device->ramdisk->origin);
607                 return -1;
608         }
609
610         /* Destroy snapshot */
611         if (destroy_device(device->ramdisk->snapshot)) {
612                 /* The state of the snapshot is unknown, but try to
613                  * resume to allow the snapshot to be remounted */
614                 set_device_active(device->ramdisk->origin, true);
615                 return -1;
616         }
617         talloc_free(device->ramdisk->snapshot);
618         device->ramdisk->snapshot = NULL;
619
620         /* Reload origin device for merging */
621         reload_snapshot(device, true);
622
623         /* Resume origin device */
624         set_device_active(device->ramdisk->origin, true);
625
626         /* Block until merge complete */
627         while (!snapshot_merge_complete(device->ramdisk->origin))
628                 usleep(MERGE_INTERVAL_US);
629
630         /* Suspend origin device */
631         set_device_active(device->ramdisk->origin, false);
632
633         /* Reload origin device */
634         reload_snapshot(device, false);
635
636         /* Re-create snapshot */
637         if (create_snapshot(device))
638                 return -1;
639
640         /* Resume origin device */
641         return set_device_active(device->ramdisk->origin, true);
642 }