mirror of https://gitee.com/openkylin/linux.git
[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>
This commit is contained in:
parent
aaaaaadbe7
commit
64bf69ddff
|
@ -246,10 +246,6 @@ static void atmtcp_c_close(struct atm_vcc *vcc)
|
||||||
{
|
{
|
||||||
struct atm_dev *atmtcp_dev;
|
struct atm_dev *atmtcp_dev;
|
||||||
struct atmtcp_dev_data *dev_data;
|
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;
|
atmtcp_dev = (struct atm_dev *) vcc->dev_data;
|
||||||
dev_data = PRIV(atmtcp_dev);
|
dev_data = PRIV(atmtcp_dev);
|
||||||
|
@ -257,20 +253,8 @@ static void atmtcp_c_close(struct atm_vcc *vcc)
|
||||||
if (dev_data->persist) return;
|
if (dev_data->persist) return;
|
||||||
atmtcp_dev->dev_data = NULL;
|
atmtcp_dev->dev_data = NULL;
|
||||||
kfree(dev_data);
|
kfree(dev_data);
|
||||||
shutdown_atm_dev(atmtcp_dev);
|
atm_dev_deregister(atmtcp_dev);
|
||||||
vcc->dev_data = NULL;
|
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);
|
module_put(THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +434,7 @@ static int atmtcp_remove_persistent(int itf)
|
||||||
if (PRIV(dev)->vcc) return 0;
|
if (PRIV(dev)->vcc) return 0;
|
||||||
kfree(dev_data);
|
kfree(dev_data);
|
||||||
atm_dev_put(dev);
|
atm_dev_put(dev);
|
||||||
shutdown_atm_dev(dev);
|
atm_dev_deregister(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -879,7 +879,7 @@ static int usbatm_atm_init(struct usbatm_data *instance)
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
instance->atm_dev = NULL;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1164,7 +1164,7 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
|
||||||
|
|
||||||
/* ATM finalize */
|
/* ATM finalize */
|
||||||
if (instance->atm_dev)
|
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 */
|
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,7 @@ enum {
|
||||||
|
|
||||||
|
|
||||||
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 */
|
int number,unsigned long *flags); /* number == -1: pick first available */
|
||||||
struct atm_dev *atm_dev_lookup(int number);
|
struct atm_dev *atm_dev_lookup(int number);
|
||||||
void atm_dev_deregister(struct atm_dev *dev);
|
void atm_dev_deregister(struct atm_dev *dev);
|
||||||
void shutdown_atm_dev(struct atm_dev *dev);
|
|
||||||
void vcc_insert_socket(struct sock *sk);
|
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)
|
static inline void atm_dev_put(struct atm_dev *dev)
|
||||||
{
|
{
|
||||||
atomic_dec(&dev->refcnt);
|
if (atomic_dec_and_test(&dev->refcnt)) {
|
||||||
|
BUG_ON(!test_bit(ATM_DF_REMOVED, &dev->flags));
|
||||||
if ((atomic_read(&dev->refcnt) == 1) &&
|
if (dev->ops->dev_close)
|
||||||
test_bit(ATM_DF_CLOSE,&dev->flags))
|
dev->ops->dev_close(dev);
|
||||||
shutdown_atm_dev(dev);
|
kfree(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,29 @@ void vcc_release_async(struct atm_vcc *vcc, int reply)
|
||||||
EXPORT_SYMBOL(vcc_release_async);
|
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)
|
static int adjust_tp(struct atm_trafprm *tp,unsigned char aal)
|
||||||
{
|
{
|
||||||
int max_sdu;
|
int max_sdu;
|
||||||
|
@ -332,12 +355,13 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
|
if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
error = 0;
|
error = -ENODEV;
|
||||||
if (!try_module_get(dev->ops->owner))
|
if (!try_module_get(dev->ops->owner))
|
||||||
return -ENODEV;
|
return error;
|
||||||
vcc->dev = dev;
|
vcc->dev = dev;
|
||||||
write_lock_irq(&vcc_sklist_lock);
|
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);
|
write_unlock_irq(&vcc_sklist_lock);
|
||||||
goto fail_module_put;
|
goto fail_module_put;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,4 +47,6 @@ static inline void atm_proc_exit(void)
|
||||||
/* SVC */
|
/* SVC */
|
||||||
int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
|
int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
|
||||||
|
|
||||||
|
void atm_dev_release_vccs(struct atm_dev *dev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct atm_dev *atm_dev_lookup(int number)
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
|
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
|
||||||
int number, unsigned long *flags)
|
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)
|
void atm_dev_deregister(struct atm_dev *dev)
|
||||||
{
|
{
|
||||||
unsigned long warning_time;
|
BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags));
|
||||||
|
set_bit(ATM_DF_REMOVED, &dev->flags);
|
||||||
atm_proc_dev_deregister(dev);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
down(&atm_dev_mutex);
|
||||||
list_del(&dev->dev_list);
|
list_del(&dev->dev_list);
|
||||||
up(&atm_dev_mutex);
|
up(&atm_dev_mutex);
|
||||||
|
|
||||||
warning_time = jiffies;
|
atm_dev_release_vccs(dev);
|
||||||
while (atomic_read(&dev->refcnt) != 1) {
|
atm_proc_dev_deregister(dev);
|
||||||
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_put(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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_register);
|
||||||
EXPORT_SYMBOL(atm_dev_deregister);
|
EXPORT_SYMBOL(atm_dev_deregister);
|
||||||
EXPORT_SYMBOL(atm_dev_lookup);
|
EXPORT_SYMBOL(atm_dev_lookup);
|
||||||
EXPORT_SYMBOL(shutdown_atm_dev);
|
|
||||||
|
|
Loading…
Reference in New Issue