libmultipath: adopt_paths: check for size match
[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                         if (pp->size != 0 && mpp->size != 0 &&
64                             pp->size != mpp->size) {
65                                 condlog(3, "%s: size mismatch for %s, not adding path",
66                                         pp->dev, mpp->alias);
67                                 continue;
68                         }
69                         condlog(3, "%s: ownership set to %s",
70                                 pp->dev, mpp->alias);
71                         pp->mpp = mpp;
72
73                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
74                                 return 1;
75
76                         if (!find_path_by_dev(mpp->paths, pp->dev) &&
77                             store_path(mpp->paths, pp))
78                                         return 1;
79                         conf = get_multipath_config();
80                         pthread_cleanup_push(put_multipath_config, conf);
81                         ret = pathinfo(pp, conf,
82                                        DI_PRIO | DI_CHECKER);
83                         pthread_cleanup_pop(1);
84                         if (ret)
85                                 return 1;
86                 }
87         }
88         return 0;
89 }
90
91 void orphan_path(struct path *pp, const char *reason)
92 {
93         condlog(3, "%s: orphan path, %s", pp->dev, reason);
94         pp->mpp = NULL;
95         pp->dmstate = PSTATE_UNDEF;
96         pp->uid_attribute = NULL;
97         pp->getuid = NULL;
98         prio_put(&pp->prio);
99         checker_put(&pp->checker);
100         if (pp->fd >= 0)
101                 close(pp->fd);
102         pp->fd = -1;
103 }
104
105 void orphan_paths(vector pathvec, struct multipath *mpp)
106 {
107         int i;
108         struct path * pp;
109
110         vector_foreach_slot (pathvec, pp, i) {
111                 if (pp->mpp == mpp) {
112                         orphan_path(pp, "map flushed");
113                 }
114         }
115 }
116
117 void
118 remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec)
119 {
120         int i;
121
122         /*
123          * clear references to this map
124          */
125         orphan_paths(vecs->pathvec, mpp);
126
127         if (purge_vec &&
128             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
129                 vector_del_slot(vecs->mpvec, i);
130
131         /*
132          * final free
133          */
134         free_multipath(mpp, KEEP_PATHS);
135 }
136
137 void
138 remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec)
139 {
140         struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
141         if (mpp) {
142                 condlog(2, "%s: removing map by alias", alias);
143                 remove_map(mpp, vecs, purge_vec);
144         }
145 }
146
147 void
148 remove_maps(struct vectors * vecs)
149 {
150         int i;
151         struct multipath * mpp;
152
153         if (!vecs)
154                 return;
155
156         vector_foreach_slot (vecs->mpvec, mpp, i) {
157                 remove_map(mpp, vecs, 1);
158                 i--;
159         }
160
161         vector_free(vecs->mpvec);
162         vecs->mpvec = NULL;
163 }
164
165 void
166 extract_hwe_from_path(struct multipath * mpp)
167 {
168         struct path * pp = NULL;
169         int i;
170
171         if (mpp->hwe || !mpp->paths)
172                 return;
173
174         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
175         /* doing this in two passes seems like paranoia to me */
176         vector_foreach_slot(mpp->paths, pp, i) {
177                 if (pp->state != PATH_UP)
178                         continue;
179                 if (pp->hwe) {
180                         mpp->hwe = pp->hwe;
181                         return;
182                 }
183         }
184         vector_foreach_slot(mpp->paths, pp, i) {
185                 if (pp->state == PATH_UP)
186                         continue;
187                 if (pp->hwe) {
188                         mpp->hwe = pp->hwe;
189                         return;
190                 }
191         }
192 }
193
194 int
195 update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
196 {
197         char params[PARAMS_SIZE] = {0};
198
199         if (!mpp)
200                 return 1;
201
202         if (dm_get_map(mpp->alias, &mpp->size, params)) {
203                 condlog(3, "%s: cannot get map", mpp->alias);
204                 return 1;
205         }
206
207         if (disassemble_map(pathvec, params, mpp, is_daemon)) {
208                 condlog(3, "%s: cannot disassemble map", mpp->alias);
209                 return 1;
210         }
211
212         return 0;
213 }
214
215 int
216 update_multipath_status (struct multipath *mpp)
217 {
218         char status[PARAMS_SIZE] = {0};
219
220         if (!mpp)
221                 return 1;
222
223         if (dm_get_status(mpp->alias, status)) {
224                 condlog(3, "%s: cannot get status", mpp->alias);
225                 return 1;
226         }
227
228         if (disassemble_status(status, mpp)) {
229                 condlog(3, "%s: cannot disassemble status", mpp->alias);
230                 return 1;
231         }
232
233         return 0;
234 }
235
236 void sync_paths(struct multipath *mpp, vector pathvec)
237 {
238         struct path *pp;
239         struct pathgroup  *pgp;
240         int found, i, j;
241
242         vector_foreach_slot (mpp->paths, pp, i) {
243                 found = 0;
244                 vector_foreach_slot(mpp->pg, pgp, j) {
245                         if (find_slot(pgp->paths, (void *)pp) != -1) {
246                                 found = 1;
247                                 break;
248                         }
249                 }
250                 if (!found) {
251                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
252                         vector_del_slot(mpp->paths, i--);
253                         orphan_path(pp, "path removed externally");
254                 }
255         }
256         update_mpp_paths(mpp, pathvec);
257         vector_foreach_slot (mpp->paths, pp, i)
258                 pp->mpp = mpp;
259 }
260
261 int
262 update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
263 {
264         if (!mpp)
265                 return 1;
266
267         update_mpp_paths(mpp, pathvec);
268         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
269
270         free_multipath_attributes(mpp);
271         free_pgvec(mpp->pg, KEEP_PATHS);
272         mpp->pg = NULL;
273
274         if (update_multipath_table(mpp, pathvec, is_daemon))
275                 return 1;
276         sync_paths(mpp, pathvec);
277
278         if (update_multipath_status(mpp))
279                 return 1;
280
281         return 0;
282 }
283
284 void enter_recovery_mode(struct multipath *mpp)
285 {
286         int checkint;
287         struct config *conf = get_multipath_config();
288         checkint = conf->checkint;
289         put_multipath_config(conf);
290
291         /*
292          * Enter retry mode.
293          * meaning of +1: retry_tick may be decremented in checkerloop before
294          * starting retry.
295          */
296         mpp->stat_queueing_timeouts++;
297         mpp->retry_tick = mpp->no_path_retry * checkint + 1;
298         condlog(1, "%s: Entering recovery mode: max_retries=%d",
299                 mpp->alias, mpp->no_path_retry);
300 }
301
302 void
303 sync_map_state(struct multipath *mpp)
304 {
305         struct pathgroup *pgp;
306         struct path *pp;
307         unsigned int i, j;
308
309         if (!mpp->pg)
310                 return;
311
312         vector_foreach_slot (mpp->pg, pgp, i){
313                 vector_foreach_slot (pgp->paths, pp, j){
314                         if (pp->state == PATH_UNCHECKED ||
315                             pp->state == PATH_WILD ||
316                             pp->state == PATH_DELAYED)
317                                 continue;
318                         if (mpp->ghost_delay_tick > 0)
319                                 continue;
320                         if ((pp->dmstate == PSTATE_FAILED ||
321                              pp->dmstate == PSTATE_UNDEF) &&
322                             (pp->state == PATH_UP || pp->state == PATH_GHOST))
323                                 dm_reinstate_path(mpp->alias, pp->dev_t);
324                         else if ((pp->dmstate == PSTATE_ACTIVE ||
325                                   pp->dmstate == PSTATE_UNDEF) &&
326                                  (pp->state == PATH_DOWN ||
327                                   pp->state == PATH_SHAKY)) {
328                                 condlog(2, "sync_map_state: failing %s state %d dmstate %d",
329                                         pp->dev, pp->state, pp->dmstate);
330                                 dm_fail_path(mpp->alias, pp->dev_t);
331                         }
332                 }
333         }
334 }
335
336 static void
337 find_existing_alias (struct multipath * mpp,
338                      struct vectors *vecs)
339 {
340         struct multipath * mp;
341         int i;
342
343         vector_foreach_slot (vecs->mpvec, mp, i)
344                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
345                         strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
346                         return;
347                 }
348 }
349
350 struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
351                                     int add_vec)
352 {
353         struct multipath * mpp;
354         struct config *conf = NULL;
355
356         if (!strlen(pp->wwid))
357                 return NULL;
358
359         if (!(mpp = alloc_multipath()))
360                 return NULL;
361
362         conf = get_multipath_config();
363         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
364         mpp->hwe = pp->hwe;
365         put_multipath_config(conf);
366
367         strcpy(mpp->wwid, pp->wwid);
368         find_existing_alias(mpp, vecs);
369         if (select_alias(conf, mpp))
370                 goto out;
371         mpp->size = pp->size;
372
373         if (adopt_paths(vecs->pathvec, mpp))
374                 goto out;
375
376         if (add_vec) {
377                 if (!vector_alloc_slot(vecs->mpvec))
378                         goto out;
379
380                 vector_set_slot(vecs->mpvec, mpp);
381         }
382
383         return mpp;
384
385 out:
386         remove_map(mpp, vecs, PURGE_VEC);
387         return NULL;
388 }
389
390 int verify_paths(struct multipath *mpp, struct vectors *vecs)
391 {
392         struct path * pp;
393         int count = 0;
394         int i, j;
395
396         if (!mpp)
397                 return 0;
398
399         vector_foreach_slot (mpp->paths, pp, i) {
400                 /*
401                  * see if path is in sysfs
402                  */
403                 if (sysfs_attr_get_value(pp->udev, "dev",
404                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
405                         if (pp->state != PATH_DOWN) {
406                                 condlog(1, "%s: removing valid path %s in state %d",
407                                         mpp->alias, pp->dev, pp->state);
408                         } else {
409                                 condlog(3, "%s: failed to access path %s",
410                                         mpp->alias, pp->dev);
411                         }
412                         count++;
413                         vector_del_slot(mpp->paths, i);
414                         i--;
415
416                         /* Make sure mpp->hwe doesn't point to freed memory.
417                          * We call extract_hwe_from_path() below to restore
418                          * mpp->hwe
419                          */
420                         if (mpp->hwe == pp->hwe)
421                                 mpp->hwe = NULL;
422                         if ((j = find_slot(vecs->pathvec,
423                                            (void *)pp)) != -1)
424                                 vector_del_slot(vecs->pathvec, j);
425                         free_path(pp);
426                 } else {
427                         condlog(4, "%s: verified path %s dev_t %s",
428                                 mpp->alias, pp->dev, pp->dev_t);
429                 }
430         }
431         extract_hwe_from_path(mpp);
432         return count;
433 }
434
435 /*
436  * mpp->no_path_retry:
437  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
438  *   -1 (FAIL)  : fail_if_no_path
439  *    0 (UNDEF) : nothing
440  *   >0         : queue_if_no_path enabled, turned off after polling n times
441  */
442 void update_queue_mode_del_path(struct multipath *mpp)
443 {
444         if (--mpp->nr_active == 0) {
445                 if (mpp->no_path_retry > 0)
446                         enter_recovery_mode(mpp);
447                 else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
448                         mpp->stat_map_failures++;
449         }
450         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
451 }
452
453 void update_queue_mode_add_path(struct multipath *mpp)
454 {
455         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
456                 /* come back to normal mode from retry mode */
457                 mpp->retry_tick = 0;
458                 dm_queue_if_no_path(mpp->alias, 1);
459                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
460                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
461         }
462         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
463 }
464
465 vector get_used_hwes(const struct _vector *pathvec)
466 {
467         int i, j;
468         struct path *pp;
469         struct hwentry *hwe;
470         vector v = vector_alloc();
471
472         if (v == NULL)
473                 return NULL;
474
475         vector_foreach_slot(pathvec, pp, i) {
476                 vector_foreach_slot_backwards(pp->hwe, hwe, j) {
477                         vector_find_or_add_slot(v, hwe);
478                 }
479         }
480
481         return v;
482 }