aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ibmvscsi/ibmvfc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c380
1 files changed, 211 insertions, 169 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bd96cecaa619..9f75a6d519a2 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -433,6 +433,9 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
433{ 433{
434 switch (tgt->action) { 434 switch (tgt->action) {
435 case IBMVFC_TGT_ACTION_DEL_RPORT: 435 case IBMVFC_TGT_ACTION_DEL_RPORT:
436 if (action == IBMVFC_TGT_ACTION_DELETED_RPORT)
437 tgt->action = action;
438 case IBMVFC_TGT_ACTION_DELETED_RPORT:
436 break; 439 break;
437 default: 440 default:
438 if (action == IBMVFC_TGT_ACTION_DEL_RPORT) 441 if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
@@ -2036,95 +2039,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
2036} 2039}
2037 2040
2038/** 2041/**
2039 * ibmvfc_abort_task_set - Abort outstanding commands to the device 2042 * ibmvfc_match_rport - Match function for specified remote port
2040 * @sdev: scsi device to abort commands 2043 * @evt: ibmvfc event struct
2041 * 2044 * @device: device to match (rport)
2042 * This sends an Abort Task Set to the VIOS for the specified device. This does
2043 * NOT send any cancel to the VIOS. That must be done separately.
2044 * 2045 *
2045 * Returns: 2046 * Returns:
2046 * 0 on success / other on failure 2047 * 1 if event matches rport / 0 if event does not match rport
2047 **/ 2048 **/
2048static int ibmvfc_abort_task_set(struct scsi_device *sdev) 2049static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport)
2049{ 2050{
2050 struct ibmvfc_host *vhost = shost_priv(sdev->host); 2051 struct fc_rport *cmd_rport;
2051 struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2052 struct ibmvfc_cmd *tmf;
2053 struct ibmvfc_event *evt, *found_evt;
2054 union ibmvfc_iu rsp_iu;
2055 struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
2056 int rsp_rc = -EBUSY;
2057 unsigned long flags;
2058 int rsp_code = 0;
2059 2052
2060 spin_lock_irqsave(vhost->host->host_lock, flags); 2053 if (evt->cmnd) {
2061 found_evt = NULL; 2054 cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
2062 list_for_each_entry(evt, &vhost->sent, queue) { 2055 if (cmd_rport == rport)
2063 if (evt->cmnd && evt->cmnd->device == sdev) { 2056 return 1;
2064 found_evt = evt;
2065 break;
2066 }
2067 }
2068
2069 if (!found_evt) {
2070 if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2071 sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
2072 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2073 return 0;
2074 }
2075
2076 if (vhost->state == IBMVFC_ACTIVE) {
2077 evt = ibmvfc_get_event(vhost);
2078 ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2079
2080 tmf = &evt->iu.cmd;
2081 memset(tmf, 0, sizeof(*tmf));
2082 tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
2083 tmf->resp.len = sizeof(tmf->rsp);
2084 tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
2085 tmf->payload_len = sizeof(tmf->iu);
2086 tmf->resp_len = sizeof(tmf->rsp);
2087 tmf->cancel_key = (unsigned long)sdev->hostdata;
2088 tmf->tgt_scsi_id = rport->port_id;
2089 int_to_scsilun(sdev->lun, &tmf->iu.lun);
2090 tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
2091 tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
2092 evt->sync_iu = &rsp_iu;
2093
2094 init_completion(&evt->comp);
2095 rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2096 } 2057 }
2058 return 0;
2059}
2097 2060
2098 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}
2099 2075
2100 if (rsp_rc != 0) { 2076/**
2101 sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); 2077 * ibmvfc_match_lun - Match function for specified LUN
2102 return -EIO; 2078 * @evt: ibmvfc event struct
2103 } 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}
2104 2090
2105 sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); 2091/**
2106 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;
2107 2108
2108 if (rsp_iu.cmd.status) 2109 ENTER;
2109 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 = ∁
2116 wait++;
2117 }
2118 }
2119 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2110 2120
2111 if (rsp_code) { 2121 if (wait) {
2112 if (fc_rsp->flags & FCP_RSP_LEN_VALID) 2122 timeout = wait_for_completion_timeout(&comp, timeout);
2113 rsp_code = fc_rsp->data.info.rsp_code;
2114 2123
2115 sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " 2124 if (!timeout) {
2116 "flags: %x fcp_rsp: %x, scsi_status: %x\n", 2125 wait = 0;
2117 ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error), 2126 spin_lock_irqsave(vhost->host->host_lock, flags);
2118 rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code, 2127 list_for_each_entry(evt, &vhost->sent, queue) {
2119 fc_rsp->scsi_status); 2128 if (match(evt, device)) {
2120 rsp_rc = -EIO; 2129 evt->eh_comp = NULL;
2121 } else 2130 wait++;
2122 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);
2123 2141
2124 spin_lock_irqsave(vhost->host->host_lock, flags); 2142 LEAVE;
2125 ibmvfc_free_event(evt); 2143 return SUCCESS;
2126 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2127 return rsp_rc;
2128} 2144}
2129 2145
2130/** 2146/**
@@ -2212,88 +2228,130 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
2212} 2228}
2213 2229
2214/** 2230/**
2215 * ibmvfc_match_target - Match function for specified target 2231 * ibmvfc_match_key - Match function for specified cancel key
2216 * @evt: ibmvfc event struct 2232 * @evt: ibmvfc event struct
2217 * @device: device to match (starget) 2233 * @key: cancel key to match
2218 * 2234 *
2219 * Returns: 2235 * Returns:
2220 * 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
2221 **/ 2237 **/
2222static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) 2238static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
2223{ 2239{
2224 if (evt->cmnd && scsi_target(evt->cmnd->device) == device) 2240 unsigned long cancel_key = (unsigned long)key;
2225 return 1;
2226 return 0;
2227}
2228 2241
2229/** 2242 if (evt->crq.format == IBMVFC_CMD_FORMAT &&
2230 * ibmvfc_match_lun - Match function for specified LUN 2243 evt->iu.cmd.cancel_key == cancel_key)
2231 * @evt: ibmvfc event struct
2232 * @device: device to match (sdev)
2233 *
2234 * Returns:
2235 * 1 if event matches sdev / 0 if event does not match sdev
2236 **/
2237static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
2238{
2239 if (evt->cmnd && evt->cmnd->device == device)
2240 return 1; 2244 return 1;
2241 return 0; 2245 return 0;
2242} 2246}
2243 2247
2244/** 2248/**
2245 * ibmvfc_wait_for_ops - Wait for ops to complete 2249 * ibmvfc_abort_task_set - Abort outstanding commands to the device
2246 * @vhost: ibmvfc host struct 2250 * @sdev: scsi device to abort commands
2247 * @device: device to match (starget or sdev) 2251 *
2248 * @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.
2249 * 2254 *
2250 * Returns: 2255 * Returns:
2251 * SUCCESS / FAILED 2256 * 0 on success / other on failure
2252 **/ 2257 **/
2253static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, 2258static int ibmvfc_abort_task_set(struct scsi_device *sdev)
2254 int (*match) (struct ibmvfc_event *, void *))
2255{ 2259{
2256 struct ibmvfc_event *evt; 2260 struct ibmvfc_host *vhost = shost_priv(sdev->host);
2257 DECLARE_COMPLETION_ONSTACK(comp); 2261 struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2258 int wait; 2262 struct ibmvfc_cmd *tmf;
2259 unsigned long flags; 2263 struct ibmvfc_event *evt, *found_evt;
2260 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;
2261 2269
2262 ENTER; 2270 spin_lock_irqsave(vhost->host->host_lock, flags);
2263 do { 2271 found_evt = NULL;
2264 wait = 0; 2272 list_for_each_entry(evt, &vhost->sent, queue) {
2265 spin_lock_irqsave(vhost->host->host_lock, flags); 2273 if (evt->cmnd && evt->cmnd->device == sdev) {
2266 list_for_each_entry(evt, &vhost->sent, queue) { 2274 found_evt = evt;
2267 if (match(evt, device)) { 2275 break;
2268 evt->eh_comp = ∁
2269 wait++;
2270 }
2271 } 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");
2272 spin_unlock_irqrestore(vhost->host->host_lock, flags); 2282 spin_unlock_irqrestore(vhost->host->host_lock, flags);
2283 return 0;
2284 }
2273 2285
2274 if (wait) { 2286 if (vhost->state == IBMVFC_ACTIVE) {
2275 timeout = wait_for_completion_timeout(&comp, timeout); 2287 evt = ibmvfc_get_event(vhost);
2288 ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2276 2289
2277 if (!timeout) { 2290 tmf = &evt->iu.cmd;
2278 wait = 0; 2291 memset(tmf, 0, sizeof(*tmf));
2279 spin_lock_irqsave(vhost->host->host_lock, flags); 2292 tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
2280 list_for_each_entry(evt, &vhost->sent, queue) { 2293 tmf->resp.len = sizeof(tmf->rsp);
2281 if (match(evt, device)) { 2294 tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
2282 evt->eh_comp = NULL; 2295 tmf->payload_len = sizeof(tmf->iu);
2283 wait++; 2296 tmf->resp_len = sizeof(tmf->rsp);
2284 } 2297 tmf->cancel_key = (unsigned long)sdev->hostdata;
2285 } 2298 tmf->tgt_scsi_id = rport->port_id;
2286 spin_unlock_irqrestore(vhost->host->host_lock, flags); 2299 int_to_scsilun(sdev->lun, &tmf->iu.lun);
2287 if (wait) 2300 tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
2288 dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); 2301 tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
2289 LEAVE; 2302 evt->sync_iu = &rsp_iu;
2290 return wait ? FAILED : SUCCESS; 2303
2291 } 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;
2292 } 2324 }
2293 } while (wait);
2294 2325
2295 LEAVE; 2326 if (rc) {
2296 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;
2297} 2355}
2298 2356
2299/** 2357/**
@@ -2351,18 +2409,6 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
2351} 2409}
2352 2410
2353/** 2411/**
2354 * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
2355 * @sdev: scsi device struct
2356 * @data: return code
2357 *
2358 **/
2359static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
2360{
2361 unsigned long *rc = data;
2362 *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
2363}
2364
2365/**
2366 * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function 2412 * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
2367 * @sdev: scsi device struct 2413 * @sdev: scsi device struct
2368 * @data: return code 2414 * @data: return code
@@ -2375,18 +2421,6 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
2375} 2421}
2376 2422
2377/** 2423/**
2378 * ibmvfc_dev_abort_all - Device iterated abort task set function
2379 * @sdev: scsi device struct
2380 * @data: return code
2381 *
2382 **/
2383static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
2384{
2385 unsigned long *rc = data;
2386 *rc |= ibmvfc_abort_task_set(sdev);
2387}
2388
2389/**
2390 * ibmvfc_eh_target_reset_handler - Reset the target 2424 * ibmvfc_eh_target_reset_handler - Reset the target
2391 * @cmd: scsi command struct 2425 * @cmd: scsi command struct
2392 * 2426 *
@@ -2440,19 +2474,22 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
2440 **/ 2474 **/
2441static void ibmvfc_terminate_rport_io(struct fc_rport *rport) 2475static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
2442{ 2476{
2443 struct scsi_target *starget = to_scsi_target(&rport->dev); 2477 struct Scsi_Host *shost = rport_to_shost(rport);
2444 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
2445 struct ibmvfc_host *vhost = shost_priv(shost); 2478 struct ibmvfc_host *vhost = shost_priv(shost);
2446 unsigned long cancel_rc = 0; 2479 struct fc_rport *dev_rport;
2447 unsigned long abort_rc = 0; 2480 struct scsi_device *sdev;
2448 int rc = FAILED; 2481 unsigned long rc;
2449 2482
2450 ENTER; 2483 ENTER;
2451 starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts); 2484 shost_for_each_device(sdev, shost) {
2452 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 }
2453 2491
2454 if (!cancel_rc && !abort_rc) 2492 rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
2455 rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
2456 2493
2457 if (rc == FAILED) 2494 if (rc == FAILED)
2458 ibmvfc_issue_fc_host_lip(shost); 2495 ibmvfc_issue_fc_host_lip(shost);
@@ -4193,11 +4230,15 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
4193 if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { 4230 if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
4194 tgt_dbg(tgt, "Deleting rport\n"); 4231 tgt_dbg(tgt, "Deleting rport\n");
4195 list_del(&tgt->queue); 4232 list_del(&tgt->queue);
4233 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
4196 spin_unlock_irqrestore(vhost->host->host_lock, flags); 4234 spin_unlock_irqrestore(vhost->host->host_lock, flags);
4197 fc_remote_port_delete(rport); 4235 fc_remote_port_delete(rport);
4198 del_timer_sync(&tgt->timer); 4236 del_timer_sync(&tgt->timer);
4199 kref_put(&tgt->kref, ibmvfc_release_tgt); 4237 kref_put(&tgt->kref, ibmvfc_release_tgt);
4200 return; 4238 return;
4239 } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
4240 spin_unlock_irqrestore(vhost->host->host_lock, flags);
4241 return;
4201 } 4242 }
4202 4243
4203 if (rport) { 4244 if (rport) {
@@ -4297,6 +4338,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
4297 rport = tgt->rport; 4338 rport = tgt->rport;
4298 tgt->rport = NULL; 4339 tgt->rport = NULL;
4299 list_del(&tgt->queue); 4340 list_del(&tgt->queue);
4341 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
4300 spin_unlock_irqrestore(vhost->host->host_lock, flags); 4342 spin_unlock_irqrestore(vhost->host->host_lock, flags);
4301 if (rport) 4343 if (rport)
4302 fc_remote_port_delete(rport); 4344 fc_remote_port_delete(rport);