multipathd: move helper functions to libmultipath
[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 "config.h"
10 #include "structs.h"
11 #include "structs_vec.h"
12 #include "sysfs.h"
13 #include "waiter.h"
14 #include "devmapper.h"
15 #include "dmparser.h"
16 #include "propsel.h"
17 #include "discovery.h"
18 #include "prio.h"
19 #include "configure.h"
20 #include "libdevmapper.h"
21
22 /*
23  * creates or updates mpp->paths reading mpp->pg
24  */
25 int update_mpp_paths(struct multipath *mpp, vector pathvec)
26 {
27         struct pathgroup * pgp;
28         struct path * pp;
29         int i,j;
30
31         if (!mpp || !mpp->pg)
32                 return 0;
33
34         if (!mpp->paths &&
35             !(mpp->paths = vector_alloc()))
36                 return 1;
37
38         vector_foreach_slot (mpp->pg, pgp, i) {
39                 vector_foreach_slot (pgp->paths, pp, j) {
40                         if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
41                             (find_path_by_devt(pathvec, pp->dev_t)) &&
42                             store_path(mpp->paths, pp))
43                                 return 1;
44                 }
45         }
46         return 0;
47 }
48
49 int adopt_paths(vector pathvec, struct multipath *mpp)
50 {
51         int i, ret;
52         struct path * pp;
53         struct config *conf;
54
55         if (!mpp)
56                 return 0;
57
58         if (update_mpp_paths(mpp, pathvec))
59                 return 1;
60
61         vector_foreach_slot (pathvec, pp, i) {
62                 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
63                         condlog(3, "%s: ownership set to %s",
64                                 pp->dev, mpp->alias);
65                         pp->mpp = mpp;
66
67                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
68                                 return 1;
69
70                         if (!find_path_by_dev(mpp->paths, pp->dev) &&
71                             store_path(mpp->paths, pp))
72                                         return 1;
73                         conf = get_multipath_config();
74                         ret = pathinfo(pp, conf,
75                                        DI_PRIO | DI_CHECKER);
76                         put_multipath_config(conf);
77                         if (ret)
78                                 return 1;
79                 }
80         }
81         return 0;
82 }
83
84 void orphan_path(struct path *pp, const char *reason)
85 {
86         condlog(3, "%s: orphan path, %s", pp->dev, reason);
87         pp->mpp = NULL;
88         pp->dmstate = PSTATE_UNDEF;
89         pp->uid_attribute = NULL;
90         pp->getuid = NULL;
91         prio_put(&pp->prio);
92         checker_put(&pp->checker);
93         if (pp->fd >= 0)
94                 close(pp->fd);
95         pp->fd = -1;
96 }
97
98 void orphan_paths(vector pathvec, struct multipath *mpp)
99 {
100         int i;
101         struct path * pp;
102
103         vector_foreach_slot (pathvec, pp, i) {
104                 if (pp->mpp == mpp) {
105                         orphan_path(pp, "map flushed");
106                 }
107         }
108 }
109
110 static void
111 set_multipath_wwid (struct multipath * mpp)
112 {
113         if (strlen(mpp->wwid))
114                 return;
115
116         dm_get_uuid(mpp->alias, mpp->wwid);
117 }
118
119 #define KEEP_WAITER 0
120 #define STOP_WAITER 1
121 #define PURGE_VEC 1
122
123 static void
124 _remove_map (struct multipath * mpp, struct vectors * vecs,
125             int stop_waiter, int purge_vec)
126 {
127         int i;
128
129         condlog(4, "%s: remove multipath map", mpp->alias);
130
131         /*
132          * stop the DM event waiter thread
133          */
134         if (stop_waiter)
135                 stop_waiter_thread(mpp, vecs);
136
137         /*
138          * clear references to this map
139          */
140         orphan_paths(vecs->pathvec, mpp);
141
142         if (purge_vec &&
143             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
144                 vector_del_slot(vecs->mpvec, i);
145
146         /*
147          * final free
148          */
149         free_multipath(mpp, KEEP_PATHS);
150 }
151
152 void remove_map(struct multipath *mpp, struct vectors *vecs, int purge_vec)
153 {
154         _remove_map(mpp, vecs, KEEP_WAITER, purge_vec);
155 }
156
157 void 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 void remove_maps(struct vectors *vecs)
182 {
183         _remove_maps(vecs, KEEP_WAITER);
184 }
185
186 void remove_maps_and_stop_waiters(struct vectors *vecs)
187 {
188         _remove_maps(vecs, STOP_WAITER);
189 }
190
191 static struct hwentry *
192 extract_hwe_from_path(struct multipath * mpp)
193 {
194         struct path * pp = NULL;
195         int pg_num = -1, p_num = -1, i;
196         struct pathgroup * pgp = NULL;
197
198         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
199
200         if (mpp && mpp->pg) {
201                 vector_foreach_slot(mpp->pg, pgp, i) {
202                         if (pgp->status == PGSTATE_ACTIVE ||
203                             pgp->status == PGSTATE_ENABLED) {
204                                 pg_num = i;
205                                 break;
206                         }
207                 }
208                 if (pg_num >= 0)
209                         pgp = VECTOR_SLOT(mpp->pg, pg_num);
210         }
211
212         if (pgp && pgp->paths) {
213                 vector_foreach_slot(pgp->paths, pp, i) {
214                         if (pp->dmstate == PSTATE_FAILED)
215                                 continue;
216                         if (strlen(pp->vendor_id) > 0 &&
217                             strlen(pp->product_id) > 0 &&
218                             strlen(pp->rev) > 0) {
219                                 p_num = i;
220                                 break;
221                         }
222                 }
223                 if (p_num >= 0)
224                         pp = VECTOR_SLOT(pgp->paths, i);
225         }
226
227         if (pp) {
228                 if (!strlen(pp->vendor_id) ||
229                     !strlen(pp->product_id) ||
230                     !strlen(pp->rev)) {
231                         condlog(3, "%s: no device details available", pp->dev);
232                         return NULL;
233                 }
234                 condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
235                 condlog(3, "%s: product = %s", pp->dev, pp->product_id);
236                 condlog(3, "%s: rev = %s", pp->dev, pp->rev);
237                 if (!pp->hwe) {
238                         struct config *conf = get_multipath_config();
239
240                         condlog(3, "searching hwtable");
241                         pp->hwe = find_hwe(conf->hwtable, pp->vendor_id,
242                                            pp->product_id, pp->rev);
243                         put_multipath_config(conf);
244                 }
245         }
246
247         return pp?pp->hwe:NULL;
248 }
249
250 static int
251 update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
252 {
253         char params[PARAMS_SIZE] = {0};
254
255         if (!mpp)
256                 return 1;
257
258         if (dm_get_map(mpp->alias, &mpp->size, params)) {
259                 condlog(3, "%s: cannot get map", mpp->alias);
260                 return 1;
261         }
262
263         if (disassemble_map(pathvec, params, mpp, is_daemon)) {
264                 condlog(3, "%s: cannot disassemble map", mpp->alias);
265                 return 1;
266         }
267
268         return 0;
269 }
270
271 static int
272 update_multipath_status (struct multipath *mpp)
273 {
274         char status[PARAMS_SIZE] = {0};
275
276         if (!mpp)
277                 return 1;
278
279         if (dm_get_status(mpp->alias, status)) {
280                 condlog(3, "%s: cannot get status", mpp->alias);
281                 return 1;
282         }
283
284         if (disassemble_status(status, mpp)) {
285                 condlog(3, "%s: cannot disassemble status", mpp->alias);
286                 return 1;
287         }
288
289         return 0;
290 }
291
292 void sync_paths(struct multipath *mpp, vector pathvec)
293 {
294         struct path *pp;
295         struct pathgroup  *pgp;
296         int found, i, j;
297
298         vector_foreach_slot (mpp->paths, pp, i) {
299                 found = 0;
300                 vector_foreach_slot(mpp->pg, pgp, j) {
301                         if (find_slot(pgp->paths, (void *)pp) != -1) {
302                                 found = 1;
303                                 break;
304                         }
305                 }
306                 if (!found) {
307                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
308                         vector_del_slot(mpp->paths, i--);
309                         orphan_path(pp, "path removed externally");
310                 }
311         }
312         update_mpp_paths(mpp, pathvec);
313         vector_foreach_slot (mpp->paths, pp, i)
314                 pp->mpp = mpp;
315 }
316
317 int
318 update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
319 {
320         if (!mpp)
321                 return 1;
322
323         update_mpp_paths(mpp, pathvec);
324         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
325
326         free_multipath_attributes(mpp);
327         free_pgvec(mpp->pg, KEEP_PATHS);
328         mpp->pg = NULL;
329
330         if (update_multipath_table(mpp, pathvec, is_daemon))
331                 return 1;
332         sync_paths(mpp, pathvec);
333
334         if (update_multipath_status(mpp))
335                 return 1;
336
337         return 0;
338 }
339
340 void set_no_path_retry(struct config *conf, 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(conf, 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                         struct config *conf = get_multipath_config();
359                         /* Enter retry mode */
360                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
361                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
362                                 mpp->alias, mpp->no_path_retry);
363                         put_multipath_config(conf);
364                 }
365                 break;
366         }
367 }
368
369 int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
370                       int reset, int is_daemon)
371 {
372         struct config *conf;
373
374         if (dm_get_info(mpp->alias, &mpp->dmi)) {
375                 /* Error accessing table */
376                 condlog(3, "%s: cannot access table", mpp->alias);
377                 goto out;
378         }
379
380         if (!dm_map_present(mpp->alias)) {
381                 /* Table has been removed */
382                 condlog(3, "%s: table does not exist", mpp->alias);
383                 goto out;
384         }
385
386         if (update_multipath_strings(mpp, vecs->pathvec, is_daemon)) {
387                 condlog(0, "%s: failed to setup multipath", mpp->alias);
388                 goto out;
389         }
390
391         set_multipath_wwid(mpp);
392         conf = get_multipath_config();
393         mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
394         put_multipath_config(conf);
395         condlog(3, "%s: discover", mpp->alias);
396
397         if (!mpp->hwe)
398                 mpp->hwe = extract_hwe_from_path(mpp);
399         if (!mpp->hwe) {
400                 condlog(3, "%s: no hardware entry found, using defaults",
401                         mpp->alias);
402         }
403         if (reset) {
404                 conf = get_multipath_config();
405                 select_rr_weight(conf, mpp);
406                 select_pgfailback(conf, mpp);
407                 set_no_path_retry(conf, mpp);
408                 select_flush_on_last_del(conf, mpp);
409                 put_multipath_config(conf);
410                 if (VECTOR_SIZE(mpp->paths) != 0)
411                         dm_cancel_deferred_remove(mpp);
412         }
413
414         return 0;
415 out:
416         remove_map(mpp, vecs, PURGE_VEC);
417         return 1;
418 }
419
420 void
421 sync_map_state(struct multipath *mpp)
422 {
423         struct pathgroup *pgp;
424         struct path *pp;
425         unsigned int i, j;
426
427         if (!mpp->pg)
428                 return;
429
430         vector_foreach_slot (mpp->pg, pgp, i){
431                 vector_foreach_slot (pgp->paths, pp, j){
432                         if (pp->state == PATH_UNCHECKED ||
433                             pp->state == PATH_WILD ||
434                             pp->state == PATH_DELAYED)
435                                 continue;
436                         if (mpp->ghost_delay_tick > 0)
437                                 continue;
438                         if ((pp->dmstate == PSTATE_FAILED ||
439                              pp->dmstate == PSTATE_UNDEF) &&
440                             (pp->state == PATH_UP || pp->state == PATH_GHOST))
441                                 dm_reinstate_path(mpp->alias, pp->dev_t);
442                         else if ((pp->dmstate == PSTATE_ACTIVE ||
443                                   pp->dmstate == PSTATE_UNDEF) &&
444                                  (pp->state == PATH_DOWN ||
445                                   pp->state == PATH_SHAKY))
446                                 dm_fail_path(mpp->alias, pp->dev_t);
447                 }
448         }
449 }
450
451 int
452 update_map (struct multipath *mpp, struct vectors *vecs)
453 {
454         int retries = 3;
455         char params[PARAMS_SIZE] = {0};
456
457 retry:
458         condlog(4, "%s: updating new map", mpp->alias);
459         if (adopt_paths(vecs->pathvec, mpp)) {
460                 condlog(0, "%s: failed to adopt paths for new map update",
461                         mpp->alias);
462                 retries = -1;
463                 goto fail;
464         }
465         verify_paths(mpp, vecs);
466         mpp->flush_on_last_del = FLUSH_UNDEF;
467         mpp->action = ACT_RELOAD;
468
469         if (setup_map(mpp, params, PARAMS_SIZE)) {
470                 condlog(0, "%s: failed to setup new map in update", mpp->alias);
471                 retries = -1;
472                 goto fail;
473         }
474         if (domap(mpp, params, 1) <= 0 && retries-- > 0) {
475                 condlog(0, "%s: map_udate sleep", mpp->alias);
476                 sleep(1);
477                 goto retry;
478         }
479         dm_lib_release();
480
481 fail:
482         if (setup_multipath(vecs, mpp))
483                 return 1;
484
485         sync_map_state(mpp);
486
487         if (retries < 0)
488                 condlog(0, "%s: failed reload in new map update", mpp->alias);
489         return 0;
490 }
491
492 struct multipath *add_map_without_path (struct vectors *vecs, char *alias)
493 {
494         struct multipath * mpp = alloc_multipath();
495
496         if (!mpp)
497                 return NULL;
498         if (!alias) {
499                 FREE(mpp);
500                 return NULL;
501         }
502
503         mpp->alias = STRDUP(alias);
504
505         if (setup_multipath(vecs, mpp))
506                 return NULL; /* mpp freed in setup_multipath */
507
508         if (adopt_paths(vecs->pathvec, mpp))
509                 goto out;
510
511         if (!vector_alloc_slot(vecs->mpvec))
512                 goto out;
513
514         vector_set_slot(vecs->mpvec, mpp);
515
516         if (start_waiter_thread(mpp, vecs))
517                 goto out;
518
519         return mpp;
520 out:
521         remove_map(mpp, vecs, PURGE_VEC);
522         return NULL;
523 }
524
525 static void
526 find_existing_alias (struct multipath * mpp,
527                      struct vectors *vecs)
528 {
529         struct multipath * mp;
530         int i;
531
532         vector_foreach_slot (vecs->mpvec, mp, i)
533                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
534                         strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
535                         return;
536                 }
537 }
538
539 struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
540                                     int add_vec)
541 {
542         struct multipath * mpp;
543         struct config *conf = NULL;
544
545         if (!strlen(pp->wwid))
546                 return NULL;
547
548         if (!(mpp = alloc_multipath()))
549                 return NULL;
550
551         conf = get_multipath_config();
552         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
553         mpp->hwe = pp->hwe;
554         put_multipath_config(conf);
555
556         strcpy(mpp->wwid, pp->wwid);
557         find_existing_alias(mpp, vecs);
558         if (select_alias(conf, mpp))
559                 goto out;
560         mpp->size = pp->size;
561
562         if (adopt_paths(vecs->pathvec, mpp))
563                 goto out;
564
565         if (add_vec) {
566                 if (!vector_alloc_slot(vecs->mpvec))
567                         goto out;
568
569                 vector_set_slot(vecs->mpvec, mpp);
570         }
571
572         return mpp;
573
574 out:
575         remove_map(mpp, vecs, PURGE_VEC);
576         return NULL;
577 }
578
579 int verify_paths(struct multipath *mpp, struct vectors *vecs)
580 {
581         struct path * pp;
582         int count = 0;
583         int i, j;
584
585         if (!mpp)
586                 return 0;
587
588         vector_foreach_slot (mpp->paths, pp, i) {
589                 /*
590                  * see if path is in sysfs
591                  */
592                 if (sysfs_attr_get_value(pp->udev, "dev",
593                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
594                         if (pp->state != PATH_DOWN) {
595                                 condlog(1, "%s: removing valid path %s in state %d",
596                                         mpp->alias, pp->dev, pp->state);
597                         } else {
598                                 condlog(3, "%s: failed to access path %s",
599                                         mpp->alias, pp->dev);
600                         }
601                         count++;
602                         vector_del_slot(mpp->paths, i);
603                         i--;
604
605                         if ((j = find_slot(vecs->pathvec,
606                                            (void *)pp)) != -1)
607                                 vector_del_slot(vecs->pathvec, j);
608                         free_path(pp);
609                 } else {
610                         condlog(4, "%s: verified path %s dev_t %s",
611                                 mpp->alias, pp->dev, pp->dev_t);
612                 }
613         }
614         return count;
615 }
616
617 int update_multipath (struct vectors *vecs, char *mapname, int reset)
618 {
619         struct multipath *mpp;
620         struct pathgroup  *pgp;
621         struct path *pp;
622         int i, j;
623
624         mpp = find_mp_by_alias(vecs->mpvec, mapname);
625
626         if (!mpp) {
627                 condlog(3, "%s: multipath map not found", mapname);
628                 return 2;
629         }
630
631         if (__setup_multipath(vecs, mpp, reset, 1))
632                 return 1; /* mpp freed in setup_multipath */
633
634         /*
635          * compare checkers states with DM states
636          */
637         vector_foreach_slot (mpp->pg, pgp, i) {
638                 vector_foreach_slot (pgp->paths, pp, j) {
639                         if (pp->dmstate != PSTATE_FAILED)
640                                 continue;
641
642                         if (pp->state != PATH_DOWN) {
643                                 struct config *conf = get_multipath_config();
644                                 int oldstate = pp->state;
645                                 condlog(2, "%s: mark as failed", pp->dev);
646                                 mpp->stat_path_failures++;
647                                 pp->state = PATH_DOWN;
648                                 if (oldstate == PATH_UP ||
649                                     oldstate == PATH_GHOST)
650                                         update_queue_mode_del_path(mpp);
651
652                                 /*
653                                  * if opportune,
654                                  * schedule the next check earlier
655                                  */
656                                 if (pp->tick > conf->checkint)
657                                         pp->tick = conf->checkint;
658                                 put_multipath_config(conf);
659                         }
660                 }
661         }
662         return 0;
663 }
664
665 /*
666  * mpp->no_path_retry:
667  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
668  *   -1 (FAIL)  : fail_if_no_path
669  *    0 (UNDEF) : nothing
670  *   >0         : queue_if_no_path enabled, turned off after polling n times
671  */
672 void update_queue_mode_del_path(struct multipath *mpp)
673 {
674         if (--mpp->nr_active == 0) {
675                 if (mpp->no_path_retry > 0) {
676                         struct config *conf = get_multipath_config();
677
678                         /*
679                          * Enter retry mode.
680                          * meaning of +1: retry_tick may be decremented in
681                          *                checkerloop before starting retry.
682                          */
683                         mpp->stat_queueing_timeouts++;
684                         mpp->retry_tick = mpp->no_path_retry *
685                                           conf->checkint + 1;
686                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
687                                 mpp->alias, mpp->no_path_retry);
688                         put_multipath_config(conf);
689                 } else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
690                         mpp->stat_map_failures++;
691         }
692         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
693 }
694
695 void update_queue_mode_add_path(struct multipath *mpp)
696 {
697         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
698                 /* come back to normal mode from retry mode */
699                 mpp->retry_tick = 0;
700                 dm_queue_if_no_path(mpp->alias, 1);
701                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
702                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
703         }
704         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
705 }