]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ieee1394/sbp2.c
PCI: Let PCI_MULTITHREAD_PROBE depend on BROKEN
[linux-2.6-omap-h63xx.git] / drivers / ieee1394 / sbp2.c
index 668b4512deffdd2fa05a63d93be119d6754a3aab..6986ac188281745eda188c9b1c26110a9fe7f62f 100644 (file)
  *       but the code needs additional debugging.
  */
 
+#include <linux/blkdev.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
-#include <linux/string.h>
-#include <linux/stringify.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
 #include <linux/wait.h>
 
-#include <asm/current.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
 #include <asm/byteorder.h>
-#include <asm/system.h>
+#include <asm/errno.h>
+#include <asm/param.h>
 #include <asm/scatterlist.h>
+#include <asm/system.h>
+#include <asm/types.h>
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+#include <asm/io.h> /* for bus_to_virt */
+#endif
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_host.h>
 
 #include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
 #include "ieee1394.h"
-#include "ieee1394_types.h"
 #include "ieee1394_core.h"
-#include "nodemgr.h"
-#include "hosts.h"
-#include "highlevel.h"
+#include "ieee1394_hotplug.h"
 #include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
 #include "sbp2.h"
 
 /*
@@ -465,6 +471,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 (unlikely(atomic_read(&scsi_id->state) == SBP2LU_STATE_IN_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 (likely(atomic_read(&scsi_id->state) != SBP2LU_STATE_IN_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 +756,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->state, SBP2LU_STATE_IN_SHUTDOWN);
                scsi_unblock_requests(scsi_id->scsi_host);
        }
        sdev = scsi_id->sdev;
@@ -763,10 +808,12 @@ static int sbp2_update(struct unit_directory *ud)
         */
        sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
 
-       /* Make sure we unblock requests (since this is likely after a bus
-        * reset). */
-       scsi_unblock_requests(scsi_id->scsi_host);
-
+       /* Accept new commands unless there was another bus reset in the
+        * meantime. */
+       if (hpsb_node_entry_valid(scsi_id->ne)) {
+               atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING);
+               scsi_unblock_requests(scsi_id->scsi_host);
+       }
        return 0;
 }
 
@@ -795,6 +842,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->state, SBP2LU_STATE_RUNNING);
+       INIT_WORK(&scsi_id->protocol_work, NULL, NULL);
 
        ud->device.driver_data = scsi_id;
 
@@ -877,11 +926,14 @@ static void sbp2_host_reset(struct hpsb_host *host)
        struct scsi_id_instance_data *scsi_id;
 
        hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
-
-       if (hi) {
-               list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+       if (!hi)
+               return;
+       list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+               if (likely(atomic_read(&scsi_id->state) !=
+                          SBP2LU_STATE_IN_SHUTDOWN)) {
+                       atomic_set(&scsi_id->state, SBP2LU_STATE_IN_RESET);
                        scsi_block_requests(scsi_id->scsi_host);
-       }
+               }
 }
 
 /*
@@ -966,8 +1018,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
         * connected to the sbp2 device being removed. That host would
         * have a certain amount of time to relogin before the sbp2 device
         * allows someone else to login instead. One second makes sense. */
-       msleep_interruptible(1000);
-       if (signal_pending(current)) {
+       if (msleep_interruptible(1000)) {
                sbp2_remove_device(scsi_id);
                return -EINTR;
        }
@@ -1032,7 +1083,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 +1712,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 +2037,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);
+       }
 }
 
 /*
@@ -2151,28 +2219,34 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
                spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 
                if (SCpnt) {
-                       if (STATUS_TEST_RS(sb->ORB_offset_hi_misc))
+                       u32 h = sb->ORB_offset_hi_misc;
+                       u32 r = STATUS_GET_RESP(h);
+
+                       if (r != RESP_STATUS_REQUEST_COMPLETE) {
+                               SBP2_WARN("resp 0x%x, sbp_status 0x%x",
+                                         r, STATUS_GET_SBP_STATUS(h));
                                scsi_status =
+                                       r == RESP_STATUS_TRANSPORT_FAILURE ?
+                                       SBP2_SCSI_STATUS_BUSY :
                                        SBP2_SCSI_STATUS_COMMAND_TERMINATED;
+                       }
                        /*
                         * See if the target stored any scsi status information.
                         */
-                       if (STATUS_GET_LEN(sb->ORB_offset_hi_misc) > 1) {
+                       if (STATUS_GET_LEN(h) > 1) {
                                SBP2_DEBUG("CHECK CONDITION");
                                scsi_status = sbp2_status_to_sense_data(
                                        (unchar *)sb, SCpnt->sense_buffer);
                        }
-
                        /*
                         * Check to see if the dead bit is set. If so, we'll
                         * have to initiate a fetch agent reset.
                         */
-                       if (STATUS_TEST_D(sb->ORB_offset_hi_misc)) {
+                       if (STATUS_TEST_DEAD(h)) {
                                SBP2_DEBUG("Dead bit set - "
                                           "initiating fetch agent reset");
                                 sbp2_agent_reset(scsi_id, 0);
                        }
-
                        SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
                }
 
@@ -2441,6 +2515,7 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
                (struct scsi_id_instance_data *)sdev->host->hostdata[0];
 
        scsi_id->sdev = sdev;
+       sdev->allow_restart = 1;
 
        if (scsi_id->workarounds & SBP2_WORKAROUND_INQUIRY_36)
                sdev->inquiry_len = 36;
@@ -2454,16 +2529,12 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
 
        blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
        sdev->use_10_for_rw = 1;
-       sdev->use_10_for_ms = 1;
 
        if (sdev->type == TYPE_DISK &&
            scsi_id->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
                sdev->skip_ms_page_8 = 1;
        if (scsi_id->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
                sdev->fix_capacity = 1;
-       if (scsi_id->ne->guid_vendor_id == 0x0010b9 && /* Maxtor's OUI */
-           (sdev->type == TYPE_DISK || sdev->type == TYPE_RBC))
-               sdev->allow_restart = 1;
        return 0;
 }
 
@@ -2517,7 +2588,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
                /*
                 * Initiate a fetch agent reset.
                 */
-               sbp2_agent_reset(scsi_id, 0);
+               sbp2_agent_reset(scsi_id, 1);
                sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
        }
 
@@ -2536,7 +2607,7 @@ static int sbp2scsi_reset(struct scsi_cmnd *SCpnt)
 
        if (sbp2util_node_is_available(scsi_id)) {
                SBP2_ERR("Generating sbp2 fetch agent reset");
-               sbp2_agent_reset(scsi_id, 0);
+               sbp2_agent_reset(scsi_id, 1);
        }
 
        return SUCCESS;