Remove all references to hand-craftes sysfs 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 "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)
72                                 pathinfo(pp, conf->hwtable,
73                                          DI_PRIO | DI_CHECKER);
74                 }
75         }
76         return 0;
77 }
78
79 extern void
80 orphan_path (struct path * pp)
81 {
82         pp->mpp = NULL;
83         pp->dmstate = PSTATE_UNDEF;
84         pp->uid_attribute = NULL;
85         pp->prio = NULL;
86         checker_put(&pp->checker);
87         if (pp->fd >= 0)
88                 close(pp->fd);
89         pp->fd = -1;
90 }
91
92 extern void
93 orphan_paths (vector pathvec, struct multipath * mpp)
94 {
95         int i;
96         struct path * pp;
97
98         vector_foreach_slot (pathvec, pp, i) {
99                 if (pp->mpp == mpp) {
100                         condlog(4, "%s: orphaned", pp->dev);
101                         orphan_path(pp);
102                 }
103         }
104 }
105
106 static void
107 set_multipath_wwid (struct multipath * mpp)
108 {
109         if (mpp->wwid)
110                 return;
111
112         dm_get_uuid(mpp->alias, mpp->wwid);
113 }
114
115 #define KEEP_WAITER 0
116 #define STOP_WAITER 1
117 #define PURGE_VEC 1
118
119 static void
120 _remove_map (struct multipath * mpp, struct vectors * vecs,
121             int stop_waiter, int purge_vec)
122 {
123         int i;
124
125         condlog(4, "%s: remove multipath map", mpp->alias);
126
127         /*
128          * stop the DM event waiter thread
129          */
130         if (stop_waiter)
131                 stop_waiter_thread(mpp, vecs);
132
133         /*
134          * clear references to this map
135          */
136         orphan_paths(vecs->pathvec, mpp);
137
138         if (purge_vec &&
139             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
140                 vector_del_slot(vecs->mpvec, i);
141
142         /*
143          * final free
144          */
145         free_multipath(mpp, KEEP_PATHS);
146 }
147
148 extern void
149 remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec)
150 {
151         _remove_map(mpp, vecs, KEEP_WAITER, purge_vec);
152 }
153
154 extern void
155 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 extern void
180 remove_maps (struct vectors * vecs)
181 {
182         _remove_maps(vecs, KEEP_WAITER);
183 }
184
185 extern void
186 remove_maps_and_stop_waiters (struct vectors * vecs)
187 {
188         _remove_maps(vecs, STOP_WAITER);
189 }
190
191 static struct hwentry *
192 extract_hwe_from_path(struct multipath * mpp)
193 {
194         struct path * pp = NULL;
195         int pg_num = -1, p_num = -1, i;
196         struct pathgroup * pgp = NULL;
197
198         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
199
200         if (mpp && mpp->pg) {
201                 vector_foreach_slot(mpp->pg, pgp, i) {
202                         if (pgp->status == PGSTATE_ACTIVE ||
203                             pgp->status == PGSTATE_ENABLED) {
204                                 pg_num = i;
205                                 break;
206                         }
207                 }
208                 if (pg_num >= 0)
209                         pgp = VECTOR_SLOT(mpp->pg, pg_num);
210         }
211
212         if (pgp && pgp->paths) {
213                 vector_foreach_slot(pgp->paths, pp, i) {
214                         if (pp->dmstate == PSTATE_FAILED)
215                                 continue;
216                         if (strlen(pp->vendor_id) > 0 &&
217                             strlen(pp->product_id) > 0 &&
218                             strlen(pp->rev) > 0) {
219                                 p_num = i;
220                                 break;
221                         }
222                 }
223                 if (p_num >= 0)
224                         pp = VECTOR_SLOT(pgp->paths, i);
225         }
226
227         if (pp) {
228                 condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
229                 condlog(3, "%s: product = %s", pp->dev, pp->product_id);
230                 condlog(3, "%s: rev = %s", pp->dev, pp->rev);
231                 if (!pp->hwe) {
232                         condlog(3, "searching hwtable");
233                         pp->hwe = find_hwe(conf->hwtable, pp->vendor_id,
234                                            pp->product_id, pp->rev);
235                 }
236         }
237
238         return pp?pp->hwe:NULL;
239 }
240
241 static int
242 update_multipath_table (struct multipath *mpp, vector pathvec)
243 {
244         char params[PARAMS_SIZE] = {0};
245
246         if (!mpp)
247                 return 1;
248
249         if (dm_get_map(mpp->alias, &mpp->size, params))
250                 return 1;
251
252         if (disassemble_map(pathvec, params, mpp))
253                 return 1;
254
255         return 0;
256 }
257
258 static int
259 update_multipath_status (struct multipath *mpp)
260 {
261         char status[PARAMS_SIZE] = {0};
262
263         if (!mpp)
264                 return 1;
265
266         if(dm_get_status(mpp->alias, status))
267                 return 1;
268
269         if (disassemble_status(status, mpp))
270                 return 1;
271
272         return 0;
273 }
274
275 extern int
276 update_multipath_strings (struct multipath *mpp, vector pathvec)
277 {
278         if (!mpp)
279                 return 1;
280
281         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
282
283         free_multipath_attributes(mpp);
284         free_pgvec(mpp->pg, KEEP_PATHS);
285         mpp->pg = NULL;
286
287         if (update_multipath_table(mpp, pathvec))
288                 return 1;
289
290         if (update_multipath_status(mpp))
291                 return 1;
292
293         return 0;
294 }
295
296 extern void
297 set_no_path_retry(struct multipath *mpp)
298 {
299         mpp->retry_tick = 0;
300         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
301         if (mpp->nr_active > 0)
302                 select_no_path_retry(mpp);
303
304         switch (mpp->no_path_retry) {
305         case NO_PATH_RETRY_UNDEF:
306                 break;
307         case NO_PATH_RETRY_FAIL:
308                 dm_queue_if_no_path(mpp->alias, 0);
309                 break;
310         case NO_PATH_RETRY_QUEUE:
311                 dm_queue_if_no_path(mpp->alias, 1);
312                 break;
313         default:
314                 dm_queue_if_no_path(mpp->alias, 1);
315                 if (mpp->nr_active == 0) {
316                         /* Enter retry mode */
317                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
318                         condlog(1, "%s: Entering recovery mode: max_retries=%d",
319                                 mpp->alias, mpp->no_path_retry);
320                 }
321                 break;
322         }
323 }
324
325 extern int
326 __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset)
327 {
328         if (dm_get_info(mpp->alias, &mpp->dmi)) {
329                 /* Error accessing table */
330                 condlog(3, "%s: cannot access table", mpp->alias);
331                 goto out;
332         }
333
334         if (!dm_map_present(mpp->alias)) {
335                 /* Table has been removed */
336                 condlog(3, "%s: table does not exist", mpp->alias);
337                 goto out;
338         }
339
340         set_multipath_wwid(mpp);
341         mpp->mpe = find_mpe(mpp->wwid);
342         condlog(3, "%s: discover", mpp->alias);
343
344         if (update_multipath_strings(mpp, vecs->pathvec)) {
345                 condlog(0, "%s: failed to setup multipath", mpp->alias);
346                 goto out;
347         }
348
349         if (!mpp->hwe)
350                 mpp->hwe = extract_hwe_from_path(mpp);
351         if (!mpp->hwe) {
352                 condlog(3, "%s: no hardware entry found, using defaults",
353                         mpp->alias);
354         }
355         if (reset) {
356                 select_rr_weight(mpp);
357                 select_pgfailback(mpp);
358                 set_no_path_retry(mpp);
359                 select_pg_timeout(mpp);
360                 select_flush_on_last_del(mpp);
361         }
362
363         return 0;
364 out:
365         remove_map(mpp, vecs, PURGE_VEC);
366         return 1;
367 }
368
369 extern struct multipath *
370 add_map_without_path (struct vectors * vecs, char * alias)
371 {
372         struct multipath * mpp = alloc_multipath();
373
374         if (!mpp || !alias)
375                 return NULL;
376
377         mpp->alias = STRDUP(alias);
378
379         if (setup_multipath(vecs, mpp))
380                 return NULL; /* mpp freed in setup_multipath */
381
382         if (adopt_paths(vecs->pathvec, mpp, 1))
383                 goto out;
384
385         if (!vector_alloc_slot(vecs->mpvec))
386                 goto out;
387
388         vector_set_slot(vecs->mpvec, mpp);
389
390         if (start_waiter_thread(mpp, vecs))
391                 goto out;
392
393         return mpp;
394 out:
395         remove_map(mpp, vecs, PURGE_VEC);
396         return NULL;
397 }
398
399 extern struct multipath *
400 add_map_with_path (struct vectors * vecs,
401                    struct path * pp, int add_vec)
402 {
403         struct multipath * mpp;
404
405         if (!(mpp = alloc_multipath()))
406                 return NULL;
407
408         mpp->mpe = find_mpe(pp->wwid);
409         mpp->hwe = pp->hwe;
410
411         strcpy(mpp->wwid, pp->wwid);
412         if (select_alias(mpp))
413                 goto out;
414         mpp->size = pp->size;
415
416         if (adopt_paths(vecs->pathvec, mpp, 1))
417                 goto out;
418
419         if (add_vec) {
420                 if (!vector_alloc_slot(vecs->mpvec))
421                         goto out;
422
423                 vector_set_slot(vecs->mpvec, mpp);
424         }
425
426         return mpp;
427
428 out:
429         remove_map(mpp, vecs, PURGE_VEC);
430         return NULL;
431 }
432
433 extern int
434 verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
435 {
436         struct path * pp;
437         int count = 0;
438         int i, j;
439
440         if (!mpp)
441                 return 0;
442
443         vector_foreach_slot (mpp->paths, pp, i) {
444                 /*
445                  * see if path is in sysfs
446                  */
447                 if (!pp->udev || sysfs_get_dev(pp->udev, pp->dev_t,
448                                                BLK_DEV_SIZE)) {
449                         if (pp->state != PATH_DOWN) {
450                                 condlog(1, "%s: removing valid path %s in state %d",
451                                         mpp->alias, pp->dev, pp->state);
452                         } else {
453                                 condlog(3, "%s: failed to access path %s",
454                                         mpp->alias, pp->dev);
455                         }
456                         count++;
457                         vector_del_slot(mpp->paths, i);
458                         i--;
459
460                         if (rpvec)
461                                 store_path(rpvec, pp);
462                         else {
463                                 if ((j = find_slot(vecs->pathvec,
464                                                    (void *)pp)) != -1)
465                                         vector_del_slot(vecs->pathvec, j);
466                                 free_path(pp);
467                         }
468                 } else {
469                         condlog(4, "%s: verified path %s dev_t %s",
470                                 mpp->alias, pp->dev, pp->dev_t);
471                 }
472         }
473         return count;
474 }
475
476 int update_multipath (struct vectors *vecs, char *mapname, int reset)
477 {
478         struct multipath *mpp;
479         struct pathgroup  *pgp;
480         struct path *pp;
481         int i, j;
482
483         mpp = find_mp_by_alias(vecs->mpvec, mapname);
484
485         if (!mpp) {
486                 condlog(3, "%s: multipath map not found\n", mapname);
487                 return 2;
488         }
489
490         free_pgvec(mpp->pg, KEEP_PATHS);
491         mpp->pg = NULL;
492
493         if (__setup_multipath(vecs, mpp, reset))
494                 return 1; /* mpp freed in setup_multipath */
495
496         adopt_paths(vecs->pathvec, mpp, 0);
497         /*
498          * compare checkers states with DM states
499          */
500         vector_foreach_slot (mpp->pg, pgp, i) {
501                 vector_foreach_slot (pgp->paths, pp, j) {
502                         if (pp->dmstate != PSTATE_FAILED)
503                                 continue;
504
505                         if (pp->state != PATH_DOWN) {
506                                 int oldstate = pp->state;
507                                 condlog(2, "%s: mark as failed", pp->dev_t);
508                                 mpp->stat_path_failures++;
509                                 pp->state = PATH_DOWN;
510                                 if (oldstate == PATH_UP ||
511                                     oldstate == PATH_GHOST)
512                                         update_queue_mode_del_path(mpp);
513
514                                 /*
515                                  * if opportune,
516                                  * schedule the next check earlier
517                                  */
518                                 if (pp->tick > conf->checkint)
519                                         pp->tick = conf->checkint;
520                         }
521                 }
522         }
523
524         return 0;
525 }
526
527 /*
528  * mpp->no_path_retry:
529  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
530  *   -1 (FAIL)  : fail_if_no_path
531  *    0 (UNDEF) : nothing
532  *   >0         : queue_if_no_path enabled, turned off after polling n times
533  */
534 void update_queue_mode_del_path(struct multipath *mpp)
535 {
536         if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
537                 /*
538                  * Enter retry mode.
539                  * meaning of +1: retry_tick may be decremented in
540                  *                checkerloop before starting retry.
541                  */
542                 mpp->stat_queueing_timeouts++;
543                 mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
544                 condlog(1, "%s: Entering recovery mode: max_retries=%d",
545                         mpp->alias, mpp->no_path_retry);
546         }
547         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
548 }
549
550 void update_queue_mode_add_path(struct multipath *mpp)
551 {
552         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
553                 /* come back to normal mode from retry mode */
554                 mpp->retry_tick = 0;
555                 dm_queue_if_no_path(mpp->alias, 1);
556                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
557                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
558         }
559         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
560 }
561