📄 sch_api.c
字号:
struct Qdisc *q = NULL; struct Qdisc *p = NULL; int err; if ((dev = __dev_get_by_index(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, tca, &err); else q = qdisc_create(dev, tcm->tcm_handle, tca, &err); if (q == NULL) return err;graft: if (1) { struct Qdisc *old_q = NULL; err = qdisc_graft(dev, p, clid, q, &old_q); if (err) { if (q) { spin_lock_bh(&dev->queue_lock); qdisc_destroy(q); spin_unlock_bh(&dev->queue_lock); } return err; } qdisc_notify(skb, n, clid, old_q, q); if (old_q) { spin_lock_bh(&dev->queue_lock); qdisc_destroy(old_q); spin_unlock_bh(&dev->queue_lock); } } return 0;}int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st, spinlock_t *lock){ spin_lock_bh(lock); RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), st); spin_unlock_bh(lock); return 0;rtattr_failure: spin_unlock_bh(lock); return -1;}static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, u32 pid, u32 seq, unsigned flags, int event){ struct tcmsg *tcm; struct nlmsghdr *nlh; unsigned char *b = skb->tail; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*tcm)); nlh->nlmsg_flags = flags; tcm = NLMSG_DATA(nlh); tcm->tcm_family = AF_UNSPEC; 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->stats.qlen = q->q.qlen; if (qdisc_copy_stats(skb, &q->stats, q->stats_lock)) goto rtattr_failure; nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); 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, RTMGRP_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); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; if (idx > s_idx) s_q_idx = 0; read_lock_bh(&qdisc_tree_lock); 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) { read_unlock_bh(&qdisc_tree_lock); goto done; } q_idx++; } read_unlock_bh(&qdisc_tree_lock); }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(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, unsigned flags, int event){ struct tcmsg *tcm; struct nlmsghdr *nlh; unsigned char *b = skb->tail; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*tcm)); nlh->nlmsg_flags = 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 (q->ops->cl_ops->dump && q->ops->cl_ops->dump(q, cl, skb, tcm) < 0) goto rtattr_failure; nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); 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, RTMGRP_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(tcm->tcm_ifindex)) == NULL) return 0; s_t = cb->args[0]; t = 0; read_lock_bh(&qdisc_tree_lock); 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++; } read_unlock_bh(&qdisc_tree_lock); cb->args[0] = t; dev_put(dev); return skb->len;}int psched_us_per_tick = 1;int psched_tick_per_us = 1;#ifdef CONFIG_PROC_FSstatic int psched_show(struct seq_file *seq, void *v){ seq_printf(seq, "%08x %08x %08x %08x\n", psched_tick_per_us, psched_us_per_tick, 1000000, HZ); return 0;}static int psched_open(struct inode *inode, struct file *file){ return single_open(file, psched_show, PDE(inode)->data);}static struct file_operations psched_fops = { .owner = THIS_MODULE, .open = psched_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,}; #endif#ifdef CONFIG_NET_SCH_CLK_GETTIMEOFDAYint psched_tod_diff(int delta_sec, int bound){ int delta; if (bound <= 1000000 || delta_sec > (0x7FFFFFFF/1000000)-1) return bound; delta = delta_sec * 1000000; if (delta > bound) delta = bound; return delta;}EXPORT_SYMBOL(psched_tod_diff);#endif#ifdef CONFIG_NET_SCH_CLK_CPUpsched_tdiff_t psched_clock_per_hz;int psched_clock_scale;EXPORT_SYMBOL(psched_clock_per_hz);EXPORT_SYMBOL(psched_clock_scale);psched_time_t psched_time_base;cycles_t psched_time_mark;EXPORT_SYMBOL(psched_time_mark);EXPORT_SYMBOL(psched_time_base);/* * Periodically adjust psched_time_base to avoid overflow * with 32-bit get_cycles(). Safe up to 4GHz CPU. */static void psched_tick(unsigned long);static struct timer_list psched_timer = TIMER_INITIALIZER(psched_tick, 0, 0);static void psched_tick(unsigned long dummy){ if (sizeof(cycles_t) == sizeof(u32)) { psched_time_t dummy_stamp; PSCHED_GET_TIME(dummy_stamp); psched_timer.expires = jiffies + 1*HZ; add_timer(&psched_timer); }}int __init psched_calibrate_clock(void){ psched_time_t stamp, stamp1; struct timeval tv, tv1; psched_tdiff_t delay; long rdelay; unsigned long stop; psched_tick(0); stop = jiffies + HZ/10; PSCHED_GET_TIME(stamp); do_gettimeofday(&tv); while (time_before(jiffies, stop)) { barrier(); cpu_relax(); } PSCHED_GET_TIME(stamp1); do_gettimeofday(&tv1); delay = PSCHED_TDIFF(stamp1, stamp); rdelay = tv1.tv_usec - tv.tv_usec; rdelay += (tv1.tv_sec - tv.tv_sec)*1000000; if (rdelay > delay) return -1; delay /= rdelay; psched_tick_per_us = delay; while ((delay>>=1) != 0) psched_clock_scale++; psched_us_per_tick = 1<<psched_clock_scale; psched_clock_per_hz = (psched_tick_per_us*(1000000/HZ))>>psched_clock_scale; return 0;}#endifstatic int __init pktsched_init(void){ struct rtnetlink_link *link_p;#ifdef CONFIG_NET_SCH_CLK_CPU if (psched_calibrate_clock() < 0) return -1;#elif defined(CONFIG_NET_SCH_CLK_JIFFIES) psched_tick_per_us = HZ<<PSCHED_JSCALE; psched_us_per_tick = 1000000;#endif link_p = rtnetlink_links[PF_UNSPEC]; /* Setup rtnetlink links. It is made here to avoid exporting large number of public symbols. */ if (link_p) { link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc; link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc; link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc; link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc; link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass; } register_qdisc(&pfifo_qdisc_ops); register_qdisc(&bfifo_qdisc_ops); proc_net_fops_create("psched", 0, &psched_fops); return 0;}subsys_initcall(pktsched_init);EXPORT_SYMBOL(qdisc_copy_stats);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 + -