This is the new seris of SMSC95XX patches to deal with the issues we
found when working on this driver for a client. The new series has been
tested on an Raspberry Pi3 B.
Changes since v1:
- Change memcpy to use {get,put}_unaligned_le32() calls
- Merge tx fixups
- Added rx_turbo attribute
The manual states that the checksum cannot lie in the last DWORD of the
transmission, so add a basic check for this and fall back to software
checksumming the packet.
This only seems to trigger for ACK packets with no options or data to
return to the other end, and the use of the tx-alignment option makes
it more likely to happen.
Signed-off-by: Ben Dooks <[email protected]>
---
Fixes for v2:
- Fix spelling of check at Sergei's suggestion
- Move skb->len check into smsc95xx_can_tx_checksum()
- Change name of smsc95xx_can_checksum to explicitly say it is tx-csum
---
drivers/net/usb/smsc95xx.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 0c083d1b7f34..19e71fe15f6c 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -2000,6 +2000,23 @@ static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
return (high_16 << 16) | low_16;
}
+/* The TX CSUM won't work if the checksum lies in the last 4 bytes of the
+ * transmission. This is fairly unlikely, only seems to trigger with some
+ * short TCP ACK packets sent.
+ *
+ * Note, this calculation should probably check for the alignment of the
+ * data as well, but a straight check for csum being in the last four bytes
+ * of the packet should be ok for now.
+*/
+static bool smsc95xx_can_tx_checksum(struct sk_buff *skb)
+{
+ unsigned int len = skb->len - skb_checksum_start_offset(skb);
+
+ if (skb->len <= 45)
+ return false;
+ return skb->csum_offset < (len - (4 + 1));
+}
+
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
@@ -2024,7 +2041,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
if (csum) {
- if (skb->len <= 45) {
+ if (!smsc95xx_can_tx_checksum(skb)) {
/* workaround - hardware tx checksum does not work
* properly with extremely small packets */
long csstart = skb_checksum_start_offset(skb);
--
2.19.1
There are a number of places in the smsc95xx driver where it gets the
private-data from the usbnet passed in. It would be sensible to have
one inline function to convert it and change all points in the driver
to use that.
Signed-off-by: Ben Dooks <[email protected]>
---
drivers/net/usb/smsc95xx.c | 47 +++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 03c3c02b569c..1eb0795ec90f 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -78,6 +78,11 @@ struct smsc95xx_priv {
struct usbnet *dev;
};
+static inline struct smsc95xx_priv *usbnet_to_smsc(struct usbnet *dev)
+{
+ return (struct smsc95xx_priv *)dev->data[0];
+}
+
static bool turbo_mode = IS_ENABLED(CONFIG_USB_NET_SMSC95XX_TURBO);
module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
@@ -467,7 +472,7 @@ static unsigned int smsc95xx_hash(char addr[ETH_ALEN])
static void smsc95xx_set_multicast(struct net_device *netdev)
{
struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
unsigned long flags;
int ret;
@@ -562,7 +567,7 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
static int smsc95xx_link_reset(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
struct mii_if_info *mii = &dev->mii;
struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
unsigned long flags;
@@ -630,7 +635,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
static void set_carrier(struct usbnet *dev, bool link)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
if (pdata->link_ok == link)
return;
@@ -759,7 +764,7 @@ static void smsc95xx_ethtool_get_wol(struct net_device *net,
struct ethtool_wolinfo *wolinfo)
{
struct usbnet *dev = netdev_priv(net);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
wolinfo->supported = SUPPORTED_WAKE;
wolinfo->wolopts = pdata->wolopts;
@@ -769,7 +774,7 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net,
struct ethtool_wolinfo *wolinfo)
{
struct usbnet *dev = netdev_priv(net);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
int ret;
pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE;
@@ -805,7 +810,7 @@ static int get_mdix_status(struct net_device *net)
static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
{
struct usbnet *dev = netdev_priv(net);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
int buf;
if ((pdata->chip_id == ID_REV_CHIP_ID_9500A_) ||
@@ -854,7 +859,7 @@ static int smsc95xx_get_link_ksettings(struct net_device *net,
struct ethtool_link_ksettings *cmd)
{
struct usbnet *dev = netdev_priv(net);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
int retval;
retval = usbnet_get_link_ksettings(net, cmd);
@@ -869,7 +874,7 @@ static int smsc95xx_set_link_ksettings(struct net_device *net,
const struct ethtool_link_ksettings *cmd)
{
struct usbnet *dev = netdev_priv(net);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
int retval;
if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl)
@@ -951,7 +956,7 @@ static int smsc95xx_set_mac_address(struct usbnet *dev)
/* starts the TX path */
static int smsc95xx_start_tx_path(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
unsigned long flags;
int ret;
@@ -971,7 +976,7 @@ static int smsc95xx_start_tx_path(struct usbnet *dev)
/* Starts the Receive path */
static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
unsigned long flags;
spin_lock_irqsave(&pdata->mac_cr_lock, flags);
@@ -1028,7 +1033,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
static int smsc95xx_reset(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
u32 read_buf, write_buf, burst_cap;
int ret = 0, timeout;
@@ -1271,7 +1276,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv),
GFP_KERNEL);
- pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ pdata = usbnet_to_smsc(dev);
if (!pdata)
return -ENOMEM;
@@ -1328,7 +1333,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
if (pdata) {
cancel_delayed_work(&pdata->carrier_check);
@@ -1388,7 +1393,7 @@ static int smsc95xx_link_ok_nopm(struct usbnet *dev)
static int smsc95xx_enter_suspend0(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
u32 val;
int ret;
@@ -1427,7 +1432,7 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev)
static int smsc95xx_enter_suspend1(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
struct mii_if_info *mii = &dev->mii;
u32 val;
int ret;
@@ -1475,7 +1480,7 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev)
static int smsc95xx_enter_suspend2(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
u32 val;
int ret;
@@ -1497,7 +1502,7 @@ static int smsc95xx_enter_suspend2(struct usbnet *dev)
static int smsc95xx_enter_suspend3(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
u32 val;
int ret;
@@ -1536,7 +1541,7 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev)
static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
int ret;
if (!netif_running(dev->net)) {
@@ -1584,7 +1589,7 @@ static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
u32 val, link_up;
int ret;
@@ -1848,7 +1853,7 @@ static int smsc95xx_resume(struct usb_interface *intf)
u32 val;
BUG_ON(!dev);
- pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ pdata = usbnet_to_smsc(dev);
suspend_flags = pdata->suspend_flags;
netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
@@ -2084,7 +2089,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
static int smsc95xx_manage_power(struct usbnet *dev, int on)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
dev->intf->needs_remote_wakeup = on;
--
2.19.1
Add a configuration option for the default state of turbo mode
on the smsc95xx networking driver. Some systems it is better
to default this to off as it causes significant increases in
soft-irq load.
Signed-off-by: Ben Dooks <[email protected]>
---
drivers/net/usb/Kconfig | 13 +++++++++++++
drivers/net/usb/smsc95xx.c | 2 +-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 418b0904cecb..c3ebc43a6582 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -351,6 +351,19 @@ config USB_NET_SMSC95XX
This option adds support for SMSC LAN95XX based USB 2.0
10/100 Ethernet adapters.
+config USB_NET_SMSC95XX_TURBO
+ bool "Use turbo receive mode by default"
+ depends on USB_NET_SMSC95XX
+ default y
+ help
+ This options sets the default turbo mode settings for the
+ driver's receive path. These can also be altered by the
+ turbo_mode module parameter.
+
+ There are some systems where the turbo mode causes higher
+ soft-irq load, thus making it useful to default the option
+ off for these.
+
config USB_NET_GL620A
tristate "GeneSys GL620USB-A based cables"
depends on USB_USBNET
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 401ec9feb495..cb19aea139d3 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -78,7 +78,7 @@ struct smsc95xx_priv {
struct usbnet *dev;
};
-static bool turbo_mode = true;
+static bool turbo_mode = IS_ENABLED(CONFIG_USB_NET_SMSC95XX_TURBO);
module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
--
2.19.1
Add attribute for the rx_turbo mode to allow run-time configuration of
this feature.
Note, this requires a restart of the device to take effect.
Signed-off-by: Ben Dooks <[email protected]>
---
drivers/net/usb/smsc95xx.c | 41 +++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 1eb0795ec90f..330c3cf5d6f6 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -73,6 +73,7 @@ struct smsc95xx_priv {
u8 features;
u8 suspend_flags;
u8 mdix_ctrl;
+ bool rx_turbo;
bool link_ok;
struct delayed_work carrier_check;
struct usbnet *dev;
@@ -1103,7 +1104,7 @@ static int smsc95xx_reset(struct usbnet *dev)
"Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n",
read_buf);
- if (!turbo_mode) {
+ if (!pdata->rx_turbo) {
burst_cap = 0;
dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
} else if (dev->udev->speed == USB_SPEED_HIGH) {
@@ -1259,6 +1260,37 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
.ndo_set_features = smsc95xx_set_features,
};
+static inline struct smsc95xx_priv *dev_to_priv(struct device *dev)
+{
+ struct usb_interface *ui = container_of(dev, struct usb_interface, dev);
+ return usbnet_to_smsc(usb_get_intfdata(ui));
+}
+
+/* Currently rx_turbo will not take effect until next close/open */
+static ssize_t rx_turbo_show(struct device *adev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smsc95xx_priv *priv = dev_to_priv(adev);
+ return snprintf(buf, PAGE_SIZE, "%d", priv->rx_turbo);
+}
+
+static ssize_t rx_turbo_store(struct device *adev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct smsc95xx_priv *priv = dev_to_priv(adev);
+ bool res;
+
+ if (kstrtobool(buf, &res))
+ return -EINVAL;
+
+ priv->rx_turbo = res;
+ return count;
+}
+
+static DEVICE_ATTR_RW(rx_turbo);
+
static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
{
struct smsc95xx_priv *pdata = NULL;
@@ -1280,6 +1312,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
if (!pdata)
return -ENOMEM;
+ pdata->rx_turbo = turbo_mode;
spin_lock_init(&pdata->mac_cr_lock);
/* LAN95xx devices do not alter the computed checksum of 0 to 0xffff.
@@ -1328,6 +1361,11 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier);
schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
+ ret = device_create_file(&dev->udev->dev, &dev_attr_rx_turbo);
+ if (ret)
+ netdev_warn(dev->net,
+ "failed to create rx_turbo attribute: %d\n", ret);
+
return 0;
}
@@ -1336,6 +1374,7 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
struct smsc95xx_priv *pdata = usbnet_to_smsc(dev);
if (pdata) {
+ device_remove_file(&dev->udev->dev, &dev_attr_rx_turbo);
cancel_delayed_work(&pdata->carrier_check);
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
kfree(pdata);
--
2.19.1
The tegra EHCI driver requires alignment of the buffer, so try and
make this better by pushing the buffer start back to an word
aligned address. At the worst this makes memcpy() easier as
it is word aligned, at best it makes sure the usb can directly
map the buffer.
Signed-off-by: Ben Dooks <[email protected]>
---
Changes since v1:
- Removed the module parameter
- Reworked the tx code to ensure retry if alignment changed
- Explicitly mention the EHCI in the tegra
- Deal with new simpler tx code
---
drivers/net/usb/Kconfig | 12 ++++++++++++
drivers/net/usb/smsc95xx.c | 22 +++++++++++++++++++---
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index c3ebc43a6582..1af6fb0cadb1 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -364,6 +364,18 @@ config USB_NET_SMSC95XX_TURBO
soft-irq load, thus making it useful to default the option
off for these.
+config USB_NET_SMSC95XX_TXALIGN
+ bool "Add bytes to align transmit buffers"
+ depends on USB_NET_SMSC95XX
+ default n
+ help
+ This option makes the tx buffers 32 bit aligned which might
+ help with systems that want tx data aligned to a 32 bit
+ boundary.
+
+ Using this option will mean there may be up to 3 bytes of
+ data per packet sent.
+
config USB_NET_GL620A
tristate "GeneSys GL620USB-A based cables"
depends on USB_USBNET
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 19e71fe15f6c..8ce190da8be0 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -2017,28 +2017,43 @@ static bool smsc95xx_can_tx_checksum(struct sk_buff *skb)
return skb->csum_offset < (len - (4 + 1));
}
+static inline u32 tx_align(struct sk_buff *skb)
+{
+#ifdef CONFIG_USB_NET_SMSC95XX_TXALIGN
+ return ((u32)skb->data) & 3;
+#else
+ return 0;
+#endif
+}
+
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
u32 tx_cmd_a, tx_cmd_b;
+ u32 align;
void *ptr;
/* We do not advertise SG, so skbs should be already linearized */
BUG_ON(skb_shinfo(skb)->nr_frags);
+retry_align:
+ align = tx_align(skb);
+
/* Make writable and expand header space by overhead if required */
- if (skb_cow_head(skb, overhead)) {
+ if (skb_cow_head(skb, overhead + align)) {
/* Must deallocate here as returning NULL to indicate error
* means the skb won't be deallocated in the caller.
*/
dev_kfree_skb_any(skb);
return NULL;
- }
+ } else if (tx_align(skb) != align)
+ goto retry_align;
tx_cmd_b = (u32)skb->len;
tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= align << 16;
if (csum) {
if (!smsc95xx_can_tx_checksum(skb)) {
@@ -2062,7 +2077,8 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
}
}
- ptr = skb_push(skb, 8);
+ /* TX command format is in section 5.4 of SMSC95XX datasbook */
+ ptr = skb_push(skb, 8 + align);
put_unaligned_le32(tx_cmd_a, ptr);
put_unaligned_le32(tx_cmd_b, ptr+4);
--
2.19.1
The smsc95xx_tx_fixup is doing multiple calls to skb_push() to
put an 8-byte command header onto the packet. It would be easier
to do one skb_push() and then copy the data in once the push is
done.
We also make the code smaller by using proper unaligned puts for
the header. This merges in the CPU to LE32 conversion as well and
makes the whole sequence easier to understand hopefully.
Signed-off-by: Ben Dooks <[email protected]>
---
Since v1:
- Add alignment changes using put_unaligned_le32()
---
drivers/net/usb/smsc95xx.c | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index cb19aea139d3..0c083d1b7f34 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -2006,6 +2006,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
u32 tx_cmd_a, tx_cmd_b;
+ void *ptr;
/* We do not advertise SG, so skbs should be already linearized */
BUG_ON(skb_shinfo(skb)->nr_frags);
@@ -2019,6 +2020,9 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
return NULL;
}
+ tx_cmd_b = (u32)skb->len;
+ tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+
if (csum) {
if (skb->len <= 45) {
/* workaround - hardware tx checksum does not work
@@ -2032,24 +2036,18 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
csum = false;
} else {
u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
- skb_push(skb, 4);
- cpu_to_le32s(&csum_preamble);
- memcpy(skb->data, &csum_preamble, 4);
+ ptr = skb_push(skb, 4);
+ put_unaligned_le32(csum_preamble, ptr);
+
+ tx_cmd_a += 4;
+ tx_cmd_b += 4;
+ tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
}
}
- skb_push(skb, 4);
- tx_cmd_b = (u32)(skb->len - 4);
- if (csum)
- tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
- cpu_to_le32s(&tx_cmd_b);
- memcpy(skb->data, &tx_cmd_b, 4);
-
- skb_push(skb, 4);
- tx_cmd_a = (u32)(skb->len - 8) | TX_CMD_A_FIRST_SEG_ |
- TX_CMD_A_LAST_SEG_;
- cpu_to_le32s(&tx_cmd_a);
- memcpy(skb->data, &tx_cmd_a, 4);
+ ptr = skb_push(skb, 8);
+ put_unaligned_le32(tx_cmd_a, ptr);
+ put_unaligned_le32(tx_cmd_b, ptr+4);
return skb;
}
--
2.19.1
Change the RX code to use get_unaligned_le32() instead of the combo
of memcpy and cpu_to_le32s(&var).
Signed-off-by: Ben Dooks <[email protected]>
---
drivers/net/usb/smsc95xx.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 8ce190da8be0..03c3c02b569c 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -618,9 +618,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
return;
}
- memcpy(&intdata, urb->transfer_buffer, 4);
- le32_to_cpus(&intdata);
-
+ intdata = get_unaligned_le32(urb->transfer_buffer);
netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
if (intdata & INT_ENP_PHY_INT_)
@@ -1922,8 +1920,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
unsigned char *packet;
u16 size;
- memcpy(&header, skb->data, sizeof(header));
- le32_to_cpus(&header);
+ header = get_unaligned_le32(skb->data);
skb_pull(skb, 4 + NET_IP_ALIGN);
packet = skb->data;
--
2.19.1
The smsc95xx driver already takes into account the NET_IP_ALIGN
parameter when setting up the receive packet data, which means
we do not need to worry about aligning the packets in the usbnet
driver.
Adding the EVENT_NO_IP_ALIGN means that the IPv4 header is now
passed to the ip_rcv() routine with the start on an aligned address.
Tested on Raspberry Pi B3.
Signed-off-by: Ben Dooks <[email protected]>
---
drivers/net/usb/smsc95xx.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 06b4d290784d..401ec9feb495 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -1292,6 +1292,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->features |= NETIF_F_RXCSUM;
dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+ set_bit(EVENT_NO_IP_ALIGN, &dev->flags);
smsc95xx_init_mac_address(dev);
--
2.19.1
Hello!
On 12.10.2018 11:34, Ben Dooks wrote:
> Change the RX code to use get_unaligned_le32() instead of the combo
> of memcpy and cpu_to_le32s(&var).
le32_to_cpus(), actually.
> Signed-off-by: Ben Dooks <[email protected]>
> ---
> drivers/net/usb/smsc95xx.c | 7 ++-----
> 1 file changed, 2 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
> index 8ce190da8be0..03c3c02b569c 100644
> --- a/drivers/net/usb/smsc95xx.c
> +++ b/drivers/net/usb/smsc95xx.c
> @@ -618,9 +618,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
> return;
> }
>
> - memcpy(&intdata, urb->transfer_buffer, 4);
> - le32_to_cpus(&intdata);
> -
> + intdata = get_unaligned_le32(urb->transfer_buffer);
> netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
>
> if (intdata & INT_ENP_PHY_INT_)
> @@ -1922,8 +1920,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
> unsigned char *packet;
> u16 size;
>
> - memcpy(&header, skb->data, sizeof(header));
> - le32_to_cpus(&header);
> + header = get_unaligned_le32(skb->data);
> skb_pull(skb, 4 + NET_IP_ALIGN);
> packet = skb->data;
>
MBR, Sergei
Ben Dooks <[email protected]> writes:
> Add a configuration option for the default state of turbo mode
> on the smsc95xx networking driver. Some systems it is better
> to default this to off as it causes significant increases in
> soft-irq load.
So there is already a module option allowing you to change this, using
e.g kernel command line or kmod config files. It's even writable,
taking effect on the next netdev open, so you can change it at runtime
without reloading the module.
What good does this new build-time setting do, except causing confusion
wrt driver defaults?
Note also that the smsc95xx and smsc75xx drivers are pretty similar.
Both have the same turbo_mode setting. If you change the defaults, then
they should at least be kept in sync to cause as little confusion as
possible..
Bjørn