move waiter code from libmultipath to multipathd
[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 "devmapper.h"
14 #include "dmparser.h"
15 #include "propsel.h"
16 #include "discovery.h"
17 #include "prio.h"
18 #include "configure.h"
19 #include "libdevmapper.h"
20 #include "io_err_stat.h"
21
22 /*
23  * creates or updates mpp->paths reading mpp->pg
24  */
25 int update_mpp_paths(struct multipath *mpp, vector pathvec)
26 {
27         struct pathgroup * pgp;
28         struct path * pp;
29         int i,j;
30
31         if (!mpp || !mpp->pg)
32                 return 0;
33
34         if (!mpp->paths &&
35             !(mpp->paths = vector_alloc()))
36                 return 1;
37
38         vector_foreach_slot (mpp->pg, pgp, i) {
39                 vector_foreach_slot (pgp->paths, pp, j) {
40                         if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
41                             (find_path_by_devt(pathvec, pp->dev_t)) &&
42                             store_path(mpp->paths, pp))
43                                 return 1;
44                 }
45         }
46         return 0;
47 }
48
49 int 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 void orphan_path(struct path *pp, const char *reason)
85 {
86         condlog(3, "%s: orphan path, %s", pp->dev, reason);
87         pp->mpp = NULL;
88         pp->dmstate = PSTATE_UNDEF;
89         pp->uid_attribute = NULL;
90         pp->getuid = NULL;
91         prio_put(&pp->prio);
92         checker_put(&pp->checker);
93         if (pp->fd >= 0)
94                 close(pp->fd);
95         pp->fd = -1;
96 }
97
98 void orphan_paths(vector pathvec, struct multipath *mpp)
99 {
100         int i;
101         struct path * pp;
102
103         vector_foreach_slot (pathvec, pp, i) {
104                 if (pp->mpp == mpp) {
105                         orphan_path(pp, "map flushed");
106                 }
107         }
108 }
109
110 void
111 remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec)
112 {
113         int i;
114
115         condlog(4, "%s: remove multipath map", mpp->alias);
116
117         /*
118          * clear references to this map
119          */
120         orphan_paths(vecs->pathvec, mpp);
121
122         if (purge_vec &&
123             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
124                 vector_del_slot(vecs->mpvec, i);
125
126         /*
127          * final free
128          */
129         free_multipath(mpp, KEEP_PATHS);
130 }
131
132 void
133 remove_maps(struct vectors * vecs)
134 {
135         int i;
136         struct multipath * mpp;
137
138         if (!vecs)
139                 return;
140
141         vector_foreach_slot (vecs->mpvec, mpp, i) {
142                 remove_map(mpp, vecs, 1);
143                 i--;
144         }
145
146         vector_free(vecs->mpvec);
147         vecs->mpvec = NULL;
148 }
149
150 void
151 extract_hwe_from_path(struct multipath * mpp)
152 {
153         struct path * pp = NULL;
154         int i;
155
156         if (mpp->hwe || !mpp->paths)
157                 return;
158
159         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
160         /* doing this in two passes seems like paranoia to me */
161         vector_foreach_slot(mpp->paths, pp, i) {
162                 if (pp->state != PATH_UP)
163                         continue;
164                 if (pp->hwe) {
165                         mpp->hwe = pp->hwe;
166                         return;
167                 }
168         }
169         vector_foreach_slot(mpp->paths, pp, i) {
170                 if (pp->state == PATH_UP)
171                         continue;
172                 if (pp->hwe) {
173                         mpp->hwe = pp->hwe;
174                         return;
175                 }
176         }
177 }
178
179 int
180 update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
181 {
182         char params[PARAMS_SIZE] = {0};
183
184         if (!mpp)
185                 return 1;
186
187         if (dm_get_map(mpp->alias, &mpp->size, params)) {
188                 condlog(3, "%s: cannot get map", mpp->alias);
189                 return 1;
190         }
191
192         if (disassemble_map(pathvec, params, mpp, is_daemon)) {
193                 condlog(3, "%s: cannot disassemble map", mpp->alias);
194                 return 1;
195         }
196
197         return 0;
198 }
199
200 int
201 update_multipath_status (struct multipath *mpp)
202 {
203         char status[PARAMS_SIZE] = {0};
204
205         if (!mpp)
206                 return 1;
207
208         if (dm_get_status(mpp->alias, status)) {
209                 condlog(3, "%s: cannot get status", mpp->alias);
210                 return 1;
211         }
212
213         if (disassemble_status(status, mpp)) {
214                 condlog(3, "%s: cannot disassemble status", mpp->alias);
215                 return 1;
216         }
217
218         return 0;
219 }
220
221 void sync_paths(struct multipath *mpp, vector pathvec)
222 {
223         struct path *pp;
224         struct pathgroup  *pgp;
225         int found, i, j;
226
227         vector_foreach_slot (mpp->paths, pp, i) {
228                 found = 0;
229                 vector_foreach_slot(mpp->pg, pgp, j) {
230                         if (find_slot(pgp->paths, (void *)pp) != -1) {
231                                 found = 1;
232                                 break;
233                         }
234                 }
235                 if (!found) {
236                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
237                         vector_del_slot(mpp->paths, i--);
238                         orphan_path(pp, "path removed externally");
239                 }
240         }
241         update_mpp_paths(mpp, pathvec);
242         vector_foreach_slot (mpp->paths, pp, i)
243                 pp->mpp = mpp;
244 }
245
246 int
247 update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
248 {
249         if (!mpp)
250                 return 1;
251
252         update_mpp_paths(mpp, pathvec);
253         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
254
255         free_multipath_attributes(mpp);
256         free_pgvec(mpp->pg, KEEP_PATHS);
257         mpp->pg = NULL;
258
259         if (update_multipath_table(mpp, pathvec, is_daemon))
260                 return 1;
261         sync_paths(mpp, pathvec);
262
263         if (update_multipath_status(mpp))
264                 return 1;
265
266         return 0;
267 }
268
269 void enter_recovery_mode(struct multipath *mpp)
270 {
271         struct config *conf = get_multipath_config();
272
273         /*
274          * Enter retry mode.
275          * meaning of +1: retry_tick may be decremented in checkerloop before
276          * starting retry.
277          */
278         mpp->stat_queueing_timeouts++;
279         mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
280         condlog(1, "%s: Entering recovery mode: max_retries=%d",
281                 mpp->alias, mpp->no_path_retry);
282         put_multipath_config(conf);
283 }
284
285 static void set_no_path_retry(struct multipath *mpp)
286 {
287         char is_queueing = 0;
288
289         mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
290         if (mpp->features && strstr(mpp->features, "queue_if_no_path"))
291                 is_queueing = 1;
292
293         switch (mpp->no_path_retry) {
294         case NO_PATH_RETRY_UNDEF:
295                 break;
296         case NO_PATH_RETRY_FAIL:
297                 if (is_queueing)
298                         dm_queue_if_no_path(mpp->alias, 0);
299                 break;
300         case NO_PATH_RETRY_QUEUE:
301                 if (!is_queueing)
302                         dm_queue_if_no_path(mpp->alias, 1);
303                 break;
304         default:
305                 if (mpp->nr_active > 0) {
306                         mpp->retry_tick = 0;
307                         dm_queue_if_no_path(mpp->alias, 1);
308                 } else if (is_queueing && mpp->retry_tick == 0)
309                         enter_recovery_mode(mpp);
310                 break;
311         }
312 }
313
314 int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
315                       int reset)
316 {
317         if (dm_get_info(mpp->alias, &mpp->dmi)) {
318                 /* Error accessing table */
319                 condlog(3, "%s: cannot access table", mpp->alias);
320                 goto out;
321         }
322
323         if (update_multipath_strings(mpp, vecs->pathvec, 1)) {
324                 condlog(0, "%s: failed to setup multipath", mpp->alias);
325                 goto out;
326         }
327
328         if (reset) {
329                 set_no_path_retry(mpp);
330                 if (VECTOR_SIZE(mpp->paths) != 0)
331                         dm_cancel_deferred_remove(mpp);
332         }
333
334         return 0;
335 out:
336         remove_map(mpp, vecs, PURGE_VEC);
337         return 1;
338 }
339
340 void
341 sync_map_state(struct multipath *mpp)
342 {
343         struct pathgroup *pgp;
344         struct path *pp;
345         unsigned int i, j;
346
347         if (!mpp->pg)
348                 return;
349
350         vector_foreach_slot (mpp->pg, pgp, i){
351                 vector_foreach_slot (pgp->paths, pp, j){
352                         if (pp->state == PATH_UNCHECKED ||
353                             pp->state == PATH_WILD ||
354                             pp->state == PATH_DELAYED)
355                                 continue;
356                         if (mpp->ghost_delay_tick > 0)
357                                 continue;
358                         if ((pp->dmstate == PSTATE_FAILED ||
359                              pp->dmstate == PSTATE_UNDEF) &&
360                             (pp->state == PATH_UP || pp->state == PATH_GHOST))
361                                 dm_reinstate_path(mpp->alias, pp->dev_t);
362                         else if ((pp->dmstate == PSTATE_ACTIVE ||
363                                   pp->dmstate == PSTATE_UNDEF) &&
364                                  (pp->state == PATH_DOWN ||
365                                   pp->state == PATH_SHAKY))
366                                 dm_fail_path(mpp->alias, pp->dev_t);
367                 }
368         }
369 }
370
371 static void
372 find_existing_alias (struct multipath * mpp,
373                      struct vectors *vecs)
374 {
375         struct multipath * mp;
376         int i;
377
378         vector_foreach_slot (vecs->mpvec, mp, i)
379                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
380                         strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
381                         return;
382                 }
383 }
384
385 struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
386                                     int add_vec)
387 {
388         struct multipath * mpp;
389         struct config *conf = NULL;
390
391         if (!strlen(pp->wwid))
392                 return NULL;
393
394         if (!(mpp = alloc_multipath()))
395                 return NULL;
396
397         conf = get_multipath_config();
398         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
399         mpp->hwe = pp->hwe;
400         put_multipath_config(conf);
401
402         strcpy(mpp->wwid, pp->wwid);
403         find_existing_alias(mpp, vecs);
404         if (select_alias(conf, mpp))
405                 goto out;
406         mpp->size = pp->size;
407
408         if (adopt_paths(vecs->pathvec, mpp))
409                 goto out;
410
411         if (add_vec) {
412                 if (!vector_alloc_slot(vecs->mpvec))
413                         goto out;
414
415                 vector_set_slot(vecs->mpvec, mpp);
416         }
417
418         return mpp;
419
420 out:
421         remove_map(mpp, vecs, PURGE_VEC);
422         return NULL;
423 }
424
425 int verify_paths(struct multipath *mpp, struct vectors *vecs)
426 {
427         struct path * pp;
428         int count = 0;
429         int i, j;
430
431         if (!mpp)
432                 return 0;
433
434         vector_foreach_slot (mpp->paths, pp, i) {
435                 /*
436                  * see if path is in sysfs
437                  */
438                 if (sysfs_attr_get_value(pp->udev, "dev",
439                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
440                         if (pp->state != PATH_DOWN) {
441                                 condlog(1, "%s: removing valid path %s in state %d",
442                                         mpp->alias, pp->dev, pp->state);
443                         } else {
444                                 condlog(3, "%s: failed to access path %s",
445                                         mpp->alias, pp->dev);
446                         }
447                         count++;
448                         vector_del_slot(mpp->paths, i);
449                         i--;
450
451                         if ((j = find_slot(vecs->pathvec,
452                                            (void *)pp)) != -1)
453                                 vector_del_slot(vecs->pathvec, j);
454                         free_path(pp);
455                 } else {
456                         condlog(4, "%s: verified path %s dev_t %s",
457                                 mpp->alias, pp->dev, pp->dev_t);
458                 }
459         }
460         return count;
461 }
462
463 int update_multipath (struct vectors *vecs, char *mapname, int reset)
464 {
465         struct multipath *mpp;
466         struct pathgroup  *pgp;
467         struct path *pp;
468         int i, j;
469
470         mpp = find_mp_by_alias(vecs->mpvec, mapname);
471
472         if (!mpp) {
473                 condlog(3, "%s: multipath map not found", mapname);
474                 return 2;
475         }
476
477         if (__setup_multipath(vecs, mpp, reset))
478                 return 1; /* mpp freed in setup_multipath */
479
480         /*
481          * compare checkers states with DM states
482          */
483         vector_foreach_slot (mpp->pg, pgp, i) {
484                 vector_foreach_slot (pgp->paths, pp, j) {
485                         if (pp->dmstate != PSTATE_FAILED)
486                                 continue;
487
488                         if (pp->state != PATH_DOWN) {
489                                 struct config *conf = get_multipath_config();
490                                 int oldstate = pp->state;
491                                 condlog(2, "%s: mark as failed", pp->dev);
492                                 mpp->stat_path_failures++;
493                                 pp->state = PATH_DOWN;
494                                 if (oldstate == PATH_UP ||
495                                     oldstate == PATH_GHOST)
496                                         update_queue_mode_del_path(mpp);
497
498                                 /*
499                                  * if opportune,
500                                  * schedule the next check earlier
501                                  */
502                                 if (pp->tick > conf->checkint)
503                                         pp->tick = conf->checkint;
504                                 put_multipath_config(conf);
505                         }
506                 }
507         }
508         return 0;
509 }
510
511 /*
512  * mpp->no_path_retry:
513  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
514  *   -1 (FAIL)  : fail_if_no_path
515  *    0 (UNDEF) : nothing
516  *   >0         : queue_if_no_path enabled, turned off after polling n times
517  */
518 void update_queue_mode_del_path(struct multipath *mpp)
519 {
520         if (--mpp->nr_active == 0) {
521                 if (mpp->no_path_retry > 0)
522                         enter_recovery_mode(mpp);
523                 else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
524                         mpp->stat_map_failures++;
525         }
526         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
527 }
528
529 void update_queue_mode_add_path(struct multipath *mpp)
530 {
531         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
532                 /* come back to normal mode from retry mode */
533                 mpp->retry_tick = 0;
534                 dm_queue_if_no_path(mpp->alias, 1);
535                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
536                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
537         }
538         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
539 }