From: viro@parcelfarce.linux.theplanet.co.uk daisy.c used to access the topology list with no locking whatsoever. Protected by spinlock, fixed numerous bugs in handling of the single-linked list (I'm not kidding - just take a look at the old parport_daisy_fini() and weep; it is supposed to go through a simple list and remove all entries that satisfy a condition). --- 25-akpm/drivers/parport/daisy.c | 120 +++++++++++++++++++++++----------------- 1 files changed, 69 insertions(+), 51 deletions(-) diff -puN drivers/parport/daisy.c~PP5-daisy-RC1 drivers/parport/daisy.c --- 25/drivers/parport/daisy.c~PP5-daisy-RC1 Wed Jan 14 13:50:50 2004 +++ 25-akpm/drivers/parport/daisy.c Wed Jan 14 13:50:50 2004 @@ -40,6 +40,7 @@ static struct daisydev { int daisy; int devnum; } *topology = NULL; +static spinlock_t topology_lock = SPIN_LOCK_UNLOCKED; static int numdevs = 0; @@ -52,22 +53,18 @@ static int assign_addrs (struct parport /* Add a device to the discovered topology. */ static void add_dev (int devnum, struct parport *port, int daisy) { - struct daisydev *newdev; + struct daisydev *newdev, **p; newdev = kmalloc (sizeof (struct daisydev), GFP_KERNEL); if (newdev) { newdev->port = port; newdev->daisy = daisy; newdev->devnum = devnum; - newdev->next = topology; - if (!topology || topology->devnum >= devnum) - topology = newdev; - else { - struct daisydev *prev = topology; - while (prev->next && prev->next->devnum < devnum) - prev = prev->next; - newdev->next = prev->next; - prev->next = newdev; - } + spin_lock(&topology_lock); + for (p = &topology; *p && (*p)->devnumnext) + ; + newdev->next = *p; + *p = newdev; + spin_unlock(&topology_lock); } } @@ -157,26 +154,25 @@ int parport_daisy_init (struct parport * /* Forget about devices on a physical port. */ void parport_daisy_fini (struct parport *port) { - struct daisydev *dev, *prev = topology; - while (prev && prev->port == port) { - topology = topology->next; - kfree (prev); - prev = topology; - } + struct daisydev **p; - while (prev) { - dev = prev->next; - if (dev && dev->port == port) { - prev->next = dev->next; - kfree (dev); + spin_lock(&topology_lock); + p = &topology; + while (*p) { + struct daisydev *dev = *p; + if (dev->port != port) { + p = &dev->next; + continue; } - prev = prev->next; + *p = dev->next; + kfree(dev); } /* Gaps in the numbering could be handled better. How should someone enumerate through all IEEE1284.3 devices in the topology?. */ if (!topology) numdevs = 0; + spin_unlock(&topology_lock); return; } @@ -205,30 +201,34 @@ struct pardevice *parport_open (int devn void (*irqf) (int, void *, struct pt_regs *), int flags, void *handle) { - struct parport *port = parport_enumerate (); + struct daisydev *p = topology; + struct parport *port; struct pardevice *dev; - int portnum; - int muxnum; - int daisynum; - - if (parport_device_coords (devnum, &portnum, &muxnum, &daisynum)) - return NULL; + int daisy; - while (port && ((port->portnum != portnum) || - (port->muxport != muxnum))) - port = port->next; + spin_lock(&topology_lock); + while (p && p->devnum != devnum) + p = p->next; - if (!port) - /* No corresponding parport. */ + if (!p) { + spin_unlock(&topology_lock); return NULL; + } + + daisy = p->daisy; + port = parport_get_port(p->port); + spin_unlock(&topology_lock); dev = parport_register_device (port, name, pf, kf, irqf, flags, handle); - if (dev) - dev->daisy = daisynum; + parport_put_port(port); + if (!dev) + return NULL; + + dev->daisy = daisy; /* Check that there really is a device to select. */ - if (daisynum >= 0) { + if (daisy >= 0) { int selected; parport_claim_or_block (dev); selected = port->daisy; @@ -271,16 +271,19 @@ void parport_close (struct pardevice *de int parport_device_num (int parport, int mux, int daisy) { - struct daisydev *dev = topology; + int res = -ENXIO; + struct daisydev *dev; + spin_lock(&topology_lock); + dev = topology; while (dev && dev->port->portnum != parport && dev->port->muxport != mux && dev->daisy != daisy) dev = dev->next; + if (dev) + res = dev->devnum; + spin_unlock(&topology_lock); - if (!dev) - return -ENXIO; - - return dev->devnum; + return res; } /** @@ -310,17 +313,23 @@ int parport_device_num (int parport, int int parport_device_coords (int devnum, int *parport, int *mux, int *daisy) { - struct daisydev *dev = topology; + struct daisydev *dev; + spin_lock(&topology_lock); + + dev = topology; while (dev && dev->devnum != devnum) dev = dev->next; - if (!dev) + if (!dev) { + spin_unlock(&topology_lock); return -ENXIO; + } if (parport) *parport = dev->port->portnum; if (mux) *mux = dev->port->muxport; if (daisy) *daisy = dev->daisy; + spin_unlock(&topology_lock); return 0; } @@ -557,9 +566,13 @@ static int assign_addrs (struct parport int parport_find_device (const char *mfg, const char *mdl, int from) { - struct daisydev *d = topology; /* sorted by devnum */ + struct daisydev *d; + int res = -1; /* Find where to start. */ + + spin_lock(&topology_lock); + d = topology; /* sorted by devnum */ while (d && d->devnum <= from) d = d->next; @@ -575,9 +588,10 @@ int parport_find_device (const char *mfg } if (d) - return d->devnum; + res = d->devnum; - return -1; + spin_unlock(&topology_lock); + return res; } /** @@ -601,8 +615,11 @@ int parport_find_device (const char *mfg int parport_find_class (parport_device_class cls, int from) { - struct daisydev *d = topology; /* sorted by devnum */ + struct daisydev *d; + int res = -1; + spin_lock(&topology_lock); + d = topology; /* sorted by devnum */ /* Find where to start. */ while (d && d->devnum <= from) d = d->next; @@ -612,7 +629,8 @@ int parport_find_class (parport_device_c d = d->next; if (d) - return d->devnum; + res = d->devnum; - return -1; + spin_unlock(&topology_lock); + return res; } _