Use 'mptable' as argument for find_mpe() and get_mpe_wwid()
[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 #include "vector.h"
7 #include "defaults.h"
8 #include "debug.h"
9 #include "structs.h"
10 #include "structs_vec.h"
11 #include "sysfs.h"
12 #include "waiter.h"
13 #include "devmapper.h"
14 #include "dmparser.h"
15 #include "config.h"
16 #include "propsel.h"
17 #include "discovery.h"
18 #include "prio.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 || !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                         if (pathinfo(pp, conf->hwtable,
73                                      DI_PRIO | DI_CHECKER))
74                                 return 1;
75                 }
76         }
77         return 0;
78 }
79
80 extern void
81 orphan_path (struct path * pp, const char *reason)
82 {
83         condlog(3, "%s: orphan path, %s", pp->dev, reason);
84         pp->mpp = NULL;
85         pp->dmstate = PSTATE_UNDEF;
86         pp->uid_attribute = NULL;
87         pp->getuid = NULL;
88         prio_put(&pp->prio);
89         checker_put(&pp->checker);
90         if (pp->fd >= 0)
91                 close(pp->fd);
92         pp->fd = -1;
93 }
94
95 extern void
96 orphan_paths (vector pathvec, struct multipath * mpp)
97 {
98         int i;
99         struct path * pp;
100
101         vector_foreach_slot (pathvec, pp, i) {
102                 if (pp->mpp == mpp) {
103                         orphan_path(pp, "map flushed");
104                 }
105         }
106 }
107
108 static void
109 set_multipath_wwid (struct multipath * mpp)
110 {
111         if (strlen(mpp->wwid))
112                 return;
113
114         dm_get_uuid(mpp->alias, mpp->wwid);
115 }
116
117 #define KEEP_WAITER 0
118 #define STOP_WAITER 1
119 #define PURGE_VEC 1
120
121 static void
122 _remove_map (struct multipath * mpp, struct vectors * vecs,
123             int stop_waiter, int purge_vec)
124 {
125         int i;
126
127         condlog(4, "%s: remove multipath map", mpp->alias);
128
129         /*
130          * stop the DM event waiter thread
131          */
132         if (stop_waiter)
133                 stop_waiter_thread(mpp, vecs);
134
135         /*
136          * clear references to this map
137          */
138         orphan_paths(vecs->pathvec, mpp);
139
140         if (purge_vec &&
141             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
142                 vector_del_slot(vecs->mpvec, i);
143
144         /*
145          * final free
146          */
147         free_multipath(mpp, KEEP_PATHS);
148 }
149
150 extern void
151 remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec)
152 {
153         _remove_map(mpp, vecs, KEEP_WAITER, purge_vec);
154 }
155
156 extern void
157 remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs,
158                             int purge_vec)
159 {
160         _remove_map(mpp, vecs, STOP_WAITER, purge_vec);
161 }
162
163 static void
164 _remove_maps (struct vectors * vecs, int stop_waiter)
165 {
166         int i;
167         struct multipath * mpp;
168
169         if (!vecs)
170                 return;
171
172         vector_foreach_slot (vecs->mpvec, mpp, i) {
173                 _remove_map(mpp, vecs, stop_waiter, 1);
174                 i--;
175         }
176
177         vector_free(vecs->mpvec);
178         vecs->mpvec = NULL;
179 }
180
181 extern void
182 remove_maps (struct vectors * vecs)
183 {
184         _remove_maps(vecs, KEEP_WAITER);
185 }
186
187 extern void
188 remove_maps_and_stop_waiters (struct vectors * vecs)
189 {
190         _remove_maps(vecs, STOP_WAITER);
191 }
192
193 static struct hwentry *
194 extract_hwe_from_path(struct multipath * mpp)
195 {
196         struct path * pp = NULL;
197         int pg_num = -1, p_num = -1, i;
198         struct pathgroup * pgp = NULL;
199
200         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
201
202         if (mpp && mpp->pg) {
203                 vector_foreach_slot(mpp->pg, pgp, i) {
204                         if (pgp->status == PGSTATE_ACTIVE ||
205                             pgp->status == PGSTATE_ENABLED) {
206                                 pg_num = i;
207                                 break;
208                         }
209                 }
210                 if (pg_num >= 0)
211                         pgp = VECTOR_SLOT(mpp->pg, pg_num);
212         }
213
214         if (pgp && pgp->paths) {
215                 vector_foreach_slot(pgp->paths, pp, i) {
216                         if (pp->dmstate == PSTATE_FAILED)
217                                 continue;
218                         if (strlen(pp->vendor_id) > 0 &&
219                             strlen(pp->product_id) > 0 &&
220                             strlen(pp->rev) > 0) {
221                                 p_num = i;
222                                 break;
223                         }
224                 }
225                 if (p_num >= 0)
226                         pp = VECTOR_SLOT(pgp->paths, i);
227         }
228
229         if (pp) {
230                 if (!strlen(pp->vendor_id) ||
231                     !strlen(pp->product_id) ||
232                     !strlen(pp->rev)) {
233                         condlog(3, "%s: no device details available", pp->dev);
234                         return NULL;
235                 }
236                 condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
237                 condlog(3, "%s: product = %s", pp->dev, pp->product_id);
238                 condlog(3, "%s: rev = %s", pp->dev, pp->rev);
239                 if (!pp->hwe) {
240                         condlog(3, "searching hwtable");
241                         pp->hwe = find_hwe(conf->hwtable, pp->vendor_id,
242                                            pp->product_id, pp->rev);
243                 }
244         }
245
246         return pp?pp->hwe:NULL;
247 }
248
249 static int
250 update_multipath_table (struct multipath *mpp, vector pathvec)
251 {
252         char params[PARAMS_SIZE] = {0};
253
254         if (!mpp)
255                 return 1;
256
257         if (dm_get_map(mpp->alias, &mpp->size, params)) {
258                 condlog(3, "%s: cannot get map", mpp->alias);
259                 return 1;
260         }
261
262         if (disassemble_map(pathvec, params, mpp)) {
263                 condlog(3, "%s: cannot disassemble map", mpp->alias);
264                 return 1;
265         }
266
267         return 0;
268 }
269
270 static int
271 update_multipath_status (struct multipath *mpp)
272 {
273         char status[PARAMS_SIZE] = {0};
274
275         if (!mpp)
276                 return 1;
277
278         if (dm_get_status(mpp->alias, status)) {
279                 condlog(3, "%s: cannot get status", mpp->alias);
280                 return 1;
281         }
282
283         if (disassemble_status(status, mpp)) {
284                 condlog(3, "%s: cannot disassemble status", mpp->alias);
285                 return 1;
286         }
287
288         return 0;
289 }
290
291 void sync_paths(struct multipath *mpp, vector pathvec)
292 {
293         struct path *pp;
294         struct pathgroup  *pgp;
295         int found, i, j;
296
297         vector_foreach_slot (mpp->paths, pp, i) {
298                 found = 0;
299                 vector_foreach_slot(mpp->pg, pgp, j) {
300                         if (find_slot(pgp->paths, (void *)pp) != -1) {
301                                 found = 1;
302                                 break;
303                         }
304                 }
305                 if (!found) {
306                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
307                         vector_del_slot(mpp->paths, i--);
308                         orphan_path(pp, "path removed externally");
309                 }
310         }
311         update_mpp_paths(mpp, pathvec);
312         vector_foreach_slot (mpp->paths, pp, i)
313                 pp->mpp = mpp;
314 }
315
316 extern int
317 update_multipath_strings (struct multipath *mpp, vector pathvec)
318 {
319         if (!mpp)
320                 return 1;
321
322         update_mpp_paths(mpp, pathvec);
323         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
324
325         free_multipath_attributes(mpp);
326         free_pgvec(mpp->pg, KEEP_PATHS);
327         mpp->pg = NULL;
328
329         if (update_multipath_table(mpp, pathvec))
330                 return 1;
331         sync_paths(mpp, pathvec);
332
333         if (update_multipath_status(mpp))
334                 return 1;
335
336         return 0;
337 }
338
339 extern void
340 set_no_path_retry(struct multipath *mpp)
341 {
342         mpp->retry_tick = 0;
343         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
344         select_no_path_retry(mpp);
345
346         switch (mpp->no_path_retry) {
347         case NO_PATH_RETRY_UNDEF:
348                 break;
349         case NO_PATH_RETRY_FAIL:
350                 dm_queue_if_no_path(mpp->alias, 0);
351                 break;
352         case NO_PATH_RETRY_QUEUE:
353                 dm_queue_if_no_path(mpp->alias, 1);
354                 break;
355         default:
356                 dm_queue_if_no_path(mpp->alias, 1);
357                 if (mpp->nr_active == 0) {
358                         /* Enter retry mode */
359                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
360                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
361                                 mpp->alias, mpp->no_path_retry);
362                 }
363                 break;
364         }
365 }
366
367 extern int
368 __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset)
369 {
370         if (dm_get_info(mpp->alias, &mpp->dmi)) {
371                 /* Error accessing table */
372                 condlog(3, "%s: cannot access table", mpp->alias);
373                 goto out;
374         }
375
376         if (!dm_map_present(mpp->alias)) {
377                 /* Table has been removed */
378                 condlog(3, "%s: table does not exist", mpp->alias);
379                 goto out;
380         }
381
382         if (update_multipath_strings(mpp, vecs->pathvec)) {
383                 condlog(0, "%s: failed to setup multipath", mpp->alias);
384                 goto out;
385         }
386
387         set_multipath_wwid(mpp);
388         mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
389         condlog(3, "%s: discover", mpp->alias);
390
391         if (!mpp->hwe)
392                 mpp->hwe = extract_hwe_from_path(mpp);
393         if (!mpp->hwe) {
394                 condlog(3, "%s: no hardware entry found, using defaults",
395                         mpp->alias);
396         }
397         if (reset) {
398                 select_rr_weight(mpp);
399                 select_pgfailback(mpp);
400                 set_no_path_retry(mpp);
401                 select_flush_on_last_del(mpp);
402                 if (VECTOR_SIZE(mpp->paths) != 0)
403                         dm_cancel_deferred_remove(mpp);
404         }
405
406         return 0;
407 out:
408         remove_map(mpp, vecs, PURGE_VEC);
409         return 1;
410 }
411
412 extern struct multipath *
413 add_map_without_path (struct vectors * vecs, char * alias)
414 {
415         struct multipath * mpp = alloc_multipath();
416
417         if (!mpp)
418                 return NULL;
419         if (!alias) {
420                 FREE(mpp);
421                 return NULL;
422         }
423
424         mpp->alias = STRDUP(alias);
425
426         if (setup_multipath(vecs, mpp))
427                 return NULL; /* mpp freed in setup_multipath */
428
429         if (adopt_paths(vecs->pathvec, mpp))
430                 goto out;
431
432         if (!vector_alloc_slot(vecs->mpvec))
433                 goto out;
434
435         vector_set_slot(vecs->mpvec, mpp);
436
437         if (start_waiter_thread(mpp, vecs))
438                 goto out;
439
440         return mpp;
441 out:
442         remove_map(mpp, vecs, PURGE_VEC);
443         return NULL;
444 }
445
446 static void
447 find_existing_alias (struct multipath * mpp,
448                      struct vectors *vecs)
449 {
450         struct multipath * mp;
451         int i;
452
453         vector_foreach_slot (vecs->mpvec, mp, i)
454                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
455                         strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
456                         return;
457                 }
458 }
459
460 extern struct multipath *
461 add_map_with_path (struct vectors * vecs,
462                    struct path * pp, int add_vec)
463 {
464         struct multipath * mpp;
465
466         if (!strlen(pp->wwid))
467                 return NULL;
468
469         if (!(mpp = alloc_multipath()))
470                 return NULL;
471
472         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
473         mpp->hwe = pp->hwe;
474
475         strcpy(mpp->wwid, pp->wwid);
476         find_existing_alias(mpp, vecs);
477         if (select_alias(mpp))
478                 goto out;
479         mpp->size = pp->size;
480
481         if (adopt_paths(vecs->pathvec, mpp))
482                 goto out;
483
484         if (add_vec) {
485                 if (!vector_alloc_slot(vecs->mpvec))
486                         goto out;
487
488                 vector_set_slot(vecs->mpvec, mpp);
489         }
490
491         return mpp;
492
493 out:
494         remove_map(mpp, vecs, PURGE_VEC);
495         return NULL;
496 }
497
498 extern int
499 verify_paths(struct multipath * mpp, struct vectors * vecs)
500 {
501         struct path * pp;
502         int count = 0;
503         int i, j;
504
505         if (!mpp)
506                 return 0;
507
508         vector_foreach_slot (mpp->paths, pp, i) {
509                 /*
510                  * see if path is in sysfs
511                  */
512                 if (sysfs_attr_get_value(pp->udev, "dev",
513                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
514                         if (pp->state != PATH_DOWN) {
515                                 condlog(1, "%s: removing valid path %s in state %d",
516                                         mpp->alias, pp->dev, pp->state);
517                         } else {
518                                 condlog(3, "%s: failed to access path %s",
519                                         mpp->alias, pp->dev);
520                         }
521                         count++;
522                         vector_del_slot(mpp->paths, i);
523                         i--;
524
525                         if ((j = find_slot(vecs->pathvec,
526                                            (void *)pp)) != -1)
527                                 vector_del_slot(vecs->pathvec, j);
528                         free_path(pp);
529                 } else {
530                         condlog(4, "%s: verified path %s dev_t %s",
531                                 mpp->alias, pp->dev, pp->dev_t);
532                 }
533         }
534         return count;
535 }
536
537 int update_multipath (struct vectors *vecs, char *mapname, int reset)
538 {
539         struct multipath *mpp;
540         struct pathgroup  *pgp;
541         struct path *pp;
542         int i, j;
543
544         mpp = find_mp_by_alias(vecs->mpvec, mapname);
545
546         if (!mpp) {
547                 condlog(3, "%s: multipath map not found", mapname);
548                 return 2;
549         }
550
551         if (__setup_multipath(vecs, mpp, reset))
552                 return 1; /* mpp freed in setup_multipath */
553
554         /*
555          * compare checkers states with DM states
556          */
557         vector_foreach_slot (mpp->pg, pgp, i) {
558                 vector_foreach_slot (pgp->paths, pp, j) {
559                         if (pp->dmstate != PSTATE_FAILED)
560                                 continue;
561
562                         if (pp->state != PATH_DOWN) {
563                                 int oldstate = pp->state;
564                                 condlog(2, "%s: mark as failed", pp->dev);
565                                 mpp->stat_path_failures++;
566                                 pp->state = PATH_DOWN;
567                                 if (oldstate == PATH_UP ||
568                                     oldstate == PATH_GHOST)
569                                         update_queue_mode_del_path(mpp);
570
571                                 /*
572                                  * if opportune,
573                                  * schedule the next check earlier
574                                  */
575                                 if (pp->tick > conf->checkint)
576                                         pp->tick = conf->checkint;
577                         }
578                 }
579         }
580         return 0;
581 }
582
583 /*
584  * mpp->no_path_retry:
585  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
586  *   -1 (FAIL)  : fail_if_no_path
587  *    0 (UNDEF) : nothing
588  *   >0         : queue_if_no_path enabled, turned off after polling n times
589  */
590 void update_queue_mode_del_path(struct multipath *mpp)
591 {
592         if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
593                 /*
594                  * Enter retry mode.
595                  * meaning of +1: retry_tick may be decremented in
596                  *                checkerloop before starting retry.
597                  */
598                 mpp->stat_queueing_timeouts++;
599                 mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
600                 condlog(1, "%s: Entering recovery mode: max_retries=%d",
601                         mpp->alias, mpp->no_path_retry);
602         }
603         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
604 }
605
606 void update_queue_mode_add_path(struct multipath *mpp)
607 {
608         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
609                 /* come back to normal mode from retry mode */
610                 mpp->retry_tick = 0;
611                 dm_queue_if_no_path(mpp->alias, 1);
612                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
613                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
614         }
615         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
616 }
617