aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ibmvscsi
diff options
context:
space:
mode:
authorBrian King <brking@linux.vnet.ibm.com>2010-08-05 17:38:34 -0400
committerJames Bottomley <James.Bottomley@suse.de>2010-08-06 13:26:36 -0400
commitd2fab5cf3979c55f802c96616daf96e9e8de1c80 (patch)
tree84f395993608c7bdc75c81ebf98746d2099e3cb3 /drivers/scsi/ibmvscsi
parentd5da3040d798df4bbb62579b97f8b6b83749da22 (diff)
[SCSI] ibmvfc: Fix terminate_rport_io
The ibmvfc driver was incorrectly obtaining a scsi_target pointer from an fc_rport. The way it is coded ensures that ibmvfc's terminate_rport_io handler does absolutely nothing. Fix this up to iterate through affected devices differently, sending cancel and abort task set as appropriate. Without this patch, fast_io_fail_tmo is broken for ibmvfc. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/ibmvscsi')
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c372
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h1
2 files changed, 204 insertions, 169 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index a13db5908426..9f75a6d519a2 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -2039,95 +2039,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
2039} 2039}
2040 2040
2041/** 2041/**
2042 * ibmvfc_abort_task_set - Abort outstanding commands to the device 2042 * ibmvfc_match_rport - Match function for specified remote port
2043 * @sdev: scsi device to abort commands 2043 * @evt: ibmvfc event struct
2044 * 2044 * @device: device to match (rport)
2045 * This sends an Abort Task Set to the VIOS for the specified device. This does
2046 * NOT send any cancel to the VIOS. That must be done separately.
2047 * 2045 *
2048 * Returns: 2046 * Returns:
2049 * 0 on success / other on failure 2047 * 1 if event matches rport / 0 if event does not match rport
2050 **/ 2048 **/
2051static int ibmvfc_abort_task_set(struct scsi_device *sdev) 2049static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport)
2052{ 2050{
2053 struct ibmvfc_host *vhost = shost_priv(sdev->host); 2051 struct fc_rport *cmd_rport;
2054 struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2055 struct ibmvfc_cmd *tmf;
2056 struct ibmvfc_event *evt, *found_evt;
2057 union ibmvfc_iu rsp_iu;
2058 struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
2059 int rsp_rc = -EBUSY;
2060 unsigned long flags;
2061 int rsp_code = 0;
2062 2052
2063 spin_lock_irqsave(vhost->host->host_lock, flags); 2053 if (evt->cmnd) {
2064 found_evt = NULL; 2054 cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
2065 list_for_each_entry(evt, &vhost->sent, queue) { 2055 if (cmd_rport == rport)
2066 if (evt->cmnd && evt->cmnd->device == sdev) { 2056 return 1;
2067 found_evt = evt;
2068 break;
2069 }
2070 }
2071
2072 if (!found_evt) {
2073 if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2074 sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
2075 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2076 return 0;
2077 }
2078
2079 if (vhost->state == IBMVFC_ACTIVE) {
2080 evt = ibmvfc_get_event(vhost);
2081 ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2082
2083 tmf = &evt->iu.cmd;
2084 memset(tmf, 0, sizeof(*tmf));
2085 tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
2086 tmf->resp.len = sizeof(tmf->rsp);
2087 tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
2088 tmf->payload_len = sizeof(tmf->iu);
2089 tmf->resp_len = sizeof(tmf->rsp);
2090 tmf->cancel_key = (unsigned long)sdev->hostdata;
2091 tmf->tgt_scsi_id = rport->port_id;
2092 int_to_scsilun(sdev->lun, &tmf->iu.lun);
2093 tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
2094 tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
2095 evt->sync_iu = &rsp_iu;
2096
2097 init_completion(&evt->comp);
2098 rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2099 } 2057 }
2058 return 0;
2059}
2100 2060
2101 spin_unlock_irqrestore(vhost->host->host_lock, flags); 2061/**
2062 * ibmvfc_match_target - Match function for specified target
2063 * @evt: ibmvfc event struct
2064 * @device: device to match (starget)
2065 *
2066 * Returns:
2067 * 1 if event matches starget / 0 if event does not match starget
2068 **/
2069static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
2070{
2071 if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
2072 return 1;
2073 return 0;
2074}
2102 2075
2103 if (rsp_rc != 0) { 2076/**
2104 sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); 2077 * ibmvfc_match_lun - Match function for specified LUN
2105 return -EIO; 2078 * @evt: ibmvfc event struct
2106 } 2079 * @device: device to match (sdev)
2080 *
2081 * Returns:
2082 * 1 if event matches sdev / 0 if event does not match sdev
2083 **/
2084static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
2085{
2086 if (evt->cmnd && evt->cmnd->device == device)
2087 return 1;
2088 return 0;
2089}
2107 2090
2108 sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); 2091/**
2109 wait_for_completion(&evt->comp); 2092 * ibmvfc_wait_for_ops - Wait for ops to complete
2093 * @vhost: ibmvfc host struct
2094 * @device: device to match (starget or sdev)
2095 * @match: match function
2096 *
2097 * Returns:
2098 * SUCCESS / FAILED
2099 **/
2100static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
2101 int (*match) (struct ibmvfc_event *, void *))
2102{
2103 struct ibmvfc_event *evt;
2104 DECLARE_COMPLETION_ONSTACK(comp);
2105 int wait;
2106 unsigned long flags;
2107 signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
2110 2108
2111 if (rsp_iu.cmd.status) 2109 ENTER;
2112 rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); 2110 do {
2111 wait = 0;
2112 spin_lock_irqsave(vhost->host->host_lock, flags);
2113 list_for_each_entry(evt, &vhost->sent, queue) {
2114 if (match(evt, device)) {
2115 evt->eh_comp = &comp;
2116 wait++;
2117 }
2118 }
2119 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2113 2120
2114 if (rsp_code) { 2121 if (wait) {
2115 if (fc_rsp->flags & FCP_RSP_LEN_VALID) 2122 timeout = wait_for_completion_timeout(&comp, timeout);
2116 rsp_code = fc_rsp->data.info.rsp_code;
2117 2123
2118 sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " 2124 if (!timeout) {
2119 "flags: %x fcp_rsp: %x, scsi_status: %x\n", 2125 wait = 0;
2120 ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error), 2126 spin_lock_irqsave(vhost->host->host_lock, flags);
2121 rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code, 2127 list_for_each_entry(evt, &vhost->sent, queue) {
2122 fc_rsp->scsi_status); 2128 if (match(evt, device)) {
2123 rsp_rc = -EIO; 2129 evt->eh_comp = NULL;
2124 } else 2130 wait++;
2125 sdev_printk(KERN_INFO, sdev, "Abort successful\n"); 2131 }
2132 }
2133 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2134 if (wait)
2135 dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
2136 LEAVE;
2137 return wait ? FAILED : SUCCESS;
2138 }
2139 }
2140 } while (wait);
2126 2141
2127 spin_lock_irqsave(vhost->host->host_lock, flags); 2142 LEAVE;
2128 ibmvfc_free_event(evt); 2143 return SUCCESS;
2129 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2130 return rsp_rc;
2131} 2144}
2132 2145
2133/** 2146/**
@@ -2215,88 +2228,130 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
2215} 2228}
2216 2229
2217/** 2230/**
2218 * ibmvfc_match_target - Match function for specified target 2231 * ibmvfc_match_key - Match function for specified cancel key
2219 * @evt: ibmvfc event struct 2232 * @evt: ibmvfc event struct
2220 * @device: device to match (starget) 2233 * @key: cancel key to match
2221 * 2234 *
2222 * Returns: 2235 * Returns:
2223 * 1 if event matches starget / 0 if event does not match starget 2236 * 1 if event matches key / 0 if event does not match key
2224 **/ 2237 **/
2225static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) 2238static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
2226{ 2239{
2227 if (evt->cmnd && scsi_target(evt->cmnd->device) == device) 2240 unsigned long cancel_key = (unsigned long)key;
2228 return 1;
2229 return 0;
2230}
2231 2241
2232/** 2242 if (evt->crq.format == IBMVFC_CMD_FORMAT &&
2233 * ibmvfc_match_lun - Match function for specified LUN 2243 evt->iu.cmd.cancel_key == cancel_key)
2234 * @evt: ibmvfc event struct
2235 * @device: device to match (sdev)
2236 *
2237 * Returns:
2238 * 1 if event matches sdev / 0 if event does not match sdev
2239 **/
2240static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
2241{
2242 if (evt->cmnd && evt->cmnd->device == device)
2243 return 1; 2244 return 1;
2244 return 0; 2245 return 0;
2245} 2246}
2246 2247
2247/** 2248/**
2248 * ibmvfc_wait_for_ops - Wait for ops to complete 2249 * ibmvfc_abort_task_set - Abort outstanding commands to the device
2249 * @vhost: ibmvfc host struct 2250 * @sdev: scsi device to abort commands
2250 * @device: device to match (starget or sdev) 2251 *
2251 * @match: match function 2252 * This sends an Abort Task Set to the VIOS for the specified device. This does
2253 * NOT send any cancel to the VIOS. That must be done separately.
2252 * 2254 *
2253 * Returns: 2255 * Returns:
2254 * SUCCESS / FAILED 2256 * 0 on success / other on failure
2255 **/ 2257 **/
2256static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, 2258static int ibmvfc_abort_task_set(struct scsi_device *sdev)
2257 int (*match) (struct ibmvfc_event *, void *))
2258{ 2259{
2259 struct ibmvfc_event *evt; 2260 struct ibmvfc_host *vhost = shost_priv(sdev->host);
2260 DECLARE_COMPLETION_ONSTACK(comp); 2261 struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2261 int wait; 2262 struct ibmvfc_cmd *tmf;
2262 unsigned long flags; 2263 struct ibmvfc_event *evt, *found_evt;
2263 signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ; 2264 union ibmvfc_iu rsp_iu;
2265 struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
2266 int rc, rsp_rc = -EBUSY;
2267 unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
2268 int rsp_code = 0;
2264 2269
2265 ENTER; 2270 spin_lock_irqsave(vhost->host->host_lock, flags);
2266 do { 2271 found_evt = NULL;
2267 wait = 0; 2272 list_for_each_entry(evt, &vhost->sent, queue) {
2268 spin_lock_irqsave(vhost->host->host_lock, flags); 2273 if (evt->cmnd && evt->cmnd->device == sdev) {
2269 list_for_each_entry(evt, &vhost->sent, queue) { 2274 found_evt = evt;
2270 if (match(evt, device)) { 2275 break;
2271 evt->eh_comp = &comp;
2272 wait++;
2273 }
2274 } 2276 }
2277 }
2278
2279 if (!found_evt) {
2280 if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2281 sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
2275 spin_unlock_irqrestore(vhost->host->host_lock, flags); 2282 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2283 return 0;
2284 }
2276 2285
2277 if (wait) { 2286 if (vhost->state == IBMVFC_ACTIVE) {
2278 timeout = wait_for_completion_timeout(&comp, timeout); 2287 evt = ibmvfc_get_event(vhost);
2288 ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2279 2289
2280 if (!timeout) { 2290 tmf = &evt->iu.cmd;
2281 wait = 0; 2291 memset(tmf, 0, sizeof(*tmf));
2282 spin_lock_irqsave(vhost->host->host_lock, flags); 2292 tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
2283 list_for_each_entry(evt, &vhost->sent, queue) { 2293 tmf->resp.len = sizeof(tmf->rsp);
2284 if (match(evt, device)) { 2294 tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
2285 evt->eh_comp = NULL; 2295 tmf->payload_len = sizeof(tmf->iu);
2286 wait++; 2296 tmf->resp_len = sizeof(tmf->rsp);
2287 } 2297 tmf->cancel_key = (unsigned long)sdev->hostdata;
2288 } 2298 tmf->tgt_scsi_id = rport->port_id;
2289 spin_unlock_irqrestore(vhost->host->host_lock, flags); 2299 int_to_scsilun(sdev->lun, &tmf->iu.lun);
2290 if (wait) 2300 tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
2291 dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); 2301 tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
2292 LEAVE; 2302 evt->sync_iu = &rsp_iu;
2293 return wait ? FAILED : SUCCESS; 2303
2294 } 2304 init_completion(&evt->comp);
2305 rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2306 }
2307
2308 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2309
2310 if (rsp_rc != 0) {
2311 sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
2312 return -EIO;
2313 }
2314
2315 sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
2316 timeout = wait_for_completion_timeout(&evt->comp, timeout);
2317
2318 if (!timeout) {
2319 rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
2320 if (!rc) {
2321 rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
2322 if (rc == SUCCESS)
2323 rc = 0;
2295 } 2324 }
2296 } while (wait);
2297 2325
2298 LEAVE; 2326 if (rc) {
2299 return SUCCESS; 2327 sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
2328 ibmvfc_reset_host(vhost);
2329 rsp_rc = 0;
2330 goto out;
2331 }
2332 }
2333
2334 if (rsp_iu.cmd.status)
2335 rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
2336
2337 if (rsp_code) {
2338 if (fc_rsp->flags & FCP_RSP_LEN_VALID)
2339 rsp_code = fc_rsp->data.info.rsp_code;
2340
2341 sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
2342 "flags: %x fcp_rsp: %x, scsi_status: %x\n",
2343 ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
2344 rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
2345 fc_rsp->scsi_status);
2346 rsp_rc = -EIO;
2347 } else
2348 sdev_printk(KERN_INFO, sdev, "Abort successful\n");
2349
2350out:
2351 spin_lock_irqsave(vhost->host->host_lock, flags);
2352 ibmvfc_free_event(evt);
2353 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2354 return rsp_rc;
2300} 2355}
2301 2356
2302/** 2357/**
@@ -2354,18 +2409,6 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
2354} 2409}
2355 2410
2356/** 2411/**
2357 * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
2358 * @sdev: scsi device struct
2359 * @data: return code
2360 *
2361 **/
2362static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
2363{
2364 unsigned long *rc = data;
2365 *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
2366}
2367
2368/**
2369 * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function 2412 * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
2370 * @sdev: scsi device struct 2413 * @sdev: scsi device struct
2371 * @data: return code 2414 * @data: return code
@@ -2378,18 +2421,6 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
2378} 2421}
2379 2422
2380/** 2423/**
2381 * ibmvfc_dev_abort_all - Device iterated abort task set function
2382 * @sdev: scsi device struct
2383 * @data: return code
2384 *
2385 **/
2386static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
2387{
2388 unsigned long *rc = data;
2389 *rc |= ibmvfc_abort_task_set(sdev);
2390}
2391
2392/**
2393 * ibmvfc_eh_target_reset_handler - Reset the target 2424 * ibmvfc_eh_target_reset_handler - Reset the target
2394 * @cmd: scsi command struct 2425 * @cmd: scsi command struct
2395 * 2426 *
@@ -2443,19 +2474,22 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
2443 **/ 2474 **/
2444static void ibmvfc_terminate_rport_io(struct fc_rport *rport) 2475static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
2445{ 2476{
2446 struct scsi_target *starget = to_scsi_target(&rport->dev); 2477 struct Scsi_Host *shost = rport_to_shost(rport);
2447 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
2448 struct ibmvfc_host *vhost = shost_priv(shost); 2478 struct ibmvfc_host *vhost = shost_priv(shost);
2449 unsigned long cancel_rc = 0; 2479 struct fc_rport *dev_rport;
2450 unsigned long abort_rc = 0; 2480 struct scsi_device *sdev;
2451 int rc = FAILED; 2481 unsigned long rc;
2452 2482
2453 ENTER; 2483 ENTER;
2454 starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts); 2484 shost_for_each_device(sdev, shost) {
2455 starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); 2485 dev_rport = starget_to_rport(scsi_target(sdev));
2486 if (dev_rport != rport)
2487 continue;
2488 ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
2489 ibmvfc_abort_task_set(sdev);
2490 }
2456 2491
2457 if (!cancel_rc && !abort_rc) 2492 rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
2458 rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
2459 2493
2460 if (rc == FAILED) 2494 if (rc == FAILED)
2461 ibmvfc_issue_fc_host_lip(shost); 2495 ibmvfc_issue_fc_host_lip(shost);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index af48172112fa..d47cefc22ed3 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -38,6 +38,7 @@
38#define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \ 38#define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \
39 (IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT) 39 (IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT)
40#define IBMVFC_INIT_TIMEOUT 120 40#define IBMVFC_INIT_TIMEOUT 120
41#define IBMVFC_ABORT_TIMEOUT 8
41#define IBMVFC_ABORT_WAIT_TIMEOUT 40 42#define IBMVFC_ABORT_WAIT_TIMEOUT 40
42#define IBMVFC_MAX_REQUESTS_DEFAULT 100 43#define IBMVFC_MAX_REQUESTS_DEFAULT 100
43 44