]> Git Repo - linux.git/blob - drivers/scsi/device_handler/scsi_dh_hp_sw.c
crypto: akcipher - Drop sign/verify operations
[linux.git] / drivers / scsi / device_handler / scsi_dh_hp_sw.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
4  * upgraded.
5  *
6  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
7  * Copyright (C) 2006 Mike Christie
8  * Copyright (C) 2008 Hannes Reinecke <[email protected]>
9  */
10
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <scsi/scsi.h>
14 #include <scsi/scsi_dbg.h>
15 #include <scsi/scsi_eh.h>
16 #include <scsi/scsi_dh.h>
17
18 #define HP_SW_NAME                      "hp_sw"
19
20 #define HP_SW_TIMEOUT                   (60 * HZ)
21 #define HP_SW_RETRIES                   3
22
23 #define HP_SW_PATH_UNINITIALIZED        -1
24 #define HP_SW_PATH_ACTIVE               0
25 #define HP_SW_PATH_PASSIVE              1
26
27 struct hp_sw_dh_data {
28         int path_state;
29         int retries;
30         int retry_cnt;
31         struct scsi_device *sdev;
32 };
33
34 static int hp_sw_start_stop(struct hp_sw_dh_data *);
35
36 /*
37  * tur_done - Handle TEST UNIT READY return status
38  * @sdev: sdev the command has been sent to
39  * @errors: blk error code
40  *
41  * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
42  */
43 static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
44                     struct scsi_sense_hdr *sshdr)
45 {
46         int ret = SCSI_DH_IO;
47
48         switch (sshdr->sense_key) {
49         case NOT_READY:
50                 if (sshdr->asc == 0x04 && sshdr->ascq == 2) {
51                         /*
52                          * LUN not ready - Initialization command required
53                          *
54                          * This is the passive path
55                          */
56                         h->path_state = HP_SW_PATH_PASSIVE;
57                         ret = SCSI_DH_OK;
58                         break;
59                 }
60                 fallthrough;
61         default:
62                 sdev_printk(KERN_WARNING, sdev,
63                            "%s: sending tur failed, sense %x/%x/%x\n",
64                            HP_SW_NAME, sshdr->sense_key, sshdr->asc,
65                            sshdr->ascq);
66                 break;
67         }
68         return ret;
69 }
70
71 /*
72  * hp_sw_tur - Send TEST UNIT READY
73  * @sdev: sdev command should be sent to
74  *
75  * Use the TEST UNIT READY command to determine
76  * the path state.
77  */
78 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
79 {
80         unsigned char cmd[6] = { TEST_UNIT_READY };
81         struct scsi_sense_hdr sshdr;
82         int ret, res;
83         blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
84                                 REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
85         struct scsi_failure failure_defs[] = {
86                 {
87                         .sense = UNIT_ATTENTION,
88                         .asc = SCMD_FAILURE_ASC_ANY,
89                         .ascq = SCMD_FAILURE_ASCQ_ANY,
90                         .allowed = SCMD_FAILURE_NO_LIMIT,
91                         .result = SAM_STAT_CHECK_CONDITION,
92                 },
93                 {}
94         };
95         struct scsi_failures failures = {
96                 .failure_definitions = failure_defs,
97         };
98         const struct scsi_exec_args exec_args = {
99                 .sshdr = &sshdr,
100                 .failures = &failures,
101         };
102
103         res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
104                                HP_SW_RETRIES, &exec_args);
105         if (res > 0 && scsi_sense_valid(&sshdr)) {
106                 ret = tur_done(sdev, h, &sshdr);
107         } else if (res == 0) {
108                 h->path_state = HP_SW_PATH_ACTIVE;
109                 ret = SCSI_DH_OK;
110         } else {
111                 sdev_printk(KERN_WARNING, sdev,
112                             "%s: sending tur failed with %x\n",
113                             HP_SW_NAME, res);
114                 ret = SCSI_DH_IO;
115         }
116
117         return ret;
118 }
119
120 /*
121  * hp_sw_start_stop - Send START STOP UNIT command
122  * @sdev: sdev command should be sent to
123  *
124  * Sending START STOP UNIT activates the SP.
125  */
126 static int hp_sw_start_stop(struct hp_sw_dh_data *h)
127 {
128         unsigned char cmd[6] = { START_STOP, 0, 0, 0, 1, 0 };
129         struct scsi_sense_hdr sshdr;
130         struct scsi_device *sdev = h->sdev;
131         int res, rc;
132         blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
133                                 REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
134         struct scsi_failure failure_defs[] = {
135                 {
136                         /*
137                          * LUN not ready - manual intervention required
138                          *
139                          * Switch-over in progress, retry.
140                          */
141                         .sense = NOT_READY,
142                         .asc = 0x04,
143                         .ascq = 0x03,
144                         .allowed = HP_SW_RETRIES,
145                         .result = SAM_STAT_CHECK_CONDITION,
146                 },
147                 {}
148         };
149         struct scsi_failures failures = {
150                 .failure_definitions = failure_defs,
151         };
152         const struct scsi_exec_args exec_args = {
153                 .sshdr = &sshdr,
154                 .failures = &failures,
155         };
156
157         res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
158                                HP_SW_RETRIES, &exec_args);
159         if (!res) {
160                 return SCSI_DH_OK;
161         } else if (res < 0 || !scsi_sense_valid(&sshdr)) {
162                 sdev_printk(KERN_WARNING, sdev,
163                             "%s: sending start_stop_unit failed, "
164                             "no sense available\n", HP_SW_NAME);
165                 return SCSI_DH_IO;
166         }
167
168         switch (sshdr.sense_key) {
169         case NOT_READY:
170                 if (sshdr.asc == 0x04 && sshdr.ascq == 3) {
171                         rc = SCSI_DH_RETRY;
172                         break;
173                 }
174                 fallthrough;
175         default:
176                 sdev_printk(KERN_WARNING, sdev,
177                             "%s: sending start_stop_unit failed, "
178                             "sense %x/%x/%x\n", HP_SW_NAME,
179                             sshdr.sense_key, sshdr.asc, sshdr.ascq);
180                 rc = SCSI_DH_IO;
181         }
182
183         return rc;
184 }
185
186 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
187 {
188         struct hp_sw_dh_data *h = sdev->handler_data;
189
190         if (h->path_state != HP_SW_PATH_ACTIVE) {
191                 req->rq_flags |= RQF_QUIET;
192                 return BLK_STS_IOERR;
193         }
194
195         return BLK_STS_OK;
196 }
197
198 /*
199  * hp_sw_activate - Activate a path
200  * @sdev: sdev on the path to be activated
201  *
202  * The HP Active/Passive firmware is pretty simple;
203  * the passive path reports NOT READY with sense codes
204  * 0x04/0x02; a START STOP UNIT command will then
205  * activate the passive path (and deactivate the
206  * previously active one).
207  */
208 static int hp_sw_activate(struct scsi_device *sdev,
209                                 activate_complete fn, void *data)
210 {
211         int ret = SCSI_DH_OK;
212         struct hp_sw_dh_data *h = sdev->handler_data;
213
214         ret = hp_sw_tur(sdev, h);
215
216         if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE)
217                 ret = hp_sw_start_stop(h);
218
219         if (fn)
220                 fn(data, ret);
221         return 0;
222 }
223
224 static int hp_sw_bus_attach(struct scsi_device *sdev)
225 {
226         struct hp_sw_dh_data *h;
227         int ret;
228
229         h = kzalloc(sizeof(*h), GFP_KERNEL);
230         if (!h)
231                 return SCSI_DH_NOMEM;
232         h->path_state = HP_SW_PATH_UNINITIALIZED;
233         h->retries = HP_SW_RETRIES;
234         h->sdev = sdev;
235
236         ret = hp_sw_tur(sdev, h);
237         if (ret != SCSI_DH_OK)
238                 goto failed;
239         if (h->path_state == HP_SW_PATH_UNINITIALIZED) {
240                 ret = SCSI_DH_NOSYS;
241                 goto failed;
242         }
243
244         sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
245                     HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
246                     "active":"passive");
247
248         sdev->handler_data = h;
249         return SCSI_DH_OK;
250 failed:
251         kfree(h);
252         return ret;
253 }
254
255 static void hp_sw_bus_detach( struct scsi_device *sdev )
256 {
257         kfree(sdev->handler_data);
258         sdev->handler_data = NULL;
259 }
260
261 static struct scsi_device_handler hp_sw_dh = {
262         .name           = HP_SW_NAME,
263         .module         = THIS_MODULE,
264         .attach         = hp_sw_bus_attach,
265         .detach         = hp_sw_bus_detach,
266         .activate       = hp_sw_activate,
267         .prep_fn        = hp_sw_prep_fn,
268 };
269
270 static int __init hp_sw_init(void)
271 {
272         return scsi_register_device_handler(&hp_sw_dh);
273 }
274
275 static void __exit hp_sw_exit(void)
276 {
277         scsi_unregister_device_handler(&hp_sw_dh);
278 }
279
280 module_init(hp_sw_init);
281 module_exit(hp_sw_exit);
282
283 MODULE_DESCRIPTION("HP Active/Passive driver");
284 MODULE_AUTHOR("Mike Christie <[email protected]");
285 MODULE_LICENSE("GPL");
This page took 0.048235 seconds and 4 git commands to generate.