multipathd: use foreign API
[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, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
17   USA.
18 */
19
20 #include <sys/sysmacros.h>
21 #include <libudev.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <libudev.h>
27 #include <pthread.h>
28 #include "vector.h"
29 #include "generic.h"
30 #include "foreign.h"
31 #include "debug.h"
32
33 const char *THIS;
34
35 struct nvme_map {
36         struct gen_multipath gen;
37         struct udev_device *udev;
38         struct udev_device *subsys;
39         dev_t devt;
40 };
41
42 #define NAME_LEN 64 /* buffer length temp model name */
43 #define const_gen_mp_to_nvme(g) ((const struct nvme_map*)(g))
44 #define gen_mp_to_nvme(g) ((struct nvme_map*)(g))
45 #define nvme_mp_to_gen(n) &((n)->gen)
46
47 static void cleanup_nvme_map(struct nvme_map *map)
48 {
49         if (map->udev)
50                 udev_device_unref(map->udev);
51         if (map->subsys)
52                 udev_device_unref(map->subsys);
53         free(map);
54 }
55
56 static const struct _vector*
57 nvme_mp_get_pgs(const struct gen_multipath *gmp) {
58         return NULL;
59 }
60
61 static void
62 nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v)
63 {
64 }
65
66 static void rstrip(char *str)
67 {
68         int n;
69
70         for (n = strlen(str) - 1; n >= 0 && str[n] == ' '; n--);
71         str[n+1] = '\0';
72 }
73
74 static int snprint_nvme_map(const struct gen_multipath *gmp,
75                             char *buff, int len, char wildcard)
76 {
77         const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
78         static const char nvme_vendor[] = "NVMe";
79         char fld[NAME_LEN];
80         const char *val;
81
82         switch (wildcard) {
83         case 'd':
84                 return snprintf(buff, len, "%s",
85                                 udev_device_get_sysname(nvm->udev));
86         case 'n':
87                 return snprintf(buff, len, "%s:NQN:%s",
88                                 udev_device_get_sysname(nvm->subsys),
89                                 udev_device_get_sysattr_value(nvm->subsys,
90                                                               "subsysnqn"));
91         case 'w':
92                 return snprintf(buff, len, "%s",
93                                 udev_device_get_sysattr_value(nvm->udev,
94                                                               "wwid"));
95         case 'S':
96                 return snprintf(buff, len, "%s",
97                                 udev_device_get_sysattr_value(nvm->udev,
98                                                               "size"));
99         case 'v':
100                 return snprintf(buff, len, "%s", nvme_vendor);
101         case 's':
102         case 'p':
103                 snprintf(fld, sizeof(fld), "%s",
104                          udev_device_get_sysattr_value(nvm->subsys,
105                                                       "model"));
106                 rstrip(fld);
107                 if (wildcard == 'p')
108                         return snprintf(buff, len, "%s", fld);
109                 return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
110                                 udev_device_get_sysattr_value(nvm->subsys,
111                                                               "firmware_rev"));
112         case 'e':
113                 return snprintf(buff, len, "%s",
114                                 udev_device_get_sysattr_value(nvm->subsys,
115                                                               "firmware_rev"));
116         case 'r':
117                 val = udev_device_get_sysattr_value(nvm->udev, "ro");
118                 if (val[0] == 1)
119                         return snprintf(buff, len, "%s", "ro");
120                 else
121                         return snprintf(buff, len, "%s", "rw");
122         case 'G':
123                 return snprintf(buff, len, "%s", THIS);
124         default:
125                 return snprintf(buff, len, "N/A");
126                 break;
127         }
128         return 0;
129 }
130
131 static const struct _vector*
132 nvme_pg_get_paths(const struct gen_pathgroup *gpg) {
133         return NULL;
134 }
135
136 static void
137 nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v)
138 {
139 }
140
141 static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
142                            char *buff, int len, char wildcard)
143 {
144         return 0;
145 }
146
147 static int snprint_nvme_path(const struct gen_path *gmp,
148                              char *buff, int len, char wildcard)
149 {
150         switch (wildcard) {
151         case 'R':
152                 return snprintf(buff, len, "[foreign: %s]", THIS);
153         default:
154                 break;
155         }
156         return 0;
157 }
158
159 static const struct gen_multipath_ops nvme_map_ops = {
160         .get_pathgroups = nvme_mp_get_pgs,
161         .rel_pathgroups = nvme_mp_rel_pgs,
162         .style = generic_style,
163         .snprint = snprint_nvme_map,
164 };
165
166 static const struct gen_pathgroup_ops nvme_pg_ops __attribute__((unused)) = {
167         .get_paths = nvme_pg_get_paths,
168         .rel_paths = nvme_pg_rel_paths,
169         .snprint = snprint_nvme_pg,
170 };
171
172 static const struct gen_path_ops nvme_path_ops __attribute__((unused)) = {
173         .snprint = snprint_nvme_path,
174 };
175
176 struct context {
177         pthread_mutex_t mutex;
178         vector mpvec;
179 };
180
181 void lock(struct context *ctx)
182 {
183         pthread_mutex_lock(&ctx->mutex);
184 }
185
186 void unlock(void *arg)
187 {
188         struct context *ctx = arg;
189
190         pthread_mutex_unlock(&ctx->mutex);
191 }
192
193 static int _delete_all(struct context *ctx)
194 {
195         struct nvme_map *nm;
196         int n = VECTOR_SIZE(ctx->mpvec), i;
197
198         if (n == 0)
199                 return FOREIGN_IGNORED;
200
201         vector_foreach_slot_backwards(ctx->mpvec, nm, i) {
202                 vector_del_slot(ctx->mpvec, i);
203                 cleanup_nvme_map(nm);
204         }
205         return FOREIGN_OK;
206 }
207
208 int delete_all(struct context *ctx)
209 {
210         int rc;
211
212         condlog(5, "%s called for \"%s\"", __func__, THIS);
213
214         lock(ctx);
215         pthread_cleanup_push(unlock, ctx);
216         rc = _delete_all(ctx);
217         pthread_cleanup_pop(1);
218
219         return rc;
220 }
221
222 void cleanup(struct context *ctx)
223 {
224         (void)delete_all(ctx);
225
226         lock(ctx);
227         /*
228          * Locking is not strictly necessary here, locking in foreign.c
229          * makes sure that no other code is called with this ctx any more.
230          * But this should make static checkers feel better.
231          */
232         pthread_cleanup_push(unlock, ctx);
233         if (ctx->udev)
234                 udev_unref(ctx->udev);
235         if (ctx->mpvec)
236                 vector_free(ctx->mpvec);
237         ctx->mpvec = NULL;
238         ctx->udev = NULL;
239         pthread_cleanup_pop(1);
240         pthread_mutex_destroy(&ctx->mutex);
241
242         free(ctx);
243 }
244
245 struct context *init(unsigned int api, const char *name)
246 {
247         struct context *ctx;
248
249         if (api > LIBMP_FOREIGN_API) {
250                 condlog(0, "%s: api version mismatch: %08x > %08x\n",
251                         __func__, api, LIBMP_FOREIGN_API);
252                 return NULL;
253         }
254
255         if ((ctx = calloc(1, sizeof(*ctx)))== NULL)
256                 return NULL;
257
258         pthread_mutex_init(&ctx->mutex, NULL);
259
260         ctx->mpvec = vector_alloc();
261         if (ctx->mpvec == NULL)
262                 goto err;
263
264         THIS = name;
265         return ctx;
266 err:
267         cleanup(ctx);
268         return NULL;
269 }
270
271 static struct nvme_map *_find_nvme_map_by_devt(const struct context *ctx,
272                                               dev_t devt)
273 {
274         struct nvme_map *nm;
275         int i;
276
277         if (ctx->mpvec == NULL)
278                 return NULL;
279
280         vector_foreach_slot(ctx->mpvec, nm, i) {
281                 if (nm->devt == devt)
282                         return nm;
283         }
284
285         return NULL;
286 }
287
288 static int _add_map(struct context *ctx, struct udev_device *ud,
289                     struct udev_device *subsys)
290 {
291         dev_t devt = udev_device_get_devnum(ud);
292         struct nvme_map *map;
293
294         if (_find_nvme_map_by_devt(ctx, devt) != NULL)
295                 return FOREIGN_OK;
296
297         map = calloc(1, sizeof(*map));
298         if (map == NULL)
299                 return FOREIGN_ERR;
300
301         map->devt = devt;
302         map->udev = udev_device_ref(ud);
303         /*
304          * subsys is implicitly referenced by map->udev,
305          * no need to take a reference here.
306          */
307         map->subsys = subsys;
308         map->gen.ops = &nvme_map_ops;
309
310         if (vector_alloc_slot(ctx->mpvec) == NULL) {
311                 cleanup_nvme_map(map);
312                 return FOREIGN_ERR;
313         }
314
315         vector_set_slot(ctx->mpvec, map);
316
317         return FOREIGN_CLAIMED;
318 }
319
320 int add(struct context *ctx, struct udev_device *ud)
321 {
322         struct udev_device *subsys;
323         int rc;
324
325         condlog(5, "%s called for \"%s\"", __func__, THIS);
326
327         if (ud == NULL)
328                 return FOREIGN_ERR;
329         if (strcmp("disk", udev_device_get_devtype(ud)))
330                 return FOREIGN_IGNORED;
331
332         subsys = udev_device_get_parent_with_subsystem_devtype(ud,
333                                                                "nvme-subsystem",
334                                                                NULL);
335         if (subsys == NULL)
336                 return FOREIGN_IGNORED;
337
338         lock(ctx);
339         pthread_cleanup_push(unlock, ctx);
340         rc = _add_map(ctx, ud, subsys);
341         pthread_cleanup_pop(1);
342
343         if (rc == FOREIGN_CLAIMED)
344                 condlog(3, "%s: %s: added map %s", __func__, THIS,
345                         udev_device_get_sysname(ud));
346         else if (rc != FOREIGN_OK)
347                 condlog(1, "%s: %s: retcode %d adding %s",
348                         __func__, THIS, rc, udev_device_get_sysname(ud));
349
350         return rc;
351 }
352
353 int change(struct context *ctx, struct udev_device *ud)
354 {
355         condlog(5, "%s called for \"%s\"", __func__, THIS);
356         return FOREIGN_IGNORED;
357 }
358
359 static int _delete_map(struct context *ctx, struct udev_device *ud)
360 {
361         int k;
362         struct nvme_map *map;
363         dev_t devt = udev_device_get_devnum(ud);
364
365         map = _find_nvme_map_by_devt(ctx, devt);
366         if (map ==NULL)
367                 return FOREIGN_IGNORED;
368
369         k = find_slot(ctx->mpvec, map);
370         if (k == -1)
371                 return FOREIGN_ERR;
372         else
373                 vector_del_slot(ctx->mpvec, k);
374
375         cleanup_nvme_map(map);
376
377         return FOREIGN_OK;
378 }
379
380 int delete(struct context *ctx, struct udev_device *ud)
381 {
382         int rc;
383
384         condlog(5, "%s called for \"%s\"", __func__, THIS);
385
386         if (ud == NULL)
387                 return FOREIGN_ERR;
388
389         lock(ctx);
390         pthread_cleanup_push(unlock, ctx);
391         rc = _delete_map(ctx, ud);
392         pthread_cleanup_pop(1);
393
394         if (rc == FOREIGN_OK)
395                 condlog(3, "%s: %s: map %s deleted", __func__, THIS,
396                         udev_device_get_sysname(ud));
397         else if (rc != FOREIGN_IGNORED)
398                 condlog(1, "%s: %s: retcode %d deleting map %s", __func__,
399                         THIS, rc, udev_device_get_sysname(ud));
400
401         return rc;
402 }
403
404 void check(struct context *ctx)
405 {
406         condlog(5, "%s called for \"%s\"", __func__, THIS);
407         return;
408 }
409
410 /*
411  * It's safe to pass our internal pointer, this is only used under the lock.
412  */
413 const struct _vector *get_multipaths(const struct context *ctx)
414 {
415         condlog(5, "%s called for \"%s\"", __func__, THIS);
416         return ctx->mpvec;
417 }
418
419 void release_multipaths(const struct context *ctx, const struct _vector *mpvec)
420 {
421         condlog(5, "%s called for \"%s\"", __func__, THIS);
422         /* NOP */
423 }
424
425 /*
426  * It's safe to pass our internal pointer, this is only used under the lock.
427  */
428 const struct _vector * get_paths(const struct context *ctx)
429 {
430         condlog(5, "%s called for \"%s\"", __func__, THIS);
431         return NULL;
432 }
433
434 void release_paths(const struct context *ctx, const struct _vector *mpvec)
435 {
436         condlog(5, "%s called for \"%s\"", __func__, THIS);
437         /* NOP */
438 }
439
440 /* compile-time check whether all methods are present and correctly typed */
441 #define _METHOD_INIT(x) .x = x
442 static struct foreign __methods __attribute__((unused)) = {
443         _METHOD_INIT(init),
444         _METHOD_INIT(cleanup),
445         _METHOD_INIT(change),
446         _METHOD_INIT(delete),
447         _METHOD_INIT(delete_all),
448         _METHOD_INIT(check),
449         _METHOD_INIT(lock),
450         _METHOD_INIT(unlock),
451         _METHOD_INIT(get_multipaths),
452         _METHOD_INIT(release_multipaths),
453         _METHOD_INIT(get_paths),
454         _METHOD_INIT(release_paths),
455 };