aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2015-12-23 07:42:43 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-23 22:52:08 -0500
commit1dfddff5fcd869fcab0c52fafae099dfa435a935 (patch)
treeb0c5d35d7cd42c16f8364f4028a9313cc1eb6666
parent184fc8b5ee601cd83dbbdf3e6cfec5f5b8d3b41a (diff)
net: cdc_ncm: avoid changing RX/TX buffers on MTU changes
NCM buffer sizes are negotiated with the device independently of the network device MTU. The RX buffers are allocated by the usbnet framework based on the rx_urb_size value set by cdc_ncm. A single RX buffer can hold a number of MTU sized packets. The default usbnet change_mtu ndo only modifies rx_urb_size if it is equal to hard_mtu. And the cdc_ncm driver will set rx_urb_size and hard_mtu independently of each other, based on dwNtbInMaxSize and dwNtbOutMaxSize respectively. It was therefore assumed that usbnet_change_mtu() would never touch rx_urb_size. This failed to consider the case where dwNtbInMaxSize and dwNtbOutMaxSize happens to be equal. Fix by implementing an NCM specific change_mtu ndo, modifying the netdev MTU without touching the buffer size settings. Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/cdc_mbim.c2
-rw-r--r--drivers/net/usb/cdc_ncm.c31
-rw-r--r--include/linux/usb/cdc_ncm.h1
3 files changed, 33 insertions, 1 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 8973abdec9f6..bdd83d95ec0a 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -100,7 +100,7 @@ static const struct net_device_ops cdc_mbim_netdev_ops = {
100 .ndo_stop = usbnet_stop, 100 .ndo_stop = usbnet_stop,
101 .ndo_start_xmit = usbnet_start_xmit, 101 .ndo_start_xmit = usbnet_start_xmit,
102 .ndo_tx_timeout = usbnet_tx_timeout, 102 .ndo_tx_timeout = usbnet_tx_timeout,
103 .ndo_change_mtu = usbnet_change_mtu, 103 .ndo_change_mtu = cdc_ncm_change_mtu,
104 .ndo_set_mac_address = eth_mac_addr, 104 .ndo_set_mac_address = eth_mac_addr,
105 .ndo_validate_addr = eth_validate_addr, 105 .ndo_validate_addr = eth_validate_addr,
106 .ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid, 106 .ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 369405271437..e8a1144c5a8b 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -41,6 +41,7 @@
41#include <linux/module.h> 41#include <linux/module.h>
42#include <linux/netdevice.h> 42#include <linux/netdevice.h>
43#include <linux/ctype.h> 43#include <linux/ctype.h>
44#include <linux/etherdevice.h>
44#include <linux/ethtool.h> 45#include <linux/ethtool.h>
45#include <linux/workqueue.h> 46#include <linux/workqueue.h>
46#include <linux/mii.h> 47#include <linux/mii.h>
@@ -689,6 +690,33 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
689 kfree(ctx); 690 kfree(ctx);
690} 691}
691 692
693/* we need to override the usbnet change_mtu ndo for two reasons:
694 * - respect the negotiated maximum datagram size
695 * - avoid unwanted changes to rx and tx buffers
696 */
697int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
698{
699 struct usbnet *dev = netdev_priv(net);
700 struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
701 int maxmtu = ctx->max_datagram_size - cdc_ncm_eth_hlen(dev);
702
703 if (new_mtu <= 0 || new_mtu > maxmtu)
704 return -EINVAL;
705 net->mtu = new_mtu;
706 return 0;
707}
708EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
709
710static const struct net_device_ops cdc_ncm_netdev_ops = {
711 .ndo_open = usbnet_open,
712 .ndo_stop = usbnet_stop,
713 .ndo_start_xmit = usbnet_start_xmit,
714 .ndo_tx_timeout = usbnet_tx_timeout,
715 .ndo_change_mtu = cdc_ncm_change_mtu,
716 .ndo_set_mac_address = eth_mac_addr,
717 .ndo_validate_addr = eth_validate_addr,
718};
719
692int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags) 720int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
693{ 721{
694 struct cdc_ncm_ctx *ctx; 722 struct cdc_ncm_ctx *ctx;
@@ -823,6 +851,9 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
823 /* add our sysfs attrs */ 851 /* add our sysfs attrs */
824 dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group; 852 dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
825 853
854 /* must handle MTU changes */
855 dev->net->netdev_ops = &cdc_ncm_netdev_ops;
856
826 return 0; 857 return 0;
827 858
828error2: 859error2:
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 1f6526c76ee8..3a375d07d0dc 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -138,6 +138,7 @@ struct cdc_ncm_ctx {
138}; 138};
139 139
140u8 cdc_ncm_select_altsetting(struct usb_interface *intf); 140u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
141int cdc_ncm_change_mtu(struct net_device *net, int new_mtu);
141int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags); 142int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags);
142void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); 143void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
143struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); 144struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);