--- zzzz-none-000/linux-2.4.17/net/atm/resources.c 2001-11-09 22:11:15.000000000 +0000 +++ sangam-fb-401/linux-2.4.17/net/atm/resources.c 2005-06-14 11:09:35.000000000 +0000 @@ -15,6 +15,9 @@ #include "common.h" #include "resources.h" +#ifdef CONFIG_PACKET_MMAP +#include +#endif #ifndef NULL @@ -186,7 +189,15 @@ void free_atm_vcc_sk(struct sock *sk) { - unlink_vcc(sk->protinfo.af_atm,NULL); + 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); } @@ -216,3 +227,234 @@ 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);