* Re: [PATCH -mm] ssb: open-code dma_alloc_coherent
From: FUJITA Tomonori @ 2010-02-16 2:05 UTC (permalink / raw)
To: Larry.Finger
Cc: fujita.tomonori, linux-kernel, netdev, mb, davem, stefano.brivio,
akpm
In-Reply-To: <4B79E251.3020005@lwfinger.net>
On Mon, 15 Feb 2010 18:09:53 -0600
Larry Finger <Larry.Finger@lwfinger.net> wrote:
> On 02/15/2010 01:25 AM, FUJITA Tomonori wrote:
> > 2.6.33-rc8:
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/tomo/linux-2.6-misc.git pending
> >
> > The ssb patch depends on the latest eight patches in the tree:
> >
> > http://marc.info/?l=linux-kernel&m=126596737604808&w=2
>
> The DMA changes work on i386 architecture with wireless devices in Cardbus (PCI)
> format. Both b43legacy and b43 were tested.
Great, thanks a lot!
I've posted the second version:
http://marc.info/?l=linux-kernel&m=126624677226865&w=2
It's also available via the git tree:
git://git.kernel.org/pub/scm/linux/kernel/git/tomo/linux-2.6-misc.git pending
Can I get your ack on b43 stuff?
Thanks,
^ permalink raw reply
* linux-next: manual merge of the wireless tree with the net tree
From: Stephen Rothwell @ 2010-02-16 1:01 UTC (permalink / raw)
To: John W. Linville
Cc: linux-next, linux-kernel, Alexey Dobriyan, David Miller, netdev,
Gertjan van Wingerde
Hi John,
Today's linux-next merge of the wireless tree got a conflict in
drivers/net/wireless/rt2x00/rt2800pci.c between commit
a3aa18842a5303fc28fcc4d57dbd16618bd830a0 ("drivers/net/: use
DEFINE_PCI_DEVICE_TABLE()") from the net tree and commit
de1ebdceb6a4fe1b7073b81d273285b7c8bed312 ("rt2x00: rt2800 - Make rt30xx
and rt35xx chipsets configurable") from the wireless tree.
Just context changes. I fixed it up (see below) and can carry the fix as
necessary.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
diff --cc drivers/net/wireless/rt2x00/rt2800pci.c
index d64181c,0e4c417..0000000
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@@ -1190,8 -1184,11 +1184,11 @@@ static const struct rt2x00_ops rt2800pc
/*
* RT2800pci module information.
*/
-static struct pci_device_id rt2800pci_device_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
- { PCI_DEVICE(0x1462, 0x891a), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0601), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0681), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0701), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0781), PCI_DEVICE_DATA(&rt2800pci_ops) },
{ PCI_DEVICE(0x1432, 0x7708), PCI_DEVICE_DATA(&rt2800pci_ops) },
{ PCI_DEVICE(0x1432, 0x7727), PCI_DEVICE_DATA(&rt2800pci_ops) },
{ PCI_DEVICE(0x1432, 0x7728), PCI_DEVICE_DATA(&rt2800pci_ops) },
^ permalink raw reply
* [PATCH] netdev: bfin_mac: drop experimental markings on RMII support
From: Mike Frysinger @ 2010-02-16 0:32 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel
The code has been around for a long time now and is known to work on a
bunch of different parts/boards.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/Kconfig | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 746cd3a..03fec5e 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -868,8 +868,8 @@ config BFIN_RX_DESC_NUM
Set the number of buffer packets used in driver.
config BFIN_MAC_RMII
- bool "RMII PHY Interface (EXPERIMENTAL)"
- depends on BFIN_MAC && EXPERIMENTAL
+ bool "RMII PHY Interface"
+ depends on BFIN_MAC
default y if BFIN527_EZKIT
default n if BFIN537_STAMP
help
--
1.7.0
^ permalink raw reply related
* Re: [PATCH -mm] ssb: open-code dma_alloc_coherent
From: Larry Finger @ 2010-02-16 0:09 UTC (permalink / raw)
To: FUJITA Tomonori; +Cc: linux-kernel, netdev, mb, davem, stefano.brivio, akpm
In-Reply-To: <20100215162426V.fujita.tomonori@lab.ntt.co.jp>
On 02/15/2010 01:25 AM, FUJITA Tomonori wrote:
> 2.6.33-rc8:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/tomo/linux-2.6-misc.git pending
>
> The ssb patch depends on the latest eight patches in the tree:
>
> http://marc.info/?l=linux-kernel&m=126596737604808&w=2
The DMA changes work on i386 architecture with wireless devices in Cardbus (PCI)
format. Both b43legacy and b43 were tested.
Larry
^ permalink raw reply
* Re: [PATCH 1/2] macvtap: rework object lifetime rules
From: Ed Swierk @ 2010-02-15 23:48 UTC (permalink / raw)
To: Arnd Bergmann
Cc: netdev, linux-kernel, Patrick McHardy, Sridhar Samudrala,
qemu-devel, Michael S. Tsirkin
In-Reply-To: <201002131133.43028.arnd@arndb.de>
On Sat, Feb 13, 2010 at 2:33 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> The original macvtap code has a number of problems
> resulting from the use of RCU for protecting the
> access to struct macvtap_queue from open files.
>
> This includes
> - need for GFP_ATOMIC allocations for skbs
> - potential deadlocks when copy_*_user sleeps
> - inability to work with vhost-net
>
> Changing the lifetime of macvtap_queue to always
> depend on the open file solves all these. The
> RCU reference simply moves one step down to
> the reference on the macvlan_dev, which we
> only need for nonblocking operations.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This works for me.
Acked-by: Ed Swierk <eswierk@aristanetworks.com>
^ permalink raw reply
* [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner (v2).
From: David Daney @ 2010-02-15 23:06 UTC (permalink / raw)
To: ralf, linux-mips, netdev, gregkh; +Cc: David Daney, Eric Dumazet
In-Reply-To: <1266264799-3510-4-git-send-email-ddaney@caviumnetworks.com>
If we wait for the once-per-second cleanup to free transmit SKBs,
sockets with small transmit buffer sizes might spend most of their
time blocked waiting for the cleanup.
Normally we do a cleanup for each transmitted packet. We add a
watchdog type timer so that we also schedule a timeout for 150uS after
a packet is transmitted. The watchdog is reset for each transmitted
packet, so for high packet rates, it never expires. At these high
rates, the cleanups are done for each packet so the extra watchdog
initiated cleanups are neither needed nor triggered.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
CC: Eric Dumazet <eric.dumazet@gmail.com>
---
This version has spelling and comment changes based on feedback from
Eric Dumazet.
drivers/staging/octeon/Kconfig | 1 -
drivers/staging/octeon/ethernet-defines.h | 5 +-
drivers/staging/octeon/ethernet-tx.c | 137 +++++++++++++++++++++++------
drivers/staging/octeon/ethernet-tx.h | 6 +-
drivers/staging/octeon/ethernet.c | 47 +++++-----
drivers/staging/octeon/octeon-ethernet.h | 9 +--
6 files changed, 142 insertions(+), 63 deletions(-)
diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig
index 579b8f1..638ad6b 100644
--- a/drivers/staging/octeon/Kconfig
+++ b/drivers/staging/octeon/Kconfig
@@ -3,7 +3,6 @@ config OCTEON_ETHERNET
depends on CPU_CAVIUM_OCTEON
select PHYLIB
select MDIO_OCTEON
- select HIGH_RES_TIMERS
help
This driver supports the builtin ethernet ports on Cavium
Networks' products in the Octeon family. This driver supports the
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index 00a8561..6a2cd50 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -95,10 +95,11 @@
/*#define DONT_WRITEBACK(x) 0 */
/* Maximum number of SKBs to try to free per xmit packet. */
-#define MAX_SKB_TO_FREE 10
#define MAX_OUT_QUEUE_DEPTH 1000
-#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(uint32_t))
+
#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 62258bd..5175247 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -48,6 +48,7 @@
#include "cvmx-wqe.h"
#include "cvmx-fau.h"
+#include "cvmx-pip.h"
#include "cvmx-pko.h"
#include "cvmx-helper.h"
@@ -66,6 +67,11 @@
#define GET_SKBUFF_QOS(skb) 0
#endif
+static void cvm_oct_tx_do_cleanup(unsigned long arg);
+static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0);
+
+/* Maximum number of SKBs to try to free per xmit packet. */
+#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2)
static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
{
@@ -77,10 +83,24 @@ static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
return skb_to_free;
}
-void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv)
+static void cvm_oct_kick_tx_poll_watchdog(void)
+{
+ union cvmx_ciu_timx ciu_timx;
+ ciu_timx.u64 = 0;
+ ciu_timx.s.one_shot = 1;
+ ciu_timx.s.len = cvm_oct_tx_poll_interval;
+ cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64);
+}
+
+void cvm_oct_free_tx_skbs(struct net_device *dev)
{
int32_t skb_to_free;
int qos, queues_per_port;
+ int total_freed = 0;
+ int total_remaining = 0;
+ unsigned long flags;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
queues_per_port = cvmx_pko_get_num_queues(priv->port);
/* Drain any pending packets in the free list */
for (qos = 0; qos < queues_per_port; qos++) {
@@ -89,24 +109,31 @@ void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv)
skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE);
skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
- while (skb_to_free > 0) {
- dev_kfree_skb_any(skb_dequeue(&priv->tx_free_list[qos]));
- skb_to_free--;
+
+ total_freed += skb_to_free;
+ if (skb_to_free > 0) {
+ struct sk_buff *to_free_list = NULL;
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_to_free > 0) {
+ struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
+ }
}
+ total_remaining += skb_queue_len(&priv->tx_free_list[qos]);
}
-}
-
-enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer)
-{
- struct octeon_ethernet *priv = container_of(timer, struct octeon_ethernet, tx_restart_timer);
- struct net_device *dev = cvm_oct_device[priv->port];
-
- cvm_oct_free_tx_skbs(priv);
-
- if (netif_queue_stopped(dev))
+ if (total_freed >= 0 && netif_queue_stopped(dev))
netif_wake_queue(dev);
-
- return HRTIMER_NORESTART;
+ if (total_remaining)
+ cvm_oct_kick_tx_poll_watchdog();
}
/**
@@ -129,6 +156,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
struct sk_buff *to_free_list;
int32_t skb_to_free;
int32_t buffers_to_free;
+ u32 total_to_clean;
unsigned long flags;
#if REUSE_SKBUFFS_WITHOUT_FREE
unsigned char *fpa_head;
@@ -232,7 +260,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
pko_command.s.subone0 = 1;
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
/* Build the PKO buffer pointer */
hw_buffer.u64 = 0;
@@ -327,7 +354,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
* We can use this buffer in the FPA. We don't need the FAU
* update anymore
*/
- pko_command.s.reg0 = 0;
pko_command.s.dontfree = 0;
hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7);
@@ -384,15 +410,17 @@ dont_put_skbuff_in_hw:
* If we're sending faster than the receive can free them then
* don't do the HW free.
*/
- if ((buffers_to_free < -100) && !pko_command.s.dontfree) {
+ if ((buffers_to_free < -100) && !pko_command.s.dontfree)
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
- }
- if (pko_command.s.dontfree)
+ if (pko_command.s.dontfree) {
queue_type = QUEUE_CORE;
- else
+ pko_command.s.reg0 = priv->fau+qos*4;
+ } else {
queue_type = QUEUE_HW;
+ }
+ if (USE_ASYNC_IOBDMA)
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
@@ -402,10 +430,7 @@ dont_put_skbuff_in_hw:
/* Drop the lock when notifying the core. */
spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
netif_stop_queue(dev);
- hrtimer_start(&priv->tx_restart_timer,
- priv->tx_restart_interval, HRTIMER_MODE_REL);
spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
-
} else {
/* If not using normal queueing. */
queue_type = QUEUE_DROP;
@@ -460,11 +485,27 @@ skip_xmit:
}
if (USE_ASYNC_IOBDMA) {
+ CVMX_SYNCIOBDMA;
+ total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
/* Restore the scratch area */
cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
+ } else {
+ total_to_clean = cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1);
}
+ if (total_to_clean & 0x3ff) {
+ /*
+ * Schedule the cleanup tasklet every 1024 packets for
+ * the pathological case of high traffic on one port
+ * delaying clean up of packets on a different port
+ * that is blocked waiting for the cleanup.
+ */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ }
+
+ cvm_oct_kick_tx_poll_watchdog();
+
return NETDEV_TX_OK;
}
@@ -624,7 +665,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
*
* @dev: Device being shutdown
*/
-void cvm_oct_tx_shutdown(struct net_device *dev)
+void cvm_oct_tx_shutdown_dev(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
unsigned long flags;
@@ -638,3 +679,45 @@ void cvm_oct_tx_shutdown(struct net_device *dev)
spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
}
}
+
+static void cvm_oct_tx_do_cleanup(unsigned long arg)
+{
+ int port;
+
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ struct net_device *dev = cvm_oct_device[port];
+ cvm_oct_free_tx_skbs(dev);
+ }
+ }
+}
+
+static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id)
+{
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Do the work in the tasklet. */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ return IRQ_HANDLED;
+}
+
+void cvm_oct_tx_initialize(void)
+{
+ int i;
+
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Register an IRQ hander for to receive CIU_TIMX(1) interrupts */
+ i = request_irq(OCTEON_IRQ_TIMER1,
+ cvm_oct_tx_cleanup_watchdog, 0,
+ "Ethernet", cvm_oct_device);
+
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1);
+}
+
+void cvm_oct_tx_shutdown(void)
+{
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
+}
diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h
index b628d8c..547680c 100644
--- a/drivers/staging/octeon/ethernet-tx.h
+++ b/drivers/staging/octeon/ethernet-tx.h
@@ -29,6 +29,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
int do_free, int qos);
-void cvm_oct_tx_shutdown(struct net_device *dev);
-void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv);
-enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer);
+void cvm_oct_tx_initialize(void);
+void cvm_oct_tx_shutdown(void);
+void cvm_oct_tx_shutdown_dev(struct net_device *dev);
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 1771c10..5ee60ab 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -140,6 +140,8 @@ atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
+u64 cvm_oct_tx_poll_interval;
+
static void cvm_oct_rx_refill_worker(struct work_struct *work);
static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
@@ -159,18 +161,19 @@ static void cvm_oct_rx_refill_worker(struct work_struct *work)
&cvm_oct_rx_refill_work, HZ);
}
-static void cvm_oct_tx_clean_worker(struct work_struct *work)
+static void cvm_oct_periodic_worker(struct work_struct *work)
{
struct octeon_ethernet *priv = container_of(work,
struct octeon_ethernet,
- tx_clean_work.work);
+ port_periodic_work.work);
if (priv->poll)
priv->poll(cvm_oct_device[priv->port]);
- cvm_oct_free_tx_skbs(priv);
+
cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]);
+
if (!atomic_read(&cvm_oct_poll_queue_stopping))
- queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ);
+ queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
}
/**
@@ -662,6 +665,9 @@ static int __init cvm_oct_init_module(void)
*/
cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ /* Initialize the FAU used for counting tx SKBs that need to be freed */
+ cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0);
+
if ((pow_send_group != -1)) {
struct net_device *dev;
pr_info("\tConfiguring device for POW only access\n");
@@ -670,18 +676,6 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
struct octeon_ethernet *priv = netdev_priv(dev);
- hrtimer_init(&priv->tx_restart_timer,
- CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- priv->tx_restart_timer.function = cvm_oct_restart_tx;
-
- /*
- * Default for 10GE 5000nS enough time to
- * transmit about 100 64byte packtes. 1GE
- * interfaces will get 50000nS below.
- */
- priv->tx_restart_interval = ktime_set(0, 5000);
-
dev->netdev_ops = &cvm_oct_pow_netdev_ops;
priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
priv->port = CVMX_PIP_NUM_INPUT_PORTS;
@@ -725,9 +719,8 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
priv = netdev_priv(dev);
- INIT_DELAYED_WORK(&priv->tx_clean_work,
- cvm_oct_tx_clean_worker);
-
+ INIT_DELAYED_WORK(&priv->port_periodic_work,
+ cvm_oct_periodic_worker);
priv->imode = imode;
priv->port = port;
priv->queue = cvmx_pko_get_base_queue(priv->port);
@@ -763,7 +756,6 @@ static int __init cvm_oct_init_module(void)
case CVMX_HELPER_INTERFACE_MODE_SGMII:
dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
- priv->tx_restart_interval = ktime_set(0, 50000);
strcpy(dev->name, "eth%d");
break;
@@ -775,7 +767,6 @@ static int __init cvm_oct_init_module(void)
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
- priv->tx_restart_interval = ktime_set(0, 50000);
strcpy(dev->name, "eth%d");
break;
}
@@ -793,13 +784,19 @@ static int __init cvm_oct_init_module(void)
cvmx_pko_get_num_queues(priv->port) *
sizeof(uint32_t);
queue_delayed_work(cvm_oct_poll_queue,
- &priv->tx_clean_work, HZ);
+ &priv->port_periodic_work, HZ);
}
}
}
+ cvm_oct_tx_initialize();
cvm_oct_rx_initialize();
+ /*
+ * 150 uS: about 10 1500-byte packtes at 1GE.
+ */
+ cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
+
queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
return 0;
@@ -826,6 +823,8 @@ static void __exit cvm_oct_cleanup_module(void)
cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
cvm_oct_rx_shutdown();
+ cvm_oct_tx_shutdown();
+
cvmx_pko_disable();
/* Free the ethernet devices */
@@ -833,9 +832,9 @@ static void __exit cvm_oct_cleanup_module(void)
if (cvm_oct_device[port]) {
struct net_device *dev = cvm_oct_device[port];
struct octeon_ethernet *priv = netdev_priv(dev);
- cancel_delayed_work_sync(&priv->tx_clean_work);
+ cancel_delayed_work_sync(&priv->port_periodic_work);
- cvm_oct_tx_shutdown(dev);
+ cvm_oct_tx_shutdown_dev(dev);
unregister_netdev(dev);
kfree(dev);
cvm_oct_device[port] = NULL;
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index 8d09210..db2a3cc 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -31,8 +31,6 @@
#ifndef OCTEON_ETHERNET_H
#define OCTEON_ETHERNET_H
-#include <linux/hrtimer.h>
-
/**
* This is the definition of the Ethernet driver's private
* driver state stored in netdev_priv(dev).
@@ -59,9 +57,7 @@ struct octeon_ethernet {
uint64_t link_info;
/* Called periodically to check link status */
void (*poll) (struct net_device *dev);
- struct hrtimer tx_restart_timer;
- ktime_t tx_restart_interval;
- struct delayed_work tx_clean_work;
+ struct delayed_work port_periodic_work;
struct work_struct port_work; /* may be unused. */
};
@@ -101,6 +97,7 @@ extern char pow_send_list[];
extern struct net_device *cvm_oct_device[];
extern struct workqueue_struct *cvm_oct_poll_queue;
extern atomic_t cvm_oct_poll_queue_stopping;
+extern u64 cvm_oct_tx_poll_interval;
extern int max_rx_cpus;
extern int rx_napi_weight;
--
1.6.6
^ permalink raw reply related
* Re: [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner.
From: David Daney @ 2010-02-15 22:05 UTC (permalink / raw)
To: Eric Dumazet; +Cc: ralf, linux-mips, netdev, gregkh
In-Reply-To: <1266268271.2859.22.camel@edumazet-laptop>
On 02/15/2010 01:11 PM, Eric Dumazet wrote:
> Le lundi 15 février 2010 à 12:41 -0800, David Daney a écrit :
>> On 02/15/2010 12:27 PM, Eric Dumazet wrote:
>>> Le lundi 15 février 2010 à 12:13 -0800, David Daney a écrit :
>>>> If we wait for the once-per-second cleanup to free transmit SKBs,
>>>> sockets with small transmit buffer sizes might spend most of their
>>>> time blocked waiting for the cleanup.
>>>>
>>>> Normally we do a cleanup for each transmitted packet. We add a
>>>> watchdog type timer so that we also schedule a timeout for 150uS after
>>>> a packet is transmitted. The watchdog is reset for each transmitted
>>>> packet, so for high packet rates, it never expires. At these high
>>>> rates, the cleanups are done for each packet so the extra watchdog
>>>> initiated cleanups are not needed.
>>>
>>> s/needed/fired/
>>>
>>
>> or perhaps s/are not needed/are neither needed nor fired/
>>
>>> Hmm, but re-arming a timer for each transmited packet must have a cost ?
>>>
>>
>> The cost is fairly low (less than 10 processor clock cycles). We didn't
>> add this for amusement, people actually do things like only send UDP
>> packets from userspace. Since we can fill the transmit queue faster
>> than it is emptied, the socket transmit buffer is quickly consumed. If
>> we don't free the SKBs in short order, the transmitting process get to
>> take a long sleep (until our previous once per second clean up task was
>> run).
>
> I understand this, but traditionaly, NIC drivers dont use a timer, but a
> 'TX complete' interrupt, that usually fires a few us after packet
> submission on Gigabit speed.
>
Indeed. Lacking this type of interrupt, the watchdog seemed the best
short term solution.
I am investigating the possibility of feeding TX complete notifications
back through the RX path where it is possible to generate interrupts.
The drawback to this is that it takes a lot more CPU cycles as well as
added cache pressure.
> A fast program could try to send X small udp packets in less than 150
> us, X being greater than the size of your TX ring.
My TX queue (it is not a ring) size can be made arbitrarily large
(currently 1000). 64bytes * 1000 packets * 10 bits/packet / 10e9
bits/sec == 640uS. My watchdog will fire after less than 1/4 of the
ring capacity is freed.
>
> So your patch makes the window smaller, but it still is there (at
> physical layer, we'll see a burst of packets, a ~100us delay, then a
> second burst)
>
With this patch, there will be no burstiness using default socket buffer
sizes and packets of arbitrary size on a standard 1gig port.
On the 10gig ports there is the possibility for burstiness as you aptly
explain. However, in practice it would be difficult to arrange things
to achieve sufficiently high packet rates, so we can live with it like this.
David Daney
^ permalink raw reply
* [net-next PATCH v4 3/3] net: reserve ports for applications using fixed port numbers
From: Octavian Purdila @ 2010-02-15 22:00 UTC (permalink / raw)
To: David Miller
Cc: Octavian Purdila, Linux Kernel Network Developers,
Linux Kernel Developers, WANG Cong, Neil Horman, Eric Dumazet
In-Reply-To: <1266271241-6293-1-git-send-email-opurdila@ixiacom.com>
This patch introduces /proc/sys/net/ipv4/ip_local_reserved_ports
(bitmap type) which allows users to reserve ports for third-party
applications.
The reserved ports will not be used by automatic port assignments
(e.g. when calling connect() or bind() with port number 0). Explicit
port allocation behavior is unchanged.
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Signed-off-by: WANG Cong <amwang@redhat.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
---
Documentation/networking/ip-sysctl.txt | 12 ++++++++++++
drivers/infiniband/core/cma.c | 7 ++++++-
include/net/ip.h | 6 ++++++
net/ipv4/af_inet.c | 8 +++++++-
net/ipv4/inet_connection_sock.c | 6 ++++++
net/ipv4/inet_hashtables.c | 2 ++
net/ipv4/sysctl_net_ipv4.c | 17 +++++++++++++++++
net/ipv4/udp.c | 3 ++-
net/sctp/socket.c | 2 ++
9 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 2dc7a1d..23be7a4 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -564,6 +564,18 @@ ip_local_port_range - 2 INTEGERS
(i.e. by default) range 1024-4999 is enough to issue up to
2000 connections per second to systems supporting timestamps.
+ip_local_reserved_ports - BITMAP of 65536 ports
+ Specify the ports which are reserved for known third-party
+ applications. These ports will not be used by automatic port assignments
+ (e.g. when calling connect() or bind() with port number 0). Explicit
+ port allocation behavior is unchanged.
+
+ Reserving ports is done by writing positive numbers in this proc entry,
+ clearing them is done by writing negative numbers (e.g. 8080 reserves
+ port number, -8080 makes it available for automatic assignment again).
+
+ Default: Empty
+
ip_nonlocal_bind - BOOLEAN
If set, allows processes to bind() to non-local IP addresses,
which can be quite useful - but may break some applications.
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index cc9b594..8248fc6 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -1979,6 +1979,8 @@ retry:
/* FIXME: add proper port randomization per like inet_csk_get_port */
do {
ret = idr_get_new_above(ps, bind_list, next_port, &port);
+ if (inet_is_reserved_local_port(port))
+ ret = -EAGAIN;
} while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL));
if (ret)
@@ -2997,10 +2999,13 @@ static int __init cma_init(void)
{
int ret, low, high, remaining;
- get_random_bytes(&next_port, sizeof next_port);
inet_get_local_port_range(&low, &high);
+again:
+ get_random_bytes(&next_port, sizeof next_port);
remaining = (high - low) + 1;
next_port = ((unsigned int) next_port % remaining) + low;
+ if (inet_is_reserved_local_port(next_port))
+ goto again;
cma_wq = create_singlethread_workqueue("rdma_cm");
if (!cma_wq)
diff --git a/include/net/ip.h b/include/net/ip.h
index fb63371..2e24256 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -184,6 +184,12 @@ extern struct local_ports {
} sysctl_local_ports;
extern void inet_get_local_port_range(int *low, int *high);
+extern unsigned long *sysctl_local_reserved_ports;
+static inline int inet_is_reserved_local_port(int port)
+{
+ return test_bit(port, sysctl_local_reserved_ports);
+}
+
extern int sysctl_ip_default_ttl;
extern int sysctl_ip_nonlocal_bind;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 7d12c6a..06810b0 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1546,9 +1546,13 @@ static int __init inet_init(void)
BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb));
+ sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
+ if (!sysctl_local_reserved_ports)
+ goto out;
+
rc = proto_register(&tcp_prot, 1);
if (rc)
- goto out;
+ goto out_free_reserved_ports;
rc = proto_register(&udp_prot, 1);
if (rc)
@@ -1647,6 +1651,8 @@ out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
+out_free_reserved_ports:
+ kfree(sysctl_local_reserved_ports);
goto out;
}
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 8da6429..1acb462 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -37,6 +37,9 @@ struct local_ports sysctl_local_ports __read_mostly = {
.range = { 32768, 61000 },
};
+unsigned long *sysctl_local_reserved_ports;
+EXPORT_SYMBOL(sysctl_local_reserved_ports);
+
void inet_get_local_port_range(int *low, int *high)
{
unsigned seq;
@@ -108,6 +111,8 @@ again:
smallest_size = -1;
do {
+ if (inet_is_reserved_local_port(rover))
+ goto next_nolock;
head = &hashinfo->bhash[inet_bhashfn(net, rover,
hashinfo->bhash_size)];
spin_lock(&head->lock);
@@ -130,6 +135,7 @@ again:
break;
next:
spin_unlock(&head->lock);
+ next_nolock:
if (++rover > high)
rover = low;
} while (--remaining > 0);
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 2b79377..d3e160a 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -456,6 +456,8 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
local_bh_disable();
for (i = 1; i <= remaining; i++) {
port = low + (i + offset) % remaining;
+ if (inet_is_reserved_local_port(port))
+ continue;
head = &hinfo->bhash[inet_bhashfn(net, port,
hinfo->bhash_size)];
spin_lock(&head->lock);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 7e3712c..ce3597a 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -298,6 +298,13 @@ static struct ctl_table ipv4_table[] = {
.mode = 0644,
.proc_handler = ipv4_local_port_range,
},
+ {
+ .procname = "ip_local_reserved_ports",
+ .data = NULL, /* initialized in sysctl_ipv4_init */
+ .maxlen = 65536,
+ .mode = 0644,
+ .proc_handler = proc_dobitmap,
+ },
#ifdef CONFIG_IP_MULTICAST
{
.procname = "igmp_max_memberships",
@@ -721,6 +728,16 @@ static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
static __init int sysctl_ipv4_init(void)
{
struct ctl_table_header *hdr;
+ struct ctl_table *i;
+
+ for (i = ipv4_table; i->procname; i++) {
+ if (strcmp(i->procname, "ip_local_reserved_ports") == 0) {
+ i->data = sysctl_local_reserved_ports;
+ break;
+ }
+ }
+ if (!i->procname[0])
+ return -EINVAL;
hdr = register_sysctl_paths(net_ipv4_ctl_path, ipv4_table);
if (hdr == NULL)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 608a544..bfd0a6a 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -232,7 +232,8 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
*/
do {
if (low <= snum && snum <= high &&
- !test_bit(snum >> udptable->log, bitmap))
+ !test_bit(snum >> udptable->log, bitmap) &&
+ !inet_is_reserved_local_port(snum))
goto found;
snum += rand;
} while (snum != first);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f6d1e59..1f839d0 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -5432,6 +5432,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
rover++;
if ((rover < low) || (rover > high))
rover = low;
+ if (inet_is_reserved_local_port(rover))
+ continue;
index = sctp_phashfn(rover);
head = &sctp_port_hashtable[index];
sctp_spin_lock(&head->lock);
--
1.5.6.5
^ permalink raw reply related
* [net-next PATCH v4 2/3] sysctl: add proc_dobitmap
From: Octavian Purdila @ 2010-02-15 22:00 UTC (permalink / raw)
To: David Miller
Cc: Octavian Purdila, Linux Kernel Network Developers,
Linux Kernel Developers, WANG Cong, Eric W. Biederman
In-Reply-To: <1266271241-6293-1-git-send-email-opurdila@ixiacom.com>
The new function can be used to update bitmaps via /proc. Bits can be
set by writing positive values in the file and cleared by writing
negative values (e.g. 0 2 will set bits 1 and 3, -0 -2 will clear
them). Reading will show only the set bits.
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Cc: WANG Cong <amwang@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
---
include/linux/sysctl.h | 2 +
kernel/sysctl.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 0 deletions(-)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 9f236cd..ba89bf2 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -985,6 +985,8 @@ extern int proc_doulongvec_minmax(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,
void __user *, size_t *, loff_t *);
+extern int proc_dobitmap(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
/*
* Register a set of sysctl names by calling register_sysctl_table
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index b0f9618..b8959f4 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2596,6 +2596,82 @@ static int proc_do_cad_pid(struct ctl_table *table, int write,
return 0;
}
+/**
+ * proc_dobitmap - read/write from/to a bitmap
+ * @table: the sysctl table
+ * @write: %TRUE if this is a write to the sysctl file
+ * @buffer: the user buffer
+ * @lenp: the size of the user buffer
+ * @ppos: file position
+ * @ppos: the current position in the file
+ *
+ * The bitmap is stored at table->data and the bitmap length (in bits)
+ * in table->maxlen. Reading from the proc file will show the set bits.
+ * Writing positive values sets the bits, negative values clears them
+ * (e.g. 0 2 sets the first and 3rd bit, -0 -2 clears them).
+ *
+ * Returns 0 on success.
+ */
+int proc_dobitmap(struct ctl_table *table, int write,
+ void __user *_buffer, size_t *lenp, loff_t *ppos)
+{
+ bool first = 1;
+ unsigned long *bitmap = (unsigned long *) table->data;
+ unsigned long bitmap_len = table->maxlen;
+ int left = *lenp, err = 0;
+ char __user *buffer = (char __user *) _buffer;
+
+ if (!bitmap_len || !left || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) {
+ while (left) {
+ unsigned long val;
+ bool neg;
+
+ err = proc_get_next_ulong(&buffer, &left, &val, &neg);
+ if (err)
+ break;
+ if (val >= bitmap_len) {
+ err = -EINVAL;
+ break;
+ }
+ if (neg)
+ clear_bit(val, bitmap);
+ else
+ set_bit(val, bitmap);
+ first = 0;
+ }
+ if (!err)
+ err = proc_skip_wspace(&buffer, &left);
+ } else {
+ unsigned long bit = 0;
+
+ while (left) {
+ bit = find_next_bit(bitmap, bitmap_len, bit);
+ if (bit >= bitmap_len)
+ break;
+ err = proc_put_ulong(&buffer, &left, bit, 0, first);
+ if (err)
+ break;
+ first = 0; bit++;
+ }
+ if (!err)
+ err = proc_put_newline(&buffer, &left);
+ }
+
+ if (first && write && !err)
+ err = -EINVAL;
+ if (err == -EFAULT /* do we really need to check for -EFAULT? */ ||
+ (write && first))
+ return err ? : -EINVAL;
+ *lenp -= left;
+ *ppos += *lenp;
+ return 0;
+}
+
#else /* CONFIG_PROC_FS */
int proc_dostring(struct ctl_table *table, int write,
--
1.5.6.5
^ permalink raw reply related
* [net-next PATCH v4 1/3] sysctl: refactor integer handling proc code
From: Octavian Purdila @ 2010-02-15 22:00 UTC (permalink / raw)
To: David Miller
Cc: Octavian Purdila, Linux Kernel Network Developers,
Linux Kernel Developers, WANG Cong, Eric W. Biederman
In-Reply-To: <1266271241-6293-1-git-send-email-opurdila@ixiacom.com>
As we are about to add another integer handling proc function a little
bit of cleanup is in order: add a few helper functions to improve code
readability and decrease code duplication.
In the process a bug is fixed as well: if the user specifies a number
with more then 20 digits it will be interpreted as two integers
(e.g. 10000...13 will be interpreted as 100.... and 13).
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Cc: WANG Cong <amwang@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
---
kernel/sysctl.c | 298 +++++++++++++++++++++++++++----------------------------
1 files changed, 144 insertions(+), 154 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 8a68b24..b0f9618 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2039,8 +2039,98 @@ int proc_dostring(struct ctl_table *table, int write,
buffer, lenp, ppos);
}
+static int proc_skip_wspace(char __user **buf, size_t *size)
+{
+ char c;
+
+ while (*size) {
+ if (get_user(c, *buf))
+ return -EFAULT;
+ if (!isspace(c))
+ break;
+ (*size)--; (*buf)++;
+ }
+
+ return 0;
+}
+
+#define TMPBUFLEN 22
+static int proc_get_next_ulong(char __user **buf, size_t *size,
+ unsigned long *val, bool *neg)
+{
+ int len;
+ char *p, tmp[TMPBUFLEN];
+ int err;
+
+ err = proc_skip_wspace(buf, size);
+ if (err)
+ return err;
+ if (!*size)
+ return -EINVAL;
+
+ len = *size;
+ if (len > TMPBUFLEN-1)
+ len = TMPBUFLEN-1;
+
+ if (copy_from_user(tmp, *buf, len))
+ return -EFAULT;
+
+ tmp[len] = 0;
+ p = tmp;
+ if (*p == '-' && *size > 1) {
+ *neg = 1;
+ p++;
+ } else
+ *neg = 0;
+ if (*p < '0' || *p > '9')
+ return -EINVAL;
+
+ *val = simple_strtoul(p, &p, 0);
+
+ len = p - tmp;
+ if (((len < *size) && *p && !isspace(*p)) ||
+ /* We don't know if the next char is whitespace thus we may accept
+ * invalid integers (e.g. 1234...a) or two integers instead of one
+ * (e.g. 123...1). So lets not allow such large numbers. */
+ len == TMPBUFLEN - 1)
+ return -EINVAL;
-static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
+ *buf += len; *size -= len;
+
+ return 0;
+}
+
+static int proc_put_ulong(char __user **buf, size_t *size, unsigned long val,
+ bool neg, bool first)
+{
+ int len;
+ char tmp[TMPBUFLEN], *p = tmp;
+
+ if (!first)
+ *p++ = '\t';
+ sprintf(p, "%s%lu", neg ? "-" : "", val);
+ len = strlen(tmp);
+ if (len > *size)
+ len = *size;
+ if (copy_to_user(*buf, tmp, len))
+ return -EFAULT;
+ *size -= len;
+ *buf += len;
+ return 0;
+}
+#undef TMPBUFLEN
+
+static int proc_put_newline(char __user **buf, size_t *size)
+{
+ if (*size) {
+ if (put_user('\n', *buf))
+ return -EFAULT;
+ (*size)--, (*buf)++;
+ }
+ return 0;
+}
+
+static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
@@ -2049,7 +2139,7 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
} else {
int val = *valp;
if (val < 0) {
- *negp = -1;
+ *negp = 1;
*lvalp = (unsigned long)-val;
} else {
*negp = 0;
@@ -2060,19 +2150,15 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
}
static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
- int write, void __user *buffer,
+ int write, void __user *_buffer,
size_t *lenp, loff_t *ppos,
- int (*conv)(int *negp, unsigned long *lvalp, int *valp,
+ int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
int write, void *data),
void *data)
{
-#define TMPBUFLEN 21
- int *i, vleft, first = 1, neg;
- unsigned long lval;
- size_t left, len;
-
- char buf[TMPBUFLEN], *p;
- char __user *s = buffer;
+ int *i, vleft, first = 1, err = 0;
+ size_t left;
+ char __user *buffer = (char __user *) _buffer;
if (!tbl_data || !table->maxlen || !*lenp ||
(*ppos && !write)) {
@@ -2088,88 +2174,39 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
conv = do_proc_dointvec_conv;
for (; left && vleft--; i++, first=0) {
- if (write) {
- while (left) {
- char c;
- if (get_user(c, s))
- return -EFAULT;
- if (!isspace(c))
- break;
- left--;
- s++;
- }
- if (!left)
- break;
- neg = 0;
- len = left;
- if (len > sizeof(buf) - 1)
- len = sizeof(buf) - 1;
- if (copy_from_user(buf, s, len))
- return -EFAULT;
- buf[len] = 0;
- p = buf;
- if (*p == '-' && left > 1) {
- neg = 1;
- p++;
- }
- if (*p < '0' || *p > '9')
- break;
-
- lval = simple_strtoul(p, &p, 0);
+ unsigned long lval;
+ bool neg;
- len = p-buf;
- if ((len < left) && *p && !isspace(*p))
+ if (write) {
+ err = proc_get_next_ulong(&buffer, &left, &lval, &neg);
+ if (err)
break;
- s += len;
- left -= len;
-
if (conv(&neg, &lval, i, 1, data))
break;
} else {
- p = buf;
- if (!first)
- *p++ = '\t';
-
if (conv(&neg, &lval, i, 0, data))
break;
-
- sprintf(p, "%s%lu", neg ? "-" : "", lval);
- len = strlen(buf);
- if (len > left)
- len = left;
- if(copy_to_user(s, buf, len))
- return -EFAULT;
- left -= len;
- s += len;
- }
- }
-
- if (!write && !first && left) {
- if(put_user('\n', s))
- return -EFAULT;
- left--, s++;
- }
- if (write) {
- while (left) {
- char c;
- if (get_user(c, s++))
- return -EFAULT;
- if (!isspace(c))
+ err = proc_put_ulong(&buffer, &left, lval, neg, first);
+ if (err)
break;
- left--;
}
}
- if (write && first)
- return -EINVAL;
+
+ if (!write && !first && left && !err)
+ err = proc_put_newline(&buffer, &left);
+ if (write && !err)
+ err = proc_skip_wspace(&buffer, &left);
+ if (err == -EFAULT /* do we really need to check for -EFAULT? */ ||
+ (write && first))
+ return err ? : -EINVAL;
*lenp -= left;
*ppos += *lenp;
return 0;
-#undef TMPBUFLEN
}
static int do_proc_dointvec(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos,
- int (*conv)(int *negp, unsigned long *lvalp, int *valp,
+ int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
int write, void *data),
void *data)
{
@@ -2237,8 +2274,8 @@ struct do_proc_dointvec_minmax_conv_param {
int *max;
};
-static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
- int *valp,
+static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
+ int *valp,
int write, void *data)
{
struct do_proc_dointvec_minmax_conv_param *param = data;
@@ -2251,7 +2288,7 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
} else {
int val = *valp;
if (val < 0) {
- *negp = -1;
+ *negp = 1;
*lvalp = (unsigned long)-val;
} else {
*negp = 0;
@@ -2289,17 +2326,15 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
}
static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
- void __user *buffer,
+ void __user *_buffer,
size_t *lenp, loff_t *ppos,
unsigned long convmul,
unsigned long convdiv)
{
-#define TMPBUFLEN 21
- unsigned long *i, *min, *max, val;
- int vleft, first=1, neg;
- size_t len, left;
- char buf[TMPBUFLEN], *p;
- char __user *s = buffer;
+ unsigned long *i, *min, *max;
+ int vleft, first = 1, err = 0;
+ size_t left;
+ char __user *buffer = (char __user *) _buffer;
if (!data || !table->maxlen || !*lenp ||
(*ppos && !write)) {
@@ -2314,82 +2349,37 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
left = *lenp;
for (; left && vleft--; i++, min++, max++, first=0) {
+ unsigned long val;
+
if (write) {
- while (left) {
- char c;
- if (get_user(c, s))
- return -EFAULT;
- if (!isspace(c))
- break;
- left--;
- s++;
- }
- if (!left)
- break;
- neg = 0;
- len = left;
- if (len > TMPBUFLEN-1)
- len = TMPBUFLEN-1;
- if (copy_from_user(buf, s, len))
- return -EFAULT;
- buf[len] = 0;
- p = buf;
- if (*p == '-' && left > 1) {
- neg = 1;
- p++;
- }
- if (*p < '0' || *p > '9')
- break;
- val = simple_strtoul(p, &p, 0) * convmul / convdiv ;
- len = p-buf;
- if ((len < left) && *p && !isspace(*p))
+ bool neg;
+
+ err = proc_get_next_ulong(&buffer, &left, &val, &neg);
+ if (err)
break;
if (neg)
- val = -val;
- s += len;
- left -= len;
-
- if(neg)
continue;
if ((min && val < *min) || (max && val > *max))
continue;
*i = val;
} else {
- p = buf;
- if (!first)
- *p++ = '\t';
- sprintf(p, "%lu", convdiv * (*i) / convmul);
- len = strlen(buf);
- if (len > left)
- len = left;
- if(copy_to_user(s, buf, len))
- return -EFAULT;
- left -= len;
- s += len;
- }
- }
-
- if (!write && !first && left) {
- if(put_user('\n', s))
- return -EFAULT;
- left--, s++;
- }
- if (write) {
- while (left) {
- char c;
- if (get_user(c, s++))
- return -EFAULT;
- if (!isspace(c))
+ val = convdiv * (*i) / convmul;
+ err = proc_put_ulong(&buffer, &left, val, 0, first);
+ if (err)
break;
- left--;
}
}
- if (write && first)
- return -EINVAL;
+
+ if (!write && !first && left && !err)
+ err = proc_put_newline(&buffer, &left);
+ if (write && !err)
+ err = proc_skip_wspace(&buffer, &left);
+ if (err == -EFAULT /* do we really need to check for -EFAULT? */ ||
+ (write && first))
+ return err ? : -EINVAL;
*lenp -= left;
*ppos += *lenp;
return 0;
-#undef TMPBUFLEN
}
static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
@@ -2450,7 +2440,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
}
-static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
+static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
@@ -2462,7 +2452,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
int val = *valp;
unsigned long lval;
if (val < 0) {
- *negp = -1;
+ *negp = 1;
lval = (unsigned long)-val;
} else {
*negp = 0;
@@ -2473,7 +2463,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
return 0;
}
-static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
+static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
@@ -2485,7 +2475,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
int val = *valp;
unsigned long lval;
if (val < 0) {
- *negp = -1;
+ *negp = 1;
lval = (unsigned long)-val;
} else {
*negp = 0;
@@ -2496,7 +2486,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
return 0;
}
-static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
+static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
@@ -2506,7 +2496,7 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
int val = *valp;
unsigned long lval;
if (val < 0) {
- *negp = -1;
+ *negp = 1;
lval = (unsigned long)-val;
} else {
*negp = 0;
--
1.5.6.5
^ permalink raw reply related
* [net-next PATCH v4 0/3] net: reserve ports for applications using fixed port
From: Octavian Purdila @ 2010-02-15 22:00 UTC (permalink / raw)
To: David Miller
Cc: Octavian Purdila, Linux Kernel Network Developers,
Linux Kernel Developers, Amerigo Wang
This iteration makes the bitmap dynamically allocated since it is
quite big (8192 bytes) and adding that much in BSS may still,
apparently, cause problems on some architectures.
Octavian Purdila (3):
sysctl: refactor integer handling proc code
sysctl: add proc_dobitmap
net: reserve ports for applications using fixed port numbers
Documentation/networking/ip-sysctl.txt | 12 +
drivers/infiniband/core/cma.c | 7 +-
include/linux/sysctl.h | 2 +
include/net/ip.h | 6 +
kernel/sysctl.c | 374 +++++++++++++++++++-------------
net/ipv4/af_inet.c | 8 +-
net/ipv4/inet_connection_sock.c | 6 +
net/ipv4/inet_hashtables.c | 2 +
net/ipv4/sysctl_net_ipv4.c | 17 ++
net/ipv4/udp.c | 3 +-
net/sctp/socket.c | 2 +
11 files changed, 282 insertions(+), 157 deletions(-)
^ permalink raw reply
* Re: [PATCH 0/4] Improvements to octeon_ethernet.
From: Greg KH @ 2010-02-15 21:05 UTC (permalink / raw)
To: David Daney; +Cc: ralf.baechle, linux-mips, Netdev
In-Reply-To: <4B79AAA6.60005@caviumnetworks.com>
On Mon, Feb 15, 2010 at 12:12:22PM -0800, David Daney wrote:
> Here are a couple of improvements to the octeon_ethernet in
> drivers/staging/octeon. The first patch is just cleanup, the rest are
> genuine bug fixes.
>
> I will reply with the four patches.
>
> We may want to merge via Ralf's linux-mips.org tree as Octeon is
> infact a MIPS based SOC and he has a bunch of other patches queued
> there that these depend on.
That's fine with me for them to go through that way.
thanks,
greg k-h
^ permalink raw reply
* [PATCH v5 4/7] tcp: input header length, prediction, and timestamp bugs
From: William Allen Simpson @ 2010-02-15 21:24 UTC (permalink / raw)
To: Linux Kernel Developers
Cc: Linux Kernel Network Developers, Andrew Morton, David Miller,
Andi Kleen
In-Reply-To: <4B793E8F.30208@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1883 bytes --]
Fix incorrect header prediction flags documentation.
Relieve register pressure in (the i386) fast path by accessing skb->len
directly, instead of carrying a rarely used len parameter.
Eliminate unused len parameters in two other functions.
Don't use output calculated tp->tcp_header_len for input decisions.
While the output header is usually the same as the input (same options
in both directions), that's a poor assumption. In particular, Sack will
be different. Newer options are not guaranteed.
Moreover, in the fast path, that only saved a shift or two. The other
efficiencies in this patch more than make up the difference.
Instead, use tp->rx_opt.tstamp_ok to accurately predict header length.
Likewise, use tp->rx_opt.tstamp_ok for received MSS calculations.
Don't use "sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED" to guess that
the timestamp is present. This may have been OK in the days with fewer
possible options, but various combinations of newer options may yield
the same header length. (This bug is in 3 places.)
Instead, use tp->rx_opt.saw_tstamp to determine a timestamp is present.
There's no need to test buffer length against header length, already
checked by tcp_v[4,6]_rcv(). Straighten code for minor efficiency gain.
Stand-alone patch, originally developed for TCPCT.
Requires:
net: tcp_header_len_th and tcp_option_len_th
tcp: harmonize tcp_vx_rcv header length assumptions
Signed-off-by: William.Allen.Simpson@gmail.com
Reviewed-by: Andi Kleen <andi@firstfloor.org>
---
include/linux/tcp.h | 6 ++-
include/net/tcp.h | 18 ++++++--
net/ipv4/tcp_input.c | 96 +++++++++++++++++++---------------------------
net/ipv4/tcp_ipv4.c | 4 +-
net/ipv4/tcp_minisocks.c | 3 +-
net/ipv4/tcp_probe.c | 2 +-
net/ipv6/tcp_ipv6.c | 4 +-
7 files changed, 63 insertions(+), 70 deletions(-)
[-- Attachment #2: len_th+2d5+2.6.33-rc8.patch --]
[-- Type: text/plain, Size: 10166 bytes --]
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 74728f7..2987ee8 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -301,7 +301,11 @@ struct tcp_sock {
/*
* Header prediction flags
- * 0x5?10 << 16 + snd_wnd in net byte order
+ * S << 28 + TCP_FLAG_ACK + snd_wnd, in net byte order
+ * (PSH flag is ignored)
+ * S is 5 (no options), or 8 (timestamp aligned)
+ * otherwise, 0 to turn it off -- for instance, when there are
+ * holes in receive space.
*/
__be32 pred_flags;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 34f5cc2..52dd185 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -310,13 +310,11 @@ extern int tcp_ioctl(struct sock *sk,
extern int tcp_rcv_state_process(struct sock *sk,
struct sk_buff *skb,
- struct tcphdr *th,
- unsigned len);
+ struct tcphdr *th);
extern int tcp_rcv_established(struct sock *sk,
struct sk_buff *skb,
- struct tcphdr *th,
- unsigned len);
+ struct tcphdr *th);
extern void tcp_rcv_space_adjust(struct sock *sk);
@@ -533,9 +531,19 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
return (tp->srtt >> 3) + tp->rttvar;
}
+static inline u8 __tcp_fast_path_header_length(const struct tcp_sock *tp)
+{
+ return tp->rx_opt.tstamp_ok
+ ? sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED
+ : sizeof(struct tcphdr);
+}
+
static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
{
- tp->pred_flags = htonl((tp->tcp_header_len << 26) |
+ /* See linux/tcp.h for pred_flags details.
+ * 'S' (doff) is 32-bit words, convert from bytes: 26 = 28 - 2.
+ */
+ tp->pred_flags = htonl((__tcp_fast_path_header_length(tp) << 26) |
ntohl(TCP_FLAG_ACK) |
snd_wnd);
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 28e0296..d314170 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -152,7 +152,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb)
* tcp header plus fixed timestamp option length.
* Resulting "len" is MSS free of SACK jitter.
*/
- len -= tcp_sk(sk)->tcp_header_len;
+ len -= __tcp_fast_path_header_length(tcp_sk(sk));
icsk->icsk_ack.last_seg_size = len;
if (len == lss) {
icsk->icsk_ack.rcv_mss = len;
@@ -5206,7 +5206,7 @@ discard:
* tcp_data_queue when everything is OK.
*/
int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
- struct tcphdr *th, unsigned len)
+ struct tcphdr *th)
{
struct tcp_sock *tp = tcp_sk(sk);
int res;
@@ -5224,32 +5224,18 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
* Our current scheme is not silly either but we take the
* extra cost of the net_bh soft interrupt processing...
* We do checksum and copy also but from device to kernel.
+ *
+ * See linux/tcp.h and net/tcp.h for pred_flags details.
*/
-
- tp->rx_opt.saw_tstamp = 0;
-
- /* pred_flags is 0xS?10 << 16 + snd_wnd
- * if header_prediction is to be made
- * 'S' will always be tp->tcp_header_len >> 2
- * '?' will be 0 for the fast path, otherwise pred_flags is 0 to
- * turn it off (when there are holes in the receive
- * space for instance)
- * PSH flag is ignored.
- */
-
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
- int tcp_header_len = tp->tcp_header_len;
-
- /* Timestamp header prediction: tcp_header_len
- * is automatically equal to th->doff*4 due to pred_flags
- * match.
- */
+ int tcp_header_len = tcp_header_len_th(th);
- /* Check timestamp */
- if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {
- /* No? Slow path! */
+ /* Timestamp header prediction */
+ if (tcp_header_len != sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) {
+ tp->rx_opt.saw_tstamp = 0; /* false */
+ } else {
if (!tcp_parse_aligned_timestamp(tp, th))
goto slow_path;
@@ -5264,35 +5250,12 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
*/
}
- if (len <= tcp_header_len) {
- /* Bulk data transfer: sender */
- if (len == tcp_header_len) {
- /* Predicted packet is in window by definition.
- * seq == rcv_nxt and rcv_wup <= rcv_nxt.
- * Hence, check seq<=rcv_wup reduces to:
- */
- if (tcp_header_len ==
- (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
- tp->rcv_nxt == tp->rcv_wup)
- tcp_store_ts_recent(tp);
-
- /* We know that such packets are checksummed
- * on entry.
- */
- tcp_ack(sk, skb, 0);
- __kfree_skb(skb);
- tcp_data_snd_check(sk);
- return 0;
- } else { /* Header too small */
- TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
- goto discard;
- }
- } else {
+ if (tcp_header_len < skb->len) {
int eaten = 0;
int copied_early = 0;
if (tp->copied_seq == tp->rcv_nxt &&
- len - tcp_header_len <= tp->ucopy.len) {
+ skb->len - tcp_header_len <= tp->ucopy.len) {
#ifdef CONFIG_NET_DMA
if (tcp_dma_try_early_copy(sk, skb, tcp_header_len)) {
copied_early = 1;
@@ -5311,9 +5274,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
*/
- if (tcp_header_len ==
- (sizeof(struct tcphdr) +
- TCPOLEN_TSTAMP_ALIGNED) &&
+ if (tp->rx_opt.saw_tstamp &&
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp);
@@ -5334,8 +5295,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
*/
- if (tcp_header_len ==
- (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
+ if (tp->rx_opt.saw_tstamp &&
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp);
@@ -5376,11 +5336,33 @@ no_ack:
else
sk->sk_data_ready(sk, 0);
return 0;
+ } else {
+ /* Bulk data transfer: sender
+ *
+ * tcp_header_len > skb->len never happens,
+ * already checked by tcp_v[4,6]_rcv()
+ *
+ * Predicted packet is in window by definition.
+ * seq == rcv_nxt and rcv_wup <= rcv_nxt.
+ * Hence, check seq<=rcv_wup reduces to:
+ */
+ if (tp->rx_opt.saw_tstamp &&
+ tp->rcv_nxt == tp->rcv_wup)
+ tcp_store_ts_recent(tp);
+
+ /* We know that such packets are checksummed
+ * on entry.
+ */
+ tcp_ack(sk, skb, 0);
+ __kfree_skb(skb);
+ tcp_data_snd_check(sk);
+ return 0;
}
}
slow_path:
- if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
+ /* Assumes header and options unchanged since checksum_init() */
+ if (tcp_checksum_complete_user(sk, skb))
goto csum_error;
/*
@@ -5416,7 +5398,7 @@ discard:
}
static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
- struct tcphdr *th, unsigned len)
+ struct tcphdr *th)
{
u8 *hash_location;
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -5693,7 +5675,7 @@ reset_and_undo:
*/
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
- struct tcphdr *th, unsigned len)
+ struct tcphdr *th)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -5740,7 +5722,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
goto discard;
case TCP_SYN_SENT:
- queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
+ queued = tcp_rcv_synsent_state_process(sk, skb, th);
if (queued >= 0)
return queued;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0a76e41..f999e06 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1551,7 +1551,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
TCP_CHECK_TIMER(sk);
- if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
+ if (tcp_rcv_established(sk, skb, tcp_hdr(skb))) {
rsk = sk;
goto reset;
}
@@ -1578,7 +1578,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
}
TCP_CHECK_TIMER(sk);
- if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
+ if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb))) {
rsk = sk;
goto reset;
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index f206ee5..37b7536 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -718,8 +718,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,
int state = child->sk_state;
if (!sock_owned_by_user(child)) {
- ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb),
- skb->len);
+ ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb));
/* Wakeup parent, send SIGIO */
if (state == TCP_SYN_RECV && child->sk_state != state)
parent->sk_data_ready(parent, 0);
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 9bc805d..de2a32e 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -88,7 +88,7 @@ static inline int tcp_probe_avail(void)
* Note: arguments must match tcp_rcv_established()!
*/
static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb,
- struct tcphdr *th, unsigned len)
+ struct tcphdr *th)
{
const struct tcp_sock *tp = tcp_sk(sk);
const struct inet_sock *inet = inet_sk(sk);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index b76939a..3d08a4d 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1586,7 +1586,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
TCP_CHECK_TIMER(sk);
- if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))
+ if (tcp_rcv_established(sk, skb, tcp_hdr(skb)))
goto reset;
TCP_CHECK_TIMER(sk);
if (opt_skb)
@@ -1618,7 +1618,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
}
TCP_CHECK_TIMER(sk);
- if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len))
+ if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb)))
goto reset;
TCP_CHECK_TIMER(sk);
if (opt_skb)
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH v4 4/7] tcp: input header length, prediction, and timestamp bugs
From: William Allen Simpson @ 2010-02-15 21:24 UTC (permalink / raw)
To: Andi Kleen
Cc: Linux Kernel Developers, Linux Kernel Network Developers,
Andrew Morton, David Miller
In-Reply-To: <20100215191548.GH21783@one.firstfloor.org>
Andi Kleen wrote:
> On Mon, Feb 15, 2010 at 02:03:34PM -0500, William Allen Simpson wrote:
>> Andi Kleen wrote:
>>> Is this a bug fix?
>>>
>> Yes. One of many, all inter-related.
>>
> It's useful to distingush bug fixes from cleanups from optimizations etc.
> ...
The bug fixes depend on the cleanups.
* 1/7 has the new functions.
* 2/7 has bug fixes that require the new functions.
* 3/7 fixes (and optimizes) the incoming code path, so that
* 4/7 can fix the bugs.
* 5/7 is needed to test the code from part 1.
* 6/7 is the cleanup of parsing (also fixes some bugs introduced by
davem's patch in December), so that
* 7/7 can fix the bugs.
I originally tried submitting these as separate individual pairs of
patches (in November, December, January), but nobody paid any attention.
^ permalink raw reply
* Re: [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner.
From: Eric Dumazet @ 2010-02-15 21:11 UTC (permalink / raw)
To: David Daney; +Cc: ralf, linux-mips, netdev, gregkh
In-Reply-To: <4B79B15F.7030506@caviumnetworks.com>
Le lundi 15 février 2010 à 12:41 -0800, David Daney a écrit :
> On 02/15/2010 12:27 PM, Eric Dumazet wrote:
> > Le lundi 15 février 2010 à 12:13 -0800, David Daney a écrit :
> >> If we wait for the once-per-second cleanup to free transmit SKBs,
> >> sockets with small transmit buffer sizes might spend most of their
> >> time blocked waiting for the cleanup.
> >>
> >> Normally we do a cleanup for each transmitted packet. We add a
> >> watchdog type timer so that we also schedule a timeout for 150uS after
> >> a packet is transmitted. The watchdog is reset for each transmitted
> >> packet, so for high packet rates, it never expires. At these high
> >> rates, the cleanups are done for each packet so the extra watchdog
> >> initiated cleanups are not needed.
> >
> > s/needed/fired/
> >
>
> or perhaps s/are not needed/are neither needed nor fired/
>
> > Hmm, but re-arming a timer for each transmited packet must have a cost ?
> >
>
> The cost is fairly low (less than 10 processor clock cycles). We didn't
> add this for amusement, people actually do things like only send UDP
> packets from userspace. Since we can fill the transmit queue faster
> than it is emptied, the socket transmit buffer is quickly consumed. If
> we don't free the SKBs in short order, the transmitting process get to
> take a long sleep (until our previous once per second clean up task was
> run).
I understand this, but traditionaly, NIC drivers dont use a timer, but a
'TX complete' interrupt, that usually fires a few us after packet
submission on Gigabit speed.
A fast program could try to send X small udp packets in less than 150
us, X being greater than the size of your TX ring.
So your patch makes the window smaller, but it still is there (at
physical layer, we'll see a burst of packets, a ~100us delay, then a
second burst)
^ permalink raw reply
* Re: 2.6.33-rc8: Reported regressions from 2.6.32
From: Rafael J. Wysocki @ 2010-02-15 20:59 UTC (permalink / raw)
To: Rafał Miłecki, Linus Torvalds
Cc: Linux Kernel Mailing List, Maciej Rutecki, Kernel Testers List,
Network Development, Linux ACPI, Linux PM List, Linux SCSI List,
Linux Wireless List, DRI, Andrew Morton
In-Reply-To: <b170af451002150152r43d7d916kd3d2013b8990627d-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Monday 15 February 2010, Rafał Miłecki wrote:
> 2010/2/15 Rafael J. Wysocki <rjw-KKrjLPT3xs0@public.gmane.org>:
> > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=15287
> > Subject : RadeonKMS segfaults kdm on mobility radeon x700 pcie
> > Submitter : Jan Kreuzer <kontrollator-Mmb7MZpHnFY@public.gmane.org>
> > Date : 2010-02-12 17:58 (3 days old)
>
> Can you reassign this according to last comments, please?
Reassigned to PCI.
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner.
From: David Daney @ 2010-02-15 20:41 UTC (permalink / raw)
To: Eric Dumazet; +Cc: ralf, linux-mips, netdev, gregkh
In-Reply-To: <1266265673.2859.5.camel@edumazet-laptop>
On 02/15/2010 12:27 PM, Eric Dumazet wrote:
> Le lundi 15 février 2010 à 12:13 -0800, David Daney a écrit :
>> If we wait for the once-per-second cleanup to free transmit SKBs,
>> sockets with small transmit buffer sizes might spend most of their
>> time blocked waiting for the cleanup.
>>
>> Normally we do a cleanup for each transmitted packet. We add a
>> watchdog type timer so that we also schedule a timeout for 150uS after
>> a packet is transmitted. The watchdog is reset for each transmitted
>> packet, so for high packet rates, it never expires. At these high
>> rates, the cleanups are done for each packet so the extra watchdog
>> initiated cleanups are not needed.
>
> s/needed/fired/
>
or perhaps s/are not needed/are neither needed nor fired/
> Hmm, but re-arming a timer for each transmited packet must have a cost ?
>
The cost is fairly low (less than 10 processor clock cycles). We didn't
add this for amusement, people actually do things like only send UDP
packets from userspace. Since we can fill the transmit queue faster
than it is emptied, the socket transmit buffer is quickly consumed. If
we don't free the SKBs in short order, the transmitting process get to
take a long sleep (until our previous once per second clean up task was
run).
>>
>> Signed-off-by: David Daney<ddaney@caviumnetworks.com>
>
> Is there any particular reason periodic is spelled preiodic ?
Ha! No good reason. I will correct the spelling.
Thanks,
David Daney
>
>> ---
>> }
>>
>> -static void cvm_oct_tx_clean_worker(struct work_struct *work)
>> +static void cvm_oct_preiodic_worker(struct work_struct *work)
>> {
>
>
>
>> - INIT_DELAYED_WORK(&priv->tx_clean_work,
>> - cvm_oct_tx_clean_worker);
>> -
>> + INIT_DELAYED_WORK(&priv->port_periodic_work,
>> + cvm_oct_preiodic_worker);
>
>
>
>
^ permalink raw reply
* Re: [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner.
From: Eric Dumazet @ 2010-02-15 20:27 UTC (permalink / raw)
To: David Daney; +Cc: ralf, linux-mips, netdev, gregkh
In-Reply-To: <1266264799-3510-4-git-send-email-ddaney@caviumnetworks.com>
Le lundi 15 février 2010 à 12:13 -0800, David Daney a écrit :
> If we wait for the once-per-second cleanup to free transmit SKBs,
> sockets with small transmit buffer sizes might spend most of their
> time blocked waiting for the cleanup.
>
> Normally we do a cleanup for each transmitted packet. We add a
> watchdog type timer so that we also schedule a timeout for 150uS after
> a packet is transmitted. The watchdog is reset for each transmitted
> packet, so for high packet rates, it never expires. At these high
> rates, the cleanups are done for each packet so the extra watchdog
> initiated cleanups are not needed.
s/needed/fired/
Hmm, but re-arming a timer for each transmited packet must have a cost ?
>
> Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Is there any particular reason periodic is spelled preiodic ?
> ---
> }
>
> -static void cvm_oct_tx_clean_worker(struct work_struct *work)
> +static void cvm_oct_preiodic_worker(struct work_struct *work)
> {
> - INIT_DELAYED_WORK(&priv->tx_clean_work,
> - cvm_oct_tx_clean_worker);
> -
> + INIT_DELAYED_WORK(&priv->port_periodic_work,
> + cvm_oct_preiodic_worker);
^ permalink raw reply
* [PATCH] SiS190/191 half-duplex initialization fix
From: Riccardo Ghetta @ 2010-02-15 20:19 UTC (permalink / raw)
To: romieu; +Cc: netdev, Riccardo Ghetta
Adds half-duplex specific setup code (taken from SiS own GPL driver).
Without those, half-duplex connections are very unreliable, often
working on small transfers and failing after a while.
Signed-off-by: Riccardo Ghetta <birrachiara@tin.it>
---
drivers/net/sis190.c | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 626de76..6f581e7 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -47,7 +47,7 @@
#define PHY_ID_ANY 0x1f
#define MII_REG_ANY 0x1f
-#define DRV_VERSION "1.3"
+#define DRV_VERSION "1.4"
#define DRV_NAME "sis190"
#define SIS190_DRIVER_NAME DRV_NAME " Gigabit Ethernet driver " DRV_VERSION
#define PFX DRV_NAME ": "
@@ -294,6 +294,7 @@ struct sis190_private {
struct mii_if_info mii_if;
struct list_head first_phy;
u32 features;
+ u32 negotiated_lpa;
};
struct sis190_phy {
@@ -1004,6 +1005,8 @@ static void sis190_phy_task(struct work_struct *work)
SIS_W32(RGDelay, 0x0440);
}
+ tp->negotiated_lpa = p->val;
+
net_link(tp, KERN_INFO "%s: link on %s mode.\n", dev->name,
p->msg);
netif_carrier_on(dev);
@@ -1211,6 +1214,13 @@ static netdev_tx_t sis190_start_xmit(struct sk_buff *skb,
wmb();
desc->status = cpu_to_le32(OWNbit | INTbit | DEFbit | CRCbit | PADbit);
+ if (tp->negotiated_lpa & (LPA_1000HALF | LPA_100HALF | LPA_10HALF))
+ {
+ /* Half Duplex */
+ desc->status |= cpu_to_le32(COLEN | CRSEN | BKFEN);
+ if (tp->negotiated_lpa & (LPA_1000HALF | LPA_1000FULL))
+ desc->status |= cpu_to_le32(EXTEN | BSTEN); /* gigabit HD */
+ }
tp->cur_tx++;
--
1.6.4.4
^ permalink raw reply related
* Re: [PATCH] net: remove INIT_RCU_HEAD() usage
From: Paul E. McKenney @ 2010-02-15 20:13 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: davem, netdev
In-Reply-To: <20100212214138.GA5866@x200>
On Fri, Feb 12, 2010 at 11:41:39PM +0200, Alexey Dobriyan wrote:
> call_rcu() will unconditionally reinitialize RCU head anyway.
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
> Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
> ---
>
> net/core/drop_monitor.c | 1 -
> net/ipv6/sit.c | 1 -
> net/ipv6/xfrm6_tunnel.c | 1 -
> net/netlabel/netlabel_domainhash.c | 1 -
> net/netlabel/netlabel_unlabeled.c | 3 ---
> net/sctp/bind_addr.c | 1 -
> net/sctp/ipv6.c | 1 -
> net/sctp/protocol.c | 1 -
> 8 files changed, 10 deletions(-)
>
> --- a/net/core/drop_monitor.c
> +++ b/net/core/drop_monitor.c
> @@ -296,7 +296,6 @@ static int dropmon_net_event(struct notifier_block *ev_block,
>
> new_stat->dev = dev;
> new_stat->last_rx = jiffies;
> - INIT_RCU_HEAD(&new_stat->rcu);
> spin_lock(&trace_state_lock);
> list_add_rcu(&new_stat->list, &hw_stats_list);
> spin_unlock(&trace_state_lock);
> --- a/net/ipv6/sit.c
> +++ b/net/ipv6/sit.c
> @@ -363,7 +363,6 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
> goto out;
> }
>
> - INIT_RCU_HEAD(&p->rcu_head);
> p->next = t->prl;
> p->addr = a->addr;
> p->flags = a->flags;
> --- a/net/ipv6/xfrm6_tunnel.c
> +++ b/net/ipv6/xfrm6_tunnel.c
> @@ -178,7 +178,6 @@ alloc_spi:
> if (!x6spi)
> goto out;
>
> - INIT_RCU_HEAD(&x6spi->rcu_head);
> memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr));
> x6spi->spi = spi;
> atomic_set(&x6spi->refcnt, 1);
> --- a/net/netlabel/netlabel_domainhash.c
> +++ b/net/netlabel/netlabel_domainhash.c
> @@ -315,7 +315,6 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
> entry_old = netlbl_domhsh_search_def(entry->domain);
> if (entry_old == NULL) {
> entry->valid = 1;
> - INIT_RCU_HEAD(&entry->rcu);
>
> if (entry->domain != NULL) {
> u32 bkt = netlbl_domhsh_hash(entry->domain);
> --- a/net/netlabel/netlabel_unlabeled.c
> +++ b/net/netlabel/netlabel_unlabeled.c
> @@ -327,7 +327,6 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
> entry->list.addr = addr->s_addr & mask->s_addr;
> entry->list.mask = mask->s_addr;
> entry->list.valid = 1;
> - INIT_RCU_HEAD(&entry->rcu);
> entry->secid = secid;
>
> spin_lock(&netlbl_unlhsh_lock);
> @@ -373,7 +372,6 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
> entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
> ipv6_addr_copy(&entry->list.mask, mask);
> entry->list.valid = 1;
> - INIT_RCU_HEAD(&entry->rcu);
> entry->secid = secid;
>
> spin_lock(&netlbl_unlhsh_lock);
> @@ -410,7 +408,6 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
> INIT_LIST_HEAD(&iface->addr4_list);
> INIT_LIST_HEAD(&iface->addr6_list);
> iface->valid = 1;
> - INIT_RCU_HEAD(&iface->rcu);
>
> spin_lock(&netlbl_unlhsh_lock);
> if (ifindex > 0) {
> --- a/net/sctp/bind_addr.c
> +++ b/net/sctp/bind_addr.c
> @@ -186,7 +186,6 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
> addr->valid = 1;
>
> INIT_LIST_HEAD(&addr->list);
> - INIT_RCU_HEAD(&addr->rcu);
>
> /* We always hold a socket lock when calling this function,
> * and that acts as a writer synchronizing lock.
> --- a/net/sctp/ipv6.c
> +++ b/net/sctp/ipv6.c
> @@ -381,7 +381,6 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,
> addr->a.v6.sin6_scope_id = dev->ifindex;
> addr->valid = 1;
> INIT_LIST_HEAD(&addr->list);
> - INIT_RCU_HEAD(&addr->rcu);
> list_add_tail(&addr->list, addrlist);
> }
> }
> --- a/net/sctp/protocol.c
> +++ b/net/sctp/protocol.c
> @@ -188,7 +188,6 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
> addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
> addr->valid = 1;
> INIT_LIST_HEAD(&addr->list);
> - INIT_RCU_HEAD(&addr->rcu);
> list_add_tail(&addr->list, addrlist);
> }
> }
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 3/4] MIPS: Octeon: Do proper acknowledgment of CIU timer interrupts.
From: David Daney @ 2010-02-15 20:13 UTC (permalink / raw)
To: ralf, linux-mips, netdev, gregkh; +Cc: David Daney
In-Reply-To: <4B79AAA6.60005@caviumnetworks.com>
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
---
arch/mips/cavium-octeon/octeon-irq.c | 67 ++++++++++++++++++++++++++++++++--
1 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index 1460d08..0bc79dc 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -51,9 +51,6 @@ static void octeon_irq_core_eoi(unsigned int irq)
*/
if (desc->status & IRQ_DISABLED)
return;
-
- /* There is a race here. We should fix it. */
-
/*
* We don't need to disable IRQs to make these atomic since
* they are already disabled earlier in the low level
@@ -202,6 +199,29 @@ static void octeon_irq_ciu0_ack_v2(unsigned int irq)
}
/*
+ * CIU timer type interrupts must be acknoleged by writing a '1' bit
+ * to their sum0 bit.
+ */
+static void octeon_irq_ciu0_timer_ack(unsigned int irq)
+{
+ int index = cvmx_get_core_num() * 2;
+ uint64_t mask = 1ull << (irq - OCTEON_IRQ_WORKQ0);
+ cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);
+}
+
+static void octeon_irq_ciu0_timer_ack_v1(unsigned int irq)
+{
+ octeon_irq_ciu0_timer_ack(irq);
+ octeon_irq_ciu0_ack(irq);
+}
+
+static void octeon_irq_ciu0_timer_ack_v2(unsigned int irq)
+{
+ octeon_irq_ciu0_timer_ack(irq);
+ octeon_irq_ciu0_ack_v2(irq);
+}
+
+/*
* Enable the irq on the current core for chips that have the EN*_W1{S,C}
* registers.
*/
@@ -304,6 +324,28 @@ static struct irq_chip octeon_irq_chip_ciu0 = {
#endif
};
+static struct irq_chip octeon_irq_chip_ciu0_timer_v2 = {
+ .name = "CIU0-T",
+ .enable = octeon_irq_ciu0_enable_v2,
+ .disable = octeon_irq_ciu0_disable_all_v2,
+ .ack = octeon_irq_ciu0_timer_ack_v2,
+ .eoi = octeon_irq_ciu0_eoi_v2,
+#ifdef CONFIG_SMP
+ .set_affinity = octeon_irq_ciu0_set_affinity_v2,
+#endif
+};
+
+static struct irq_chip octeon_irq_chip_ciu0_timer = {
+ .name = "CIU0-T",
+ .enable = octeon_irq_ciu0_enable,
+ .disable = octeon_irq_ciu0_disable,
+ .ack = octeon_irq_ciu0_timer_ack_v1,
+ .eoi = octeon_irq_ciu0_eoi,
+#ifdef CONFIG_SMP
+ .set_affinity = octeon_irq_ciu0_set_affinity,
+#endif
+};
+
static void octeon_irq_ciu1_ack(unsigned int irq)
{
@@ -587,6 +629,7 @@ void __init arch_init_irq(void)
{
int irq;
struct irq_chip *chip0;
+ struct irq_chip *chip0_timer;
struct irq_chip *chip1;
#ifdef CONFIG_SMP
@@ -602,9 +645,11 @@ void __init arch_init_irq(void)
OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) ||
OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X)) {
chip0 = &octeon_irq_chip_ciu0_v2;
+ chip0_timer = &octeon_irq_chip_ciu0_timer_v2;
chip1 = &octeon_irq_chip_ciu1_v2;
} else {
chip0 = &octeon_irq_chip_ciu0;
+ chip0_timer = &octeon_irq_chip_ciu0_timer;
chip1 = &octeon_irq_chip_ciu1;
}
@@ -618,7 +663,21 @@ void __init arch_init_irq(void)
/* 24 - 87 CIU_INT_SUM0 */
for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_BOOTDMA; irq++) {
- set_irq_chip_and_handler(irq, chip0, handle_percpu_irq);
+ switch (irq) {
+ case OCTEON_IRQ_GMX_DRP0:
+ case OCTEON_IRQ_GMX_DRP1:
+ case OCTEON_IRQ_IPD_DRP:
+ case OCTEON_IRQ_KEY_ZERO:
+ case OCTEON_IRQ_TIMER0:
+ case OCTEON_IRQ_TIMER1:
+ case OCTEON_IRQ_TIMER2:
+ case OCTEON_IRQ_TIMER3:
+ set_irq_chip_and_handler(irq, chip0_timer, handle_percpu_irq);
+ break;
+ default:
+ set_irq_chip_and_handler(irq, chip0, handle_percpu_irq);
+ break;
+ }
}
/* 88 - 151 CIU_INT_SUM1 */
--
1.6.6
^ permalink raw reply related
* [PATCH 4/4] Staging: Octeon: Free transmit SKBs in a timely manner.
From: David Daney @ 2010-02-15 20:13 UTC (permalink / raw)
To: ralf, linux-mips, netdev, gregkh; +Cc: David Daney
In-Reply-To: <4B79AAA6.60005@caviumnetworks.com>
If we wait for the once-per-second cleanup to free transmit SKBs,
sockets with small transmit buffer sizes might spend most of their
time blocked waiting for the cleanup.
Normally we do a cleanup for each transmitted packet. We add a
watchdog type timer so that we also schedule a timeout for 150uS after
a packet is transmitted. The watchdog is reset for each transmitted
packet, so for high packet rates, it never expires. At these high
rates, the cleanups are done for each packet so the extra watchdog
initiated cleanups are not needed.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
---
drivers/staging/octeon/Kconfig | 1 -
drivers/staging/octeon/ethernet-defines.h | 5 +-
drivers/staging/octeon/ethernet-tx.c | 137 +++++++++++++++++++++++------
drivers/staging/octeon/ethernet-tx.h | 6 +-
drivers/staging/octeon/ethernet.c | 47 +++++-----
drivers/staging/octeon/octeon-ethernet.h | 9 +--
6 files changed, 142 insertions(+), 63 deletions(-)
diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig
index 579b8f1..638ad6b 100644
--- a/drivers/staging/octeon/Kconfig
+++ b/drivers/staging/octeon/Kconfig
@@ -3,7 +3,6 @@ config OCTEON_ETHERNET
depends on CPU_CAVIUM_OCTEON
select PHYLIB
select MDIO_OCTEON
- select HIGH_RES_TIMERS
help
This driver supports the builtin ethernet ports on Cavium
Networks' products in the Octeon family. This driver supports the
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index 00a8561..6a2cd50 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -95,10 +95,11 @@
/*#define DONT_WRITEBACK(x) 0 */
/* Maximum number of SKBs to try to free per xmit packet. */
-#define MAX_SKB_TO_FREE 10
#define MAX_OUT_QUEUE_DEPTH 1000
-#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(uint32_t))
+
#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 62258bd..5175247 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -48,6 +48,7 @@
#include "cvmx-wqe.h"
#include "cvmx-fau.h"
+#include "cvmx-pip.h"
#include "cvmx-pko.h"
#include "cvmx-helper.h"
@@ -66,6 +67,11 @@
#define GET_SKBUFF_QOS(skb) 0
#endif
+static void cvm_oct_tx_do_cleanup(unsigned long arg);
+static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0);
+
+/* Maximum number of SKBs to try to free per xmit packet. */
+#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2)
static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
{
@@ -77,10 +83,24 @@ static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
return skb_to_free;
}
-void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv)
+static void cvm_oct_kick_tx_poll_watchdog(void)
+{
+ union cvmx_ciu_timx ciu_timx;
+ ciu_timx.u64 = 0;
+ ciu_timx.s.one_shot = 1;
+ ciu_timx.s.len = cvm_oct_tx_poll_interval;
+ cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64);
+}
+
+void cvm_oct_free_tx_skbs(struct net_device *dev)
{
int32_t skb_to_free;
int qos, queues_per_port;
+ int total_freed = 0;
+ int total_remaining = 0;
+ unsigned long flags;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
queues_per_port = cvmx_pko_get_num_queues(priv->port);
/* Drain any pending packets in the free list */
for (qos = 0; qos < queues_per_port; qos++) {
@@ -89,24 +109,31 @@ void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv)
skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE);
skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
- while (skb_to_free > 0) {
- dev_kfree_skb_any(skb_dequeue(&priv->tx_free_list[qos]));
- skb_to_free--;
+
+ total_freed += skb_to_free;
+ if (skb_to_free > 0) {
+ struct sk_buff *to_free_list = NULL;
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_to_free > 0) {
+ struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
+ }
}
+ total_remaining += skb_queue_len(&priv->tx_free_list[qos]);
}
-}
-
-enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer)
-{
- struct octeon_ethernet *priv = container_of(timer, struct octeon_ethernet, tx_restart_timer);
- struct net_device *dev = cvm_oct_device[priv->port];
-
- cvm_oct_free_tx_skbs(priv);
-
- if (netif_queue_stopped(dev))
+ if (total_freed >= 0 && netif_queue_stopped(dev))
netif_wake_queue(dev);
-
- return HRTIMER_NORESTART;
+ if (total_remaining)
+ cvm_oct_kick_tx_poll_watchdog();
}
/**
@@ -129,6 +156,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
struct sk_buff *to_free_list;
int32_t skb_to_free;
int32_t buffers_to_free;
+ u32 total_to_clean;
unsigned long flags;
#if REUSE_SKBUFFS_WITHOUT_FREE
unsigned char *fpa_head;
@@ -232,7 +260,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
pko_command.s.subone0 = 1;
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
/* Build the PKO buffer pointer */
hw_buffer.u64 = 0;
@@ -327,7 +354,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
* We can use this buffer in the FPA. We don't need the FAU
* update anymore
*/
- pko_command.s.reg0 = 0;
pko_command.s.dontfree = 0;
hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7);
@@ -384,15 +410,17 @@ dont_put_skbuff_in_hw:
* If we're sending faster than the receive can free them then
* don't do the HW free.
*/
- if ((buffers_to_free < -100) && !pko_command.s.dontfree) {
+ if ((buffers_to_free < -100) && !pko_command.s.dontfree)
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
- }
- if (pko_command.s.dontfree)
+ if (pko_command.s.dontfree) {
queue_type = QUEUE_CORE;
- else
+ pko_command.s.reg0 = priv->fau+qos*4;
+ } else {
queue_type = QUEUE_HW;
+ }
+ if (USE_ASYNC_IOBDMA)
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
@@ -402,10 +430,7 @@ dont_put_skbuff_in_hw:
/* Drop the lock when notifying the core. */
spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
netif_stop_queue(dev);
- hrtimer_start(&priv->tx_restart_timer,
- priv->tx_restart_interval, HRTIMER_MODE_REL);
spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
-
} else {
/* If not using normal queueing. */
queue_type = QUEUE_DROP;
@@ -460,11 +485,27 @@ skip_xmit:
}
if (USE_ASYNC_IOBDMA) {
+ CVMX_SYNCIOBDMA;
+ total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
/* Restore the scratch area */
cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
+ } else {
+ total_to_clean = cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1);
}
+ if (total_to_clean & 0x3ff) {
+ /*
+ * Schedule the cleanup tasklet every 1024 packets for
+ * the pathological case of high traffic on one port
+ * delaying clean up of packets on a different port
+ * that is blocked waiting for the cleanup.
+ */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ }
+
+ cvm_oct_kick_tx_poll_watchdog();
+
return NETDEV_TX_OK;
}
@@ -624,7 +665,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
*
* @dev: Device being shutdown
*/
-void cvm_oct_tx_shutdown(struct net_device *dev)
+void cvm_oct_tx_shutdown_dev(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
unsigned long flags;
@@ -638,3 +679,45 @@ void cvm_oct_tx_shutdown(struct net_device *dev)
spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
}
}
+
+static void cvm_oct_tx_do_cleanup(unsigned long arg)
+{
+ int port;
+
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ struct net_device *dev = cvm_oct_device[port];
+ cvm_oct_free_tx_skbs(dev);
+ }
+ }
+}
+
+static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id)
+{
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Do the work in the tasklet. */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ return IRQ_HANDLED;
+}
+
+void cvm_oct_tx_initialize(void)
+{
+ int i;
+
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Register an IRQ hander for to receive CIU_TIMX(1) interrupts */
+ i = request_irq(OCTEON_IRQ_TIMER1,
+ cvm_oct_tx_cleanup_watchdog, 0,
+ "Ethernet", cvm_oct_device);
+
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1);
+}
+
+void cvm_oct_tx_shutdown(void)
+{
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
+}
diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h
index b628d8c..547680c 100644
--- a/drivers/staging/octeon/ethernet-tx.h
+++ b/drivers/staging/octeon/ethernet-tx.h
@@ -29,6 +29,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
int do_free, int qos);
-void cvm_oct_tx_shutdown(struct net_device *dev);
-void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv);
-enum hrtimer_restart cvm_oct_restart_tx(struct hrtimer *timer);
+void cvm_oct_tx_initialize(void);
+void cvm_oct_tx_shutdown(void);
+void cvm_oct_tx_shutdown_dev(struct net_device *dev);
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 1771c10..f2ec8a6 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -140,6 +140,8 @@ atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
+u64 cvm_oct_tx_poll_interval;
+
static void cvm_oct_rx_refill_worker(struct work_struct *work);
static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
@@ -159,18 +161,19 @@ static void cvm_oct_rx_refill_worker(struct work_struct *work)
&cvm_oct_rx_refill_work, HZ);
}
-static void cvm_oct_tx_clean_worker(struct work_struct *work)
+static void cvm_oct_preiodic_worker(struct work_struct *work)
{
struct octeon_ethernet *priv = container_of(work,
struct octeon_ethernet,
- tx_clean_work.work);
+ port_periodic_work.work);
if (priv->poll)
priv->poll(cvm_oct_device[priv->port]);
- cvm_oct_free_tx_skbs(priv);
+
cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]);
+
if (!atomic_read(&cvm_oct_poll_queue_stopping))
- queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ);
+ queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
}
/**
@@ -662,6 +665,9 @@ static int __init cvm_oct_init_module(void)
*/
cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ /* Initialize the FAU used for counting tx SKBs that need to be freed */
+ cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0);
+
if ((pow_send_group != -1)) {
struct net_device *dev;
pr_info("\tConfiguring device for POW only access\n");
@@ -670,18 +676,6 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
struct octeon_ethernet *priv = netdev_priv(dev);
- hrtimer_init(&priv->tx_restart_timer,
- CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- priv->tx_restart_timer.function = cvm_oct_restart_tx;
-
- /*
- * Default for 10GE 5000nS enough time to
- * transmit about 100 64byte packtes. 1GE
- * interfaces will get 50000nS below.
- */
- priv->tx_restart_interval = ktime_set(0, 5000);
-
dev->netdev_ops = &cvm_oct_pow_netdev_ops;
priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
priv->port = CVMX_PIP_NUM_INPUT_PORTS;
@@ -725,9 +719,8 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
priv = netdev_priv(dev);
- INIT_DELAYED_WORK(&priv->tx_clean_work,
- cvm_oct_tx_clean_worker);
-
+ INIT_DELAYED_WORK(&priv->port_periodic_work,
+ cvm_oct_preiodic_worker);
priv->imode = imode;
priv->port = port;
priv->queue = cvmx_pko_get_base_queue(priv->port);
@@ -763,7 +756,6 @@ static int __init cvm_oct_init_module(void)
case CVMX_HELPER_INTERFACE_MODE_SGMII:
dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
- priv->tx_restart_interval = ktime_set(0, 50000);
strcpy(dev->name, "eth%d");
break;
@@ -775,7 +767,6 @@ static int __init cvm_oct_init_module(void)
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
- priv->tx_restart_interval = ktime_set(0, 50000);
strcpy(dev->name, "eth%d");
break;
}
@@ -793,13 +784,19 @@ static int __init cvm_oct_init_module(void)
cvmx_pko_get_num_queues(priv->port) *
sizeof(uint32_t);
queue_delayed_work(cvm_oct_poll_queue,
- &priv->tx_clean_work, HZ);
+ &priv->port_periodic_work, HZ);
}
}
}
+ cvm_oct_tx_initialize();
cvm_oct_rx_initialize();
+ /*
+ * 150 uS: about 10 1500-byte packtes at 1GE.
+ */
+ cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
+
queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
return 0;
@@ -826,6 +823,8 @@ static void __exit cvm_oct_cleanup_module(void)
cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
cvm_oct_rx_shutdown();
+ cvm_oct_tx_shutdown();
+
cvmx_pko_disable();
/* Free the ethernet devices */
@@ -833,9 +832,9 @@ static void __exit cvm_oct_cleanup_module(void)
if (cvm_oct_device[port]) {
struct net_device *dev = cvm_oct_device[port];
struct octeon_ethernet *priv = netdev_priv(dev);
- cancel_delayed_work_sync(&priv->tx_clean_work);
+ cancel_delayed_work_sync(&priv->port_periodic_work);
- cvm_oct_tx_shutdown(dev);
+ cvm_oct_tx_shutdown_dev(dev);
unregister_netdev(dev);
kfree(dev);
cvm_oct_device[port] = NULL;
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index 8d09210..db2a3cc 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -31,8 +31,6 @@
#ifndef OCTEON_ETHERNET_H
#define OCTEON_ETHERNET_H
-#include <linux/hrtimer.h>
-
/**
* This is the definition of the Ethernet driver's private
* driver state stored in netdev_priv(dev).
@@ -59,9 +57,7 @@ struct octeon_ethernet {
uint64_t link_info;
/* Called periodically to check link status */
void (*poll) (struct net_device *dev);
- struct hrtimer tx_restart_timer;
- ktime_t tx_restart_interval;
- struct delayed_work tx_clean_work;
+ struct delayed_work port_periodic_work;
struct work_struct port_work; /* may be unused. */
};
@@ -101,6 +97,7 @@ extern char pow_send_list[];
extern struct net_device *cvm_oct_device[];
extern struct workqueue_struct *cvm_oct_poll_queue;
extern atomic_t cvm_oct_poll_queue_stopping;
+extern u64 cvm_oct_tx_poll_interval;
extern int max_rx_cpus;
extern int rx_napi_weight;
--
1.6.6
^ permalink raw reply related
* [PATCH 1/4] Staging: octeon: remove unneeded includes
From: David Daney @ 2010-02-15 20:13 UTC (permalink / raw)
To: ralf, linux-mips, netdev, gregkh; +Cc: David Daney
In-Reply-To: <4B79AAA6.60005@caviumnetworks.com>
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
---
drivers/staging/octeon/ethernet-mdio.h | 1 -
drivers/staging/octeon/ethernet-rgmii.c | 1 -
drivers/staging/octeon/ethernet-sgmii.c | 1 -
drivers/staging/octeon/ethernet-spi.c | 1 -
drivers/staging/octeon/ethernet-xaui.c | 1 -
drivers/staging/octeon/ethernet.c | 1 -
6 files changed, 0 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/octeon/ethernet-mdio.h b/drivers/staging/octeon/ethernet-mdio.h
index 55d0614..a417d4f 100644
--- a/drivers/staging/octeon/ethernet-mdio.h
+++ b/drivers/staging/octeon/ethernet-mdio.h
@@ -32,7 +32,6 @@
#include <linux/ip.h>
#include <linux/string.h>
#include <linux/ethtool.h>
-#include <linux/mii.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <net/dst.h>
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
index 3820f1e..f90d46e 100644
--- a/drivers/staging/octeon/ethernet-rgmii.c
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet-sgmii.c b/drivers/staging/octeon/ethernet-sgmii.c
index 6061d01..2d8589e 100644
--- a/drivers/staging/octeon/ethernet-sgmii.c
+++ b/drivers/staging/octeon/ethernet-sgmii.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c
index 00dc0f4..b58b897 100644
--- a/drivers/staging/octeon/ethernet-spi.c
+++ b/drivers/staging/octeon/ethernet-spi.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet-xaui.c b/drivers/staging/octeon/ethernet-xaui.c
index ee3dc41..3fca1cc 100644
--- a/drivers/staging/octeon/ethernet-xaui.c
+++ b/drivers/staging/octeon/ethernet-xaui.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 9d63202..5afece0 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -29,7 +29,6 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/delay.h>
#include <linux/phy.h>
#include <net/dst.h>
--
1.6.6
^ permalink raw reply related
* [PATCH 2/4] Staging: Octeon: Run phy bus accesses on a workqueue.
From: David Daney @ 2010-02-15 20:13 UTC (permalink / raw)
To: ralf, linux-mips, netdev, gregkh; +Cc: David Daney
In-Reply-To: <4B79AAA6.60005@caviumnetworks.com>
When directly accessing a phy, we must acquire the mdio bus lock. To
do that we cannot be in interrupt context, so we need to move these
operations to a workqueue.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
---
drivers/staging/octeon/ethernet-rgmii.c | 55 +++++++++++----
drivers/staging/octeon/ethernet.c | 113 +++++++++++++++++-------------
drivers/staging/octeon/octeon-ethernet.h | 4 +
3 files changed, 109 insertions(+), 63 deletions(-)
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
index f90d46e..a0d4d4b 100644
--- a/drivers/staging/octeon/ethernet-rgmii.c
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -26,6 +26,7 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <linux/phy.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
@@ -47,14 +48,20 @@ static int number_rgmii_ports;
static void cvm_oct_rgmii_poll(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
- unsigned long flags;
+ unsigned long flags = 0;
cvmx_helper_link_info_t link_info;
+ int use_global_register_lock = (priv->phydev == NULL);
- /*
- * Take the global register lock since we are going to touch
- * registers that affect more than one port.
- */
- spin_lock_irqsave(&global_register_lock, flags);
+ BUG_ON(in_interrupt());
+ if (use_global_register_lock) {
+ /*
+ * Take the global register lock since we are going to
+ * touch registers that affect more than one port.
+ */
+ spin_lock_irqsave(&global_register_lock, flags);
+ } else {
+ mutex_lock(&priv->phydev->bus->mdio_lock);
+ }
link_info = cvmx_helper_link_get(priv->port);
if (link_info.u64 == priv->link_info) {
@@ -114,7 +121,11 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
dev->name);
}
}
- spin_unlock_irqrestore(&global_register_lock, flags);
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
return;
}
@@ -150,7 +161,12 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
link_info = cvmx_helper_link_autoconf(priv->port);
priv->link_info = link_info.u64;
}
- spin_unlock_irqrestore(&global_register_lock, flags);
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else {
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
+ }
if (priv->phydev == NULL) {
/* Tell core. */
@@ -212,8 +228,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
struct net_device *dev =
cvm_oct_device[cvmx_helper_get_ipd_port
(interface, index)];
- if (dev)
- cvm_oct_rgmii_poll(dev);
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue, &priv->port_work);
+
gmx_rx_int_reg.u64 = 0;
gmx_rx_int_reg.s.phy_dupx = 1;
gmx_rx_int_reg.s.phy_link = 1;
@@ -251,8 +270,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
struct net_device *dev =
cvm_oct_device[cvmx_helper_get_ipd_port
(interface, index)];
- if (dev)
- cvm_oct_rgmii_poll(dev);
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue, &priv->port_work);
+
gmx_rx_int_reg.u64 = 0;
gmx_rx_int_reg.s.phy_dupx = 1;
gmx_rx_int_reg.s.phy_link = 1;
@@ -301,6 +323,12 @@ int cvm_oct_rgmii_stop(struct net_device *dev)
return 0;
}
+static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work);
+ cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
+}
+
int cvm_oct_rgmii_init(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
@@ -308,7 +336,7 @@ int cvm_oct_rgmii_init(struct net_device *dev)
cvm_oct_common_init(dev);
dev->netdev_ops->ndo_stop(dev);
-
+ INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
/*
* Due to GMX errata in CN3XXX series chips, it is necessary
* to take the link down immediately when the PHY changes
@@ -396,4 +424,5 @@ void cvm_oct_rgmii_uninit(struct net_device *dev)
number_rgmii_ports--;
if (number_rgmii_ports == 0)
free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
+ cancel_work_sync(&priv->port_work);
}
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 5afece0..1771c10 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -123,9 +123,16 @@ MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
static unsigned int cvm_oct_mac_addr_offset;
/**
- * Periodic timer to check auto negotiation
+ * cvm_oct_poll_queue - Workqueue for polling operations.
*/
-static struct timer_list cvm_oct_poll_timer;
+struct workqueue_struct *cvm_oct_poll_queue;
+
+/**
+ * cvm_oct_poll_queue_stopping - flag to indicate polling should stop.
+ *
+ * Set to one right before cvm_oct_poll_queue is destroyed.
+ */
+atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
/**
* Array of every ethernet device owned by this driver indexed by
@@ -133,47 +140,39 @@ static struct timer_list cvm_oct_poll_timer;
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
-/**
- * Periodic timer tick for slow management operations
- *
- * @arg: Device to check
- */
-static void cvm_do_timer(unsigned long arg)
+static void cvm_oct_rx_refill_worker(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work)
{
- static int port;
- if (port < CVMX_PIP_NUM_INPUT_PORTS) {
- if (cvm_oct_device[port]) {
- struct octeon_ethernet *priv = netdev_priv(cvm_oct_device[port]);
- if (priv->poll)
- priv->poll(cvm_oct_device[port]);
- cvm_oct_free_tx_skbs(priv);
- cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]);
- }
- port++;
- /*
- * Poll the next port in a 50th of a second. This
- * spreads the polling of ports out a little bit.
- */
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ/50);
- } else {
- port = 0;
- /*
- * FPA 0 may have been drained, try to refill it if we
- * need more than num_packet_buffers / 2, otherwise
- * normal receive processing will refill it. If it
- * were drained, no packets could be received so
- * cvm_oct_napi_poll would never be invoked to do the
- * refill.
- */
- cvm_oct_rx_refill_pool(num_packet_buffers / 2);
- /*
- * All ports have been polled. Start the next iteration through
- * the ports in one second.
- */
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
- }
+ /*
+ * FPA 0 may have been drained, try to refill it if we need
+ * more than num_packet_buffers / 2, otherwise normal receive
+ * processing will refill it. If it were drained, no packets
+ * could be received so cvm_oct_napi_poll would never be
+ * invoked to do the refill.
+ */
+ cvm_oct_rx_refill_pool(num_packet_buffers / 2);
+
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue,
+ &cvm_oct_rx_refill_work, HZ);
}
+static void cvm_oct_tx_clean_worker(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work,
+ struct octeon_ethernet,
+ tx_clean_work.work);
+
+ if (priv->poll)
+ priv->poll(cvm_oct_device[priv->port]);
+ cvm_oct_free_tx_skbs(priv);
+ cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]);
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ);
+ }
+
/**
* Configure common hardware for all interfaces
*/
@@ -624,6 +623,12 @@ static int __init cvm_oct_init_module(void)
else
cvm_oct_mac_addr_offset = 0;
+ cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
+ if (cvm_oct_poll_queue == NULL) {
+ pr_err("octeon-ethernet: Cannot create workqueue");
+ return -ENOMEM;
+ }
+
cvm_oct_proc_initialize();
cvm_oct_configure_common_hw();
@@ -719,7 +724,9 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
priv = netdev_priv(dev);
- memset(priv, 0, sizeof(struct octeon_ethernet));
+
+ INIT_DELAYED_WORK(&priv->tx_clean_work,
+ cvm_oct_tx_clean_worker);
priv->imode = imode;
priv->port = port;
@@ -785,17 +792,15 @@ static int __init cvm_oct_init_module(void)
fau -=
cvmx_pko_get_num_queues(priv->port) *
sizeof(uint32_t);
+ queue_delayed_work(cvm_oct_poll_queue,
+ &priv->tx_clean_work, HZ);
}
}
}
cvm_oct_rx_initialize();
- /* Enable the poll timer for checking RGMII status */
- init_timer(&cvm_oct_poll_timer);
- cvm_oct_poll_timer.data = 0;
- cvm_oct_poll_timer.function = cvm_do_timer;
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
+ queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
return 0;
}
@@ -817,20 +822,28 @@ static void __exit cvm_oct_cleanup_module(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_device);
- del_timer(&cvm_oct_poll_timer);
+ atomic_inc_return(&cvm_oct_poll_queue_stopping);
+ cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
+
cvm_oct_rx_shutdown();
cvmx_pko_disable();
/* Free the ethernet devices */
for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
if (cvm_oct_device[port]) {
- cvm_oct_tx_shutdown(cvm_oct_device[port]);
- unregister_netdev(cvm_oct_device[port]);
- kfree(cvm_oct_device[port]);
+ struct net_device *dev = cvm_oct_device[port];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cancel_delayed_work_sync(&priv->tx_clean_work);
+
+ cvm_oct_tx_shutdown(dev);
+ unregister_netdev(dev);
+ kfree(dev);
cvm_oct_device[port] = NULL;
}
}
+ destroy_workqueue(cvm_oct_poll_queue);
+
cvmx_pko_shutdown();
cvm_oct_proc_shutdown();
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index 40b6956..8d09210 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -61,6 +61,8 @@ struct octeon_ethernet {
void (*poll) (struct net_device *dev);
struct hrtimer tx_restart_timer;
ktime_t tx_restart_interval;
+ struct delayed_work tx_clean_work;
+ struct work_struct port_work; /* may be unused. */
};
/**
@@ -97,6 +99,8 @@ extern int pow_send_group;
extern int pow_receive_group;
extern char pow_send_list[];
extern struct net_device *cvm_oct_device[];
+extern struct workqueue_struct *cvm_oct_poll_queue;
+extern atomic_t cvm_oct_poll_queue_stopping;
extern int max_rx_cpus;
extern int rx_napi_weight;
--
1.6.6
^ permalink raw reply related
* [PATCH 0/4] Improvements to octeon_ethernet.
From: David Daney @ 2010-02-15 20:12 UTC (permalink / raw)
To: ralf.baechle, linux-mips, Netdev, Greg Kroah-Hartman
Here are a couple of improvements to the octeon_ethernet in
drivers/staging/octeon. The first patch is just cleanup, the rest are
genuine bug fixes.
I will reply with the four patches.
We may want to merge via Ralf's linux-mips.org tree as Octeon is
infact a MIPS based SOC and he has a bunch of other patches queued
there that these depend on.
David Daney (4):
Staging: octeon: remove unneeded includes
Staging: Octeon: Run phy bus accesses on a workqueue.
MIPS: Octeon: Do proper acknowledgment of CIU timer interrupts.
Staging: Octeon: Free transmit SKBs in a timely manner.
arch/mips/cavium-octeon/octeon-irq.c | 67 +++++++++++++-
drivers/staging/octeon/Kconfig | 1 -
drivers/staging/octeon/ethernet-defines.h | 5 +-
drivers/staging/octeon/ethernet-mdio.h | 1 -
drivers/staging/octeon/ethernet-rgmii.c | 56 +++++++++---
drivers/staging/octeon/ethernet-sgmii.c | 1 -
drivers/staging/octeon/ethernet-spi.c | 1 -
drivers/staging/octeon/ethernet-tx.c | 137
++++++++++++++++++++++------
drivers/staging/octeon/ethernet-tx.h | 6 +-
drivers/staging/octeon/ethernet-xaui.c | 1 -
drivers/staging/octeon/ethernet.c | 141
++++++++++++++++-------------
drivers/staging/octeon/octeon-ethernet.h | 11 ++-
12 files changed, 303 insertions(+), 125 deletions(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox