Commit 861c1242 authored by Chas Williams's avatar Chas Williams Committed by Hideaki Yoshifuji

[ATM]: Add refcounting to atmdev.

parent 7f080bc0
...@@ -153,6 +153,7 @@ static void atmtcp_v_close(struct atm_vcc *vcc) ...@@ -153,6 +153,7 @@ static void atmtcp_v_close(struct atm_vcc *vcc)
static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
{ {
unsigned long flags;
struct atm_cirange ci; struct atm_cirange ci;
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -162,9 +163,14 @@ static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) ...@@ -162,9 +163,14 @@ static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS;
if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 ||
ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL; ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
for (vcc = dev->vccs; vcc; vcc = vcc->next) for (vcc = dev->vccs; vcc; vcc = vcc->next)
if ((vcc->vpi >> ci.vpi_bits) || if ((vcc->vpi >> ci.vpi_bits) ||
(vcc->vci >> ci.vci_bits)) return -EBUSY; (vcc->vci >> ci.vci_bits)) {
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
dev->ci_range = ci; dev->ci_range = ci;
return 0; return 0;
} }
...@@ -227,6 +233,7 @@ static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) ...@@ -227,6 +233,7 @@ static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page)
static void atmtcp_c_close(struct atm_vcc *vcc) static void atmtcp_c_close(struct atm_vcc *vcc)
{ {
unsigned long flags;
struct atm_dev *atmtcp_dev; struct atm_dev *atmtcp_dev;
struct atmtcp_dev_data *dev_data; struct atmtcp_dev_data *dev_data;
struct atm_vcc *walk; struct atm_vcc *walk;
...@@ -239,13 +246,16 @@ static void atmtcp_c_close(struct atm_vcc *vcc) ...@@ -239,13 +246,16 @@ static void atmtcp_c_close(struct atm_vcc *vcc)
kfree(dev_data); kfree(dev_data);
shutdown_atm_dev(atmtcp_dev); shutdown_atm_dev(atmtcp_dev);
vcc->dev_data = NULL; vcc->dev_data = NULL;
spin_lock_irqsave(&atmtcp_dev->lock, flags);
for (walk = atmtcp_dev->vccs; walk; walk = walk->next) for (walk = atmtcp_dev->vccs; walk; walk = walk->next)
wake_up(&walk->sleep); wake_up(&walk->sleep);
spin_unlock_irqrestore(&atmtcp_dev->lock, flags);
} }
static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
{ {
unsigned long flags;
struct atm_dev *dev; struct atm_dev *dev;
struct atmtcp_hdr *hdr; struct atmtcp_hdr *hdr;
struct atm_vcc *out_vcc; struct atm_vcc *out_vcc;
...@@ -260,11 +270,13 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) ...@@ -260,11 +270,13 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
(struct atmtcp_control *) skb->data); (struct atmtcp_control *) skb->data);
goto done; goto done;
} }
spin_lock_irqsave(&dev->lock, flags);
for (out_vcc = dev->vccs; out_vcc; out_vcc = out_vcc->next) for (out_vcc = dev->vccs; out_vcc; out_vcc = out_vcc->next)
if (out_vcc->vpi == ntohs(hdr->vpi) && if (out_vcc->vpi == ntohs(hdr->vpi) &&
out_vcc->vci == ntohs(hdr->vci) && out_vcc->vci == ntohs(hdr->vci) &&
out_vcc->qos.rxtp.traffic_class != ATM_NONE) out_vcc->qos.rxtp.traffic_class != ATM_NONE)
break; break;
spin_unlock_irqrestore(&dev->lock, flags);
if (!out_vcc) { if (!out_vcc) {
atomic_inc(&vcc->stats->tx_err); atomic_inc(&vcc->stats->tx_err);
goto done; goto done;
...@@ -318,6 +330,7 @@ static struct atm_dev atmtcp_control_dev = { ...@@ -318,6 +330,7 @@ static struct atm_dev atmtcp_control_dev = {
.ops = &atmtcp_c_dev_ops, .ops = &atmtcp_c_dev_ops,
.type = "atmtcp", .type = "atmtcp",
.number = 999, .number = 999,
.lock = SPIN_LOCK_UNLOCKED
}; };
...@@ -350,9 +363,12 @@ int atmtcp_attach(struct atm_vcc *vcc,int itf) ...@@ -350,9 +363,12 @@ int atmtcp_attach(struct atm_vcc *vcc,int itf)
struct atm_dev *dev; struct atm_dev *dev;
dev = NULL; dev = NULL;
if (itf != -1) dev = atm_find_dev(itf); if (itf != -1) dev = atm_dev_lookup(itf);
if (dev) { if (dev) {
if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; if (dev->ops != &atmtcp_v_dev_ops) {
atm_dev_release(dev);
return -EMEDIUMTYPE;
}
if (PRIV(dev)->vcc) return -EBUSY; if (PRIV(dev)->vcc) return -EBUSY;
} }
else { else {
...@@ -383,14 +399,18 @@ int atmtcp_remove_persistent(int itf) ...@@ -383,14 +399,18 @@ int atmtcp_remove_persistent(int itf)
struct atm_dev *dev; struct atm_dev *dev;
struct atmtcp_dev_data *dev_data; struct atmtcp_dev_data *dev_data;
dev = atm_find_dev(itf); dev = atm_dev_lookup(itf);
if (!dev) return -ENODEV; if (!dev) return -ENODEV;
if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; if (dev->ops != &atmtcp_v_dev_ops) {
atm_dev_release(dev);
return -EMEDIUMTYPE;
}
dev_data = PRIV(dev); dev_data = PRIV(dev);
if (!dev_data->persist) return 0; if (!dev_data->persist) return 0;
dev_data->persist = 0; dev_data->persist = 0;
if (PRIV(dev)->vcc) return 0; if (PRIV(dev)->vcc) return 0;
kfree(dev_data); kfree(dev_data);
atm_dev_release(dev);
shutdown_atm_dev(dev); shutdown_atm_dev(dev);
return 0; return 0;
} }
......
...@@ -1887,8 +1887,10 @@ static void eni_close(struct atm_vcc *vcc) ...@@ -1887,8 +1887,10 @@ static void eni_close(struct atm_vcc *vcc)
static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci) static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
{ {
unsigned long flags;
struct atm_vcc *walk; struct atm_vcc *walk;
spin_lock_irqsave(&vcc->dev->lock, flags);
if (*vpi == ATM_VPI_ANY) *vpi = 0; if (*vpi == ATM_VPI_ANY) *vpi = 0;
if (*vci == ATM_VCI_ANY) { if (*vci == ATM_VCI_ANY) {
for (*vci = ATM_NOT_RSV_VCI; *vci < NR_VCI; (*vci)++) { for (*vci = ATM_NOT_RSV_VCI; *vci < NR_VCI; (*vci)++) {
...@@ -1907,17 +1909,29 @@ static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci) ...@@ -1907,17 +1909,29 @@ static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
} }
break; break;
} }
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return *vci == NR_VCI ? -EADDRINUSE : 0; return *vci == NR_VCI ? -EADDRINUSE : 0;
} }
if (*vci == ATM_VCI_UNSPEC) return 0; if (*vci == ATM_VCI_UNSPEC) {
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0;
}
if (vcc->qos.rxtp.traffic_class != ATM_NONE && if (vcc->qos.rxtp.traffic_class != ATM_NONE &&
ENI_DEV(vcc->dev)->rx_map[*vci]) ENI_DEV(vcc->dev)->rx_map[*vci]) {
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return -EADDRINUSE; return -EADDRINUSE;
if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; }
if (vcc->qos.txtp.traffic_class == ATM_NONE) {
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0;
}
for (walk = vcc->dev->vccs; walk; walk = walk->next) for (walk = vcc->dev->vccs; walk; walk = walk->next)
if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vci == *vci && if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vci == *vci &&
walk->qos.txtp.traffic_class != ATM_NONE) walk->qos.txtp.traffic_class != ATM_NONE) {
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return -EADDRINUSE; return -EADDRINUSE;
}
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0; return 0;
} }
...@@ -2125,6 +2139,7 @@ static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) ...@@ -2125,6 +2139,7 @@ static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr)
static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
{ {
unsigned long flags;
static const char *signal[] = { "LOST","unknown","okay" }; static const char *signal[] = { "LOST","unknown","okay" };
struct eni_dev *eni_dev = ENI_DEV(dev); struct eni_dev *eni_dev = ENI_DEV(dev);
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -2197,6 +2212,7 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) ...@@ -2197,6 +2212,7 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
return sprintf(page,"%10sbacklog %u packets\n","", return sprintf(page,"%10sbacklog %u packets\n","",
skb_queue_len(&tx->backlog)); skb_queue_len(&tx->backlog));
} }
spin_lock_irqsave(&dev->lock, flags);
for (vcc = dev->vccs; vcc; vcc = vcc->next) { for (vcc = dev->vccs; vcc; vcc = vcc->next) {
struct eni_vcc *eni_vcc = ENI_VCC(vcc); struct eni_vcc *eni_vcc = ENI_VCC(vcc);
int length; int length;
...@@ -2215,8 +2231,10 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) ...@@ -2215,8 +2231,10 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
length += sprintf(page+length,"tx[%d], txing %d bytes", length += sprintf(page+length,"tx[%d], txing %d bytes",
eni_vcc->tx->index,eni_vcc->txing); eni_vcc->tx->index,eni_vcc->txing);
page[length] = '\n'; page[length] = '\n';
spin_unlock_irqrestore(&dev->lock, flags);
return length+1; return length+1;
} }
spin_unlock_irqrestore(&dev->lock, flags);
for (i = 0; i < eni_dev->free_len; i++) { for (i = 0; i < eni_dev->free_len; i++) {
struct eni_free *fe = eni_dev->free_list+i; struct eni_free *fe = eni_dev->free_list+i;
unsigned long offset; unsigned long offset;
......
...@@ -1074,13 +1074,16 @@ fore200e_supply(struct fore200e* fore200e) ...@@ -1074,13 +1074,16 @@ fore200e_supply(struct fore200e* fore200e)
static struct atm_vcc* static struct atm_vcc*
fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd) fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd)
{ {
unsigned long flags;
struct atm_vcc* vcc; struct atm_vcc* vcc;
spin_lock_irqsave(&fore200e->atm_dev->lock, flags);
for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) { for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) {
if (vcc->vpi == rpd->atm_header.vpi && vcc->vci == rpd->atm_header.vci) if (vcc->vpi == rpd->atm_header.vpi && vcc->vci == rpd->atm_header.vci)
break; break;
} }
spin_unlock_irqrestore(&fore200e->atm_dev->lock, flags);
return vcc; return vcc;
} }
...@@ -1352,9 +1355,13 @@ fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc* ...@@ -1352,9 +1355,13 @@ fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc*
static int static int
fore200e_walk_vccs(struct atm_vcc *vcc, short *vpi, int *vci) fore200e_walk_vccs(struct atm_vcc *vcc, short *vpi, int *vci)
{ {
unsigned long flags;
struct atm_vcc* walk; struct atm_vcc* walk;
/* find a free VPI */ /* find a free VPI */
spin_lock_irqsave(&vcc->dev->lock, flags);
if (*vpi == ATM_VPI_ANY) { if (*vpi == ATM_VPI_ANY) {
for (*vpi = 0, walk = vcc->dev->vccs; walk; walk = walk->next) { for (*vpi = 0, walk = vcc->dev->vccs; walk; walk = walk->next) {
...@@ -1378,6 +1385,8 @@ fore200e_walk_vccs(struct atm_vcc *vcc, short *vpi, int *vci) ...@@ -1378,6 +1385,8 @@ fore200e_walk_vccs(struct atm_vcc *vcc, short *vpi, int *vci)
} }
} }
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0; return 0;
} }
...@@ -2638,6 +2647,7 @@ fore200e_module_cleanup(void) ...@@ -2638,6 +2647,7 @@ fore200e_module_cleanup(void)
static int static int
fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page) fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
{ {
unsigned long flags;
struct fore200e* fore200e = FORE200E_DEV(dev); struct fore200e* fore200e = FORE200E_DEV(dev);
int len, left = *pos; int len, left = *pos;
...@@ -2884,6 +2894,7 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page) ...@@ -2884,6 +2894,7 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
len = sprintf(page,"\n" len = sprintf(page,"\n"
" VCCs:\n address\tVPI.VCI:AAL\t(min/max tx PDU size) (min/max rx PDU size)\n"); " VCCs:\n address\tVPI.VCI:AAL\t(min/max tx PDU size) (min/max rx PDU size)\n");
spin_lock_irqsave(&fore200e->atm_dev->lock, flags);
for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) { for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) {
fore200e_vcc = FORE200E_VCC(vcc); fore200e_vcc = FORE200E_VCC(vcc);
...@@ -2898,6 +2909,7 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page) ...@@ -2898,6 +2909,7 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
fore200e_vcc->rx_max_pdu fore200e_vcc->rx_max_pdu
); );
} }
spin_unlock_irqrestore(&fore200e->atm_dev->lock, flags);
return len; return len;
} }
......
...@@ -345,6 +345,7 @@ he_readl_internal(struct he_dev *he_dev, unsigned addr, unsigned flags) ...@@ -345,6 +345,7 @@ he_readl_internal(struct he_dev *he_dev, unsigned addr, unsigned flags)
static __inline__ struct atm_vcc* static __inline__ struct atm_vcc*
he_find_vcc(struct he_dev *he_dev, unsigned cid) he_find_vcc(struct he_dev *he_dev, unsigned cid)
{ {
unsigned long flags;
struct atm_vcc *vcc; struct atm_vcc *vcc;
short vpi; short vpi;
int vci; int vci;
...@@ -352,10 +353,15 @@ he_find_vcc(struct he_dev *he_dev, unsigned cid) ...@@ -352,10 +353,15 @@ he_find_vcc(struct he_dev *he_dev, unsigned cid)
vpi = cid >> he_dev->vcibits; vpi = cid >> he_dev->vcibits;
vci = cid & ((1<<he_dev->vcibits)-1); vci = cid & ((1<<he_dev->vcibits)-1);
spin_lock_irqsave(&he_dev->atm_dev->lock, flags);
for (vcc = he_dev->atm_dev->vccs; vcc; vcc = vcc->next) for (vcc = he_dev->atm_dev->vccs; vcc; vcc = vcc->next)
if (vcc->vci == vci && vcc->vpi == vpi if (vcc->vci == vci && vcc->vpi == vpi
&& vcc->qos.rxtp.traffic_class != ATM_NONE) return vcc; && vcc->qos.rxtp.traffic_class != ATM_NONE) {
spin_unlock_irqrestore(&he_dev->atm_dev->lock, flags);
return vcc;
}
spin_unlock_irqrestore(&he_dev->atm_dev->lock, flags);
return NULL; return NULL;
} }
......
...@@ -2402,8 +2402,10 @@ idt77252_init_rx(struct idt77252_dev *card, struct vc_map *vc, ...@@ -2402,8 +2402,10 @@ idt77252_init_rx(struct idt77252_dev *card, struct vc_map *vc,
static int static int
idt77252_find_vcc(struct atm_vcc *vcc, short *vpi, int *vci) idt77252_find_vcc(struct atm_vcc *vcc, short *vpi, int *vci)
{ {
unsigned long flags;
struct atm_vcc *walk; struct atm_vcc *walk;
spin_lock_irqsave(&vcc->dev->lock, flags);
if (*vpi == ATM_VPI_ANY) { if (*vpi == ATM_VPI_ANY) {
*vpi = 0; *vpi = 0;
walk = vcc->dev->vccs; walk = vcc->dev->vccs;
...@@ -2430,6 +2432,7 @@ idt77252_find_vcc(struct atm_vcc *vcc, short *vpi, int *vci) ...@@ -2430,6 +2432,7 @@ idt77252_find_vcc(struct atm_vcc *vcc, short *vpi, int *vci)
} }
} }
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0; return 0;
} }
......
...@@ -331,6 +331,8 @@ struct atm_dev { ...@@ -331,6 +331,8 @@ struct atm_dev {
struct k_atm_dev_stats stats; /* statistics */ struct k_atm_dev_stats stats; /* statistics */
char signal; /* signal status (ATM_PHY_SIG_*) */ char signal; /* signal status (ATM_PHY_SIG_*) */
int link_rate; /* link rate (default: OC3) */ int link_rate; /* link rate (default: OC3) */
atomic_t refcnt; /* reference count */
spinlock_t lock; /* protect internal members */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_entry; /* proc entry */ struct proc_dir_entry *proc_entry; /* proc entry */
char *proc_name; /* proc entry name */ char *proc_name; /* proc entry name */
...@@ -392,7 +394,7 @@ struct atm_skb_data { ...@@ -392,7 +394,7 @@ struct atm_skb_data {
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); /* number == -1: pick first available */ int number,unsigned long *flags); /* number == -1: pick first available */
struct atm_dev *atm_find_dev(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 shutdown_atm_dev(struct atm_dev *dev);
void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev); void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev);
...@@ -403,30 +405,46 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev); ...@@ -403,30 +405,46 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev);
* *
*/ */
static __inline__ int atm_guess_pdu2truesize(int pdu_size) static inline int atm_guess_pdu2truesize(int pdu_size)
{ {
return ((pdu_size+15) & ~15) + sizeof(struct sk_buff); return ((pdu_size+15) & ~15) + sizeof(struct sk_buff);
} }
static __inline__ void atm_force_charge(struct atm_vcc *vcc,int truesize) static inline void atm_force_charge(struct atm_vcc *vcc,int truesize)
{ {
atomic_add(truesize, &vcc->sk->rmem_alloc); atomic_add(truesize, &vcc->sk->rmem_alloc);
} }
static __inline__ void atm_return(struct atm_vcc *vcc,int truesize) static inline void atm_return(struct atm_vcc *vcc,int truesize)
{ {
atomic_sub(truesize, &vcc->sk->rmem_alloc); atomic_sub(truesize, &vcc->sk->rmem_alloc);
} }
static __inline__ int atm_may_send(struct atm_vcc *vcc,unsigned int size) static inline int atm_may_send(struct atm_vcc *vcc,unsigned int size)
{ {
return (size + atomic_read(&vcc->sk->wmem_alloc)) < vcc->sk->sndbuf; return (size + atomic_read(&vcc->sk->wmem_alloc)) < vcc->sk->sndbuf;
} }
static inline void atm_dev_hold(struct atm_dev *dev)
{
atomic_inc(&dev->refcnt);
}
static inline void atm_dev_release(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);
}
int atm_charge(struct atm_vcc *vcc,int truesize); int atm_charge(struct atm_vcc *vcc,int truesize);
struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size, struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size,
int gfp_flags); int gfp_flags);
......
...@@ -36,14 +36,6 @@ static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b) ...@@ -36,14 +36,6 @@ static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
} }
/*
* Avoid modification of any list of local interfaces while reading it
* (which may involve page faults and therefore rescheduling)
*/
static DECLARE_MUTEX(local_lock);
extern spinlock_t atm_dev_lock;
static void notify_sigd(struct atm_dev *dev) static void notify_sigd(struct atm_dev *dev)
{ {
struct sockaddr_atmpvc pvc; struct sockaddr_atmpvc pvc;
...@@ -52,46 +44,46 @@ static void notify_sigd(struct atm_dev *dev) ...@@ -52,46 +44,46 @@ static void notify_sigd(struct atm_dev *dev)
sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL); sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
} }
/*
* This is called from atm_ioctl only. You must hold the lock as a caller
*/
void atm_reset_addr(struct atm_dev *dev) void atm_reset_addr(struct atm_dev *dev)
{ {
unsigned long flags;
struct atm_dev_addr *this; struct atm_dev_addr *this;
down(&local_lock); spin_lock_irqsave(&dev->lock, flags);
while (dev->local) { while (dev->local) {
this = dev->local; this = dev->local;
dev->local = this->next; dev->local = this->next;
kfree(this); kfree(this);
} }
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev); notify_sigd(dev);
} }
int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr) int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
{ {
unsigned long flags;
struct atm_dev_addr **walk; struct atm_dev_addr **walk;
int error; int error;
error = check_addr(addr); error = check_addr(addr);
if (error) return error; if (error)
down(&local_lock); return error;
spin_lock_irqsave(&dev->lock, flags);
for (walk = &dev->local; *walk; walk = &(*walk)->next) for (walk = &dev->local; *walk; walk = &(*walk)->next)
if (identical(&(*walk)->addr,addr)) { if (identical(&(*walk)->addr,addr)) {
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return -EEXIST; return -EEXIST;
} }
*walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL); *walk = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
if (!*walk) { if (!*walk) {
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return -ENOMEM; return -ENOMEM;
} }
(*walk)->addr = *addr; (*walk)->addr = *addr;
(*walk)->next = NULL; (*walk)->next = NULL;
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev); notify_sigd(dev);
return 0; return 0;
} }
...@@ -99,22 +91,24 @@ int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr) ...@@ -99,22 +91,24 @@ int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr) int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
{ {
unsigned long flags;
struct atm_dev_addr **walk,*this; struct atm_dev_addr **walk,*this;
int error; int error;
error = check_addr(addr); error = check_addr(addr);
if (error) return error; if (error)
down(&local_lock); return error;
spin_lock_irqsave(&dev->lock, flags);
for (walk = &dev->local; *walk; walk = &(*walk)->next) for (walk = &dev->local; *walk; walk = &(*walk)->next)
if (identical(&(*walk)->addr,addr)) break; if (identical(&(*walk)->addr,addr)) break;
if (!*walk) { if (!*walk) {
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return -ENOENT; return -ENOENT;
} }
this = *walk; this = *walk;
*walk = this->next; *walk = this->next;
kfree(this); kfree(this);
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev); notify_sigd(dev);
return 0; return 0;
} }
...@@ -122,24 +116,25 @@ int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr) ...@@ -122,24 +116,25 @@ int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size) int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size)
{ {
unsigned long flags;
struct atm_dev_addr *walk; struct atm_dev_addr *walk;
int total; int total;
down(&local_lock); spin_lock_irqsave(&dev->lock, flags);
total = 0; total = 0;
for (walk = dev->local; walk; walk = walk->next) { for (walk = dev->local; walk; walk = walk->next) {
total += sizeof(struct sockaddr_atmsvc); total += sizeof(struct sockaddr_atmsvc);
if (total > size) { if (total > size) {
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return -E2BIG; return -E2BIG;
} }
if (copy_to_user(u_buf,&walk->addr, if (copy_to_user(u_buf,&walk->addr,
sizeof(struct sockaddr_atmsvc))) { sizeof(struct sockaddr_atmsvc))) {
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return -EFAULT; return -EFAULT;
} }
u_buf++; u_buf++;
} }
up(&local_lock); spin_unlock_irqrestore(&dev->lock, flags);
return total; return total;
} }
...@@ -63,13 +63,19 @@ static int check_ci(struct atm_vcc *vcc,short vpi,int vci) ...@@ -63,13 +63,19 @@ static int check_ci(struct atm_vcc *vcc,short vpi,int vci)
int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci) int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
{ {
unsigned long flags;
static short p = 0; /* poor man's per-device cache */ static short p = 0; /* poor man's per-device cache */
static int c = 0; static int c = 0;
short old_p; short old_p;
int old_c; int old_c;
int err;
if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) spin_lock_irqsave(&vcc->dev->lock, flags);
return check_ci(vcc,*vpi,*vci); if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) {
err = check_ci(vcc,*vpi,*vci);
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return err;
}
/* last scan may have left values out of bounds for current device */ /* last scan may have left values out of bounds for current device */
if (*vpi != ATM_VPI_ANY) p = *vpi; if (*vpi != ATM_VPI_ANY) p = *vpi;
else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0; else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
...@@ -82,6 +88,7 @@ int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci) ...@@ -82,6 +88,7 @@ int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
if (!check_ci(vcc,p,c)) { if (!check_ci(vcc,p,c)) {
*vpi = p; *vpi = p;
*vci = c; *vci = c;
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return 0; return 0;
} }
if (*vci == ATM_VCI_ANY) { if (*vci == ATM_VCI_ANY) {
...@@ -96,6 +103,7 @@ int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci) ...@@ -96,6 +103,7 @@ int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
} }
} }
while (old_p != p || old_c != c); while (old_p != p || old_c != c);
spin_unlock_irqrestore(&vcc->dev->lock, flags);
return -EADDRINUSE; return -EADDRINUSE;
} }
......
...@@ -717,6 +717,7 @@ static struct atm_dev atmarpd_dev = { ...@@ -717,6 +717,7 @@ static struct atm_dev atmarpd_dev = {
.ops = &atmarpd_dev_ops, .ops = &atmarpd_dev_ops,
.type = "arpd", .type = "arpd",
.number = 999, .number = 999,
.lock = SPIN_LOCK_UNLOCKED
}; };
......
...@@ -115,7 +115,6 @@ EXPORT_SYMBOL(br2684_ioctl_hook); ...@@ -115,7 +115,6 @@ EXPORT_SYMBOL(br2684_ioctl_hook);
#define DPRINTK(format,args...) #define DPRINTK(format,args...)
#endif #endif
spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;
static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size) static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size)
{ {
...@@ -175,19 +174,17 @@ void atm_release_vcc_sk(struct sock *sk,int free_sk) ...@@ -175,19 +174,17 @@ void atm_release_vcc_sk(struct sock *sk,int free_sk)
atm_return(vcc,skb->truesize); atm_return(vcc,skb->truesize);
kfree_skb(skb); kfree_skb(skb);
} }
spin_lock (&atm_dev_lock);
fops_put (vcc->dev->ops); module_put(vcc->dev->ops->owner);
atm_dev_release(vcc->dev);
if (atomic_read(&vcc->sk->rmem_alloc)) if (atomic_read(&vcc->sk->rmem_alloc))
printk(KERN_WARNING "atm_release_vcc: strange ... " printk(KERN_WARNING "atm_release_vcc: strange ... "
"rmem_alloc == %d after closing\n", "rmem_alloc == %d after closing\n",
atomic_read(&vcc->sk->rmem_alloc)); atomic_read(&vcc->sk->rmem_alloc));
bind_vcc(vcc,NULL); bind_vcc(vcc,NULL);
} else }
spin_lock (&atm_dev_lock);
if (free_sk) free_atm_vcc_sk(sk); if (free_sk) free_atm_vcc_sk(sk);
spin_unlock (&atm_dev_lock);
} }
...@@ -280,11 +277,11 @@ static int atm_do_connect_dev(struct atm_vcc *vcc,struct atm_dev *dev,int vpi, ...@@ -280,11 +277,11 @@ static int atm_do_connect_dev(struct atm_vcc *vcc,struct atm_dev *dev,int vpi,
vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu); vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu);
DPRINTK(" RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.traffic_class, DPRINTK(" RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.traffic_class,
vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu); vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu);
fops_get (dev->ops); __module_get(dev->ops->owner);
if (dev->ops->open) { if (dev->ops->open) {
error = dev->ops->open(vcc,vpi,vci); error = dev->ops->open(vcc,vpi,vci);
if (error) { if (error) {
fops_put (dev->ops); module_put(dev->ops->owner);
bind_vcc(vcc,NULL); bind_vcc(vcc,NULL);
return error; return error;
} }
...@@ -298,14 +295,13 @@ static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci) ...@@ -298,14 +295,13 @@ static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci)
struct atm_dev *dev; struct atm_dev *dev;
int return_val; int return_val;
spin_lock (&atm_dev_lock); dev = atm_dev_lookup(itf);
dev = atm_find_dev(itf);
if (!dev) if (!dev)
return_val = -ENODEV; return_val = -ENODEV;
else else {
return_val = atm_do_connect_dev(vcc,dev,vpi,vci); return_val = atm_do_connect_dev(vcc,dev,vpi,vci);
if (return_val) atm_dev_release(dev);
spin_unlock (&atm_dev_lock); }
return return_val; return return_val;
} }
...@@ -336,15 +332,20 @@ int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci) ...@@ -336,15 +332,20 @@ int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci)
} }
else { else {
struct atm_dev *dev = NULL; struct atm_dev *dev = NULL;
struct list_head *p; struct list_head *p, *next;
spin_lock (&atm_dev_lock); spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each_safe(p, next, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
if (!atm_do_connect_dev(vcc,dev,vpi,vci)) break; atm_dev_hold(dev);
spin_unlock(&atm_dev_lock);
if (!atm_do_connect_dev(vcc,dev,vpi,vci))
break;
atm_dev_release(dev);
dev = NULL; dev = NULL;
spin_lock(&atm_dev_lock);
} }
spin_unlock (&atm_dev_lock); spin_unlock(&atm_dev_lock);
if (!dev) return -ENODEV; if (!dev) return -ENODEV;
} }
if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
...@@ -562,7 +563,6 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -562,7 +563,6 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
int error,len,size,number, ret_val; int error,len,size,number, ret_val;
ret_val = 0; ret_val = 0;
spin_lock (&atm_dev_lock);
vcc = ATM_SD(sock); vcc = ATM_SD(sock);
switch (cmd) { switch (cmd) {
case SIOCOUTQ: case SIOCOUTQ:
...@@ -600,14 +600,17 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -600,14 +600,17 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
goto done; goto done;
} }
size = 0; size = 0;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) list_for_each(p, &atm_devs)
size += sizeof(int); size += sizeof(int);
if (size > len) { if (size > len) {
spin_unlock(&atm_dev_lock);
ret_val = -E2BIG; ret_val = -E2BIG;
goto done; goto done;
} }
tmp_buf = kmalloc(size,GFP_KERNEL); tmp_buf = kmalloc(size, GFP_ATOMIC);
if (!tmp_buf) { if (!tmp_buf) {
spin_unlock(&atm_dev_lock);
ret_val = -ENOMEM; ret_val = -ENOMEM;
goto done; goto done;
} }
...@@ -616,6 +619,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -616,6 +619,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
*tmp_p++ = dev->number; *tmp_p++ = dev->number;
} }
spin_unlock(&atm_dev_lock);
ret_val = ((copy_to_user(buf, tmp_buf, size)) || ret_val = ((copy_to_user(buf, tmp_buf, size)) ||
put_user(size, &((struct atm_iobuf *) arg)->length) put_user(size, &((struct atm_iobuf *) arg)->length)
) ? -EFAULT : 0; ) ? -EFAULT : 0;
...@@ -840,7 +844,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -840,7 +844,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done;
} }
if (!(dev = atm_find_dev(number))) { if (!(dev = atm_dev_lookup(number))) {
ret_val = -ENODEV; ret_val = -ENODEV;
goto done; goto done;
} }
...@@ -851,14 +855,14 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -851,14 +855,14 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
size = strlen(dev->type)+1; size = strlen(dev->type)+1;
if (copy_to_user(buf,dev->type,size)) { if (copy_to_user(buf,dev->type,size)) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
break; break;
case ATM_GETESI: case ATM_GETESI:
size = ESI_LEN; size = ESI_LEN;
if (copy_to_user(buf,dev->esi,size)) { if (copy_to_user(buf,dev->esi,size)) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
break; break;
case ATM_SETESI: case ATM_SETESI:
...@@ -868,7 +872,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -868,7 +872,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
for (i = 0; i < ESI_LEN; i++) for (i = 0; i < ESI_LEN; i++)
if (dev->esi[i]) { if (dev->esi[i]) {
ret_val = -EEXIST; ret_val = -EEXIST;
goto done; goto done_release;
} }
} }
/* fall through */ /* fall through */
...@@ -878,20 +882,20 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -878,20 +882,20 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN)) {
ret_val = -EPERM; ret_val = -EPERM;
goto done; goto done_release;
} }
if (copy_from_user(esi,buf,ESI_LEN)) { if (copy_from_user(esi,buf,ESI_LEN)) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
memcpy(dev->esi,esi,ESI_LEN); memcpy(dev->esi,esi,ESI_LEN);
ret_val = ESI_LEN; ret_val = ESI_LEN;
goto done; goto done_release;
} }
case ATM_GETSTATZ: case ATM_GETSTATZ:
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN)) {
ret_val = -EPERM; ret_val = -EPERM;
goto done; goto done_release;
} }
/* fall through */ /* fall through */
case ATM_GETSTAT: case ATM_GETSTAT:
...@@ -899,27 +903,27 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -899,27 +903,27 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
error = fetch_stats(dev,buf,cmd == ATM_GETSTATZ); error = fetch_stats(dev,buf,cmd == ATM_GETSTATZ);
if (error) { if (error) {
ret_val = error; ret_val = error;
goto done; goto done_release;
} }
break; break;
case ATM_GETCIRANGE: case ATM_GETCIRANGE:
size = sizeof(struct atm_cirange); size = sizeof(struct atm_cirange);
if (copy_to_user(buf,&dev->ci_range,size)) { if (copy_to_user(buf,&dev->ci_range,size)) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
break; break;
case ATM_GETLINKRATE: case ATM_GETLINKRATE:
size = sizeof(int); size = sizeof(int);
if (copy_to_user(buf,&dev->link_rate,size)) { if (copy_to_user(buf,&dev->link_rate,size)) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
break; break;
case ATM_RSTADDR: case ATM_RSTADDR:
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN)) {
ret_val = -EPERM; ret_val = -EPERM;
goto done; goto done_release;
} }
atm_reset_addr(dev); atm_reset_addr(dev);
break; break;
...@@ -927,20 +931,20 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -927,20 +931,20 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
case ATM_DELADDR: case ATM_DELADDR:
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN)) {
ret_val = -EPERM; ret_val = -EPERM;
goto done; goto done_release;
} }
{ {
struct sockaddr_atmsvc addr; struct sockaddr_atmsvc addr;
if (copy_from_user(&addr,buf,sizeof(addr))) { if (copy_from_user(&addr,buf,sizeof(addr))) {
ret_val = -EFAULT; ret_val = -EFAULT;
goto done; goto done_release;
} }
if (cmd == ATM_ADDADDR) if (cmd == ATM_ADDADDR)
ret_val = atm_add_addr(dev,&addr); ret_val = atm_add_addr(dev,&addr);
else else
ret_val = atm_del_addr(dev,&addr); ret_val = atm_del_addr(dev,&addr);
goto done; goto done_release;
} }
case ATM_GETADDR: case ATM_GETADDR:
size = atm_get_addr(dev,buf,len); size = atm_get_addr(dev,buf,len);
...@@ -951,13 +955,13 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -951,13 +955,13 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
write the length" */ write the length" */
ret_val = put_user(size, ret_val = put_user(size,
&((struct atmif_sioc *) arg)->length) ? -EFAULT : 0; &((struct atmif_sioc *) arg)->length) ? -EFAULT : 0;
goto done; goto done_release;
case ATM_SETLOOP: case ATM_SETLOOP:
if (__ATM_LM_XTRMT((int) (long) buf) && if (__ATM_LM_XTRMT((int) (long) buf) &&
__ATM_LM_XTLOC((int) (long) buf) > __ATM_LM_XTLOC((int) (long) buf) >
__ATM_LM_XTRMT((int) (long) buf)) { __ATM_LM_XTRMT((int) (long) buf)) {
ret_val = -EINVAL; ret_val = -EINVAL;
goto done; goto done_release;
} }
/* fall through */ /* fall through */
case ATM_SETCIRANGE: case ATM_SETCIRANGE:
...@@ -967,18 +971,18 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -967,18 +971,18 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
case SONET_SETFRAMING: case SONET_SETFRAMING:
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN)) {
ret_val = -EPERM; ret_val = -EPERM;
goto done; goto done_release;
} }
/* fall through */ /* fall through */
default: default:
if (!dev->ops->ioctl) { if (!dev->ops->ioctl) {
ret_val = -EINVAL; ret_val = -EINVAL;
goto done; goto done_release;
} }
size = dev->ops->ioctl(dev,cmd,buf); size = dev->ops->ioctl(dev,cmd,buf);
if (size < 0) { if (size < 0) {
ret_val = (size == -ENOIOCTLCMD ? -EINVAL : size); ret_val = (size == -ENOIOCTLCMD ? -EINVAL : size);
goto done; goto done_release;
} }
} }
...@@ -987,9 +991,10 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) ...@@ -987,9 +991,10 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
-EFAULT : 0; -EFAULT : 0;
else else
ret_val = 0; ret_val = 0;
done_release:
atm_dev_release(dev);
done: done:
spin_unlock (&atm_dev_lock);
return ret_val; return ret_val;
} }
......
...@@ -554,6 +554,7 @@ static struct atm_dev lecatm_dev = { ...@@ -554,6 +554,7 @@ static struct atm_dev lecatm_dev = {
.ops = &lecdev_ops, .ops = &lecdev_ops,
.type = "lec", .type = "lec",
.number = 999, /* dummy device number */ .number = 999, /* dummy device number */
.lock = SPIN_LOCK_UNLOCKED
}; };
/* /*
......
...@@ -746,6 +746,7 @@ static struct atm_dev mpc_dev = { ...@@ -746,6 +746,7 @@ static struct atm_dev mpc_dev = {
.ops = &mpc_ops, .ops = &mpc_ops,
.type = "mpc", .type = "mpc",
.number = 42, .number = 42,
.lock = SPIN_LOCK_UNLOCKED
/* members not explicitly initialised will be 0 */ /* members not explicitly initialised will be 0 */
}; };
......
...@@ -86,6 +86,7 @@ static void dev_info(const struct atm_dev *dev,char *buf) ...@@ -86,6 +86,7 @@ static void dev_info(const struct atm_dev *dev,char *buf)
add_stats(buf,"0",&dev->stats.aal0); add_stats(buf,"0",&dev->stats.aal0);
strcat(buf," "); strcat(buf," ");
add_stats(buf,"5",&dev->stats.aal5); add_stats(buf,"5",&dev->stats.aal5);
sprintf(strchr(buf,0), "\t[%d]", atomic_read(&dev->refcnt));
strcat(buf,"\n"); strcat(buf,"\n");
} }
...@@ -163,7 +164,7 @@ static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry, ...@@ -163,7 +164,7 @@ static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
#endif #endif
static void pvc_info(struct atm_vcc *vcc,char *buf) static void pvc_info(struct atm_vcc *vcc, char *buf, int clip_info)
{ {
static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" }; static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
static const char *aal_name[] = { static const char *aal_name[] = {
...@@ -180,8 +181,7 @@ static void pvc_info(struct atm_vcc *vcc,char *buf) ...@@ -180,8 +181,7 @@ static void pvc_info(struct atm_vcc *vcc,char *buf)
class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr, class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
class_name[vcc->qos.txtp.traffic_class]); class_name[vcc->qos.txtp.traffic_class]);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (try_atm_clip_ops()) { if (clip_info && (vcc->push == atm_clip_ops->clip_push)) {
if (vcc->push == atm_clip_ops->clip_push) {
struct clip_vcc *clip_vcc = CLIP_VCC(vcc); struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
struct net_device *dev; struct net_device *dev;
...@@ -193,8 +193,6 @@ static void pvc_info(struct atm_vcc *vcc,char *buf) ...@@ -193,8 +193,6 @@ static void pvc_info(struct atm_vcc *vcc,char *buf)
else else
off += sprintf(buf+off,"None"); off += sprintf(buf+off,"None");
} }
module_put(atm_clip_ops->owner);
}
#endif #endif
strcpy(buf+off,"\n"); strcpy(buf+off,"\n");
} }
...@@ -314,16 +312,19 @@ static int atm_devices_info(loff_t pos,char *buf) ...@@ -314,16 +312,19 @@ static int atm_devices_info(loff_t pos,char *buf)
if (!pos) { if (!pos) {
return sprintf(buf,"Itf Type ESI/\"MAC\"addr " return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
"AAL(TX,err,RX,err,drop) ...\n"); "AAL(TX,err,RX,err,drop) ... [refcnt]\n");
} }
left = pos-1; left = pos-1;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
if (left-- == 0) { if (left-- == 0) {
dev_info(dev,buf); dev_info(dev,buf);
spin_unlock(&atm_dev_lock);
return strlen(buf); return strlen(buf);
} }
} }
spin_unlock(&atm_dev_lock);
return 0; return 0;
} }
...@@ -334,31 +335,50 @@ static int atm_devices_info(loff_t pos,char *buf) ...@@ -334,31 +335,50 @@ static int atm_devices_info(loff_t pos,char *buf)
static int atm_pvc_info(loff_t pos,char *buf) static int atm_pvc_info(loff_t pos,char *buf)
{ {
unsigned long flags;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
struct atm_vcc *vcc; struct atm_vcc *vcc;
int left; int left, clip_info = 0;
if (!pos) { if (!pos) {
return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) " return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
"TX(PCR,Class)\n"); "TX(PCR,Class)\n");
} }
left = pos-1; left = pos-1;
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (try_atm_clip_ops())
clip_info = 1;
#endif
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
spin_lock_irqsave(&dev->lock, flags);
for (vcc = dev->vccs; vcc; vcc = vcc->next) for (vcc = dev->vccs; vcc; vcc = vcc->next)
if (vcc->sk->family == PF_ATMPVC && if (vcc->sk->family == PF_ATMPVC && vcc->dev && !left--) {
vcc->dev && !left--) { pvc_info(vcc,buf,clip_info);
pvc_info(vcc,buf); spin_unlock_irqrestore(&dev->lock, flags);
spin_unlock(&atm_dev_lock);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (clip_info)
module_put(atm_clip_ops->owner);
#endif
return strlen(buf); return strlen(buf);
} }
spin_unlock_irqrestore(&dev->lock, flags);
} }
spin_unlock(&atm_dev_lock);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (clip_info)
module_put(atm_clip_ops->owner);
#endif
return 0; return 0;
} }
static int atm_vc_info(loff_t pos,char *buf) static int atm_vc_info(loff_t pos,char *buf)
{ {
unsigned long flags;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -369,19 +389,20 @@ static int atm_vc_info(loff_t pos,char *buf) ...@@ -369,19 +389,20 @@ static int atm_vc_info(loff_t pos,char *buf)
"Address"," Itf VPI VCI Fam Flags Reply Send buffer" "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
" Recv buffer\n"); " Recv buffer\n");
left = pos-1; left = pos-1;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
spin_lock_irqsave(&dev->lock, flags);
for (vcc = dev->vccs; vcc; vcc = vcc->next) for (vcc = dev->vccs; vcc; vcc = vcc->next)
if (!left--) { if (!left--) {
vc_info(vcc,buf); vc_info(vcc,buf);
spin_unlock_irqrestore(&dev->lock, flags);
spin_unlock(&atm_dev_lock);
return strlen(buf); return strlen(buf);
} }
spin_unlock_irqrestore(&dev->lock, flags);
} }
for (vcc = nodev_vccs; vcc; vcc = vcc->next) spin_unlock(&atm_dev_lock);
if (!left--) {
vc_info(vcc,buf);
return strlen(buf);
}
return 0; return 0;
} }
...@@ -389,6 +410,7 @@ static int atm_vc_info(loff_t pos,char *buf) ...@@ -389,6 +410,7 @@ static int atm_vc_info(loff_t pos,char *buf)
static int atm_svc_info(loff_t pos,char *buf) static int atm_svc_info(loff_t pos,char *buf)
{ {
unsigned long flags;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -397,19 +419,21 @@ static int atm_svc_info(loff_t pos,char *buf) ...@@ -397,19 +419,21 @@ static int atm_svc_info(loff_t pos,char *buf)
if (!pos) if (!pos)
return sprintf(buf,"Itf VPI VCI State Remote\n"); return sprintf(buf,"Itf VPI VCI State Remote\n");
left = pos-1; left = pos-1;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
spin_lock_irqsave(&dev->lock, flags);
for (vcc = dev->vccs; vcc; vcc = vcc->next) for (vcc = dev->vccs; vcc; vcc = vcc->next)
if (vcc->sk->family == PF_ATMSVC && !left--) { if (vcc->sk->family == PF_ATMSVC && !left--) {
svc_info(vcc,buf); svc_info(vcc,buf);
spin_unlock_irqrestore(&dev->lock, flags);
spin_unlock(&atm_dev_lock);
return strlen(buf); return strlen(buf);
} }
spin_unlock_irqrestore(&dev->lock, flags);
} }
for (vcc = nodev_vccs; vcc; vcc = vcc->next) spin_unlock(&atm_dev_lock);
if (vcc->sk->family == PF_ATMSVC && !left--) {
svc_info(vcc,buf);
return strlen(buf);
}
return 0; return 0;
} }
......
...@@ -27,10 +27,8 @@ ...@@ -27,10 +27,8 @@
LIST_HEAD(atm_devs); LIST_HEAD(atm_devs);
struct atm_vcc *nodev_vccs = NULL; spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;
extern spinlock_t atm_dev_lock;
/* Caller must hold atm_dev_lock. */
static struct atm_dev *__alloc_atm_dev(const char *type) static struct atm_dev *__alloc_atm_dev(const char *type)
{ {
struct atm_dev *dev; struct atm_dev *dev;
...@@ -42,67 +40,78 @@ static struct atm_dev *__alloc_atm_dev(const char *type) ...@@ -42,67 +40,78 @@ static struct atm_dev *__alloc_atm_dev(const char *type)
dev->type = type; dev->type = type;
dev->signal = ATM_PHY_SIG_UNKNOWN; dev->signal = ATM_PHY_SIG_UNKNOWN;
dev->link_rate = ATM_OC3_PCR; dev->link_rate = ATM_OC3_PCR;
list_add_tail(&dev->dev_list, &atm_devs); spin_lock_init(&dev->lock);
return dev; return dev;
} }
/* Caller must hold atm_dev_lock. */
static void __free_atm_dev(struct atm_dev *dev) static void __free_atm_dev(struct atm_dev *dev)
{ {
list_del(&dev->dev_list);
kfree(dev); kfree(dev);
} }
/* Caller must hold atm_dev_lock. */ static struct atm_dev *__atm_dev_lookup(int number)
struct atm_dev *atm_find_dev(int number)
{ {
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
if (dev->ops && dev->number == number) if ((dev->ops) && (dev->number == number)) {
atm_dev_hold(dev);
return dev; return dev;
} }
}
return NULL; return NULL;
} }
struct atm_dev *atm_dev_lookup(int number)
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
int number, unsigned long *flags)
{ {
struct atm_dev *dev; struct atm_dev *dev;
spin_lock(&atm_dev_lock); spin_lock(&atm_dev_lock);
dev = __atm_dev_lookup(number);
spin_unlock(&atm_dev_lock);
return dev;
}
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
int number, unsigned long *flags)
{
struct atm_dev *dev, *inuse;
dev = __alloc_atm_dev(type); dev = __alloc_atm_dev(type);
if (!dev) { if (!dev) {
printk(KERN_ERR "atm_dev_register: no space for dev %s\n", printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
type); type);
goto done; return NULL;
} }
spin_lock(&atm_dev_lock);
if (number != -1) { if (number != -1) {
if (atm_find_dev(number)) { if ((inuse = __atm_dev_lookup(number))) {
atm_dev_release(inuse);
spin_unlock(&atm_dev_lock);
__free_atm_dev(dev); __free_atm_dev(dev);
dev = NULL; return NULL;
goto done;
} }
dev->number = number; dev->number = number;
} else { } else {
dev->number = 0; dev->number = 0;
while (atm_find_dev(dev->number)) while ((inuse = __atm_dev_lookup(dev->number))) {
atm_dev_release(inuse);
dev->number++; dev->number++;
} }
dev->vccs = dev->last = NULL; }
dev->dev_data = NULL;
barrier();
dev->ops = ops; dev->ops = ops;
if (flags) if (flags)
dev->flags = *flags; dev->flags = *flags;
else else
memset(&dev->flags, 0, sizeof(dev->flags)); memset(&dev->flags, 0, sizeof(dev->flags));
memset(&dev->stats, 0, sizeof(dev->stats)); memset(&dev->stats, 0, sizeof(dev->stats));
atomic_set(&dev->refcnt, 1);
list_add_tail(&dev->dev_list, &atm_devs);
spin_unlock(&atm_dev_lock);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (ops->proc_read) { if (ops->proc_read) {
...@@ -110,33 +119,50 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, ...@@ -110,33 +119,50 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
printk(KERN_ERR "atm_dev_register: " printk(KERN_ERR "atm_dev_register: "
"atm_proc_dev_register failed for dev %s\n", "atm_proc_dev_register failed for dev %s\n",
type); type);
spin_lock(&atm_dev_lock);
list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock);
__free_atm_dev(dev); __free_atm_dev(dev);
dev = NULL; return NULL;
goto done;
} }
} }
#endif #endif
done:
spin_unlock(&atm_dev_lock);
return dev; return dev;
} }
void atm_dev_deregister(struct atm_dev *dev) void atm_dev_deregister(struct atm_dev *dev)
{ {
unsigned long warning_time;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (dev->ops->proc_read) if (dev->ops->proc_read)
atm_proc_dev_deregister(dev); atm_proc_dev_deregister(dev);
#endif #endif
spin_lock(&atm_dev_lock); spin_lock(&atm_dev_lock);
__free_atm_dev(dev); list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock); spin_unlock(&atm_dev_lock);
warning_time = jiffies;
while (atomic_read(&dev->refcnt) != 1) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ / 4);
current->state = TASK_RUNNING;
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;
}
}
__free_atm_dev(dev);
} }
void shutdown_atm_dev(struct atm_dev *dev) void shutdown_atm_dev(struct atm_dev *dev)
{ {
if (dev->vccs) { if (atomic_read(&dev->refcnt) > 1) {
set_bit(ATM_DF_CLOSE, &dev->flags); set_bit(ATM_DF_CLOSE, &dev->flags);
return; return;
} }
...@@ -161,44 +187,44 @@ struct sock *alloc_atm_vcc_sk(int family) ...@@ -161,44 +187,44 @@ struct sock *alloc_atm_vcc_sk(int family)
sock_init_data(NULL, sk); sock_init_data(NULL, sk);
memset(vcc, 0, sizeof(*vcc)); memset(vcc, 0, sizeof(*vcc));
vcc->sk = sk; vcc->sk = sk;
if (nodev_vccs)
nodev_vccs->prev = vcc;
vcc->prev = NULL;
vcc->next = nodev_vccs;
nodev_vccs = vcc;
return sk; return sk;
} }
static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev) static void unlink_vcc(struct atm_vcc *vcc)
{ {
unsigned long flags;
if (vcc->dev) {
spin_lock_irqsave(&vcc->dev->lock, flags);
if (vcc->prev) if (vcc->prev)
vcc->prev->next = vcc->next; vcc->prev->next = vcc->next;
else if (vcc->dev)
vcc->dev->vccs = vcc->next;
else else
nodev_vccs = vcc->next; vcc->dev->vccs = vcc->next;
if (vcc->next) if (vcc->next)
vcc->next->prev = vcc->prev; vcc->next->prev = vcc->prev;
else if (vcc->dev) else
vcc->dev->last = vcc->prev; vcc->dev->last = vcc->prev;
if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs && spin_unlock_irqrestore(&vcc->dev->lock, flags);
test_bit(ATM_DF_CLOSE,&vcc->dev->flags)) }
shutdown_atm_dev(vcc->dev);
} }
void free_atm_vcc_sk(struct sock *sk) void free_atm_vcc_sk(struct sock *sk)
{ {
unlink_vcc(atm_sk(sk), NULL); unlink_vcc(atm_sk(sk));
sk_free(sk); sk_free(sk);
} }
void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
{ {
unlink_vcc(vcc,dev); unsigned long flags;
unlink_vcc(vcc);
vcc->dev = dev; vcc->dev = dev;
if (dev) { if (dev) {
spin_lock_irqsave(&dev->lock, flags);
vcc->next = NULL; vcc->next = NULL;
vcc->prev = dev->last; vcc->prev = dev->last;
if (dev->vccs) if (dev->vccs)
...@@ -206,17 +232,12 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) ...@@ -206,17 +232,12 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
else else
dev->vccs = vcc; dev->vccs = vcc;
dev->last = vcc; dev->last = vcc;
} else { spin_unlock_irqrestore(&dev->lock, flags);
if (nodev_vccs)
nodev_vccs->prev = vcc;
vcc->next = nodev_vccs;
vcc->prev = NULL;
nodev_vccs = vcc;
} }
} }
EXPORT_SYMBOL(atm_dev_register); EXPORT_SYMBOL(atm_dev_register);
EXPORT_SYMBOL(atm_dev_deregister); EXPORT_SYMBOL(atm_dev_deregister);
EXPORT_SYMBOL(atm_find_dev); EXPORT_SYMBOL(atm_dev_lookup);
EXPORT_SYMBOL(shutdown_atm_dev); EXPORT_SYMBOL(shutdown_atm_dev);
EXPORT_SYMBOL(bind_vcc); EXPORT_SYMBOL(bind_vcc);
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
extern struct list_head atm_devs; extern struct list_head atm_devs;
extern struct atm_vcc *nodev_vccs; /* VCCs not linked to any device */ extern spinlock_t atm_dev_lock;
struct sock *alloc_atm_vcc_sk(int family); struct sock *alloc_atm_vcc_sk(int family);
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
struct atm_vcc *sigd = NULL; struct atm_vcc *sigd = NULL;
static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep); static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep);
extern spinlock_t atm_dev_lock;
static void sigd_put_skb(struct sk_buff *skb) static void sigd_put_skb(struct sk_buff *skb)
{ {
...@@ -211,6 +210,7 @@ static void purge_vccs(struct atm_vcc *vcc) ...@@ -211,6 +210,7 @@ static void purge_vccs(struct atm_vcc *vcc)
static void sigd_close(struct atm_vcc *vcc) static void sigd_close(struct atm_vcc *vcc)
{ {
unsigned long flags;
struct atm_dev *dev; struct atm_dev *dev;
struct list_head *p; struct list_head *p;
...@@ -219,33 +219,29 @@ static void sigd_close(struct atm_vcc *vcc) ...@@ -219,33 +219,29 @@ static void sigd_close(struct atm_vcc *vcc)
if (skb_peek(&vcc->sk->receive_queue)) if (skb_peek(&vcc->sk->receive_queue))
printk(KERN_ERR "sigd_close: closing with requests pending\n"); printk(KERN_ERR "sigd_close: closing with requests pending\n");
skb_queue_purge(&vcc->sk->receive_queue); skb_queue_purge(&vcc->sk->receive_queue);
purge_vccs(nodev_vccs);
spin_lock (&atm_dev_lock); spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
spin_lock_irqsave(&dev->lock, flags);
purge_vccs(dev->vccs); purge_vccs(dev->vccs);
spin_unlock_irqrestore(&dev->lock, flags);
} }
spin_unlock (&atm_dev_lock); spin_unlock(&atm_dev_lock);
} }
static struct atmdev_ops sigd_dev_ops = { static struct atmdev_ops sigd_dev_ops = {
.close =sigd_close, .close = sigd_close,
.send = sigd_send .send = sigd_send
}; };
static struct atm_dev sigd_dev = { static struct atm_dev sigd_dev = {
&sigd_dev_ops, .ops = &sigd_dev_ops,
NULL, /* no PHY */ .type = "sig",
"sig", /* type */ .number = 999,
999, /* dummy device number */ .lock = SPIN_LOCK_UNLOCKED
NULL,NULL, /* pretend not to have any VCCs */
NULL,NULL, /* no data */
0, /* no flags */
NULL, /* no local address */
{ 0 } /* no ESI, no statistics */
}; };
......
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