网络子系统23_skb常用函数

//分配新的skb->data,将旧的skb->data、skb_shinfo(skb)->frags、skb_shinfo(skb)->frag_list中的内容拷贝到新skb->data的连续内存空间中,释放frags或frag_list
//其中frags用于支持分散聚集IO,frags_list用于支持数据分片
1.1 int __skb_linearize(struct sk_buff *skb, int gfp_mask)
{
	unsigned int size;
	u8 *data;
	long offset;
	struct skb_shared_info *ninfo;
	int headerlen = skb->data - skb->head;
	int expand = (skb->tail + skb->data_len) - skb->end;
	//如果此skb被共享
	if (skb_shared(skb))
		BUG();//产生BUG oops

	//还需要的内存大小
	if (expand <= 0)
		expand = 0;
	//新申请的skb的大小
	size = skb->end - skb->head + expand;
	//将size对齐到SMP_CACHE_BYTES
	size = SKB_DATA_ALIGN(size);
	//分配物理上联系的内存
	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
	if (!data)
		return -ENOMEM;
	//拷贝
	if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))
		BUG();

	//初始化skb的skb_shared_info结构
	ninfo = (struct skb_shared_info*)(data + size);
	atomic_set(&ninfo->dataref, 1);
	ninfo->tso_size = skb_shinfo(skb)->tso_size;
	ninfo->tso_segs = skb_shinfo(skb)->tso_segs;
	//fraglist为NULL
	ninfo->nr_frags = 0;
	ninfo->frag_list = NULL;

	offset = data - skb->head;

	//释放之前skb的data
	skb_release_data(skb);

	//将skb指向新的data
	skb->head = data;
	skb->end  = data + size;
	//重新初始化新skb的各个报头指针
	skb->h.raw   += offset;
	skb->nh.raw  += offset;
	skb->mac.raw += offset;
	skb->tail    += offset;
	skb->data    += offset;

	skb->cloned    = 0;

	skb->tail     += skb->data_len;
	skb->data_len  = 0;
	return 0;
}

1.2 SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES - 1)) & \
				 ~(SMP_CACHE_BYTES - 1))

//将skb中起始offset的内容拷贝到to中,拷贝长度为len
1.3 int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
{
	int i, copy;
	//skb->len-skb->data_len,得到skb->head到skb->end之间的数据量
	int start = skb_headlen(skb);
	//偏移量+len > skb->len,说明可供拷贝的数据量不够
	if (offset > (int)skb->len - len)
		goto fault;
	//计算需要拷贝的数据量
	if ((copy = start - offset) > 0) {
		if (copy > len)
			copy = len;
		//拷贝
		memcpy(to, skb->data + offset, copy);
		if ((len -= copy) == 0)//拷贝量=需要拷贝的长度
			return 0;
		offset += copy;//更新偏移量
		to     += copy;
	}
	//接下来的数据从skb_shinfo的frags数组中进行拷贝
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		int end;

		//遍历frags
		end = start + skb_shinfo(skb)->frags[i].size;
		if ((copy = end - offset) > 0) {
			u8 *vaddr;

			if (copy > len)
				copy = len;
			//映射skb的frag到内核地址空间
			vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
			//拷贝
			memcpy(to,
			       vaddr + skb_shinfo(skb)->frags[i].page_offset+
			       offset - start, copy);
			//解除映射
			kunmap_skb_frag(vaddr);

			if ((len -= copy) == 0)
				return 0;
			offset += copy;
			to     += copy;
		}
		start = end;
	}
	//从skb的frag_list中拷贝
	if (skb_shinfo(skb)->frag_list) {
		struct sk_buff *list = skb_shinfo(skb)->frag_list;

		for (; list; list = list->next) {
			int end;

			BUG_TRAP(start <= offset + len);

			end = start + list->len;
			if ((copy = end - offset) > 0) {
				if (copy > len)
					copy = len;
				//递归调用
				if (skb_copy_bits(list, offset - start,
						  to, copy))
					goto fault;
				if ((len -= copy) == 0)
					return 0;
				offset += copy;
				to     += copy;
			}
			start = end;
		}
	}
	if (!len)
		return 0;

fault:
	return -EFAULT;
}
//保证skb->data 到 skb->tail之间有len长度的数据
2.1 static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
{
	//skb->data 到 skb->tail之间的数据足够len长度
	if (likely(len <= skb_headlen(skb)))
		return 1;
	//len长度超过skb总长度
	if (unlikely(len > skb->len))
		return 0;
	//移动后边的数据到skb->data中
	return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL;
}

//调用流程pskb_may_pull->__pskb_pull_tail
//delta为需要从frags或者frag_list向前移动的数据量
2.2 unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta)
{	//eat为去除当前skb可用内存,还需要多少内存
	int i, k, eat = (skb->tail + delta) - skb->end;
	//判断当前skb是否被克隆
	if (eat > 0 || skb_cloned(skb)) {
		//对sk_buff重新分配头
		if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,
				     GFP_ATOMIC))
			return NULL;
	}
	//从skb的offset(skb->tail),拷贝delta个字节到skb->tail之后
	if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
		BUG();
	//没有分段
	if (!skb_shinfo(skb)->frag_list)
		goto pull_pages;
	//由于数据已经拷贝到了skb->data中,因此需要释放frags,frag_list中被拷贝过的数据
	//计算从frags数组中拷贝的数据量
	eat = delta;
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		//寻找到满足eat这么多数据量的最后一个page
		if (skb_shinfo(skb)->frags[i].size >= eat)
			//在frags数组中的数据量可以满足delta时,则只释放frags即可
			goto pull_pages;
		eat -= skb_shinfo(skb)->frags[i].size;
	}
	//eat仍不为0,说明从frag_list中进行了拷贝,释放frag_list
	if (eat) {
		struct sk_buff *list = skb_shinfo(skb)->frag_list;
		struct sk_buff *clone = NULL;
		struct sk_buff *insp = NULL;

		do {
			//list为null,说明数据量不够
			if (!list)
				BUG();
			//当前skb的长度小于需要的长度
			if (list->len <= eat) {
				//找到下一个skb
				eat -= list->len;
				//list指向下一个需要的skb
				list = list->next;
				//insp指向当前的skb
				insp = list;
			} else {
				//此时insp指向前一个skb
				//说明当前skb可以满足需要的数据量
				if (skb_shared(list)) {//但是当前skb被共享
					clone = skb_clone(list, GFP_ATOMIC);//对最后那个拷贝不完全的skb,进行克隆
					if (!clone)
						return NULL;
					//list指向当前被克隆的的skb
					//insp指向下一个skb
					insp = list->next;
					list = clone;
				} else {
					//list与insp指向当前的skb
					insp = list;
				}
				//修改最后一个skb,移动指针,删除掉被拷贝的数据
				if (!pskb_pull(list, eat)) {
					if (clone)
						kfree_skb(clone);//递减clone的引用计数
					return NULL;
				}
				break;
			}
		} while (eat);
		//list指向frag_list头
		//直到list遍历到数据量足够的最后一个skb
		while ((list = skb_shinfo(skb)->frag_list) != insp) {
			skb_shinfo(skb)->frag_list = list->next;
			//释放当前的skb
			kfree_skb(list);//递减当前skb的引用技术,如果引用计数=0,则释放list
		}
		//说明最后一个skb只被拷贝了一部分,将此skb挂到frag_list头
		if (clone) {
			clone->next = list;
			skb_shinfo(skb)->frag_list = clone;
		}
	}

pull_pages:
	eat = delta;
	k = 0;
	//释放frags中的page
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		if (skb_shinfo(skb)->frags[i].size <= eat) {
			put_page(skb_shinfo(skb)->frags[i].page);
			eat -= skb_shinfo(skb)->frags[i].size;
		} else {
			skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
			if (eat) {
				skb_shinfo(skb)->frags[k].page_offset += eat;
				skb_shinfo(skb)->frags[k].size -= eat;
				eat = 0;
			}
			k++;
		}
	}
	skb_shinfo(skb)->nr_frags = k;

	skb->tail     += delta;
	skb->data_len -= delta;

	return skb->tail;
}


//skb->users指定skb被引用的个数
3.1 static inline int skb_shared(const struct sk_buff *skb)
{
	return atomic_read(&skb->users) != 1;
}
时间: 2024-11-08 21:57:08

网络子系统23_skb常用函数的相关文章

日常收集整理的JavaScript常用函数方法_javascript技巧

函数就是包裹在花括号中的代码块,前面使用了关键词 function: function functionname() { 这里是要执行的代码 } 当调用该函数时,会执行函数内的代码. 可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用. 提示:JavaScript 对大小写敏感.关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数. 字符串长度截取 functiocutstr(strlen{ vatemp, i

js常用函数2008-8-16整理第1/2页_javascript技巧

//js常用函数 更新2008-8-16 取自网络 function $(id) { return document.getElementById(id); } /************** 函数:getElementsByClassName 使用方法: 获取document内的超链接class是"info-links"的. getElementsByClassName(document, "a", "info-links"); 获取conta

prototype.js常用函数和用法

函数名  解释  举例  Element.toggle  交替隐藏或显示  Element.toggle(''div1'',''div2'')  Element.hide  隐藏  Element.hide(''div1'',''div2'')  Element.show 显示  Element.show(''div1'',''div2'')  Element.remove  删除  Element.remove(''div1'',''div2'')  Element.getHeight  取得

asp+XMLHTTP组件做采集常用函数收集

asp+|xml|采集|函数 asp+XMLHTTP组件做采集常用函数其中的html代码处理函数很管用,写得相当漂亮 <%'=================================================='函数名:GetHttpPage'作 用:获取网页源码'参 数:HttpUrl ------网页地址'==================================================Function GetHttpPage(HttpUrl)If IsNul

PHP第八课 字符串拆分常用函数

课程概要: 通过这节课能够对字符串进行基本的操作. 字符串知识点: 1.字符串的处理介绍 2.常用的字符串输出函数 3.常用的字符串格式化函数 4.字符串比较函数 5.正则表达式在字符串中的应用 6.与per1兼用的正则表达式 1.pathinfo();//返回域名的path信息 2.parse_url(); 3.parse_str();//用来拆分参数用的 pathinfo(); <?php $str="http://blog.csdn.net/junzaivip"; $arr

JavaScript常用函数列表

JavaScript常用函数列表,方便自己查询. click()   对象.click()   使对象被点击.     closed   对象.closed   对象窗口是否已关闭true/false     clearTimeout(对象)   清除已设置的setTimeout对象     clearInterval(对象)   清除已设置的setInterval对象     confirm("提示信息")   弹出确认框,确定返回true取消返回false     cursor:样

PHP常用函数小全

  纪录了PHP的一些常用函数 usleep() 函数延迟代码执行若干微秒. unpack() 函数从二进制字符串对数据进行解包. uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID. time_sleep_until() 函数延迟代码执行直到指定的时间. time_nanosleep() 函数延迟代码执行若干秒和纳秒. sleep() 函数延迟代码执行若干秒. show_source() 函数对文件进行语法高亮显示. strip_whitespace() 函数返回已删除 PHP

ODBC API常用函数诠释

odbc|函数 ODBC API常用函数诠释记得kony曾经在我的这个坛子上转过一个帖子,解释使用ODBC数据源来连接数据库进行数据库操作速度慢的原因,同时那个帖子也提出了提高ODBC数据源连接数据库进行数据库操作速度的两个办法,一个是利用Visiual Basic中的RDO组件,一个就是直接调用odbc api函数进行相关数据库操作,应答应过这里的弟弟妹妹们写点关于ODBC API函数方面的东东,所以只能赶鸭子上架了:      以下为ODBC API的常用十四个函数,先列出在PowerBui

自已常用的 Asp.Net常用函数

asp.net|函数 发部一个自已常用的 Asp.Net常用函数库,包括如下常用函数: 把普通字符转换成网页标签 网页标签转换成空白 WinForm 和 WebForm 字符格式转换 计算文本长度,区分中英文字符,中文算两个长度,英文算一个长度 截取用户输入长度的文本,并再文本后加'...' [中文算两个长度,英文算一个长度] 用于防止SQL注入式攻击检测 去掉文本里的网页标签代码 转换SQL语句插入操作或还原取出文本时可能出现的关键标点符号"'" 新建一个空文件夹 新建一个空文件 删