[libmultipath] Remove libsysfs
[multipath-tools/.git] / libmultipath / structs_vec.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4
5 #include <checkers.h>
6
7 #include "vector.h"
8 #include "defaults.h"
9 #include "debug.h"
10 #include "structs.h"
11 #include "structs_vec.h"
12 #include "devmapper.h"
13 #include "dmparser.h"
14 #include "config.h"
15 #include "propsel.h"
16 #include "sysfs.h"
17 #include "discovery.h"
18 #include "waiter.h"
19
20 /*
21  * creates or updates mpp->paths reading mpp->pg
22  */
23 extern int
24 update_mpp_paths(struct multipath * mpp, vector pathvec)
25 {
26         struct pathgroup * pgp;
27         struct path * pp;
28         int i,j;
29
30         if (!mpp->pg)
31                 return 0;
32
33         if (!mpp->paths &&
34             !(mpp->paths = vector_alloc()))
35                 return 1;
36
37         vector_foreach_slot (mpp->pg, pgp, i) {
38                 vector_foreach_slot (pgp->paths, pp, j) {
39                         if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
40                             (find_path_by_devt(pathvec, pp->dev_t)) &&
41                             store_path(mpp->paths, pp))
42                                 return 1;
43                 }
44         }
45         return 0;
46 }
47
48 extern int
49 adopt_paths (vector pathvec, struct multipath * mpp)
50 {
51         int i;
52         struct path * pp;
53
54         if (!mpp)
55                 return 0;
56
57         if (update_mpp_paths(mpp, pathvec))
58                 return 1;
59
60         vector_foreach_slot (pathvec, pp, i) {
61                 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
62                         condlog(3, "%s: ownership set to %s",
63                                 pp->dev, mpp->alias);
64                         pp->mpp = mpp;
65                         
66                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
67                                 return 1;
68
69                         if (!find_path_by_dev(mpp->paths, pp->dev) &&
70                             store_path(mpp->paths, pp))
71                                         return 1;
72                         pathinfo(pp, conf->hwtable, DI_PRIO | DI_CHECKER);
73                 }
74         }
75         return 0;
76 }
77
78 extern void
79 orphan_path (struct path * pp)
80 {
81         pp->mpp = NULL;
82         pp->dmstate = PSTATE_UNDEF;
83         pp->getuid = NULL;
84         pp->getprio = NULL;
85         pp->getprio_selected = 0;
86         checker_put(&pp->checker);
87         if (pp->fd >= 0)
88                 close(pp->fd);
89         pp->fd = -1;
90 }
91
92 extern void
93 orphan_paths (vector pathvec, struct multipath * mpp)
94 {
95         int i;
96         struct path * pp;
97
98         vector_foreach_slot (pathvec, pp, i) {
99                 if (pp->mpp == mpp) {
100                         condlog(4, "%s: orphaned", pp->dev);
101                         orphan_path(pp);
102                 }
103         }
104 }
105
106 static void
107 set_multipath_wwid (struct multipath * mpp)
108 {
109         if (mpp->wwid)
110                 return;
111
112         dm_get_uuid(mpp->alias, mpp->wwid);
113 }
114
115 extern void
116 remove_map (struct multipath * mpp, struct vectors * vecs,
117             stop_waiter_thread_func *stop_waiter, int purge_vec)
118 {
119         int i;
120
121         condlog(4, "%s: remove multipath map", mpp->alias);
122
123         /*
124          * stop the DM event waiter thread
125          */
126         if (stop_waiter)
127                 stop_waiter(mpp, vecs);
128
129         /*
130          * clear references to this map
131          */
132         orphan_paths(vecs->pathvec, mpp);
133
134         if (purge_vec &&
135             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
136                 vector_del_slot(vecs->mpvec, i);
137
138         /*
139          * final free
140          */
141         free_multipath(mpp, KEEP_PATHS);
142 }
143
144 extern void
145 remove_maps (struct vectors * vecs,
146              stop_waiter_thread_func *stop_waiter)
147 {
148         int i;
149         struct multipath * mpp;
150
151         vector_foreach_slot (vecs->mpvec, mpp, i) {
152                 remove_map(mpp, vecs, stop_waiter, 1);
153                 i--;
154         }
155
156         vector_free(vecs->mpvec);
157         vecs->mpvec = NULL;
158 }
159
160 static struct hwentry *
161 extract_hwe_from_path(struct multipath * mpp)
162 {
163         struct path * pp;
164         struct pathgroup * pgp;
165
166         pgp = VECTOR_SLOT(mpp->pg, 0);
167         pp = VECTOR_SLOT(pgp->paths, 0);
168
169         return pp->hwe;
170 }
171
172 static int
173 update_multipath_table (struct multipath *mpp, vector pathvec)
174 {
175         if (!mpp)
176                 return 1;
177
178         if (dm_get_map(mpp->alias, &mpp->size, mpp->params))
179                 return 1;
180
181         if (disassemble_map(pathvec, mpp->params, mpp))
182                 return 1;
183
184         return 0;
185 }
186
187 static int
188 update_multipath_status (struct multipath *mpp)
189 {
190         if (!mpp)
191                 return 1;
192
193         if(dm_get_status(mpp->alias, mpp->status))
194                 return 1;
195
196         if (disassemble_status(mpp->status, mpp))
197                 return 1;
198
199         return 0;
200 }
201
202 extern int
203 update_multipath_strings (struct multipath *mpp, vector pathvec)
204 {
205         free_multipath_attributes(mpp);
206         free_pgvec(mpp->pg, KEEP_PATHS);
207         mpp->pg = NULL;
208
209         if (update_multipath_table(mpp, pathvec))
210                 return 1;
211
212         if (update_multipath_status(mpp))
213                 return 1;
214
215         return 0;
216 }
217
218 extern void
219 set_no_path_retry(struct multipath *mpp)
220 {
221         mpp->retry_tick = 0;
222         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
223         select_no_path_retry(mpp);
224
225         switch (mpp->no_path_retry) {
226         case NO_PATH_RETRY_UNDEF:
227                 break;
228         case NO_PATH_RETRY_FAIL:
229                 dm_queue_if_no_path(mpp->alias, 0);
230                 break;
231         case NO_PATH_RETRY_QUEUE:
232                 dm_queue_if_no_path(mpp->alias, 1);
233                 break;
234         default:
235                 dm_queue_if_no_path(mpp->alias, 1);
236                 if (mpp->nr_active == 0) {
237                         /* Enter retry mode */
238                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
239                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
240                                 mpp->alias, mpp->no_path_retry);
241                 }
242                 break;
243         }
244 }
245
246 extern int
247 setup_multipath (struct vectors * vecs, struct multipath * mpp)
248 {
249 retry:
250         if (dm_get_info(mpp->alias, &mpp->dmi)) {
251                 /* Error accessing table */
252                 condlog(3, "%s: cannot access table", mpp->alias); 
253                 goto out;
254         }
255
256         if (!dm_map_present(mpp->alias)) {
257                 /* Table has been removed */
258                 condlog(3, "%s: table does not exist", mpp->alias); 
259                 goto out;
260         }
261
262         set_multipath_wwid(mpp);
263         mpp->mpe = find_mpe(mpp->wwid);
264         condlog(3, "%s: discover", mpp->alias);
265
266         if (update_multipath_strings(mpp, vecs->pathvec)) {
267                 char new_alias[WWID_SIZE];
268
269                 /*
270                  * detect an external rename of the multipath device
271                  */
272                 if (dm_get_name(mpp->wwid, DEFAULT_TARGET, new_alias)) {
273                         condlog(3, "%s multipath mapped device name has "
274                                 "changed from %s to %s", mpp->wwid,
275                                 mpp->alias, new_alias);
276                         strcpy(mpp->alias, new_alias);
277 #if DAEMON
278                         if (mpp->waiter) 
279                                 strncpy(((struct event_thread *)mpp->waiter)->mapname,
280                                         new_alias, WWID_SIZE);
281 #endif
282                         goto retry;
283                 }
284                 condlog(0, "%s: failed to setup multipath", mpp->alias);
285                 goto out;
286         }
287
288         //adopt_paths(vecs->pathvec, mpp);
289         mpp->hwe = extract_hwe_from_path(mpp);
290         select_rr_weight(mpp);
291         select_pgfailback(mpp);
292         set_no_path_retry(mpp);
293         select_pg_timeout(mpp);
294
295         return 0;
296 out:
297         remove_map(mpp, vecs, NULL, 1);
298         return 1;
299 }
300
301 extern struct multipath *
302 add_map_without_path (struct vectors * vecs,
303                       int minor, char * alias,
304                       start_waiter_thread_func *start_waiter)
305 {
306         struct multipath * mpp = alloc_multipath();
307
308         if (!mpp)
309                 return NULL;
310
311         mpp->alias = alias;
312
313         if (setup_multipath(vecs, mpp))
314                 return NULL; /* mpp freed in setup_multipath */
315
316         if (adopt_paths(vecs->pathvec, mpp))
317                 goto out;
318         
319         if (!vector_alloc_slot(vecs->mpvec))
320                 goto out;
321
322         vector_set_slot(vecs->mpvec, mpp);
323
324         if (start_waiter(mpp, vecs))
325                 goto out;
326
327         return mpp;
328 out:
329         remove_map(mpp, vecs, NULL, 1);
330         return NULL;
331 }
332
333 extern struct multipath *
334 add_map_with_path (struct vectors * vecs,
335                    struct path * pp, int add_vec)
336 {
337         struct multipath * mpp;
338
339         if (!(mpp = alloc_multipath()))
340                 return NULL;
341
342         mpp->mpe = find_mpe(pp->wwid);
343         mpp->hwe = pp->hwe;
344
345         strcpy(mpp->wwid, pp->wwid);
346         select_alias(mpp);
347         mpp->size = pp->size;
348
349         if (adopt_paths(vecs->pathvec, mpp))
350                 goto out;
351
352         if (add_vec) {
353                 if (!vector_alloc_slot(vecs->mpvec))
354                         goto out;
355
356                 vector_set_slot(vecs->mpvec, mpp);
357         }
358
359         return mpp;
360
361 out:
362         remove_map(mpp, vecs, NULL, add_vec);
363         return NULL;
364 }
365
366 extern int
367 verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
368 {
369         struct path * pp;
370         int count = 0;
371         int i, j;
372
373         vector_foreach_slot (mpp->paths, pp, i) {
374                 /*
375                  * see if path is in sysfs
376                  */
377                 if (!pp->sysdev || sysfs_get_dev(pp->sysdev,
378                                                  pp->dev_t, BLK_DEV_SIZE)) {
379                         condlog(0, "%s: failed to access path %s", mpp->alias,
380                                 pp->sysdev ? pp->sysdev->devpath : pp->dev_t);
381                         count++;
382                         vector_del_slot(mpp->paths, i);
383                         i--;
384
385                         if (rpvec)
386                                 store_path(rpvec, pp);
387                         else {
388                                 if ((j = find_slot(vecs->pathvec,
389                                                    (void *)pp)) != -1)
390                                         vector_del_slot(vecs->pathvec, j);
391                                 free_path(pp);
392                         }
393                 }
394         }
395         return count;
396 }
397
398 int update_multipath (struct vectors *vecs, char *mapname)
399 {
400         struct multipath *mpp;
401         struct pathgroup  *pgp;
402         struct path *pp;
403         int i, j;
404
405         mpp = find_mp_by_alias(vecs->mpvec, mapname);
406
407         if (!mpp) {
408                 condlog(3, "%s: multipath map not found\n", mapname);
409                 return 2;
410         }
411
412         free_pgvec(mpp->pg, KEEP_PATHS);
413         mpp->pg = NULL;
414
415         if (setup_multipath(vecs, mpp))
416                 return 1; /* mpp freed in setup_multipath */
417
418         /*
419          * compare checkers states with DM states
420          */
421         vector_foreach_slot (mpp->pg, pgp, i) {
422                 vector_foreach_slot (pgp->paths, pp, j) {
423                         if (pp->dmstate != PSTATE_FAILED)
424                                 continue;
425
426                         if (pp->state != PATH_DOWN) {
427                                 int oldstate = pp->state;
428                                 condlog(2, "%s: mark as failed", pp->dev_t);
429                                 mpp->stat_path_failures++;
430                                 pp->state = PATH_DOWN;
431                                 if (oldstate == PATH_UP ||
432                                     oldstate == PATH_GHOST)
433                                         update_queue_mode_del_path(mpp);
434
435                                 /*
436                                  * if opportune,
437                                  * schedule the next check earlier
438                                  */
439                                 if (pp->tick > conf->checkint)
440                                         pp->tick = conf->checkint;
441                         }
442                 }
443         }
444
445         return 0;
446 }
447
448 /*
449  * mpp->no_path_retry:
450  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
451  *   -1 (FAIL)  : fail_if_no_path
452  *    0 (UNDEF) : nothing
453  *   >0         : queue_if_no_path enabled, turned off after polling n times
454  */
455 void update_queue_mode_del_path(struct multipath *mpp)
456 {
457         if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
458                 /*
459                  * Enter retry mode.
460                  * meaning of +1: retry_tick may be decremented in
461                  *                checkerloop before starting retry.
462                  */
463                 mpp->stat_queueing_timeouts++;
464                 mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
465                 condlog(1, "%s: Entering recovery mode: max_retries=%d",
466                         mpp->alias, mpp->no_path_retry);
467         }
468         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
469 }
470
471 void update_queue_mode_add_path(struct multipath *mpp)
472 {
473         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
474                 /* come back to normal mode from retry mode */
475                 mpp->retry_tick = 0;
476                 dm_queue_if_no_path(mpp->alias, 1);
477                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
478                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
479         }
480         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
481 }
482