[PATCH] pcmcia: make config_t independent, add reference counting

Handle config_t structs independent of struct pcmcia_socket, and add
reference counting for them.

Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
This commit is contained in:
Dominik Brodowski 2006-01-10 20:50:39 +01:00
parent 855cdf134d
commit 360b65b95b
4 changed files with 45 additions and 30 deletions

View File

@ -406,8 +406,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
cb_free(s); cb_free(s);
#endif #endif
s->functions = 0; s->functions = 0;
kfree(s->config);
s->config = NULL;
s->ops->get_status(s, &status); s->ops->get_status(s, &status);
if (status & SS_POWERON) { if (status & SS_POWERON) {

View File

@ -16,6 +16,7 @@
#define _LINUX_CS_INTERNAL_H #define _LINUX_CS_INTERNAL_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/kref.h>
/* Flags in client state */ /* Flags in client state */
#define CLIENT_CONFIG_LOCKED 0x0001 #define CLIENT_CONFIG_LOCKED 0x0001
@ -40,6 +41,7 @@ typedef struct region_t {
/* Each card function gets one of these guys */ /* Each card function gets one of these guys */
typedef struct config_t { typedef struct config_t {
struct kref ref;
u_int state; u_int state;
u_int Attributes; u_int Attributes;
u_int IntType; u_int IntType;

View File

@ -23,6 +23,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/kref.h>
#define IN_CARD_SERVICES #define IN_CARD_SERVICES
#include <pcmcia/cs_types.h> #include <pcmcia/cs_types.h>
@ -343,12 +344,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev)
put_device(&p_dev->dev); put_device(&p_dev->dev);
} }
static void pcmcia_release_function(struct kref *ref)
{
struct config_t *c = container_of(ref, struct config_t, ref);
kfree(c);
}
static void pcmcia_release_dev(struct device *dev) static void pcmcia_release_dev(struct device *dev)
{ {
struct pcmcia_device *p_dev = to_pcmcia_dev(dev); struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
ds_dbg(1, "releasing dev %p\n", p_dev); ds_dbg(1, "releasing dev %p\n", p_dev);
pcmcia_put_socket(p_dev->socket); pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname); kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev); kfree(p_dev);
} }
@ -377,30 +385,14 @@ static int pcmcia_device_probe(struct device * dev)
p_drv = to_pcmcia_drv(dev->driver); p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket; s = p_dev->socket;
if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) { if ((!p_drv->probe) || (!p_dev->function_config) ||
(!try_module_get(p_drv->owner))) {
ret = -EINVAL; ret = -EINVAL;
goto put_dev; goto put_dev;
} }
p_dev->state &= ~CLIENT_UNBOUND; p_dev->state &= ~CLIENT_UNBOUND;
/* set up the device configuration, if it hasn't been done before */
if (!s->functions) {
cistpl_longlink_mfc_t mfc;
if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC,
&mfc) == CS_SUCCESS)
s->functions = mfc.nfn;
else
s->functions = 1;
s->config = kzalloc(sizeof(config_t) * s->functions,
GFP_KERNEL);
if (!s->config) {
ret = -ENOMEM;
goto put_module;
}
}
p_dev->function_config = &s->config[p_dev->func];
ret = p_drv->probe(p_dev); ret = p_drv->probe(p_dev);
if (ret) if (ret)
goto put_module; goto put_module;
@ -576,7 +568,7 @@ static DECLARE_MUTEX(device_add_lock);
struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
{ {
struct pcmcia_device *p_dev; struct pcmcia_device *p_dev, *tmp_dev;
unsigned long flags; unsigned long flags;
int bus_id_len; int bus_id_len;
@ -597,6 +589,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
p_dev->socket = s; p_dev->socket = s;
p_dev->device_no = (s->device_count++); p_dev->device_no = (s->device_count++);
p_dev->func = function; p_dev->func = function;
if (s->functions < function)
s->functions = function;
p_dev->dev.bus = &pcmcia_bus_type; p_dev->dev.bus = &pcmcia_bus_type;
p_dev->dev.parent = s->dev.dev; p_dev->dev.parent = s->dev.dev;
@ -611,28 +605,50 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
/* compat */ /* compat */
p_dev->state = CLIENT_UNBOUND; p_dev->state = CLIENT_UNBOUND;
/* Add to the list in pcmcia_bus_socket */
spin_lock_irqsave(&pcmcia_dev_list_lock, flags); spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
/*
* p_dev->function_config must be the same for all card functions.
* Note that this is serialized by the device_add_lock, so that
* only one such struct will be created.
*/
list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
if (p_dev->func == tmp_dev->func) {
p_dev->function_config = tmp_dev->function_config;
kref_get(&p_dev->function_config->ref);
}
/* Add to the list in pcmcia_bus_socket */
list_add_tail(&p_dev->socket_device_list, &s->devices_list); list_add_tail(&p_dev->socket_device_list, &s->devices_list);
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
if (!p_dev->function_config) {
p_dev->function_config = kzalloc(sizeof(struct config_t),
GFP_KERNEL);
if (!p_dev->function_config)
goto err_unreg;
kref_init(&p_dev->function_config->ref);
}
printk(KERN_NOTICE "pcmcia: registering new device %s\n", printk(KERN_NOTICE "pcmcia: registering new device %s\n",
p_dev->devname); p_dev->devname);
pcmcia_device_query(p_dev); pcmcia_device_query(p_dev);
if (device_register(&p_dev->dev)) { if (device_register(&p_dev->dev))
spin_lock_irqsave(&pcmcia_dev_list_lock, flags); goto err_unreg;
list_del(&p_dev->socket_device_list);
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
goto err_free;
}
up(&device_add_lock); up(&device_add_lock);
return p_dev; return p_dev;
err_unreg:
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
list_del(&p_dev->socket_device_list);
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
err_free: err_free:
kfree(p_dev->devname); kfree(p_dev->devname);
kfree(p_dev); kfree(p_dev);

View File

@ -186,7 +186,6 @@ struct pcmcia_socket {
u_short lock_count; u_short lock_count;
pccard_mem_map cis_mem; pccard_mem_map cis_mem;
void __iomem *cis_virt; void __iomem *cis_virt;
struct config_t *config;
struct { struct {
u_int AssignedIRQ; u_int AssignedIRQ;
u_int Config; u_int Config;