您的位置:首页 > 理论基础 > 计算机网络

【转载】高性能网络I/O框架-netmap源码分析(4)

2014-07-25 10:03 447 查看

高性能网络I/O框架-netmap源码分析(4)

作者:gfree.wind@gmail.com

原文地址:<http://blog.chinaunix.net/uid-23629988-id-3642305.html>

前面的文章学习了netmap对驱动的修改,以及netmap的初始化和加载。接下来就要从netmap的使用,自上而下的学习分析一下netmap的代码了。

netmap的应用示例

netmap的网站上给出了一个简单的例子——说简单,其实也涵盖了netmap的框架的调用。
struct netmap_if *nifp;
struct nmreq req;
int i, len;
char *buf;

fd = open("/dev/netmap", 0);
strcpy(req.nr_name, "ix0"); // register the interface
ioctl(fd, NIOCREG, &req); // offset of the structure
mem = mmap(NULL, req.nr_memsize, PROT_READ|PROT_WRITE, 0, fd, 0);
nifp = NETMAP_IF(mem, req.nr_offset);
for (;;) {
struct pollfd x[1];
struct netmap_ring *ring = NETMAP_RX_RING(nifp, 0);

x[0].fd = fd;
x[0].events = POLLIN;
poll(x, 1, 1000);
for ( ; ring->avail > 0 ; ring->avail--) {
i = ring->cur;
buf = NETMAP_BUF(ring, i);
use_data(buf, ring->slot[i].len);
ring->cur = NETMAP_NEXT(ring, i);
}
}


咱们还是一路走来,走到哪看到哪。

open操作

这个其实跟netmap没有多大关系。记得前文中的netmap注册了一个misc设备netmap_cdevsw吗?
static struct file_operations netmap_fops = {
.mmap = linux_netmap_mmap,
LIN_IOCTL_NAME = linux_netmap_ioctl,
.poll = linux_netmap_poll,
.release = netmap_release,
};

static struct miscdevice netmap_cdevsw = {  /* same name as FreeBSD */
MISC_DYNAMIC_MINOR,
"netmap",
&netmap_fops,
};


netmapcdevsw为对应的设备结构体定义,netmapfops为对应的操作函数。这里面没有自定义的open函数,那么应该就使用linux内核默认的open——这个是我的推测,暂时不去查看linux代码了。

NIOCREG ioctl操作

ioctl就是内核的一个垃圾桶啊,什么都往里装,什么都能做。

netmap的ioctl

long
linux_netmap_ioctl(struct file *file, u_int cmd, u_long data /* arg */)
{
int ret;
struct nmreq nmr;
bzero(&nmr, sizeof(nmr));

/*
从上面的例子和这里可以看出,struct nmreq就是netmap内核与用户空间的消息结构体。
两者的互动就靠它了。
*/
if (data && copy_from_user(&nmr, (void *)data, sizeof(nmr) ) != 0)
return -EFAULT;
ret = netmap_ioctl(NULL, cmd, (caddr_t)&nmr, 0, (void *)file);
if (data && copy_to_user((void*)data, &nmr, sizeof(nmr) ) != 0)
return -EFAULT;
return -ret;
}


进入netmap_ioctl,真正的netmap的ioctl处理函数
static int
netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
int fflag, struct thread *td)
{
struct netmap_priv_d *priv = NULL;
struct ifnet *ifp;
struct nmreq *nmr = (struct nmreq *) data;
struct netmap_adapter *na;
int error;
u_int i, lim;
struct netmap_if *nifp;

/*
为了去除warning警告——没用的参数。
void应用的一个小技巧
*/
(void)dev;  /* UNUSED */
(void)fflag;    /* UNUSED */

/* Linux下这两个红都是空的 */
CURVNET_SET(TD_TO_VNET(td));

/*
devfs_get_cdevpriv在linux下是一个宏定义。
得到struct file->private_data;
当private_data不为NULL时,返回0;为null时,返回ENOENT。
所以对于linux,后面的条件判断永远为假
*/
error = devfs_get_cdevpriv((void **)&priv);
if (error != ENOENT && error != 0) {
CURVNET_RESTORE();
return (error);
}

error = 0;  /* Could be ENOENT */
/*
又可见到高手代码健壮性的体现。
对于运行在kernel中的代码,一定要稳定!强制保证nmr->nr_name字符串长度的合法性
*/
nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';  /* truncate name */

。。。。。。 。。。。。。


为了流程的清楚,对于netmap_ioctl的分析就到这里。依然按照之前的使用的流程走。

写到这里我发现netmap网站给的实例应该是老古董了。按照netmap当前的代码,上面的例子根本无法使用。不过木已成舟,大家凑合意会理解这个例子吧,还好流程没有太大的变化。

既然示例代码不可信了,那么就按照ioctl支持的命令顺序,来分析netmap吧。

NIOCGINFO

用于返回netmap的基本信息
case NIOCGINFO:     /* return capabilities etc */
/* memsize is always valid */
/*
如果是我写,我可能先去做后面的版本检查
netmap这样选择,应该是因为这些信息与版本无关。
*/
nmr->nr_memsize = nm_mem->nm_totalsize;
nmr->nr_offset = 0;
nmr->nr_rx_rings = nmr->nr_tx_rings = 0;
nmr->nr_rx_slots = nmr->nr_tx_slots = 0;
if (nmr->nr_version != NETMAP_API) {
D("API mismatch got %d have %d",
nmr->nr_version, NETMAP_API);
nmr->nr_version = NETMAP_API;
error = EINVAL;
break;
}
if (nmr->nr_name[0] == '\0')    /* just get memory info */
break;
/*
Linux下调用dev_get_by_name通过网卡名得到网卡struct net_device。
并且通过NETMAP_CAPABLE来检查netmap是否attach了这个net_device——忘记NETMAP_CAPABLE和attach的同学请自行查看前面几篇文章。
*/
error = get_ifp(nmr->nr_name, &ifp); /* get a refcount */
if (error)
break;
/* 得到attach到网卡结构的netmap结构体 */
na = NA(ifp); /* retrieve netmap_adapter */
/* 得到ring的个数,以及每个ring有多少slot */
nmr->nr_rx_rings = na->num_rx_rings;
nmr->nr_tx_rings = na->num_tx_rings;
nmr->nr_rx_slots = na->num_rx_desc;
nmr->nr_tx_slots = na->num_tx_desc;
nm_if_rele(ifp);    /* return the refcount */
break;

NIOCREGIF

将特定的网卡设置为netmap模式
case NIOCREGIF:
if (nmr->nr_version != NETMAP_API) {
nmr->nr_version = NETMAP_API;
error = EINVAL;
break;
}
if (priv != NULL) { /* thread already registered */
/* 重新设置对哪个ring感兴趣,这个函数,留到后面说 */
error = netmap_set_ringid(priv, nmr->nr_ringid);
break;
}
/* 下面几行拿到netmap_device结构的代码,和NIOCGINFO case没什么区别 */
/* find the interface and a reference */
error = get_ifp(nmr->nr_name, &ifp); /* keep reference */
if (error)
break;
na = NA(ifp); /* retrieve netmap adapter */

/*
* Allocate the private per-thread structure.
* XXX perhaps we can use a blocking malloc ?
*/
priv = malloc(sizeof(struct netmap_priv_d), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (priv == NULL) {
error = ENOMEM;
nm_if_rele(ifp);   /* return the refcount */
break;
}

/* 这里循环等待net_device可用 */
for (i = 10; i > 0; i--) {
na->nm_lock(ifp, NETMAP_REG_LOCK, 0);
if (!NETMAP_DELETING(na))
break;
na->nm_lock(ifp, NETMAP_REG_UNLOCK, 0);
tsleep(na, 0, "NIOCREGIF", hz/10);
}
if (i == 0) {
D("too many NIOCREGIF attempts, give up");
error = EINVAL;
free(priv, M_DEVBUF);
nm_if_rele(ifp);    /* return the refcount */
break;
}

/* 保存设备net_device指针*/
priv->np_ifp = ifp; /* store the reference */
/* 设置感兴趣的ring,即准备哪些ring来与用户态交互 */
error = netmap_set_ringid(priv, nmr->nr_ringid);
if (error)
goto error;
/*
每一个netmap的描述符,对应每一个网卡,都有一个struct netmap_if, 即priv->np_nifp.
*/
priv->np_nifp = nifp = netmap_if_new(nmr->nr_name, na);
if (nifp == NULL) { /* allocation failed */
error = ENOMEM;
} else if (ifp->if_capenable & IFCAP_NETMAP) {
/* was already set */
/* 网卡对应的netmap_device的扩展已经设置过了 */
} else {
/* Otherwise set the card in netmap mode
* and make it use the shared buffers.
*/
/* 这时,这块网卡真正要进入netmap模式,开始初始化一些成员变量 */
for (i = 0 ; i < na->num_tx_rings + 1; i++)
mtx_init(&na->tx_rings[i].q_lock, "nm_txq_lock", MTX_NETWORK_LOCK, MTX_DEF);
for (i = 0 ; i < na->num_rx_rings + 1; i++) {
mtx_init(&na->rx_rings[i].q_lock, "nm_rxq_lock", MTX_NETWORK_LOCK, MTX_DEF);
}
/*
设置网卡为netmap mode为打开模式
对于e1000驱动来说,nm_register即e1000_netmap_reg
*/
error = na->nm_register(ifp, 1); /* mode on */
if (error)
netmap_dtor_locked(priv);
}

if (error) {    /* reg. failed, release priv and ref */
error:
na->nm_lock(ifp, NETMAP_REG_UNLOCK, 0);
nm_if_rele(ifp);    /* return the refcount */
bzero(priv, sizeof(*priv));
free(priv, M_DEVBUF);
break;
}

na->nm_lock(ifp, NETMAP_REG_UNLOCK, 0);
/* Linux平台,将priv保存到file->private_data*/
error = devfs_set_cdevpriv(priv, netmap_dtor);

if (error != 0) {
/* could not assign the private storage for the
* thread, call the destructor explicitly.
*/
netmap_dtor(priv);
break;
}

/* return the offset of the netmap_if object */
nmr->nr_rx_rings = na->num_rx_rings;
nmr->nr_tx_rings = na->num_tx_rings;
nmr->nr_rx_slots = na->num_rx_desc;
nmr->nr_tx_slots = na->num_tx_desc;
nmr->nr_memsize = nm_mem->nm_totalsize;
/*
得到nifp在内存池中的偏移。
因为netmap的基础就是利用内核与用户空间的内存共享。但是众所周知,内核和用户空间的地址范围是不用的。
这样同样的物理内存,在内核态和用户态地址肯定不同。所以必须利用偏移来对应相同的内存。
*/
nmr->nr_offset = netmap_if_offset(nifp);
break;


这个NIOCREGIF太长了,其它的case留到下一篇吧
http://info.iet.unipi.it/~luigi/netmap/ http://www.manualpages.de/FreeBSD/FreeBSD-8.3-RELEASE/man4/netmap.4.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: