From patchwork Sun Dec 4 10:10:49 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: sjur.brandeland@stericsson.com X-Patchwork-Id: 129134 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 30EA41007D4 for ; Sun, 4 Dec 2011 21:11:30 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752993Ab1LDKLS (ORCPT ); Sun, 4 Dec 2011 05:11:18 -0500 Received: from mail-ee0-f46.google.com ([74.125.83.46]:40103 "EHLO mail-ee0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752686Ab1LDKLN (ORCPT ); Sun, 4 Dec 2011 05:11:13 -0500 Received: by eeaq14 with SMTP id q14so1458960eea.19 for ; Sun, 04 Dec 2011 02:11:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=lNH4my29cmSsz07y+ZKTcDMTxpMGDFF9nWlq3uZcb28=; b=MyGvaBWTb4q/NTrUxa1xK3WJIROq2DhYRYcaCA+EjDr2f30gMTypcKQNdeuVxdaW8W 8VrmhimkK9d9ATwsJDDhtprXoZMDUY02r5sDDsmB5vMZHgaGwyugIw+GEMiOxHT3dpBc caIaEyTJmOIA405cLjTnKgddzG4YQjNf0Dl58= Received: by 10.14.10.222 with SMTP id 70mr310564eev.131.1322993472181; Sun, 04 Dec 2011 02:11:12 -0800 (PST) Received: from localhost.localdomain (125.80-203-142.nextgentel.com. [80.203.142.125]) by mx.google.com with ESMTPS id q28sm45772369eea.6.2011.12.04.02.11.10 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 04 Dec 2011 02:11:11 -0800 (PST) From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= To: netdev@vger.kernel.org, David Miller Cc: Alexey Orishko , Eric Dumazet , =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Subject: [PATCHv2 net-next 3/4] caif: Add support for flow-control on device's tx-queue Date: Sun, 4 Dec 2011 11:10:49 +0100 Message-Id: <1322993450-8802-4-git-send-email-sjur.brandeland@stericsson.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1322993450-8802-1-git-send-email-sjur.brandeland@stericsson.com> References: <1322993450-8802-1-git-send-email-sjur.brandeland@stericsson.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Flow control is implemented by inspecting the qdisc queue length in order to detect potential overflow on the TX queue. When a threshold is reached flow-off is sent upwards in the CAIF stack. At the same time the skb->destructor is hi-jacked by orphaning the SKB and the original destructor is replaced with a "flow-on" callback. When the "hi-jacked" SKB is consumed the queue should be empty, and the "flow-on" callback is called and xon is sent upwards in the CAIF stack. Signed-off-by: Sjur Brændeland --- Changes from V1: - Add RCU protection when accessing qdisc. - Add a spin_lock when setting xoff/xon. net/caif/caif_dev.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 84 insertions(+), 1 deletions(-) diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index f7be07e..1980404 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ struct caif_device_entry { struct list_head list; struct net_device *netdev; int __percpu *pcpu_refcnt; + spinlock_t flow_lock; + bool xoff; }; struct caif_device_entry_list { @@ -49,6 +52,7 @@ struct caif_net { }; static int caif_net_id; +static int q_high = 50; /* Percent */ struct cfcnfg *get_cfcnfg(struct net *net) { @@ -127,17 +131,94 @@ static struct caif_device_entry *caif_get(struct net_device *dev) return NULL; } +void caif_flow_cb(struct sk_buff *skb) +{ + struct caif_device_entry *caifd; + bool send_xoff; + + WARN_ON(skb->dev == NULL); + + rcu_read_lock(); + caifd = caif_get(skb->dev); + caifd_hold(caifd); + rcu_read_unlock(); + + spin_lock_bh(&caifd->flow_lock); + send_xoff = caifd->xoff; + caifd->xoff = 0; + spin_unlock_bh(&caifd->flow_lock); + + if (send_xoff) + caifd->layer.up-> + ctrlcmd(caifd->layer.up, + _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, + caifd->layer.id); + caifd_put(caifd); +} + static int transmit(struct cflayer *layer, struct cfpkt *pkt) { - int err; + int err, high = 0, qlen = 0; + struct caif_dev_common *caifdev; struct caif_device_entry *caifd = container_of(layer, struct caif_device_entry, layer); struct sk_buff *skb; + struct netdev_queue *txq; + + rcu_read_lock_bh(); skb = cfpkt_tonative(pkt); skb->dev = caifd->netdev; skb_reset_network_header(skb); skb->protocol = htons(ETH_P_CAIF); + caifdev = netdev_priv(caifd->netdev); + + /* Check if we need to handle xoff */ + if (likely(caifd->netdev->tx_queue_len == 0)) + goto noxoff; + + if (unlikely(caifd->xoff)) + goto noxoff; + + if (likely(!netif_queue_stopped(caifd->netdev))) { + /* If we run with a TX queue, check if the queue is too long*/ + txq = netdev_get_tx_queue(skb->dev, 0); + qlen = qdisc_qlen(rcu_dereference_bh(txq->qdisc)); + + if (likely(qlen == 0)) + goto noxoff; + + high = (caifd->netdev->tx_queue_len * q_high) / 100; + if (likely(qlen < high)) + goto noxoff; + } + + /* Hold lock while accessing xoff */ + spin_lock_bh(&caifd->flow_lock); + if (caifd->xoff) { + spin_unlock_bh(&caifd->flow_lock); + goto noxoff; + } + + /* + * Handle flow off, we do this by temporary hi-jacking this + * skb's destructor function, and replace it with our own + * flow-on callback. The callback will set flow-on and call + * the original destructor. + */ + + pr_debug("queue has stopped(%d) or is full (%d > %d)\n", + netif_queue_stopped(caifd->netdev), + qlen, high); + caifd->xoff = 1; + spin_unlock_bh(&caifd->flow_lock); + skb_orphan(skb); + + caifd->layer.up->ctrlcmd(caifd->layer.up, + _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, + caifd->layer.id); +noxoff: + rcu_read_unlock_bh(); err = dev_queue_xmit(skb); if (err > 0) @@ -233,6 +314,7 @@ void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, if (!caifd) return; *layer = &caifd->layer; + spin_lock_init(&caifd->flow_lock); switch (caifdev->link_select) { case CAIF_LINK_HIGH_BANDW: @@ -316,6 +398,7 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, break; } + caifd->xoff = 0; cfcnfg_set_phy_state(cfg, &caifd->layer, true); rcu_read_unlock();