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