From: viro@parcelfarce.linux.theplanet.co.uk * fixed missing wakeups in ppa_pb_claim()/ppa_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/ppa.c | 99 +++++++++++++++++++++++++++++---------------- 1 files changed, 64 insertions(+), 35 deletions(-) diff -puN drivers/scsi/ppa.c~PPA5-ppa_wakeup-RC1 drivers/scsi/ppa.c --- 25/drivers/scsi/ppa.c~PPA5-ppa_wakeup-RC1 Wed Jan 14 13:35:49 2004 +++ 25-akpm/drivers/scsi/ppa.c Wed Jan 14 13:35:49 2004 @@ -31,7 +31,8 @@ typedef struct { unsigned long jstart; /* Jiffies at start */ unsigned long recon_tmo; /* How many usecs to wait for reconnection (6th bit) */ unsigned int failed:1; /* Failure flag */ - unsigned int p_busy:1; /* Parport sharing busy flag */ + unsigned wanted:1; /* Parport sharing busy flag */ + wait_queue_head_t *waiting; } ppa_struct; #include "ppa.h" @@ -44,33 +45,56 @@ static inline ppa_struct *ppa_dev(struct return &ppa_hosts[host->unique_id]; } +static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED; + +static void got_it(ppa_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 ppa_wakeup(void *ref) { ppa_struct *dev = (ppa_struct *) ref; + unsigned long flags; - if (!dev->p_busy) - return; - - if (parport_claim(dev->dev)) { - printk("ppa: bug in ppa_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++; + spin_unlock_irqrestore(&arbitration_lock, flags); return; } static int ppa_pb_claim(ppa_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 ppa_pb_dismiss(ppa_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 ppa_pb_release(ppa_struct *dev) @@ -90,6 +114,8 @@ static Scsi_Host_Template ppa_template; static int ppa_probe(ppa_struct *dev, struct parport *pb) { struct Scsi_Host *host; + DECLARE_WAIT_QUEUE_HEAD(waiting); + DEFINE_WAIT(wait); int ports; int err; int modes, ppb, ppb_hi; @@ -97,6 +123,7 @@ static int ppa_probe(ppa_struct *dev, st dev->base = -1; dev->mode = PPA_AUTODETECT; dev->recon_tmo = PPA_RECON_TMO; + init_waitqueue_head(&waiting); dev->dev = parport_register_device(pb, "ppa", NULL, ppa_wakeup, NULL, 0, dev); @@ -107,19 +134,21 @@ static int ppa_probe(ppa_struct *dev, st * registers. [ CTR and ECP ] */ err = -EBUSY; - if (ppa_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 - "ppa%d: failed to claim parport because a " - "pardevice is owning the port for too longtime!\n", - dev - ppa_hosts); - goto out; - } - } + dev->waiting = &waiting; + prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE); + if (ppa_pb_claim(dev)) + schedule_timeout(3 * HZ); + if (dev->wanted) { + printk(KERN_ERR "ppa%d: failed to claim parport because " + "a pardevice is owning the port for too long " + "time!\n", dev - ppa_hosts); + ppa_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; ppb_hi = dev->dev->port->base_hi; w_ctr(ppb, 0x0c); @@ -759,8 +788,8 @@ static void ppa_interrupt(void *data) if (cmd->SCp.phase > 1) ppa_disconnect(dev); - if (cmd->SCp.phase > 0) - ppa_pb_release(dev); + + ppa_pb_dismiss(dev); dev->cur_cmd = 0; @@ -784,7 +813,7 @@ static int ppa_engine(ppa_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 @@ -905,11 +934,11 @@ static int ppa_queuecommand(Scsi_Cmnd *c cmd->result = DID_ERROR << 16; /* default return code */ cmd->SCp.phase = 0; /* bus free */ - ppa_pb_claim(dev); - dev->ppa_tq.data = dev; schedule_work(&dev->ppa_tq); + ppa_pb_claim(dev); + return 0; } _