/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include "util.h" #include "vector.h" #include "memory.h" #include "checkers.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "devmapper.h" #include "debug.h" #include "lock.h" #include "waiter.h" #include "main.h" pthread_attr_t waiter_attr; struct mutex_lock waiter_lock = { .mutex = PTHREAD_MUTEX_INITIALIZER }; static struct event_thread *alloc_waiter (void) { struct event_thread *wp; wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); memset(wp, 0, sizeof(struct event_thread)); return wp; } static void free_waiter (void *data) { struct event_thread *wp = (struct event_thread *)data; if (wp->dmt) dm_task_destroy(wp->dmt); rcu_unregister_thread(); FREE(wp); } void stop_waiter_thread (struct multipath *mpp) { pthread_t thread; if (mpp->waiter == (pthread_t)0) { condlog(3, "%s: event checker thread already stopped", mpp->alias); return; } /* Don't cancel yourself. __setup_multipath is called by by the waiter thread, and may remove a multipath device */ if (pthread_equal(mpp->waiter, pthread_self())) return; condlog(3, "%s: stop event checker thread (%lu)", mpp->alias, mpp->waiter); thread = mpp->waiter; mpp->waiter = (pthread_t)0; pthread_cleanup_push(cleanup_lock, &waiter_lock); lock(&waiter_lock); pthread_kill(thread, SIGUSR2); pthread_cancel(thread); lock_cleanup_pop(&waiter_lock); } /* * returns the reschedule delay * negative means *stop* */ static int waiteventloop (struct event_thread *waiter) { sigset_t set, oldset; int event_nr; int r; if (!waiter->event_nr) waiter->event_nr = dm_geteventnr(waiter->mapname); if (!(waiter->dmt = libmp_dm_task_create(DM_DEVICE_WAITEVENT))) { condlog(0, "%s: devmap event #%i dm_task_create error", waiter->mapname, waiter->event_nr); return 1; } if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { condlog(0, "%s: devmap event #%i dm_task_set_name error", waiter->mapname, waiter->event_nr); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; return 1; } if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, waiter->event_nr)) { condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", waiter->mapname, waiter->event_nr); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; return 1; } dm_task_no_open_count(waiter->dmt); /* wait */ sigemptyset(&set); sigaddset(&set, SIGUSR2); pthread_sigmask(SIG_UNBLOCK, &set, &oldset); pthread_testcancel(); r = dm_task_run(waiter->dmt); pthread_testcancel(); pthread_sigmask(SIG_SETMASK, &oldset, NULL); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; if (!r) { /* wait interrupted by signal. check for cancellation */ pthread_cleanup_push(cleanup_lock, &waiter_lock); lock(&waiter_lock); pthread_testcancel(); lock_cleanup_pop(&waiter_lock); return 1; /* If we weren't cancelled, just reschedule */ } waiter->event_nr++; /* * upon event ... */ while (1) { condlog(3, "%s: devmap event #%i", waiter->mapname, waiter->event_nr); /* * event might be : * * 1) a table reload, which means our mpp structure is * obsolete : refresh it through update_multipath() * 2) a path failed by DM : mark as such through * update_multipath() * 3) map has gone away : stop the thread. * 4) a path reinstate : nothing to do * 5) a switch group : nothing to do */ pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock); lock(&waiter->vecs->lock); pthread_testcancel(); r = update_multipath(waiter->vecs, waiter->mapname, 1); lock_cleanup_pop(waiter->vecs->lock); if (r) { condlog(2, "%s: event checker exit", waiter->mapname); return -1; /* stop the thread */ } event_nr = dm_geteventnr(waiter->mapname); if (waiter->event_nr == event_nr) return 1; /* upon problem reschedule 1s later */ waiter->event_nr = event_nr; } return -1; /* never reach there */ } static void *waitevent (void *et) { int r; struct event_thread *waiter; mlockall(MCL_CURRENT | MCL_FUTURE); waiter = (struct event_thread *)et; pthread_cleanup_push(free_waiter, et); rcu_register_thread(); while (1) { r = waiteventloop(waiter); if (r < 0) break; sleep(r); } pthread_cleanup_pop(1); return NULL; } int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) { struct event_thread *wp; if (!mpp) return 0; wp = alloc_waiter(); if (!wp) goto out; strlcpy(wp->mapname, mpp->alias, WWID_SIZE); wp->vecs = vecs; if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { condlog(0, "%s: cannot create event checker", wp->mapname); goto out1; } mpp->waiter = wp->thread; condlog(3, "%s: event checker started", wp->mapname); return 0; out1: free_waiter(wp); mpp->waiter = (pthread_t)0; out: condlog(0, "failed to start waiter thread"); return 1; }