4fb2cff9b543e188e8d16209d9a23d75f90e04b8
[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
32         return wp;
33 }
34
35 void free_waiter (void *data)
36 {
37         sigset_t old;
38         struct event_thread *wp = (struct event_thread *)data;
39
40         /*
41          * indicate in mpp that the wp is already freed storage
42          */
43         block_signal(SIGHUP, &old);
44         lock(wp->vecs->lock);
45
46         if (wp->mpp)
47                 /*
48                  * be careful, mpp may already be freed -- null if so
49                  */
50                 wp->mpp->waiter = NULL;
51         else
52                 /*
53                 * This is OK condition during shutdown.
54                 */
55                 condlog(3, "free_waiter, mpp freed before wp=%p (%s).", wp, wp->mapname);
56
57         unlock(wp->vecs->lock);
58         pthread_sigmask(SIG_SETMASK, &old, NULL);
59
60         if (wp->dmt)
61                 dm_task_destroy(wp->dmt);
62
63         FREE(wp);
64 }
65
66 void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
67 {
68         struct event_thread *wp = (struct event_thread *)mpp->waiter;
69         pthread_t thread;
70
71         if (!wp) {
72                 condlog(3, "%s: no waiter thread", mpp->alias);
73                 return;
74         }
75         thread = wp->thread;
76         condlog(2, "%s: stop event checker thread (%lu)", wp->mapname, thread);
77
78         pthread_kill(thread, SIGUSR1);
79 }
80
81 static sigset_t unblock_signals(void)
82 {
83         sigset_t set, old;
84
85         sigemptyset(&set);
86         sigaddset(&set, SIGHUP);
87         sigaddset(&set, SIGUSR1);
88         pthread_sigmask(SIG_UNBLOCK, &set, &old);
89         return old;
90 }
91
92 /*
93  * returns the reschedule delay
94  * negative means *stop*
95  */
96 int waiteventloop (struct event_thread *waiter)
97 {
98         sigset_t set;
99         int event_nr;
100         int r;
101
102         if (!waiter->event_nr)
103                 waiter->event_nr = dm_geteventnr(waiter->mapname);
104
105         if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
106                 condlog(0, "%s: devmap event #%i dm_task_create error",
107                                 waiter->mapname, waiter->event_nr);
108                 return 1;
109         }
110
111         if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
112                 condlog(0, "%s: devmap event #%i dm_task_set_name error",
113                                 waiter->mapname, waiter->event_nr);
114                 dm_task_destroy(waiter->dmt);
115                 return 1;
116         }
117
118         if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
119                                                       waiter->event_nr)) {
120                 condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
121                                 waiter->mapname, waiter->event_nr);
122                 dm_task_destroy(waiter->dmt);
123                 return 1;
124         }
125
126         dm_task_no_open_count(waiter->dmt);
127
128         /* accept wait interruption */
129         set = unblock_signals();
130
131         /* wait */
132         r = dm_task_run(waiter->dmt);
133
134         /* wait is over : event or interrupt */
135         pthread_sigmask(SIG_SETMASK, &set, NULL);
136
137         if (!r) /* wait interrupted by signal */
138                 return -1;
139
140         dm_task_destroy(waiter->dmt);
141         waiter->dmt = NULL;
142         waiter->event_nr++;
143
144         /*
145          * upon event ...
146          */
147         while (1) {
148                 condlog(3, "%s: devmap event #%i",
149                                 waiter->mapname, waiter->event_nr);
150
151                 /*
152                  * event might be :
153                  *
154                  * 1) a table reload, which means our mpp structure is
155                  *    obsolete : refresh it through update_multipath()
156                  * 2) a path failed by DM : mark as such through
157                  *    update_multipath()
158                  * 3) map has gone away : stop the thread.
159                  * 4) a path reinstate : nothing to do
160                  * 5) a switch group : nothing to do
161                  */
162                 pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock);
163                 lock(waiter->vecs->lock);
164                 r = update_multipath(waiter->vecs, waiter->mapname);
165                 lock_cleanup_pop(waiter->vecs->lock);
166
167                 if (r) {
168                         condlog(2, "%s: event checker exit",
169                                 waiter->mapname);
170                         return -1; /* stop the thread */
171                 }
172
173                 event_nr = dm_geteventnr(waiter->mapname);
174
175                 if (waiter->event_nr == event_nr)
176                         return 1; /* upon problem reschedule 1s later */
177
178                 waiter->event_nr = event_nr;
179         }
180         return -1; /* never reach there */
181 }
182
183 void *waitevent (void *et)
184 {
185         int r;
186         struct event_thread *waiter;
187
188         mlockall(MCL_CURRENT | MCL_FUTURE);
189
190         waiter = (struct event_thread *)et;
191         pthread_cleanup_push(free_waiter, et);
192
193         block_signal(SIGUSR1, NULL);
194         block_signal(SIGHUP, NULL);
195         while (1) {
196                 r = waiteventloop(waiter);
197
198                 if (r < 0)
199                         break;
200
201                 sleep(r);
202         }
203
204         pthread_cleanup_pop(1);
205         return NULL;
206 }
207
208 int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
209 {
210         struct event_thread *wp;
211
212         if (!mpp)
213                 return 0;
214
215         wp = alloc_waiter();
216
217         if (!wp)
218                 goto out;
219
220         mpp->waiter = (void *)wp;
221         strncpy(wp->mapname, mpp->alias, WWID_SIZE);
222         wp->vecs = vecs;
223         wp->mpp = mpp;
224
225         if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
226                 condlog(0, "%s: cannot create event checker", wp->mapname);
227                 goto out1;
228         }
229         condlog(2, "%s: event checker started", wp->mapname);
230
231         return 0;
232 out1:
233         free_waiter(wp);
234         mpp->waiter = NULL;
235 out:
236         condlog(0, "failed to start waiter thread");
237         return 1;
238 }
239