From: viro@parcelfarce.linux.theplanet.co.uk

* fixed missing wakeups in imm_pb_claim()/imm_wakeup() - if the former had
  been called just as current holder of port was giving it up, we could set
  "I'm waiting" flag too late.  Cleaned up the timeout logics.



---

 25-akpm/drivers/scsi/imm.c |  100 ++++++++++++++++++++++++++++-----------------
 1 files changed, 64 insertions(+), 36 deletions(-)

diff -puN drivers/scsi/imm.c~IMM5-imm_wakeup-RC1 drivers/scsi/imm.c
--- 25/drivers/scsi/imm.c~IMM5-imm_wakeup-RC1	Wed Jan 14 13:30:35 2004
+++ 25-akpm/drivers/scsi/imm.c	Wed Jan 14 13:30:35 2004
@@ -41,7 +41,8 @@ typedef struct {
 	unsigned failed:1;	/* Failure flag                 */
 	unsigned dp:1;		/* Data phase present           */
 	unsigned rd:1;		/* Read data in data phase      */
-	unsigned p_busy:1;	/* Parport sharing busy flag    */
+	unsigned wanted:1;	/* Parport sharing busy flag    */
+	wait_queue_head_t *waiting;
 } imm_struct;
 
 static void imm_reset_pulse(unsigned int base);
@@ -62,33 +63,55 @@ static inline imm_struct *imm_dev(struct
 	return &imm_hosts[host->unique_id];
 }
 
+static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED;
+
+static void got_it(imm_struct *dev)
+{
+	dev->base = dev->dev->port->base;
+	if (dev->cur_cmd)
+		dev->cur_cmd->SCp.phase = 1;
+	else
+		wake_up(dev->waiting);
+}
+
 static void imm_wakeup(void *ref)
 {
 	imm_struct *dev = (imm_struct *) ref;
+	unsigned long flags;
 
-	if (!dev->p_busy)
-		return;
-
-	if (parport_claim(dev->dev)) {
-		printk("imm: bug in imm_wakeup\n");
-		return;
+	spin_lock_irqsave(&arbitration_lock, flags);
+	if (dev->wanted) {
+		parport_claim(dev->dev);
+		got_it(dev);
+		dev->wanted = 0;
 	}
-	dev->p_busy = 0;
-	dev->base = dev->dev->port->base;
-	if (dev->cur_cmd)
-		dev->cur_cmd->SCp.phase++;
-	return;
+	spin_unlock_irqrestore(&arbitration_lock, flags);
 }
 
 static int imm_pb_claim(imm_struct *dev)
 {
-	if (parport_claim(dev->dev)) {
-		dev->p_busy = 1;
-		return 1;
-	}
-	if (dev->cur_cmd)
-		dev->cur_cmd->SCp.phase++;
-	return 0;
+	unsigned long flags;
+	int res = 1;
+	spin_lock_irqsave(&arbitration_lock, flags);
+	if (parport_claim(dev->dev) == 0) {
+		got_it(dev);
+		res = 0;
+	}
+	dev->wanted = res;
+	spin_unlock_irqrestore(&arbitration_lock, flags);
+	return res;
+}
+
+static void imm_pb_dismiss(imm_struct *dev)
+{
+	unsigned long flags;
+	int wanted;
+	spin_lock_irqsave(&arbitration_lock, flags);
+	wanted = dev->wanted;
+	dev->wanted = 0;
+	spin_unlock_irqrestore(&arbitration_lock, flags);
+	if (!wanted)
+		parport_release(dev->dev);
 }
 
 static inline void imm_pb_release(imm_struct *dev)
@@ -105,10 +128,13 @@ static Scsi_Host_Template imm_template;
 static int imm_probe(imm_struct *dev, struct parport *pb)
 {
 	struct Scsi_Host *host;
+	DECLARE_WAIT_QUEUE_HEAD(waiting);
+	DEFINE_WAIT(wait);
 	int ports;
 	int modes, ppb;
 	int err;
 
+	init_waitqueue_head(&waiting);
 	dev->dev = parport_register_device(pb, "imm", NULL, imm_wakeup,
 						NULL, 0, dev);
 
@@ -119,19 +145,21 @@ static int imm_probe(imm_struct *dev, st
 	 * registers. [ CTR and ECP ]
 	 */
 	err = -EBUSY;
-	if (imm_pb_claim(dev)) {
-		unsigned long now = jiffies;
-		while (dev->p_busy) {
-			schedule();	/* We are safe to schedule here */
-			if (time_after(jiffies, now + 3 * HZ)) {
-				printk(KERN_ERR
-				       "imm%d: failed to claim parport because a "
-				       "pardevice is owning the port for too longtime!\n",
-				       dev - imm_hosts);
-				goto out;
-			}
-		}
+	dev->waiting = &waiting;
+	prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE);
+	if (imm_pb_claim(dev))
+		schedule_timeout(3 * HZ);
+	if (dev->wanted) {
+		printk(KERN_ERR "imm%d: failed to claim parport because "
+			"a pardevice is owning the port for too long "
+			"time!\n", dev - imm_hosts);
+		imm_pb_dismiss(dev);
+		dev->waiting = NULL;
+		finish_wait(&waiting, &wait);
+		goto out;
 	}
+	dev->waiting = NULL;
+	finish_wait(&waiting, &wait);
 	ppb = dev->base = dev->dev->port->base;
 	dev->base_hi = dev->dev->port->base_hi;
 	w_ctr(ppb, 0x0c);
@@ -867,8 +895,8 @@ static void imm_interrupt(void *data)
 
 	if (cmd->SCp.phase > 1)
 		imm_disconnect(dev);
-	if (cmd->SCp.phase > 0)
-		imm_pb_release(dev);
+
+	imm_pb_dismiss(dev);
 
 	spin_lock_irqsave(host->host_lock, flags);
 	dev->cur_cmd = 0;
@@ -891,7 +919,7 @@ static int imm_engine(imm_struct *dev, S
 
 	switch (cmd->SCp.phase) {
 	case 0:		/* Phase 0 - Waiting for parport */
-		if ((jiffies - dev->jstart) > HZ) {
+		if (time_after(jiffies, dev->jstart + HZ)) {
 			/*
 			 * We waited more than a second
 			 * for parport to call us
@@ -1033,11 +1061,11 @@ static int imm_queuecommand(Scsi_Cmnd *c
 	cmd->result = DID_ERROR << 16;	/* default return code */
 	cmd->SCp.phase = 0;	/* bus free */
 
-	imm_pb_claim(dev);
-
 	INIT_WORK(&dev->imm_tq, imm_interrupt, dev);
 	schedule_work(&dev->imm_tq);
 
+	imm_pb_claim(dev);
+
 	return 0;
 }
 

_