ef346d795f31440d0d17a5dfaee0893da3e0e5be
[platform/upstream/multipath-tools.git] / libmultipath / checkers.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stddef.h>
4 #include <dlfcn.h>
5 #include <sys/stat.h>
6 #include <urcu.h>
7 #include <urcu/uatomic.h>
8
9 #include "debug.h"
10 #include "checkers.h"
11 #include "vector.h"
12 #include "util.h"
13
14 struct checker_class {
15         struct list_head node;
16         void *handle;
17         int refcount;
18         int sync;
19         char name[CHECKER_NAME_LEN];
20         int (*check)(struct checker *);
21         int (*init)(struct checker *);       /* to allocate the context */
22         int (*mp_init)(struct checker *);    /* to allocate the mpcontext */
23         void (*free)(struct checker *);      /* to free the context */
24         void (*reset)(void);                 /* to reset the global variables */
25         void *(*thread)(void *);             /* async thread entry point */
26         const char **msgtable;
27         short msgtable_size;
28 };
29
30 static const char *checker_state_names[PATH_MAX_STATE] = {
31         [PATH_WILD] = "wild",
32         [PATH_UNCHECKED] = "unchecked",
33         [PATH_DOWN] = "down",
34         [PATH_UP] = "up",
35         [PATH_SHAKY] = "shaky",
36         [PATH_GHOST] = "ghost",
37         [PATH_PENDING] = "pending",
38         [PATH_TIMEOUT] = "timeout",
39         [PATH_REMOVED] = "removed",
40         [PATH_DELAYED] = "delayed",
41 };
42
43 static LIST_HEAD(checkers);
44
45 const char *checker_state_name(int i)
46 {
47         if (i < 0 || i >= PATH_MAX_STATE) {
48                 condlog (2, "invalid state index = %d", i);
49                 return INVALID;
50         }
51         return checker_state_names[i];
52 }
53
54 static struct checker_class *alloc_checker_class(void)
55 {
56         struct checker_class *c;
57
58         c = calloc(1, sizeof(struct checker_class));
59         if (c) {
60                 INIT_LIST_HEAD(&c->node);
61                 uatomic_set(&c->refcount, 1);
62         }
63         return c;
64 }
65
66 /* Use uatomic_{sub,add}_return() to ensure proper memory barriers */
67 static int checker_class_ref(struct checker_class *cls)
68 {
69         return uatomic_add_return(&cls->refcount, 1);
70 }
71
72 static int checker_class_unref(struct checker_class *cls)
73 {
74         return uatomic_sub_return(&cls->refcount, 1);
75 }
76
77 void free_checker_class(struct checker_class *c)
78 {
79         int cnt;
80
81         if (!c)
82                 return;
83         cnt = checker_class_unref(c);
84         if (cnt != 0) {
85                 condlog(cnt < 0 ? 1 : 4, "%s checker refcount %d",
86                         c->name, cnt);
87                 return;
88         }
89         condlog(3, "unloading %s checker", c->name);
90         list_del(&c->node);
91         if (c->reset)
92                 c->reset();
93         if (c->handle) {
94                 if (dlclose(c->handle) != 0) {
95                         condlog(0, "Cannot unload checker %s: %s",
96                                 c->name, dlerror());
97                 }
98         }
99         free(c);
100 }
101
102 void cleanup_checkers (void)
103 {
104         struct checker_class *checker_loop;
105         struct checker_class *checker_temp;
106
107         list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) {
108                 free_checker_class(checker_loop);
109         }
110 }
111
112 static struct checker_class *checker_class_lookup(const char *name)
113 {
114         struct checker_class *c;
115
116         if (!name || !strlen(name))
117                 return NULL;
118         list_for_each_entry(c, &checkers, node) {
119                 if (!strncmp(name, c->name, CHECKER_NAME_LEN))
120                         return c;
121         }
122         return NULL;
123 }
124
125 void reset_checker_classes(void)
126 {
127         struct checker_class *c;
128
129         list_for_each_entry(c, &checkers, node) {
130                 if (c->reset)
131                         c->reset();
132         }
133 }
134
135 static struct checker_class *add_checker_class(const char *multipath_dir,
136                                                const char *name)
137 {
138         char libname[LIB_CHECKER_NAMELEN];
139         struct stat stbuf;
140         struct checker_class *c;
141         char *errstr;
142
143         c = alloc_checker_class();
144         if (!c)
145                 return NULL;
146         snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
147         if (!strncmp(c->name, NONE, 4))
148                 goto done;
149         snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
150                  multipath_dir, name);
151         if (stat(libname,&stbuf) < 0) {
152                 condlog(0,"Checker '%s' not found in %s",
153                         name, multipath_dir);
154                 goto out;
155         }
156         condlog(3, "loading %s checker", libname);
157         c->handle = dlopen(libname, RTLD_NOW);
158         if (!c->handle) {
159                 if ((errstr = dlerror()) != NULL)
160                         condlog(0, "A dynamic linking error occurred: (%s)",
161                                 errstr);
162                 goto out;
163         }
164         c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check");
165         errstr = dlerror();
166         if (errstr != NULL)
167                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
168         if (!c->check)
169                 goto out;
170
171         c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init");
172         errstr = dlerror();
173         if (errstr != NULL)
174                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
175         if (!c->init)
176                 goto out;
177
178         c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init");
179         c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset");
180         c->thread = (void *(*)(void*)) dlsym(c->handle, "libcheck_thread");
181         /* These 3 functions can be NULL. call dlerror() to clear out any
182          * error string */
183         dlerror();
184
185         c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free");
186         errstr = dlerror();
187         if (errstr != NULL)
188                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
189         if (!c->free)
190                 goto out;
191
192         c->msgtable_size = 0;
193         c->msgtable = dlsym(c->handle, "libcheck_msgtable");
194
195         if (c->msgtable != NULL) {
196                 const char **p;
197
198                 for (p = c->msgtable;
199                      *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++)
200                         /* nothing */;
201
202                 c->msgtable_size = p - c->msgtable;
203         } else
204                 c->msgtable_size = 0;
205         condlog(3, "checker %s: message table size = %d",
206                 c->name, c->msgtable_size);
207
208 done:
209         c->sync = 1;
210         list_add(&c->node, &checkers);
211         return c;
212 out:
213         free_checker_class(c);
214         return NULL;
215 }
216
217 void checker_set_fd (struct checker * c, int fd)
218 {
219         if (!c)
220                 return;
221         c->fd = fd;
222 }
223
224 void checker_set_sync (struct checker * c)
225 {
226         if (!c || !c->cls)
227                 return;
228         c->cls->sync = 1;
229 }
230
231 void checker_set_async (struct checker * c)
232 {
233         if (!c || !c->cls)
234                 return;
235         c->cls->sync = 0;
236 }
237
238 void checker_enable (struct checker * c)
239 {
240         if (!c)
241                 return;
242         c->disable = 0;
243 }
244
245 void checker_disable (struct checker * c)
246 {
247         if (!c)
248                 return;
249         c->disable = 1;
250 }
251
252 int checker_init (struct checker * c, void ** mpctxt_addr)
253 {
254         if (!c || !c->cls)
255                 return 1;
256         c->mpcontext = mpctxt_addr;
257         if (c->cls->init && c->cls->init(c) != 0)
258                 return 1;
259         if (mpctxt_addr && *mpctxt_addr == NULL && c->cls->mp_init &&
260             c->cls->mp_init(c) != 0) /* continue even if mp_init fails */
261                 c->mpcontext = NULL;
262         return 0;
263 }
264
265 int checker_mp_init(struct checker * c, void ** mpctxt_addr)
266 {
267         if (!c || !c->cls)
268                 return 1;
269         if (c->mpcontext || !mpctxt_addr)
270                 return 0;
271         c->mpcontext = mpctxt_addr;
272         if (*mpctxt_addr == NULL && c->cls->mp_init &&
273             c->cls->mp_init(c) != 0) {
274                 c->mpcontext = NULL;
275                 return 1;
276         }
277         return 0;
278 }
279
280 void checker_clear (struct checker *c)
281 {
282         memset(c, 0x0, sizeof(struct checker));
283         c->fd = -1;
284 }
285
286 void checker_put (struct checker * dst)
287 {
288         struct checker_class *src;
289
290         if (!dst)
291                 return;
292         src = dst->cls;
293
294         if (src && src->free)
295                 src->free(dst);
296         checker_clear(dst);
297         free_checker_class(src);
298 }
299
300 int checker_check (struct checker * c, int path_state)
301 {
302         int r;
303
304         if (!c)
305                 return PATH_WILD;
306
307         c->msgid = CHECKER_MSGID_NONE;
308         if (c->disable) {
309                 c->msgid = CHECKER_MSGID_DISABLED;
310                 return PATH_UNCHECKED;
311         }
312         if (!strncmp(c->cls->name, NONE, 4))
313                 return path_state;
314
315         if (c->fd < 0) {
316                 c->msgid = CHECKER_MSGID_NO_FD;
317                 return PATH_WILD;
318         }
319         r = c->cls->check(c);
320
321         return r;
322 }
323
324 const char *checker_name(const struct checker *c)
325 {
326         if (!c || !c->cls)
327                 return NULL;
328         return c->cls->name;
329 }
330
331 int checker_is_sync(const struct checker *c)
332 {
333         return c && c->cls && c->cls->sync;
334 }
335
336 static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = {
337         [CHECKER_MSGID_NONE] = "",
338         [CHECKER_MSGID_DISABLED] = " is disabled",
339         [CHECKER_MSGID_NO_FD] = " has no usable fd",
340         [CHECKER_MSGID_INVALID] = " provided invalid message id",
341         [CHECKER_MSGID_UP] = " reports path is up",
342         [CHECKER_MSGID_DOWN] = " reports path is down",
343         [CHECKER_MSGID_GHOST] = " reports path is ghost",
344         [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device",
345 };
346
347 const char *checker_message(const struct checker *c)
348 {
349         int id;
350
351         if (!c || !c->cls || c->msgid < 0 ||
352             (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE &&
353              c->msgid < CHECKER_FIRST_MSGID))
354                 goto bad_id;
355
356         if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE)
357                 return generic_msg[c->msgid];
358
359         id = c->msgid - CHECKER_FIRST_MSGID;
360         if (id < c->cls->msgtable_size)
361                 return c->cls->msgtable[id];
362
363 bad_id:
364         return generic_msg[CHECKER_MSGID_NONE];
365 }
366
367 static void checker_cleanup_thread(void *arg)
368 {
369         struct checker_class *cls = arg;
370
371         free_checker_class(cls);
372         rcu_unregister_thread();
373 }
374
375 static void *checker_thread_entry(void *arg)
376 {
377         struct checker_context *ctx = arg;
378         void *rv;
379
380         rcu_register_thread();
381         pthread_cleanup_push(checker_cleanup_thread, ctx->cls);
382         rv = ctx->cls->thread(ctx);
383         pthread_cleanup_pop(1);
384         return rv;
385 }
386
387 int start_checker_thread(pthread_t *thread, const pthread_attr_t *attr,
388                          struct checker_context *ctx)
389 {
390         int rv;
391
392         assert(ctx && ctx->cls && ctx->cls->thread);
393         /* Take a ref here, lest the class be freed before the thread starts */
394         (void)checker_class_ref(ctx->cls);
395         rv = pthread_create(thread, attr, checker_thread_entry, ctx);
396         if (rv != 0) {
397                 condlog(1, "failed to start checker thread for %s: %m",
398                         ctx->cls->name);
399                 checker_class_unref(ctx->cls);
400         }
401         return rv;
402 }
403
404 void checker_clear_message (struct checker *c)
405 {
406         if (!c)
407                 return;
408         c->msgid = CHECKER_MSGID_NONE;
409 }
410
411 void checker_get(const char *multipath_dir, struct checker *dst,
412                  const char *name)
413 {
414         struct checker_class *src = NULL;
415
416         if (!dst)
417                 return;
418
419         if (name && strlen(name)) {
420                 src = checker_class_lookup(name);
421                 if (!src)
422                         src = add_checker_class(multipath_dir, name);
423         }
424         dst->cls = src;
425         if (!src)
426                 return;
427
428         (void)checker_class_ref(dst->cls);
429 }
430
431 int init_checkers(const char *multipath_dir)
432 {
433 #ifdef LOAD_ALL_SHARED_LIBS
434         static const char *const all_checkers[] = {
435                 DIRECTIO,
436                 TUR,
437                 HP_SW,
438                 RDAC,
439                 EMC_CLARIION,
440                 READSECTOR0,
441                 CCISS_TUR,
442         };
443         unsigned int i;
444
445         for (i = 0; i < ARRAY_SIZE(all_checkers); i++)
446                 add_checker_class(multipath_dir, all_checkers[i]);
447 #else
448         if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
449                 return 1;
450 #endif
451         return 0;
452 }