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