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