summaryrefslogtreecommitdiff
path: root/drivers/net/usb/cdc_ncm.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-14 13:18:27 +0200
committerIngo Molnar <mingo@kernel.org>2012-04-14 13:19:04 +0200
commit6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch)
tree021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/net/usb/cdc_ncm.c
parent682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff)
parenta385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff)
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree), to prepare for tooling changes, and also to pick up v3.4 MM changes that the uprobes code needs to take care of. Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/net/usb/cdc_ncm.c')
-rw-r--r--drivers/net/usb/cdc_ncm.c236
1 files changed, 115 insertions, 121 deletions
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 3a539a9cac54..7adc9f6b0ea1 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1,7 +1,7 @@
/*
* cdc_ncm.c
*
- * Copyright (C) ST-Ericsson 2010-2011
+ * Copyright (C) ST-Ericsson 2010-2012
* Contact: Alexey Orishko <alexey.orishko@stericsson.com>
* Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com>
*
@@ -47,20 +47,19 @@
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/usb.h>
-#include <linux/timer.h>
-#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
#include <linux/atomic.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc.h>
-#define DRIVER_VERSION "04-Aug-2011"
+#define DRIVER_VERSION "14-Mar-2012"
/* CDC NCM subclass 3.2.1 */
#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10
/* Maximum NTB length */
-#define CDC_NCM_NTB_MAX_SIZE_TX 16384 /* bytes */
-#define CDC_NCM_NTB_MAX_SIZE_RX 16384 /* bytes */
+#define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */
+#define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */
/* Minimum value for MaxDatagramSize, ch. 6.2.9 */
#define CDC_NCM_MIN_DATAGRAM_SIZE 1514 /* bytes */
@@ -68,19 +67,18 @@
#define CDC_NCM_MIN_TX_PKT 512 /* bytes */
/* Default value for MaxDatagramSize */
-#define CDC_NCM_MAX_DATAGRAM_SIZE 2048 /* bytes */
+#define CDC_NCM_MAX_DATAGRAM_SIZE 8192 /* bytes */
/*
* Maximum amount of datagrams in NCM Datagram Pointer Table, not counting
- * the last NULL entry. Any additional datagrams in NTB would be discarded.
+ * the last NULL entry.
*/
-#define CDC_NCM_DPT_DATAGRAMS_MAX 32
-
-/* Maximum amount of IN datagrams in NTB */
-#define CDC_NCM_DPT_DATAGRAMS_IN_MAX 0 /* unlimited */
+#define CDC_NCM_DPT_DATAGRAMS_MAX 40
/* Restart the timer, if amount of datagrams is less than given value */
#define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3
+#define CDC_NCM_TIMER_PENDING_CNT 2
+#define CDC_NCM_TIMER_INTERVAL (400UL * NSEC_PER_USEC)
/* The following macro defines the minimum header space */
#define CDC_NCM_MIN_HDR_SIZE \
@@ -94,10 +92,10 @@ struct cdc_ncm_data {
};
struct cdc_ncm_ctx {
- struct cdc_ncm_data rx_ncm;
struct cdc_ncm_data tx_ncm;
struct usb_cdc_ncm_ntb_parameters ncm_parm;
- struct timer_list tx_timer;
+ struct hrtimer tx_timer;
+ struct tasklet_struct bh;
const struct usb_cdc_ncm_desc *func_desc;
const struct usb_cdc_header_desc *header_desc;
@@ -117,6 +115,7 @@ struct cdc_ncm_ctx {
struct sk_buff *tx_rem_skb;
spinlock_t mtx;
+ atomic_t stop;
u32 tx_timer_pending;
u32 tx_curr_offset;
@@ -132,10 +131,13 @@ struct cdc_ncm_ctx {
u16 tx_modulus;
u16 tx_ndp_modulus;
u16 tx_seq;
+ u16 rx_seq;
u16 connected;
};
-static void cdc_ncm_tx_timeout(unsigned long arg);
+static void cdc_ncm_txpath_bh(unsigned long param);
+static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
+static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
static const struct driver_info cdc_ncm_info;
static struct usb_driver cdc_ncm_driver;
static const struct ethtool_ops cdc_ncm_ethtool_ops;
@@ -361,27 +363,25 @@ size_err:
if (err < 0) {
pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
CDC_NCM_MIN_DATAGRAM_SIZE);
- kfree(max_datagram_size);
} else {
ctx->max_datagram_size =
le16_to_cpu(*max_datagram_size);
/* Check Eth descriptor value */
- if (eth_max_sz < CDC_NCM_MAX_DATAGRAM_SIZE) {
- if (ctx->max_datagram_size > eth_max_sz)
+ if (ctx->max_datagram_size > eth_max_sz)
ctx->max_datagram_size = eth_max_sz;
- } else {
- if (ctx->max_datagram_size >
- CDC_NCM_MAX_DATAGRAM_SIZE)
- ctx->max_datagram_size =
+
+ if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
+ ctx->max_datagram_size =
CDC_NCM_MAX_DATAGRAM_SIZE;
- }
if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
ctx->max_datagram_size =
CDC_NCM_MIN_DATAGRAM_SIZE;
/* if value changed, update device */
- err = usb_control_msg(ctx->udev,
+ if (ctx->max_datagram_size !=
+ le16_to_cpu(*max_datagram_size)) {
+ err = usb_control_msg(ctx->udev,
usb_sndctrlpipe(ctx->udev, 0),
USB_CDC_SET_MAX_DATAGRAM_SIZE,
USB_TYPE_CLASS | USB_DIR_OUT
@@ -389,14 +389,14 @@ size_err:
0,
iface_no, max_datagram_size,
2, 1000);
- kfree(max_datagram_size);
-max_dgram_err:
- if (err < 0)
- pr_debug("SET_MAX_DATAGRAM_SIZE failed\n");
+ if (err < 0)
+ pr_debug("SET_MAX_DGRAM_SIZE failed\n");
+ }
}
-
+ kfree(max_datagram_size);
}
+max_dgram_err:
if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN))
ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN;
@@ -441,8 +441,6 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
if (ctx == NULL)
return;
- del_timer_sync(&ctx->tx_timer);
-
if (ctx->tx_rem_skb != NULL) {
dev_kfree_skb_any(ctx->tx_rem_skb);
ctx->tx_rem_skb = NULL;
@@ -469,7 +467,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
if (ctx == NULL)
return -ENODEV;
- init_timer(&ctx->tx_timer);
+ hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
+ ctx->bh.data = (unsigned long)ctx;
+ ctx->bh.func = cdc_ncm_txpath_bh;
+ atomic_set(&ctx->stop, 0);
spin_lock_init(&ctx->mtx);
ctx->netdev = dev->net;
@@ -579,11 +581,7 @@ advance:
if (temp)
goto error2;
- dev_info(&dev->udev->dev, "MAC-Address: "
- "0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
- dev->net->dev_addr[0], dev->net->dev_addr[1],
- dev->net->dev_addr[2], dev->net->dev_addr[3],
- dev->net->dev_addr[4], dev->net->dev_addr[5]);
+ dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
dev->in = usb_rcvbulkpipe(dev->udev,
ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
@@ -621,6 +619,13 @@ static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
if (ctx == NULL)
return; /* no setup */
+ atomic_set(&ctx->stop, 1);
+
+ if (hrtimer_active(&ctx->tx_timer))
+ hrtimer_cancel(&ctx->tx_timer);
+
+ tasklet_kill(&ctx->bh);
+
/* disconnect master --> disconnect slave */
if (intf == ctx->control && ctx->data) {
usb_set_intfdata(ctx->data, NULL);
@@ -791,7 +796,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
ctx->tx_curr_last_offset = last_offset;
/* set the pending count */
if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
- ctx->tx_timer_pending = 2;
+ ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
goto exit_no_skb;
} else {
@@ -871,44 +876,49 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
/* return skb */
ctx->tx_curr_skb = NULL;
+ ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num;
return skb_out;
exit_no_skb:
+ /* Start timer, if there is a remaining skb */
+ if (ctx->tx_curr_skb != NULL)
+ cdc_ncm_tx_timeout_start(ctx);
return NULL;
}
static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
{
/* start timer, if not already started */
- if (timer_pending(&ctx->tx_timer) == 0) {
- ctx->tx_timer.function = &cdc_ncm_tx_timeout;
- ctx->tx_timer.data = (unsigned long)ctx;
- ctx->tx_timer.expires = jiffies + ((HZ + 999) / 1000);
- add_timer(&ctx->tx_timer);
- }
+ if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
+ hrtimer_start(&ctx->tx_timer,
+ ktime_set(0, CDC_NCM_TIMER_INTERVAL),
+ HRTIMER_MODE_REL);
}
-static void cdc_ncm_tx_timeout(unsigned long arg)
+static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)
{
- struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)arg;
- u8 restart;
+ struct cdc_ncm_ctx *ctx =
+ container_of(timer, struct cdc_ncm_ctx, tx_timer);
- spin_lock(&ctx->mtx);
- if (ctx->tx_timer_pending != 0) {
- ctx->tx_timer_pending--;
- restart = 1;
- } else {
- restart = 0;
- }
+ if (!atomic_read(&ctx->stop))
+ tasklet_schedule(&ctx->bh);
+ return HRTIMER_NORESTART;
+}
- spin_unlock(&ctx->mtx);
+static void cdc_ncm_txpath_bh(unsigned long param)
+{
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param;
- if (restart) {
- spin_lock(&ctx->mtx);
+ spin_lock_bh(&ctx->mtx);
+ if (ctx->tx_timer_pending != 0) {
+ ctx->tx_timer_pending--;
cdc_ncm_tx_timeout_start(ctx);
- spin_unlock(&ctx->mtx);
+ spin_unlock_bh(&ctx->mtx);
} else if (ctx->netdev != NULL) {
+ spin_unlock_bh(&ctx->mtx);
+ netif_tx_lock_bh(ctx->netdev);
usbnet_start_xmit(NULL, ctx->netdev);
+ netif_tx_unlock_bh(ctx->netdev);
}
}
@@ -917,7 +927,6 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
{
struct sk_buff *skb_out;
struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
- u8 need_timer = 0;
/*
* The Ethernet API we are using does not support transmitting
@@ -929,19 +938,9 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
if (ctx == NULL)
goto error;
- spin_lock(&ctx->mtx);
+ spin_lock_bh(&ctx->mtx);
skb_out = cdc_ncm_fill_tx_frame(ctx, skb);
- if (ctx->tx_curr_skb != NULL)
- need_timer = 1;
-
- /* Start timer, if there is a remaining skb */
- if (need_timer)
- cdc_ncm_tx_timeout_start(ctx);
-
- if (skb_out)
- dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
-
- spin_unlock(&ctx->mtx);
+ spin_unlock_bh(&ctx->mtx);
return skb_out;
error:
@@ -954,108 +953,103 @@ error:
static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
{
struct sk_buff *skb;
- struct cdc_ncm_ctx *ctx;
- int sumlen;
- int actlen;
- int temp;
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+ int len;
int nframes;
int x;
int offset;
+ struct usb_cdc_ncm_nth16 *nth16;
+ struct usb_cdc_ncm_ndp16 *ndp16;
+ struct usb_cdc_ncm_dpe16 *dpe16;
- ctx = (struct cdc_ncm_ctx *)dev->data[0];
if (ctx == NULL)
goto error;
- actlen = skb_in->len;
- sumlen = CDC_NCM_NTB_MAX_SIZE_RX;
-
- if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) {
+ if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +
+ sizeof(struct usb_cdc_ncm_ndp16))) {
pr_debug("frame too short\n");
goto error;
}
- memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data),
- sizeof(ctx->rx_ncm.nth16));
+ nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data;
- if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) !=
- USB_CDC_NCM_NTH16_SIGN) {
+ if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) {
pr_debug("invalid NTH16 signature <%u>\n",
- le32_to_cpu(ctx->rx_ncm.nth16.dwSignature));
+ le32_to_cpu(nth16->dwSignature));
goto error;
}
- temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength);
- if (temp > sumlen) {
- pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen);
+ len = le16_to_cpu(nth16->wBlockLength);
+ if (len > ctx->rx_max) {
+ pr_debug("unsupported NTB block length %u/%u\n", len,
+ ctx->rx_max);
goto error;
}
- temp = le16_to_cpu(ctx->rx_ncm.nth16.wNdpIndex);
- if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) {
- pr_debug("invalid DPT16 index\n");
+ if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) &&
+ (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
+ !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
+ pr_debug("sequence number glitch prev=%d curr=%d\n",
+ ctx->rx_seq, le16_to_cpu(nth16->wSequence));
+ }
+ ctx->rx_seq = le16_to_cpu(nth16->wSequence);
+
+ len = le16_to_cpu(nth16->wNdpIndex);
+ if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
+ pr_debug("invalid DPT16 index <%u>\n",
+ le16_to_cpu(nth16->wNdpIndex));
goto error;
}
- memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp,
- sizeof(ctx->rx_ncm.ndp16));
+ ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len);
- if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) !=
- USB_CDC_NCM_NDP16_NOCRC_SIGN) {
+ if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
pr_debug("invalid DPT16 signature <%u>\n",
- le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
+ le32_to_cpu(ndp16->dwSignature));
goto error;
}
- if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) <
- USB_CDC_NCM_NDP16_LENGTH_MIN) {
+ if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
pr_debug("invalid DPT16 length <%u>\n",
- le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
+ le32_to_cpu(ndp16->dwSignature));
goto error;
}
- nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) -
+ nframes = ((le16_to_cpu(ndp16->wLength) -
sizeof(struct usb_cdc_ncm_ndp16)) /
sizeof(struct usb_cdc_ncm_dpe16));
nframes--; /* we process NDP entries except for the last one */
- pr_debug("nframes = %u\n", nframes);
-
- temp += sizeof(ctx->rx_ncm.ndp16);
+ len += sizeof(struct usb_cdc_ncm_ndp16);
- if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) {
+ if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) >
+ skb_in->len) {
pr_debug("Invalid nframes = %d\n", nframes);
goto error;
}
- if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) {
- pr_debug("Truncating number of frames from %u to %u\n",
- nframes, CDC_NCM_DPT_DATAGRAMS_MAX);
- nframes = CDC_NCM_DPT_DATAGRAMS_MAX;
- }
-
- memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp,
- nframes * (sizeof(struct usb_cdc_ncm_dpe16)));
+ dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len);
- for (x = 0; x < nframes; x++) {
- offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex);
- temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength);
+ for (x = 0; x < nframes; x++, dpe16++) {
+ offset = le16_to_cpu(dpe16->wDatagramIndex);
+ len = le16_to_cpu(dpe16->wDatagramLength);
/*
* CDC NCM ch. 3.7
* All entries after first NULL entry are to be ignored
*/
- if ((offset == 0) || (temp == 0)) {
+ if ((offset == 0) || (len == 0)) {
if (!x)
goto error; /* empty NTB */
break;
}
/* sanity checking */
- if (((offset + temp) > actlen) ||
- (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) {
+ if (((offset + len) > skb_in->len) ||
+ (len > ctx->rx_max) || (len < ETH_HLEN)) {
pr_debug("invalid frame detected (ignored)"
"offset[%u]=%u, length=%u, skb=%p\n",
- x, offset, temp, skb_in);
+ x, offset, len, skb_in);
if (!x)
goto error;
break;
@@ -1064,9 +1058,9 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
skb = skb_clone(skb_in, GFP_ATOMIC);
if (!skb)
goto error;
- skb->len = temp;
+ skb->len = len;
skb->data = ((u8 *)skb_in->data) + offset;
- skb_set_tail_pointer(skb, temp);
+ skb_set_tail_pointer(skb, len);
usbnet_skb_return(dev, skb);
}
}