7 #include <urcu/uatomic.h>
14 struct checker_class {
15 struct list_head node;
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;
30 static const char *checker_state_names[PATH_MAX_STATE] = {
32 [PATH_UNCHECKED] = "unchecked",
35 [PATH_SHAKY] = "shaky",
36 [PATH_GHOST] = "ghost",
37 [PATH_PENDING] = "pending",
38 [PATH_TIMEOUT] = "timeout",
39 [PATH_REMOVED] = "removed",
40 [PATH_DELAYED] = "delayed",
43 static LIST_HEAD(checkers);
45 const char *checker_state_name(int i)
47 if (i < 0 || i >= PATH_MAX_STATE) {
48 condlog (2, "invalid state index = %d", i);
51 return checker_state_names[i];
54 static struct checker_class *alloc_checker_class(void)
56 struct checker_class *c;
58 c = calloc(1, sizeof(struct checker_class));
60 INIT_LIST_HEAD(&c->node);
61 uatomic_set(&c->refcount, 1);
66 /* Use uatomic_{sub,add}_return() to ensure proper memory barriers */
67 static int checker_class_ref(struct checker_class *cls)
69 return uatomic_add_return(&cls->refcount, 1);
72 static int checker_class_unref(struct checker_class *cls)
74 return uatomic_sub_return(&cls->refcount, 1);
77 void free_checker_class(struct checker_class *c)
83 cnt = checker_class_unref(c);
85 condlog(cnt < 0 ? 1 : 4, "%s checker refcount %d",
89 condlog(3, "unloading %s checker", c->name);
94 if (dlclose(c->handle) != 0) {
95 condlog(0, "Cannot unload checker %s: %s",
102 void cleanup_checkers (void)
104 struct checker_class *checker_loop;
105 struct checker_class *checker_temp;
107 list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) {
108 free_checker_class(checker_loop);
112 static struct checker_class *checker_class_lookup(const char *name)
114 struct checker_class *c;
116 if (!name || !strlen(name))
118 list_for_each_entry(c, &checkers, node) {
119 if (!strncmp(name, c->name, CHECKER_NAME_LEN))
125 void reset_checker_classes(void)
127 struct checker_class *c;
129 list_for_each_entry(c, &checkers, node) {
135 static struct checker_class *add_checker_class(const char *multipath_dir,
138 char libname[LIB_CHECKER_NAMELEN];
140 struct checker_class *c;
143 c = alloc_checker_class();
146 snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
147 if (!strncmp(c->name, NONE, 4))
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);
156 condlog(3, "loading %s checker", libname);
157 c->handle = dlopen(libname, RTLD_NOW);
159 if ((errstr = dlerror()) != NULL)
160 condlog(0, "A dynamic linking error occurred: (%s)",
164 c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check");
167 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
171 c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init");
174 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
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
185 c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free");
188 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
192 c->msgtable_size = 0;
193 c->msgtable = dlsym(c->handle, "libcheck_msgtable");
195 if (c->msgtable != NULL) {
198 for (p = c->msgtable;
199 *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++)
202 c->msgtable_size = p - c->msgtable;
204 c->msgtable_size = 0;
205 condlog(3, "checker %s: message table size = %d",
206 c->name, c->msgtable_size);
210 list_add(&c->node, &checkers);
213 free_checker_class(c);
217 void checker_set_fd (struct checker * c, int fd)
224 void checker_set_sync (struct checker * c)
231 void checker_set_async (struct checker * c)
238 void checker_enable (struct checker * c)
245 void checker_disable (struct checker * c)
252 int checker_init (struct checker * c, void ** mpctxt_addr)
256 c->mpcontext = mpctxt_addr;
257 if (c->cls->init && c->cls->init(c) != 0)
259 if (mpctxt_addr && *mpctxt_addr == NULL && c->cls->mp_init &&
260 c->cls->mp_init(c) != 0) /* continue even if mp_init fails */
265 int checker_mp_init(struct checker * c, void ** mpctxt_addr)
269 if (c->mpcontext || !mpctxt_addr)
271 c->mpcontext = mpctxt_addr;
272 if (*mpctxt_addr == NULL && c->cls->mp_init &&
273 c->cls->mp_init(c) != 0) {
280 void checker_clear (struct checker *c)
282 memset(c, 0x0, sizeof(struct checker));
286 void checker_put (struct checker * dst)
288 struct checker_class *src;
294 if (src && src->free)
297 free_checker_class(src);
300 int checker_check (struct checker * c, int path_state)
307 c->msgid = CHECKER_MSGID_NONE;
309 c->msgid = CHECKER_MSGID_DISABLED;
310 return PATH_UNCHECKED;
312 if (!strncmp(c->cls->name, NONE, 4))
316 c->msgid = CHECKER_MSGID_NO_FD;
319 r = c->cls->check(c);
324 const char *checker_name(const struct checker *c)
331 int checker_is_sync(const struct checker *c)
333 return c && c->cls && c->cls->sync;
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",
347 const char *checker_message(const struct checker *c)
351 if (!c || !c->cls || c->msgid < 0 ||
352 (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE &&
353 c->msgid < CHECKER_FIRST_MSGID))
356 if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE)
357 return generic_msg[c->msgid];
359 id = c->msgid - CHECKER_FIRST_MSGID;
360 if (id < c->cls->msgtable_size)
361 return c->cls->msgtable[id];
364 return generic_msg[CHECKER_MSGID_NONE];
367 static void checker_cleanup_thread(void *arg)
369 struct checker_class *cls = arg;
371 free_checker_class(cls);
372 rcu_unregister_thread();
375 static void *checker_thread_entry(void *arg)
377 struct checker_context *ctx = arg;
380 rcu_register_thread();
381 pthread_cleanup_push(checker_cleanup_thread, ctx->cls);
382 rv = ctx->cls->thread(ctx);
383 pthread_cleanup_pop(1);
387 int start_checker_thread(pthread_t *thread, const pthread_attr_t *attr,
388 struct checker_context *ctx)
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);
397 condlog(1, "failed to start checker thread for %s: %m",
399 checker_class_unref(ctx->cls);
404 void checker_clear_message (struct checker *c)
408 c->msgid = CHECKER_MSGID_NONE;
411 void checker_get(const char *multipath_dir, struct checker *dst,
414 struct checker_class *src = NULL;
419 if (name && strlen(name)) {
420 src = checker_class_lookup(name);
422 src = add_checker_class(multipath_dir, name);
428 (void)checker_class_ref(dst->cls);
431 int init_checkers(const char *multipath_dir)
433 #ifdef LOAD_ALL_SHARED_LIBS
434 static const char *const all_checkers[] = {
445 for (i = 0; i < ARRAY_SIZE(all_checkers); i++)
446 add_checker_class(multipath_dir, all_checkers[i]);
448 if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))