Check for valid argument in update_multipath_strings()
[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 "sysfs.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, int get_info)
50 {
51         int i;
52         struct path * pp;
53
54         if (!mpp)
55                 return 0;
56
57         if (update_mpp_paths(mpp, pathvec))
58                 return 1;
59
60         vector_foreach_slot (pathvec, pp, i) {
61                 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
62                         condlog(3, "%s: ownership set to %s",
63                                 pp->dev, mpp->alias);
64                         pp->mpp = mpp;
65
66                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
67                                 return 1;
68
69                         if (!find_path_by_dev(mpp->paths, pp->dev) &&
70                             store_path(mpp->paths, pp))
71                                         return 1;
72                         if (get_info)
73                                 pathinfo(pp, conf->hwtable,
74                                          DI_PRIO | DI_CHECKER);
75                 }
76         }
77         return 0;
78 }
79
80 extern void
81 orphan_path (struct path * pp)
82 {
83         pp->mpp = NULL;
84         pp->dmstate = PSTATE_UNDEF;
85         pp->getuid = NULL;
86         pp->prio = NULL;
87         checker_put(&pp->checker);
88         if (pp->fd >= 0)
89                 close(pp->fd);
90         pp->fd = -1;
91 }
92
93 extern void
94 orphan_paths (vector pathvec, struct multipath * mpp)
95 {
96         int i;
97         struct path * pp;
98
99         vector_foreach_slot (pathvec, pp, i) {
100                 if (pp->mpp == mpp) {
101                         condlog(4, "%s: orphaned", pp->dev);
102                         orphan_path(pp);
103                 }
104         }
105 }
106
107 static void
108 set_multipath_wwid (struct multipath * mpp)
109 {
110         if (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                 return 1;
252
253         if (disassemble_map(pathvec, params, mpp))
254                 return 1;
255
256         return 0;
257 }
258
259 static int
260 update_multipath_status (struct multipath *mpp)
261 {
262         char status[PARAMS_SIZE] = {0};
263
264         if (!mpp)
265                 return 1;
266
267         if(dm_get_status(mpp->alias, status))
268                 return 1;
269
270         if (disassemble_status(status, mpp))
271                 return 1;
272
273         return 0;
274 }
275
276 extern int
277 update_multipath_strings (struct multipath *mpp, vector pathvec)
278 {
279         if (!mpp)
280                 return 1;
281
282         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
283
284         free_multipath_attributes(mpp);
285         free_pgvec(mpp->pg, KEEP_PATHS);
286         mpp->pg = NULL;
287
288         if (update_multipath_table(mpp, pathvec))
289                 return 1;
290
291         if (update_multipath_status(mpp))
292                 return 1;
293
294         return 0;
295 }
296
297 extern void
298 set_no_path_retry(struct multipath *mpp)
299 {
300         mpp->retry_tick = 0;
301         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
302         if (mpp->nr_active > 0)
303                 select_no_path_retry(mpp);
304
305         switch (mpp->no_path_retry) {
306         case NO_PATH_RETRY_UNDEF:
307                 break;
308         case NO_PATH_RETRY_FAIL:
309                 dm_queue_if_no_path(mpp->alias, 0);
310                 break;
311         case NO_PATH_RETRY_QUEUE:
312                 dm_queue_if_no_path(mpp->alias, 1);
313                 break;
314         default:
315                 dm_queue_if_no_path(mpp->alias, 1);
316                 if (mpp->nr_active == 0) {
317                         /* Enter retry mode */
318                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
319                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
320                                 mpp->alias, mpp->no_path_retry);
321                 }
322                 break;
323         }
324 }
325
326 extern int
327 setup_multipath (struct vectors * vecs, struct multipath * mpp)
328 {
329 retry:
330         if (dm_get_info(mpp->alias, &mpp->dmi)) {
331                 /* Error accessing table */
332                 condlog(3, "%s: cannot access table", mpp->alias);
333                 goto out;
334         }
335
336         if (!dm_map_present(mpp->alias)) {
337                 /* Table has been removed */
338                 condlog(3, "%s: table does not exist", mpp->alias);
339                 goto out;
340         }
341
342         set_multipath_wwid(mpp);
343         mpp->mpe = find_mpe(mpp->wwid);
344         condlog(3, "%s: discover", mpp->alias);
345
346         if (update_multipath_strings(mpp, vecs->pathvec)) {
347                 char *new_alias;
348
349                 /*
350                  * detect an external rename of the multipath device
351                  */
352                 new_alias = dm_get_name(mpp->wwid);
353                 if (new_alias) {
354                         condlog(3, "%s multipath mapped device name has "
355                                 "changed from %s to %s", mpp->wwid,
356                                 mpp->alias, new_alias);
357                         FREE(mpp->alias);
358                         mpp->alias = new_alias;
359
360                         if (mpp->waiter)
361                                 strncpy(((struct event_thread *)mpp->waiter)->mapname,
362                                         new_alias, WWID_SIZE);
363                         goto retry;
364                 }
365                 condlog(0, "%s: failed to setup multipath", mpp->alias);
366                 goto out;
367         }
368
369         if (!mpp->hwe)
370                 mpp->hwe = extract_hwe_from_path(mpp);
371         if (!mpp->hwe) {
372                 condlog(3, "%s: no hardware entry found, using defaults",
373                         mpp->alias);
374         }
375         select_rr_weight(mpp);
376         select_pgfailback(mpp);
377         set_no_path_retry(mpp);
378         select_pg_timeout(mpp);
379         select_flush_on_last_del(mpp);
380
381         return 0;
382 out:
383         remove_map(mpp, vecs, PURGE_VEC);
384         return 1;
385 }
386
387 extern struct multipath *
388 add_map_without_path (struct vectors * vecs, char * alias)
389 {
390         struct multipath * mpp = alloc_multipath();
391
392         if (!mpp || !alias)
393                 return NULL;
394
395         mpp->alias = alias;
396
397         if (setup_multipath(vecs, mpp)) {
398                 mpp->alias = NULL;
399                 return NULL; /* mpp freed in setup_multipath */
400         }
401
402         if (adopt_paths(vecs->pathvec, mpp, 1))
403                 goto out;
404
405         if (!vector_alloc_slot(vecs->mpvec))
406                 goto out;
407
408         vector_set_slot(vecs->mpvec, mpp);
409
410         if (start_waiter_thread(mpp, vecs))
411                 goto out;
412
413         return mpp;
414 out:
415         remove_map(mpp, vecs, PURGE_VEC);
416         return NULL;
417 }
418
419 extern struct multipath *
420 add_map_with_path (struct vectors * vecs,
421                    struct path * pp, int add_vec)
422 {
423         struct multipath * mpp;
424
425         if (!(mpp = alloc_multipath()))
426                 return NULL;
427
428         mpp->mpe = find_mpe(pp->wwid);
429         mpp->hwe = pp->hwe;
430
431         strcpy(mpp->wwid, pp->wwid);
432         if (select_alias(mpp))
433                 goto out;
434         mpp->size = pp->size;
435
436         if (adopt_paths(vecs->pathvec, mpp, 1))
437                 goto out;
438
439         if (add_vec) {
440                 if (!vector_alloc_slot(vecs->mpvec))
441                         goto out;
442
443                 vector_set_slot(vecs->mpvec, mpp);
444         }
445
446         return mpp;
447
448 out:
449         remove_map(mpp, vecs, PURGE_VEC);
450         return NULL;
451 }
452
453 extern int
454 verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
455 {
456         struct path * pp;
457         int count = 0;
458         int i, j;
459
460         if (!mpp)
461                 return 0;
462
463         vector_foreach_slot (mpp->paths, pp, i) {
464                 /*
465                  * see if path is in sysfs
466                  */
467                 if (!pp->sysdev || sysfs_get_dev(pp->sysdev,
468                                                  pp->dev_t, BLK_DEV_SIZE)) {
469                         condlog(0, "%s: failed to access path %s", mpp->alias,
470                                 pp->sysdev ? pp->sysdev->devpath : pp->dev_t);
471                         count++;
472                         vector_del_slot(mpp->paths, i);
473                         i--;
474
475                         if (rpvec)
476                                 store_path(rpvec, pp);
477                         else {
478                                 if ((j = find_slot(vecs->pathvec,
479                                                    (void *)pp)) != -1)
480                                         vector_del_slot(vecs->pathvec, j);
481                                 free_path(pp);
482                         }
483                 } else {
484                         condlog(4, "%s: verified path %s dev_t %s",
485                                 mpp->alias, pp->dev, pp->dev_t);
486                 }
487         }
488         return count;
489 }
490
491 int update_multipath (struct vectors *vecs, char *mapname)
492 {
493         struct multipath *mpp;
494         struct pathgroup  *pgp;
495         struct path *pp;
496         int i, j;
497
498         mpp = find_mp_by_alias(vecs->mpvec, mapname);
499
500         if (!mpp) {
501                 condlog(3, "%s: multipath map not found\n", mapname);
502                 return 2;
503         }
504
505         free_pgvec(mpp->pg, KEEP_PATHS);
506         mpp->pg = NULL;
507
508         if (setup_multipath(vecs, mpp))
509                 return 1; /* mpp freed in setup_multipath */
510
511         adopt_paths(vecs->pathvec, mpp, 0);
512         /*
513          * compare checkers states with DM states
514          */
515         vector_foreach_slot (mpp->pg, pgp, i) {
516                 vector_foreach_slot (pgp->paths, pp, j) {
517                         if (pp->dmstate != PSTATE_FAILED)
518                                 continue;
519
520                         if (pp->state != PATH_DOWN) {
521                                 int oldstate = pp->state;
522                                 condlog(2, "%s: mark as failed", pp->dev_t);
523                                 mpp->stat_path_failures++;
524                                 pp->state = PATH_DOWN;
525                                 if (oldstate == PATH_UP ||
526                                     oldstate == PATH_GHOST)
527                                         update_queue_mode_del_path(mpp);
528
529                                 /*
530                                  * if opportune,
531                                  * schedule the next check earlier
532                                  */
533                                 if (pp->tick > conf->checkint)
534                                         pp->tick = conf->checkint;
535                         }
536                 }
537         }
538
539         return 0;
540 }
541
542 /*
543  * mpp->no_path_retry:
544  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
545  *   -1 (FAIL)  : fail_if_no_path
546  *    0 (UNDEF) : nothing
547  *   >0         : queue_if_no_path enabled, turned off after polling n times
548  */
549 void update_queue_mode_del_path(struct multipath *mpp)
550 {
551         if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
552                 /*
553                  * Enter retry mode.
554                  * meaning of +1: retry_tick may be decremented in
555                  *                checkerloop before starting retry.
556                  */
557                 mpp->stat_queueing_timeouts++;
558                 mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
559                 condlog(1, "%s: Entering recovery mode: max_retries=%d",
560                         mpp->alias, mpp->no_path_retry);
561         }
562         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
563 }
564
565 void update_queue_mode_add_path(struct multipath *mpp)
566 {
567         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
568                 /* come back to normal mode from retry mode */
569                 mpp->retry_tick = 0;
570                 dm_queue_if_no_path(mpp->alias, 1);
571                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
572                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
573         }
574         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
575 }
576