📄 dm-mpath.c
字号:
/* * Copyright (C) 2003 Sistina Software Limited. * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */#include "dm.h"#include "dm-path-selector.h"#include "dm-hw-handler.h"#include "dm-bio-list.h"#include "dm-bio-record.h"#include "dm-uevent.h"#include <linux/ctype.h>#include <linux/init.h>#include <linux/mempool.h>#include <linux/module.h>#include <linux/pagemap.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/workqueue.h>#include <asm/atomic.h>#define DM_MSG_PREFIX "multipath"#define MESG_STR(x) x, sizeof(x)/* Path properties */struct pgpath { struct list_head list; struct priority_group *pg; /* Owning PG */ unsigned fail_count; /* Cumulative failure count */ struct dm_path path;};#define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)/* * Paths are grouped into Priority Groups and numbered from 1 upwards. * Each has a path selector which controls which path gets used. */struct priority_group { struct list_head list; struct multipath *m; /* Owning multipath instance */ struct path_selector ps; unsigned pg_num; /* Reference number */ unsigned bypassed; /* Temporarily bypass this PG? */ unsigned nr_pgpaths; /* Number of paths in PG */ struct list_head pgpaths;};/* Multipath context */struct multipath { struct list_head list; struct dm_target *ti; spinlock_t lock; struct hw_handler hw_handler; unsigned nr_priority_groups; struct list_head priority_groups; unsigned pg_init_required; /* pg_init needs calling? */ unsigned pg_init_in_progress; /* Only one pg_init allowed at once */ unsigned nr_valid_paths; /* Total number of usable paths */ struct pgpath *current_pgpath; struct priority_group *current_pg; struct priority_group *next_pg; /* Switch to this PG if set */ unsigned repeat_count; /* I/Os left before calling PS again */ unsigned queue_io; /* Must we queue all I/O? */ unsigned queue_if_no_path; /* Queue I/O if last path fails? */ unsigned saved_queue_if_no_path;/* Saved state during suspension */ unsigned pg_init_retries; /* Number of times to retry pg_init */ unsigned pg_init_count; /* Number of times pg_init called */ struct work_struct process_queued_ios; struct bio_list queued_ios; unsigned queue_size; struct work_struct trigger_event; /* * We must use a mempool of dm_mpath_io structs so that we * can resubmit bios on error. */ mempool_t *mpio_pool;};/* * Context information attached to each bio we process. */struct dm_mpath_io { struct pgpath *pgpath; struct dm_bio_details details;};typedef int (*action_fn) (struct pgpath *pgpath);#define MIN_IOS 256 /* Mempool size */static struct kmem_cache *_mpio_cache;struct workqueue_struct *kmultipathd;static void process_queued_ios(struct work_struct *work);static void trigger_event(struct work_struct *work);/*----------------------------------------------- * Allocation routines *-----------------------------------------------*/static struct pgpath *alloc_pgpath(void){ struct pgpath *pgpath = kzalloc(sizeof(*pgpath), GFP_KERNEL); if (pgpath) pgpath->path.is_active = 1; return pgpath;}static void free_pgpath(struct pgpath *pgpath){ kfree(pgpath);}static struct priority_group *alloc_priority_group(void){ struct priority_group *pg; pg = kzalloc(sizeof(*pg), GFP_KERNEL); if (pg) INIT_LIST_HEAD(&pg->pgpaths); return pg;}static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti){ struct pgpath *pgpath, *tmp; list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { list_del(&pgpath->list); dm_put_device(ti, pgpath->path.dev); free_pgpath(pgpath); }}static void free_priority_group(struct priority_group *pg, struct dm_target *ti){ struct path_selector *ps = &pg->ps; if (ps->type) { ps->type->destroy(ps); dm_put_path_selector(ps->type); } free_pgpaths(&pg->pgpaths, ti); kfree(pg);}static struct multipath *alloc_multipath(struct dm_target *ti){ struct multipath *m; m = kzalloc(sizeof(*m), GFP_KERNEL); if (m) { INIT_LIST_HEAD(&m->priority_groups); spin_lock_init(&m->lock); m->queue_io = 1; INIT_WORK(&m->process_queued_ios, process_queued_ios); INIT_WORK(&m->trigger_event, trigger_event); m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache); if (!m->mpio_pool) { kfree(m); return NULL; } m->ti = ti; ti->private = m; } return m;}static void free_multipath(struct multipath *m){ struct priority_group *pg, *tmp; struct hw_handler *hwh = &m->hw_handler; list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) { list_del(&pg->list); free_priority_group(pg, m->ti); } if (hwh->type) { hwh->type->destroy(hwh); dm_put_hw_handler(hwh->type); } mempool_destroy(m->mpio_pool); kfree(m);}/*----------------------------------------------- * Path selection *-----------------------------------------------*/static void __switch_pg(struct multipath *m, struct pgpath *pgpath){ struct hw_handler *hwh = &m->hw_handler; m->current_pg = pgpath->pg; /* Must we initialise the PG first, and queue I/O till it's ready? */ if (hwh->type && hwh->type->pg_init) { m->pg_init_required = 1; m->queue_io = 1; } else { m->pg_init_required = 0; m->queue_io = 0; } m->pg_init_count = 0;}static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg){ struct dm_path *path; path = pg->ps.type->select_path(&pg->ps, &m->repeat_count); if (!path) return -ENXIO; m->current_pgpath = path_to_pgpath(path); if (m->current_pg != pg) __switch_pg(m, m->current_pgpath); return 0;}static void __choose_pgpath(struct multipath *m){ struct priority_group *pg; unsigned bypassed = 1; if (!m->nr_valid_paths) goto failed; /* Were we instructed to switch PG? */ if (m->next_pg) { pg = m->next_pg; m->next_pg = NULL; if (!__choose_path_in_pg(m, pg)) return; } /* Don't change PG until it has no remaining paths */ if (m->current_pg && !__choose_path_in_pg(m, m->current_pg)) return; /* * Loop through priority groups until we find a valid path. * First time we skip PGs marked 'bypassed'. * Second time we only try the ones we skipped. */ do { list_for_each_entry(pg, &m->priority_groups, list) { if (pg->bypassed == bypassed) continue; if (!__choose_path_in_pg(m, pg)) return; } } while (bypassed--);failed: m->current_pgpath = NULL; m->current_pg = NULL;}/* * Check whether bios must be queued in the device-mapper core rather * than here in the target. * * m->lock must be held on entry. * * If m->queue_if_no_path and m->saved_queue_if_no_path hold the * same value then we are not between multipath_presuspend() * and multipath_resume() calls and we have no need to check * for the DMF_NOFLUSH_SUSPENDING flag. */static int __must_push_back(struct multipath *m){ return (m->queue_if_no_path != m->saved_queue_if_no_path && dm_noflush_suspending(m->ti));}static int map_io(struct multipath *m, struct bio *bio, struct dm_mpath_io *mpio, unsigned was_queued){ int r = DM_MAPIO_REMAPPED; unsigned long flags; struct pgpath *pgpath; spin_lock_irqsave(&m->lock, flags); /* Do we need to select a new pgpath? */ if (!m->current_pgpath || (!m->queue_io && (m->repeat_count && --m->repeat_count == 0))) __choose_pgpath(m); pgpath = m->current_pgpath; if (was_queued) m->queue_size--; if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path)) { /* Queue for the daemon to resubmit */ bio_list_add(&m->queued_ios, bio); m->queue_size++; if ((m->pg_init_required && !m->pg_init_in_progress) || !m->queue_io) queue_work(kmultipathd, &m->process_queued_ios); pgpath = NULL; r = DM_MAPIO_SUBMITTED; } else if (pgpath) bio->bi_bdev = pgpath->path.dev->bdev; else if (__must_push_back(m)) r = DM_MAPIO_REQUEUE; else r = -EIO; /* Failed */ mpio->pgpath = pgpath; spin_unlock_irqrestore(&m->lock, flags); return r;}/* * If we run out of usable paths, should we queue I/O or error it? */static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path, unsigned save_old_value){ unsigned long flags; spin_lock_irqsave(&m->lock, flags); if (save_old_value) m->saved_queue_if_no_path = m->queue_if_no_path; else m->saved_queue_if_no_path = queue_if_no_path; m->queue_if_no_path = queue_if_no_path; if (!m->queue_if_no_path && m->queue_size) queue_work(kmultipathd, &m->process_queued_ios); spin_unlock_irqrestore(&m->lock, flags); return 0;}/*----------------------------------------------------------------- * The multipath daemon is responsible for resubmitting queued ios. *---------------------------------------------------------------*/static void dispatch_queued_ios(struct multipath *m){ int r; unsigned long flags; struct bio *bio = NULL, *next; struct dm_mpath_io *mpio; union map_info *info; spin_lock_irqsave(&m->lock, flags); bio = bio_list_get(&m->queued_ios); spin_unlock_irqrestore(&m->lock, flags); while (bio) { next = bio->bi_next; bio->bi_next = NULL; info = dm_get_mapinfo(bio); mpio = info->ptr; r = map_io(m, bio, mpio, 1); if (r < 0) bio_endio(bio, r); else if (r == DM_MAPIO_REMAPPED) generic_make_request(bio); else if (r == DM_MAPIO_REQUEUE) bio_endio(bio, -EIO); bio = next; }}static void process_queued_ios(struct work_struct *work){ struct multipath *m = container_of(work, struct multipath, process_queued_ios); struct hw_handler *hwh = &m->hw_handler; struct pgpath *pgpath = NULL; unsigned init_required = 0, must_queue = 1; unsigned long flags; spin_lock_irqsave(&m->lock, flags); if (!m->queue_size) goto out; if (!m->current_pgpath) __choose_pgpath(m); pgpath = m->current_pgpath; if ((pgpath && !m->queue_io) || (!pgpath && !m->queue_if_no_path)) must_queue = 0; if (m->pg_init_required && !m->pg_init_in_progress) { m->pg_init_count++; m->pg_init_required = 0; m->pg_init_in_progress = 1; init_required = 1; }out: spin_unlock_irqrestore(&m->lock, flags); if (init_required) hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path); if (!must_queue) dispatch_queued_ios(m);}/* * An event is triggered whenever a path is taken out of use. * Includes path failure and PG bypass. */static void trigger_event(struct work_struct *work){ struct multipath *m = container_of(work, struct multipath, trigger_event); dm_table_event(m->ti->table);}/*----------------------------------------------------------------- * Constructor/argument parsing: * <#multipath feature args> [<arg>]* * <#hw_handler args> [hw_handler [<arg>]*] * <#priority groups> * <initial priority group> * [<selector> <#selector args> [<arg>]* * <#paths> <#per-path selector args> * [<path> [<arg>]* ]+ ]+ *---------------------------------------------------------------*/struct param { unsigned min; unsigned max; char *error;};static int read_param(struct param *param, char *str, unsigned *v, char **error){ if (!str || (sscanf(str, "%u", v) != 1) || (*v < param->min) || (*v > param->max)) { *error = param->error; return -EINVAL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -