multipath: Update multipath device on show topology
[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         if (mpp->waiter == (pthread_t)0) {
55                 condlog(3, "%s: event checker thread already stopped",
56                         mpp->alias);
57                 return;
58         }
59         condlog(2, "%s: stop event checker thread (%lu)", mpp->alias,
60                 mpp->waiter);
61         pthread_kill(mpp->waiter, SIGUSR1);
62         mpp->waiter = (pthread_t)0;
63 }
64
65 static sigset_t unblock_signals(void)
66 {
67         sigset_t set, old;
68
69         sigemptyset(&set);
70         sigaddset(&set, SIGHUP);
71         sigaddset(&set, SIGUSR1);
72         pthread_sigmask(SIG_UNBLOCK, &set, &old);
73         return old;
74 }
75
76 /*
77  * returns the reschedule delay
78  * negative means *stop*
79  */
80 int waiteventloop (struct event_thread *waiter)
81 {
82         sigset_t set;
83         struct dm_task *dmt = NULL;
84         int event_nr;
85         int r;
86
87         pthread_mutex_lock(&waiter->lock);
88         if (!waiter->event_nr)
89                 waiter->event_nr = dm_geteventnr(waiter->mapname);
90
91         if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
92                 condlog(0, "%s: devmap event #%i dm_task_create error",
93                                 waiter->mapname, waiter->event_nr);
94                 pthread_mutex_unlock(&waiter->lock);
95                 return 1;
96         }
97
98         if (!dm_task_set_name(dmt, waiter->mapname)) {
99                 condlog(0, "%s: devmap event #%i dm_task_set_name error",
100                                 waiter->mapname, waiter->event_nr);
101                 dm_task_destroy(dmt);
102                 pthread_mutex_unlock(&waiter->lock);
103                 return 1;
104         }
105
106         if (waiter->event_nr && !dm_task_set_event_nr(dmt,
107                                                       waiter->event_nr)) {
108                 condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
109                                 waiter->mapname, waiter->event_nr);
110                 dm_task_destroy(dmt);
111                 pthread_mutex_unlock(&waiter->lock);
112                 return 1;
113         }
114         pthread_mutex_unlock(&waiter->lock);
115
116         dm_task_no_open_count(dmt);
117
118         /* accept wait interruption */
119         set = unblock_signals();
120
121         /* wait */
122         r = dm_task_run(dmt);
123
124         /* wait is over : event or interrupt */
125         pthread_sigmask(SIG_SETMASK, &set, NULL);
126
127         dm_task_destroy(dmt);
128
129         if (!r) /* wait interrupted by signal */
130                 return -1;
131
132         pthread_mutex_lock(&waiter->lock);
133         if (!strlen(waiter->mapname)) {
134                 /* waiter should exit */
135                 pthread_mutex_unlock(&waiter->lock);
136                 return -1;
137         }
138         waiter->event_nr++;
139
140         /*
141          * upon event ...
142          */
143         while (1) {
144                 condlog(3, "%s: devmap event #%i",
145                                 waiter->mapname, waiter->event_nr);
146
147                 /*
148                  * event might be :
149                  *
150                  * 1) a table reload, which means our mpp structure is
151                  *    obsolete : refresh it through update_multipath()
152                  * 2) a path failed by DM : mark as such through
153                  *    update_multipath()
154                  * 3) map has gone away : stop the thread.
155                  * 4) a path reinstate : nothing to do
156                  * 5) a switch group : nothing to do
157                  */
158                 pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock);
159                 lock(waiter->vecs->lock);
160                 r = update_multipath(waiter->vecs, waiter->mapname, 1);
161                 lock_cleanup_pop(waiter->vecs->lock);
162
163                 if (r) {
164                         condlog(2, "%s: event checker exit",
165                                 waiter->mapname);
166                         pthread_mutex_unlock(&waiter->lock);
167                         return -1; /* stop the thread */
168                 }
169
170                 event_nr = dm_geteventnr(waiter->mapname);
171
172                 if (waiter->event_nr == event_nr) {
173                         pthread_mutex_unlock(&waiter->lock);
174                         return 1; /* upon problem reschedule 1s later */
175                 }
176
177                 waiter->event_nr = event_nr;
178         }
179         pthread_mutex_unlock(&waiter->lock);
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(signal_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         free_waiter(waiter);
206         return NULL;
207 }
208
209 int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
210 {
211         struct event_thread *wp;
212
213         if (!mpp)
214                 return 0;
215
216         wp = alloc_waiter();
217
218         if (!wp)
219                 goto out;
220
221         pthread_mutex_lock(&wp->lock);
222         strncpy(wp->mapname, mpp->alias, WWID_SIZE);
223         wp->vecs = vecs;
224         pthread_mutex_unlock(&wp->lock);
225
226         if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
227                 condlog(0, "%s: cannot create event checker", wp->mapname);
228                 goto out1;
229         }
230         mpp->waiter = wp->thread;
231         condlog(2, "%s: event checker started", wp->mapname);
232
233         return 0;
234 out1:
235         free_waiter(wp);
236         mpp->waiter = (pthread_t)0;
237 out:
238         condlog(0, "failed to start waiter thread");
239         return 1;
240 }
241