nfsidmap: Added Error Logging
[nfs-utils.git] / utils / blkmapd / dm-device.c
1 /*
2  * dm-device.c: create or remove device via device mapper API.
3  *
4  * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <linux/kdev_t.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <libdevmapper.h>
40
41 #include "device-discovery.h"
42
43 #define DM_DEV_NAME_LEN         256
44
45 #ifndef DM_MAX_TYPE_NAME
46 #define DM_MAX_TYPE_NAME        16
47 #endif
48
49 #define DM_PARAMS_LEN           512     /* XXX: is this enough for target? */
50 #define TYPE_HAS_DEV(type)      ((type == BLOCK_VOLUME_SIMPLE) || \
51                          (type == BLOCK_VOLUME_PSEUDO))
52
53 struct bl_dm_table {
54         uint64_t offset;
55         uint64_t size;
56         char target_type[DM_MAX_TYPE_NAME];
57         char params[DM_PARAMS_LEN];
58         struct bl_dm_table *next;
59 };
60
61 struct bl_dm_tree {
62         uint64_t dev;
63         struct dm_tree *tree;
64         struct bl_dm_tree *next;
65 };
66
67 static const char dm_name[] = "pnfs_vol_%u";
68
69 static unsigned int dev_count;
70
71 static inline struct bl_dm_table *bl_dm_table_alloc(void)
72 {
73         return (struct bl_dm_table *)calloc(1, sizeof(struct bl_dm_table));
74 }
75
76 static void bl_dm_table_free(struct bl_dm_table *bl_table_head)
77 {
78         struct bl_dm_table *p;
79
80         while (bl_table_head) {
81                 p = bl_table_head->next;
82                 free(bl_table_head);
83                 bl_table_head = p;
84         }
85 }
86
87 static void add_to_bl_dm_table(struct bl_dm_table **bl_table_head,
88                         struct bl_dm_table *table)
89 {
90         struct bl_dm_table *p;
91
92         if (!*bl_table_head) {
93                 *bl_table_head = table;
94                 return;
95         }
96         p = *bl_table_head;
97         while (p->next)
98                 p = p->next;
99         p->next = table;
100 }
101
102 struct bl_dm_tree *bl_tree_head;
103
104 static struct bl_dm_tree *find_bl_dm_tree(uint64_t dev)
105 {
106         struct bl_dm_tree *p;
107
108         for (p = bl_tree_head; p; p = p->next) {
109                 if (p->dev == dev)
110                         break;
111         }
112         return p;
113 }
114
115 static void del_from_bl_dm_tree(uint64_t dev)
116 {
117         struct bl_dm_tree *p, *pre = bl_tree_head;
118
119         for (p = pre; p; p = p->next) {
120                 if (p->dev == dev) {
121                         pre->next = p->next;
122                         if (p == bl_tree_head)
123                                 bl_tree_head = bl_tree_head->next;
124                         free(p);
125                         break;
126                 }
127                 pre = p;
128         }
129 }
130
131 static void add_to_bl_dm_tree(struct bl_dm_tree *tree)
132 {
133         struct bl_dm_tree *p;
134
135         if (!bl_tree_head) {
136                 bl_tree_head = tree;
137                 return;
138         }
139         p = bl_tree_head;
140         while (p->next)
141                 p = p->next;
142         p->next = tree;
143         return;
144 }
145
146 /*
147  * Create device via device mapper
148  * return 0 when creation failed
149  * return dev no for created device
150  */
151 static uint64_t
152 dm_device_create_mapped(const char *dev_name, struct bl_dm_table *p)
153 {
154         struct dm_task *dmt;
155         struct dm_info dminfo;
156         int ret = 0;
157
158         dmt = dm_task_create(DM_DEVICE_CREATE);
159         if (!dmt) {
160                 BL_LOG_ERR("Create dm_task for %s failed\n", dev_name);
161                 return 0;
162         }
163         ret = dm_task_set_name(dmt, dev_name);
164         if (!ret)
165                 goto err_out;
166
167         while (p) {
168                 ret =
169                     dm_task_add_target(dmt, p->offset, p->size, p->target_type,
170                                        p->params);
171                 if (!ret)
172                         goto err_out;
173                 p = p->next;
174         }
175
176         ret = dm_task_run(dmt) && dm_task_get_info(dmt, &dminfo)
177             && dminfo.exists;
178
179         if (!ret)
180                 goto err_out;
181
182         dm_task_update_nodes();
183
184  err_out:
185         dm_task_destroy(dmt);
186
187         if (!ret) {
188                 BL_LOG_ERR("Create device %s failed\n", dev_name);
189                 return 0;
190         }
191         return MKDEV(dminfo.major, dminfo.minor);
192 }
193
194 static int dm_device_remove_byname(const char *dev_name)
195 {
196         struct dm_task *dmt;
197         int ret = 0;
198
199         BL_LOG_INFO("%s: %s\n", __func__, dev_name);
200
201         dmt = dm_task_create(DM_DEVICE_REMOVE);
202         if (!dmt)
203                 return 0;
204
205         ret = dm_task_set_name(dmt, dev_name) && dm_task_run(dmt);
206
207         dm_task_update_nodes();
208         dm_task_destroy(dmt);
209
210         return ret;
211 }
212
213 int dm_device_remove(uint64_t dev)
214 {
215         struct dm_task *dmt;
216         struct dm_names *dmnames;
217         char *name = NULL;
218         int ret = 0;
219
220         /* Look for dev_name via dev, if dev_name could be transferred here,
221            we could jump to DM_DEVICE_REMOVE directly */
222
223         dmt = dm_task_create(DM_DEVICE_LIST);
224         if (!dmt) {
225                 BL_LOG_ERR("dm_task creation failed\n");
226                 goto out;
227         }
228
229         ret = dm_task_run(dmt);
230         if (!ret) {
231                 BL_LOG_ERR("dm_task_run failed\n");
232                 goto out;
233         }
234
235         dmnames = dm_task_get_names(dmt);
236         if (!dmnames || !dmnames->dev) {
237                 BL_LOG_ERR("dm_task_get_names failed\n");
238                 goto out;
239         }
240
241         while (dmnames) {
242                 if (dmnames->dev == dev) {
243                         name = strdup(dmnames->name);
244                         break;
245                 }
246                 dmnames = (void *)dmnames + dmnames->next;
247         }
248
249         if (!name) {
250                 BL_LOG_ERR("Could not find device\n");
251                 goto out;
252         }
253
254         dm_task_update_nodes();
255
256  out:
257         if (dmt)
258                 dm_task_destroy(dmt);
259
260         /* Start to remove device */
261         if (name) {
262                 ret = dm_device_remove_byname(name);
263                 free(name);
264         }
265
266         return ret;
267 }
268
269 static void dm_devicelist_remove(unsigned int start, unsigned int end)
270 {
271         char dev_name[DM_DEV_NAME_LEN];
272         unsigned int count;
273
274         if (start >= dev_count || end <= 1 || start >= end - 1)
275                 return;
276
277         for (count = end - 1; count > start; count--) {
278                 snprintf(dev_name, sizeof dev_name, dm_name, count - 1);
279                 dm_device_remove_byname(dev_name);
280         }
281
282         return;
283 }
284
285 static void bl_dm_remove_tree(uint64_t dev)
286 {
287         struct bl_dm_tree *p;
288
289         p = find_bl_dm_tree(dev);
290         if (!p)
291                 return;
292
293         dm_tree_free(p->tree);
294         del_from_bl_dm_tree(dev);
295 }
296
297 static int bl_dm_create_tree(uint64_t dev)
298 {
299         struct dm_tree *tree;
300         struct bl_dm_tree *bl_tree;
301
302         bl_tree = find_bl_dm_tree(dev);
303         if (bl_tree)
304                 return 1;
305
306         tree = dm_tree_create();
307         if (!tree)
308                 return 0;
309
310         if (!dm_tree_add_dev(tree, MAJOR(dev), MINOR(dev))) {
311                 dm_tree_free(tree);
312                 return 0;
313         }
314
315         bl_tree = malloc(sizeof(struct bl_dm_tree));
316         if (!bl_tree) {
317                 dm_tree_free(tree);
318                 return 0;
319         }
320
321         bl_tree->dev = dev;
322         bl_tree->tree = tree;
323         bl_tree->next = NULL;
324         add_to_bl_dm_tree(bl_tree);
325
326         return 1;
327 }
328
329 int dm_device_remove_all(uint64_t *dev)
330 {
331         struct bl_dm_tree *p;
332         struct dm_tree_node *node;
333         const char *uuid;
334         int ret = 0;
335         uint32_t major, minor;
336         uint64_t bl_dev;
337
338         memcpy(&major, dev, sizeof(uint32_t));
339         memcpy(&minor, (void *)dev + sizeof(uint32_t), sizeof(uint32_t));
340         bl_dev = MKDEV(major, minor);
341         p = find_bl_dm_tree(bl_dev);
342         if (!p)
343                 return ret;
344
345         node = dm_tree_find_node(p->tree, MAJOR(bl_dev), MINOR(bl_dev));
346         if (!node)
347                 return ret;
348
349         uuid = dm_tree_node_get_uuid(node);
350         if (!uuid)
351                 return ret;
352
353         dm_device_remove(bl_dev);
354         ret = dm_tree_deactivate_children(node, uuid, strlen(uuid));
355         dm_task_update_nodes();
356         bl_dm_remove_tree(bl_dev);
357
358         return ret;
359 }
360
361 static int dm_device_exists(char *dev_name)
362 {
363         char fullname[DM_DEV_NAME_LEN];
364
365         snprintf(fullname, sizeof fullname, "/dev/mapper/%s", dev_name);
366         return (access(fullname, F_OK) >= 0);
367 }
368
369 /* TODO: check the value for DM_DEV_NAME_LEN, DM_TYPE_LEN, DM_PARAMS_LEN */
370 uint64_t dm_device_create(struct bl_volume *vols, int num_vols)
371 {
372         uint64_t size, stripe_unit, dev = 0;
373         unsigned int count = dev_count;
374         int volnum, i, pos;
375         struct bl_volume *node;
376         char *tmp;
377         struct bl_dm_table *table = NULL;
378         struct bl_dm_table *bl_table_head = NULL;
379         unsigned int len;
380         char *dev_name = NULL;
381
382         /* Create pseudo device here */
383         for (volnum = 0; volnum < num_vols; volnum++) {
384                 node = &vols[volnum];
385                 switch (node->bv_type) {
386                 case BLOCK_VOLUME_SIMPLE:
387                         /* Do not need to create device here */
388                         dev = node->param.bv_dev;
389                         goto continued;
390                 case BLOCK_VOLUME_SLICE:
391                         table = bl_dm_table_alloc();
392                         if (!table)
393                                 goto out;
394                         table->offset = 0;
395                         table->size = node->bv_size;
396                         strcpy(table->target_type, "linear");
397                         if (!TYPE_HAS_DEV(node->bv_vols[0]->bv_type)) {
398                                 free(table);
399                                 goto out;
400                         }
401                         dev = node->bv_vols[0]->param.bv_dev;
402                         tmp = table->params;
403                         if (!dm_format_dev(tmp, DM_PARAMS_LEN,
404                                            MAJOR(dev), MINOR(dev))) {
405                                 free(table);
406                                 goto out;
407                         }
408                         tmp += strlen(tmp);
409                         sprintf(tmp, " %lu", node->param.bv_offset);
410                         add_to_bl_dm_table(&bl_table_head, table);
411                         break;
412                 case BLOCK_VOLUME_STRIPE:
413                         table = bl_dm_table_alloc();
414                         if (!table)
415                                 goto out;
416                         table->offset = 0;
417                         /* Truncate size to a stripe unit boundary */
418                         stripe_unit = node->param.bv_stripe_unit;
419                         table->size =
420                             node->bv_size - (node->bv_size % stripe_unit);
421                         strcpy(table->target_type, "striped");
422                         sprintf(table->params, "%d %llu %n", node->bv_vol_n,
423                                 (long long unsigned) stripe_unit, &pos);
424                         /* Copy subdev major:minor to params */
425                         tmp = table->params + pos;
426                         len = DM_PARAMS_LEN - pos;
427                         for (i = 0; i < node->bv_vol_n; i++) {
428                                 if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) {
429                                         free(table);
430                                         goto out;
431                                 }
432                                 dev = node->bv_vols[i]->param.bv_dev;
433                                 if (!dm_format_dev(tmp, len, MAJOR(dev),
434                                                    MINOR(dev))) {
435                                         free(table);
436                                         goto out;
437                                 }
438                                 pos = strlen(tmp);
439                                 tmp += pos;
440                                 len -= pos;
441                                 sprintf(tmp, " %d ", 0);
442                                 tmp += 3;
443                                 len -= 3;
444                         }
445                         add_to_bl_dm_table(&bl_table_head, table);
446                         break;
447                 case BLOCK_VOLUME_CONCAT:
448                         size = 0;
449                         for (i = 0; i < node->bv_vol_n; i++) {
450                                 table = bl_dm_table_alloc();
451                                 if (!table)
452                                         goto out;
453                                 table->offset = size;
454                                 table->size = node->bv_vols[i]->bv_size;
455                                 if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) {
456                                         free(table);
457                                         goto out;
458                                 }
459                                 strcpy(table->target_type, "linear");
460                                 tmp = table->params;
461                                 dev = node->bv_vols[i]->param.bv_dev;
462                                 if (!dm_format_dev(tmp, DM_PARAMS_LEN,
463                                                    MAJOR(dev), MINOR(dev))) {
464                                         free(table);
465                                         goto out;
466                                 }
467                                 tmp += strlen(tmp);
468                                 sprintf(tmp, " %d", 0);
469                                 size += table->size;
470                                 add_to_bl_dm_table(&bl_table_head, table);
471                         }
472                         break;
473                 default:
474                         /* Delete previous temporary devices */
475                         dm_devicelist_remove(count, dev_count);
476                         goto out;
477                 }               /* end of swtich */
478                 /* Create dev_name here. Name of device is pnfs_vol_XXX */
479                 if (dev_name)
480                         free(dev_name);
481                 dev_name = (char *)calloc(DM_DEV_NAME_LEN, sizeof(char));
482                 if (!dev_name) {
483                         BL_LOG_ERR("%s: Out of memory\n", __func__);
484                         goto out;
485                 }
486                 do {
487                         snprintf(dev_name, DM_DEV_NAME_LEN, dm_name,
488                                  dev_count++);
489                 } while (dm_device_exists(dev_name));
490
491                 dev = dm_device_create_mapped(dev_name, bl_table_head);
492                 BL_LOG_INFO("%s: %d %s %d:%d\n", __func__, volnum, dev_name,
493                             (int) MAJOR(dev), (int) MINOR(dev));
494                 if (!dev) {
495                         /* Delete previous temporary devices */
496                         dm_devicelist_remove(count, dev_count);
497                         goto out;
498                 }
499                 node->param.bv_dev = dev;
500                 /* TODO: extend use with PSEUDO later */
501                 node->bv_type = BLOCK_VOLUME_PSEUDO;
502
503  continued:
504                 if (bl_table_head)
505                         bl_dm_table_free(bl_table_head);
506                 bl_table_head = NULL;
507         }
508  out:
509         if (bl_table_head) {
510                 bl_dm_table_free(bl_table_head);
511                 bl_table_head = NULL;
512         }
513         if (dev)
514                 bl_dm_create_tree(dev);
515         if (dev_name)
516                 free(dev_name);
517         return dev;
518 }