diff options
| author | Hannes Reinecke <hare@suse.de> | 2008-07-17 19:53:21 -0400 |
|---|---|---|
| committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 15:14:52 -0400 |
| commit | 057ea7c9683c3d684128cced796f03c179ecf1c2 (patch) | |
| tree | ff9494b4da41258c06ef44ab5f7a6ed04daf3d98 | |
| parent | ca9f0089867c9e476cf2e6d4615d2aae887171b2 (diff) | |
[SCSI] scsi_dh: add generic SPC-3 alua handler
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
| -rw-r--r-- | drivers/scsi/device_handler/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/Makefile | 1 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 802 | ||||
| -rw-r--r-- | include/scsi/scsi.h | 3 |
4 files changed, 814 insertions, 0 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 2adc0f666b68..67070257919f 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig | |||
| @@ -30,3 +30,11 @@ config SCSI_DH_EMC | |||
| 30 | depends on SCSI_DH | 30 | depends on SCSI_DH |
| 31 | help | 31 | help |
| 32 | If you have a EMC CLARiiON select y. Otherwise, say N. | 32 | If you have a EMC CLARiiON select y. Otherwise, say N. |
| 33 | |||
| 34 | config SCSI_DH_ALUA | ||
| 35 | tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" | ||
| 36 | depends on SCSI_DH && EXPERIMENTAL | ||
| 37 | help | ||
| 38 | SCSI Device handler for generic SPC-3 Asymmetric Logical Unit | ||
| 39 | Access (ALUA). | ||
| 40 | |||
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 35272e93b1c8..e1d2ea083e15 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile | |||
| @@ -5,3 +5,4 @@ obj-$(CONFIG_SCSI_DH) += scsi_dh.o | |||
| 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o | 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o |
| 6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o | 6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o |
| 7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o | 7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o |
| 8 | obj-$(CONFIG_SCSI_DH_ALUA) += scsi_dh_alua.o | ||
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c new file mode 100644 index 000000000000..6e464488bcab --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh_alua.c | |||
| @@ -0,0 +1,802 @@ | |||
| 1 | /* | ||
| 2 | * Generic SCSI-3 ALUA SCSI Device Handler | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH. | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | #include <scsi/scsi.h> | ||
| 23 | #include <scsi/scsi_eh.h> | ||
| 24 | #include <scsi/scsi_dh.h> | ||
| 25 | |||
| 26 | #define ALUA_DH_NAME "alua" | ||
| 27 | #define ALUA_DH_VER "1.2" | ||
| 28 | |||
| 29 | #define TPGS_STATE_OPTIMIZED 0x0 | ||
| 30 | #define TPGS_STATE_NONOPTIMIZED 0x1 | ||
| 31 | #define TPGS_STATE_STANDBY 0x2 | ||
| 32 | #define TPGS_STATE_UNAVAILABLE 0x3 | ||
| 33 | #define TPGS_STATE_OFFLINE 0xe | ||
| 34 | #define TPGS_STATE_TRANSITIONING 0xf | ||
| 35 | |||
| 36 | #define TPGS_SUPPORT_NONE 0x00 | ||
| 37 | #define TPGS_SUPPORT_OPTIMIZED 0x01 | ||
| 38 | #define TPGS_SUPPORT_NONOPTIMIZED 0x02 | ||
| 39 | #define TPGS_SUPPORT_STANDBY 0x04 | ||
| 40 | #define TPGS_SUPPORT_UNAVAILABLE 0x08 | ||
| 41 | #define TPGS_SUPPORT_OFFLINE 0x40 | ||
| 42 | #define TPGS_SUPPORT_TRANSITION 0x80 | ||
| 43 | |||
| 44 | #define TPGS_MODE_UNINITIALIZED -1 | ||
| 45 | #define TPGS_MODE_NONE 0x0 | ||
| 46 | #define TPGS_MODE_IMPLICIT 0x1 | ||
| 47 | #define TPGS_MODE_EXPLICIT 0x2 | ||
| 48 | |||
| 49 | #define ALUA_INQUIRY_SIZE 36 | ||
| 50 | #define ALUA_FAILOVER_TIMEOUT (60 * HZ) | ||
| 51 | #define ALUA_FAILOVER_RETRIES 5 | ||
| 52 | |||
| 53 | struct alua_dh_data { | ||
| 54 | int group_id; | ||
| 55 | int rel_port; | ||
| 56 | int tpgs; | ||
| 57 | int state; | ||
| 58 | unsigned char inq[ALUA_INQUIRY_SIZE]; | ||
| 59 | unsigned char *buff; | ||
| 60 | int bufflen; | ||
| 61 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | ||
| 62 | int senselen; | ||
| 63 | }; | ||
| 64 | |||
| 65 | #define ALUA_POLICY_SWITCH_CURRENT 0 | ||
| 66 | #define ALUA_POLICY_SWITCH_ALL 1 | ||
| 67 | |||
| 68 | static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) | ||
| 69 | { | ||
| 70 | struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; | ||
| 71 | BUG_ON(scsi_dh_data == NULL); | ||
| 72 | return ((struct alua_dh_data *) scsi_dh_data->buf); | ||
| 73 | } | ||
| 74 | |||
| 75 | static int realloc_buffer(struct alua_dh_data *h, unsigned len) | ||
| 76 | { | ||
| 77 | if (h->buff && h->buff != h->inq) | ||
| 78 | kfree(h->buff); | ||
| 79 | |||
| 80 | h->buff = kmalloc(len, GFP_NOIO); | ||
| 81 | if (!h->buff) { | ||
| 82 | h->buff = h->inq; | ||
| 83 | h->bufflen = ALUA_INQUIRY_SIZE; | ||
| 84 | return 1; | ||
| 85 | } | ||
| 86 | h->bufflen = len; | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | static struct request *get_alua_req(struct scsi_device *sdev, | ||
| 91 | void *buffer, unsigned buflen, int rw) | ||
| 92 | { | ||
| 93 | struct request *rq; | ||
| 94 | struct request_queue *q = sdev->request_queue; | ||
| 95 | |||
| 96 | rq = blk_get_request(q, rw, GFP_NOIO); | ||
| 97 | |||
| 98 | if (!rq) { | ||
| 99 | sdev_printk(KERN_INFO, sdev, | ||
| 100 | "%s: blk_get_request failed\n", __FUNCTION__); | ||
| 101 | return NULL; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { | ||
| 105 | blk_put_request(rq); | ||
| 106 | sdev_printk(KERN_INFO, sdev, | ||
| 107 | "%s: blk_rq_map_kern failed\n", __FUNCTION__); | ||
| 108 | return NULL; | ||
| 109 | } | ||
| 110 | |||
| 111 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | ||
| 112 | rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; | ||
| 113 | rq->retries = ALUA_FAILOVER_RETRIES; | ||
| 114 | rq->timeout = ALUA_FAILOVER_TIMEOUT; | ||
| 115 | |||
| 116 | return rq; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* | ||
| 120 | * submit_std_inquiry - Issue a standard INQUIRY command | ||
| 121 | * @sdev: sdev the command should be send to | ||
| 122 | */ | ||
| 123 | static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 124 | { | ||
| 125 | struct request *rq; | ||
| 126 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 127 | |||
| 128 | rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); | ||
| 129 | if (!rq) | ||
| 130 | goto done; | ||
| 131 | |||
| 132 | /* Prepare the command. */ | ||
| 133 | rq->cmd[0] = INQUIRY; | ||
| 134 | rq->cmd[1] = 0; | ||
| 135 | rq->cmd[2] = 0; | ||
| 136 | rq->cmd[4] = ALUA_INQUIRY_SIZE; | ||
| 137 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | ||
| 138 | |||
| 139 | rq->sense = h->sense; | ||
| 140 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 141 | rq->sense_len = h->senselen = 0; | ||
| 142 | |||
| 143 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
| 144 | if (err == -EIO) { | ||
| 145 | sdev_printk(KERN_INFO, sdev, | ||
| 146 | "%s: std inquiry failed with %x\n", | ||
| 147 | ALUA_DH_NAME, rq->errors); | ||
| 148 | h->senselen = rq->sense_len; | ||
| 149 | err = SCSI_DH_IO; | ||
| 150 | } | ||
| 151 | blk_put_request(rq); | ||
| 152 | done: | ||
| 153 | return err; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command | ||
| 158 | * @sdev: sdev the command should be sent to | ||
| 159 | */ | ||
