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