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