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