Improve logging for orphan_path()
[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                 condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
230                 condlog(3, "%s: product = %s", pp->dev, pp->product_id);
231                 condlog(3, "%s: rev = %s", pp->dev, pp->rev);
232                 if (!pp->hwe) {
233                         condlog(3, "searching hwtable");
234                         pp->hwe = find_hwe(conf->hwtable, pp->vendor_id,
235                                            pp->product_id, pp->rev);
236                 }
237         }
238
239         return pp?pp->hwe:NULL;
240 }
241
242 static int
243 update_multipath_table (struct multipath *mpp, vector pathvec)
244 {
245         char params[PARAMS_SIZE] = {0};
246
247         if (!mpp)
248                 return 1;
249
250         if (dm_get_map(mpp->alias, &mpp->size, params)) {
251                 condlog(3, "%s: cannot get map", mpp->alias);
252                 return 1;
253         }
254
255         if (disassemble_map(pathvec, params, mpp)) {
256                 condlog(3, "%s: cannot disassemble map", mpp->alias);
257                 return 1;
258         }
259
260         return 0;
261 }
262
263 static int
264 update_multipath_status (struct multipath *mpp)
265 {
266         char status[PARAMS_SIZE] = {0};
267
268         if (!mpp)
269                 return 1;
270
271         if (dm_get_status(mpp->alias, status)) {
272                 condlog(3, "%s: cannot get status", mpp->alias);
273                 return 1;
274         }
275
276         if (disassemble_status(status, mpp)) {
277                 condlog(3, "%s: cannot disassemble status", mpp->alias);
278                 return 1;
279         }
280
281         return 0;
282 }
283
284 extern int
285 update_multipath_strings (struct multipath *mpp, vector pathvec)
286 {
287         if (!mpp)
288                 return 1;
289
290         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
291
292         free_multipath_attributes(mpp);
293         free_pgvec(mpp->pg, KEEP_PATHS);
294         mpp->pg = NULL;
295
296         if (update_multipath_table(mpp, pathvec))
297                 return 1;
298
299         if (update_multipath_status(mpp))
300                 return 1;
301
302         return 0;
303 }
304
305 extern void
306 set_no_path_retry(struct multipath *mpp)
307 {
308         mpp->retry_tick = 0;
309         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
310         select_no_path_retry(mpp);
311
312         switch (mpp->no_path_retry) {
313         case NO_PATH_RETRY_UNDEF:
314                 break;
315         case NO_PATH_RETRY_FAIL:
316                 dm_queue_if_no_path(mpp->alias, 0);
317                 break;
318         case NO_PATH_RETRY_QUEUE:
319                 dm_queue_if_no_path(mpp->alias, 1);
320                 break;
321         default:
322                 dm_queue_if_no_path(mpp->alias, 1);
323                 if (mpp->nr_active == 0) {
324                         /* Enter retry mode */
325                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
326                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
327                                 mpp->alias, mpp->no_path_retry);
328                 }
329                 break;
330         }
331 }
332
333 extern int
334 __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset)
335 {
336         if (dm_get_info(mpp->alias, &mpp->dmi)) {
337                 /* Error accessing table */
338                 condlog(3, "%s: cannot access table", mpp->alias);
339                 goto out;
340         }
341
342         if (!dm_map_present(mpp->alias)) {
343                 /* Table has been removed */
344                 condlog(3, "%s: table does not exist", mpp->alias);
345                 goto out;
346         }
347
348         set_multipath_wwid(mpp);
349         mpp->mpe = find_mpe(mpp->wwid);
350         condlog(3, "%s: discover", mpp->alias);
351
352         if (update_multipath_strings(mpp, vecs->pathvec)) {
353                 condlog(0, "%s: failed to setup multipath", mpp->alias);
354                 goto out;
355         }
356
357         if (!mpp->hwe)
358                 mpp->hwe = extract_hwe_from_path(mpp);
359         if (!mpp->hwe) {
360                 condlog(3, "%s: no hardware entry found, using defaults",
361                         mpp->alias);
362         }
363         if (reset) {
364                 select_rr_weight(mpp);
365                 select_pgfailback(mpp);
366                 set_no_path_retry(mpp);
367                 select_flush_on_last_del(mpp);
368         }
369
370         return 0;
371 out:
372         remove_map(mpp, vecs, PURGE_VEC);
373         return 1;
374 }
375
376 extern struct multipath *
377 add_map_without_path (struct vectors * vecs, char * alias)
378 {
379         struct multipath * mpp = alloc_multipath();
380
381         if (!mpp || !alias)
382                 return NULL;
383
384         mpp->alias = STRDUP(alias);
385
386         if (setup_multipath(vecs, mpp))
387                 return NULL; /* mpp freed in setup_multipath */
388
389         if (adopt_paths(vecs->pathvec, mpp, 1))
390                 goto out;
391
392         if (!vector_alloc_slot(vecs->mpvec))
393                 goto out;
394
395         vector_set_slot(vecs->mpvec, mpp);
396
397         if (start_waiter_thread(mpp, vecs))
398                 goto out;
399
400         return mpp;
401 out:
402         remove_map(mpp, vecs, PURGE_VEC);
403         return NULL;
404 }
405
406 extern struct multipath *
407 add_map_with_path (struct vectors * vecs,
408                    struct path * pp, int add_vec)
409 {
410         struct multipath * mpp;
411
412         if (!(mpp = alloc_multipath()))
413                 return NULL;
414
415         mpp->mpe = find_mpe(pp->wwid);
416         mpp->hwe = pp->hwe;
417
418         strcpy(mpp->wwid, pp->wwid);
419         if (select_alias(mpp))
420                 goto out;
421         mpp->size = pp->size;
422
423         if (adopt_paths(vecs->pathvec, mpp, 1))
424                 goto out;
425
426         if (add_vec) {
427                 if (!vector_alloc_slot(vecs->mpvec))
428                         goto out;
429
430                 vector_set_slot(vecs->mpvec, mpp);
431         }
432
433         return mpp;
434
435 out:
436         remove_map(mpp, vecs, PURGE_VEC);
437         return NULL;
438 }
439
440 extern int
441 verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
442 {
443         struct path * pp;
444         int count = 0;
445         int i, j;
446
447         if (!mpp)
448                 return 0;
449
450         vector_foreach_slot (mpp->paths, pp, i) {
451                 /*
452                  * see if path is in sysfs
453                  */
454                 if (!pp->udev || sysfs_get_dev(pp->udev, pp->dev_t,
455                                                BLK_DEV_SIZE)) {
456                         if (pp->state != PATH_DOWN) {
457                                 condlog(1, "%s: removing valid path %s in state %d",
458                                         mpp->alias, pp->dev, pp->state);
459                         } else {
460                                 condlog(3, "%s: failed to access path %s",
461                                         mpp->alias, pp->dev);
462                         }
463                         count++;
464                         vector_del_slot(mpp->paths, i);
465                         i--;
466
467                         if (rpvec)
468                                 store_path(rpvec, pp);
469                         else {
470                                 if ((j = find_slot(vecs->pathvec,
471                                                    (void *)pp)) != -1)
472                                         vector_del_slot(vecs->pathvec, j);
473                                 free_path(pp);
474                         }
475                 } else {
476                         condlog(4, "%s: verified path %s dev_t %s",
477                                 mpp->alias, pp->dev, pp->dev_t);
478                 }
479         }
480         return count;
481 }
482
483 int update_multipath (struct vectors *vecs, char *mapname, int reset)
484 {
485         struct multipath *mpp;
486         struct pathgroup  *pgp;
487         struct path *pp;
488         int i, j;
489
490         mpp = find_mp_by_alias(vecs->mpvec, mapname);
491
492         if (!mpp) {
493                 condlog(3, "%s: multipath map not found", mapname);
494                 return 2;
495         }
496
497         free_pgvec(mpp->pg, KEEP_PATHS);
498         mpp->pg = NULL;
499
500         if (__setup_multipath(vecs, mpp, reset))
501                 return 1; /* mpp freed in setup_multipath */
502
503         adopt_paths(vecs->pathvec, mpp, 0);
504         /*
505          * compare checkers states with DM states
506          */
507         vector_foreach_slot (mpp->pg, pgp, i) {
508                 vector_foreach_slot (pgp->paths, pp, j) {
509                         if (pp->dmstate != PSTATE_FAILED)
510                                 continue;
511
512                         if (pp->state != PATH_DOWN) {
513                                 int oldstate = pp->state;
514                                 condlog(2, "%s: mark as failed", pp->dev_t);
515                                 mpp->stat_path_failures++;
516                                 pp->state = PATH_DOWN;
517                                 if (oldstate == PATH_UP ||
518                                     oldstate == PATH_GHOST)
519                                         update_queue_mode_del_path(mpp);
520
521                                 /*
522                                  * if opportune,
523                                  * schedule the next check earlier
524                                  */
525                                 if (pp->tick > conf->checkint)
526                                         pp->tick = conf->checkint;
527                         }
528                 }
529         }
530
531         return 0;
532 }
533
534 /*
535  * mpp->no_path_retry:
536  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
537  *   -1 (FAIL)  : fail_if_no_path
538  *    0 (UNDEF) : nothing
539  *   >0         : queue_if_no_path enabled, turned off after polling n times
540  */
541 void update_queue_mode_del_path(struct multipath *mpp)
542 {
543         if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
544                 /*
545                  * Enter retry mode.
546                  * meaning of +1: retry_tick may be decremented in
547                  *                checkerloop before starting retry.
548                  */
549                 mpp->stat_queueing_timeouts++;
550                 mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
551                 condlog(1, "%s: Entering recovery mode: max_retries=%d",
552                         mpp->alias, mpp->no_path_retry);
553         }
554         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
555 }
556
557 void update_queue_mode_add_path(struct multipath *mpp)
558 {
559         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
560                 /* come back to normal mode from retry mode */
561                 mpp->retry_tick = 0;
562                 dm_queue_if_no_path(mpp->alias, 1);
563                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
564                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
565         }
566         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
567 }
568