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