/* net/atm/resources.c - Staticly allocated resources */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ #include #include #include #include #include /* for barrier */ #include #include #include /* for struct sock */ #include /* for get_fs_long and put_fs_long */ #include "common.h" #include "resources.h" #ifndef NULL #define NULL 0 #endif struct atm_dev *atm_devs = NULL; static struct atm_dev *last_dev = NULL; struct atm_vcc *nodev_vccs = NULL; extern spinlock_t atm_dev_lock; static struct atm_dev *alloc_atm_dev(const char *type) { struct atm_dev *dev; dev = kmalloc(sizeof(*dev),GFP_KERNEL); if (!dev) return NULL; memset(dev,0,sizeof(*dev)); dev->type = type; dev->signal = ATM_PHY_SIG_UNKNOWN; dev->link_rate = ATM_OC3_PCR; dev->next = NULL; spin_lock(&atm_dev_lock); dev->prev = last_dev; if (atm_devs) last_dev->next = dev; else atm_devs = dev; last_dev = dev; spin_unlock(&atm_dev_lock); return dev; } static void free_atm_dev(struct atm_dev *dev) { spin_lock (&atm_dev_lock); if (dev->prev) dev->prev->next = dev->next; else atm_devs = dev->next; if (dev->next) dev->next->prev = dev->prev; else last_dev = dev->prev; kfree(dev); spin_unlock (&atm_dev_lock); } struct atm_dev *atm_find_dev(int number) { struct atm_dev *dev; for (dev = atm_devs; dev; dev = dev->next) if (dev->ops && dev->number == number) return dev; return NULL; } struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, int number,atm_dev_flags_t *flags) { struct atm_dev *dev; dev = alloc_atm_dev(type); if (!dev) { printk(KERN_ERR "atm_dev_register: no space for dev %s\n", type); return NULL; } if (number != -1) { if (atm_find_dev(number)) { free_atm_dev(dev); return NULL; } dev->number = number; } else { dev->number = 0; while (atm_find_dev(dev->number)) dev->number++; } dev->vccs = dev->last = NULL; dev->dev_data = NULL; barrier(); dev->ops = ops; if (flags) dev->flags = *flags; else memset(&dev->flags,0,sizeof(dev->flags)); memset((void *) &dev->stats,0,sizeof(dev->stats)); #ifdef CONFIG_PROC_FS if (ops->proc_read) if (atm_proc_dev_register(dev) < 0) { printk(KERN_ERR "atm_dev_register: " "atm_proc_dev_register failed for dev %s\n",type); spin_unlock (&atm_dev_lock); free_atm_dev(dev); return NULL; } #endif spin_unlock (&atm_dev_lock); return dev; } void atm_dev_deregister(struct atm_dev *dev) { #ifdef CONFIG_PROC_FS if (dev->ops->proc_read) atm_proc_dev_deregister(dev); #endif free_atm_dev(dev); } void shutdown_atm_dev(struct atm_dev *dev) { if (dev->vccs) { set_bit(ATM_DF_CLOSE,&dev->flags); return; } if (dev->ops->dev_close) dev->ops->dev_close(dev); atm_dev_deregister(dev); } /* Handler for sk->destruct, invoked by sk_free() */ static void atm_free_sock(struct sock *sk) { kfree(sk->protinfo.af_atm); } struct sock *alloc_atm_vcc_sk(int family) { struct sock *sk; struct atm_vcc *vcc; sk = sk_alloc(family, GFP_KERNEL, 1); if (!sk) return NULL; vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc),GFP_KERNEL); if (!vcc) { sk_free(sk); return NULL; } sock_init_data(NULL,sk); sk->destruct = atm_free_sock; memset(vcc,0,sizeof(*vcc)); vcc->sk = sk; if (nodev_vccs) nodev_vccs->prev = vcc; vcc->prev = NULL; vcc->next = nodev_vccs; nodev_vccs = vcc; return sk; } static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev) { if (vcc->prev) vcc->prev->next = vcc->next; else if (vcc->dev) vcc->dev->vccs = vcc->next; else nodev_vccs = vcc->next; if (vcc->next) vcc->next->prev = vcc->prev; else if (vcc->dev) vcc->dev->last = vcc->prev; if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs && test_bit(ATM_DF_CLOSE,&vcc->dev->flags)) shutdown_atm_dev(vcc->dev); } void free_atm_vcc_sk(struct sock *sk) { unlink_vcc(sk->protinfo.af_atm,NULL); sk_free(sk); } void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) { unlink_vcc(vcc,dev); vcc->dev = dev; if (dev) { vcc->next = NULL; vcc->prev = dev->last; if (dev->vccs) dev->last->next = vcc; else dev->vccs = vcc; dev->last = vcc; } else { 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_deregister); EXPORT_SYMBOL(atm_find_dev); EXPORT_SYMBOL(shutdown_atm_dev); EXPORT_SYMBOL(bind_vcc);