diff options
Diffstat (limited to 'drivers/ieee1394/sbp2.c')
-rw-r--r-- | drivers/ieee1394/sbp2.c | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 668b4512deff..c4d30a7b8b1e 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -465,6 +465,44 @@ static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, return 0; } +static void sbp2util_notify_fetch_agent(struct scsi_id_instance_data *scsi_id, + u64 offset, quadlet_t *data, size_t len) +{ + /* + * There is a small window after a bus reset within which the node + * entry's generation is current but the reconnect wasn't completed. + */ + if (atomic_read(&scsi_id->unfinished_reset)) + return; + + if (hpsb_node_write(scsi_id->ne, + scsi_id->sbp2_command_block_agent_addr + offset, + data, len)) + SBP2_ERR("sbp2util_notify_fetch_agent failed."); + /* + * Now accept new SCSI commands, unless a bus reset happended during + * hpsb_node_write. + */ + if (!atomic_read(&scsi_id->unfinished_reset)) + scsi_unblock_requests(scsi_id->scsi_host); +} + +static void sbp2util_write_orb_pointer(void *p) +{ + quadlet_t data[2]; + + data[0] = ORB_SET_NODE_ID( + ((struct scsi_id_instance_data *)p)->hi->host->node_id); + data[1] = ((struct scsi_id_instance_data *)p)->last_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + sbp2util_notify_fetch_agent(p, SBP2_ORB_POINTER_OFFSET, data, 8); +} + +static void sbp2util_write_doorbell(void *p) +{ + sbp2util_notify_fetch_agent(p, SBP2_DOORBELL_OFFSET, NULL, 4); +} + /* * This function is called to create a pool of command orbs used for * command processing. It is called when a new sbp2 device is detected. @@ -712,6 +750,7 @@ static int sbp2_remove(struct device *dev) sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT); /* scsi_remove_device() will trigger shutdown functions of SCSI * highlevel drivers which would deadlock if blocked. */ + atomic_set(&scsi_id->unfinished_reset, 0); scsi_unblock_requests(scsi_id->scsi_host); } sdev = scsi_id->sdev; @@ -765,6 +804,7 @@ static int sbp2_update(struct unit_directory *ud) /* Make sure we unblock requests (since this is likely after a bus * reset). */ + atomic_set(&scsi_id->unfinished_reset, 0); scsi_unblock_requests(scsi_id->scsi_host); return 0; @@ -795,6 +835,8 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); INIT_LIST_HEAD(&scsi_id->scsi_list); spin_lock_init(&scsi_id->sbp2_command_orb_lock); + atomic_set(&scsi_id->unfinished_reset, 0); + INIT_WORK(&scsi_id->protocol_work, NULL, NULL); ud->device.driver_data = scsi_id; @@ -879,8 +921,10 @@ static void sbp2_host_reset(struct hpsb_host *host) hi = hpsb_get_hostinfo(&sbp2_highlevel, host); if (hi) { - list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) + list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) { + atomic_set(&scsi_id->unfinished_reset, 1); scsi_block_requests(scsi_id->scsi_host); + } } } @@ -1032,7 +1076,7 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) scsi_remove_host(scsi_id->scsi_host); scsi_host_put(scsi_id->scsi_host); } - + flush_scheduled_work(); sbp2util_remove_command_orb_pool(scsi_id); list_del(&scsi_id->scsi_list); @@ -1661,6 +1705,10 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) SBP2_DEBUG_ENTER(); + cancel_delayed_work(&scsi_id->protocol_work); + if (wait) + flush_scheduled_work(); + data = ntohl(SBP2_AGENT_RESET_DATA); addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; @@ -1982,9 +2030,22 @@ static void sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, SBP2_ORB_DEBUG("write to %s register, command orb %p", last_orb ? "DOORBELL" : "ORB_POINTER", command_orb); - if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) - SBP2_ERR("sbp2util_node_write_no_wait failed.\n"); - /* We rely on SCSI EH to deal with _node_write_ failures. */ + if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) { + /* + * sbp2util_node_write_no_wait failed. We certainly ran out + * of transaction labels, perhaps just because there were no + * context switches which gave khpsbpkt a chance to collect + * free tlabels. Try again in non-atomic context. If necessary, + * the workqueue job will sleep to guaranteedly get a tlabel. + * We do not accept new commands until the job is over. + */ + scsi_block_requests(scsi_id->scsi_host); + PREPARE_WORK(&scsi_id->protocol_work, + last_orb ? sbp2util_write_doorbell: + sbp2util_write_orb_pointer, + scsi_id); + schedule_work(&scsi_id->protocol_work); + } } /* |