LINUX.ORG.RU

Производительность AF_PACKET RAW и AF_INET SOCK_STREAM сокетов в ядре.


0

1

Здравствуйте, столкнулся со следующей проблемой, есть две виртуальные машины с Ubuntu 9.10, требуется сравнить производительность TCP и непосредственно Ethernet на уровне ядра, для этого были написаны по 2 модуля, соответственно, два отправляющих пакеты и два принимающих, передача на L2 идёт через AF_PACKET RAW, по TCP общаются через AF_INET SOCK_STREAM, логично предположить, что «сырой» Ethernet будет работать быстрее, однако получается наоборот, может кто подскажет в чем дело? код модулей ниже

Ethernet sender

#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>

MODULE_LICENSE("Dual BSD/GPL");

int inline dst_state_poll(struct socket *sock)
{
	unsigned int revents = POLLHUP | POLLERR;
	revents = sock->ops->poll(NULL, sock, NULL);
	return revents;
}

int inline send_packet(void *buffer, int size, struct socket *sock, struct sockaddr_ll *dest_addr )
{	
	struct msghdr msg;
	struct kvec iov;

	iov.iov_base = buffer;
	iov.iov_len = size;

	msg.msg_iov = (struct iovec*)&iov;
	msg.msg_iovlen = 1;
	msg.msg_flags = MSG_DONTWAIT;   /* non-blocking */
	msg.msg_name = dest_addr;
	msg.msg_namelen = sizeof(struct sockaddr_ll);
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	
	return kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);			
}

int send_all(void *buffer, int block_size, int mtu, struct socket *sock, struct sockaddr_ll *dest_addr)
{
	int i,err, iter = 1500, packets;
	// sending extra packets to cover packets loss that's why sending 1500 packets 
	// while on receiving server waiting only 1000
	unsigned long begin = 0, result;
	packets = block_size*iter/mtu + 1;
	
	
	for(i = 0; i < iter; ){
		err = send_packet(buffer, mtu, sock, dest_addr );
		if( i == 0)
			begin = jiffies;
		if(err>0)
			i++;
//		printk(KERN_INFO "err %d\n",(err));	
	}
	result = jiffies-begin;
	printk(KERN_INFO "time %lu\n",(result))	;
	return 0;
}

static int client_init(void)
{
	int err, mtu, block_size = 1500;//, max_block_size = 1024*1024, inc = 2;
	struct sockaddr_ll addr, dest_addr;
	struct socket *sock;
	struct net_device *ifp;
	int ifindex = 2;
	
	void* buffer;
	
	/*our MAC address*/
	unsigned char src_mac[6] = {0x08, 0x00, 0x27, 0x91, 0x34, 0x51};
	/*other host MAC address*/
	unsigned char dest_mac[6] = {0x08, 0x00, 0x27, 0xb7, 0x1e, 0xa0};	
		
	/* find device by name*/
	read_lock(&dev_base_lock);
	for_each_netdev(&init_net, ifp) {
		if (!strcmp(ifp->name,"eth0")){
			ifindex = ifp->ifindex;
			printk(KERN_INFO "mtu: %d", ifp->mtu);
	    		break;
		}
	}
	read_unlock(&dev_base_lock);
	
	/*buffer for ethernet frame*/
	buffer = kmalloc( ifp->mtu + ETH_HLEN, GFP_KERNEL);
	
	/*create socket*/
	err = sock_create(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL), &sock);
	if( err < 0){
		printk(KERN_INFO "socket create err %d", err);
	}
	
	/*fill src sockaddr_ll*/
	addr.sll_family = AF_PACKET;
	addr.sll_protocol = htons(ETH_P_ALL);
	addr.sll_ifindex = 2;
	addr.sll_hatype = ARPHRD_ETHER;
	addr.sll_pkttype = PACKET_OTHERHOST;
	addr.sll_halen = ETH_ALEN;
	/*MAC*/
	memcpy(addr.sll_addr, src_mac, ETH_ALEN);
	addr.sll_addr[6]  = 0x00;/*not used*/
	addr.sll_addr[7]  = 0x00;/*not used*/
	
	/*fill dest sockaddr_ll*/
	dest_addr.sll_family = AF_PACKET;
	dest_addr.sll_protocol = htons(ETH_P_ALL);
	dest_addr.sll_ifindex = 2;
	dest_addr.sll_hatype = ARPHRD_ETHER;
	dest_addr.sll_pkttype = PACKET_OTHERHOST;
	dest_addr.sll_halen = ETH_ALEN;
	/*MAC*/
	memcpy(dest_addr.sll_addr, dest_mac, ETH_ALEN);
	dest_addr.sll_addr[6]  = 0x00;/*not used*/
	dest_addr.sll_addr[7]  = 0x00;/*not used*/	
	
	
	err = kernel_bind(sock, (struct sockaddr*)&addr, sizeof(addr));
	if( err < 0)
	        printk(KERN_INFO "socket bind err %d", err);	
	else {
		send_all ( buffer, block_size, ifp->mtu, sock, &dest_addr);//test
	}
	kfree(buffer);
	sock_release(sock);
		
	return 0;
}

static void client_exit(void)
{
	printk(KERN_INFO "Goodbye, cruel world\n");
}

module_init(client_init);
module_exit(client_exit);

Etherner receiver

#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/kthread.h>
#include <linux/jiffies.h>
#include <asm/param.h>

MODULE_LICENSE("Dual BSD/GPL");

unsigned inline int dst_state_poll(struct socket *sock)
{
	unsigned int revents = POLLHUP | POLLERR;
	revents = sock->ops->poll(NULL, sock, NULL);
	return revents;
}

int inline await(struct socket *sock){
	unsigned int revents = 0;
	unsigned int err_mask = POLLERR | POLLHUP | POLLRDHUP;
	unsigned int mask = err_mask | POLLIN;
	revents = dst_state_poll(sock);
	if (!(revents & mask)) {
		for (;;){			
			revents = dst_state_poll(sock);
			if (revents & mask){
				break;
			}	
		}
	}
	return revents;
}

int inline eth_recv_packet(void *buffer, int size, struct socket *sock )
{	
	unsigned int revents = 0;
	struct msghdr msg;
	struct kvec iov;
	int err = 0;
	iov.iov_base = buffer;
	iov.iov_len = size;
	msg.msg_iov = (struct iovec*)&iov;
	msg.msg_iovlen = 1;
	msg.msg_flags = MSG_DONTWAIT;   /* non-blocking */
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	
	do{
		revents = await(sock);//wait for incoming packet
		if(revents & POLLIN){
			err = kernel_recvmsg(sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags);
		}
	} while ( err < 0);
//	printk(KERN_INFO "mark %d ", *(unsigned char *)(buffer + 14));
	return err;	
}

void recv_all(void *buffer, int block_size, int mtu, struct socket *sock)
{
	int i,  iter = 1000;//, err, packets;
	unsigned long begin = 0, result;
	
	
	for(i = 0; i < iter; i++)
	{
		eth_recv_packet(buffer, mtu, sock);
		if(i == 0)			
			begin = jiffies;
//		printk(KERN_INFO "jiffies  %lu", jiffies - begin);
//		printk(KERN_INFO "iteration: %d, num: %u", i, *(unsigned int*)(buffer+14));
	}
	result = jiffies-begin;
	printk(KERN_INFO "time %lu\n",(result))	; //receiving time
}

static int test_init(void)
{

        void *buffer;
	int mtu = 1500, err, ifindex = 2, block_size = 1500;//, inc = 2, max_block_size = 1024*1024;
	struct sockaddr_ll addr;
	struct socket *sock;
	struct net_device *ifp;
	unsigned char src_mac[6] = {0x08, 0x00, 0x27, 0xb7, 0x1e, 0xa0};
	
	 /* find device by name*/
	read_lock(&dev_base_lock);
	for_each_netdev(&init_net, ifp) {
		if (!strcmp(ifp->name,"eth0")){
			ifindex = ifp->ifindex;
			printk(KERN_INFO "mtu: %d", ifp->mtu);
	    		break;
		}
	}
	read_unlock(&dev_base_lock);
	
	err = sock_create(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL), &sock);
	if(err < 0)
	        printk(KERN_INFO "sock_create error");

	addr.sll_family = AF_PACKET;
	addr.sll_protocol = htons(ETH_P_ALL);
	addr.sll_ifindex = ifindex;
	addr.sll_hatype = ARPHRD_ETHER;
	addr.sll_pkttype = PACKET_OTHERHOST;
	addr.sll_halen = ETH_ALEN;
	
	/*MAC*/
	memcpy(addr.sll_addr, src_mac, ETH_ALEN);
	addr.sll_addr[6]  = 0x00;/*not used*/
	addr.sll_addr[7]  = 0x00;/*not used*/
	
	err = kernel_bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_ll));
	if( err	< 0)
	        printk(KERN_INFO "kernel_bind error");
	        
        buffer = kmalloc(mtu + ETH_HLEN, GFP_KERNEL);
        if(err){
        	printk(KERN_INFO "%d" ,err);
        }else{  
        	recv_all(buffer, block_size, ifp->mtu, sock);//test
 	} 
        kfree(buffer);
        sock_release(sock);
        printk(KERN_INFO "buff freed");

        return 0;
}

static void test_exit(void)
{
	printk(KERN_INFO "Goodbye, cruel world\n");
}

module_init(test_init);
module_exit(test_exit);

Всё не влезло, пришлось разбить на 2 сообщения

TCP sender

#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>

MODULE_LICENSE("Dual BSD/GPL");

void tcp_send(int block_size, void *buffer, struct socket *sock){
	struct msghdr msg;
	struct kvec iov;
	int err, i, iter = 1000; 

	for(i = 0; i < iter;){
		iov.iov_base = buffer;
		iov.iov_len =  block_size;

		msg.msg_iov = (struct iovec *)&iov;
		msg.msg_iovlen = 1;
		msg.msg_name = NULL;    
		msg.msg_namelen = 0;     
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
		msg.msg_flags = MSG_WAITALL;
		err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
		if(err > 0){
			 i++;
//			printk(KERN_INFO "%d %d", err,i);
		}
	}	
}

static int client_init(void)
{
	int err, block_size = 1500, inc = 2;
	struct sockaddr_in addr;
	struct socket *sock;
	struct net_device *ifp;
	int ifindex = 2;
		
	unsigned char dest_ip[4] = {0xc0, 0xa8, 0x38, 0x65};
	void* buffer; 
	
	memset(&addr, 0, sizeof(struct sockaddr_in));

	memcpy( &(addr.sin_addr.s_addr), (void *)dest_ip, 4);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(666);
//	addr.sin_addr.s_addr = htonl(INADDR_ANY);

	 /* find device by index */
	read_lock(&dev_base_lock);
	for_each_netdev(&init_net, ifp) {
		if (ifp->ifindex == ifindex){
			ifp->mtu = 1500;
	    		break;
		}
	}
	read_unlock(&dev_base_lock);


	err = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
	if(err)
	        printk(KERN_INFO "create err");
	
	err = kernel_connect(sock, (struct sockaddr *)&addr,sizeof(struct sockaddr_in), 0);
	if(err)
		printk(KERN_INFO "connect err %d ", err);
	if(err)
		return 0;
	buffer = kmalloc( 1024*1024, GFP_KERNEL);
	
	tcp_send(block_size, buffer, sock);/* for test
	for(; ifp->mtu <= 9000; ifp->mtu += 500 )
	{
		block_size = 512;
		while( block_size <= 1024*1024)
		{	
		
//			printk("mtu:%d size: %d\n", ifp->mtu, block_size);
			tcp_send(block_size, buffer, sock);
			block_size *= inc;
		}
	
	}*/
	kfree(buffer);	
	sock_release(sock);
	return 0;
}

static void client_exit(void)
{
	printk(KERN_INFO "Goodbye, cruel world\n");
}

module_init(client_init);
module_exit(client_exit);

TCP receiver

#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <asm/msr.h>
#include <linux/jiffies.h>
#include <asm/param.h>

MODULE_LICENSE("Dual BSD/GPL");

void tcp_receive(int block_size, void *buffer, struct socket *sock){
	struct msghdr msg;
	struct kvec iov;
	int err, i = 0, iter = 1000;
	unsigned long begin = 0, end, result;	
	
	while(i < iter){
		iov.iov_base = buffer;
		iov.iov_len = block_size;	

		msg.msg_iov = (struct iovec *)&iov;
		msg.msg_iovlen = 1;
		msg.msg_name = NULL;
		msg.msg_namelen = 0;
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
		msg.msg_flags = MSG_WAITALL;	
			
		err = kernel_recvmsg( sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags);
		if(i==0)
			begin = jiffies;
		if(err >= 0) {
			i++;
//			printk(KERN_INFO "received bytes %d %d", err, i);	
		}
	}
	end = jiffies;
	result = end - begin;
	printk(KERN_INFO "result: %lu", result );

}
static int test_init(void)
{
	int block_size = 1500, inc = 2, mtu;
	int err;
	struct net_device *ifp;
	int ifindex = 3;

	struct sockaddr_in addr;
	struct socket *sock, *acpt_sock;
	void* buffer;

	memset(&addr, 0, sizeof(struct sockaddr_in));
	unsigned char src_ip[4] = {0xc0, 0xa8, 0x38, 0x65};

	memcpy(&(addr.sin_addr.s_addr), &src_ip, 4);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(666);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);	

	err = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
	if(err < 0)
	        printk(KERN_INFO "sock_create error");	
	printk(KERN_INFO "create");
	
	if(kernel_bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)))
	        printk(KERN_INFO "kernel_bind error");
	printk(KERN_INFO "bind");
		
	err = kernel_listen(sock, 1024);
	if(err)
	        printk(KERN_INFO "kernel_listen error");
	printk(KERN_INFO "kernel_listen");
	err = kernel_accept(sock, &acpt_sock, 0);
	if(err)		
		printk(KERN_INFO "kernel_accept error");
	printk(KERN_INFO "accept");


	if(err)
		return -1;
	 /* find device by index */
	read_lock(&dev_base_lock);
	for_each_netdev(&init_net, ifp) {
		if (!strcmp(ifp->name,"eth0")){
			ifindex = ifp->ifindex;
	    		break;
		}
	}
	printk(KERN_INFO "index :%d", ifindex);
	read_unlock(&dev_base_lock);
	buffer = kmalloc(1024*1024, GFP_KERNEL);
	tcp_receive(block_size, buffer, acpt_sock);
/*	for(ifp->mtu = 1500; ifp->mtu <= 9000; ifp->mtu += 500 )
	{	
		block_size = 512;
		while( block_size <= 1024*1024)
		{
			printk(KERN_INFO "Mtu: %d, block size: %d", ifp->mtu, block_size);
			tcp_receive(block_size, buffer, acpt_sock);
			block_size *= inc;
		}	
	}*/
	kfree(buffer);		
	sock_release(acpt_sock);
	sock_release(sock);
	
	return 0;
}



static void test_exit(void)
{
	printk(KERN_INFO "Goodbye, cruel world\n");
}

module_init(test_init);
module_exit(test_exit);

kernNewbie
() автор топика

в двух словах, как мерял? Что ты подразумеваешь под производительностью -нагрузку на проц или утилизацию канала?

true_admin ★★★★★
()
Ответ на: комментарий от kernNewbie

Вообще надо померить скорость, пока проще именно на время смотреть.

kernNewbie
() автор топика
Ответ на: комментарий от kernNewbie

во сколько раз скорость оказалась разной?

Вообще, возможно, TCP просто как-то заоптимизирован. RAW SOCKETS это целый интерфейс, каждый пакет сам генеришь а вот у TCP забил данными буфер а ось дальше сама ими в сеть кидает, иногда ack получая :). Это моя версия.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

ну в среднем получалось, что TCP раза в 2 быстрее, просто через sk_buff что-то не очень хочется

kernNewbie
() автор топика
Ответ на: комментарий от kernNewbie

для такого большого кода лучи заливай на pastbin а сюда кидай ссылку

enep ★★★★★
()

> может кто подскажет в чем дело?

Хочешь, чтобы мы подсказали тебе хорошую книгу по сетевым протоколам?

Ты хоть примерно знаешь, как работает TCP? Что он буферизует пакеты, и потом отправляет их единым сообщением? Тыуверен, что он не использует jumbo frames? Что не загружает в очередь карты несколько пакетов сразу?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> может кто подскажет в чем дело?

Хочешь, чтобы мы подсказали тебе хорошую книгу по сетевым протоколам?

Я буду рад любой информации по проблеме: подскажете хорошую книжку - хорошо, подскажете в чем именно у меня может быть проблема, почему Ethernet медленнее(например, дело в том что использую AF_PACKET или в коде глупые вещи пишу) - ещё лучше, а если подскажете возможное решение вообще супер!

Ты хоть примерно знаешь, как работает TCP?

Примерно знаю.

Тыуверен, что он не использует jumbo frames?

Ну я не очень понимаю, как TCP может использовать jumbo frames, если я вручную выставляю mtu карточки 1500

для такого большого кода лучи заливай на pastbin а сюда кидай ссылку

Подумал, что людям качать что-то лениво будет, а так можно быстро можули просмотреть, немного позже добавлю ссылку.

kernNewbie
() автор топика

А без виртуалки какой результат? И в случае с tcp, у вас все байты передаются?

mky ★★★★★
()
Ответ на: комментарий от kernNewbie

> например, дело в том что использую AF_PACKET

Думаю, что именно так. Я бы вообще не лез в ядро, а написал тест в юзерспейсе, и, для TCP, поставил бы NODELAY.

tailgunner ★★★★★
()
Ответ на: комментарий от mky

Без виртуалок не пробовал, возможности пока нет, а TCP без потерь передаёт

kernNewbie
() автор топика
Ответ на: комментарий от tailgunner

Без ядра в данном случае никак) Дальнейшая работа будет в ядре.

kernNewbie
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.