diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2007-06-13 18:12:26 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-06-17 16:52:53 -0400 |
commit | 3d0e91f7ace12499c4b00088e9a6b1361e1bb0ca (patch) | |
tree | 273cd6b7b751b371ef5584475456d253d4f97577 /drivers/scsi/ibmvscsi/ibmvscsi.c | |
parent | 6c0a60ec52042ece8bf4904c91ac497188e8d70b (diff) |
[SCSI] ibmvscsi: Add eh_host_reset_handler
Adds an eh_host_reset_handler to ibmvscsi which resets the connection
to the vscsi server. This patch also adds a timer to internally
issues commands to prevent client hangs in the case of a misbehaving
server. Tested by modifying the VIOS such that it would occasionally
drop one or more request in sequence.
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvscsi.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.c | 136 |
1 files changed, 106 insertions, 30 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index c63a26e2fbc7..0a16972b2599 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c | |||
@@ -509,6 +509,70 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, | |||
509 | return map_single_data(cmd, srp_cmd, dev); | 509 | return map_single_data(cmd, srp_cmd, dev); |
510 | } | 510 | } |
511 | 511 | ||
512 | /** | ||
513 | * purge_requests: Our virtual adapter just shut down. purge any sent requests | ||
514 | * @hostdata: the adapter | ||
515 | */ | ||
516 | static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) | ||
517 | { | ||
518 | struct srp_event_struct *tmp_evt, *pos; | ||
519 | unsigned long flags; | ||
520 | |||
521 | spin_lock_irqsave(hostdata->host->host_lock, flags); | ||
522 | list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { | ||
523 | list_del(&tmp_evt->list); | ||
524 | del_timer(&tmp_evt->timer); | ||
525 | if (tmp_evt->cmnd) { | ||
526 | tmp_evt->cmnd->result = (error_code << 16); | ||
527 | unmap_cmd_data(&tmp_evt->iu.srp.cmd, | ||
528 | tmp_evt, | ||
529 | tmp_evt->hostdata->dev); | ||
530 | if (tmp_evt->cmnd_done) | ||
531 | tmp_evt->cmnd_done(tmp_evt->cmnd); | ||
532 | } else if (tmp_evt->done) | ||
533 | tmp_evt->done(tmp_evt); | ||
534 | free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); | ||
535 | } | ||
536 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); | ||
537 | } | ||
538 | |||
539 | /** | ||
540 | * ibmvscsi_reset_host - Reset the connection to the server | ||
541 | * @hostdata: struct ibmvscsi_host_data to reset | ||
542 | */ | ||
543 | static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata) | ||
544 | { | ||
545 | scsi_block_requests(hostdata->host); | ||
546 | atomic_set(&hostdata->request_limit, 0); | ||
547 | |||
548 | purge_requests(hostdata, DID_ERROR); | ||
549 | if ((ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata)) || | ||
550 | (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0)) || | ||
551 | (vio_enable_interrupts(to_vio_dev(hostdata->dev)))) { | ||
552 | atomic_set(&hostdata->request_limit, -1); | ||
553 | dev_err(hostdata->dev, "error after reset\n"); | ||
554 | } | ||
555 | |||
556 | scsi_unblock_requests(hostdata->host); | ||
557 | } | ||
558 | |||
559 | /** | ||
560 | * ibmvscsi_timeout - Internal command timeout handler | ||
561 | * @evt_struct: struct srp_event_struct that timed out | ||
562 | * | ||
563 | * Called when an internally generated command times out | ||
564 | */ | ||
565 | static void ibmvscsi_timeout(struct srp_event_struct *evt_struct) | ||
566 | { | ||
567 | struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; | ||
568 | |||
569 | dev_err(hostdata->dev, "Command timed out (%x). Resetting connection\n", | ||
570 | evt_struct->iu.srp.cmd.opcode); | ||
571 | |||
572 | ibmvscsi_reset_host(hostdata); | ||
573 | } | ||
574 | |||
575 | |||
512 | /* ------------------------------------------------------------ | 576 | /* ------------------------------------------------------------ |
513 | * Routines for sending and receiving SRPs | 577 | * Routines for sending and receiving SRPs |
514 | */ | 578 | */ |
@@ -516,12 +580,14 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, | |||
516 | * ibmvscsi_send_srp_event: - Transforms event to u64 array and calls send_crq() | 580 | * ibmvscsi_send_srp_event: - Transforms event to u64 array and calls send_crq() |
517 | * @evt_struct: evt_struct to be sent | 581 | * @evt_struct: evt_struct to be sent |
518 | * @hostdata: ibmvscsi_host_data of host | 582 | * @hostdata: ibmvscsi_host_data of host |
583 | * @timeout: timeout in seconds - 0 means do not time command | ||
519 | * | 584 | * |
520 | * Returns the value returned from ibmvscsi_send_crq(). (Zero for success) | 585 | * Returns the value returned from ibmvscsi_send_crq(). (Zero for success) |
521 | * Note that this routine assumes that host_lock is held for synchronization | 586 | * Note that this routine assumes that host_lock is held for synchronization |
522 | */ | 587 | */ |
523 | static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, | 588 | static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, |
524 | struct ibmvscsi_host_data *hostdata) | 589 | struct ibmvscsi_host_data *hostdata, |
590 | unsigned long timeout) | ||
525 | { | 591 | { |
526 | u64 *crq_as_u64 = (u64 *) &evt_struct->crq; | 592 | u64 *crq_as_u64 = (u64 *) &evt_struct->crq; |
527 | int request_status; | 593 | int request_status; |
@@ -577,9 +643,18 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, | |||
577 | */ | 643 | */ |
578 | list_add_tail(&evt_struct->list, &hostdata->sent); | 644 | list_add_tail(&evt_struct->list, &hostdata->sent); |
579 | 645 | ||
646 | init_timer(&evt_struct->timer); | ||
647 | if (timeout) { | ||
648 | evt_struct->timer.data = (unsigned long) evt_struct; | ||
649 | evt_struct->timer.expires = jiffies + (timeout * HZ); | ||
650 | evt_struct->timer.function = (void (*)(unsigned long))ibmvscsi_timeout; | ||
651 | add_timer(&evt_struct->timer); | ||
652 | } | ||
653 | |||
580 | if ((rc = | 654 | if ((rc = |
581 | ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) { | 655 | ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) { |
582 | list_del(&evt_struct->list); | 656 | list_del(&evt_struct->list); |
657 | del_timer(&evt_struct->timer); | ||
583 | 658 | ||
584 | dev_err(hostdata->dev, "send error %d\n", rc); | 659 | dev_err(hostdata->dev, "send error %d\n", rc); |
585 | atomic_inc(&hostdata->request_limit); | 660 | atomic_inc(&hostdata->request_limit); |
@@ -709,7 +784,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, | |||
709 | offsetof(struct srp_indirect_buf, desc_list); | 784 | offsetof(struct srp_indirect_buf, desc_list); |
710 | } | 785 | } |
711 | 786 | ||
712 | return ibmvscsi_send_srp_event(evt_struct, hostdata); | 787 | return ibmvscsi_send_srp_event(evt_struct, hostdata, 0); |
713 | } | 788 | } |
714 | 789 | ||
715 | /* ------------------------------------------------------------ | 790 | /* ------------------------------------------------------------ |
@@ -800,7 +875,7 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) | |||
800 | return; | 875 | return; |
801 | } | 876 | } |
802 | 877 | ||
803 | if (ibmvscsi_send_srp_event(evt_struct, hostdata)) { | 878 | if (ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2)) { |
804 | dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n"); | 879 | dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n"); |
805 | dma_unmap_single(hostdata->dev, | 880 | dma_unmap_single(hostdata->dev, |
806 | addr, | 881 | addr, |
@@ -889,7 +964,7 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata) | |||
889 | */ | 964 | */ |
890 | atomic_set(&hostdata->request_limit, 1); | 965 | atomic_set(&hostdata->request_limit, 1); |
891 | 966 | ||
892 | rc = ibmvscsi_send_srp_event(evt_struct, hostdata); | 967 | rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2); |
893 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); | 968 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); |
894 | dev_info(hostdata->dev, "sent SRP login\n"); | 969 | dev_info(hostdata->dev, "sent SRP login\n"); |
895 | return rc; | 970 | return rc; |
@@ -969,7 +1044,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) | |||
969 | 1044 | ||
970 | evt->sync_srp = &srp_rsp; | 1045 | evt->sync_srp = &srp_rsp; |
971 | init_completion(&evt->comp); | 1046 | init_completion(&evt->comp); |
972 | rsp_rc = ibmvscsi_send_srp_event(evt, hostdata); | 1047 | rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2); |
973 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); | 1048 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); |
974 | if (rsp_rc != 0) { | 1049 | if (rsp_rc != 0) { |
975 | sdev_printk(KERN_ERR, cmd->device, | 1050 | sdev_printk(KERN_ERR, cmd->device, |
@@ -1077,7 +1152,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) | |||
1077 | 1152 | ||
1078 | evt->sync_srp = &srp_rsp; | 1153 | evt->sync_srp = &srp_rsp; |
1079 | init_completion(&evt->comp); | 1154 | init_completion(&evt->comp); |
1080 | rsp_rc = ibmvscsi_send_srp_event(evt, hostdata); | 1155 | rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2); |
1081 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); | 1156 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); |
1082 | if (rsp_rc != 0) { | 1157 | if (rsp_rc != 0) { |
1083 | sdev_printk(KERN_ERR, cmd->device, | 1158 | sdev_printk(KERN_ERR, cmd->device, |
@@ -1133,32 +1208,30 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) | |||
1133 | } | 1208 | } |
1134 | 1209 | ||
1135 | /** | 1210 | /** |
1136 | * purge_requests: Our virtual adapter just shut down. purge any sent requests | 1211 | * ibmvscsi_eh_host_reset_handler - Reset the connection to the server |
1137 | * @hostdata: the adapter | 1212 | * @cmd: struct scsi_cmnd having problems |
1138 | */ | 1213 | */ |
1139 | static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) | 1214 | static int ibmvscsi_eh_host_reset_handler(struct scsi_cmnd *cmd) |
1140 | { | 1215 | { |
1141 | struct srp_event_struct *tmp_evt, *pos; | 1216 | unsigned long wait_switch = 0; |
1142 | unsigned long flags; | 1217 | struct ibmvscsi_host_data *hostdata = |
1218 | (struct ibmvscsi_host_data *)cmd->device->host->hostdata; | ||
1143 | 1219 | ||
1144 | spin_lock_irqsave(hostdata->host->host_lock, flags); | 1220 | dev_err(hostdata->dev, "Resetting connection due to error recovery\n"); |
1145 | list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { | 1221 | |
1146 | list_del(&tmp_evt->list); | 1222 | ibmvscsi_reset_host(hostdata); |
1147 | if (tmp_evt->cmnd) { | 1223 | |
1148 | tmp_evt->cmnd->result = (error_code << 16); | 1224 | for (wait_switch = jiffies + (init_timeout * HZ); |
1149 | unmap_cmd_data(&tmp_evt->iu.srp.cmd, | 1225 | time_before(jiffies, wait_switch) && |
1150 | tmp_evt, | 1226 | atomic_read(&hostdata->request_limit) < 2;) { |
1151 | tmp_evt->hostdata->dev); | 1227 | |
1152 | if (tmp_evt->cmnd_done) | 1228 | msleep(10); |
1153 | tmp_evt->cmnd_done(tmp_evt->cmnd); | ||
1154 | } else { | ||
1155 | if (tmp_evt->done) { | ||
1156 | tmp_evt->done(tmp_evt); | ||
1157 | } | ||
1158 | } | ||
1159 | free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); | ||
1160 | } | 1229 | } |
1161 | spin_unlock_irqrestore(hostdata->host->host_lock, flags); | 1230 | |
1231 | if (atomic_read(&hostdata->request_limit) <= 0) | ||
1232 | return FAILED; | ||
1233 | |||
1234 | return SUCCESS; | ||
1162 | } | 1235 | } |
1163 | 1236 | ||
1164 | /** | 1237 | /** |
@@ -1258,6 +1331,8 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq, | |||
1258 | atomic_add(evt_struct->xfer_iu->srp.rsp.req_lim_delta, | 1331 | atomic_add(evt_struct->xfer_iu->srp.rsp.req_lim_delta, |
1259 | &hostdata->request_limit); | 1332 | &hostdata->request_limit); |
1260 | 1333 | ||
1334 | del_timer(&evt_struct->timer); | ||
1335 | |||
1261 | if (evt_struct->done) | 1336 | if (evt_struct->done) |
1262 | evt_struct->done(evt_struct); | 1337 | evt_struct->done(evt_struct); |
1263 | else | 1338 | else |
@@ -1313,7 +1388,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, | |||
1313 | } | 1388 | } |
1314 | 1389 | ||
1315 | init_completion(&evt_struct->comp); | 1390 | init_completion(&evt_struct->comp); |
1316 | rc = ibmvscsi_send_srp_event(evt_struct, hostdata); | 1391 | rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2); |
1317 | if (rc == 0) | 1392 | if (rc == 0) |
1318 | wait_for_completion(&evt_struct->comp); | 1393 | wait_for_completion(&evt_struct->comp); |
1319 | dma_unmap_single(hostdata->dev, addr, length, DMA_BIDIRECTIONAL); | 1394 | dma_unmap_single(hostdata->dev, addr, length, DMA_BIDIRECTIONAL); |
@@ -1504,6 +1579,7 @@ static struct scsi_host_template driver_template = { | |||
1504 | .queuecommand = ibmvscsi_queuecommand, | 1579 | .queuecommand = ibmvscsi_queuecommand, |
1505 | .eh_abort_handler = ibmvscsi_eh_abort_handler, | 1580 | .eh_abort_handler = ibmvscsi_eh_abort_handler, |
1506 | .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, | 1581 | .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, |
1582 | .eh_host_reset_handler = ibmvscsi_eh_host_reset_handler, | ||
1507 | .slave_configure = ibmvscsi_slave_configure, | 1583 | .slave_configure = ibmvscsi_slave_configure, |
1508 | .change_queue_depth = ibmvscsi_change_queue_depth, | 1584 | .change_queue_depth = ibmvscsi_change_queue_depth, |
1509 | .cmd_per_lun = 16, | 1585 | .cmd_per_lun = 16, |