Commit a2cafb3c authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] parport fixes (6/6)

	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).
parent 9d00a8c0
...@@ -40,6 +40,7 @@ static struct daisydev { ...@@ -40,6 +40,7 @@ static struct daisydev {
int daisy; int daisy;
int devnum; int devnum;
} *topology = NULL; } *topology = NULL;
static spinlock_t topology_lock = SPIN_LOCK_UNLOCKED;
static int numdevs = 0; static int numdevs = 0;
...@@ -52,22 +53,18 @@ static int assign_addrs (struct parport *port); ...@@ -52,22 +53,18 @@ static int assign_addrs (struct parport *port);
/* Add a device to the discovered topology. */ /* Add a device to the discovered topology. */
static void add_dev (int devnum, struct parport *port, int daisy) 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); newdev = kmalloc (sizeof (struct daisydev), GFP_KERNEL);
if (newdev) { if (newdev) {
newdev->port = port; newdev->port = port;
newdev->daisy = daisy; newdev->daisy = daisy;
newdev->devnum = devnum; newdev->devnum = devnum;
newdev->next = topology; spin_lock(&topology_lock);
if (!topology || topology->devnum >= devnum) for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next)
topology = newdev; ;
else { newdev->next = *p;
struct daisydev *prev = topology; *p = newdev;
while (prev->next && prev->next->devnum < devnum) spin_unlock(&topology_lock);
prev = prev->next;
newdev->next = prev->next;
prev->next = newdev;
}
} }
} }
...@@ -157,26 +154,25 @@ int parport_daisy_init (struct parport *port) ...@@ -157,26 +154,25 @@ int parport_daisy_init (struct parport *port)
/* Forget about devices on a physical port. */ /* Forget about devices on a physical port. */
void parport_daisy_fini (struct parport *port) void parport_daisy_fini (struct parport *port)
{ {
struct daisydev *dev, *prev = topology; struct daisydev **p;
while (prev && prev->port == port) {
topology = topology->next; spin_lock(&topology_lock);
kfree (prev); p = &topology;
prev = topology; while (*p) {
} struct daisydev *dev = *p;
if (dev->port != port) {
while (prev) { p = &dev->next;
dev = prev->next; continue;
if (dev && dev->port == port) {
prev->next = dev->next;
kfree (dev);
} }
prev = prev->next; *p = dev->next;
kfree(dev);
} }
/* Gaps in the numbering could be handled better. How should /* Gaps in the numbering could be handled better. How should
someone enumerate through all IEEE1284.3 devices in the someone enumerate through all IEEE1284.3 devices in the
topology?. */ topology?. */
if (!topology) numdevs = 0; if (!topology) numdevs = 0;
spin_unlock(&topology_lock);
return; return;
} }
...@@ -205,30 +201,34 @@ struct pardevice *parport_open (int devnum, const char *name, ...@@ -205,30 +201,34 @@ struct pardevice *parport_open (int devnum, const char *name,
void (*irqf) (int, void *, struct pt_regs *), void (*irqf) (int, void *, struct pt_regs *),
int flags, void *handle) int flags, void *handle)
{ {
struct parport *port = parport_enumerate (); struct daisydev *p = topology;
struct parport *port;
struct pardevice *dev; struct pardevice *dev;
int portnum; int daisy;
int muxnum;
int daisynum;
if (parport_device_coords (devnum, &portnum, &muxnum, &daisynum))
return NULL;
while (port && ((port->portnum != portnum) || spin_lock(&topology_lock);
(port->muxport != muxnum))) while (p && p->devnum != devnum)
port = port->next; p = p->next;
if (!port) if (!p) {
/* No corresponding parport. */ spin_unlock(&topology_lock);
return NULL; return NULL;
}
daisy = p->daisy;
port = parport_get_port(p->port);
spin_unlock(&topology_lock);
dev = parport_register_device (port, name, pf, kf, dev = parport_register_device (port, name, pf, kf,
irqf, flags, handle); irqf, flags, handle);
if (dev) parport_put_port(port);
dev->daisy = daisynum; if (!dev)
return NULL;
dev->daisy = daisy;
/* Check that there really is a device to select. */ /* Check that there really is a device to select. */
if (daisynum >= 0) { if (daisy >= 0) {
int selected; int selected;
parport_claim_or_block (dev); parport_claim_or_block (dev);
selected = port->daisy; selected = port->daisy;
...@@ -271,16 +271,19 @@ void parport_close (struct pardevice *dev) ...@@ -271,16 +271,19 @@ void parport_close (struct pardevice *dev)
int parport_device_num (int parport, int mux, int daisy) 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 && while (dev && dev->port->portnum != parport &&
dev->port->muxport != mux && dev->daisy != daisy) dev->port->muxport != mux && dev->daisy != daisy)
dev = dev->next; dev = dev->next;
if (dev)
res = dev->devnum;
spin_unlock(&topology_lock);
if (!dev) return res;
return -ENXIO;
return dev->devnum;
} }
/** /**
...@@ -310,17 +313,23 @@ int parport_device_num (int parport, int mux, int daisy) ...@@ -310,17 +313,23 @@ int parport_device_num (int parport, int mux, int daisy)
int parport_device_coords (int devnum, int *parport, int *mux, int *daisy) 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) while (dev && dev->devnum != devnum)
dev = dev->next; dev = dev->next;
if (!dev) if (!dev) {
spin_unlock(&topology_lock);
return -ENXIO; return -ENXIO;
}
if (parport) *parport = dev->port->portnum; if (parport) *parport = dev->port->portnum;
if (mux) *mux = dev->port->muxport; if (mux) *mux = dev->port->muxport;
if (daisy) *daisy = dev->daisy; if (daisy) *daisy = dev->daisy;
spin_unlock(&topology_lock);
return 0; return 0;
} }
...@@ -557,9 +566,13 @@ static int assign_addrs (struct parport *port) ...@@ -557,9 +566,13 @@ static int assign_addrs (struct parport *port)
int parport_find_device (const char *mfg, const char *mdl, int from) 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. */ /* Find where to start. */
spin_lock(&topology_lock);
d = topology; /* sorted by devnum */
while (d && d->devnum <= from) while (d && d->devnum <= from)
d = d->next; d = d->next;
...@@ -575,9 +588,10 @@ int parport_find_device (const char *mfg, const char *mdl, int from) ...@@ -575,9 +588,10 @@ int parport_find_device (const char *mfg, const char *mdl, int from)
} }
if (d) 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, const char *mdl, int from) ...@@ -601,8 +615,11 @@ int parport_find_device (const char *mfg, const char *mdl, int from)
int parport_find_class (parport_device_class cls, int from) 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. */ /* Find where to start. */
while (d && d->devnum <= from) while (d && d->devnum <= from)
d = d->next; d = d->next;
...@@ -612,7 +629,8 @@ int parport_find_class (parport_device_class cls, int from) ...@@ -612,7 +629,8 @@ int parport_find_class (parport_device_class cls, int from)
d = d->next; d = d->next;
if (d) if (d)
return d->devnum; res = d->devnum;
return -1; spin_unlock(&topology_lock);
return res;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment