2 Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 #include <sys/sysmacros.h>
38 static const char nvme_vendor[] = "NVMe";
39 static const char N_A[] = "n/a";
45 struct udev_device *udev;
46 struct udev_device *ctl;
51 struct nvme_pathgroup {
52 struct gen_pathgroup gen;
57 struct gen_multipath gen;
58 struct udev_device *udev;
59 struct udev_device *subsys;
61 /* Just one static pathgroup for NVMe for now */
62 struct nvme_pathgroup pg;
63 struct gen_pathgroup *gpg;
69 #define NAME_LEN 64 /* buffer length for temp attributes */
70 #define const_gen_mp_to_nvme(g) ((const struct nvme_map*)(g))
71 #define gen_mp_to_nvme(g) ((struct nvme_map*)(g))
72 #define nvme_mp_to_gen(n) &((n)->gen)
73 #define const_gen_pg_to_nvme(g) ((const struct nvme_pathgroup*)(g))
74 #define gen_pg_to_nvme(g) ((struct nvme_pathgroup*)(g))
75 #define nvme_pg_to_gen(n) &((n)->gen)
76 #define const_gen_path_to_nvme(g) ((const struct nvme_path*)(g))
77 #define gen_path_to_nvme(g) ((struct nvme_path*)(g))
78 #define nvme_path_to_gen(n) &((n)->gen)
80 static void cleanup_nvme_path(struct nvme_path *path)
82 condlog(5, "%s: %p %p", __func__, path, path->udev);
84 udev_device_unref(path->udev);
85 /* ctl is implicitly referenced by udev, no need to unref */
89 static void cleanup_nvme_map(struct nvme_map *map)
92 struct nvme_path *path;
95 vector_foreach_slot_backwards(map->pathvec, path, i) {
96 condlog(5, "%s: %d %p", __func__, i, path);
97 cleanup_nvme_path(path);
98 vector_del_slot(map->pathvec, i);
101 vector_free(map->pathvec);
103 udev_device_unref(map->udev);
104 /* subsys is implicitly referenced by udev, no need to unref */
108 static const struct _vector*
109 nvme_mp_get_pgs(const struct gen_multipath *gmp) {
110 const struct nvme_map *nvme = const_gen_mp_to_nvme(gmp);
112 /* This is all used under the lock, no need to copy */
117 nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v)
122 static void rstrip(char *str)
126 for (n = strlen(str) - 1; n >= 0 && str[n] == ' '; n--);
130 static int snprint_nvme_map(const struct gen_multipath *gmp,
131 char *buff, int len, char wildcard)
133 const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
139 return snprintf(buff, len, "%s",
140 udev_device_get_sysname(nvm->udev));
142 return snprintf(buff, len, "%s:NQN:%s",
143 udev_device_get_sysname(nvm->subsys),
144 udev_device_get_sysattr_value(nvm->subsys,
147 return snprintf(buff, len, "%s",
148 udev_device_get_sysattr_value(nvm->udev,
151 return snprintf(buff, len, "%u", nvm->nr_live);
153 return snprintf(buff, len, "%s",
154 udev_device_get_sysattr_value(nvm->udev,
157 return snprintf(buff, len, "%s", nvme_vendor);
160 snprintf(fld, sizeof(fld), "%s",
161 udev_device_get_sysattr_value(nvm->subsys,
165 return snprintf(buff, len, "%s", fld);
166 return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
167 udev_device_get_sysattr_value(nvm->subsys,
170 return snprintf(buff, len, "%s",
171 udev_device_get_sysattr_value(nvm->subsys,
174 val = udev_device_get_sysattr_value(nvm->udev, "ro");
176 return snprintf(buff, len, "%s", "ro");
178 return snprintf(buff, len, "%s", "rw");
180 return snprintf(buff, len, "%s", THIS);
182 return snprintf(buff, len, N_A);
188 static const struct _vector*
189 nvme_pg_get_paths(const struct gen_pathgroup *gpg) {
190 const struct nvme_pathgroup *gp = const_gen_pg_to_nvme(gpg);
192 /* This is all used under the lock, no need to copy */
197 nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v)
202 static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
203 char *buff, int len, char wildcard)
205 return snprintf(buff, len, N_A);
208 static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
210 unsigned int nvmeid, ctlid, nsid;
212 const char *sysname = udev_device_get_sysname(np->udev);
214 rc = sscanf(sysname, "nvme%uc%un%u", &nvmeid, &ctlid, &nsid);
216 condlog(1, "%s: failed to scan %s", __func__, sysname);
217 rc = snprintf(buf, len, "(ERR:%s)", sysname);
219 rc = snprintf(buf, len, "%u:%u:%u", nvmeid, ctlid, nsid);
220 return (rc < len ? rc : len);
223 static int snprint_nvme_path(const struct gen_path *gp,
224 char *buff, int len, char wildcard)
226 const struct nvme_path *np = const_gen_path_to_nvme(gp);
229 struct udev_device *pci;
233 return snprintf(buff, len, "%s",
234 udev_device_get_sysattr_value(np->udev,
237 return snprintf(buff, len, "%s",
238 udev_device_get_sysname(np->udev));
240 return snprint_hcil(np, buff, len);
242 devt = udev_device_get_devnum(np->udev);
243 return snprintf(buff, len, "%u:%u", major(devt), minor(devt));
245 sysfs_attr_get_value(np->ctl, "state", fld, sizeof(fld));
246 return snprintf(buff, len, "%s", fld);
248 snprintf(fld, sizeof(fld), "%s",
249 udev_device_get_sysattr_value(np->ctl,
252 return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
253 udev_device_get_sysattr_value(np->ctl,
256 return snprintf(buff, len, "%s",
257 udev_device_get_sysattr_value(np->udev,
260 return snprintf(buff, len, "%s",
261 udev_device_get_sysattr_value(np->ctl,
264 return snprintf(buff, len, "%s",
265 udev_device_get_sysname(np->map->udev));
268 return snprintf(buff, len, "%s:%s",
269 udev_device_get_sysattr_value(np->ctl,
271 udev_device_get_sysattr_value(np->ctl,
274 return snprintf(buff, len, "[%s]", THIS);
276 pci = udev_device_get_parent_with_subsystem_devtype(np->ctl,
280 return snprintf(buff, len, "PCI:%s",
281 udev_device_get_sysname(pci));
284 return snprintf(buff, len, "%s", N_A);
290 static const struct gen_multipath_ops nvme_map_ops = {
291 .get_pathgroups = nvme_mp_get_pgs,
292 .rel_pathgroups = nvme_mp_rel_pgs,
293 .style = generic_style,
294 .snprint = snprint_nvme_map,
297 static const struct gen_pathgroup_ops nvme_pg_ops __attribute__((unused)) = {
298 .get_paths = nvme_pg_get_paths,
299 .rel_paths = nvme_pg_rel_paths,
300 .snprint = snprint_nvme_pg,
303 static const struct gen_path_ops nvme_path_ops __attribute__((unused)) = {
304 .snprint = snprint_nvme_path,
308 pthread_mutex_t mutex;
313 void lock(struct context *ctx)
315 pthread_mutex_lock(&ctx->mutex);
318 void unlock(void *arg)
320 struct context *ctx = arg;
322 pthread_mutex_unlock(&ctx->mutex);
325 static int _delete_all(struct context *ctx)
328 int n = VECTOR_SIZE(ctx->mpvec), i;
331 return FOREIGN_IGNORED;
333 vector_foreach_slot_backwards(ctx->mpvec, nm, i) {
334 vector_del_slot(ctx->mpvec, i);
335 cleanup_nvme_map(nm);
340 int delete_all(struct context *ctx)
344 condlog(5, "%s called for \"%s\"", __func__, THIS);
347 pthread_cleanup_push(unlock, ctx);
348 rc = _delete_all(ctx);
349 pthread_cleanup_pop(1);
354 void cleanup(struct context *ctx)
356 (void)delete_all(ctx);
360 * Locking is not strictly necessary here, locking in foreign.c
361 * makes sure that no other code is called with this ctx any more.
362 * But this should make static checkers feel better.
364 pthread_cleanup_push(unlock, ctx);
366 udev_unref(ctx->udev);
368 vector_free(ctx->mpvec);
371 pthread_cleanup_pop(1);
372 pthread_mutex_destroy(&ctx->mutex);
377 struct context *init(unsigned int api, const char *name)
381 if (api > LIBMP_FOREIGN_API) {
382 condlog(0, "%s: api version mismatch: %08x > %08x\n",
383 __func__, api, LIBMP_FOREIGN_API);
387 if ((ctx = calloc(1, sizeof(*ctx)))== NULL)
390 pthread_mutex_init(&ctx->mutex, NULL);
392 ctx->udev = udev_new();
393 if (ctx->udev == NULL)
396 ctx->mpvec = vector_alloc();
397 if (ctx->mpvec == NULL)
407 static struct nvme_map *_find_nvme_map_by_devt(const struct context *ctx,
413 if (ctx->mpvec == NULL)
416 vector_foreach_slot(ctx->mpvec, nm, i) {
417 if (nm->devt == devt)
424 static struct nvme_path *
425 _find_path_by_syspath(struct nvme_map *map, const char *syspath)
427 struct nvme_path *path;
432 ppath = realpath(syspath, real);
434 condlog(1, "%s: %s: error in realpath", __func__, THIS);
438 vector_foreach_slot(map->pathvec, path, i) {
440 udev_device_get_syspath(path->udev)))
443 condlog(4, "%s: %s: %s not found", __func__, THIS, ppath);
447 static int no_dotfiles(const struct dirent *di)
449 return di->d_name[0] != '.';
452 static void _find_slaves(struct context *ctx, struct nvme_map *map)
454 char pathbuf[PATH_MAX];
455 struct dirent **di = NULL;
456 struct nvme_path *path;
459 if (map == NULL || map->udev == NULL)
462 vector_foreach_slot(map->pathvec, path, i)
465 snprintf(pathbuf, sizeof(pathbuf),
467 udev_device_get_syspath(map->udev));
469 r = scandir(pathbuf, &di, no_dotfiles, alphasort);
472 condlog(3, "%s: %s: no paths for %s", __func__, THIS,
473 udev_device_get_sysname(map->udev));
476 condlog(1, "%s: %s: error %d scanning paths of %s", __func__,
477 THIS, errno, udev_device_get_sysname(map->udev));
481 pthread_cleanup_push(free, di);
482 for (i = 0; i < r; i++) {
483 char *fn = di[i]->d_name;
484 struct udev_device *udev;
486 if (snprintf(pathbuf, sizeof(pathbuf), "%s/slaves/%s",
487 udev_device_get_syspath(map->udev), fn)
491 path = _find_path_by_syspath(map, pathbuf);
494 condlog(4, "%s: %s already known",
499 udev = udev_device_new_from_syspath(ctx->udev, pathbuf);
501 condlog(1, "%s: %s: failed to get udev device for %s",
506 path = calloc(1, sizeof(*path));
510 path->gen.ops = &nvme_path_ops;
514 path->ctl = udev_device_get_parent_with_subsystem_devtype
515 (udev, "nvme", NULL);
516 if (path->ctl == NULL) {
517 condlog(1, "%s: %s: failed to get controller for %s",
519 cleanup_nvme_path(path);
523 if (vector_alloc_slot(map->pathvec) == NULL) {
524 cleanup_nvme_path(path);
527 condlog(3, "%s: %s: new path %s added to %s",
528 __func__, THIS, udev_device_get_sysname(udev),
529 udev_device_get_sysname(map->udev));
530 vector_set_slot(map->pathvec, path);
532 pthread_cleanup_pop(1);
535 vector_foreach_slot_backwards(map->pathvec, path, i) {
537 condlog(1, "path %d not found in %s any more",
538 i, udev_device_get_sysname(map->udev));
539 vector_del_slot(map->pathvec, i);
540 cleanup_nvme_path(path);
542 static const char live_state[] = "live";
545 if ((sysfs_attr_get_value(path->ctl, "state", state,
546 sizeof(state)) > 0) &&
547 !strncmp(state, live_state, sizeof(live_state) - 1))
551 condlog(3, "%s: %s: map %s has %d/%d live paths", __func__, THIS,
552 udev_device_get_sysname(map->udev), map->nr_live,
553 VECTOR_SIZE(map->pathvec));
556 static int _add_map(struct context *ctx, struct udev_device *ud,
557 struct udev_device *subsys)
559 dev_t devt = udev_device_get_devnum(ud);
560 struct nvme_map *map;
562 if (_find_nvme_map_by_devt(ctx, devt) != NULL)
565 map = calloc(1, sizeof(*map));
570 map->udev = udev_device_ref(ud);
572 * subsys is implicitly referenced by map->udev,
573 * no need to take a reference here.
575 map->subsys = subsys;
576 map->gen.ops = &nvme_map_ops;
578 map->pathvec = vector_alloc();
579 if (map->pathvec == NULL) {
580 cleanup_nvme_map(map);
584 map->pg.gen.ops = &nvme_pg_ops;
585 map->pg.pathvec = map->pathvec;
586 map->gpg = nvme_pg_to_gen(&map->pg);
588 map->pgvec.allocated = 1;
589 map->pgvec.slot = (void**)&map->gpg;
591 if (vector_alloc_slot(ctx->mpvec) == NULL) {
592 cleanup_nvme_map(map);
595 vector_set_slot(ctx->mpvec, map);
596 _find_slaves(ctx, map);
598 return FOREIGN_CLAIMED;
601 int add(struct context *ctx, struct udev_device *ud)
603 struct udev_device *subsys;
606 condlog(5, "%s called for \"%s\"", __func__, THIS);
610 if (strcmp("disk", udev_device_get_devtype(ud)))
611 return FOREIGN_IGNORED;
613 subsys = udev_device_get_parent_with_subsystem_devtype(ud,
617 return FOREIGN_IGNORED;
620 pthread_cleanup_push(unlock, ctx);
621 rc = _add_map(ctx, ud, subsys);
622 pthread_cleanup_pop(1);
624 if (rc == FOREIGN_CLAIMED)
625 condlog(3, "%s: %s: added map %s", __func__, THIS,
626 udev_device_get_sysname(ud));
627 else if (rc != FOREIGN_OK)
628 condlog(1, "%s: %s: retcode %d adding %s",
629 __func__, THIS, rc, udev_device_get_sysname(ud));
634 int change(struct context *ctx, struct udev_device *ud)
636 condlog(5, "%s called for \"%s\"", __func__, THIS);
637 return FOREIGN_IGNORED;
640 static int _delete_map(struct context *ctx, struct udev_device *ud)
643 struct nvme_map *map;
644 dev_t devt = udev_device_get_devnum(ud);
646 map = _find_nvme_map_by_devt(ctx, devt);
648 return FOREIGN_IGNORED;
650 k = find_slot(ctx->mpvec, map);
654 vector_del_slot(ctx->mpvec, k);
656 cleanup_nvme_map(map);
661 int delete(struct context *ctx, struct udev_device *ud)
665 condlog(5, "%s called for \"%s\"", __func__, THIS);
671 pthread_cleanup_push(unlock, ctx);
672 rc = _delete_map(ctx, ud);
673 pthread_cleanup_pop(1);
675 if (rc == FOREIGN_OK)
676 condlog(3, "%s: %s: map %s deleted", __func__, THIS,
677 udev_device_get_sysname(ud));
678 else if (rc != FOREIGN_IGNORED)
679 condlog(1, "%s: %s: retcode %d deleting map %s", __func__,
680 THIS, rc, udev_device_get_sysname(ud));
685 void _check(struct context *ctx)
687 struct gen_multipath *gm;
690 vector_foreach_slot(ctx->mpvec, gm, i) {
691 struct nvme_map *map = gen_mp_to_nvme(gm);
693 _find_slaves(ctx, map);
697 void check(struct context *ctx)
699 condlog(4, "%s called for \"%s\"", __func__, THIS);
701 pthread_cleanup_push(unlock, ctx);
703 pthread_cleanup_pop(1);
708 * It's safe to pass our internal pointer, this is only used under the lock.
710 const struct _vector *get_multipaths(const struct context *ctx)
712 condlog(5, "%s called for \"%s\"", __func__, THIS);
716 void release_multipaths(const struct context *ctx, const struct _vector *mpvec)
718 condlog(5, "%s called for \"%s\"", __func__, THIS);
723 * It's safe to pass our internal pointer, this is only used under the lock.
725 const struct _vector * get_paths(const struct context *ctx)
728 const struct gen_multipath *gm;
731 condlog(5, "%s called for \"%s\"", __func__, THIS);
732 vector_foreach_slot(ctx->mpvec, gm, i) {
733 const struct nvme_map *nm = const_gen_mp_to_nvme(gm);
734 paths = vector_convert(paths, nm->pathvec,
735 struct gen_path, identity);
740 void release_paths(const struct context *ctx, const struct _vector *mpvec)
742 condlog(5, "%s called for \"%s\"", __func__, THIS);
743 vector_free_const(mpvec);
746 /* compile-time check whether all methods are present and correctly typed */
747 #define _METHOD_INIT(x) .x = x
748 static struct foreign __methods __attribute__((unused)) = {
750 _METHOD_INIT(cleanup),
751 _METHOD_INIT(change),
752 _METHOD_INIT(delete),
753 _METHOD_INIT(delete_all),
756 _METHOD_INIT(unlock),
757 _METHOD_INIT(get_multipaths),
758 _METHOD_INIT(release_multipaths),
759 _METHOD_INIT(get_paths),
760 _METHOD_INIT(release_paths),