diff options
Diffstat (limited to 'drivers/net/usb/cdc_ncm.c')
| -rw-r--r-- | drivers/net/usb/cdc_ncm.c | 70 | 
1 files changed, 50 insertions, 20 deletions
| diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index b5cec1824a78..d103a1d4fb36 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -89,6 +89,8 @@ static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = {  	CDC_NCM_SIMPLE_STAT(rx_ntbs),  }; +#define CDC_NCM_LOW_MEM_MAX_CNT 10 +  static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset)  {  	switch (sset) { @@ -1017,7 +1019,7 @@ static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remai  	if (skb->len + align > max)  		align = max - skb->len;  	if (align && skb_tailroom(skb) >= align) -		memset(skb_put(skb, align), 0, align); +		skb_put_zero(skb, align);  }  /* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly @@ -1055,10 +1057,10 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  	/* align new NDP */  	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) -		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); +		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);  	/* verify that there is room for the NDP and the datagram (reserve) */ -	if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) +	if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)  		return NULL;  	/* link to it */ @@ -1069,7 +1071,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_  	/* push a new empty NDP */  	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) -		ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size); +		ndp16 = skb_put_zero(skb, ctx->max_ndp_size);  	else  		ndp16 = ctx->delayed_ndp16; @@ -1111,16 +1113,44 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  	/* allocate a new OUT skb */  	if (!skb_out) { -		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC); +		if (ctx->tx_low_mem_val == 0) { +			ctx->tx_curr_size = ctx->tx_max; +			skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); +			/* If the memory allocation fails we will wait longer +			 * each time before attempting another full size +			 * allocation again to not overload the system +			 * further. +			 */ +			if (skb_out == NULL) { +				ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1, +							      (unsigned)CDC_NCM_LOW_MEM_MAX_CNT); +				ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt; +			} +		}  		if (skb_out == NULL) { -			if (skb != NULL) { -				dev_kfree_skb_any(skb); -				dev->net->stats.tx_dropped++; +			/* See if a very small allocation is possible. +			 * We will send this packet immediately and hope +			 * that there is more memory available later. +			 */ +			if (skb) +				ctx->tx_curr_size = max(skb->len, +					(u32)USB_CDC_NCM_NTB_MIN_OUT_SIZE); +			else +				ctx->tx_curr_size = USB_CDC_NCM_NTB_MIN_OUT_SIZE; +			skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); + +			/* No allocation possible so we will abort */ +			if (skb_out == NULL) { +				if (skb != NULL) { +					dev_kfree_skb_any(skb); +					dev->net->stats.tx_dropped++; +				} +				goto exit_no_skb;  			} -			goto exit_no_skb; +			ctx->tx_low_mem_val--;  		}  		/* fill out the initial 16-bit NTB header */ -		nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16)); +		nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16));  		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);  		nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));  		nth16->wSequence = cpu_to_le16(ctx->tx_seq++); @@ -1148,10 +1178,10 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  		ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);  		/* align beginning of next frame */ -		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max); +		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size);  		/* check if we had enough room left for both NDP and frame */ -		if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_max) { +		if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) {  			if (n == 0) {  				/* won't fit, MTU problem? */  				dev_kfree_skb_any(skb); @@ -1180,7 +1210,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  		ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);  		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);  		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); -		memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); +		skb_put_data(skb_out, skb->data, skb->len);  		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */  		dev_kfree_skb_any(skb);  		skb = NULL; @@ -1227,9 +1257,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  	/* If requested, put NDP at end of frame. */  	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {  		nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; -		cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_max); +		cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);  		nth16->wNdpIndex = cpu_to_le16(skb_out->len); -		memcpy(skb_put(skb_out, ctx->max_ndp_size), ctx->delayed_ndp16, ctx->max_ndp_size); +		skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);  		/* Zero out delayed NDP - signature checking will naturally fail. */  		ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size); @@ -1246,11 +1276,11 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)  	 */  	if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&  	    skb_out->len > ctx->min_tx_pkt) { -		padding_count = ctx->tx_max - skb_out->len; -		memset(skb_put(skb_out, padding_count), 0, padding_count); -	} else if (skb_out->len < ctx->tx_max && +		padding_count = ctx->tx_curr_size - skb_out->len; +		skb_put_zero(skb_out, padding_count); +	} else if (skb_out->len < ctx->tx_curr_size &&  		   (skb_out->len % dev->maxpacket) == 0) { -		*skb_put(skb_out, 1) = 0;	/* force short packet */ +		skb_put_u8(skb_out, 0);	/* force short packet */  	}  	/* set final frame length */ @@ -1497,7 +1527,7 @@ next_ndp:  			skb = netdev_alloc_skb_ip_align(dev->net,  len);  			if (!skb)  				goto error; -			memcpy(skb_put(skb, len), skb_in->data + offset, len); +			skb_put_data(skb, skb_in->data + offset, len);  			usbnet_skb_return(dev, skb);  			payload += len;	/* count payload bytes in this NTB */  		} | 
