Linux C语言:用零拷贝技术实现TCP代理(源代码+测试服务端客户端代码)
目录
test-server-client
Makefile
eproxy-original.c
eproxy.c
list.h
https://github.com/Rtoax/test/tree/master/zero-copy/github/eproxy
test-server-client
点击查看服务端客户端代码
Makefile
CFLAGS := -g -Wall -O2
all:
gcc eproxy.c -o eproxy.out ${CFLAGS} -DDEBUG
clean:
rm -f eproxy.out
eproxy-original.c
/*
* Copyright (c) 2013, Intel Corporation
* Author: Andi Kleen
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Simple port forwarder
* proxy inport outip outport
* Uses pipes to splice two sockets together. This should give something
* approaching zero copy, if the NIC driver is capable.
* This method is rather file descriptor intensive (4 fds/conn), so make sure you
* have enough.
* Written 2012 by Andi Kleen
*/
#define _GNU_SOURCE 1
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include "list.h"
#define err(x) perror(x), exit(1)
#define NEW(x) ((x) = xmalloc(sizeof(*(x))))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
int connection_timeout = 5; /* XXX configurable */
void oom(void)
{
fprintf(stderr, "Out of memory\n");
exit(1);
}
void *xmalloc(size_t size)
{
void *p = calloc(size, 1);
if (!p)
oom();
return p;
}
void *xrealloc(void *old, size_t size)
{
void *p = realloc(old, size);
if (!p)
oom();
return p;
}
struct addrinfo *resolve(char *name, char *port, int flags)
{
int ret;
struct addrinfo *adr;
struct addrinfo hint = { .ai_flags = flags };
ret = getaddrinfo(name, port, &hint, &adr);
if (ret) {
fprintf(stderr, "proxy: Cannot resolve %s %s: %s\n",
name, port, gai_strerror(ret));
exit(1);
}
return adr;
}
void setnonblock(int fd, int *cache)
{
int flags;
if (!cache || *cache == -1) {
flags = fcntl(fd, F_GETFL, 0);
if (cache)
*cache = flags;
} else
flags = *cache;
fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
struct buffer {
int pipe[2];
int bytes;
};
struct conn {
struct conn *other;
int fd;
struct buffer buf;
time_t expire;
struct list_head expire_node;
};
LIST_HEAD(expire_list);
#define MIN_EVENTS 32
struct epoll_event *events;
int num_events, max_events;
int epoll_add(int efd, int fd, int revents, void *conn)
{
struct epoll_event ev = { .events = revents, .data.ptr = conn };
if (++num_events >= max_events) {
max_events = MAX(max_events * 2, MIN_EVENTS);
events = xrealloc(events,
sizeof(struct epoll_event) * max_events);
}
return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}
int epoll_del(int efd, int fd)
{
num_events--;
assert(num_events >= 0);
return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (void *)1L);
}
/* Create buffer between two connections */
struct buffer *newbuffer(struct buffer *buf)
{
if (pipe2(buf->pipe, O_NONBLOCK) < 0) {
perror("pipe");
return NULL;
}
return buf;
}
void delbuffer(struct buffer *buf)
{
close(buf->pipe[0]);
close(buf->pipe[1]);
}
void delconn(int efd, struct conn *conn)
{
list_del(&conn->expire_node);
delbuffer(&conn->buf);
epoll_del(efd, conn->fd);
close(conn->fd);
free(conn);
}
struct conn *newconn(int efd, int fd, time_t now)
{
struct conn *conn;
NEW(conn);
conn->fd = fd;
if (!newbuffer(&conn->buf)) {
delconn(efd, conn);
return NULL;
}
if (epoll_add(efd, fd, EPOLLIN|EPOLLOUT|EPOLLET, conn) < 0) {
perror("epoll");
delconn(efd, conn);
return NULL;
}
conn->expire = now + connection_timeout;
list_add_tail(&conn->expire_node, &expire_list);
return conn;
}
/* Process incoming connection. */
void new_request(int efd, int lfd, int *cache, time_t now)
{
int newsk = accept(lfd, NULL, NULL);
if (newsk < 0) {
perror("accept");
return;
}
// xxx log
setnonblock(newsk, cache);
newconn(efd, newsk, now);
}
/* Open outgoing connection */
struct conn *
openconn(int efd, struct addrinfo *host, int *cache, struct conn *other,
time_t now)
{
int outfd = socket(host->ai_family, SOCK_STREAM, 0);
if (outfd < 0)
return NULL;
setnonblock(outfd, cache);
int n = connect(outfd, host->ai_addr, host->ai_addrlen);
if (n < 0 && errno != EINPROGRESS) {
perror("connect");
close(outfd);
return NULL;
}
struct conn *conn = newconn(efd, outfd, now);
if (conn) {
conn->other = other;
other->other = conn;
}
return conn;
}
#define BUFSZ 16384 /* XXX */
/* Move from socket to pipe */
bool move_data_in(int srcfd, struct buffer *buf)
{
for (;;) {
int n = splice(srcfd, NULL, buf->pipe[1], NULL,
BUFSZ, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
if (n > 0)
buf->bytes += n;
if (n == 0)
return false;
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return true;
return false;
}
}
return true;
}
/* From pipe to socket */
bool move_data_out(struct buffer *buf, int dstfd)
{
while (buf->bytes > 0) {
int bytes = buf->bytes;
if (bytes > BUFSZ)
bytes = BUFSZ;
int n = splice(buf->pipe[0], NULL, dstfd, NULL,
bytes, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
if (n == 0)
break;
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
return false;
}
buf->bytes -= n;
}
/* bytes > 0, add dst to epoll set */
/* else remove if it was added */
return true;
}
void closeconn(int efd, struct conn *conn)
{
if (conn->other)
delconn(efd, conn->other);
delconn(efd, conn);
}
int expire_connections(int efd, time_t now)
{
struct conn *conn, *tmp;
list_for_each_entry_safe (conn, tmp, &expire_list, expire_node) {
if (conn->expire > now)
return (conn->expire - now) * 1000;
closeconn(efd, conn);
}
return -1;
}
void touch_conn(struct conn *conn, time_t now)
{
conn->expire = now + connection_timeout;
list_del(&conn->expire_node);
list_add_tail(&conn->expire_node, &expire_list);
}
int listen_socket(int efd, char *lname, char *port)
{
struct addrinfo *laddr = resolve(lname, port, AI_PASSIVE);
int lfd = socket(laddr->ai_family, SOCK_STREAM, 0);
if (lfd < 0)
err("socket");
int opt = 1;
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0)
err("SO_REUSEADDR");
if (bind(lfd, laddr->ai_addr, laddr->ai_addrlen) < 0)
err("bind");
if (listen(lfd, 20) < 0)
err("listen");
setnonblock(lfd, NULL);
freeaddrinfo(laddr);
if (epoll_add(efd, lfd, EPOLLIN, NULL) < 0)
err("epoll add listen fd");
return lfd;
}
int main(int ac, char **av)
{
if (ac != 4 && ac != 5) {
fprintf(stderr,
"Usage: proxy inport outhost outport [listenaddr]\n");
exit(1);
}
struct addrinfo *outhost = resolve(av[2], av[3], 0);
int efd = epoll_create(10);
if (efd < 0)
err("epoll_create");
int lfd = listen_socket(efd, av[4] ? av[4] : "0.0.0.0", av[1]);
int cache_in = -1, cache_out = -1;
int timeo = -1;
for (;;) {
int nfds = epoll_wait(efd, events, num_events, timeo);
if (nfds < 0) {
perror("epoll");
continue;
}
time_t now = time(NULL);
int i;
for (i = 0; i < nfds; i++) {
struct epoll_event *ev = &events[i];
struct conn *conn = ev->data.ptr;
/* listen socket */
if (conn == NULL) {
if (ev->events & EPOLLIN)
new_request(efd, lfd, &cache_in, now);
continue;
}
if (ev->events & (EPOLLERR|EPOLLHUP)) {
closeconn(efd, conn);
continue;
}
struct conn *other = conn->other;
/* No attempt for partial close right now */
if (ev->events & EPOLLIN) {
touch_conn(conn, now);
if (!other)
other = openconn(efd, outhost, &cache_out,
conn, now);
bool in = move_data_in(conn->fd, &conn->buf);
bool out = move_data_out(&conn->buf, other->fd);
if (!in || !out) {
closeconn(efd, conn);
continue;
}
touch_conn(other, now);
}
if ((ev->events & EPOLLOUT) && other) {
if (!move_data_out(&other->buf, conn->fd))
delconn(efd, conn);
else
touch_conn(conn, now);
/* When the pipe filled up could have
lost input events. Unfortunately
splice doesn't tell us which end
was responsible for 0, so have to ask
explicitely. */
int len = 0;
if (ioctl(other->fd, FIONREAD, &len) < 0)
perror("ioctl");
if (len > 0) {
if (!move_data_in(other->fd,
&other->buf))
closeconn(efd, other);
}
}
}
timeo = expire_connections(efd, now);
}
return 0;
}
eproxy.c
/*
* Copyright (c) 2013, Intel Corporation
* Author: Andi Kleen
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Simple port forwarder
* proxy inport outip outport
* Uses pipes to splice two sockets together. This should give something
* approaching zero copy, if the NIC driver is capable.
* This method is rather file descriptor intensive (4 fds/eproxy_conn), so make sure you
* have enough.
* Written 2012 by Andi Kleen
*/
#define _GNU_SOURCE 1
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include "list.h"
#define err(x) perror(x), exit(1)
#define NEW(x) ((x) = eproxy_xmalloc(sizeof(*(x))))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#ifdef DEBUG
#define eproxy_debug(fmt...) do{\
fprintf(stderr,"[%s:%d]:", __func__, __LINE__);\
fprintf(stderr,fmt);\
}while(0)
#else
#define eproxy_debug(fmt...)
#endif
struct eproxy_buffer {
int pipe[2];
int bytes;
};
struct eproxy_conn {
struct eproxy_conn *other;
int fd;
struct eproxy_buffer buf;
time_t expire;
struct list_head expire_node;
};
LIST_HEAD(expire_list);
#define eproxy_MIN_EVENTS 32
struct epoll_event *eproxy_events;
int eproxy_num_events, eproxy_max_events;
int connection_timeout = 5; /* XXX configurable */
void eproxy_oom(void)
{
fprintf(stderr, "Out of memory\n");
exit(1);
}
void *eproxy_xmalloc(size_t size)
{
void *p = calloc(size, 1);
if (!p)
eproxy_oom();
return p;
}
void *eproxy_xrealloc(void *old, size_t size)
{
void *p = realloc(old, size);
if (!p)
eproxy_oom();
return p;
}
struct addrinfo *eproxy_resolve(char *name, char *port, int flags)
{
eproxy_debug("\n");
int ret;
struct addrinfo *adr;
struct addrinfo hint = { .ai_flags = flags };
ret = getaddrinfo(name, port, &hint, &adr);
if (ret) {
fprintf(stderr, "proxy: Cannot eproxy_resolve %s %s: %s\n",
name, port, gai_strerror(ret));
exit(1);
}
return adr;
}
void eproxy_setnonblock(int fd, int *cache)
{
eproxy_debug("\n");
int flags;
if (!cache || *cache == -1) {
flags = fcntl(fd, F_GETFL, 0);
if (cache)
*cache = flags;
} else
flags = *cache;
fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
int epoll_add(int efd, int fd, int revents, void *eproxy_conn)
{
eproxy_debug("\n");
struct epoll_event ev = { .events = revents, .data.ptr = eproxy_conn };
if (++eproxy_num_events >= eproxy_max_events) {
eproxy_max_events = MAX(eproxy_max_events * 2, eproxy_MIN_EVENTS);
eproxy_events = eproxy_xrealloc(eproxy_events,
sizeof(struct epoll_event) * eproxy_max_events);
}
return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}
int epoll_del(int efd, int fd)
{
eproxy_debug("\n");
eproxy_num_events--;
assert(eproxy_num_events >= 0);
return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (void *)1L);
}
/* Create eproxy_buffer between two connections */
struct eproxy_buffer *eproxy_newbuffer(struct eproxy_buffer *buf)
{
eproxy_debug("\n");
if (pipe2(buf->pipe, O_NONBLOCK) < 0) {
perror("pipe");
return NULL;
}
return buf;
}
void eproxy_delbuffer(struct eproxy_buffer *buf)
{
eproxy_debug("\n");
close(buf->pipe[0]);
close(buf->pipe[1]);
}
void eproxy_delconn(int efd, struct eproxy_conn *conn)
{
eproxy_debug("%p:%p\n", &conn->expire_node, &expire_list);
// if(!list_empty(&expire_list))
list_del(&conn->expire_node);
eproxy_debug("\n");
eproxy_delbuffer(&conn->buf);
eproxy_debug("\n");
epoll_del(efd, conn->fd);
close(conn->fd);
free(conn);
}
struct eproxy_conn *eproxy_newconn(int efd, int fd, time_t now)
{
eproxy_debug("\n");
struct eproxy_conn *conn;
NEW(conn);
conn->fd = fd;
if (!eproxy_newbuffer(&conn->buf)) {
eproxy_delconn(efd, conn);
return NULL;
}
if (epoll_add(efd, fd, EPOLLIN|EPOLLOUT|EPOLLET, conn) < 0) {
perror("epoll");
eproxy_delconn(efd, conn);
return NULL;
}
conn->expire = now + connection_timeout;
list_add_tail(&conn->expire_node, &expire_list);
return conn;
}
/* Process incoming connection. */
void eproxy_new_request(int efd, int lfd, int *cache, time_t now)
{
eproxy_debug("\n");
int newsk = accept(lfd, NULL, NULL);
if (newsk < 0) {
perror("accept");
return;
}
// xxx log
eproxy_setnonblock(newsk, cache);
eproxy_newconn(efd, newsk, now);
}
/* Open outgoing connection */
struct eproxy_conn *
eproxy_openconn(int efd, struct addrinfo *host, int *cache, struct eproxy_conn *other,
time_t now)
{
eproxy_debug("\n");
int outfd = socket(host->ai_family, SOCK_STREAM, 0);
if (outfd < 0)
return NULL;
eproxy_setnonblock(outfd, cache);
int n = connect(outfd, host->ai_addr, host->ai_addrlen);
if (n < 0 && errno != EINPROGRESS) {
perror("connect");
close(outfd);
return NULL;
}
struct eproxy_conn *conn = eproxy_newconn(efd, outfd, now);
if (conn) {
conn->other = other;
other->other = conn;
}
return conn;
}
#define eproxy_BUFSZ 16384 /* XXX */
/* Move from socket to pipe */
bool eproxy_move_data_in(int srcfd, struct eproxy_buffer *buf)
{
eproxy_debug("\n");
for (;;) {
int n = splice(srcfd, NULL, buf->pipe[1], NULL,
eproxy_BUFSZ, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
if (n > 0)
buf->bytes += n;
if (n == 0)
return false;
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return true;
return false;
}
}
return true;
}
/* From pipe to socket */
bool eproxy_move_data_out(struct eproxy_buffer *buf, int dstfd)
{
eproxy_debug("\n");
while (buf->bytes > 0) {
int bytes = buf->bytes;
if (bytes > eproxy_BUFSZ)
bytes = eproxy_BUFSZ;
int n = splice(buf->pipe[0], NULL, dstfd, NULL,
bytes, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
if (n == 0)
break;
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
return false;
}
buf->bytes -= n;
}
/* bytes > 0, add dst to epoll set */
/* else remove if it was added */
return true;
}
void eproxy_closeconn(int efd, struct eproxy_conn *conn)
{
eproxy_debug("\n");
// if (conn->other){
// eproxy_debug("Del other CONN\n");
// eproxy_delconn(efd, conn->other);
//
// }
eproxy_debug("Del CONN\n");
eproxy_delconn(efd, conn);
}
int eproxy_expire_connections(int efd, time_t now)
{
eproxy_debug("\n");
struct eproxy_conn *conn, *tmp;
// if(!list_empty(&expire_list))
list_for_each_entry_safe (conn, tmp, &expire_list, expire_node) {
if (conn->expire > now)
return (conn->expire - now) * 1000;
eproxy_closeconn(efd, conn);
conn = NULL;
}
return -1;
}
void eproxy_touch_conn(struct eproxy_conn *conn, time_t now)
{
eproxy_debug("\n");
conn->expire = now + connection_timeout;
list_del(&conn->expire_node);
list_add_tail(&conn->expire_node, &expire_list);
}
int eproxy_listen_socket(int efd, char *lname, char *port)
{
eproxy_debug("\n");
struct addrinfo *laddr = eproxy_resolve(lname, port, AI_PASSIVE);
int lfd = socket(laddr->ai_family, SOCK_STREAM, 0);
if (lfd < 0)
err("socket");
int opt = 1;
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0)
err("SO_REUSEADDR");
if (bind(lfd, laddr->ai_addr, laddr->ai_addrlen) < 0)
err("bind");
if (listen(lfd, 20) < 0)
err("listen");
eproxy_setnonblock(lfd, NULL);
freeaddrinfo(laddr);
if (epoll_add(efd, lfd, EPOLLIN, NULL) < 0)
err("epoll add listen fd");
return lfd;
}
int main(int ac, char **av)
{
eproxy_debug("\n");
if (ac != 4 && ac != 5) {
fprintf(stderr,
"Usage: proxy inport outhost outport [listenaddr]\n");
exit(1);
}
/* 创建一个转发的目的地址信息 */
struct addrinfo *outhost = eproxy_resolve(av[2], av[3], 0);
int efd = epoll_create(10);
if (efd < 0)
err("epoll_create");
/* */
int lfd = eproxy_listen_socket(efd, av[4] ? av[4] : "0.0.0.0", av[1]);
int cache_in = -1, cache_out = -1;
int timeo = -1;
for (;;) {
int nfds = epoll_wait(efd, eproxy_events, eproxy_num_events, timeo);
if (nfds <= 0) {
eproxy_debug("epoll_wait return %d, %s\n", nfds, strerror(errno));
continue;
}
/* 获取当前时间 */
time_t now = time(NULL);
eproxy_debug("nfds = %d, now = %ld.\n", nfds, now);
int i;
for (i = 0; i < nfds; i++) {
eproxy_debug("nfds = %d.\n", nfds);
struct epoll_event *ev = &eproxy_events[i];
struct eproxy_conn *conn = ev->data.ptr;
/* listen socket */
if (conn == NULL) {
if (ev->events & EPOLLIN) {
eproxy_debug("A New Client.\n");
eproxy_new_request(efd, lfd, &cache_in, now);
}
eproxy_debug("A New Client Done.\n");
continue;
}
eproxy_debug("-----------------------\n");
if (ev->events & (EPOLLERR|EPOLLHUP)) {
eproxy_debug("Close A Client.\n");
eproxy_closeconn(efd, conn);
continue;
}
eproxy_debug("-----------------------\n");
struct eproxy_conn *other = conn->other;
/* No attempt for partial close right now */
if (ev->events & EPOLLIN) {
eproxy_debug("Gotta something IN.\n");
eproxy_touch_conn(conn, now);
if (!other)
other = eproxy_openconn(efd, outhost, &cache_out,
conn, now);
bool in = eproxy_move_data_in(conn->fd, &conn->buf);
bool out = eproxy_move_data_out(&conn->buf, other->fd);
if (!in || !out) {
eproxy_closeconn(efd, conn);
continue;
}
eproxy_touch_conn(other, now);
}
if ((ev->events & EPOLLOUT) && other) {
eproxy_debug("Gotta something OUT.\n");
if (!eproxy_move_data_out(&other->buf, conn->fd))
eproxy_delconn(efd, conn);
else
eproxy_touch_conn(conn, now);
/* When the pipe filled up could have
lost input eproxy_events. Unfortunately
splice doesn't tell us which end
was responsible for 0, so have to ask
explicitely. */
int len = 0;
if (ioctl(other->fd, FIONREAD, &len) < 0)
perror("ioctl");
if (len > 0) {
if (!eproxy_move_data_in(other->fd,
&other->buf))
eproxy_closeconn(efd, other);
}
}
}
//过期连接
timeo = eproxy_expire_connections(efd, now);
eproxy_debug("timeo = %d\n", timeo);
}
return 0;
}
list.h
/* Stripped down version of the Linux 2.6.30 list.h */
#include <stddef.h>
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); \
pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}