⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dm-raid1.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (C) 2003 Sistina Software Limited. * * This file is released under the GPL. */#include "dm.h"#include "dm-bio-list.h"#include "dm-io.h"#include "dm-log.h"#include "kcopyd.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/vmalloc.h>#include <linux/workqueue.h>#include <linux/log2.h>#define DM_MSG_PREFIX "raid1"#define DM_IO_PAGES 64#define DM_RAID1_HANDLE_ERRORS 0x01#define errors_handled(p)	((p)->features & DM_RAID1_HANDLE_ERRORS)static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);/*----------------------------------------------------------------- * Region hash * * The mirror splits itself up into discrete regions.  Each * region can be in one of three states: clean, dirty, * nosync.  There is no need to put clean regions in the hash. * * In addition to being present in the hash table a region _may_ * be present on one of three lists. * *   clean_regions: Regions on this list have no io pending to *   them, they are in sync, we are no longer interested in them, *   they are dull.  rh_update_states() will remove them from the *   hash table. * *   quiesced_regions: These regions have been spun down, ready *   for recovery.  rh_recovery_start() will remove regions from *   this list and hand them to kmirrord, which will schedule the *   recovery io with kcopyd. * *   recovered_regions: Regions that kcopyd has successfully *   recovered.  rh_update_states() will now schedule any delayed *   io, up the recovery_count, and remove the region from the *   hash. * * There are 2 locks: *   A rw spin lock 'hash_lock' protects just the hash table, *   this is never held in write mode from interrupt context, *   which I believe means that we only have to disable irqs when *   doing a write lock. * *   An ordinary spin lock 'region_lock' that protects the three *   lists in the region_hash, with the 'state', 'list' and *   'bhs_delayed' fields of the regions.  This is used from irq *   context, so all other uses will have to suspend local irqs. *---------------------------------------------------------------*/struct mirror_set;struct region_hash {	struct mirror_set *ms;	uint32_t region_size;	unsigned region_shift;	/* holds persistent region state */	struct dirty_log *log;	/* hash table */	rwlock_t hash_lock;	mempool_t *region_pool;	unsigned int mask;	unsigned int nr_buckets;	struct list_head *buckets;	spinlock_t region_lock;	atomic_t recovery_in_flight;	struct semaphore recovery_count;	struct list_head clean_regions;	struct list_head quiesced_regions;	struct list_head recovered_regions;	struct list_head failed_recovered_regions;};enum {	RH_CLEAN,	RH_DIRTY,	RH_NOSYNC,	RH_RECOVERING};struct region {	struct region_hash *rh;	/* FIXME: can we get rid of this ? */	region_t key;	int state;	struct list_head hash_list;	struct list_head list;	atomic_t pending;	struct bio_list delayed_bios;};/*----------------------------------------------------------------- * Mirror set structures. *---------------------------------------------------------------*/struct mirror {	struct mirror_set *ms;	atomic_t error_count;	struct dm_dev *dev;	sector_t offset;};struct mirror_set {	struct dm_target *ti;	struct list_head list;	struct region_hash rh;	struct kcopyd_client *kcopyd_client;	uint64_t features;	spinlock_t lock;	/* protects the next two lists */	struct bio_list reads;	struct bio_list writes;	struct dm_io_client *io_client;	/* recovery */	region_t nr_regions;	int in_sync;	int log_failure;	struct mirror *default_mirror;	/* Default mirror */	struct workqueue_struct *kmirrord_wq;	struct work_struct kmirrord_work;	unsigned int nr_mirrors;	struct mirror mirror[0];};/* * Conversion fns */static inline region_t bio_to_region(struct region_hash *rh, struct bio *bio){	return (bio->bi_sector - rh->ms->ti->begin) >> rh->region_shift;}static inline sector_t region_to_sector(struct region_hash *rh, region_t region){	return region << rh->region_shift;}static void wake(struct mirror_set *ms){	queue_work(ms->kmirrord_wq, &ms->kmirrord_work);}/* FIXME move this */static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw);#define MIN_REGIONS 64#define MAX_RECOVERY 1static int rh_init(struct region_hash *rh, struct mirror_set *ms,		   struct dirty_log *log, uint32_t region_size,		   region_t nr_regions){	unsigned int nr_buckets, max_buckets;	size_t i;	/*	 * Calculate a suitable number of buckets for our hash	 * table.	 */	max_buckets = nr_regions >> 6;	for (nr_buckets = 128u; nr_buckets < max_buckets; nr_buckets <<= 1)		;	nr_buckets >>= 1;	rh->ms = ms;	rh->log = log;	rh->region_size = region_size;	rh->region_shift = ffs(region_size) - 1;	rwlock_init(&rh->hash_lock);	rh->mask = nr_buckets - 1;	rh->nr_buckets = nr_buckets;	rh->buckets = vmalloc(nr_buckets * sizeof(*rh->buckets));	if (!rh->buckets) {		DMERR("unable to allocate region hash memory");		return -ENOMEM;	}	for (i = 0; i < nr_buckets; i++)		INIT_LIST_HEAD(rh->buckets + i);	spin_lock_init(&rh->region_lock);	sema_init(&rh->recovery_count, 0);	atomic_set(&rh->recovery_in_flight, 0);	INIT_LIST_HEAD(&rh->clean_regions);	INIT_LIST_HEAD(&rh->quiesced_regions);	INIT_LIST_HEAD(&rh->recovered_regions);	INIT_LIST_HEAD(&rh->failed_recovered_regions);	rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,						      sizeof(struct region));	if (!rh->region_pool) {		vfree(rh->buckets);		rh->buckets = NULL;		return -ENOMEM;	}	return 0;}static void rh_exit(struct region_hash *rh){	unsigned int h;	struct region *reg, *nreg;	BUG_ON(!list_empty(&rh->quiesced_regions));	for (h = 0; h < rh->nr_buckets; h++) {		list_for_each_entry_safe(reg, nreg, rh->buckets + h, hash_list) {			BUG_ON(atomic_read(&reg->pending));			mempool_free(reg, rh->region_pool);		}	}	if (rh->log)		dm_destroy_dirty_log(rh->log);	if (rh->region_pool)		mempool_destroy(rh->region_pool);	vfree(rh->buckets);}#define RH_HASH_MULT 2654435387Ustatic inline unsigned int rh_hash(struct region_hash *rh, region_t region){	return (unsigned int) ((region * RH_HASH_MULT) >> 12) & rh->mask;}static struct region *__rh_lookup(struct region_hash *rh, region_t region){	struct region *reg;	list_for_each_entry (reg, rh->buckets + rh_hash(rh, region), hash_list)		if (reg->key == region)			return reg;	return NULL;}static void __rh_insert(struct region_hash *rh, struct region *reg){	unsigned int h = rh_hash(rh, reg->key);	list_add(&reg->hash_list, rh->buckets + h);}static struct region *__rh_alloc(struct region_hash *rh, region_t region){	struct region *reg, *nreg;	read_unlock(&rh->hash_lock);	nreg = mempool_alloc(rh->region_pool, GFP_ATOMIC);	if (unlikely(!nreg))		nreg = kmalloc(sizeof(struct region), GFP_NOIO);	nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?		RH_CLEAN : RH_NOSYNC;	nreg->rh = rh;	nreg->key = region;	INIT_LIST_HEAD(&nreg->list);	atomic_set(&nreg->pending, 0);	bio_list_init(&nreg->delayed_bios);	write_lock_irq(&rh->hash_lock);	reg = __rh_lookup(rh, region);	if (reg)		/* we lost the race */		mempool_free(nreg, rh->region_pool);	else {		__rh_insert(rh, nreg);		if (nreg->state == RH_CLEAN) {			spin_lock(&rh->region_lock);			list_add(&nreg->list, &rh->clean_regions);			spin_unlock(&rh->region_lock);		}		reg = nreg;	}	write_unlock_irq(&rh->hash_lock);	read_lock(&rh->hash_lock);	return reg;}static inline struct region *__rh_find(struct region_hash *rh, region_t region){	struct region *reg;	reg = __rh_lookup(rh, region);	if (!reg)		reg = __rh_alloc(rh, region);	return reg;}static int rh_state(struct region_hash *rh, region_t region, int may_block){	int r;	struct region *reg;	read_lock(&rh->hash_lock);	reg = __rh_lookup(rh, region);	read_unlock(&rh->hash_lock);	if (reg)		return reg->state;	/*	 * The region wasn't in the hash, so we fall back to the	 * dirty log.	 */	r = rh->log->type->in_sync(rh->log, region, may_block);	/*	 * Any error from the dirty log (eg. -EWOULDBLOCK) gets	 * taken as a RH_NOSYNC	 */	return r == 1 ? RH_CLEAN : RH_NOSYNC;}static inline int rh_in_sync(struct region_hash *rh,			     region_t region, int may_block){	int state = rh_state(rh, region, may_block);	return state == RH_CLEAN || state == RH_DIRTY;}static void dispatch_bios(struct mirror_set *ms, struct bio_list *bio_list){	struct bio *bio;	while ((bio = bio_list_pop(bio_list))) {		queue_bio(ms, bio, WRITE);	}}static void complete_resync_work(struct region *reg, int success){	struct region_hash *rh = reg->rh;	rh->log->type->set_region_sync(rh->log, reg->key, success);	dispatch_bios(rh->ms, &reg->delayed_bios);	if (atomic_dec_and_test(&rh->recovery_in_flight))		wake_up_all(&_kmirrord_recovery_stopped);	up(&rh->recovery_count);}static void rh_update_states(struct region_hash *rh){	struct region *reg, *next;	LIST_HEAD(clean);	LIST_HEAD(recovered);	LIST_HEAD(failed_recovered);	/*	 * Quickly grab the lists.	 */	write_lock_irq(&rh->hash_lock);	spin_lock(&rh->region_lock);	if (!list_empty(&rh->clean_regions)) {		list_splice(&rh->clean_regions, &clean);		INIT_LIST_HEAD(&rh->clean_regions);		list_for_each_entry(reg, &clean, list)			list_del(&reg->hash_list);	}	if (!list_empty(&rh->recovered_regions)) {		list_splice(&rh->recovered_regions, &recovered);		INIT_LIST_HEAD(&rh->recovered_regions);		list_for_each_entry (reg, &recovered, list)			list_del(&reg->hash_list);	}	if (!list_empty(&rh->failed_recovered_regions)) {		list_splice(&rh->failed_recovered_regions, &failed_recovered);		INIT_LIST_HEAD(&rh->failed_recovered_regions);		list_for_each_entry(reg, &failed_recovered, list)			list_del(&reg->hash_list);	}	spin_unlock(&rh->region_lock);	write_unlock_irq(&rh->hash_lock);	/*	 * All the regions on the recovered and clean lists have	 * now been pulled out of the system, so no need to do	 * any more locking.	 */	list_for_each_entry_safe (reg, next, &recovered, list) {		rh->log->type->clear_region(rh->log, reg->key);		complete_resync_work(reg, 1);		mempool_free(reg, rh->region_pool);	}	list_for_each_entry_safe(reg, next, &failed_recovered, list) {		complete_resync_work(reg, errors_handled(rh->ms) ? 0 : 1);		mempool_free(reg, rh->region_pool);	}	list_for_each_entry_safe(reg, next, &clean, list) {		rh->log->type->clear_region(rh->log, reg->key);		mempool_free(reg, rh->region_pool);	}	rh->log->type->flush(rh->log);}static void rh_inc(struct region_hash *rh, region_t region){	struct region *reg;	read_lock(&rh->hash_lock);	reg = __rh_find(rh, region);	spin_lock_irq(&rh->region_lock);	atomic_inc(&reg->pending);	if (reg->state == RH_CLEAN) {		reg->state = RH_DIRTY;		list_del_init(&reg->list);	/* take off the clean list */		spin_unlock_irq(&rh->region_lock);		rh->log->type->mark_region(rh->log, reg->key);	} else		spin_unlock_irq(&rh->region_lock);	read_unlock(&rh->hash_lock);}static void rh_inc_pending(struct region_hash *rh, struct bio_list *bios){	struct bio *bio;	for (bio = bios->head; bio; bio = bio->bi_next)		rh_inc(rh, bio_to_region(rh, bio));}static void rh_dec(struct region_hash *rh, region_t region){	unsigned long flags;	struct region *reg;	int should_wake = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -