diff options
Diffstat (limited to 'net/packet/af_packet.c')
| -rw-r--r-- | net/packet/af_packet.c | 115 |
1 files changed, 61 insertions, 54 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 499ae3df4a44..3e2462760413 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
| @@ -1587,23 +1587,47 @@ static inline struct page *pg_vec_endpage(char *one_pg_vec, unsigned int order) | |||
| 1587 | return virt_to_page(one_pg_vec + (PAGE_SIZE << order) - 1); | 1587 | return virt_to_page(one_pg_vec + (PAGE_SIZE << order) - 1); |
| 1588 | } | 1588 | } |
| 1589 | 1589 | ||
| 1590 | static void free_pg_vec(char **pg_vec, unsigned order, unsigned len) | 1590 | static void free_pg_vec(char **pg_vec, unsigned int order, unsigned int len) |
| 1591 | { | 1591 | { |
| 1592 | int i; | 1592 | int i; |
| 1593 | 1593 | ||
| 1594 | for (i=0; i<len; i++) { | 1594 | for (i = 0; i < len; i++) { |
| 1595 | if (pg_vec[i]) { | 1595 | if (likely(pg_vec[i])) |
| 1596 | struct page *page, *pend; | 1596 | free_pages((unsigned long) pg_vec[i], order); |
| 1597 | |||
| 1598 | pend = pg_vec_endpage(pg_vec[i], order); | ||
| 1599 | for (page = virt_to_page(pg_vec[i]); page <= pend; page++) | ||
| 1600 | ClearPageReserved(page); | ||
| 1601 | free_pages((unsigned long)pg_vec[i], order); | ||
| 1602 | } | ||
| 1603 | } | 1597 | } |
| 1604 | kfree(pg_vec); | 1598 | kfree(pg_vec); |
| 1605 | } | 1599 | } |
| 1606 | 1600 | ||
| 1601 | static inline char *alloc_one_pg_vec_page(unsigned long order) | ||
| 1602 | { | ||
| 1603 | return (char *) __get_free_pages(GFP_KERNEL | __GFP_COMP | __GFP_ZERO, | ||
| 1604 | order); | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | static char **alloc_pg_vec(struct tpacket_req *req, int order) | ||
| 1608 | { | ||
| 1609 | unsigned int block_nr = req->tp_block_nr; | ||
| 1610 | char **pg_vec; | ||
| 1611 | int i; | ||
| 1612 | |||
| 1613 | pg_vec = kzalloc(block_nr * sizeof(char *), GFP_KERNEL); | ||
| 1614 | if (unlikely(!pg_vec)) | ||
| 1615 | goto out; | ||
| 1616 | |||
| 1617 | for (i = 0; i < block_nr; i++) { | ||
| 1618 | pg_vec[i] = alloc_one_pg_vec_page(order); | ||
| 1619 | if (unlikely(!pg_vec[i])) | ||
| 1620 | goto out_free_pgvec; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | out: | ||
| 1624 | return pg_vec; | ||
| 1625 | |||
| 1626 | out_free_pgvec: | ||
| 1627 | free_pg_vec(pg_vec, order, block_nr); | ||
| 1628 | pg_vec = NULL; | ||
| 1629 | goto out; | ||
| 1630 | } | ||
| 1607 | 1631 | ||
| 1608 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing) | 1632 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing) |
| 1609 | { | 1633 | { |
| @@ -1617,64 +1641,46 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing | |||
| 1617 | 1641 | ||
| 1618 | /* Sanity tests and some calculations */ | 1642 | /* Sanity tests and some calculations */ |
| 1619 | 1643 | ||
| 1620 | if (po->pg_vec) | 1644 | if (unlikely(po->pg_vec)) |
| 1621 | return -EBUSY; | 1645 | return -EBUSY; |
| 1622 | 1646 | ||
| 1623 | if ((int)req->tp_block_size <= 0) | 1647 | if (unlikely((int)req->tp_block_size <= 0)) |
| 1624 | return -EINVAL; | 1648 | return -EINVAL; |
| 1625 | if (req->tp_block_size&(PAGE_SIZE-1)) | 1649 | if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) |
| 1626 | return -EINVAL; | 1650 | return -EINVAL; |
| 1627 | if (req->tp_frame_size < TPACKET_HDRLEN) | 1651 | if (unlikely(req->tp_frame_size < TPACKET_HDRLEN)) |
| 1628 | return -EINVAL; | 1652 | return -EINVAL; |
| 1629 | if (req->tp_frame_size&(TPACKET_ALIGNMENT-1)) | 1653 | if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) |
| 1630 | return -EINVAL; | 1654 | return -EINVAL; |
| 1631 | 1655 | ||
| 1632 | po->frames_per_block = req->tp_block_size/req->tp_frame_size; | 1656 | po->frames_per_block = req->tp_block_size/req->tp_frame_size; |
| 1633 | if (po->frames_per_block <= 0) | 1657 | if (unlikely(po->frames_per_block <= 0)) |
| 1634 | return -EINVAL; | 1658 | return -EINVAL; |
| 1635 | if (po->frames_per_block*req->tp_block_nr != req->tp_frame_nr) | 1659 | if (unlikely((po->frames_per_block * req->tp_block_nr) != |
| 1660 | req->tp_frame_nr)) | ||
| 1636 | return -EINVAL; | 1661 | return -EINVAL; |
| 1637 | /* OK! */ | ||
| 1638 | |||
| 1639 | /* Allocate page vector */ | ||
| 1640 | while ((PAGE_SIZE<<order) < req->tp_block_size) | ||
| 1641 | order++; | ||
| 1642 | 1662 | ||
| 1643 | err = -ENOMEM; | 1663 | err = -ENOMEM; |
| 1644 | 1664 | order = get_order(req->tp_block_size); | |
| 1645 | pg_vec = kmalloc(req->tp_block_nr*sizeof(char *), GFP_KERNEL); | 1665 | pg_vec = alloc_pg_vec(req, order); |
| 1646 | if (pg_vec == NULL) | 1666 | if (unlikely(!pg_vec)) |
| 1647 | goto out; | 1667 | goto out; |
| 1648 | memset(pg_vec, 0, req->tp_block_nr*sizeof(char **)); | ||
| 1649 | |||
| 1650 | for (i=0; i<req->tp_block_nr; i++) { | ||
| 1651 | struct page *page, *pend; | ||
| 1652 | pg_vec[i] = (char *)__get_free_pages(GFP_KERNEL, order); | ||
| 1653 | if (!pg_vec[i]) | ||
| 1654 | goto out_free_pgvec; | ||
| 1655 | |||
| 1656 | pend = pg_vec_endpage(pg_vec[i], order); | ||
| 1657 | for (page = virt_to_page(pg_vec[i]); page <= pend; page++) | ||
| 1658 | SetPageReserved(page); | ||
| 1659 | } | ||
| 1660 | /* Page vector is allocated */ | ||
| 1661 | 1668 | ||
| 1662 | l = 0; | 1669 | l = 0; |
| 1663 | for (i=0; i<req->tp_block_nr; i++) { | 1670 | for (i = 0; i < req->tp_block_nr; i++) { |
| 1664 | char *ptr = pg_vec[i]; | 1671 | char *ptr = pg_vec[i]; |
| 1665 | struct tpacket_hdr *header; | 1672 | struct tpacket_hdr *header; |
| 1666 | int k; | 1673 | int k; |
| 1667 | 1674 | ||
| 1668 | for (k=0; k<po->frames_per_block; k++) { | 1675 | for (k = 0; k < po->frames_per_block; k++) { |
| 1669 | 1676 | header = (struct tpacket_hdr *) ptr; | |
| 1670 | header = (struct tpacket_hdr*)ptr; | ||
| 1671 | header->tp_status = TP_STATUS_KERNEL; | 1677 | header->tp_status = TP_STATUS_KERNEL; |
| 1672 | ptr += req->tp_frame_size; | 1678 | ptr += req->tp_frame_size; |
| 1673 | } | 1679 | } |
| 1674 | } | 1680 | } |
| 1675 | /* Done */ | 1681 | /* Done */ |
| 1676 | } else { | 1682 | } else { |
| 1677 | if (req->tp_frame_nr) | 1683 | if (unlikely(req->tp_frame_nr)) |
| 1678 | return -EINVAL; | 1684 | return -EINVAL; |
| 1679 | } | 1685 | } |
| 1680 | 1686 | ||
| @@ -1701,7 +1707,7 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing | |||
| 1701 | 1707 | ||
| 1702 | spin_lock_bh(&sk->sk_receive_queue.lock); | 1708 | spin_lock_bh(&sk->sk_receive_queue.lock); |
| 1703 | pg_vec = XC(po->pg_vec, pg_vec); | 1709 | pg_vec = XC(po->pg_vec, pg_vec); |
| 1704 | po->frame_max = req->tp_frame_nr-1; | 1710 | po->frame_max = (req->tp_frame_nr - 1); |
| 1705 | po->head = 0; | 1711 | po->head = 0; |
| 1706 | po->frame_size = req->tp_frame_size; | 1712 | po->frame_size = req->tp_frame_size; |
| 1707 | spin_unlock_bh(&sk->sk_receive_queue.lock); | 1713 | spin_unlock_bh(&sk->sk_receive_queue.lock); |
| @@ -1728,7 +1734,6 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing | |||
| 1728 | 1734 | ||
| 1729 | release_sock(sk); | 1735 | release_sock(sk); |
| 1730 | 1736 | ||
| 1731 | out_free_pgvec: | ||
| 1732 | if (pg_vec) | 1737 | if (pg_vec) |
| 1733 | free_pg_vec(pg_vec, order, req->tp_block_nr); | 1738 | free_pg_vec(pg_vec, order, req->tp_block_nr); |
| 1734 | out: | 1739 | out: |
| @@ -1755,17 +1760,19 @@ static int packet_mmap(struct file *file, struct socket *sock, struct vm_area_st | |||
| 1755 | if (size != po->pg_vec_len*po->pg_vec_pages*PAGE_SIZE) | 1760 | if (size != po->pg_vec_len*po->pg_vec_pages*PAGE_SIZE) |
| 1756 | goto out; | 1761 | goto out; |
| 1757 | 1762 | ||
| 1758 | atomic_inc(&po->mapped); | ||
| 1759 | start = vma->vm_start; | 1763 | start = vma->vm_start; |
| 1760 | err = -EAGAIN; | 1764 | for (i = 0; i < po->pg_vec_len; i++) { |
| 1761 | for (i=0; i<po->pg_vec_len; i++) { | 1765 | struct page *page = virt_to_page(po->pg_vec[i]); |
| 1762 | if (remap_pfn_range(vma, start, | 1766 | int pg_num; |
| 1763 | __pa(po->pg_vec[i]) >> PAGE_SHIFT, | 1767 | |
| 1764 | po->pg_vec_pages*PAGE_SIZE, | 1768 | for (pg_num = 0; pg_num < po->pg_vec_pages; pg_num++, page++) { |
| 1765 | vma->vm_page_prot)) | 1769 | err = vm_insert_page(vma, start, page); |
| 1766 | goto out; | 1770 | if (unlikely(err)) |
| 1767 | start += po->pg_vec_pages*PAGE_SIZE; | 1771 | goto out; |
| 1772 | start += PAGE_SIZE; | ||
| 1773 | } | ||
| 1768 | } | 1774 | } |
| 1775 | atomic_inc(&po->mapped); | ||
| 1769 | vma->vm_ops = &packet_mmap_ops; | 1776 | vma->vm_ops = &packet_mmap_ops; |
| 1770 | err = 0; | 1777 | err = 0; |
| 1771 | 1778 | ||
