diff options
Diffstat (limited to 'net/sched/sch_taprio.c')
| -rw-r--r-- | net/sched/sch_taprio.c | 68 | 
1 files changed, 41 insertions, 27 deletions
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 8c9cfff7fd05..1cb5e41c0ec7 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -2099,11 +2099,8 @@ static int taprio_init(struct Qdisc *sch, struct nlattr *opt,  		return -EOPNOTSUPP;  	} -	/* pre-allocate qdisc, attachment can't fail */ -	q->qdiscs = kcalloc(dev->num_tx_queues, -			    sizeof(q->qdiscs[0]), +	q->qdiscs = kcalloc(dev->num_tx_queues, sizeof(q->qdiscs[0]),  			    GFP_KERNEL); -  	if (!q->qdiscs)  		return -ENOMEM; @@ -2145,25 +2142,32 @@ static void taprio_attach(struct Qdisc *sch)  	/* Attach underlying qdisc */  	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { -		struct Qdisc *qdisc = q->qdiscs[ntx]; -		struct Qdisc *old; +		struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx); +		struct Qdisc *old, *dev_queue_qdisc;  		if (FULL_OFFLOAD_IS_ENABLED(q->flags)) { +			struct Qdisc *qdisc = q->qdiscs[ntx]; + +			/* In offload mode, the root taprio qdisc is bypassed +			 * and the netdev TX queues see the children directly +			 */  			qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; -			old = dev_graft_qdisc(qdisc->dev_queue, qdisc); +			dev_queue_qdisc = qdisc;  		} else { -			old = dev_graft_qdisc(qdisc->dev_queue, sch); -			qdisc_refcount_inc(sch); +			/* In software mode, attach the root taprio qdisc +			 * to all netdev TX queues, so that dev_qdisc_enqueue() +			 * goes through taprio_enqueue(). +			 */ +			dev_queue_qdisc = sch;  		} +		old = dev_graft_qdisc(dev_queue, dev_queue_qdisc); +		/* The qdisc's refcount requires to be elevated once +		 * for each netdev TX queue it is grafted onto +		 */ +		qdisc_refcount_inc(dev_queue_qdisc);  		if (old)  			qdisc_put(old);  	} - -	/* access to the child qdiscs is not needed in offload mode */ -	if (FULL_OFFLOAD_IS_ENABLED(q->flags)) { -		kfree(q->qdiscs); -		q->qdiscs = NULL; -	}  }  static struct netdev_queue *taprio_queue_get(struct Qdisc *sch, @@ -2192,13 +2196,23 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,  	if (dev->flags & IFF_UP)  		dev_deactivate(dev); +	/* In offload mode, the child Qdisc is directly attached to the netdev +	 * TX queue, and thus, we need to keep its refcount elevated in order +	 * to counteract qdisc_graft()'s call to qdisc_put() once per TX queue. +	 * However, save the reference to the new qdisc in the private array in +	 * both software and offload cases, to have an up-to-date reference to +	 * our children. +	 */ +	*old = q->qdiscs[cl - 1];  	if (FULL_OFFLOAD_IS_ENABLED(q->flags)) { -		*old = dev_graft_qdisc(dev_queue, new); -	} else { -		*old = q->qdiscs[cl - 1]; -		q->qdiscs[cl - 1] = new; +		WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old); +		if (new) +			qdisc_refcount_inc(new); +		if (*old) +			qdisc_put(*old);  	} +	q->qdiscs[cl - 1] = new;  	if (new)  		new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; @@ -2436,12 +2450,14 @@ start_error:  static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl)  { -	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); +	struct taprio_sched *q = qdisc_priv(sch); +	struct net_device *dev = qdisc_dev(sch); +	unsigned int ntx = cl - 1; -	if (!dev_queue) +	if (ntx >= dev->num_tx_queues)  		return NULL; -	return rtnl_dereference(dev_queue->qdisc_sleeping); +	return q->qdiscs[ntx];  }  static unsigned long taprio_find(struct Qdisc *sch, u32 classid) @@ -2456,11 +2472,11 @@ static unsigned long taprio_find(struct Qdisc *sch, u32 classid)  static int taprio_dump_class(struct Qdisc *sch, unsigned long cl,  			     struct sk_buff *skb, struct tcmsg *tcm)  { -	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); +	struct Qdisc *child = taprio_leaf(sch, cl);  	tcm->tcm_parent = TC_H_ROOT;  	tcm->tcm_handle |= TC_H_MIN(cl); -	tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; +	tcm->tcm_info = child->handle;  	return 0;  } @@ -2470,16 +2486,14 @@ static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,  	__releases(d->lock)  	__acquires(d->lock)  { -	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); +	struct Qdisc *child = taprio_leaf(sch, cl);  	struct tc_taprio_qopt_offload offload = {  		.cmd = TAPRIO_CMD_QUEUE_STATS,  		.queue_stats = {  			.queue = cl - 1,  		},  	}; -	struct Qdisc *child; -	child = rtnl_dereference(dev_queue->qdisc_sleeping);  	if (gnet_stats_copy_basic(d, NULL, &child->bstats, true) < 0 ||  	    qdisc_qstats_copy(d, child) < 0)  		return -1;  | 
