Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 175f9c1b authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by David S. Miller
Browse files

net_sched: Add size table for qdiscs

Add size table functions for qdiscs and calculate packet size in
qdisc_enqueue().

Based on patch by Patrick McHardy
 http://marc.info/?l=linux-netdev&m=115201979221729&w=2



Signed-off-by: default avatarJussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0abf77e5
Loading
Loading
Loading
Loading
+20 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,26 @@ struct tc_ratespec


#define TC_RTAB_SIZE	1024
#define TC_RTAB_SIZE	1024


struct tc_sizespec {
	unsigned char	cell_log;
	unsigned char	size_log;
	short		cell_align;
	int		overhead;
	unsigned int	linklayer;
	unsigned int	mpu;
	unsigned int	mtu;
	unsigned int	tsize;
};

enum {
	TCA_STAB_UNSPEC,
	TCA_STAB_BASE,
	TCA_STAB_DATA,
	__TCA_STAB_MAX
};

#define TCA_STAB_MAX (__TCA_STAB_MAX - 1)

/* FIFO section */
/* FIFO section */


struct tc_fifo_qopt
struct tc_fifo_qopt
+1 −0
Original line number Original line Diff line number Diff line
@@ -482,6 +482,7 @@ enum
	TCA_RATE,
	TCA_RATE,
	TCA_FCNT,
	TCA_FCNT,
	TCA_STATS2,
	TCA_STATS2,
	TCA_STAB,
	__TCA_MAX
	__TCA_MAX
};
};


+1 −0
Original line number Original line Diff line number Diff line
@@ -83,6 +83,7 @@ extern struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle);
extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
		struct nlattr *tab);
		struct nlattr *tab);
extern void qdisc_put_rtab(struct qdisc_rate_table *tab);
extern void qdisc_put_rtab(struct qdisc_rate_table *tab);
extern void qdisc_put_stab(struct qdisc_size_table *tab);


extern void __qdisc_run(struct Qdisc *q);
extern void __qdisc_run(struct Qdisc *q);


+24 −1
Original line number Original line Diff line number Diff line
@@ -29,6 +29,13 @@ enum qdisc_state_t
	__QDISC_STATE_SCHED,
	__QDISC_STATE_SCHED,
};
};


struct qdisc_size_table {
	struct list_head	list;
	struct tc_sizespec	szopts;
	int			refcnt;
	u16			data[];
};

struct Qdisc
struct Qdisc
{
{
	int 			(*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
	int 			(*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
@@ -39,6 +46,7 @@ struct Qdisc
#define TCQ_F_INGRESS	4
#define TCQ_F_INGRESS	4
	int			padded;
	int			padded;
	struct Qdisc_ops	*ops;
	struct Qdisc_ops	*ops;
	struct qdisc_size_table	*stab;
	u32			handle;
	u32			handle;
	u32			parent;
	u32			parent;
	atomic_t		refcnt;
	atomic_t		refcnt;
@@ -165,6 +173,16 @@ struct tcf_proto
	struct tcf_proto_ops	*ops;
	struct tcf_proto_ops	*ops;
};
};


struct qdisc_skb_cb {
	unsigned int		pkt_len;
	char			data[];
};

static inline struct qdisc_skb_cb *qdisc_skb_cb(struct sk_buff *skb)
{
	return (struct qdisc_skb_cb *)skb->cb;
}

static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc)
static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc)
{
{
	return &qdisc->q.lock;
	return &qdisc->q.lock;
@@ -257,6 +275,8 @@ extern struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
extern struct Qdisc *qdisc_create_dflt(struct net_device *dev,
extern struct Qdisc *qdisc_create_dflt(struct net_device *dev,
				       struct netdev_queue *dev_queue,
				       struct netdev_queue *dev_queue,
				       struct Qdisc_ops *ops, u32 parentid);
				       struct Qdisc_ops *ops, u32 parentid);
extern void qdisc_calculate_pkt_len(struct sk_buff *skb,
				   struct qdisc_size_table *stab);
extern void tcf_destroy(struct tcf_proto *tp);
extern void tcf_destroy(struct tcf_proto *tp);
extern void tcf_destroy_chain(struct tcf_proto **fl);
extern void tcf_destroy_chain(struct tcf_proto **fl);


@@ -308,16 +328,19 @@ static inline bool qdisc_tx_is_noop(const struct net_device *dev)


static inline unsigned int qdisc_pkt_len(struct sk_buff *skb)
static inline unsigned int qdisc_pkt_len(struct sk_buff *skb)
{
{
	return skb->len;
	return qdisc_skb_cb(skb)->pkt_len;
}
}


static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
{
	if (sch->stab)
		qdisc_calculate_pkt_len(skb, sch->stab);
	return sch->enqueue(skb, sch);
	return sch->enqueue(skb, sch);
}
}


static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
{
{
	qdisc_skb_cb(skb)->pkt_len = skb->len;
	return qdisc_enqueue(skb, sch);
	return qdisc_enqueue(skb, sch);
}
}


+149 −2
Original line number Original line Diff line number Diff line
@@ -286,6 +286,129 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab)
}
}
EXPORT_SYMBOL(qdisc_put_rtab);
EXPORT_SYMBOL(qdisc_put_rtab);


static LIST_HEAD(qdisc_stab_list);
static DEFINE_SPINLOCK(qdisc_stab_lock);

static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = {
	[TCA_STAB_BASE]	= { .len = sizeof(struct tc_sizespec) },
	[TCA_STAB_DATA] = { .type = NLA_BINARY },
};

static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
{
	struct nlattr *tb[TCA_STAB_MAX + 1];
	struct qdisc_size_table *stab;
	struct tc_sizespec *s;
	unsigned int tsize = 0;
	u16 *tab = NULL;
	int err;

	err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy);
	if (err < 0)
		return ERR_PTR(err);
	if (!tb[TCA_STAB_BASE])
		return ERR_PTR(-EINVAL);

	s = nla_data(tb[TCA_STAB_BASE]);

	if (s->tsize > 0) {
		if (!tb[TCA_STAB_DATA])
			return ERR_PTR(-EINVAL);
		tab = nla_data(tb[TCA_STAB_DATA]);
		tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
	}

	if (!s || tsize != s->tsize || (!tab && tsize > 0))
		return ERR_PTR(-EINVAL);

	spin_lock(&qdisc_stab_lock);

	list_for_each_entry(stab, &qdisc_stab_list, list) {
		if (memcmp(&stab->szopts, s, sizeof(*s)))
			continue;
		if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16)))
			continue;
		stab->refcnt++;
		spin_unlock(&qdisc_stab_lock);
		return stab;
	}

	spin_unlock(&qdisc_stab_lock);

	stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL);
	if (!stab)
		return ERR_PTR(-ENOMEM);

	stab->refcnt = 1;
	stab->szopts = *s;
	if (tsize > 0)
		memcpy(stab->data, tab, tsize * sizeof(u16));

	spin_lock(&qdisc_stab_lock);
	list_add_tail(&stab->list, &qdisc_stab_list);
	spin_unlock(&qdisc_stab_lock);

	return stab;
}

void qdisc_put_stab(struct qdisc_size_table *tab)
{
	if (!tab)
		return;

	spin_lock(&qdisc_stab_lock);

	if (--tab->refcnt == 0) {
		list_del(&tab->list);
		kfree(tab);
	}

	spin_unlock(&qdisc_stab_lock);
}
EXPORT_SYMBOL(qdisc_put_stab);

static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab)
{
	struct nlattr *nest;

	nest = nla_nest_start(skb, TCA_STAB);
	NLA_PUT(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts);
	nla_nest_end(skb, nest);

	return skb->len;

nla_put_failure:
	return -1;
}

void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab)
{
	int pkt_len, slot;

	pkt_len = skb->len + stab->szopts.overhead;
	if (unlikely(!stab->szopts.tsize))
		goto out;

	slot = pkt_len + stab->szopts.cell_align;
	if (unlikely(slot < 0))
		slot = 0;

	slot >>= stab->szopts.cell_log;
	if (likely(slot < stab->szopts.tsize))
		pkt_len = stab->data[slot];
	else
		pkt_len = stab->data[stab->szopts.tsize - 1] *
				(slot / stab->szopts.tsize) +
				stab->data[slot % stab->szopts.tsize];

	pkt_len <<= stab->szopts.size_log;
out:
	if (unlikely(pkt_len < 1))
		pkt_len = 1;
	qdisc_skb_cb(skb)->pkt_len = pkt_len;
}
EXPORT_SYMBOL(qdisc_calculate_pkt_len);

static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
{
{
	struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
	struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
@@ -613,6 +736,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
	struct nlattr *kind = tca[TCA_KIND];
	struct nlattr *kind = tca[TCA_KIND];
	struct Qdisc *sch;
	struct Qdisc *sch;
	struct Qdisc_ops *ops;
	struct Qdisc_ops *ops;
	struct qdisc_size_table *stab;


	ops = qdisc_lookup_ops(kind);
	ops = qdisc_lookup_ops(kind);
#ifdef CONFIG_KMOD
#ifdef CONFIG_KMOD
@@ -670,6 +794,14 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
	sch->handle = handle;
	sch->handle = handle;


	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
		if (tca[TCA_STAB]) {
			stab = qdisc_get_stab(tca[TCA_STAB]);
			if (IS_ERR(stab)) {
				err = PTR_ERR(stab);
				goto err_out3;
			}
			sch->stab = stab;
		}
		if (tca[TCA_RATE]) {
		if (tca[TCA_RATE]) {
			err = gen_new_estimator(&sch->bstats, &sch->rate_est,
			err = gen_new_estimator(&sch->bstats, &sch->rate_est,
						qdisc_root_lock(sch),
						qdisc_root_lock(sch),
@@ -691,6 +823,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
		return sch;
		return sch;
	}
	}
err_out3:
err_out3:
	qdisc_put_stab(sch->stab);
	dev_put(dev);
	dev_put(dev);
	kfree((char *) sch - sch->padded);
	kfree((char *) sch - sch->padded);
err_out2:
err_out2:
@@ -702,15 +835,26 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,


static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
{
{
	if (tca[TCA_OPTIONS]) {
	struct qdisc_size_table *stab = NULL;
		int err;
	int err = 0;


	if (tca[TCA_OPTIONS]) {
		if (sch->ops->change == NULL)
		if (sch->ops->change == NULL)
			return -EINVAL;
			return -EINVAL;
		err = sch->ops->change(sch, tca[TCA_OPTIONS]);
		err = sch->ops->change(sch, tca[TCA_OPTIONS]);
		if (err)
		if (err)
			return err;
			return err;
	}
	}

	if (tca[TCA_STAB]) {
		stab = qdisc_get_stab(tca[TCA_STAB]);
		if (IS_ERR(stab))
			return PTR_ERR(stab);
	}

	qdisc_put_stab(sch->stab);
	sch->stab = stab;

	if (tca[TCA_RATE])
	if (tca[TCA_RATE])
		gen_replace_estimator(&sch->bstats, &sch->rate_est,
		gen_replace_estimator(&sch->bstats, &sch->rate_est,
				      qdisc_root_lock(sch), tca[TCA_RATE]);
				      qdisc_root_lock(sch), tca[TCA_RATE]);
@@ -994,6 +1138,9 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
		goto nla_put_failure;
		goto nla_put_failure;
	q->qstats.qlen = q->q.qlen;
	q->qstats.qlen = q->q.qlen;


	if (q->stab && qdisc_dump_stab(skb, q->stab) < 0)
		goto nla_put_failure;

	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
					 TCA_XSTATS, qdisc_root_lock(q), &d) < 0)
					 TCA_XSTATS, qdisc_root_lock(q), &d) < 0)
		goto nla_put_failure;
		goto nla_put_failure;
Loading