📄 dm-mpath.c
字号:
list_for_each_entry(pg, &m->priority_groups, list) { pg->bypassed = 0; if (--pgnum) continue; m->current_pgpath = NULL; m->current_pg = NULL; m->next_pg = pg; } spin_unlock_irqrestore(&m->lock, flags); queue_work(kmultipathd, &m->trigger_event); return 0;}/* * Set/clear bypassed status of a PG. * PGs are numbered upwards from 1 in the order they were declared. */static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed){ struct priority_group *pg; unsigned pgnum; if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum || (pgnum > m->nr_priority_groups)) { DMWARN("invalid PG number supplied to bypass_pg"); return -EINVAL; } list_for_each_entry(pg, &m->priority_groups, list) { if (!--pgnum) break; } bypass_pg(m, pg, bypassed); return 0;}/* * Should we retry pg_init immediately? */static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath){ unsigned long flags; int limit_reached = 0; spin_lock_irqsave(&m->lock, flags); if (m->pg_init_count <= m->pg_init_retries) m->pg_init_required = 1; else limit_reached = 1; spin_unlock_irqrestore(&m->lock, flags); return limit_reached;}/* * pg_init must call this when it has completed its initialisation */void dm_pg_init_complete(struct dm_path *path, unsigned err_flags){ struct pgpath *pgpath = path_to_pgpath(path); struct priority_group *pg = pgpath->pg; struct multipath *m = pg->m; unsigned long flags; /* * If requested, retry pg_init until maximum number of retries exceeded. * If retry not requested and PG already bypassed, always fail the path. */ if (err_flags & MP_RETRY) { if (pg_init_limit_reached(m, pgpath)) err_flags |= MP_FAIL_PATH; } else if (err_flags && pg->bypassed) err_flags |= MP_FAIL_PATH; if (err_flags & MP_FAIL_PATH) fail_path(pgpath); if (err_flags & MP_BYPASS_PG) bypass_pg(m, pg, 1); spin_lock_irqsave(&m->lock, flags); if (err_flags & ~MP_RETRY) { m->current_pgpath = NULL; m->current_pg = NULL; } else if (!m->pg_init_required) m->queue_io = 0; m->pg_init_in_progress = 0; queue_work(kmultipathd, &m->process_queued_ios); spin_unlock_irqrestore(&m->lock, flags);}/* * end_io handling */static int do_end_io(struct multipath *m, struct bio *bio, int error, struct dm_mpath_io *mpio){ struct hw_handler *hwh = &m->hw_handler; unsigned err_flags = MP_FAIL_PATH; /* Default behavior */ unsigned long flags; if (!error) return 0; /* I/O complete */ if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio)) return error; if (error == -EOPNOTSUPP) return error; spin_lock_irqsave(&m->lock, flags); if (!m->nr_valid_paths) { if (__must_push_back(m)) { spin_unlock_irqrestore(&m->lock, flags); return DM_ENDIO_REQUEUE; } else if (!m->queue_if_no_path) { spin_unlock_irqrestore(&m->lock, flags); return -EIO; } else { spin_unlock_irqrestore(&m->lock, flags); goto requeue; } } spin_unlock_irqrestore(&m->lock, flags); if (hwh->type && hwh->type->error) err_flags = hwh->type->error(hwh, bio); if (mpio->pgpath) { if (err_flags & MP_FAIL_PATH) fail_path(mpio->pgpath); if (err_flags & MP_BYPASS_PG) bypass_pg(m, mpio->pgpath->pg, 1); } if (err_flags & MP_ERROR_IO) return -EIO; requeue: dm_bio_restore(&mpio->details, bio); /* queue for the daemon to resubmit or fail */ spin_lock_irqsave(&m->lock, flags); bio_list_add(&m->queued_ios, bio); m->queue_size++; if (!m->queue_io) queue_work(kmultipathd, &m->process_queued_ios); spin_unlock_irqrestore(&m->lock, flags); return DM_ENDIO_INCOMPLETE; /* io not complete */}static int multipath_end_io(struct dm_target *ti, struct bio *bio, int error, union map_info *map_context){ struct multipath *m = ti->private; struct dm_mpath_io *mpio = map_context->ptr; struct pgpath *pgpath = mpio->pgpath; struct path_selector *ps; int r; r = do_end_io(m, bio, error, mpio); if (pgpath) { ps = &pgpath->pg->ps; if (ps->type->end_io) ps->type->end_io(ps, &pgpath->path); } if (r != DM_ENDIO_INCOMPLETE) mempool_free(mpio, m->mpio_pool); return r;}/* * Suspend can't complete until all the I/O is processed so if * the last path fails we must error any remaining I/O. * Note that if the freeze_bdev fails while suspending, the * queue_if_no_path state is lost - userspace should reset it. */static void multipath_presuspend(struct dm_target *ti){ struct multipath *m = (struct multipath *) ti->private; queue_if_no_path(m, 0, 1);}/* * Restore the queue_if_no_path setting. */static void multipath_resume(struct dm_target *ti){ struct multipath *m = (struct multipath *) ti->private; unsigned long flags; spin_lock_irqsave(&m->lock, flags); m->queue_if_no_path = m->saved_queue_if_no_path; spin_unlock_irqrestore(&m->lock, flags);}/* * Info output has the following format: * num_multipath_feature_args [multipath_feature_args]* * num_handler_status_args [handler_status_args]* * num_groups init_group_number * [A|D|E num_ps_status_args [ps_status_args]* * num_paths num_selector_args * [path_dev A|F fail_count [selector_args]* ]+ ]+ * * Table output has the following format (identical to the constructor string): * num_feature_args [features_args]* * num_handler_args hw_handler [hw_handler_args]* * num_groups init_group_number * [priority selector-name num_ps_args [ps_args]* * num_paths num_selector_args [path_dev [selector_args]* ]+ ]+ */static int multipath_status(struct dm_target *ti, status_type_t type, char *result, unsigned int maxlen){ int sz = 0; unsigned long flags; struct multipath *m = (struct multipath *) ti->private; struct hw_handler *hwh = &m->hw_handler; struct priority_group *pg; struct pgpath *p; unsigned pg_num; char state; spin_lock_irqsave(&m->lock, flags); /* Features */ if (type == STATUSTYPE_INFO) DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count); else { DMEMIT("%u ", m->queue_if_no_path + (m->pg_init_retries > 0) * 2); if (m->queue_if_no_path) DMEMIT("queue_if_no_path "); if (m->pg_init_retries) DMEMIT("pg_init_retries %u ", m->pg_init_retries); } if (hwh->type && hwh->type->status) sz += hwh->type->status(hwh, type, result + sz, maxlen - sz); else if (!hwh->type || type == STATUSTYPE_INFO) DMEMIT("0 "); else DMEMIT("1 %s ", hwh->type->name); DMEMIT("%u ", m->nr_priority_groups); if (m->next_pg) pg_num = m->next_pg->pg_num; else if (m->current_pg) pg_num = m->current_pg->pg_num; else pg_num = 1; DMEMIT("%u ", pg_num); switch (type) { case STATUSTYPE_INFO: list_for_each_entry(pg, &m->priority_groups, list) { if (pg->bypassed) state = 'D'; /* Disabled */ else if (pg == m->current_pg) state = 'A'; /* Currently Active */ else state = 'E'; /* Enabled */ DMEMIT("%c ", state); if (pg->ps.type->status) sz += pg->ps.type->status(&pg->ps, NULL, type, result + sz, maxlen - sz); else DMEMIT("0 "); DMEMIT("%u %u ", pg->nr_pgpaths, pg->ps.type->info_args); list_for_each_entry(p, &pg->pgpaths, list) { DMEMIT("%s %s %u ", p->path.dev->name, p->path.is_active ? "A" : "F", p->fail_count); if (pg->ps.type->status) sz += pg->ps.type->status(&pg->ps, &p->path, type, result + sz, maxlen - sz); } } break; case STATUSTYPE_TABLE: list_for_each_entry(pg, &m->priority_groups, list) { DMEMIT("%s ", pg->ps.type->name); if (pg->ps.type->status) sz += pg->ps.type->status(&pg->ps, NULL, type, result + sz, maxlen - sz); else DMEMIT("0 "); DMEMIT("%u %u ", pg->nr_pgpaths, pg->ps.type->table_args); list_for_each_entry(p, &pg->pgpaths, list) { DMEMIT("%s ", p->path.dev->name); if (pg->ps.type->status) sz += pg->ps.type->status(&pg->ps, &p->path, type, result + sz, maxlen - sz); } } break; } spin_unlock_irqrestore(&m->lock, flags); return 0;}static int multipath_message(struct dm_target *ti, unsigned argc, char **argv){ int r; struct dm_dev *dev; struct multipath *m = (struct multipath *) ti->private; action_fn action; if (argc == 1) { if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) return queue_if_no_path(m, 1, 0); else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) return queue_if_no_path(m, 0, 0); } if (argc != 2) goto error; if (!strnicmp(argv[0], MESG_STR("disable_group"))) return bypass_pg_num(m, argv[1], 1); else if (!strnicmp(argv[0], MESG_STR("enable_group"))) return bypass_pg_num(m, argv[1], 0); else if (!strnicmp(argv[0], MESG_STR("switch_group"))) return switch_pg_num(m, argv[1]); else if (!strnicmp(argv[0], MESG_STR("reinstate_path"))) action = reinstate_path; else if (!strnicmp(argv[0], MESG_STR("fail_path"))) action = fail_path; else goto error; r = dm_get_device(ti, argv[1], ti->begin, ti->len, dm_table_get_mode(ti->table), &dev); if (r) { DMWARN("message: error getting device %s", argv[1]); return -EINVAL; } r = action_dev(m, dev, action); dm_put_device(ti, dev); return r;error: DMWARN("Unrecognised multipath message received."); return -EINVAL;}static int multipath_ioctl(struct dm_target *ti, struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ struct multipath *m = (struct multipath *) ti->private; struct block_device *bdev = NULL; unsigned long flags; struct file fake_file = {}; struct dentry fake_dentry = {}; int r = 0; fake_file.f_path.dentry = &fake_dentry; spin_lock_irqsave(&m->lock, flags); if (!m->current_pgpath) __choose_pgpath(m); if (m->current_pgpath) { bdev = m->current_pgpath->path.dev->bdev; fake_dentry.d_inode = bdev->bd_inode; fake_file.f_mode = m->current_pgpath->path.dev->mode; } if (m->queue_io) r = -EAGAIN; else if (!bdev) r = -EIO; spin_unlock_irqrestore(&m->lock, flags); return r ? : blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg);}/*----------------------------------------------------------------- * Module setup *---------------------------------------------------------------*/static struct target_type multipath_target = { .name = "multipath", .version = {1, 0, 5}, .module = THIS_MODULE, .ctr = multipath_ctr, .dtr = multipath_dtr, .map = multipath_map, .end_io = multipath_end_io, .presuspend = multipath_presuspend, .resume = multipath_resume, .status = multipath_status, .message = multipath_message, .ioctl = multipath_ioctl,};static int __init dm_multipath_init(void){ int r; /* allocate a slab for the dm_ios */ _mpio_cache = KMEM_CACHE(dm_mpath_io, 0); if (!_mpio_cache) return -ENOMEM; r = dm_register_target(&multipath_target); if (r < 0) { DMERR("register failed %d", r); kmem_cache_destroy(_mpio_cache); return -EINVAL; } kmultipathd = create_workqueue("kmpathd"); if (!kmultipathd) { DMERR("failed to create workqueue kmpathd"); dm_unregister_target(&multipath_target); kmem_cache_destroy(_mpio_cache); return -ENOMEM; } DMINFO("version %u.%u.%u loaded", multipath_target.version[0], multipath_target.version[1], multipath_target.version[2]); return r;}static void __exit dm_multipath_exit(void){ int r; destroy_workqueue(kmultipathd); r = dm_unregister_target(&multipath_target); if (r < 0) DMERR("target unregister failed %d", r); kmem_cache_destroy(_mpio_cache);}EXPORT_SYMBOL_GPL(dm_pg_init_complete);module_init(dm_multipath_init);module_exit(dm_multipath_exit);MODULE_DESCRIPTION(DM_NAME " multipath target");MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -