📄 sch_api.c
字号:
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){ spin_lock_bh(st->lock); RTA_PUT(skb, TCA_STATS, (char*)&st->lock - (char*)st, st); spin_unlock_bh(st->lock); return 0;rtattr_failure: spin_unlock_bh(st->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 ? q->dev->ifindex : 0; 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)) 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(&qdisc_tree_lock); for (q = dev->qdisc_list, q_idx = 0; q; q = q->next, q_idx++) { if (q_idx < s_q_idx) continue; if (tc_fill_qdisc(skb, q, 0, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) { read_unlock(&qdisc_tree_lock); goto done; } } read_unlock(&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 ? q->dev->ifindex : 0; 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]; read_lock(&qdisc_tree_lock); for (q=dev->qdisc_list, t=0; q; q = q->next, t++) { if (t < s_t) continue; if (!q->ops->cl_ops) continue; if (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle) 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; } read_unlock(&qdisc_tree_lock); cb->args[0] = t; dev_put(dev); return skb->len;}#endifint psched_us_per_tick = 1;int psched_tick_per_us = 1;#ifdef CONFIG_PROC_FSstatic int psched_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data){ int len; len = sprintf(buffer, "%08x %08x %08x %08x\n", psched_tick_per_us, psched_us_per_tick, 1000000, HZ); len -= offset; if (len > length) len = length; if(len < 0) len = 0; *start = buffer + offset; *eof = 1; return len;}#endif#if PSCHED_CLOCK_SOURCE == PSCHED_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;}#endifpsched_time_t psched_time_base;#if PSCHED_CLOCK_SOURCE == PSCHED_CPUpsched_tdiff_t psched_clock_per_hz;int psched_clock_scale;#endif#ifdef PSCHED_WATCHERPSCHED_WATCHER psched_time_mark;static void psched_tick(unsigned long);static struct timer_list psched_timer = { function: psched_tick };static void psched_tick(unsigned long dummy){#if PSCHED_CLOCK_SOURCE == PSCHED_CPU psched_time_t dummy_stamp; PSCHED_GET_TIME(dummy_stamp); /* It is OK up to 4GHz cpu */ psched_timer.expires = jiffies + 1*HZ;#else unsigned long now = jiffies; psched_time_base = ((u64)now)<<PSCHED_JSCALE; psched_time_mark = now; psched_timer.expires = now + 60*60*HZ;#endif add_timer(&psched_timer);}#endif#if PSCHED_CLOCK_SOURCE == PSCHED_CPUint __init psched_calibrate_clock(void){ psched_time_t stamp, stamp1; struct timeval tv, tv1; psched_tdiff_t delay; long rdelay; unsigned long stop;#ifdef PSCHED_WATCHER psched_tick(0);#endif stop = jiffies + HZ/10; PSCHED_GET_TIME(stamp); do_gettimeofday(&tv); while (time_before(jiffies, stop)) barrier(); 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;}#endifint __init pktsched_init(void){#ifdef CONFIG_RTNETLINK struct rtnetlink_link *link_p;#endif#if PSCHED_CLOCK_SOURCE == PSCHED_CPU if (psched_calibrate_clock() < 0) return -1;#elif PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES psched_tick_per_us = HZ<<PSCHED_JSCALE; psched_us_per_tick = 1000000;#ifdef PSCHED_WATCHER psched_tick(0);#endif#endif#ifdef CONFIG_RTNETLINK 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; }#endif#define INIT_QDISC(name) { \ extern struct Qdisc_ops name##_qdisc_ops; \ register_qdisc(& name##_qdisc_ops); \ } INIT_QDISC(pfifo); INIT_QDISC(bfifo);#ifdef CONFIG_NET_SCH_CBQ INIT_QDISC(cbq);#endif#ifdef CONFIG_NET_SCH_CSZ INIT_QDISC(csz);#endif#ifdef CONFIG_NET_SCH_HPFQ INIT_QDISC(hpfq);#endif#ifdef CONFIG_NET_SCH_HFSC INIT_QDISC(hfsc);#endif#ifdef CONFIG_NET_SCH_RED INIT_QDISC(red);#endif#ifdef CONFIG_NET_SCH_GRED INIT_QDISC(gred);#endif#ifdef CONFIG_NET_SCH_INGRESS INIT_QDISC(ingress);#endif#ifdef CONFIG_NET_SCH_DSMARK INIT_QDISC(dsmark);#endif#ifdef CONFIG_NET_SCH_SFQ INIT_QDISC(sfq);#endif#ifdef CONFIG_NET_SCH_TBF INIT_QDISC(tbf);#endif#ifdef CONFIG_NET_SCH_TEQL teql_init();#endif#ifdef CONFIG_NET_SCH_PRIO INIT_QDISC(prio);#endif#ifdef CONFIG_NET_SCH_ATM INIT_QDISC(atm);#endif#ifdef CONFIG_NET_CLS tc_filter_init();#endif#ifdef CONFIG_PROC_FS create_proc_read_entry("net/psched", 0, 0, psched_read_proc, NULL);#endif return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -