summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2016-01-03 16:05:22 +1100
committerMartin K. Petersen <martin.petersen@oracle.com>2016-01-06 21:42:56 -0500
commit2f854b82b001b5d05b4e1c81acb947f7bd9cee9f (patch)
treee0efdc6bc54882856e8e5631336cdb2b96676570
parent0ad0eff98fec3c940ec199047ff580abaaa5bb1a (diff)
downloadlinux-stable-2f854b82b001b5d05b4e1c81acb947f7bd9cee9f.tar.gz
linux-stable-2f854b82b001b5d05b4e1c81acb947f7bd9cee9f.tar.bz2
linux-stable-2f854b82b001b5d05b4e1c81acb947f7bd9cee9f.zip
ncr5380: Sleep when polling, if possible
When in process context, sleep during polling if doing so won't add significant latency. In interrupt context or if the lock is held, poll briefly then give up. Keep both core drivers in sync. Calibrate busy-wait iterations to allow for variation in chip register access times between different 5380 hardware implementations. Signed-off-by: Finn Thain <fthain@telegraphics.com.au> Reviewed-by: Hannes Reinecke <hare@suse.com> Tested-by: Ondrej Zary <linux@rainbow-software.org> Tested-by: Michael Schmitz <schmitzmic@gmail.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/scsi/NCR5380.c84
-rw-r--r--drivers/scsi/NCR5380.h1
-rw-r--r--drivers/scsi/atari_NCR5380.c62
3 files changed, 94 insertions, 53 deletions
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index a66fffc83475..a1c7901791f8 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -293,44 +293,48 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd)
}
/**
- * NCR5380_poll_politely - wait for NCR5380 status bits
- * @instance: controller to poll
- * @reg: 5380 register to poll
- * @bit: Bitmask to check
- * @val: Value required to exit
- *
- * Polls the NCR5380 in a reasonably efficient manner waiting for
- * an event to occur, after a short quick poll we begin giving the
- * CPU back in non IRQ contexts
- *
- * Returns the value of the register or a negative error code.
+ * NCR5380_poll_politely - wait for chip register value
+ * @instance: controller to poll
+ * @reg: 5380 register to poll
+ * @bit: Bitmask to check
+ * @val: Value required to exit
+ * @wait: Time-out in jiffies
+ *
+ * Polls the chip in a reasonably efficient manner waiting for an
+ * event to occur. After a short quick poll we begin to yield the CPU
+ * (if possible). In irq contexts the time-out is arbitrarily limited.
+ * Callers may hold locks as long as they are held in irq mode.
+ *
+ * Returns 0 if event occurred otherwise -ETIMEDOUT.
*/
-
-static int NCR5380_poll_politely(struct Scsi_Host *instance, int reg, int bit, int val, int t)
+
+static int NCR5380_poll_politely(struct Scsi_Host *instance,
+ int reg, int bit, int val, int wait)
{
- int n = 500; /* At about 8uS a cycle for the cpu access */
- unsigned long end = jiffies + t;
- int r;
-
- while( n-- > 0)
- {
- r = NCR5380_read(reg);
- if((r & bit) == val)
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long deadline = jiffies + wait;
+ unsigned long n;
+
+ /* Busy-wait for up to 10 ms */
+ n = min(10000U, jiffies_to_usecs(wait));
+ n *= hostdata->accesses_per_ms;
+ n /= 1000;
+ do {
+ if ((NCR5380_read(reg) & bit) == val)
return 0;
cpu_relax();
- }
-
- /* t time yet ? */
- while(time_before(jiffies, end))
- {
- r = NCR5380_read(reg);
- if((r & bit) == val)
+ } while (n--);
+
+ if (irqs_disabled() || in_interrupt())
+ return -ETIMEDOUT;
+
+ /* Repeatedly sleep for 1 ms until deadline */
+ while (time_is_after_jiffies(deadline)) {
+ schedule_timeout_uninterruptible(1);
+ if ((NCR5380_read(reg) & bit) == val)
return 0;
- if(!in_interrupt())
- cond_resched();
- else
- cpu_relax();
}
+
return -ETIMEDOUT;
}
@@ -773,6 +777,7 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags)
{
int i;
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ unsigned long deadline;
if(in_interrupt())
printk(KERN_ERR "NCR5380_init called with interrupts off!\n");
@@ -812,6 +817,21 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags)
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(TARGET_COMMAND_REG, 0);
NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /* Calibrate register polling loop */
+ i = 0;
+ deadline = jiffies + 1;
+ do {
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ deadline += msecs_to_jiffies(256);
+ do {
+ NCR5380_read(STATUS_REG);
+ ++i;
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ hostdata->accesses_per_ms = i / 256;
+
return 0;
}
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
index f9c861d083d0..412a344ac676 100644
--- a/drivers/scsi/NCR5380.h
+++ b/drivers/scsi/NCR5380.h
@@ -285,6 +285,7 @@ struct NCR5380_hostdata {
unsigned spin_max_w;
#endif
struct workqueue_struct *work_q;
+ unsigned long accesses_per_ms; /* chip register accesses per ms */
};
#ifdef __KERNEL__
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c
index 0b0a225167bc..8b4e321b8399 100644
--- a/drivers/scsi/atari_NCR5380.c
+++ b/drivers/scsi/atari_NCR5380.c
@@ -479,43 +479,48 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd)
}
/**
- * NCR5380_poll_politely - wait for NCR5380 status bits
+ * NCR5380_poll_politely - wait for chip register value
* @instance: controller to poll
* @reg: 5380 register to poll
* @bit: Bitmask to check
* @val: Value required to exit
+ * @wait: Time-out in jiffies
*
- * Polls the NCR5380 in a reasonably efficient manner waiting for
- * an event to occur, after a short quick poll we begin giving the
- * CPU back in non IRQ contexts
+ * Polls the chip in a reasonably efficient manner waiting for an
+ * event to occur. After a short quick poll we begin to yield the CPU
+ * (if possible). In irq contexts the time-out is arbitrarily limited.
+ * Callers may hold locks as long as they are held in irq mode.
*
- * Returns the value of the register or a negative error code.
+ * Returns 0 if event occurred otherwise -ETIMEDOUT.
*/
static int NCR5380_poll_politely(struct Scsi_Host *instance,
- int reg, int bit, int val, int t)
+ int reg, int bit, int val, int wait)
{
- int n = 500;
- unsigned long end = jiffies + t;
- int r;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long deadline = jiffies + wait;
+ unsigned long n;
- while (n-- > 0) {
- r = NCR5380_read(reg);
- if ((r & bit) == val)
+ /* Busy-wait for up to 10 ms */
+ n = min(10000U, jiffies_to_usecs(wait));
+ n *= hostdata->accesses_per_ms;
+ n /= 1000;
+ do {
+ if ((NCR5380_read(reg) & bit) == val)
return 0;
cpu_relax();
- }
+ } while (n--);
- /* t time yet ? */
- while (time_before(jiffies, end)) {
- r = NCR5380_read(reg);
- if ((r & bit) == val)
+ if (irqs_disabled() || in_interrupt())
+ return -ETIMEDOUT;
+
+ /* Repeatedly sleep for 1 ms until deadline */
+ while (time_is_after_jiffies(deadline)) {
+ schedule_timeout_uninterruptible(1);
+ if ((NCR5380_read(reg) & bit) == val)
return 0;
- if (!in_interrupt())
- cond_resched();
- else
- cpu_relax();
}
+
return -ETIMEDOUT;
}
@@ -811,6 +816,7 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
{
int i;
SETUP_HOSTDATA(instance);
+ unsigned long deadline;
hostdata->host = instance;
hostdata->id_mask = 1 << instance->this_id;
@@ -845,6 +851,20 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
NCR5380_write(TARGET_COMMAND_REG, 0);
NCR5380_write(SELECT_ENABLE_REG, 0);
+ /* Calibrate register polling loop */
+ i = 0;
+ deadline = jiffies + 1;
+ do {
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ deadline += msecs_to_jiffies(256);
+ do {
+ NCR5380_read(STATUS_REG);
+ ++i;
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ hostdata->accesses_per_ms = i / 256;
+
return 0;
}