⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sch_api.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
		if (!clid)			return -EINVAL;		if (q->handle == 0)			return -ENOENT;		if ((err = qdisc_graft(dev, p, clid, NULL, &q)) != 0)			return err;		if (q) {			qdisc_notify(skb, n, clid, q, NULL);			qdisc_lock_tree(dev);			qdisc_destroy(q);			qdisc_unlock_tree(dev);		}	} else {		qdisc_notify(skb, n, clid, NULL, q);	}	return 0;}/*   Create/change qdisc. */static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg){	struct tcmsg *tcm;	struct rtattr **tca;	struct net_device *dev;	u32 clid;	struct Qdisc *q, *p;	int err;replay:	/* Reinit, just in case something touches this. */	tcm = NLMSG_DATA(n);	tca = arg;	clid = tcm->tcm_parent;	q = p = NULL;	if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)		return -ENODEV;	if (clid) {		if (clid != TC_H_ROOT) {			if (clid != TC_H_INGRESS) {				if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)					return -ENOENT;				q = qdisc_leaf(p, clid);			} else { /*ingress */				q = dev->qdisc_ingress;			}		} else {			q = dev->qdisc_sleeping;		}		/* It may be default qdisc, ignore it */		if (q && q->handle == 0)			q = NULL;		if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {			if (tcm->tcm_handle) {				if (q && !(n->nlmsg_flags&NLM_F_REPLACE))					return -EEXIST;				if (TC_H_MIN(tcm->tcm_handle))					return -EINVAL;				if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)					goto create_n_graft;				if (n->nlmsg_flags&NLM_F_EXCL)					return -EEXIST;				if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))					return -EINVAL;				if (q == p ||				    (p && check_loop(q, p, 0)))					return -ELOOP;				atomic_inc(&q->refcnt);				goto graft;			} else {				if (q == NULL)					goto create_n_graft;				/* This magic test requires explanation.				 *				 *   We know, that some child q is already				 *   attached to this parent and have choice:				 *   either to change it or to create/graft new one.				 *				 *   1. We are allowed to create/graft only				 *   if CREATE and REPLACE flags are set.				 *				 *   2. If EXCL is set, requestor wanted to say,				 *   that qdisc tcm_handle is not expected				 *   to exist, so that we choose create/graft too.				 *				 *   3. The last case is when no flags are set.				 *   Alas, it is sort of hole in API, we				 *   cannot decide what to do unambiguously.				 *   For now we select create/graft, if				 *   user gave KIND, which does not match existing.				 */				if ((n->nlmsg_flags&NLM_F_CREATE) &&				    (n->nlmsg_flags&NLM_F_REPLACE) &&				    ((n->nlmsg_flags&NLM_F_EXCL) ||				     (tca[TCA_KIND-1] &&				      rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))))					goto create_n_graft;			}		}	} else {		if (!tcm->tcm_handle)			return -EINVAL;		q = qdisc_lookup(dev, tcm->tcm_handle);	}	/* Change qdisc parameters */	if (q == NULL)		return -ENOENT;	if (n->nlmsg_flags&NLM_F_EXCL)		return -EEXIST;	if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))		return -EINVAL;	err = qdisc_change(q, tca);	if (err == 0)		qdisc_notify(skb, n, clid, NULL, q);	return err;create_n_graft:	if (!(n->nlmsg_flags&NLM_F_CREATE))		return -ENOENT;	if (clid == TC_H_INGRESS)		q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_parent,				 tca, &err);	else		q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_handle,				 tca, &err);	if (q == NULL) {		if (err == -EAGAIN)			goto replay;		return err;	}graft:	if (1) {		struct Qdisc *old_q = NULL;		err = qdisc_graft(dev, p, clid, q, &old_q);		if (err) {			if (q) {				qdisc_lock_tree(dev);				qdisc_destroy(q);				qdisc_unlock_tree(dev);			}			return err;		}		qdisc_notify(skb, n, clid, old_q, q);		if (old_q) {			qdisc_lock_tree(dev);			qdisc_destroy(old_q);			qdisc_unlock_tree(dev);		}	}	return 0;}static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,			 u32 pid, u32 seq, u16 flags, int event){	struct tcmsg *tcm;	struct nlmsghdr  *nlh;	unsigned char *b = skb_tail_pointer(skb);	struct gnet_dump d;	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);	tcm = NLMSG_DATA(nlh);	tcm->tcm_family = AF_UNSPEC;	tcm->tcm__pad1 = 0;	tcm->tcm__pad2 = 0;	tcm->tcm_ifindex = q->dev->ifindex;	tcm->tcm_parent = clid;	tcm->tcm_handle = q->handle;	tcm->tcm_info = atomic_read(&q->refcnt);	RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id);	if (q->ops->dump && q->ops->dump(q, skb) < 0)		goto rtattr_failure;	q->qstats.qlen = q->q.qlen;	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,			TCA_XSTATS, q->stats_lock, &d) < 0)		goto rtattr_failure;	if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)		goto rtattr_failure;	if (gnet_stats_copy_basic(&d, &q->bstats) < 0 ||	    gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 ||	    gnet_stats_copy_queue(&d, &q->qstats) < 0)		goto rtattr_failure;	if (gnet_stats_finish_copy(&d) < 0)		goto rtattr_failure;	nlh->nlmsg_len = skb_tail_pointer(skb) - b;	return skb->len;nlmsg_failure:rtattr_failure:	nlmsg_trim(skb, b);	return -1;}static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,			u32 clid, struct Qdisc *old, struct Qdisc *new){	struct sk_buff *skb;	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);	if (!skb)		return -ENOBUFS;	if (old && old->handle) {		if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)			goto err_out;	}	if (new) {		if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)			goto err_out;	}	if (skb->len)		return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);err_out:	kfree_skb(skb);	return -EINVAL;}static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb){	int idx, q_idx;	int s_idx, s_q_idx;	struct net_device *dev;	struct Qdisc *q;	s_idx = cb->args[0];	s_q_idx = q_idx = cb->args[1];	read_lock(&dev_base_lock);	idx = 0;	for_each_netdev(&init_net, dev) {		if (idx < s_idx)			goto cont;		if (idx > s_idx)			s_q_idx = 0;		q_idx = 0;		list_for_each_entry(q, &dev->qdisc_list, list) {			if (q_idx < s_q_idx) {				q_idx++;				continue;			}			if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid,					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)				goto done;			q_idx++;		}cont:		idx++;	}done:	read_unlock(&dev_base_lock);	cb->args[0] = idx;	cb->args[1] = q_idx;	return skb->len;}/************************************************ *	Traffic classes manipulation.		* ************************************************/static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg){	struct tcmsg *tcm = NLMSG_DATA(n);	struct rtattr **tca = arg;	struct net_device *dev;	struct Qdisc *q = NULL;	struct Qdisc_class_ops *cops;	unsigned long cl = 0;	unsigned long new_cl;	u32 pid = tcm->tcm_parent;	u32 clid = tcm->tcm_handle;	u32 qid = TC_H_MAJ(clid);	int err;	if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)		return -ENODEV;	/*	   parent == TC_H_UNSPEC - unspecified parent.	   parent == TC_H_ROOT   - class is root, which has no parent.	   parent == X:0	 - parent is root class.	   parent == X:Y	 - parent is a node in hierarchy.	   parent == 0:Y	 - parent is X:Y, where X:0 is qdisc.	   handle == 0:0	 - generate handle from kernel pool.	   handle == 0:Y	 - class is X:Y, where X:0 is qdisc.	   handle == X:Y	 - clear.	   handle == X:0	 - root class.	 */	/* Step 1. Determine qdisc handle X:0 */	if (pid != TC_H_ROOT) {		u32 qid1 = TC_H_MAJ(pid);		if (qid && qid1) {			/* If both majors are known, they must be identical. */			if (qid != qid1)				return -EINVAL;		} else if (qid1) {			qid = qid1;		} else if (qid == 0)			qid = dev->qdisc_sleeping->handle;		/* Now qid is genuine qdisc handle consistent		   both with parent and child.		   TC_H_MAJ(pid) still may be unspecified, complete it now.		 */		if (pid)			pid = TC_H_MAKE(qid, pid);	} else {		if (qid == 0)			qid = dev->qdisc_sleeping->handle;	}	/* OK. Locate qdisc */	if ((q = qdisc_lookup(dev, qid)) == NULL)		return -ENOENT;	/* An check that it supports classes */	cops = q->ops->cl_ops;	if (cops == NULL)		return -EINVAL;	/* Now try to get class */	if (clid == 0) {		if (pid == TC_H_ROOT)			clid = qid;	} else		clid = TC_H_MAKE(qid, clid);	if (clid)		cl = cops->get(q, clid);	if (cl == 0) {		err = -ENOENT;		if (n->nlmsg_type != RTM_NEWTCLASS || !(n->nlmsg_flags&NLM_F_CREATE))			goto out;	} else {		switch (n->nlmsg_type) {		case RTM_NEWTCLASS:			err = -EEXIST;			if (n->nlmsg_flags&NLM_F_EXCL)				goto out;			break;		case RTM_DELTCLASS:			err = cops->delete(q, cl);			if (err == 0)				tclass_notify(skb, n, q, cl, RTM_DELTCLASS);			goto out;		case RTM_GETTCLASS:			err = tclass_notify(skb, n, q, cl, RTM_NEWTCLASS);			goto out;		default:			err = -EINVAL;			goto out;		}	}	new_cl = cl;	err = cops->change(q, clid, pid, tca, &new_cl);	if (err == 0)		tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS);out:	if (cl)		cops->put(q, cl);	return err;}static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,			  unsigned long cl,			  u32 pid, u32 seq, u16 flags, int event){	struct tcmsg *tcm;	struct nlmsghdr  *nlh;	unsigned char *b = skb_tail_pointer(skb);	struct gnet_dump d;	struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);	tcm = NLMSG_DATA(nlh);	tcm->tcm_family = AF_UNSPEC;	tcm->tcm_ifindex = q->dev->ifindex;	tcm->tcm_parent = q->handle;	tcm->tcm_handle = q->handle;	tcm->tcm_info = 0;	RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id);	if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0)		goto rtattr_failure;	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,			TCA_XSTATS, q->stats_lock, &d) < 0)		goto rtattr_failure;	if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0)		goto rtattr_failure;	if (gnet_stats_finish_copy(&d) < 0)		goto rtattr_failure;	nlh->nlmsg_len = skb_tail_pointer(skb) - b;	return skb->len;nlmsg_failure:rtattr_failure:	nlmsg_trim(skb, b);	return -1;}static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,			  struct Qdisc *q, unsigned long cl, int event){	struct sk_buff *skb;	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);	if (!skb)		return -ENOBUFS;	if (tc_fill_tclass(skb, q, cl, pid, n->nlmsg_seq, 0, event) < 0) {		kfree_skb(skb);		return -EINVAL;	}	return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);}struct qdisc_dump_args{	struct qdisc_walker w;	struct sk_buff *skb;	struct netlink_callback *cb;};static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walker *arg){	struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg;	return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).pid,			      a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS);}static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb){	int t;	int s_t;	struct net_device *dev;	struct Qdisc *q;	struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh);	struct qdisc_dump_args arg;	if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))		return 0;	if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)		return 0;	s_t = cb->args[0];	t = 0;	list_for_each_entry(q, &dev->qdisc_list, list) {		if (t < s_t || !q->ops->cl_ops ||		    (tcm->tcm_parent &&		     TC_H_MAJ(tcm->tcm_parent) != q->handle)) {			t++;			continue;		}		if (t > s_t)			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));		arg.w.fn = qdisc_class_dump;		arg.skb = skb;		arg.cb = cb;		arg.w.stop  = 0;		arg.w.skip = cb->args[1];		arg.w.count = 0;		q->ops->cl_ops->walk(q, &arg.w);		cb->args[1] = arg.w.count;		if (arg.w.stop)			break;		t++;	}	cb->args[0] = t;	dev_put(dev);	return skb->len;}/* Main classifier routine: scans classifier chain attached   to this qdisc, (optionally) tests for protocol and asks   specific classifiers. */int tc_classify_compat(struct sk_buff *skb, struct tcf_proto *tp,		       struct tcf_result *res){	__be16 protocol = skb->protocol;	int err = 0;	for (; tp; tp = tp->next) {		if ((tp->protocol == protocol ||		     tp->protocol == htons(ETH_P_ALL)) &&		    (err = tp->classify(skb, tp, res)) >= 0) {#ifdef CONFIG_NET_CLS_ACT			if (err != TC_ACT_RECLASSIFY && skb->tc_verd)				skb->tc_verd = SET_TC_VERD(skb->tc_verd, 0);#endif			return err;		}	}	return -1;}EXPORT_SYMBOL(tc_classify_compat);int tc_classify(struct sk_buff *skb, struct tcf_proto *tp,		struct tcf_result *res){	int err = 0;	__be16 protocol;#ifdef CONFIG_NET_CLS_ACT	struct tcf_proto *otp = tp;reclassify:#endif	protocol = skb->protocol;	err = tc_classify_compat(skb, tp, res);#ifdef CONFIG_NET_CLS_ACT	if (err == TC_ACT_RECLASSIFY) {		u32 verd = G_TC_VERD(skb->tc_verd);		tp = otp;		if (verd++ >= MAX_REC_LOOP) {			printk("rule prio %u protocol %02x reclassify loop, "			       "packet dropped\n",			       tp->prio&0xffff, ntohs(tp->protocol));			return TC_ACT_SHOT;		}		skb->tc_verd = SET_TC_VERD(skb->tc_verd, verd);		goto reclassify;	}#endif	return err;}EXPORT_SYMBOL(tc_classify);void tcf_destroy(struct tcf_proto *tp){	tp->ops->destroy(tp);	module_put(tp->ops->owner);	kfree(tp);}void tcf_destroy_chain(struct tcf_proto *fl){	struct tcf_proto *tp;	while ((tp = fl) != NULL) {		fl = tp->next;		tcf_destroy(tp);	}}EXPORT_SYMBOL(tcf_destroy_chain);#ifdef CONFIG_PROC_FSstatic int psched_show(struct seq_file *seq, void *v){	struct timespec ts;	hrtimer_get_res(CLOCK_MONOTONIC, &ts);	seq_printf(seq, "%08x %08x %08x %08x\n",		   (u32)NSEC_PER_USEC, (u32)PSCHED_US2NS(1),		   1000000,		   (u32)NSEC_PER_SEC/(u32)ktime_to_ns(timespec_to_ktime(ts)));	return 0;}static int psched_open(struct inode *inode, struct file *file){	return single_open(file, psched_show, PDE(inode)->data);}static const struct file_operations psched_fops = {	.owner = THIS_MODULE,	.open = psched_open,	.read  = seq_read,	.llseek = seq_lseek,	.release = single_release,};#endifstatic int __init pktsched_init(void){	register_qdisc(&pfifo_qdisc_ops);	register_qdisc(&bfifo_qdisc_ops);	proc_net_fops_create(&init_net, "psched", 0, &psched_fops);	rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL);	rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL);	rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc);	rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL);	rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL);	rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass);	return 0;}subsys_initcall(pktsched_init);EXPORT_SYMBOL(qdisc_get_rtab);EXPORT_SYMBOL(qdisc_put_rtab);EXPORT_SYMBOL(register_qdisc);EXPORT_SYMBOL(unregister_qdisc);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -