libmultipath/foreign/nvme: use failover topology
[multipath-tools/.git] / libmultipath / foreign / nvme.c
1 /*
2   Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH
3
4   This program is free software; you can redistribute it and/or
5   modify it under the terms of the GNU General Public License
6   as published by the Free Software Foundation; either version 2
7   of the License, or (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include <sys/sysmacros.h>
19 #include <libudev.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
24 #include <libudev.h>
25 #include <pthread.h>
26 #include <limits.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include "util.h"
31 #include "vector.h"
32 #include "generic.h"
33 #include "foreign.h"
34 #include "debug.h"
35 #include "structs.h"
36 #include "sysfs.h"
37
38 static const char nvme_vendor[] = "NVMe";
39 static const char N_A[] = "n/a";
40 const char *THIS;
41
42 struct nvme_map;
43 struct nvme_pathgroup {
44         struct gen_pathgroup gen;
45         struct _vector pathvec;
46 };
47
48 struct nvme_path {
49         struct gen_path gen;
50         struct udev_device *udev;
51         struct udev_device *ctl;
52         struct nvme_map *map;
53         bool seen;
54         /*
55          * The kernel works in failover mode.
56          * Each path has a separate path group.
57          */
58         struct nvme_pathgroup pg;
59 };
60
61 struct nvme_map {
62         struct gen_multipath gen;
63         struct udev_device *udev;
64         struct udev_device *subsys;
65         dev_t devt;
66         struct _vector pgvec;
67         int nr_live;
68 };
69
70 #define NAME_LEN 64 /* buffer length for temp attributes */
71 #define const_gen_mp_to_nvme(g) ((const struct nvme_map*)(g))
72 #define gen_mp_to_nvme(g) ((struct nvme_map*)(g))
73 #define nvme_mp_to_gen(n) &((n)->gen)
74 #define const_gen_pg_to_nvme(g) ((const struct nvme_pathgroup*)(g))
75 #define gen_pg_to_nvme(g) ((struct nvme_pathgroup*)(g))
76 #define nvme_pg_to_gen(n) &((n)->gen)
77 #define const_gen_path_to_nvme(g) ((const struct nvme_path*)(g))
78 #define gen_path_to_nvme(g) ((struct nvme_path*)(g))
79 #define nvme_path_to_gen(n) &((n)->gen)
80 #define nvme_pg_to_path(x) (VECTOR_SLOT(&((x)->pathvec), 0))
81 #define nvme_path_to_pg(x) &((x)->pg)
82
83 static void cleanup_nvme_path(struct nvme_path *path)
84 {
85         condlog(5, "%s: %p %p", __func__, path, path->udev);
86         if (path->udev)
87                 udev_device_unref(path->udev);
88         vector_reset(&path->pg.pathvec);
89
90         /* ctl is implicitly referenced by udev, no need to unref */
91         free(path);
92 }
93
94 static void cleanup_nvme_map(struct nvme_map *map)
95 {
96         struct nvme_pathgroup *pg;
97         struct nvme_path *path;
98         int i;
99
100         vector_foreach_slot_backwards(&map->pgvec, pg, i) {
101                 path = nvme_pg_to_path(pg);
102                 condlog(5, "%s: %d %p", __func__, i, path);
103                 cleanup_nvme_path(path);
104                 vector_del_slot(&map->pgvec, i);
105         }
106         vector_reset(&map->pgvec);
107         if (map->udev)
108                 udev_device_unref(map->udev);
109         /* subsys is implicitly referenced by udev, no need to unref */
110         free(map);
111 }
112
113 static const struct _vector*
114 nvme_mp_get_pgs(const struct gen_multipath *gmp) {
115         const struct nvme_map *nvme = const_gen_mp_to_nvme(gmp);
116
117         /* This is all used under the lock, no need to copy */
118         return &nvme->pgvec;
119 }
120
121 static void
122 nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v)
123 {
124         /* empty */
125 }
126
127 static void rstrip(char *str)
128 {
129         int n;
130
131         for (n = strlen(str) - 1; n >= 0 && str[n] == ' '; n--);
132         str[n+1] = '\0';
133 }
134
135 static int snprint_nvme_map(const struct gen_multipath *gmp,
136                             char *buff, int len, char wildcard)
137 {
138         const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
139         char fld[NAME_LEN];
140         const char *val;
141
142         switch (wildcard) {
143         case 'd':
144                 return snprintf(buff, len, "%s",
145                                 udev_device_get_sysname(nvm->udev));
146         case 'n':
147                 return snprintf(buff, len, "%s:NQN:%s",
148                                 udev_device_get_sysname(nvm->subsys),
149                                 udev_device_get_sysattr_value(nvm->subsys,
150                                                               "subsysnqn"));
151         case 'w':
152                 return snprintf(buff, len, "%s",
153                                 udev_device_get_sysattr_value(nvm->udev,
154                                                               "wwid"));
155         case 'N':
156                 return snprintf(buff, len, "%u", nvm->nr_live);
157         case 'S':
158                 return snprintf(buff, len, "%s",
159                                 udev_device_get_sysattr_value(nvm->udev,
160                                                               "size"));
161         case 'v':
162                 return snprintf(buff, len, "%s", nvme_vendor);
163         case 's':
164         case 'p':
165                 snprintf(fld, sizeof(fld), "%s",
166                          udev_device_get_sysattr_value(nvm->subsys,
167                                                       "model"));
168                 rstrip(fld);
169                 if (wildcard == 'p')
170                         return snprintf(buff, len, "%s", fld);
171                 return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
172                                 udev_device_get_sysattr_value(nvm->subsys,
173                                                               "firmware_rev"));
174         case 'e':
175                 return snprintf(buff, len, "%s",
176                                 udev_device_get_sysattr_value(nvm->subsys,
177                                                               "firmware_rev"));
178         case 'r':
179                 val = udev_device_get_sysattr_value(nvm->udev, "ro");
180                 if (val[0] == 1)
181                         return snprintf(buff, len, "%s", "ro");
182                 else
183                         return snprintf(buff, len, "%s", "rw");
184         case 'G':
185                 return snprintf(buff, len, "%s", THIS);
186         default:
187                 return snprintf(buff, len, N_A);
188                 break;
189         }
190         return 0;
191 }
192
193 static const struct _vector*
194 nvme_pg_get_paths(const struct gen_pathgroup *gpg) {
195         const struct nvme_pathgroup *gp = const_gen_pg_to_nvme(gpg);
196
197         /* This is all used under the lock, no need to copy */
198         return &gp->pathvec;
199 }
200
201 static void
202 nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v)
203 {
204         /* empty */
205 }
206
207 static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
208                            char *buff, int len, char wildcard)
209 {
210         return snprintf(buff, len, N_A);
211 }
212
213 static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
214 {
215         unsigned int nvmeid, ctlid, nsid;
216         int rc;
217         const char *sysname = udev_device_get_sysname(np->udev);
218
219         rc = sscanf(sysname, "nvme%uc%un%u", &nvmeid, &ctlid, &nsid);
220         if (rc != 3) {
221                 condlog(1, "%s: failed to scan %s", __func__, sysname);
222                 rc = snprintf(buf, len, "(ERR:%s)", sysname);
223         } else
224                 rc = snprintf(buf, len, "%u:%u:%u", nvmeid, ctlid, nsid);
225         return (rc < len ? rc : len);
226 }
227
228 static int snprint_nvme_path(const struct gen_path *gp,
229                              char *buff, int len, char wildcard)
230 {
231         const struct nvme_path *np = const_gen_path_to_nvme(gp);
232         dev_t devt;
233         char fld[NAME_LEN];
234         struct udev_device *pci;
235
236         switch (wildcard) {
237         case 'w':
238                 return snprintf(buff, len, "%s",
239                                 udev_device_get_sysattr_value(np->udev,
240                                                               "wwid"));
241         case 'd':
242                 return snprintf(buff, len, "%s",
243                                 udev_device_get_sysname(np->udev));
244         case 'i':
245                 return snprint_hcil(np, buff, len);
246         case 'D':
247                 devt = udev_device_get_devnum(np->udev);
248                 return snprintf(buff, len, "%u:%u", major(devt), minor(devt));
249         case 'o':
250                 sysfs_attr_get_value(np->ctl, "state", fld, sizeof(fld));
251                 return snprintf(buff, len, "%s", fld);
252         case 's':
253                 snprintf(fld, sizeof(fld), "%s",
254                          udev_device_get_sysattr_value(np->ctl,
255                                                       "model"));
256                 rstrip(fld);
257                 return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
258                                 udev_device_get_sysattr_value(np->ctl,
259                                                               "firmware_rev"));
260         case 'S':
261                 return snprintf(buff, len, "%s",
262                         udev_device_get_sysattr_value(np->udev,
263                                                       "size"));
264         case 'z':
265                 return snprintf(buff, len, "%s",
266                                 udev_device_get_sysattr_value(np->ctl,
267                                                               "serial"));
268         case 'm':
269                 return snprintf(buff, len, "%s",
270                                 udev_device_get_sysname(np->map->udev));
271         case 'N':
272         case 'R':
273                 return snprintf(buff, len, "%s:%s",
274                         udev_device_get_sysattr_value(np->ctl,
275                                                       "transport"),
276                         udev_device_get_sysattr_value(np->ctl,
277                                                       "address"));
278         case 'G':
279                 return snprintf(buff, len, "[%s]", THIS);
280         case 'a':
281                 pci = udev_device_get_parent_with_subsystem_devtype(np->ctl,
282                                                                     "pci",
283                                                                     NULL);
284                 if (pci != NULL)
285                         return snprintf(buff, len, "PCI:%s",
286                                         udev_device_get_sysname(pci));
287                 /* fall through */
288         default:
289                 return snprintf(buff, len, "%s", N_A);
290                 break;
291         }
292         return 0;
293 }
294
295 static int nvme_style(const struct gen_multipath* gm,
296                       char *buf, int len, int verbosity)
297 {
298         int n = snprintf(buf, len, "%%w [%%G]:%%d %%s");
299
300         return (n < len ? n : len - 1);
301 }
302
303 static const struct gen_multipath_ops nvme_map_ops = {
304         .get_pathgroups = nvme_mp_get_pgs,
305         .rel_pathgroups = nvme_mp_rel_pgs,
306         .style = nvme_style,
307         .snprint = snprint_nvme_map,
308 };
309
310 static const struct gen_pathgroup_ops nvme_pg_ops __attribute__((unused)) = {
311         .get_paths = nvme_pg_get_paths,
312         .rel_paths = nvme_pg_rel_paths,
313         .snprint = snprint_nvme_pg,
314 };
315
316 static const struct gen_path_ops nvme_path_ops __attribute__((unused)) = {
317         .snprint = snprint_nvme_path,
318 };
319
320 struct context {
321         pthread_mutex_t mutex;
322         vector mpvec;
323         struct udev *udev;
324 };
325
326 void lock(struct context *ctx)
327 {
328         pthread_mutex_lock(&ctx->mutex);
329 }
330
331 void unlock(void *arg)
332 {
333         struct context *ctx = arg;
334
335         pthread_mutex_unlock(&ctx->mutex);
336 }
337
338 static int _delete_all(struct context *ctx)
339 {
340         struct nvme_map *nm;
341         int n = VECTOR_SIZE(ctx->mpvec), i;
342
343         if (n == 0)
344                 return FOREIGN_IGNORED;
345
346         vector_foreach_slot_backwards(ctx->mpvec, nm, i) {
347                 vector_del_slot(ctx->mpvec, i);
348                 cleanup_nvme_map(nm);
349         }
350         return FOREIGN_OK;
351 }
352
353 int delete_all(struct context *ctx)
354 {
355         int rc;
356
357         condlog(5, "%s called for \"%s\"", __func__, THIS);
358
359         lock(ctx);
360         pthread_cleanup_push(unlock, ctx);
361         rc = _delete_all(ctx);
362         pthread_cleanup_pop(1);
363
364         return rc;
365 }
366
367 void cleanup(struct context *ctx)
368 {
369         (void)delete_all(ctx);
370
371         lock(ctx);
372         /*
373          * Locking is not strictly necessary here, locking in foreign.c
374          * makes sure that no other code is called with this ctx any more.
375          * But this should make static checkers feel better.
376          */
377         pthread_cleanup_push(unlock, ctx);
378         if (ctx->udev)
379                 udev_unref(ctx->udev);
380         if (ctx->mpvec)
381                 vector_free(ctx->mpvec);
382         ctx->mpvec = NULL;
383         ctx->udev = NULL;
384         pthread_cleanup_pop(1);
385         pthread_mutex_destroy(&ctx->mutex);
386
387         free(ctx);
388 }
389
390 struct context *init(unsigned int api, const char *name)
391 {
392         struct context *ctx;
393
394         if (api > LIBMP_FOREIGN_API) {
395                 condlog(0, "%s: api version mismatch: %08x > %08x\n",
396                         __func__, api, LIBMP_FOREIGN_API);
397                 return NULL;
398         }
399
400         if ((ctx = calloc(1, sizeof(*ctx)))== NULL)
401                 return NULL;
402
403         pthread_mutex_init(&ctx->mutex, NULL);
404
405         ctx->udev = udev_new();
406         if (ctx->udev == NULL)
407                 goto err;
408
409         ctx->mpvec = vector_alloc();
410         if (ctx->mpvec == NULL)
411                 goto err;
412
413         THIS = name;
414         return ctx;
415 err:
416         cleanup(ctx);
417         return NULL;
418 }
419
420 static struct nvme_map *_find_nvme_map_by_devt(const struct context *ctx,
421                                               dev_t devt)
422 {
423         struct nvme_map *nm;
424         int i;
425
426         if (ctx->mpvec == NULL)
427                 return NULL;
428
429         vector_foreach_slot(ctx->mpvec, nm, i) {
430                 if (nm->devt == devt)
431                         return nm;
432         }
433
434         return NULL;
435 }
436
437 static struct nvme_path *
438 _find_path_by_syspath(struct nvme_map *map, const char *syspath)
439 {
440         struct nvme_pathgroup *pg;
441         char real[PATH_MAX];
442         const char *ppath;
443         int i;
444
445         ppath = realpath(syspath, real);
446         if (ppath == NULL) {
447                 condlog(1, "%s: %s: error in realpath", __func__, THIS);
448                 ppath = syspath;
449         }
450
451         vector_foreach_slot(&map->pgvec, pg, i) {
452                 struct nvme_path *path = nvme_pg_to_path(pg);
453
454                 if (!strcmp(ppath,
455                             udev_device_get_syspath(path->udev)))
456                         return path;
457         }
458         condlog(4, "%s: %s: %s not found", __func__, THIS, ppath);
459         return NULL;
460 }
461
462 static void _udev_device_unref(void *p)
463 {
464         udev_device_unref(p);
465 }
466
467 static void _udev_enumerate_unref(void *p)
468 {
469         udev_enumerate_unref(p);
470 }
471
472 static int _dirent_controller(const struct dirent *di)
473 {
474         static const char nvme_prefix[] = "nvme";
475         const char *p;
476
477 #ifdef _DIRENT_HAVE_D_TYPE
478         if (di->d_type != DT_LNK)
479                 return 0;
480 #endif
481         if (strncmp(di->d_name, nvme_prefix, sizeof(nvme_prefix) - 1))
482                 return 0;
483         p = di->d_name + sizeof(nvme_prefix) - 1;
484         if (*p == '\0' || !isdigit(*p))
485                 return 0;
486         for (++p; *p != '\0'; ++p)
487                 if (!isdigit(*p))
488                         return 0;
489         return 1;
490 }
491
492 /* Find the block device for a given nvme controller */
493 struct udev_device *get_ctrl_blkdev(const struct context *ctx,
494                                     struct udev_device *ctrl)
495 {
496         struct udev_list_entry *item;
497         struct udev_device *blkdev = NULL;
498         struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
499
500         if (enm == NULL)
501                 return NULL;
502
503         pthread_cleanup_push(_udev_enumerate_unref, enm);
504         if (udev_enumerate_add_match_parent(enm, ctrl) < 0)
505                 goto out;
506         if (udev_enumerate_add_match_subsystem(enm, "block"))
507                 goto out;
508
509         if (udev_enumerate_scan_devices(enm) < 0) {
510                 condlog(1, "%s: %s: error enumerating devices", __func__, THIS);
511                 goto out;
512         }
513
514         for (item = udev_enumerate_get_list_entry(enm);
515              item != NULL;
516              item = udev_list_entry_get_next(item)) {
517                 struct udev_device *tmp;
518
519                 tmp = udev_device_new_from_syspath(ctx->udev,
520                                            udev_list_entry_get_name(item));
521                 if (tmp == NULL)
522                         continue;
523                 if (!strcmp(udev_device_get_devtype(tmp), "disk")) {
524                         blkdev = tmp;
525                         break;
526                 } else
527                         udev_device_unref(tmp);
528         }
529
530         if (blkdev == NULL)
531                 condlog(1, "%s: %s: failed to get blockdev for %s",
532                         __func__, THIS, udev_device_get_sysname(ctrl));
533         else
534                 condlog(5, "%s: %s: got %s", __func__, THIS,
535                         udev_device_get_sysname(blkdev));
536 out:
537         pthread_cleanup_pop(1);
538         return blkdev;
539 }
540
541 static void _find_controllers(struct context *ctx, struct nvme_map *map)
542 {
543         char pathbuf[PATH_MAX], realbuf[PATH_MAX];
544         struct dirent **di = NULL;
545         struct scandir_result sr;
546         struct udev_device *subsys;
547         struct nvme_pathgroup *pg;
548         struct nvme_path *path;
549         int r, i, n;
550
551         if (map == NULL || map->udev == NULL)
552                 return;
553
554         vector_foreach_slot(&map->pgvec, pg, i) {
555                 path = nvme_pg_to_path(pg);
556                 path->seen = false;
557         }
558
559         subsys = udev_device_get_parent_with_subsystem_devtype(map->udev,
560                                                                "nvme-subsystem",
561                                                                NULL);
562         if (subsys == NULL) {
563                 condlog(1, "%s: %s: BUG: no NVME subsys for %s", __func__, THIS,
564                         udev_device_get_sysname(map->udev));
565                 return;
566         }
567
568         n = snprintf(pathbuf, sizeof(pathbuf), "%s",
569                      udev_device_get_syspath(subsys));
570         r = scandir(pathbuf, &di, _dirent_controller, alphasort);
571
572         if (r == 0) {
573                 condlog(3, "%s: %s: no controllers for %s", __func__, THIS,
574                         udev_device_get_sysname(map->udev));
575                 return;
576         } else if (r < 0) {
577                 condlog(1, "%s: %s: error %d scanning controllers of %s",
578                         __func__, THIS, errno,
579                         udev_device_get_sysname(map->udev));
580                 return;
581         }
582
583         sr.di = di;
584         sr.n = r;
585         pthread_cleanup_push_cast(free_scandir_result, &sr);
586         for (i = 0; i < r; i++) {
587                 char *fn = di[i]->d_name;
588                 struct udev_device *ctrl, *udev;
589
590                 if (snprintf(pathbuf + n, sizeof(pathbuf) - n, "/%s", fn)
591                     >= sizeof(pathbuf) - n)
592                         continue;
593                 if (realpath(pathbuf, realbuf) == NULL) {
594                         condlog(3, "%s: %s: realpath: %s", __func__, THIS,
595                                 strerror(errno));
596                         continue;
597                 }
598                 condlog(4, "%s: %s: found %s", __func__, THIS, realbuf);
599
600                 ctrl = udev_device_new_from_syspath(ctx->udev, realbuf);
601                 if (ctrl == NULL) {
602                         condlog(1, "%s: %s: failed to get udev device for %s",
603                                 __func__, THIS, realbuf);
604                         continue;
605                 }
606
607                 pthread_cleanup_push(_udev_device_unref, ctrl);
608                 udev = get_ctrl_blkdev(ctx, ctrl);
609                 /*
610                  * We give up the reference to the nvme device here and get
611                  * it back from the child below.
612                  * This way we don't need to worry about unreffing it.
613                  */
614                 pthread_cleanup_pop(1);
615
616                 if (udev == NULL)
617                         continue;
618
619                 path = _find_path_by_syspath(map,
620                                              udev_device_get_syspath(udev));
621                 if (path != NULL) {
622                         path->seen = true;
623                         condlog(4, "%s: %s already known",
624                                 __func__, fn);
625                         continue;
626                 }
627
628                 path = calloc(1, sizeof(*path));
629                 if (path == NULL)
630                         continue;
631
632                 path->gen.ops = &nvme_path_ops;
633                 path->udev = udev;
634                 path->seen = true;
635                 path->map = map;
636                 path->ctl = udev_device_get_parent_with_subsystem_devtype
637                         (udev, "nvme", NULL);
638                 if (path->ctl == NULL) {
639                         condlog(1, "%s: %s: failed to get controller for %s",
640                                 __func__, THIS, fn);
641                         cleanup_nvme_path(path);
642                         continue;
643                 }
644                 path->pg.gen.ops = &nvme_pg_ops;
645                 if (vector_alloc_slot(&path->pg.pathvec) == NULL) {
646                         cleanup_nvme_path(path);
647                         continue;
648                 }
649                 vector_set_slot(&path->pg.pathvec, path);
650                 if (vector_alloc_slot(&map->pgvec) == NULL) {
651                         cleanup_nvme_path(path);
652                         continue;
653                 }
654                 vector_set_slot(&map->pgvec, &path->pg);
655                 condlog(3, "%s: %s: new path %s added to %s",
656                         __func__, THIS, udev_device_get_sysname(udev),
657                         udev_device_get_sysname(map->udev));
658         }
659         pthread_cleanup_pop(1);
660
661         map->nr_live = 0;
662         vector_foreach_slot_backwards(&map->pgvec, pg, i) {
663                 path = nvme_pg_to_path(pg);
664                 if (!path->seen) {
665                         condlog(1, "path %d not found in %s any more",
666                                 i, udev_device_get_sysname(map->udev));
667                         vector_del_slot(&map->pgvec, i);
668                         cleanup_nvme_path(path);
669                 } else {
670                         static const char live_state[] = "live";
671                         char state[16];
672
673                         if ((sysfs_attr_get_value(path->ctl, "state", state,
674                                                   sizeof(state)) > 0) &&
675                             !strncmp(state, live_state, sizeof(live_state) - 1))
676                                 map->nr_live++;
677                 }
678         }
679         condlog(3, "%s: %s: map %s has %d/%d live paths", __func__, THIS,
680                 udev_device_get_sysname(map->udev), map->nr_live,
681                 VECTOR_SIZE(&map->pgvec));
682 }
683
684 static int _add_map(struct context *ctx, struct udev_device *ud,
685                     struct udev_device *subsys)
686 {
687         dev_t devt = udev_device_get_devnum(ud);
688         struct nvme_map *map;
689
690         if (_find_nvme_map_by_devt(ctx, devt) != NULL)
691                 return FOREIGN_OK;
692
693         map = calloc(1, sizeof(*map));
694         if (map == NULL)
695                 return FOREIGN_ERR;
696
697         map->devt = devt;
698         map->udev = udev_device_ref(ud);
699         /*
700          * subsys is implicitly referenced by map->udev,
701          * no need to take a reference here.
702          */
703         map->subsys = subsys;
704         map->gen.ops = &nvme_map_ops;
705
706         if (vector_alloc_slot(ctx->mpvec) == NULL) {
707                 cleanup_nvme_map(map);
708                 return FOREIGN_ERR;
709         }
710         vector_set_slot(ctx->mpvec, map);
711         _find_controllers(ctx, map);
712
713         return FOREIGN_CLAIMED;
714 }
715
716 int add(struct context *ctx, struct udev_device *ud)
717 {
718         struct udev_device *subsys;
719         int rc;
720
721         condlog(5, "%s called for \"%s\"", __func__, THIS);
722
723         if (ud == NULL)
724                 return FOREIGN_ERR;
725         if (strcmp("disk", udev_device_get_devtype(ud)))
726                 return FOREIGN_IGNORED;
727
728         subsys = udev_device_get_parent_with_subsystem_devtype(ud,
729                                                                "nvme-subsystem",
730                                                                NULL);
731         if (subsys == NULL)
732                 return FOREIGN_IGNORED;
733
734         lock(ctx);
735         pthread_cleanup_push(unlock, ctx);
736         rc = _add_map(ctx, ud, subsys);
737         pthread_cleanup_pop(1);
738
739         if (rc == FOREIGN_CLAIMED)
740                 condlog(3, "%s: %s: added map %s", __func__, THIS,
741                         udev_device_get_sysname(ud));
742         else if (rc != FOREIGN_OK)
743                 condlog(1, "%s: %s: retcode %d adding %s",
744                         __func__, THIS, rc, udev_device_get_sysname(ud));
745
746         return rc;
747 }
748
749 int change(struct context *ctx, struct udev_device *ud)
750 {
751         condlog(5, "%s called for \"%s\"", __func__, THIS);
752         return FOREIGN_IGNORED;
753 }
754
755 static int _delete_map(struct context *ctx, struct udev_device *ud)
756 {
757         int k;
758         struct nvme_map *map;
759         dev_t devt = udev_device_get_devnum(ud);
760
761         map = _find_nvme_map_by_devt(ctx, devt);
762         if (map ==NULL)
763                 return FOREIGN_IGNORED;
764
765         k = find_slot(ctx->mpvec, map);
766         if (k == -1)
767                 return FOREIGN_ERR;
768         else
769                 vector_del_slot(ctx->mpvec, k);
770
771         cleanup_nvme_map(map);
772
773         return FOREIGN_OK;
774 }
775
776 int delete(struct context *ctx, struct udev_device *ud)
777 {
778         int rc;
779
780         condlog(5, "%s called for \"%s\"", __func__, THIS);
781
782         if (ud == NULL)
783                 return FOREIGN_ERR;
784
785         lock(ctx);
786         pthread_cleanup_push(unlock, ctx);
787         rc = _delete_map(ctx, ud);
788         pthread_cleanup_pop(1);
789
790         if (rc == FOREIGN_OK)
791                 condlog(3, "%s: %s: map %s deleted", __func__, THIS,
792                         udev_device_get_sysname(ud));
793         else if (rc != FOREIGN_IGNORED)
794                 condlog(1, "%s: %s: retcode %d deleting map %s", __func__,
795                         THIS, rc, udev_device_get_sysname(ud));
796
797         return rc;
798 }
799
800 void _check(struct context *ctx)
801 {
802         struct gen_multipath *gm;
803         int i;
804
805         vector_foreach_slot(ctx->mpvec, gm, i) {
806                 struct nvme_map *map = gen_mp_to_nvme(gm);
807
808                 _find_controllers(ctx, map);
809         }
810 }
811
812 void check(struct context *ctx)
813 {
814         condlog(4, "%s called for \"%s\"", __func__, THIS);
815         lock(ctx);
816         pthread_cleanup_push(unlock, ctx);
817         _check(ctx);
818         pthread_cleanup_pop(1);
819         return;
820 }
821
822 /*
823  * It's safe to pass our internal pointer, this is only used under the lock.
824  */
825 const struct _vector *get_multipaths(const struct context *ctx)
826 {
827         condlog(5, "%s called for \"%s\"", __func__, THIS);
828         return ctx->mpvec;
829 }
830
831 void release_multipaths(const struct context *ctx, const struct _vector *mpvec)
832 {
833         condlog(5, "%s called for \"%s\"", __func__, THIS);
834         /* NOP */
835 }
836
837 /*
838  * It's safe to pass our internal pointer, this is only used under the lock.
839  */
840 const struct _vector * get_paths(const struct context *ctx)
841 {
842         vector paths = NULL;
843         const struct gen_multipath *gm;
844         int i;
845
846         condlog(5, "%s called for \"%s\"", __func__, THIS);
847         vector_foreach_slot(ctx->mpvec, gm, i) {
848                 const struct nvme_map *nm = const_gen_mp_to_nvme(gm);
849                 paths = vector_convert(paths, &nm->pgvec,
850                                        struct nvme_pathgroup, nvme_pg_to_path);
851         }
852         return paths;
853 }
854
855 void release_paths(const struct context *ctx, const struct _vector *mpvec)
856 {
857         condlog(5, "%s called for \"%s\"", __func__, THIS);
858         vector_free_const(mpvec);
859 }
860
861 /* compile-time check whether all methods are present and correctly typed */
862 #define _METHOD_INIT(x) .x = x
863 static struct foreign __methods __attribute__((unused)) = {
864         _METHOD_INIT(init),
865         _METHOD_INIT(cleanup),
866         _METHOD_INIT(change),
867         _METHOD_INIT(delete),
868         _METHOD_INIT(delete_all),
869         _METHOD_INIT(check),
870         _METHOD_INIT(lock),
871         _METHOD_INIT(unlock),
872         _METHOD_INIT(get_multipaths),
873         _METHOD_INIT(release_multipaths),
874         _METHOD_INIT(get_paths),
875         _METHOD_INIT(release_paths),
876 };