Blame view

drivers/md/dm-mpath-hp-sw.c 6.23 KB
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * Copyright (C) 2005 Mike Christie, All rights reserved.
   * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
   * Authors: Mike Christie
   *          Dave Wysochanski
   *
   * This file is released under the GPL.
   *
   * This module implements the specific path activation code for
   * HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
   * storage arrays.
   * These storage arrays have controller-based failover, not
   * LUN-based failover.  However, LUN-based failover is the design
   * of dm-multipath. Thus, this module is written for LUN-based failover.
   */
  #include <linux/blkdev.h>
  #include <linux/list.h>
  #include <linux/types.h>
  #include <scsi/scsi.h>
  #include <scsi/scsi_cmnd.h>
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
21
  #include <scsi/scsi_dbg.h>
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
22
23
24
25
26
27
  
  #include "dm.h"
  #include "dm-hw-handler.h"
  
  #define DM_MSG_PREFIX "multipath hp-sw"
  #define DM_HP_HWH_NAME "hp-sw"
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
28
  #define DM_HP_HWH_VER "1.0.0"
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
29
30
31
32
33
34
  
  struct hp_sw_context {
  	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
  };
  
  /*
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
   * hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
   * @req: path activation request
   *
   * Examine error codes of request and determine whether the error is retryable.
   * Some error codes are already retried by scsi-ml (see
   * scsi_decide_disposition), but some HP specific codes are not.
   * The intent of this routine is to supply the logic for the HP specific
   * check conditions.
   *
   * Returns:
   *  1 - command completed with retryable error
   *  0 - command completed with non-retryable error
   *
   * Possible optimizations
   * 1. More hardware-specific error codes
   */
  static int hp_sw_error_is_retryable(struct request *req)
  {
  	/*
  	 * NOT_READY is known to be retryable
  	 * For now we just dump out the sense data and call it retryable
  	 */
  	if (status_byte(req->errors) == CHECK_CONDITION)
  		__scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
  
  	/*
  	 * At this point we don't have complete information about all the error
  	 * codes from this hardware, so we are just conservative and retry
  	 * when in doubt.
  	 */
  	return 1;
  }
  
  /*
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
69
70
71
72
73
74
75
76
77
   * hp_sw_end_io - Completion handler for HP path activation.
   * @req: path activation request
   * @error: scsi-ml error
   *
   *  Check sense data, free request structure, and notify dm that
   *  pg initialization has completed.
   *
   * Context: scsi-ml softirq
   *
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
78
79
80
81
82
   */
  static void hp_sw_end_io(struct request *req, int error)
  {
  	struct dm_path *path = req->end_io_data;
  	unsigned err_flags = 0;
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
83
  	if (!error) {
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
84
85
  		DMDEBUG("%s path activation command - success",
  			path->dev->name);
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
86
  		goto out;
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
87
  	}
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
88
89
90
91
92
93
94
95
96
97
98
99
  	if (hp_sw_error_is_retryable(req)) {
  		DMDEBUG("%s path activation command - retry",
  			path->dev->name);
  		err_flags = MP_RETRY;
  		goto out;
  	}
  
  	DMWARN("%s path activation fail - error=0x%x",
  	       path->dev->name, error);
  	err_flags = MP_FAIL_PATH;
  
  out:
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  	req->end_io_data = NULL;
  	__blk_put_request(req->q, req);
  	dm_pg_init_complete(path, err_flags);
  }
  
  /*
   * hp_sw_get_request - Allocate an HP specific path activation request
   * @path: path on which request will be sent (needed for request queue)
   *
   * The START command is used for path activation request.
   * These arrays are controller-based failover, not LUN based.
   * One START command issued to a single path will fail over all
   * LUNs for the same controller.
   *
   * Possible optimizations
   * 1. Make timeout configurable
   * 2. Preallocate request
   */
  static struct request *hp_sw_get_request(struct dm_path *path)
  {
  	struct request *req;
  	struct block_device *bdev = path->dev->bdev;
  	struct request_queue *q = bdev_get_queue(bdev);
  	struct hp_sw_context *h = path->hwhcontext;
  
  	req = blk_get_request(q, WRITE, GFP_NOIO);
  	if (!req)
  		goto out;
  
  	req->timeout = 60 * HZ;
  
  	req->errors = 0;
  	req->cmd_type = REQ_TYPE_BLOCK_PC;
  	req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
  	req->end_io_data = path;
  	req->sense = h->sense;
  	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  	req->cmd[0] = START_STOP;
  	req->cmd[4] = 1;
  	req->cmd_len = COMMAND_SIZE(req->cmd[0]);
  
  out:
  	return req;
  }
  
  /*
   * hp_sw_pg_init - HP path activation implementation.
   * @hwh: hardware handler specific data
   * @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
   * @path: path to send initialization command
   *
   * Send an HP-specific path activation command on 'path'.
   * Do not try to optimize in any way, just send the activation command.
   * More than one path activation command may be sent to the same controller.
   * This seems to work fine for basic failover support.
   *
   * Possible optimizations
   * 1. Detect an in-progress activation request and avoid submitting another one
   * 2. Model the controller and only send a single activation request at a time
   * 3. Determine the state of a path before sending an activation request
   *
   * Context: kmpathd (see process_queued_ios() in dm-mpath.c)
   */
  static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
  			  struct dm_path *path)
  {
  	struct request *req;
  	struct hp_sw_context *h;
  
  	path->hwhcontext = hwh->context;
  	h = hwh->context;
  
  	req = hp_sw_get_request(path);
  	if (!req) {
  		DMERR("%s path activation command - allocation fail",
  		      path->dev->name);
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
176
  		goto retry;
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
177
178
179
180
181
182
  	}
  
  	DMDEBUG("%s path activation command - sent", path->dev->name);
  
  	blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
  	return;
fe97e2aa0   Dave Wysochanski   dm mpath: hp retr...
183
184
  retry:
  	dm_pg_init_complete(path, MP_RETRY);
16ebbf358   Dave Wysochanski   dm mpath: add hp ...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  }
  
  static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
  {
  	struct hp_sw_context *h;
  
  	h = kmalloc(sizeof(*h), GFP_KERNEL);
  	if (!h)
  		return -ENOMEM;
  
  	hwh->context = h;
  
  	return 0;
  }
  
  static void hp_sw_destroy(struct hw_handler *hwh)
  {
  	struct hp_sw_context *h = hwh->context;
  
  	kfree(h);
  }
  
  static struct hw_handler_type hp_sw_hwh = {
  	.name = DM_HP_HWH_NAME,
  	.module = THIS_MODULE,
  	.create = hp_sw_create,
  	.destroy = hp_sw_destroy,
  	.pg_init = hp_sw_pg_init,
  };
  
  static int __init hp_sw_init(void)
  {
  	int r;
  
  	r = dm_register_hw_handler(&hp_sw_hwh);
  	if (r < 0)
  		DMERR("register failed %d", r);
  	else
  		DMINFO("version " DM_HP_HWH_VER " loaded");
  
  	return r;
  }
  
  static void __exit hp_sw_exit(void)
  {
  	int r;
  
  	r = dm_unregister_hw_handler(&hp_sw_hwh);
  	if (r < 0)
  		DMERR("unregister failed %d", r);
  }
  
  module_init(hp_sw_init);
  module_exit(hp_sw_exit);
  
  MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
  MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(DM_HP_HWH_VER);