libmultipath: update waiter handling
[multipath-tools/.git] / libmultipath / waiter.c
1 /*
2  * Copyright (c) 2004, 2005 Christophe Varoqui
3  * Copyright (c) 2005 Kiyoshi Ueda, NEC
4  * Copyright (c) 2005 Benjamin Marzinski, Redhat
5  * Copyright (c) 2005 Edward Goggin, EMC
6  */
7 #include <unistd.h>
8 #include <libdevmapper.h>
9 #include <sys/mman.h>
10 #include <pthread.h>
11 #include <signal.h>
12
13 #include "vector.h"
14 #include "memory.h"
15 #include "checkers.h"
16 #include "structs.h"
17 #include "structs_vec.h"
18 #include "devmapper.h"
19 #include "debug.h"
20 #include "lock.h"
21 #include "waiter.h"
22
23 pthread_attr_t waiter_attr;
24
25 struct event_thread *alloc_waiter (void)
26 {
27
28         struct event_thread *wp;
29
30         wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
31         memset(wp, 0, sizeof(struct event_thread));
32         pthread_mutex_init(&wp->lock, NULL);
33
34         return wp;
35 }
36
37 void signal_waiter (void *data)
38 {
39         struct event_thread *wp = (struct event_thread *)data;
40
41         pthread_mutex_lock(&wp->lock);
42         memset(wp->mapname, 0, WWID_SIZE);
43         pthread_mutex_unlock(&wp->lock);
44 }
45
46 void free_waiter (struct event_thread *wp)
47 {
48         pthread_mutex_destroy(&wp->lock);
49         FREE(wp);
50 }
51
52 void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
53 {
54         struct event_thread *wp = (struct event_thread *)mpp->waiter;
55         pthread_t thread;
56
57         if (!wp) {
58                 condlog(3, "%s: no waiter thread", mpp->alias);
59                 return;
60         }
61         if (wp->thread == (pthread_t)0) {
62                 condlog(3, "%s: event checker thread already stopped",
63                         mpp->alias);
64                 return;
65         }
66         thread = wp->thread;
67         wp->thread = (pthread_t)0;
68         mpp->waiter = NULL;
69
70         condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread);
71         pthread_kill(thread, SIGUSR1);
72 }
73
74 static sigset_t unblock_signals(void)
75 {
76         sigset_t set, old;
77
78         sigemptyset(&set);
79         sigaddset(&set, SIGHUP);
80         sigaddset(&set, SIGUSR1);
81         pthread_sigmask(SIG_UNBLOCK, &set, &old);
82         return old;
83 }
84
85 /*
86  * returns the reschedule delay
87  * negative means *stop*
88  */
89 int waiteventloop (struct event_thread *waiter)
90 {
91         sigset_t set;
92         struct dm_task *dmt = NULL;
93         int event_nr;
94         int r;
95
96         pthread_mutex_lock(&waiter->lock);
97         if (!waiter->event_nr)
98                 waiter->event_nr = dm_geteventnr(waiter->mapname);
99
100         if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
101                 condlog(0, "%s: devmap event #%i dm_task_create error",
102                                 waiter->mapname, waiter->event_nr);
103                 pthread_mutex_unlock(&waiter->lock);
104                 return 1;
105         }
106
107         if (!dm_task_set_name(dmt, waiter->mapname)) {
108                 condlog(0, "%s: devmap event #%i dm_task_set_name error",
109                                 waiter->mapname, waiter->event_nr);
110                 dm_task_destroy(dmt);
111                 pthread_mutex_unlock(&waiter->lock);
112                 return 1;
113         }
114
115         if (waiter->event_nr && !dm_task_set_event_nr(dmt,
116                                                       waiter->event_nr)) {
117                 condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
118                                 waiter->mapname, waiter->event_nr);
119                 dm_task_destroy(dmt);
120                 pthread_mutex_unlock(&waiter->lock);
121                 return 1;
122         }
123         pthread_mutex_unlock(&waiter->lock);
124
125         dm_task_no_open_count(dmt);
126
127         /* accept wait interruption */
128         set = unblock_signals();
129
130         /* wait */
131         r = dm_task_run(dmt);
132
133         /* wait is over : event or interrupt */
134         pthread_sigmask(SIG_SETMASK, &set, NULL);
135
136         dm_task_destroy(dmt);
137
138         if (!r) /* wait interrupted by signal */
139                 return -1;
140
141         pthread_mutex_lock(&waiter->lock);
142         if (!strlen(waiter->mapname)) {
143                 /* waiter should exit */
144                 pthread_mutex_unlock(&waiter->lock);
145                 return -1;
146         }
147         waiter->event_nr++;
148
149         /*
150          * upon event ...
151          */
152         while (1) {
153                 condlog(3, "%s: devmap event #%i",
154                                 waiter->mapname, waiter->event_nr);
155
156                 /*
157                  * event might be :
158                  *
159                  * 1) a table reload, which means our mpp structure is
160                  *    obsolete : refresh it through update_multipath()
161                  * 2) a path failed by DM : mark as such through
162                  *    update_multipath()
163                  * 3) map has gone away : stop the thread.
164                  * 4) a path reinstate : nothing to do
165                  * 5) a switch group : nothing to do
166                  */
167                 pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock);
168                 lock(waiter->vecs->lock);
169                 r = update_multipath(waiter->vecs, waiter->mapname);
170                 lock_cleanup_pop(waiter->vecs->lock);
171
172                 if (r) {
173                         condlog(2, "%s: event checker exit",
174                                 waiter->mapname);
175                         pthread_mutex_unlock(&waiter->lock);
176                         return -1; /* stop the thread */
177                 }
178
179                 event_nr = dm_geteventnr(waiter->mapname);
180
181                 if (waiter->event_nr == event_nr) {
182                         pthread_mutex_unlock(&waiter->lock);
183                         return 1; /* upon problem reschedule 1s later */
184                 }
185
186                 waiter->event_nr = event_nr;
187         }
188         pthread_mutex_unlock(&waiter->lock);
189         return -1; /* never reach there */
190 }
191
192 void *waitevent (void *et)
193 {
194         int r;
195         struct event_thread *waiter;
196
197         mlockall(MCL_CURRENT | MCL_FUTURE);
198
199         waiter = (struct event_thread *)et;
200         pthread_cleanup_push(signal_waiter, et);
201
202         block_signal(SIGUSR1, NULL);
203         block_signal(SIGHUP, NULL);
204         while (1) {
205                 r = waiteventloop(waiter);
206
207                 if (r < 0)
208                         break;
209
210                 sleep(r);
211         }
212
213         pthread_cleanup_pop(1);
214         free_waiter(waiter);
215         return NULL;
216 }
217
218 int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
219 {
220         struct event_thread *wp;
221
222         if (!mpp)
223                 return 0;
224
225         wp = alloc_waiter();
226
227         if (!wp)
228                 goto out;
229
230         pthread_mutex_lock(&wp->lock);
231         mpp->waiter = (void *)wp;
232         strncpy(wp->mapname, mpp->alias, WWID_SIZE);
233         wp->vecs = vecs;
234         pthread_mutex_unlock(&wp->lock);
235
236         if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
237                 condlog(0, "%s: cannot create event checker", wp->mapname);
238                 goto out1;
239         }
240         condlog(2, "%s: event checker started", wp->mapname);
241
242         return 0;
243 out1:
244         free_waiter(wp);
245         mpp->waiter = NULL;
246 out:
247         condlog(0, "failed to start waiter thread");
248         return 1;
249 }
250