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

📄 dm-emc.c

📁 linux 内核源代码
💻 C
字号:
/* * Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved. * Copyright (C) 2004 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. * * Multipath support for EMC CLARiiON AX/CX-series hardware. */#include "dm.h"#include "dm-hw-handler.h"#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#define DM_MSG_PREFIX "multipath emc"struct emc_handler {	spinlock_t lock;	/* Whether we should send the short trespass command (FC-series)	 * or the long version (default for AX/CX CLARiiON arrays). */	unsigned short_trespass;	/* Whether or not to honor SCSI reservations when initiating a	 * switch-over. Default: Don't. */	unsigned hr;	unsigned char sense[SCSI_SENSE_BUFFERSIZE];};#define TRESPASS_PAGE 0x22#define EMC_FAILOVER_TIMEOUT (60 * HZ)/* Code borrowed from dm-lsi-rdac by Mike Christie */static inline void free_bio(struct bio *bio){	__free_page(bio->bi_io_vec[0].bv_page);	bio_put(bio);}static void emc_endio(struct bio *bio, int error){	struct dm_path *path = bio->bi_private;	/* We also need to look at the sense keys here whether or not to	 * switch to the next PG etc.	 *	 * For now simple logic: either it works or it doesn't.	 */	if (error)		dm_pg_init_complete(path, MP_FAIL_PATH);	else		dm_pg_init_complete(path, 0);	/* request is freed in block layer */	free_bio(bio);}static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size){	struct bio *bio;	struct page *page;	bio = bio_alloc(GFP_ATOMIC, 1);	if (!bio) {		DMERR("get_failover_bio: bio_alloc() failed.");		return NULL;	}	bio->bi_rw |= (1 << BIO_RW);	bio->bi_bdev = path->dev->bdev;	bio->bi_sector = 0;	bio->bi_private = path;	bio->bi_end_io = emc_endio;	page = alloc_page(GFP_ATOMIC);	if (!page) {		DMERR("get_failover_bio: alloc_page() failed.");		bio_put(bio);		return NULL;	}	if (bio_add_page(bio, page, data_size, 0) != data_size) {		DMERR("get_failover_bio: bio_add_page() failed.");		__free_page(page);		bio_put(bio);		return NULL;	}	return bio;}static struct request *get_failover_req(struct emc_handler *h,					struct bio *bio, struct dm_path *path){	struct request *rq;	struct block_device *bdev = bio->bi_bdev;	struct request_queue *q = bdev_get_queue(bdev);	/* FIXME: Figure out why it fails with GFP_ATOMIC. */	rq = blk_get_request(q, WRITE, __GFP_WAIT);	if (!rq) {		DMERR("get_failover_req: blk_get_request failed");		return NULL;	}	blk_rq_append_bio(q, rq, bio);	rq->sense = h->sense;	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);	rq->sense_len = 0;	memset(&rq->cmd, 0, BLK_MAX_CDB);	rq->timeout = EMC_FAILOVER_TIMEOUT;	rq->cmd_type = REQ_TYPE_BLOCK_PC;	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;	return rq;}static struct request *emc_trespass_get(struct emc_handler *h,					struct dm_path *path){	struct bio *bio;	struct request *rq;	unsigned char *page22;	unsigned char long_trespass_pg[] = {		0, 0, 0, 0,		TRESPASS_PAGE,        /* Page code */		0x09,                 /* Page length - 2 */		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */		0xff, 0xff,           /* Trespass target */		0, 0, 0, 0, 0, 0      /* Reserved bytes / unknown */		};	unsigned char short_trespass_pg[] = {		0, 0, 0, 0,		TRESPASS_PAGE,        /* Page code */		0x02,                 /* Page length - 2 */		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */		0xff,                 /* Trespass target */		};	unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :				sizeof(long_trespass_pg);	/* get bio backing */	if (data_size > PAGE_SIZE)		/* this should never happen */		return NULL;	bio = get_failover_bio(path, data_size);	if (!bio) {		DMERR("emc_trespass_get: no bio");		return NULL;	}	page22 = (unsigned char *)bio_data(bio);	memset(page22, 0, data_size);	memcpy(page22, h->short_trespass ?		short_trespass_pg : long_trespass_pg, data_size);	/* get request for block layer packet command */	rq = get_failover_req(h, bio, path);	if (!rq) {		DMERR("emc_trespass_get: no rq");		free_bio(bio);		return NULL;	}	/* Prepare the command. */	rq->cmd[0] = MODE_SELECT;	rq->cmd[1] = 0x10;	rq->cmd[4] = data_size;	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);	return rq;}static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,			struct dm_path *path){	struct request *rq;	struct request_queue *q = bdev_get_queue(path->dev->bdev);	/*	 * We can either blindly init the pg (then look at the sense),	 * or we can send some commands to get the state here (then	 * possibly send the fo cmnd), or we can also have the	 * initial state passed into us and then get an update here.	 */	if (!q) {		DMINFO("emc_pg_init: no queue");		goto fail_path;	}	/* FIXME: The request should be pre-allocated. */	rq = emc_trespass_get(hwh->context, path);	if (!rq) {		DMERR("emc_pg_init: no rq");		goto fail_path;	}	DMINFO("emc_pg_init: sending switch-over command");	elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);	return;fail_path:	dm_pg_init_complete(path, MP_FAIL_PATH);}static struct emc_handler *alloc_emc_handler(void){	struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);	if (h)		spin_lock_init(&h->lock);	return h;}static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv){	struct emc_handler *h;	unsigned hr, short_trespass;	if (argc == 0) {		/* No arguments: use defaults */		hr = 0;		short_trespass = 0;	} else if (argc != 2) {		DMWARN("incorrect number of arguments");		return -EINVAL;	} else {		if ((sscanf(argv[0], "%u", &short_trespass) != 1)			|| (short_trespass > 1)) {			DMWARN("invalid trespass mode selected");			return -EINVAL;		}		if ((sscanf(argv[1], "%u", &hr) != 1)			|| (hr > 1)) {			DMWARN("invalid honor reservation flag selected");			return -EINVAL;		}	}	h = alloc_emc_handler();	if (!h)		return -ENOMEM;	hwh->context = h;	if ((h->short_trespass = short_trespass))		DMWARN("short trespass command will be send");	else		DMWARN("long trespass command will be send");	if ((h->hr = hr))		DMWARN("honor reservation bit will be set");	else		DMWARN("honor reservation bit will not be set (default)");	return 0;}static void emc_destroy(struct hw_handler *hwh){	struct emc_handler *h = (struct emc_handler *) hwh->context;	kfree(h);	hwh->context = NULL;}static unsigned emc_error(struct hw_handler *hwh, struct bio *bio){	/* FIXME: Patch from axboe still missing */#if 0	int sense;	if (bio->bi_error & BIO_SENSE) {		sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */		if (sense == 0x020403) {			/* LUN Not Ready - Manual Intervention Required			 * indicates this is a passive path.			 *			 * FIXME: However, if this is seen and EVPD C0			 * indicates that this is due to a NDU in			 * progress, we should set FAIL_PATH too.			 * This indicates we might have to do a SCSI			 * inquiry in the end_io path. Ugh. */			return MP_BYPASS_PG | MP_RETRY_IO;		} else if (sense == 0x052501) {			/* An array based copy is in progress. Do not			 * fail the path, do not bypass to another PG,			 * do not retry. Fail the IO immediately.			 * (Actually this is the same conclusion as in			 * the default handler, but lets make sure.) */			return 0;		} else if (sense == 0x062900) {			/* Unit Attention Code. This is the first IO			 * to the new path, so just retry. */			return MP_RETRY_IO;		}	}#endif	/* Try default handler */	return dm_scsi_err_handler(hwh, bio);}static struct hw_handler_type emc_hwh = {	.name = "emc",	.module = THIS_MODULE,	.create = emc_create,	.destroy = emc_destroy,	.pg_init = emc_pg_init,	.error = emc_error,};static int __init dm_emc_init(void){	int r = dm_register_hw_handler(&emc_hwh);	if (r < 0)		DMERR("register failed %d", r);	DMINFO("version 0.0.3 loaded");	return r;}static void __exit dm_emc_exit(void){	int r = dm_unregister_hw_handler(&emc_hwh);	if (r < 0)		DMERR("unregister failed %d", r);}module_init(dm_emc_init);module_exit(dm_emc_exit);MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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