Packet socket: mmapped IO: PACKET_TX_RING
Subsystems (4)
PATCH 1/1 Packet socket: mmapped IO: PACKET_TX_RING
New packet socket feature that makes packet socket more efficient for transmission.
- It reduces number of system call through a PACKET_TX_RING mechanism, based on PACKET_RX_RING (Circular buffer allocated in kernel space which is mmapped from user space).
- It minimizes CPU copy using fragmented SKB.
--
Hi All,
Before going on submitting process, I would like to get your opinion on those below points:
1#:
In order to make such feature, each packet header of the circular buffer can be set to one of those three statuses:
- TP_STATUS_USER: packet buffer needs to be transmitted.
On this status, Kernel:
- Allocates a new skb
- Attaches packet buffer pages
- Changes skb destructor
- Stores packet header pointer or index into skb
- Changes the packet header status to TP_STATUS_COPY;
- Sends it to device.
- TP_STATUS_COPY: packet transmission is ongoing
On this status, the skb destructor (called during skb release)
- Gets packet header pointer linked to skb pointer;
- Changes the packet header status to TP_STATUS_KERNEL;
- TP_STATUS_KERNEL: packet buffer has been transmitted and buffer is ready for user.
As you can see, this skb destructor needs packet header pointer related to sk_buff pointer to change header status. I've first used skb->cb (control buffer) to forward this parameter to destructor.
Unfortunately, this one seems to be overwritten in packet queue mechanism. So I've finally chosen skb->mark to store buffer index.
Can I use skb->mark in such condition?
If not, what is the best solution:
- new field in sk_buff struct?
- a sk_buff pointer array [NB_FRAME] (one index matches to one skb pointer)? (parsing is not really good for cpu load)
- other ?
2#:
Do I need to protect send() procedure with some locks?
Especially when changing status from TP_STATUS_USER to TP_STATUS_COPY to prevent kernel from sending a packet buffer twice? (If two send() are called from different threads In SMP for example)
Or is it implicit?
Thanks in advance,
Johann Baudy
--
Documentation/networking/packet_mmap.txt | 132 ++++++++-- include/linux/if_packet.h | 3 +- net/packet/af_packet.c | 422 +++++++++++++++++++++++++----- 3 files changed, 470 insertions(+), 87 deletions(-)
Documentation/networking/packet_mmap.txt +116 -16
@@ -4,8 +4,8 @@ This file documents the CONFIG_PACKET_MMAP option available with the PACKET socket interface on 2.4 and 2.6 kernels. This type of sockets is used for -capture network traffic with utilities like tcpdump or any other that uses -the libpcap library. +capture network traffic with utilities like tcpdump or any other that needs +raw acces to network interface. You can find the latest version of this document at
@@ -14,6 +14,7 @@ You can find the latest version of this document at Please send me your comments to Ulisses Alonso Camaró <[email protected]> + Johann Baudy <[email protected]> (TX RING) ------------------------------------------------------------------------------- + Why use PACKET_MMAP
@@ -25,19 +26,24 @@ to capture each packet, it requires two if you want to get packet's timestamp (like libpcap always does). In the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size -configurable circular buffer mapped in user space. This way reading packets just -needs to wait for them, most of the time there is no need to issue a single -system call. By using a shared buffer between the kernel and the user -also has the benefit of minimizing packet copies. - -It's fine to use PACKET_MMAP to improve the performance of the capture process, -but it isn't everything. At least, if you are capturing at high speeds (this -is relative to the cpu speed), you should check if the device driver of your -network interface card supports some sort of interrupt load mitigation or -(even better) if it supports NAPI, also make sure it is enabled. +configurable circular buffer mapped in user space that can be used to either +send or receive packets. This way reading packets just needs to wait for them, +most of the time there is no need to issue a single system call. Concerning +transmission, multiple packets can be sent through one system call to get the +highest bandwidth. +By using a shared buffer between the kernel and the user also has the benefit +of minimizing packet copies. + +It's fine to use PACKET_MMAP to improve the performance of the capture and +transmission process, but it isn't everything. At least, if you are capturing +at high speeds (this is relative to the cpu speed), you should check if the +device driver of your network interface card supports some sort of interrupt +load mitigation or (even better) if it supports NAPI, also make sure it is +enabled. For transmission, check the MTU (Maximum Transmission Unit) used and +supported by devices of your network. -------------------------------------------------------------------------------- -+ How to use CONFIG_PACKET_MMAP ++ How to use CONFIG_PACKET_MMAP to improve capture process -------------------------------------------------------------------------------- From the user standpoint, you should use the higher level libpcap library, which
@@ -57,7 +63,7 @@ the low level details or want to improve libpcap by including PACKET_MMAP support. -------------------------------------------------------------------------------- -+ How to use CONFIG_PACKET_MMAP directly ++ How to use CONFIG_PACKET_MMAP directly to improve capture porcess -------------------------------------------------------------------------------- From the system calls stand point, the use of PACKET_MMAP involves
@@ -66,6 +72,7 @@ the following process: [setup] socket() -------> creation of the capture socket setsockopt() ---> allocation of the circular buffer (ring) + option: PACKET_RX_RING mmap() ---------> mapping of the allocated buffer to the user process
@@ -97,13 +104,75 @@ also the mapping of the circular buffer in the user process and the use of this buffer. -------------------------------------------------------------------------------- ++ How to use CONFIG_PACKET_MMAP directly to improve transmission process +-------------------------------------------------------------------------------- +Transmission process is similar to capture as shown below. + +[setup] socket() -------> creation of the transmission socket + setsockopt() ---> allocation of the circular buffer (ring) + option: PACKET_TX_RING + bind() ---------> bind transmission socket with a network interface + mmap() ---------> mapping of the allocated buffer to the + user process + +[transmission] poll() ---------> wait for free packets (optional) + send() ---------> send all packets that are set as ready in + the ring + The flag MSG_DONTWAIT can be used to return + before end of transfer. + +[shutdown] close() --------> destruction of the transmission socket and + deallocation of all associated resources. + +Binding the socket to your network interface is mandatory (with zero copy) to +know the header size of frames used in the circular buffer. + +As capture, each frame contains two parts: + + -------------------- +| struct tpacket_hdr | Header. It contains the status of +| | of this frame +|--------------------| +| data buffer | +. . Data that will be sent over the network interface. +. . + -------------------- + + bind() associates the socket to your network interface thanks to + sll_ifindex parameter of struct sockaddr_ll. + + Initialization example: + + struct sockaddr_ll my_addr; + struct ifreq s_ifr; + ... + + strncpy (s_ifr.ifr_name, "eth0", sizeof(s_ifr.ifr_name)); + + /* get interface index of eth0 */ + ioctl(this->socket, SIOCGIFINDEX, &s_ifr); + + /* fill sockaddr_ll struct to prepare binding */ + my_addr.sll_family = AF_PACKET; + my_addr.sll_protocol = ETH_P_ALL; + my_addr.sll_ifindex = s_ifr.ifr_ifindex; + + /* bind socket to eth0 */ + bind(this->socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)); + + A complete tutorial is available at: http://wiki.gnu-log.net/ + +-------------------------------------------------------------------------------- + PACKET_MMAP settings -------------------------------------------------------------------------------- To setup PACKET_MMAP from user level code is done with a call like + - Capture process setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req)) + - Transmission process + setsockopt(fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req)) The most significant argument in the previous call is the req parameter, this parameter must to have the following structure:
@@ -117,11 +186,11 @@ this parameter must to have the following structure: }; This structure is defined in /usr/include/linux/if_packet.h and establishes a -circular buffer (ring) of unswappable memory mapped in the capture process. +circular buffer (ring) of unswappable memory. Being mapped in the capture process allows reading the captured frames and related meta-information like timestamps without requiring a system call. -Captured frames are grouped in blocks. Each block is a physically contiguous +Frames are grouped in blocks. Each block is a physically contiguous region of memory and holds tp_block_size/tp_frame_size frames. The total number of blocks is tp_block_nr. Note that tp_frame_nr is a redundant parameter because
@@ -336,6 +405,7 @@ struct tpacket_hdr). If this field is 0 means that the frame is ready to be used for the kernel, If not, there is a frame the user can read and the following flags apply: ++++ Capture process: from include/linux/if_packet.h #define TP_STATUS_COPY 2
@@ -391,6 +461,36 @@ packets are in the ring: It doesn't incur in a race condition to first check the status value and then poll for frames. + +++ Transmission process +Those defines are also used for transmission: + + #define TP_STATUS_KERNEL 0 // Frame is available + #define TP_STATUS_USER 1 // Frame will be sent on next send() + #define TP_STATUS_COPY 2 // Frame is currently in transmission + +First, the kernel initializes all frames to TP_STATUS_KERNEL. To send a packet, +the user fills a data buffer of an available frame, sets tp_len to current +data buffer size and sets its status field to TP_STATUS_USER. This can be done +on multiple frames. Once the user is ready to transmit, it calls send(). +Then all buffers with status equal to TP_STATUS_USER are forwarded to the +network device. The kernel updates each status of sent frames with +TP_STATUS_COPY until the end of transfer. +At the end of each transfer, buffer status returns to TP_STATUS_KERNEL. + + header->tp_len = in_i_size; + header->tp_status = TP_STATUS_USER; + retval = send(this->socket, NULL, 0, 0); + +The user can also use poll() to check if a buffer is available: +(status == TP_STATUS_KERNEL) + + struct pollfd pfd; + pfd.fd = fd; + pfd.revents = 0; + pfd.events = POLLOUT; + retval = poll(&pfd, 1, timeout); + -------------------------------------------------------------------------------- + THANKS --------------------------------------------------------------------------------
include/linux/if_packet.h +2 -1
@@ -46,6 +46,7 @@ struct sockaddr_ll#define PACKET_VERSION 10#define PACKET_HDRLEN 11#define PACKET_RESERVE 12 +#define PACKET_TX_RING 13struct tpacket_stats {
@@ -100,7 +101,7 @@ struct tpacket2_hdrenum tpacket_versions {TPACKET_V1 , -TPACKET_V2 , +TPACKET_V2 };/*
net/packet/af_packet.c +352 -70
605 lines not on this page · raw message
David Miller
maintainerFrom: Johann Baudy <[email protected]>
Date: Mon, 27 Oct 2008 10:33:25 +0100
include/linux/if_packet.h struct tpacket2_hdr · line 107
Quoted 9 lines
include/linux/if_packet.h +1
-1@@ -100,7 +101,7 @@ struct tpacket2_hdrenum tpacket_versions {TPACKET_V1 , -TPACKET_V2 , +TPACKET_V2 };/*
Please don't do this, the comma there is intentional.
If a future patch adds a new version, the diff will be the addition of one new line, instead of one changed line and one new line.