/* 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" #ifdef CONFIG_PACKET_MMAP #include #endif #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) { struct atm_vcc *vcc = sk->protinfo.af_atm; #ifdef CONFIG_PACKET_MMAP if (vcc->pg_vec) { struct tpacket_req req; memset(&req, 0, sizeof(req)); atm_set_ring(sk, &req, 1); } #endif unlink_vcc(vcc,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); #ifdef CONFIG_PACKET_MMAP /* Dirty? Well, I still did not learn better way to account * for user mmaps. */ static void atm_mm_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct inode *inode = file->f_dentry->d_inode; struct socket * sock = &inode->u.socket_i; struct sock *sk = sock->sk; if (sk) atomic_inc(&sk->protinfo.af_atm->mapped); } static void atm_mm_close(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct inode *inode = file->f_dentry->d_inode; struct socket * sock = &inode->u.socket_i; struct sock *sk = sock->sk; if (sk) atomic_dec(&sk->protinfo.af_atm->mapped); } static struct vm_operations_struct atm_mmap_ops = { open: atm_mm_open, close: atm_mm_close, }; static void free_pg_vec(unsigned long *pg_vec, unsigned order, unsigned len) { int i; for (i=0; iprotinfo.af_atm; int order = 0; int err = 0; if (req->tp_block_nr) { int i, l; int frames_per_block; /* Sanity tests and some calculations */ if ((int)req->tp_block_size <= 0) return -EINVAL; if (req->tp_block_size&(PAGE_SIZE-1)) return -EINVAL; if (req->tp_frame_size < TPACKET_HDRLEN) return -EINVAL; if (req->tp_frame_size&(TPACKET_ALIGNMENT-1)) return -EINVAL; frames_per_block = req->tp_block_size/req->tp_frame_size; if (frames_per_block <= 0) return -EINVAL; if (frames_per_block*req->tp_block_nr != req->tp_frame_nr) return -EINVAL; /* OK! */ /* Allocate page vector */ while ((PAGE_SIZE<tp_block_size) order++; err = -ENOMEM; pg_vec = kmalloc(req->tp_block_nr*sizeof(unsigned long*), GFP_KERNEL); if (pg_vec == NULL) goto out; memset(pg_vec, 0, req->tp_block_nr*sizeof(unsigned long*)); for (i=0; itp_block_nr; i++) { struct page *page, *pend; pg_vec[i] = __get_free_pages(GFP_KERNEL, order); if (!pg_vec[i]) goto out_free_pgvec; pend = virt_to_page(pg_vec[i] + (PAGE_SIZE << order) - 1); for (page = virt_to_page(pg_vec[i]); page <= pend; page++) SetPageReserved(page); } /* Page vector is allocated */ /* Draw frames */ io_vec = kmalloc(req->tp_frame_nr*sizeof(struct tpacket_hdr*), GFP_KERNEL); if (io_vec == NULL) goto out_free_pgvec; memset(io_vec, 0, req->tp_frame_nr*sizeof(struct tpacket_hdr*)); l = 0; for (i=0; itp_block_nr; i++) { unsigned long ptr = pg_vec[i]; int k; for (k=0; ktp_status = TP_STATUS_KERNEL; ptr += req->tp_frame_size; } } /* Done */ } else { if (req->tp_frame_nr) return -EINVAL; } lock_sock(sk); #if 0 /* Detach socket from network */ spin_lock(&po->bind_lock); if (po->running) dev_remove_pack(&po->prot_hook); spin_unlock(&po->bind_lock); #endif err = -EBUSY; if (closing || atomic_read(&vcc->mapped) == 0) { struct sk_buff *skb; err = 0; #define XC(a, b) ({ __typeof__ ((a)) __t; __t = (a); (a) = (b); __t; }) spin_lock_bh(&vcc->recvq.lock); pg_vec = XC(vcc->pg_vec, pg_vec); io_vec = XC(vcc->iovec, io_vec); vcc->iovmax = req->tp_frame_nr-1; vcc->head = 0; vcc->frame_size = req->tp_frame_size; spin_unlock_bh(&vcc->recvq.lock); order = XC(vcc->pg_vec_order, order); req->tp_block_nr = XC(vcc->pg_vec_len, req->tp_block_nr); vcc->pg_vec_pages = req->tp_block_size/PAGE_SIZE; while ((skb = skb_dequeue(&vcc->recvq)) != 0) { atm_return(vcc,skb->truesize); dev_kfree_skb_any(skb); } #undef XC if (atomic_read(&vcc->mapped)) printk(KERN_DEBUG "atm_mmap: vma is busy: %d\n", atomic_read(&vcc->mapped)); } #if 0 spin_lock(&po->bind_lock); if (po->running) dev_add_pack(&po->prot_hook); spin_unlock(&po->bind_lock); #endif release_sock(sk); if (io_vec) kfree(io_vec); out_free_pgvec: if (pg_vec) free_pg_vec(pg_vec, order, req->tp_block_nr); out: return err; } int atm_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma) { struct sock *sk = sock->sk; struct atm_vcc *po = sk->protinfo.af_atm; unsigned long size; unsigned long start; int err = -EINVAL; int i; if (vma->vm_pgoff) return -EINVAL; size = vma->vm_end - vma->vm_start; lock_sock(sk); if (po->pg_vec == NULL) goto out; if (size != po->pg_vec_len*po->pg_vec_pages*PAGE_SIZE) goto out; atomic_inc(&po->mapped); start = vma->vm_start; err = -EAGAIN; for (i=0; ipg_vec_len; i++) { if (remap_page_range(start, __pa(po->pg_vec[i]), po->pg_vec_pages*PAGE_SIZE, vma->vm_page_prot)) goto out; start += po->pg_vec_pages*PAGE_SIZE; } vma->vm_ops = &atm_mmap_ops; err = 0; out: release_sock(sk); return err; } #else int atm_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma) { return -EINVAL; } int atm_set_ring(struct sock *sk, struct tpacket_req *req, int closing) { return -ESUPPORT; } #endif EXPORT_SYMBOL(atm_mmap); EXPORT_SYMBOL(atm_set_ring);