diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a44118b..388841c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -178,6 +178,7 @@ enum { struct neighbour; struct neigh_parms; struct sk_buff; +struct nlattr; struct netif_rx_stats { @@ -636,6 +637,12 @@ struct net_device_ops { int (*ndo_fcoe_ddp_done)(struct net_device *dev, u16 xid); #endif + int (*ndo_queue_config)(struct net_device *dev, + unsigned int qnum, + const struct nlattr *nla[]); + int (*ndo_get_queue_config)(struct net_device *dev, + struct sk_buff *skb, + unsigned int qnum); }; /* diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index d51a2b3..742db43 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -518,4 +518,14 @@ struct tc_drr_stats __u32 deficit; }; +/* MQ */ + +enum +{ + TCA_MQ_UNSPEC, + __TCA_MQ_MAX +}; + +#define TCA_MQ_MAX (__TCA_MQ_MAX - 1) + #endif diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index dd5ee02..13132b9 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -171,15 +171,61 @@ static void mq_put(struct Qdisc *sch, unsigned long cl) return; } +static const struct nla_policy mq_policy[TCA_MQ_MAX + 1] = { + /* nothing so far */ +}; + +static int mq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + struct nlattr **tca, unsigned long *arg) +{ + struct net_device *dev = qdisc_dev(sch); + struct nlattr *tb[TCA_MQ_MAX + 1]; + unsigned long ntx; + int err; + + if (*arg == 0) + return -EOPNOTSUPP; + if (mq_queue_get(sch, *arg)) + return -ENOENT; + ntx = *arg - 1; + + if (tca == NULL) + return -EINVAL; + + err = nla_parse_nested(tb, TCA_MQ_MAX, tca[TCA_OPTIONS], mq_policy); + if (err < 0) + return err; + + if (dev->netdev_ops->ndo_queue_config == NULL) + return -EOPNOTSUPP; + return dev->netdev_ops->ndo_queue_config(dev, ntx, (void *)tb); +} + static int mq_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + struct net_device *dev = qdisc_dev(sch); + struct nlattr *nest; tcm->tcm_parent = TC_H_ROOT; tcm->tcm_handle |= TC_H_MIN(cl); tcm->tcm_info = dev_queue->qdisc_sleeping->handle; - return 0; + + if (dev->netdev_ops->ndo_get_queue_config) { + nest = nla_nest_start(skb, TCA_OPTIONS); + if (nest == NULL) + goto nla_put_failure; + if (dev->netdev_ops->ndo_get_queue_config(dev, skb, cl - 1) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest); + } + + return skb->len; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; } static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, @@ -214,6 +260,7 @@ static void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg) static const struct Qdisc_class_ops mq_class_ops = { .select_queue = mq_select_queue, + .change = mq_change_class, .graft = mq_graft, .leaf = mq_leaf, .get = mq_get,