hehe numbering on the subject was messed up on previous mails. sorry. 02-async-ps-init-RFC.patch - Basically, for incoming IO the map function will simply sleep until the ps has been activated. For failed IO that can not be mapped becuase the ps is initializing the map or end_io function will just place the bio on the on the dispatched_failed list and set things up so that work is not dequeued until the ps has been initialized. dm-mpath.c | 173 ++++++++++++++++++++++++++++++++--------------------- dm-path-selector.h | 17 ++++- 2 files changed, 119 insertions(+), 71 deletions(-)
diff -aurp linux-2.6.5-orig/drivers/md/dm-mpath.c linux-2.6.5-rdac/drivers/md/dm-mpath.c --- linux-2.6.5-orig/drivers/md/dm-mpath.c 2004-04-20 23:20:29.254721055 -0700 +++ linux-2.6.5-rdac/drivers/md/dm-mpath.c 2004-04-20 23:42:27.492963026 -0700 @@ -53,12 +53,14 @@ struct priority_group { struct list_head list; struct multipath *m; - struct path_selector *ps; + struct path_selector ps; unsigned nr_paths; struct list_head paths; }; +#define ps_to_pg(__ps) container_of((__ps), struct priority_group, ps) + /* Multipath context */ struct multipath { struct list_head list; @@ -96,6 +98,7 @@ struct mpath_io { #define MIN_IOS 256 static kmem_cache_t *_details_cache; +struct workqueue_struct *kmultipathd; static void dispatch_failed_ios(void *data); static void trigger_event(void *data); @@ -121,13 +124,7 @@ static struct priority_group *alloc_prio pg = kmalloc(sizeof(*pg), GFP_KERNEL); if (!pg) return NULL; - - pg->ps = kmalloc(sizeof(*pg->ps), GFP_KERNEL); - if (!pg->ps) { - kfree(pg); - return NULL; - } - memset(pg->ps, 0, sizeof(*pg->ps)); + memset(pg, 0, sizeof(*pg)); INIT_LIST_HEAD(&pg->paths); @@ -148,14 +145,13 @@ static void free_paths(struct list_head static void free_priority_group(struct priority_group *pg, struct dm_target *ti) { - struct path_selector *ps = pg->ps; + struct path_selector *ps = &pg->ps; if (ps) { if (ps->type) { ps->type->dtr(ps); dm_put_path_selector(ps->type); } - kfree(ps); } free_paths(&pg->paths, ti); @@ -199,93 +195,117 @@ static void free_multipath(struct multip kfree(m); } -static struct path *__choose_path(struct multipath *m) +void dm_ps_init_done(struct path_selector *ps) { - struct priority_group *pg, *orig_pg = m->current_pg; - struct path *path = NULL; + struct multipath *m = ps_to_pg(ps)->m; - m->current_pg = NULL; + spin_lock_irq(&m->lock); + m->initializing_pg = 0; + complete_all(&m->init_pg_wait); + queue_work(kmultipathd, &m->dispatch_failed); + spin_unlock_irq(&m->lock); +} - init_completion(&m->init_pg_wait); +static int __choose_path(struct multipath *m, struct path **path) +{ + struct priority_group *pg, *orig_pg; + + orig_pg = m->current_pg; + m->current_pg = NULL; m->initializing_pg = 1; + init_completion(&m->init_pg_wait); list_for_each_entry (pg, &m->priority_groups, list) { if (orig_pg == pg) continue; - if (pg->ps->type->init) { - int err; + if (pg->ps.type->init) { + switch(pg->ps.type->init(&pg->ps, dm_ps_init_done)) { - spin_unlock_irq(&m->lock); - err = pg->ps->type->init(pg->ps); - spin_lock_irq(&m->lock); - if (err) + case DM_PS_INITIALIZING: + return DM_PS_INITIALIZING; + case DM_PS_FAILED: continue; + } } - path = pg->ps->type->select_path(pg->ps); - if (path) { + *path = pg->ps.type->select_path(&pg->ps); + if (*path) { m->current_pg = pg; break; } } m->initializing_pg = 0; - complete_all(&m->init_pg_wait); - - return path; + return (*path ? DM_PS_SUCCESS : DM_PS_FAILED); } -static struct path *get_current_path(struct multipath *m) +static int get_current_path(struct multipath *m, struct path **path, int wait) { - struct path *path = NULL; + *path = NULL; retry: - spin_lock_irq(&m->lock); - - if (m->current_path && --m->current_count > 0) - goto done; + if (m->current_path && --m->current_count > 0) { + *path = m->current_path; + return 0; + } /* - * completion event, current_pg, and initializing_pg - * are protected under the m->lock to avoid races. + * completion event, current_pg, initializing_pg and + * in the case of wait=0 adding to the failed_ios list for + * resubmission are protected under the m->lock to avoid races. */ if (unlikely(m->initializing_pg)) { - spin_unlock_irq(&m->lock); - wait_for_completion(&m->init_pg_wait); - goto retry; + + ps_init: + if (wait) { + spin_unlock_irq(&m->lock); + wait_for_completion(&m->init_pg_wait); + spin_lock_irq(&m->lock); + goto retry; + } + return -EWOULDBLOCK; } if (m->current_pg) { - path = m->current_pg->ps->type->select_path(m->current_pg->ps); - if (!path) - path = __choose_path(m); + *path = m->current_pg->ps.type->select_path(&m->current_pg->ps); + if (!*path && + (__choose_path(m, path) == DM_PS_INITIALIZING)) + goto ps_init; } m->current_count = MPATH_MIN_IO; - m->current_path = path; - done: - path = m->current_path; + m->current_path = *path; - spin_unlock_irq(&m->lock); - - return path; + return *path ? 0 : -EIO; } -static int map_io(struct multipath *m, struct bio *bio, struct path **chosen) +static int map_io(struct multipath *m, struct bio *bio, int wait) { - *chosen = get_current_path(m); - if (!*chosen) - return -EIO; + int err; + struct mpath_io *io; + union map_info *info; + + info = dm_get_mapinfo(bio); + io = info->ptr; + + spin_lock_irq(&m->lock); + err = get_current_path(m, &io->path, wait); + if (err == -EWOULDBLOCK) + /* we will submit the bio later */ + bio_list_add(&m->failed_ios, bio); + spin_unlock_irq(&m->lock); - bio->bi_bdev = (*chosen)->dev->bdev; + if (err) + return err; + + bio->bi_bdev = io->path->dev->bdev; return 0; } /*----------------------------------------------------------------- * The multipath daemon is responsible for resubmitting failed ios. *---------------------------------------------------------------*/ -struct workqueue_struct *kmultipathd; static void dispatch_failed_ios(void *data) { @@ -307,7 +327,13 @@ static void dispatch_failed_ios(void *da info = dm_get_mapinfo(bio); io = info->ptr; - if (map_io(m, bio, &io->path)) + /* + * For -EWOULDBLOCK the bio could not be mapped + * due to a ps initialization. That work has been + * requeued and will be processed when the + * initialization is completed. + */ + if (map_io(m, bio, 0) == -EIO) /* no paths left */ bio_endio(bio, bio->bi_size, -EIO); else @@ -452,13 +478,13 @@ static struct priority_group *parse_prio if (as->argc < nr_args) goto bad; - r = pst->ctr(pg->ps, nr_args, as->argv, &ti->error); + r = pst->ctr(&pg->ps, nr_args, as->argv, &ti->error); if (r) { /* FIXME: need to put the pst ? fix after * factoring out the register */ goto bad; } - pg->ps->type = pst; + pg->ps.type = pst; consume(as, nr_args); /* @@ -486,7 +512,7 @@ static struct priority_group *parse_prio path_args.argc = nr_args; path_args.argv = as->argv; - path = parse_path(&path_args, pg->ps, ti); + path = parse_path(&path_args, &pg->ps, ti); if (!path) goto bad; @@ -563,17 +589,22 @@ static int multipath_map(struct dm_targe struct multipath *m = (struct multipath *) ti->private; io = mempool_alloc(m->details_pool, GFP_NOIO); + map_context->ptr = io; dm_bio_record(&io->details, bio); bio->bi_rw |= (1 << BIO_RW_FAILFAST); - r = map_io(m, bio, &io->path); - if (r) { + /* + * For bios that could not be mapped because the ps was + * initializing we pass wait=1 so map_io just blocks untill + * the initialization is completed. + */ + r = map_io(m, bio, 1); + if (r < 0) { mempool_free(io, m->details_pool); return r; } - map_context->ptr = io; - return 1; + return r; } static void update_path(struct path *path, int error) @@ -581,7 +612,7 @@ static void update_path(struct path *pat unsigned long flags; struct multipath *m = path->pg->m; - path->pg->ps->type->update_path(path->pg->ps, path, error); + path->pg->ps.type->update_path(&path->pg->ps, path, error); spin_lock_irqsave(&m->lock, flags); if (path == m->current_path) @@ -605,9 +636,15 @@ static int do_end_io(struct multipath *m /* queue for the daemon to resubmit */ spin_lock(&m->lock); bio_list_add(&m->failed_ios, bio); + /* + * If a ps is initializing we do not queue the work + * becuase when the ps initialization has completed + * it will queue the dispatch function to be run. + */ + if (!m->initializing_pg) + queue_work(kmultipathd, &m->dispatch_failed); spin_unlock(&m->lock); - queue_work(kmultipathd, &m->dispatch_failed); return 1; /* io not complete */ } @@ -654,16 +691,16 @@ static int multipath_status(struct dm_ta EMIT("%u ", m->nr_priority_groups); list_for_each_entry(pg, &m->priority_groups, list) { - pst = pg->ps->type; + pst = pg->ps.type; - sz += pst->ps_status(pg->ps, type, result + sz, + sz += pst->ps_status(&pg->ps, type, result + sz, maxlen - sz); EMIT("%u ", pg->nr_paths); list_for_each_entry(p, &pg->paths, list) { format_dev_t(buffer, p->dev->bdev->bd_dev); EMIT("%s ", buffer); - sz += pst->path_status(pg->ps, p, type, + sz += pst->path_status(&pg->ps, p, type, result + sz, maxlen - sz); } @@ -674,17 +711,17 @@ static int multipath_status(struct dm_ta EMIT("%u ", m->nr_priority_groups); list_for_each_entry(pg, &m->priority_groups, list) { - pst = pg->ps->type; + pst = pg->ps.type; EMIT("%s ", pst->name); - sz += pst->ps_status(pg->ps, type, result + sz, + sz += pst->ps_status(&pg->ps, type, result + sz, maxlen - sz); EMIT("%u ", pg->nr_paths); list_for_each_entry(p, &pg->paths, list) { format_dev_t(buffer, p->dev->bdev->bd_dev); EMIT("%s ", buffer); - sz += pst->path_status(pg->ps, p, type, + sz += pst->path_status(&pg->ps, p, type, result + sz, maxlen - sz); diff -aurp linux-2.6.5-orig/drivers/md/dm-path-selector.h linux-2.6.5-rdac/drivers/md/dm-path-selector.h --- linux-2.6.5-orig/drivers/md/dm-path-selector.h 2004-04-20 23:20:29.287716764 -0700 +++ linux-2.6.5-rdac/drivers/md/dm-path-selector.h 2004-04-20 23:40:46.054062030 -0700 @@ -36,10 +36,21 @@ typedef int (*ps_ctr_fn) (struct path_se typedef void (*ps_dtr_fn) (struct path_selector *ps); /* - * Allows the ps to initialize itself. This fn may sleep. - * The multipath context lock is not held. + * Allows the ps to initialize itself. It should return one + * of the following return values. iif DM_PS_INITIALIZING is + * returned the path-selector must call ps_init_completion_fn. + * + * The Mulipath lock is held, so the init funtion should not sleep. */ -typedef int (*ps_init_fn) (struct path_selector *ps); +enum { + DM_PS_SUCCESS, + DM_PS_FAILED, + DM_PS_INITIALIZING, +}; + +typedef void (*ps_init_completion_fn)(struct path_selector *ps); +typedef int (*ps_init_fn) (struct path_selector *ps, + ps_init_completion_fn done); /* * Add an opaque path object, along with some selector specific