注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

蒙奇D小豌豆的博客

蒙奇D小豌豆的学习记录

 
 
 

日志

 
 

Virtnic two --- vhost_net driver  

2014-11-28 22:54:18|  分类: sdn |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 
vhost_net驱动以字符设备(/dev/vhost-net)呈现给qemu, qemu配置vhost_net设置feature, 配置vring以准备与虚拟机数据流的通信的通道, 以及设置backend的tun driver. 每打开一个/dev/vhost-net的fd与一个tun driver的fd相关联, 可以表示为虚拟网卡一个queue通路. 每个vhost_net实例开启一个内核线程接收和发送数据.

1. init
static const struct file_operations vhost_net_fops = {
	.unlocked_ioctl = vhost_net_ioctl,
	.open           = vhost_net_open,
};

static struct miscdevice vhost_net_misc = {
	.name = "vhost-net",
	.fops = &vhost_net_fops,
};

static int vhost_net_init(void)
{
	return misc_register(&vhost_net_misc);
}

2. open
struct vhost_net {
	struct vhost_dev dev;
	struct vhost_net_virtqueue vqs[VHOST_NET_VQ_MAX];
	struct vhost_poll poll[VHOST_NET_VQ_MAX];
	unsigned tx_packets;
};

void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
		     unsigned long mask, struct vhost_dev *dev)
{
	init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup);
	/*在tun/eventfd--poll里面poll_wait, 执行函数vhost_poll_func把
	vhost_poll_wakeup放在等待队列头上,有报文时使得tun/eventfd唤醒*/
	init_poll_funcptr(&poll->table, vhost_poll_func);
	poll->mask = mask;
	poll->dev = dev;
	poll->wqh = NULL;

	/*vhost_poll_wakeup把worker(fn)放到vhost_dev->work_list上, vhost内核
	线程去读取执行*/
	vhost_work_init(&poll->work, fn);
}

static int vhost_net_open(struct inode *inode, struct file *f)
{
	/*分配vhost_net包含vhost_dev以及2个virtqueue 收发*/
	struct vhost_net *n = kmalloc(sizeof *n, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);

	/*kick poll函数, poll是否收到虚拟机的报文tx, 能否发送给虚拟机rx(这里rx用处不大)*/
	n->vqs[VHOST_NET_VQ_TX].vq.handle_kick = handle_tx_kick;/*虚拟机向外发送*/
	n->vqs[VHOST_NET_VQ_RX].vq.handle_kick = handle_rx_kick;/*虚拟机接收*/
	/*初始化dev和virtqueue*/
	vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX);
	//vhost_poll_init(&vq->poll, vq->handle_kick, POLLIN, dev);
	
	/*设置backend poll函数, poll是否收到tun的报文rx 能否向tun发送报文tx(这里tx用处不大)*/
	vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT, dev);/*虚拟机向外发送*/
	vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN, dev);/*虚拟机接收*/
}


3. ioctl
a. vhost init
1) set owner: 启动内核线程
long vhost_dev_set_owner(struct vhost_dev *dev)
{
	dev->mm = get_task_mm(current);
	/*vhost_work读取vhost_dev->work_list的fun去执行(handle_tx/rx_net, hanlde_tx/rx_kick*/
	worker = kthread_create(vhost_worker, dev, "vhost-%d", current->pid);
	dev->worker = worker;
	wake_up_process(worker);
}

2) set feature
3) 设置vring call:通知虚拟机的机制

b. vhost start
1. 设置vhost_vring_ioctl: 包括虚拟机与vhost共享内存, viring number base地址等
2. 设置vring_kick: 去poll前端虚拟机通过kvm发出的通知
case VHOST_SET_VRING_KICK:
	eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
	if (eventfp != vq->kick) {
		pollstop = (filep = vq->kick) != NULL;
		pollstart = (vq->kick = eventfp) != NULL;
	} else
		filep = eventfp;
	/*开始poll kick, 在eventfd(kvm管理)的poll里通过poll_wait把vhost_poll_wakeup
	放入eventfd的等待队列, 这样虚拟机发出报文, kvm通过eventfd_signal唤醒其等待队列
	从而vhost_poll_wakeup把worker(handle_tx_kick)放到dev->work_list上
	让内核vhost线程去处理*/
	if (pollstart && vq->handle_kick)
		r = vhost_poll_start(&vq->poll, vq->kick);

3. 设置backend: 去poll后端tun发出的通知
long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
{	
	/*根据tun的fd获取其sock*/
	sock = get_socket(fd);
	
	oldsock = vq->private_data;
	if (sock != oldsock) {
		vhost_net_disable_vq(n, vq);
		vq->private_data = sock;
		r = vhost_net_enable_vq(n, vq);
		/*去poll n->poll, 在tun的poll里通过poll_wait把vhost_poll_wakeup
		放入sock的等待队列, 这样tun每次xmit后都会唤醒sock上的等待队列
		从而vhost_poll_wakeup把worker(handle_rx_net)放到dev->work_list上
		让内核vhost线程去处理*/
		//vhost_poll_start(poll, sock->file);
	}
}


4. receive
int  handle_rx_net/kick--->handle_rx(struct vhost_net *net)
{
	struct msghdr msg = {
		.msg_iov = vq->iov,
		.msg_flags = MSG_DONTWAIT,
	};
	/*查看sock skb队列上是否有报文*/
	while ((sock_len = peek_head_len(sock->sk))) {
		/*获取avail描述符, 放于vq->iov*/
		headcount = get_rx_bufs(vq, vq->heads, vhost_len,
					&in, vq_log, &log,
					likely(mergeable) ? UIO_MAXIOV : 1);
		/*通过tun_recvmsg接收报文*/
		err = sock->ops->recvmsg(NULL, sock, &msg,
					 sock_len, MSG_DONTWAIT | MSG_TRUNC);
		/*更新used描述符号, notify虚拟机*/
		vhost_add_used_and_signal_n(&net->dev, vq, vq->heads,
					    headcount);
	}	
}



5. send
int  handle_tx_kick/net--->handle_tx(struct vhost_net *net)
{
	struct msghdr msg = {
		.msg_iov = vq->iov,
		.msg_flags = MSG_DONTWAIT,
	};
	/*获取存放数据的avail描述符 转换放于vq->iov*/
	head = vhost_get_vq_desc(vq, vq->iov,
					 ARRAY_SIZE(vq->iov),
					 &out, &in,
					 NULL, NULL);	
	/*通过tun_senmsg发送注入宿主机内核*/
	err = sock->ops->sendmsg(NULL, sock, &msg, len);
	
	/*更新used描述符*/
	vhost_add_used_and_signal(&net->dev, vq, head, 0);
}
  评论这张
 
阅读(97)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018