aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2008-07-17 19:53:21 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-07-26 15:14:52 -0400
commit057ea7c9683c3d684128cced796f03c179ecf1c2 (patch)
treeff9494b4da41258c06ef44ab5f7a6ed04daf3d98
parentca9f0089867c9e476cf2e6d4615d2aae887171b2 (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/Kconfig8
-rw-r--r--drivers/scsi/device_handler/Makefile1
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c802
-rw-r--r--include/scsi/scsi.h3
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
34config 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
5obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o 5obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o
6obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o 6obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o
7obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o 7obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o
8obj-$(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
53struct 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
68static 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
75static 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
90static 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 */
123static 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);
152done:
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 */