diff options
| author | Stanislaw Gruszka <stf_xl@wp.pl> | 2005-11-29 19:16:41 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2005-11-29 19:16:41 -0500 |
| commit | 64bf69ddff7637b7ed7acf9b2a823cc0ee519439 (patch) | |
| tree | fb3a746e36bcfa307979bef2a20ce5f1d32ec537 | |
| parent | aaaaaadbe7a663d110814db50fcbe7d320eb4c32 (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.c | 20 | ||||
| -rw-r--r-- | drivers/usb/atm/usbatm.c | 4 | ||||
| -rw-r--r-- | include/linux/atmdev.h | 14 | ||||
| -rw-r--r-- | net/atm/common.c | 30 | ||||
| -rw-r--r-- | net/atm/common.h | 2 | ||||
| -rw-r--r-- | net/atm/resources.c | 39 |
6 files changed, 52 insertions, 57 deletions
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c index 57f1810fdccd..fc518d85543d 100644 --- a/drivers/atm/atmtcp.c +++ b/drivers/atm/atmtcp.c | |||
| @@ -246,10 +246,6 @@ static void atmtcp_c_close(struct atm_vcc *vcc) | |||
| 246 | { | 246 | { |
| 247 | struct atm_dev *atmtcp_dev; | 247 | struct atm_dev *atmtcp_dev; |
| 248 | struct atmtcp_dev_data *dev_data; | 248 | struct atmtcp_dev_data *dev_data; |
| 249 | struct sock *s; | ||
| 250 | struct hlist_node *node; | ||
| 251 | struct atm_vcc *walk; | ||
| 252 | int i; | ||
| 253 | 249 | ||
| 254 | atmtcp_dev = (struct atm_dev *) vcc->dev_data; | 250 | atmtcp_dev = (struct atm_dev *) vcc->dev_data; |
| 255 | dev_data = PRIV(atmtcp_dev); | 251 | dev_data = PRIV(atmtcp_dev); |
| @@ -257,20 +253,8 @@ static void atmtcp_c_close(struct atm_vcc *vcc) | |||
| 257 | if (dev_data->persist) return; | 253 | if (dev_data->persist) return; |
| 258 | atmtcp_dev->dev_data = NULL; | 254 | atmtcp_dev->dev_data = NULL; |
| 259 | kfree(dev_data); | 255 | kfree(dev_data); |
| 260 | shutdown_atm_dev(atmtcp_dev); | 256 | atm_dev_deregister(atmtcp_dev); |
| 261 | vcc->dev_data = NULL; | 257 | vcc->dev_data = NULL; |
| 262 | read_lock(&vcc_sklist_lock); | ||
| 263 | for(i = 0; i < VCC_HTABLE_SIZE; ++i) { | ||
| 264 | struct hlist_head *head = &vcc_hash[i]; | ||
| 265 | |||
| 266 | sk_for_each(s, node, head) { | ||
| 267 | walk = atm_sk(s); | ||
| 268 | if (walk->dev != atmtcp_dev) | ||
| 269 | continue; | ||
| 270 | wake_up(s->sk_sleep); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | read_unlock(&vcc_sklist_lock); | ||
| 274 | module_put(THIS_MODULE); | 258 | module_put(THIS_MODULE); |
| 275 | } | 259 | } |
| 276 | 260 | ||
| @@ -450,7 +434,7 @@ static int atmtcp_remove_persistent(int itf) | |||
| 450 | if (PRIV(dev)->vcc) return 0; | 434 | if (PRIV(dev)->vcc) return 0; |
| 451 | kfree(dev_data); | 435 | kfree(dev_data); |
| 452 | atm_dev_put(dev); | 436 | atm_dev_put(dev); |
| 453 | shutdown_atm_dev(dev); | 437 | atm_dev_deregister(dev); |
| 454 | return 0; | 438 | return 0; |
| 455 | } | 439 | } |
| 456 | 440 | ||
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index c466739428b2..2e6593e6c1bd 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) | |||
| 879 | 879 | ||
| 880 | fail: | 880 | fail: |
| 881 | instance->atm_dev = NULL; | 881 | instance->atm_dev = NULL; |
| 882 | shutdown_atm_dev(atm_dev); /* usbatm_atm_dev_close will eventually be called */ | 882 | atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */ |
| 883 | return ret; | 883 | return ret; |
| 884 | } | 884 | } |
| 885 | 885 | ||
| @@ -1164,7 +1164,7 @@ void usbatm_usb_disconnect(struct usb_interface *intf) | |||
| 1164 | 1164 | ||
| 1165 | /* ATM finalize */ | 1165 | /* ATM finalize */ |
| 1166 | if (instance->atm_dev) | 1166 | if (instance->atm_dev) |
| 1167 | shutdown_atm_dev(instance->atm_dev); | 1167 | atm_dev_deregister(instance->atm_dev); |
| 1168 | 1168 | ||
| 1169 | usbatm_put_instance(instance); /* taken in usbatm_usb_probe */ | 1169 | usbatm_put_instance(instance); /* taken in usbatm_usb_probe */ |
| 1170 | } | 1170 | } |
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 8fadb073c834..b203ea82a0a8 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h | |||
| @@ -274,7 +274,7 @@ enum { | |||
| 274 | 274 | ||
| 275 | 275 | ||
| 276 | enum { | 276 | enum { |
| 277 | ATM_DF_CLOSE, /* close device when last VCC is closed */ | 277 | ATM_DF_REMOVED, /* device was removed from atm_devs list */ |
| 278 | }; | 278 | }; |
| 279 | 279 | ||
| 280 | 280 | ||
| @@ -415,7 +415,6 @@ struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, | |||
| 415 | int number,unsigned long *flags); /* number == -1: pick first available */ | 415 | int number,unsigned long *flags); /* number == -1: pick first available */ |
| 416 | struct atm_dev *atm_dev_lookup(int number); | 416 | struct atm_dev *atm_dev_lookup(int number); |
| 417 | void atm_dev_deregister(struct atm_dev *dev); | 417 | void atm_dev_deregister(struct atm_dev *dev); |
| 418 | void shutdown_atm_dev(struct atm_dev *dev); | ||
| 419 | void vcc_insert_socket(struct sock *sk); | 418 | void vcc_insert_socket(struct sock *sk); |
| 420 | 419 | ||
| 421 | 420 | ||
| @@ -457,11 +456,12 @@ static inline void atm_dev_hold(struct atm_dev *dev) | |||
| 457 | 456 | ||
| 458 | static inline void atm_dev_put(struct atm_dev *dev) | 457 | static inline void atm_dev_put(struct atm_dev *dev) |
| 459 | { | 458 | { |
| 460 | atomic_dec(&dev->refcnt); | 459 | if (atomic_dec_and_test(&dev->refcnt)) { |
| 461 | 460 | BUG_ON(!test_bit(ATM_DF_REMOVED, &dev->flags)); | |
| 462 | if ((atomic_read(&dev->refcnt) == 1) && | 461 | if (dev->ops->dev_close) |
| 463 | test_bit(ATM_DF_CLOSE,&dev->flags)) | 462 | dev->ops->dev_close(dev); |
| 464 | shutdown_atm_dev(dev); | 463 | kfree(dev); |
| 464 | } | ||
| 465 | } | 465 | } |
| 466 | 466 | ||
| 467 | 467 | ||
diff --git a/net/atm/common.c b/net/atm/common.c index 9e016f404e14..6656b111cc05 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) | |||
| 221 | EXPORT_SYMBOL(vcc_release_async); | 221 | EXPORT_SYMBOL(vcc_release_async); |
| 222 | 222 | ||
| 223 | 223 | ||
| 224 | void atm_dev_release_vccs(struct atm_dev *dev) | ||
| 225 | { | ||
| 226 | int i; | ||
| 227 | |||
| 228 | write_lock_irq(&vcc_sklist_lock); | ||
| 229 | for (i = 0; i < VCC_HTABLE_SIZE; i++) { | ||
| 230 | struct hlist_head *head = &vcc_hash[i]; | ||
| 231 | struct hlist_node *node, *tmp; | ||
| 232 | struct sock *s; | ||
| 233 | struct atm_vcc *vcc; | ||
| 234 | |||
| 235 | sk_for_each_safe(s, node, tmp, head) { | ||
| 236 | vcc = atm_sk(s); | ||
| 237 | if (vcc->dev == dev) { | ||
| 238 | vcc_release_async(vcc, -EPIPE); | ||
| 239 | sk_del_node_init(s); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | write_unlock_irq(&vcc_sklist_lock); | ||
| 244 | } | ||
| 245 | |||
| 246 | |||
| 224 | static int adjust_tp(struct atm_trafprm *tp,unsigned char aal) | 247 | static int adjust_tp(struct atm_trafprm *tp,unsigned char aal) |
| 225 | { | 248 | { |
| 226 | int max_sdu; | 249 | int max_sdu; |
| @@ -332,12 +355,13 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, | |||
| 332 | return -EINVAL; | 355 | return -EINVAL; |
| 333 | if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) | 356 | if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) |
| 334 | return -EPERM; | 357 | return -EPERM; |
| 335 | error = 0; | 358 | error = -ENODEV; |
| 336 | if (!try_module_get(dev->ops->owner)) | 359 | if (!try_module_get(dev->ops->owner)) |
| 337 | return -ENODEV; | 360 | return error; |
| 338 | vcc->dev = dev; | 361 | vcc->dev = dev; |
| 339 | write_lock_irq(&vcc_sklist_lock); | 362 | write_lock_irq(&vcc_sklist_lock); |
| 340 | if ((error = find_ci(vcc, &vpi, &vci))) { | 363 | if (test_bit(ATM_DF_REMOVED, &dev->flags) || |
| 364 | (error = find_ci(vcc, &vpi, &vci))) { | ||
| 341 | write_unlock_irq(&vcc_sklist_lock); | 365 | write_unlock_irq(&vcc_sklist_lock); |
| 342 | goto fail_module_put; | 366 | goto fail_module_put; |
| 343 | } | 367 | } |
diff --git a/net/atm/common.h b/net/atm/common.h index e49ed41c0e33..4887c317cefe 100644 --- a/net/atm/common.h +++ b/net/atm/common.h | |||
| @@ -47,4 +47,6 @@ static inline void atm_proc_exit(void) | |||
| 47 | /* SVC */ | 47 | /* SVC */ |
| 48 | int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos); | 48 | int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos); |
| 49 | 49 | ||
| 50 | void atm_dev_release_vccs(struct atm_dev *dev); | ||
| 51 | |||
| 50 | #endif | 52 | #endif |
diff --git a/net/atm/resources.c b/net/atm/resources.c index ad533b02b84f..c8c459fcb038 100644 --- a/net/atm/resources.c +++ b/net/atm/resources.c | |||
| @@ -70,6 +70,7 @@ struct atm_dev *atm_dev_lookup(int number) | |||
| 70 | return dev; | 70 | return dev; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | |||
| 73 | struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, | 74 | struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, |
| 74 | int number, unsigned long *flags) | 75 | int number, unsigned long *flags) |
| 75 | { | 76 | { |
| @@ -123,37 +124,22 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, | |||
| 123 | 124 | ||
| 124 | void atm_dev_deregister(struct atm_dev *dev) | 125 | void atm_dev_deregister(struct atm_dev *dev) |
| 125 | { | 126 | { |
| 126 | unsigned long warning_time; | 127 | BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags)); |
| 127 | 128 | set_bit(ATM_DF_REMOVED, &dev->flags); | |
| 128 | atm_proc_dev_deregister(dev); | 129 | |
| 129 | 130 | /* | |
| 131 | * if we remove current device from atm_devs list, new device | ||
| 132 | * with same number can appear, such we need deregister proc, | ||
| 133 | * release async all vccs and remove them from vccs list too | ||
| 134 | */ | ||
| 130 | down(&atm_dev_mutex); | 135 | down(&atm_dev_mutex); |
| 131 | list_del(&dev->dev_list); | 136 | list_del(&dev->dev_list); |
| 132 | up(&atm_dev_mutex); | 137 | up(&atm_dev_mutex); |
| 133 | 138 | ||
| 134 | warning_time = jiffies; | 139 | atm_dev_release_vccs(dev); |
| 135 | while (atomic_read(&dev->refcnt) != 1) { | 140 | atm_proc_dev_deregister(dev); |
| 136 | msleep(250); | ||
| 137 | if ((jiffies - warning_time) > 10 * HZ) { | ||
| 138 | printk(KERN_EMERG "atm_dev_deregister: waiting for " | ||
| 139 | "dev %d to become free. Usage count = %d\n", | ||
| 140 | dev->number, atomic_read(&dev->refcnt)); | ||
| 141 | warning_time = jiffies; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | kfree(dev); | ||
| 146 | } | ||
| 147 | 141 | ||
| 148 | void shutdown_atm_dev(struct atm_dev *dev) | 142 | atm_dev_put(dev); |
| 149 | { | ||
| 150 | if (atomic_read(&dev->refcnt) > 1) { | ||
| 151 | set_bit(ATM_DF_CLOSE, &dev->flags); | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | if (dev->ops->dev_close) | ||
| 155 | dev->ops->dev_close(dev); | ||
| 156 | atm_dev_deregister(dev); | ||
| 157 | } | 143 | } |
| 158 | 144 | ||
| 159 | 145 | ||
| @@ -433,4 +419,3 @@ void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
| 433 | EXPORT_SYMBOL(atm_dev_register); | 419 | EXPORT_SYMBOL(atm_dev_register); |
| 434 | EXPORT_SYMBOL(atm_dev_deregister); | 420 | EXPORT_SYMBOL(atm_dev_deregister); |
| 435 | EXPORT_SYMBOL(atm_dev_lookup); | 421 | EXPORT_SYMBOL(atm_dev_lookup); |
| 436 | EXPORT_SYMBOL(shutdown_atm_dev); | ||
