cb0ebae6901672fad1d8e0512461a3a0258ff81a
[platform/upstream/multipath-tools.git] / libmultipath / structs_vec.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <libudev.h>
5
6 #include "util.h"
7 #include "checkers.h"
8 #include "vector.h"
9 #include "defaults.h"
10 #include "debug.h"
11 #include "config.h"
12 #include "structs.h"
13 #include "structs_vec.h"
14 #include "sysfs.h"
15 #include "devmapper.h"
16 #include "dmparser.h"
17 #include "propsel.h"
18 #include "discovery.h"
19 #include "prio.h"
20 #include "configure.h"
21 #include "libdevmapper.h"
22 #include "io_err_stat.h"
23 #include "switchgroup.h"
24
25 /*
26  * creates or updates mpp->paths reading mpp->pg
27  */
28 int update_mpp_paths(struct multipath *mpp, vector pathvec)
29 {
30         struct pathgroup * pgp;
31         struct path * pp;
32         int i,j;
33         bool store_failure = false;
34
35         if (!mpp || !mpp->pg)
36                 return 0;
37
38         if (!mpp->paths &&
39             !(mpp->paths = vector_alloc()))
40                 return 1;
41
42         vector_foreach_slot (mpp->pg, pgp, i) {
43                 vector_foreach_slot (pgp->paths, pp, j) {
44                         if (!find_path_by_devt(mpp->paths, pp->dev_t)) {
45                                 struct path *pp1;
46
47                                 /*
48                                  * Avoid adding removed paths to the map again
49                                  * when we reload it. Such paths may exist in
50                                  * ev_remove_paths() or if it returns failure.
51                                  */
52                                 pp1 = find_path_by_devt(pathvec, pp->dev_t);
53                                 if (pp1 && pp->initialized != INIT_REMOVED &&
54                                     store_path(mpp->paths, pp))
55                                         store_failure = true;
56                         }
57                 }
58         }
59
60         return store_failure;
61 }
62
63 static bool guess_mpp_wwid(struct multipath *mpp)
64 {
65         int i, j;
66         struct pathgroup *pgp;
67         struct path *pp;
68
69         if (strlen(mpp->wwid) || !mpp->pg)
70                 return true;
71
72         vector_foreach_slot(mpp->pg, pgp, i) {
73                 if (!pgp->paths)
74                         continue;
75                 vector_foreach_slot(pgp->paths, pp, j) {
76                         if (pp->initialized == INIT_OK && strlen(pp->wwid)) {
77                                 strlcpy(mpp->wwid, pp->wwid, sizeof(mpp->wwid));
78                                 condlog(2, "%s: guessed WWID %s from path %s",
79                                         mpp->alias, mpp->wwid, pp->dev);
80                                 return true;
81                         }
82                 }
83         }
84         condlog(1, "%s: unable to guess WWID", mpp->alias);
85         return false;
86 }
87
88 /*
89  * update_pathvec_from_dm() - update pathvec after disassemble_map()
90  *
91  * disassemble_map() may return block devices that are members in
92  * multipath maps but haven't been discovered. Check whether they
93  * need to be added to pathvec or discarded.
94  *
95  * Returns: true if immediate map reload is desirable
96  *
97  * Side effects:
98  * - may delete non-existing paths and empty pathgroups from mpp
99  * - may set pp->wwid and / or mpp->wwid
100  * - calls pathinfo() on existing paths is pathinfo_flags is not 0
101  */
102 static bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
103         int pathinfo_flags)
104 {
105         int i, j;
106         struct pathgroup *pgp;
107         struct path *pp;
108         struct config *conf;
109         bool mpp_has_wwid;
110         bool must_reload = false;
111
112         if (!mpp->pg)
113                 return false;
114
115         /*
116          * This will initialize mpp->wwid with an educated guess,
117          * either from the dm uuid or from a member path with properly
118          * determined WWID.
119          */
120         mpp_has_wwid = guess_mpp_wwid(mpp);
121
122         vector_foreach_slot(mpp->pg, pgp, i) {
123                 if (!pgp->paths)
124                         goto delete_pg;
125
126                 vector_foreach_slot(pgp->paths, pp, j) {
127
128                         if (pp->mpp && pp->mpp != mpp) {
129                                 condlog(0, "BUG: %s: found path %s which is already in %s",
130                                         mpp->alias, pp->dev, pp->mpp->alias);
131
132                                 /*
133                                  * Either we added this path to the other mpp
134                                  * explicitly, or we came by here earlier and
135                                  * decided it belonged there. In both cases,
136                                  * the path should remain in the other map,
137                                  * and be deleted here.
138                                  */
139                                 must_reload = true;
140                                 dm_fail_path(mpp->alias, pp->dev_t);
141                                 vector_del_slot(pgp->paths, j--);
142                                 continue;
143                         }
144                         pp->mpp = mpp;
145
146                         /*
147                          * The way disassemble_map() works: If it encounters a
148                          * path device which isn't found in pathvec, it adds an
149                          * uninitialized struct path to pgp->paths, with only
150                          * pp->dev_t filled in. Thus if pp->udev is set here,
151                          * we know that the path is in pathvec already.
152                          */
153                         if (pp->udev) {
154                                 if (pathinfo_flags & ~DI_NOIO) {
155                                         conf = get_multipath_config();
156                                         pthread_cleanup_push(put_multipath_config,
157                                                              conf);
158                                         if (pathinfo(pp, conf, pathinfo_flags) != PATHINFO_OK)
159                                                 condlog(2, "%s: pathinfo failed for existing path %s (flags=0x%x)",
160                                                         __func__, pp->dev, pathinfo_flags);
161                                         pthread_cleanup_pop(1);
162                                 }
163                         } else {
164                                 /* If this fails, the device is not in sysfs */
165                                 pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
166
167                                 if (!pp->udev) {
168                                         condlog(2, "%s: discarding non-existing path %s",
169                                                 mpp->alias, pp->dev_t);
170                                         vector_del_slot(pgp->paths, j--);
171                                         free_path(pp);
172                                         must_reload = true;
173                                         continue;
174                                 } else {
175                                         int rc;
176
177                                         strlcpy(pp->dev, udev_device_get_sysname(pp->udev),
178                                                 sizeof(pp->dev));
179                                         conf = get_multipath_config();
180                                         pthread_cleanup_push(put_multipath_config,
181                                                              conf);
182                                         pp->checkint = conf->checkint;
183                                         rc = pathinfo(pp, conf,
184                                                       DI_SYSFS|DI_WWID|DI_BLACKLIST|DI_NOFALLBACK|pathinfo_flags);
185                                         pthread_cleanup_pop(1);
186                                         if (rc != PATHINFO_OK) {
187                                                 condlog(1, "%s: error %d in pathinfo, discarding path",
188                                                         pp->dev, rc);
189                                                 vector_del_slot(pgp->paths, j--);
190                                                 free_path(pp);
191                                                 must_reload = true;
192                                                 continue;
193                                         }
194                                         condlog(2, "%s: adding new path %s",
195                                                 mpp->alias, pp->dev);
196                                         pp->initialized = INIT_PARTIAL;
197                                         pp->partial_retrigger_delay = 180;
198                                         store_path(pathvec, pp);
199                                         pp->tick = 1;
200                                 }
201                         }
202
203                         /* We don't set the map WWID from paths here */
204                         if (!mpp_has_wwid)
205                                 continue;
206
207                         /*
208                          * At this point, pp->udev is valid and and pp->wwid
209                          * is the best we could get
210                          */
211                         if (*pp->wwid && strcmp(mpp->wwid, pp->wwid)) {
212                                 condlog(0, "%s: path %s WWID %s doesn't match, removing from map",
213                                         mpp->wwid, pp->dev_t, pp->wwid);
214                                 /*
215                                  * This path exists, but in the wrong map.
216                                  * We can't reload the map from here.
217                                  * Make sure it isn't used in this map
218                                  * any more, and let the checker re-add
219                                  * it as it sees fit.
220                                  */
221                                 dm_fail_path(mpp->alias, pp->dev_t);
222                                 vector_del_slot(pgp->paths, j--);
223                                 orphan_path(pp, "WWID mismatch");
224                                 pp->tick = 1;
225                                 must_reload = true;
226                         } else if (!*pp->wwid) {
227                                 condlog(3, "%s: setting wwid from map: %s",
228                                         pp->dev, mpp->wwid);
229                                 strlcpy(pp->wwid, mpp->wwid,
230                                         sizeof(pp->wwid));
231                         }
232                 }
233                 if (VECTOR_SIZE(pgp->paths) != 0)
234                         continue;
235         delete_pg:
236                 condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
237                 vector_del_slot(mpp->pg, i--);
238                 free_pathgroup(pgp, KEEP_PATHS);
239                 must_reload = true;
240         }
241         mpp->need_reload = mpp->need_reload || must_reload;
242         return must_reload;
243 }
244
245 int adopt_paths(vector pathvec, struct multipath *mpp)
246 {
247         int i, ret;
248         struct path * pp;
249         struct config *conf;
250
251         if (!mpp)
252                 return 0;
253
254         if (update_mpp_paths(mpp, pathvec))
255                 return 1;
256
257         vector_foreach_slot (pathvec, pp, i) {
258                 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
259                         if (pp->size != 0 && mpp->size != 0 &&
260                             pp->size != mpp->size) {
261                                 condlog(3, "%s: size mismatch for %s, not adding path",
262                                         pp->dev, mpp->alias);
263                                 continue;
264                         }
265                         if (pp->initialized == INIT_REMOVED)
266                                 continue;
267                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
268                                 goto err;
269
270                         conf = get_multipath_config();
271                         pthread_cleanup_push(put_multipath_config, conf);
272                         ret = pathinfo(pp, conf,
273                                        DI_PRIO | DI_CHECKER);
274                         pthread_cleanup_pop(1);
275                         if (ret) {
276                                 condlog(3, "%s: pathinfo failed for %s",
277                                         __func__, pp->dev);
278                                 continue;
279                         }
280
281                         if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
282                             store_path(mpp->paths, pp))
283                                 goto err;
284
285                         pp->mpp = mpp;
286                         condlog(3, "%s: ownership set to %s",
287                                 pp->dev, mpp->alias);
288                 }
289         }
290         return 0;
291 err:
292         condlog(1, "error setting ownership of %s to %s", pp->dev, mpp->alias);
293         return 1;
294 }
295
296 void orphan_path(struct path *pp, const char *reason)
297 {
298         condlog(3, "%s: orphan path, %s", pp->dev, reason);
299         pp->mpp = NULL;
300         uninitialize_path(pp);
301 }
302
303 static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
304 {
305         int i;
306         struct path * pp;
307
308         vector_foreach_slot (pathvec, pp, i) {
309                 if (pp->mpp == mpp) {
310                         if (pp->initialized == INIT_REMOVED ||
311                             pp->initialized == INIT_PARTIAL) {
312                                 condlog(3, "%s: freeing path in %s state",
313                                         pp->dev,
314                                         pp->initialized == INIT_REMOVED ?
315                                         "removed" : "partial");
316                                 vector_del_slot(pathvec, i--);
317                                 free_path(pp);
318                         } else
319                                 orphan_path(pp, reason);
320                 }
321         }
322 }
323
324 void set_path_removed(struct path *pp)
325 {
326         struct multipath *mpp = pp->mpp;
327
328         orphan_path(pp, "removed");
329         /*
330          * Keep link to mpp. It will be removed when the path
331          * is successfully removed from the map.
332          */
333         if (!mpp)
334                 condlog(0, "%s: internal error: mpp == NULL", pp->dev);
335         pp->mpp = mpp;
336         pp->initialized = INIT_REMOVED;
337 }
338
339 void
340 remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
341 {
342         int i;
343
344         free_pathvec(mpp->paths, KEEP_PATHS);
345         free_pgvec(mpp->pg, KEEP_PATHS);
346         mpp->paths = mpp->pg = NULL;
347
348         /*
349          * clear references to this map
350          */
351         orphan_paths(pathvec, mpp, "map removed internally");
352
353         if (mpvec &&
354             (i = find_slot(mpvec, (void *)mpp)) != -1)
355                 vector_del_slot(mpvec, i);
356
357         /*
358          * final free
359          */
360         free_multipath(mpp, KEEP_PATHS);
361 }
362
363 void
364 remove_map_by_alias(const char *alias, struct vectors * vecs)
365 {
366         struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
367         if (mpp) {
368                 condlog(2, "%s: removing map by alias", alias);
369                 remove_map(mpp, vecs->pathvec, vecs->mpvec);
370         }
371 }
372
373 void
374 remove_maps(struct vectors * vecs)
375 {
376         int i;
377         struct multipath * mpp;
378
379         if (!vecs)
380                 return;
381
382         vector_foreach_slot (vecs->mpvec, mpp, i) {
383                 remove_map(mpp, vecs->pathvec, vecs->mpvec);
384                 i--;
385         }
386
387         vector_free(vecs->mpvec);
388         vecs->mpvec = NULL;
389 }
390
391 void
392 extract_hwe_from_path(struct multipath * mpp)
393 {
394         struct path * pp = NULL;
395         int i;
396
397         if (mpp->hwe || !mpp->paths)
398                 return;
399
400         condlog(4, "%s: searching paths for valid hwe", mpp->alias);
401         /* doing this in two passes seems like paranoia to me */
402         vector_foreach_slot(mpp->paths, pp, i) {
403                 if (pp->state == PATH_UP && pp->initialized != INIT_PARTIAL &&
404                     pp->initialized != INIT_REMOVED && pp->hwe)
405                         goto done;
406         }
407         vector_foreach_slot(mpp->paths, pp, i) {
408                 if ((pp->state != PATH_UP || pp->initialized == INIT_PARTIAL) &&
409                     pp->initialized != INIT_REMOVED && pp->hwe)
410                         goto done;
411         }
412 done:
413         if (i < VECTOR_SIZE(mpp->paths))
414                 (void)set_mpp_hwe(mpp, pp);
415
416         if (mpp->hwe)
417                 condlog(3, "%s: got hwe from path %s", mpp->alias, pp->dev);
418         else
419                 condlog(2, "%s: no hwe found", mpp->alias);
420 }
421
422 int
423 update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
424 {
425         int r = DMP_ERR;
426         char *params = NULL;
427
428         if (!mpp)
429                 return r;
430
431         r = dm_get_map(mpp->alias, &mpp->size, &params);
432         if (r != DMP_OK) {
433                 condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
434                 return r;
435         }
436
437         if (disassemble_map(pathvec, params, mpp)) {
438                 condlog(2, "%s: cannot disassemble map", mpp->alias);
439                 free(params);
440                 return DMP_ERR;
441         }
442
443         free(params);
444         params = NULL;
445         if (dm_get_status(mpp->alias, &params) != DMP_OK)
446                 condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
447         else if (disassemble_status(params, mpp))
448                 condlog(2, "%s: cannot disassemble status", mpp->alias);
449         free(params);
450
451         /* FIXME: we should deal with the return value here */
452         update_pathvec_from_dm(pathvec, mpp, flags);
453
454         return DMP_OK;
455 }
456
457 static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
458                                             const char *dev_t)
459 {
460         struct pathgroup  *pgp;
461         struct path *pp;
462         int j;
463
464         vector_foreach_slot(mpp->pg, pgp, j) {
465                 pp = find_path_by_devt(pgp->paths, dev_t);
466                 if (pp)
467                         return pp;
468         }
469         return NULL;
470 }
471
472 static void check_removed_paths(const struct multipath *mpp, vector pathvec)
473 {
474         struct path *pp;
475         int i;
476
477         vector_foreach_slot(pathvec, pp, i) {
478                 if (pp->mpp == mpp &&
479                     (pp->initialized == INIT_REMOVED ||
480                      pp->initialized == INIT_PARTIAL) &&
481                     !find_devt_in_pathgroups(mpp, pp->dev_t)) {
482                         condlog(2, "%s: %s: freeing path in %s state",
483                                 __func__, pp->dev,
484                                 pp->initialized == INIT_REMOVED ?
485                                 "removed" : "partial");
486                         vector_del_slot(pathvec, i--);
487                         free_path(pp);
488                 }
489         }
490 }
491
492 void sync_paths(struct multipath *mpp, vector pathvec)
493 {
494         struct path *pp;
495         struct pathgroup  *pgp;
496         int found, i, j;
497
498         vector_foreach_slot (mpp->paths, pp, i) {
499                 found = 0;
500                 vector_foreach_slot(mpp->pg, pgp, j) {
501                         if (find_slot(pgp->paths, (void *)pp) != -1) {
502                                 found = 1;
503                                 break;
504                         }
505                 }
506                 if (!found) {
507                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
508                         vector_del_slot(mpp->paths, i--);
509                         orphan_path(pp, "path removed externally");
510                 }
511         }
512         check_removed_paths(mpp, pathvec);
513         update_mpp_paths(mpp, pathvec);
514         vector_foreach_slot (mpp->paths, pp, i)
515                 pp->mpp = mpp;
516 }
517
518 int
519 update_multipath_strings(struct multipath *mpp, vector pathvec)
520 {
521         struct pathgroup *pgp;
522         int i, r = DMP_ERR;
523
524         if (!mpp)
525                 return r;
526
527         update_mpp_paths(mpp, pathvec);
528         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
529
530         free_multipath_attributes(mpp);
531         free_pgvec(mpp->pg, KEEP_PATHS);
532         mpp->pg = NULL;
533
534         r = update_multipath_table(mpp, pathvec, 0);
535         if (r != DMP_OK)
536                 return r;
537
538         sync_paths(mpp, pathvec);
539
540         vector_foreach_slot(mpp->pg, pgp, i)
541                 if (pgp->paths)
542                         path_group_prio_update(pgp);
543
544         return DMP_OK;
545 }
546
547 static void enter_recovery_mode(struct multipath *mpp)
548 {
549         unsigned int checkint;
550         struct config *conf;
551
552         if (mpp->in_recovery || mpp->no_path_retry <= 0)
553                 return;
554
555         conf = get_multipath_config();
556         checkint = conf->checkint;
557         put_multipath_config(conf);
558
559         /*
560          * Enter retry mode.
561          * meaning of +1: retry_tick may be decremented in checkerloop before
562          * starting retry.
563          */
564         mpp->in_recovery = true;
565         mpp->stat_queueing_timeouts++;
566         mpp->retry_tick = mpp->no_path_retry * checkint + 1;
567         condlog(1, "%s: Entering recovery mode: max_retries=%d",
568                 mpp->alias, mpp->no_path_retry);
569 }
570
571 static void leave_recovery_mode(struct multipath *mpp)
572 {
573         bool recovery = mpp->in_recovery;
574
575         mpp->in_recovery = false;
576         mpp->retry_tick = 0;
577
578         /*
579          * in_recovery is only ever set if mpp->no_path_retry > 0
580          * (see enter_recovery_mode()). But no_path_retry may have been
581          * changed while the map was recovering, so test it here again.
582          */
583         if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE ||
584                          mpp->no_path_retry > 0)) {
585                 dm_queue_if_no_path(mpp->alias, 1);
586                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
587                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
588         }
589 }
590
591 void __set_no_path_retry(struct multipath *mpp, bool check_features)
592 {
593         bool is_queueing = false; /* assign a value to make gcc happy */
594
595         check_features = check_features && mpp->features != NULL;
596         if (check_features)
597                 is_queueing = strstr(mpp->features, "queue_if_no_path");
598
599         switch (mpp->no_path_retry) {
600         case NO_PATH_RETRY_UNDEF:
601                 break;
602         case NO_PATH_RETRY_FAIL:
603                 if (!check_features || is_queueing)
604                         dm_queue_if_no_path(mpp->alias, 0);
605                 break;
606         case NO_PATH_RETRY_QUEUE:
607                 if (!check_features || !is_queueing)
608                         dm_queue_if_no_path(mpp->alias, 1);
609                 break;
610         default:
611                 if (count_active_paths(mpp) > 0) {
612                         /*
613                          * If in_recovery is set, leave_recovery_mode() takes
614                          * care of dm_queue_if_no_path. Otherwise, do it here.
615                          */
616                         if ((!check_features || !is_queueing) &&
617                             !mpp->in_recovery)
618                                 dm_queue_if_no_path(mpp->alias, 1);
619                         leave_recovery_mode(mpp);
620                 } else
621                         enter_recovery_mode(mpp);
622                 break;
623         }
624 }
625
626 void
627 sync_map_state(struct multipath *mpp)
628 {
629         struct pathgroup *pgp;
630         struct path *pp;
631         unsigned int i, j;
632
633         if (!mpp->pg)
634                 return;
635
636         vector_foreach_slot (mpp->pg, pgp, i){
637                 vector_foreach_slot (pgp->paths, pp, j){
638                         if (pp->state == PATH_UNCHECKED ||
639                             pp->state == PATH_WILD ||
640                             pp->state == PATH_DELAYED)
641                                 continue;
642                         if (mpp->ghost_delay_tick > 0)
643                                 continue;
644                         if ((pp->dmstate == PSTATE_FAILED ||
645                              pp->dmstate == PSTATE_UNDEF) &&
646                             (pp->state == PATH_UP || pp->state == PATH_GHOST))
647                                 dm_reinstate_path(mpp->alias, pp->dev_t);
648                         else if ((pp->dmstate == PSTATE_ACTIVE ||
649                                   pp->dmstate == PSTATE_UNDEF) &&
650                                  (pp->state == PATH_DOWN ||
651                                   pp->state == PATH_SHAKY)) {
652                                 condlog(2, "sync_map_state: failing %s state %d dmstate %d",
653                                         pp->dev, pp->state, pp->dmstate);
654                                 dm_fail_path(mpp->alias, pp->dev_t);
655                         }
656                 }
657         }
658 }
659
660 static void
661 find_existing_alias (struct multipath * mpp,
662                      struct vectors *vecs)
663 {
664         struct multipath * mp;
665         int i;
666
667         vector_foreach_slot (vecs->mpvec, mp, i)
668                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
669                         strlcpy(mpp->alias_old, mp->alias, WWID_SIZE);
670                         return;
671                 }
672 }
673
674 struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
675                                     int add_vec)
676 {
677         struct multipath * mpp;
678         struct config *conf = NULL;
679
680         if (!strlen(pp->wwid))
681                 return NULL;
682
683         if (!(mpp = alloc_multipath()))
684                 return NULL;
685
686         conf = get_multipath_config();
687         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
688         put_multipath_config(conf);
689
690         /*
691          * We need to call this before select_alias(),
692          * because that accesses hwe properties.
693          */
694         if (pp->hwe && !set_mpp_hwe(mpp, pp))
695                 goto out;
696
697         strcpy(mpp->wwid, pp->wwid);
698         find_existing_alias(mpp, vecs);
699         if (select_alias(conf, mpp))
700                 goto out;
701         mpp->size = pp->size;
702
703         if (adopt_paths(vecs->pathvec, mpp) || pp->mpp != mpp ||
704             find_slot(mpp->paths, pp) == -1)
705                 goto out;
706
707         if (add_vec) {
708                 if (!vector_alloc_slot(vecs->mpvec))
709                         goto out;
710
711                 vector_set_slot(vecs->mpvec, mpp);
712         }
713
714         return mpp;
715
716 out:
717         remove_map(mpp, vecs->pathvec, vecs->mpvec);
718         return NULL;
719 }
720
721 int verify_paths(struct multipath *mpp)
722 {
723         struct path * pp;
724         int count = 0;
725         int i;
726
727         if (!mpp)
728                 return 0;
729
730         vector_foreach_slot (mpp->paths, pp, i) {
731                 /*
732                  * see if path is in sysfs
733                  */
734                 if (!pp->udev || sysfs_attr_get_value(pp->udev, "dev",
735                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
736                         if (pp->state != PATH_DOWN) {
737                                 condlog(1, "%s: removing valid path %s in state %d",
738                                         mpp->alias, pp->dev, pp->state);
739                         } else {
740                                 condlog(2, "%s: failed to access path %s",
741                                         mpp->alias, pp->dev);
742                         }
743                         count++;
744                         vector_del_slot(mpp->paths, i);
745                         i--;
746
747                         /*
748                          * Don't delete path from pathvec yet. We'll do this
749                          * after the path has been removed from the map, in
750                          * sync_paths().
751                          */
752                         set_path_removed(pp);
753                 } else {
754                         condlog(4, "%s: verified path %s dev_t %s",
755                                 mpp->alias, pp->dev, pp->dev_t);
756                 }
757         }
758         return count;
759 }
760
761 /*
762  * mpp->no_path_retry:
763  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
764  *   -1 (FAIL)  : fail_if_no_path
765  *    0 (UNDEF) : nothing
766  *   >0         : queue_if_no_path enabled, turned off after polling n times
767  */
768 void update_queue_mode_del_path(struct multipath *mpp)
769 {
770         int active = count_active_paths(mpp);
771
772         if (active == 0) {
773                 enter_recovery_mode(mpp);
774                 if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
775                         mpp->stat_map_failures++;
776         }
777         condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
778 }
779
780 void update_queue_mode_add_path(struct multipath *mpp)
781 {
782         int active = count_active_paths(mpp);
783
784         if (active > 0)
785                 leave_recovery_mode(mpp);
786         condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
787 }
788
789 vector get_used_hwes(const struct _vector *pathvec)
790 {
791         int i, j;
792         struct path *pp;
793         struct hwentry *hwe;
794         vector v = vector_alloc();
795
796         if (v == NULL)
797                 return NULL;
798
799         vector_foreach_slot(pathvec, pp, i) {
800                 vector_foreach_slot_backwards(pp->hwe, hwe, j) {
801                         vector_find_or_add_slot(v, hwe);
802                 }
803         }
804
805         return v;
806 }