summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStanislaw Gruszka <stf_xl@wp.pl>2005-11-29 16:16:41 -0800
committerDavid S. Miller <davem@davemloft.net>2005-11-29 16:16:41 -0800
commit64bf69ddff7637b7ed7acf9b2a823cc0ee519439 (patch)
treefb3a746e36bcfa307979bef2a20ce5f1d32ec537
parentaaaaaadbe7a663d110814db50fcbe7d320eb4c32 (diff)
[ATM]: deregistration removes device from atm_devs list immediately
atm_dev_deregister() removes device from atm_dev list immediately to prevent operations on a phantom device. Decision to free device based only on ->refcnt now. Remove shutdown_atm_dev() use atm_dev_deregister() instead. atm_dev_deregister() also asynchronously releases all vccs related to device. Signed-off-by: Stanislaw Gruszka <stf_xl@wp.pl> Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/atm/atmtcp.c20
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--include/linux/atmdev.h14
-rw-r--r--net/atm/common.c30
-rw-r--r--net/atm/common.h2
-rw-r--r--net/atm/resources.c39
6 files changed, 52 insertions, 57 deletions
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index 57f1810fdcc..fc518d85543 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -246,10 +246,6 @@ static void atmtcp_c_close(struct atm_vcc *vcc)
{
struct atm_dev *atmtcp_dev;
struct atmtcp_dev_data *dev_data;
- struct sock *s;
- struct hlist_node *node;
- struct atm_vcc *walk;
- int i;
atmtcp_dev = (struct atm_dev *) vcc->dev_data;
dev_data = PRIV(atmtcp_dev);
@@ -257,20 +253,8 @@ static void atmtcp_c_close(struct atm_vcc *vcc)
if (dev_data->persist) return;
atmtcp_dev->dev_data = NULL;
kfree(dev_data);
- shutdown_atm_dev(atmtcp_dev);
+ atm_dev_deregister(atmtcp_dev);
vcc->dev_data = NULL;
- read_lock(&vcc_sklist_lock);
- for(i = 0; i < VCC_HTABLE_SIZE; ++i) {
- struct hlist_head *head = &vcc_hash[i];
-
- sk_for_each(s, node, head) {
- walk = atm_sk(s);
- if (walk->dev != atmtcp_dev)
- continue;
- wake_up(s->sk_sleep);
- }
- }
- read_unlock(&vcc_sklist_lock);
module_put(THIS_MODULE);
}
@@ -450,7 +434,7 @@ static int atmtcp_remove_persistent(int itf)
if (PRIV(dev)->vcc) return 0;
kfree(dev_data);
atm_dev_put(dev);
- shutdown_atm_dev(dev);
+ atm_dev_deregister(dev);
return 0;
}
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index c466739428b..2e6593e6c1b 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -879,7 +879,7 @@ static int usbatm_atm_init(struct usbatm_data *instance)
fail:
instance->atm_dev = NULL;
- shutdown_atm_dev(atm_dev); /* usbatm_atm_dev_close will eventually be called */
+ atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */
return ret;
}
@@ -1164,7 +1164,7 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
/* ATM finalize */
if (instance->atm_dev)
- shutdown_atm_dev(instance->atm_dev);
+ atm_dev_deregister(instance->atm_dev);
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
}
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index 8fadb073c83..b203ea82a0a 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -274,7 +274,7 @@ enum {
enum {
- ATM_DF_CLOSE, /* close device when last VCC is closed */
+ ATM_DF_REMOVED, /* device was removed from atm_devs list */
};
@@ -415,7 +415,6 @@ struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
int number,unsigned long *flags); /* number == -1: pick first available */
struct atm_dev *atm_dev_lookup(int number);
void atm_dev_deregister(struct atm_dev *dev);
-void shutdown_atm_dev(struct atm_dev *dev);
void vcc_insert_socket(struct sock *sk);
@@ -457,11 +456,12 @@ static inline void atm_dev_hold(struct atm_dev *dev)
static inline void atm_dev_put(struct atm_dev *dev)
{
- atomic_dec(&dev->refcnt);
-
- if ((atomic_read(&dev->refcnt) == 1) &&
- test_bit(ATM_DF_CLOSE,&dev->flags))
- shutdown_atm_dev(dev);
+ if (atomic_dec_and_test(&dev->refcnt)) {
+ BUG_ON(!test_bit(ATM_DF_REMOVED, &dev->flags));
+ if (dev->ops->dev_close)
+ dev->ops->dev_close(dev);
+ kfree(dev);
+ }
}
diff --git a/net/atm/common.c b/net/atm/common.c
index 9e016f404e1..6656b111cc0 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -221,6 +221,29 @@ void vcc_release_async(struct atm_vcc *vcc, int reply)
EXPORT_SYMBOL(vcc_release_async);
+void atm_dev_release_vccs(struct atm_dev *dev)
+{
+ int i;
+
+ write_lock_irq(&vcc_sklist_lock);
+ for (i = 0; i < VCC_HTABLE_SIZE; i++) {
+ struct hlist_head *head = &vcc_hash[i];
+ struct hlist_node *node, *tmp;
+ struct sock *s;
+ struct atm_vcc *vcc;
+
+ sk_for_each_safe(s, node, tmp, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev == dev) {
+ vcc_release_async(vcc, -EPIPE);
+ sk_del_node_init(s);
+ }
+ }
+ }
+ write_unlock_irq(&vcc_sklist_lock);
+}
+
+
static int adjust_tp(struct atm_trafprm *tp,unsigned char aal)
{
int max_sdu;
@@ -332,12 +355,13 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi,
return -EINVAL;
if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
return -EPERM;
- error = 0;
+ error = -ENODEV;
if (!try_module_get(dev->ops->owner))
- return -ENODEV;
+ return error;
vcc->dev = dev;
write_lock_irq(&vcc_sklist_lock);
- if ((error = find_ci(vcc, &vpi, &vci))) {
+ if (test_bit(ATM_DF_REMOVED, &dev->flags) ||
+ (error = find_ci(vcc, &vpi, &vci))) {
write_unlock_irq(&vcc_sklist_lock);
goto fail_module_put;
}
diff --git a/net/atm/common.h b/net/atm/common.h
index e49ed41c0e3..4887c317cef 100644
--- a/net/atm/common.h
+++ b/net/atm/common.h
@@ -47,4 +47,6 @@ static inline void atm_proc_exit(void)
/* SVC */
int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
+void atm_dev_release_vccs(struct atm_dev *dev);
+
#endif
diff --git a/net/atm/resources.c b/net/atm/resources.c
index ad533b02b84..c8c459fcb03 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -70,6 +70,7 @@ struct atm_dev *atm_dev_lookup(int number)
return dev;
}
+
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
int number, unsigned long *flags)
{
@@ -123,37 +124,22 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
void atm_dev_deregister(struct atm_dev *dev)
{
- unsigned long warning_time;
-
- atm_proc_dev_deregister(dev);
-
+ BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags));
+ set_bit(ATM_DF_REMOVED, &dev->flags);
+
+ /*
+ * if we remove current device from atm_devs list, new device
+ * with same number can appear, such we need deregister proc,
+ * release async all vccs and remove them from vccs list too
+ */
down(&atm_dev_mutex);
list_del(&dev->dev_list);
up(&atm_dev_mutex);
- warning_time = jiffies;
- while (atomic_read(&dev->refcnt) != 1) {
- msleep(250);
- if ((jiffies - warning_time) > 10 * HZ) {
- printk(KERN_EMERG "atm_dev_deregister: waiting for "
- "dev %d to become free. Usage count = %d\n",
- dev->number, atomic_read(&dev->refcnt));
- warning_time = jiffies;
- }
- }
-
- kfree(dev);
-}
+ atm_dev_release_vccs(dev);
+ atm_proc_dev_deregister(dev);
-void shutdown_atm_dev(struct atm_dev *dev)
-{
- if (atomic_read(&dev->refcnt) > 1) {
- set_bit(ATM_DF_CLOSE, &dev->flags);
- return;
- }
- if (dev->ops->dev_close)
- dev->ops->dev_close(dev);
- atm_dev_deregister(dev);
+ atm_dev_put(dev);
}
@@ -433,4 +419,3 @@ void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
EXPORT_SYMBOL(atm_dev_register);
EXPORT_SYMBOL(atm_dev_deregister);
EXPORT_SYMBOL(atm_dev_lookup);
-EXPORT_SYMBOL(shutdown_atm_dev);