* Re: BNX2: Kernel crashes with 2.6.31 and 2.6.31.9
From: Bruno Prémont @ 2010-03-02 7:10 UTC (permalink / raw)
To: Benjamin Li; +Cc: NetDEV, Michael Chan, Linux-Kernel
In-Reply-To: <1267493170.2762.45.camel@dhcp-10-12-137-104.broadcom.com>
Hi Benjamin,
On Mon, 1 Mar 2010 17:26:10 "Benjamin Li" <benli@broadcom.com> wrote:
> Thanks for trying the patch. I still haven't been able to reproduce
> what you are seeing here. I am able to run scp and 'echo t
> > /proc/sysrq-trigger' multiple times. I was wondering if you had any
> success reproducing the problem with a stack trace?
Last week I couldn't trigger it again after booting with console=ttyS1,
but today I finally got the box to crash (same procedure but matter of
"luck" as to when it happens)
System continues printing the bnx2 debug messages but does not accept any
input (not even on serial console). Every few minutes it complains about
collectd being blocked for more than 120 seconds (collectd wants to send
UDP packets with system state)
The running kernel is 2.6.33-rc8 (though I did not forward-port your
patch in this thread, that is bnx2_dump_ftq() is missing - half of it
having been applied to 2.6.33). Looking at the trace I got I will add
that part and try again.
Regards,
Bruno
[575872.163771] ------------[ cut here ]------------
[575872.172467] WARNING: at /usr/src/linux-2.6.33-rc8-git7/kernel/softirq.c:143 local_bh_enable_ip+0x72/0xa0()
[575872.172890] Hardware name: ProLiant DL360 G5
[575872.172890] Modules linked in: qla2xxx netbomb ipmi_devintf loop dm_mod sg sr_mod cdrom ata_piix ahci uhci_hcd ipmi_si ipmi_msghandler hpwdt bnx2 ehci_hcd libata [last unloaded: qla2xxx]
[575872.172890] Pid: 32564, comm: cat Not tainted 2.6.33-rc8-git7-x86_64 #1
[575872.172890] Call Trace:
[575872.172890] [<ffffffff8103f002>] ? local_bh_enable_ip+0x72/0xa0
[575872.172890] [<ffffffff81039368>] warn_slowpath_common+0x78/0xd0
[575872.172890] [<ffffffff810393cf>] warn_slowpath_null+0xf/0x20
[575872.172890] [<ffffffff8103f002>] local_bh_enable_ip+0x72/0xa0
[575872.172890] [<ffffffff814002af>] _raw_spin_unlock_bh+0xf/0x20
[575872.172890] [<ffffffffa0026ed4>] bnx2_reg_rd_ind+0x44/0x60 [bnx2]
[575872.172890] [<ffffffffa0026eff>] bnx2_shmem_rd+0xf/0x20 [bnx2]
[575872.172890] [<ffffffffa0030ff4>] bnx2_poll+0x194/0x228 [bnx2]
[575872.172890] [<ffffffff8135c081>] netpoll_poll+0xe1/0x3c0
[575872.172890] [<ffffffff8135c518>] netpoll_send_skb+0x118/0x210
[575872.172890] [<ffffffff8135c80b>] netpoll_send_udp+0x1fb/0x210
[575872.172890] [<ffffffffa00501c5>] write_msg+0x95/0xd0 [netbomb]
[575872.172890] [<ffffffffa0050255>] netbomb_write+0x55/0xa4 [netbomb]
[575872.172890] [<ffffffff810f6571>] proc_reg_write+0x71/0xb0
[575872.172890] [<ffffffff810ab6db>] vfs_write+0xcb/0x180
[575872.172890] [<ffffffff810ab880>] sys_write+0x50/0x90
[575872.172890] [<ffffffff8102a1a4>] sysenter_dispatch+0x7/0x2b
[575872.172890] ---[ end trace d4f601a1b34bd327 ]---
[575878.950008] ------------[ cut here ]------------
[575878.955297] WARNING: at /usr/src/linux-2.6.33-rc8-git7/net/sched/sch_generic.c:255 dev_watchdog+0x25e/0x270()
[575878.966541] Hardware name: ProLiant DL360 G5
[575878.972352] NETDEV WATCHDOG: eth0 (bnx2): transmit queue 0 timed out
[575878.976435] Modules linked in: qla2xxx netbomb ipmi_devintf loop dm_mod sg sr_mod cdrom ata_piix ahci uhci_hcd ipmi_si ipmi_msghandler hpwdt bnx2 ehci_hcd libata [last unloaded: qla2xxx]
[575878.985325] Pid: 0, comm: swapper Tainted: G W 2.6.33-rc8-git7-x86_64 #1
[575878.988929] Call Trace:
[575878.990124] <IRQ> [<ffffffff8135f84e>] ? dev_watchdog+0x25e/0x270
[575878.994317] [<ffffffff81039368>] warn_slowpath_common+0x78/0xd0
[575878.998118] [<ffffffff81039444>] warn_slowpath_fmt+0x64/0x70
[575879.010352] [<ffffffff81362206>] ? nlmsg_notify+0x46/0xc0
[575879.012956] [<ffffffff8119f339>] ? strlcpy+0x49/0x60
[575879.015487] [<ffffffff81349b33>] ? netdev_drivername+0x43/0x50
[575879.027713] [<ffffffff8135f84e>] dev_watchdog+0x25e/0x270
[575879.030339] [<ffffffff810354fc>] ? scheduler_tick+0x12c/0x290
[575879.034202] [<ffffffff8135f5f0>] ? dev_watchdog+0x0/0x270
[575879.047296] [<ffffffff810440fc>] run_timer_softirq+0x13c/0x210
[575879.051101] [<ffffffff8105b4b7>] ? clockevents_program_event+0x57/0xa0
[575879.054422] [<ffffffff8103edb6>] __do_softirq+0xa6/0x130
[575879.065396] [<ffffffff81003bcc>] call_softirq+0x1c/0x30
[575879.070825] [<ffffffff81005be5>] do_softirq+0x55/0x90
[575879.073266] [<ffffffff8103eb35>] irq_exit+0x75/0x90
[575879.075618] [<ffffffff8101aeed>] smp_apic_timer_interrupt+0x6d/0xa0
[575879.094357] [<ffffffff81003693>] apic_timer_interrupt+0x13/0x20
[575879.099195] <EOI> [<ffffffff8100b186>] ? mwait_idle+0x66/0x80
[575879.102225] [<ffffffff81001f90>] ? enter_idle+0x20/0x30
[575879.119572] [<ffffffff81002003>] cpu_idle+0x63/0xb0
[575879.121925] [<ffffffff818ab5de>] start_secondary+0x158/0x1aa
[575879.125708] ---[ end trace d4f601a1b34bd328 ]---
[575879.130792] bnx2: eth0 DEBUG: intr_sem[0]
[575879.132880] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575879.149587] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575879.152959] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575883.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575883.961580] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575883.974574] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575883.985269] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575888.951879] bnx2: eth0 DEBUG: intr_sem[0]
[575888.955092] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575888.967583] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575888.973761] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575893.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575893.953954] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575893.964160] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575893.968596] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575898.951879] bnx2: eth0 DEBUG: intr_sem[0]
[575898.957913] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575898.962225] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575898.972121] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575903.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575903.956728] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575903.961380] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575903.972171] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575908.951880] bnx2: eth0 DEBUG: intr_sem[0]
[575908.956790] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575908.961398] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575908.974628] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575913.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575913.958417] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575913.962159] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575913.965457] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575918.951879] bnx2: eth0 DEBUG: intr_sem[0]
[575918.958634] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575918.965095] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575918.974149] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575923.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575923.964361] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575923.970549] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575923.975969] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575928.951879] bnx2: eth0 DEBUG: intr_sem[0]
[575928.961375] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575928.965103] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575928.969552] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575933.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575933.963067] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575933.970543] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575933.993032] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575938.951879] bnx2: eth0 DEBUG: intr_sem[0]
[575938.958134] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575938.961695] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575938.968855] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575943.951883] bnx2: eth0 DEBUG: intr_sem[0]
[575943.959389] bnx2: eth0 DEBUG: EMAC_TX_STATUS[00000008] RPM_MGMT_PKT_CTRL[00000000]
[575943.964102] bnx2: eth0 DEBUG: MCP_STATE_P0[00000000] MCP_STATE_P1[00000000]
[575943.967612] bnx2: eth0 DEBUG: HC_STATS_INTERRUPT_STATUS[00000000]
[575948.951879] bnx2: eth0 DEBUG: intr_sem[0]
....
Blocked collectd task:
[576003.053144] INFO: task collectd:3019 blocked for more than 120 seconds.
[576003.066206] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[576003.072659] collectd D 0000000000000000 0 3019 1 0x00020000
[576003.072663] ffff8802aa3afa78 0000000000200086 0000000000000000 0000000000000008
[576003.072666] 0000000000012780 000000000000dda8 ffff8802aa3affd8 ffff8802aa256fa0
[576003.072670] ffff8802af86e9c0 ffff8802aa257208 00000005ab1a3000 00000001036e43c3
[576003.072673] Call Trace:
[576003.072682] [<ffffffff813ff33e>] __mutex_lock_slowpath+0x12e/0x180
[576003.072687] [<ffffffff813fecfe>] mutex_lock+0x1e/0x40
[576003.072690] [<ffffffff81357fd8>] rtnetlink_rcv+0x18/0x40
[576003.072694] [<ffffffff813621b5>] netlink_unicast+0x285/0x290
[576003.072698] [<ffffffff81344f83>] ? memcpy_fromiovec+0x63/0x80
[576003.072701] [<ffffffff81362c63>] netlink_sendmsg+0x1d3/0x2c0
[576003.072705] [<ffffffff810bdd9e>] ? __d_instantiate+0x5e/0xd0
[576003.072708] [<ffffffff8133981b>] sock_sendmsg+0xbb/0xf0
[576003.072712] [<ffffffff810c72aa>] ? seq_open+0x4a/0xa0
[576003.072714] [<ffffffff81338ed9>] ? copy_from_user+0x9/0x10
[576003.072717] [<ffffffff81338f0c>] ? move_addr_to_kernel+0x2c/0x40
[576003.072720] [<ffffffff8135e09c>] ? verify_compat_iovec+0x9c/0x110
[576003.072723] [<ffffffff8133b734>] sys_sendmsg+0x184/0x320
[576003.072728] [<ffffffff810f5d60>] ? proc_delete_inode+0x0/0x50
[576003.072731] [<ffffffff810700e0>] ? call_rcu_sched+0x10/0x20
[576003.072733] [<ffffffff810700f9>] ? call_rcu+0x9/0x10
[576003.072736] [<ffffffff810bca9a>] ? d_free+0x3a/0x60
[576003.072741] [<ffffffff810acae8>] ? __fput+0x178/0x1f0
[576003.072744] [<ffffffff81058014>] ? getnstimeofday+0x64/0xf0
[576003.072747] [<ffffffff8135d94c>] compat_sys_socketcall+0xcc/0x210
[576003.072751] [<ffffffff8102a1a4>] sysenter_dispatch+0x7/0x2b
^ permalink raw reply
* Re: linux-next: Tree for March 1 (bluetooth/hci_sysfs)
From: Marcel Holtmann @ 2010-03-02 7:06 UTC (permalink / raw)
To: David Miller
Cc: sfr-3FnU+UHB4dNDw9hX6IcOSA, randy.dunlap-QHcLZuEGTsvQT0dZR+AlfA,
linux-next-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20100301.181415.266447389.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
Hi Dave,
> >> static ssize_t inquiry_cache_read(struct file *file, char __user *userbuf,
> >> size_t count, loff_t *ppos)
> >> {
> >> struct hci_dev *hdev = file->private_data;
> >> struct inquiry_cache *cache = &hdev->inq_cache;
> >> struct inquiry_entry *e;
> >> char buf[4096]; // <<<<<<<<<<<<<<<<<<<<<<<<<<< huh? don't do that on stack.
> >> int n = 0;
> >
> > Dave Miller is following up on that.
>
> This looks like a job for.... SEQ FILE! :-)
>
> I'm testing the following fix.
the patch looks good, but it doesn't do the job. I have to look into why
that is. Will try to come up with a proper patch soon.
Regards
Marcel
^ permalink raw reply
* Re: [PATCH] be2net: fix tx completion polling
From: Sathya Perla @ 2010-03-02 6:36 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20100226.041926.36027143.davem@davemloft.net>
On 26/02/10 04:19 -0800, David Miller wrote:
> From: Sathya Perla <sathyap@serverengines.com>
> Date: Thu, 25 Feb 2010 17:25:16 +0530
>
> > In tx/mcc polling, napi_complete() is being incorrectly called
> > before reaping tx completions. This can cause tx compl processing
> > to be scheduled on another cpu concurrently which can result in a panic.
> > This if fixed by calling napi complete() after tx/mcc compl processing
> > but before re-enabling interrupts (via a cq notify).
> >
> > Pls apply to net-2.6.
>
> net-2.6 is closed, 2.6.33 has been released.
>
> > Signed-off-by: Sathya Perla <sathyap@serverengines.com>
>
> This patch does not apply to net-next-2.6
I've re-done the patch for net-next-2.6: Pls apply; thanks!
In tx/mcc polling, napi_complete() is being incorrectly called
before reaping tx completions. This can cause tx compl processing
to be scheduled on another cpu concurrently which can result in a panic.
This if fixed by calling napi complete() after tx/mcc compl processing
but before re-enabling interrupts (via a cq notify).
Signed-off-by: Sathya Perla <sathyap@serverengines.com>
---
drivers/net/benet/be_cmds.c | 26 +++++++++++-----------
drivers/net/benet/be_cmds.h | 2 +-
drivers/net/benet/be_main.c | 47 +++++++++++++++++++++----------------------
3 files changed, 37 insertions(+), 38 deletions(-)
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c
index c8a2bac..4b1f805 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/benet/be_cmds.c
@@ -119,10 +119,10 @@ void be_async_mcc_disable(struct be_adapter *adapter)
adapter->mcc_obj.rearm_cq = false;
}
-int be_process_mcc(struct be_adapter *adapter)
+int be_process_mcc(struct be_adapter *adapter, int *status)
{
struct be_mcc_compl *compl;
- int num = 0, status = 0;
+ int num = 0;
struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
spin_lock_bh(&adapter->mcc_cq_lock);
@@ -135,31 +135,31 @@ int be_process_mcc(struct be_adapter *adapter)
be_async_link_state_process(adapter,
(struct be_async_event_link_state *) compl);
} else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
- status = be_mcc_compl_process(adapter, compl);
+ *status = be_mcc_compl_process(adapter, compl);
atomic_dec(&mcc_obj->q.used);
}
be_mcc_compl_use(compl);
num++;
}
- if (num)
- be_cq_notify(adapter, mcc_obj->cq.id, mcc_obj->rearm_cq, num);
-
spin_unlock_bh(&adapter->mcc_cq_lock);
- return status;
+ return num;
}
/* Wait till no more pending mcc requests are present */
static int be_mcc_wait_compl(struct be_adapter *adapter)
{
#define mcc_timeout 120000 /* 12s timeout */
- int i, status;
+ int i, num, status = 0;
+ struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
+
for (i = 0; i < mcc_timeout; i++) {
- status = be_process_mcc(adapter);
- if (status)
- return status;
+ num = be_process_mcc(adapter, &status);
+ if (num)
+ be_cq_notify(adapter, mcc_obj->cq.id,
+ mcc_obj->rearm_cq, num);
- if (atomic_read(&adapter->mcc_obj.q.used) == 0)
+ if (atomic_read(&mcc_obj->q.used) == 0)
break;
udelay(100);
}
@@ -167,7 +167,7 @@ static int be_mcc_wait_compl(struct be_adapter *adapter)
dev_err(&adapter->pdev->dev, "mccq poll timed out\n");
return -1;
}
- return 0;
+ return status;
}
/* Notify MCC requests and wait for completion */
diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h
index 728b0d7..cce61f9 100644
--- a/drivers/net/benet/be_cmds.h
+++ b/drivers/net/benet/be_cmds.h
@@ -920,7 +920,7 @@ extern int be_cmd_get_flow_control(struct be_adapter *adapter,
extern int be_cmd_query_fw_cfg(struct be_adapter *adapter,
u32 *port_num, u32 *cap);
extern int be_cmd_reset_function(struct be_adapter *adapter);
-extern int be_process_mcc(struct be_adapter *adapter);
+extern int be_process_mcc(struct be_adapter *adapter, int *status);
extern int be_cmd_set_beacon_state(struct be_adapter *adapter,
u8 port_num, u8 beacon, u8 status, u8 state);
extern int be_cmd_get_beacon_state(struct be_adapter *adapter,
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c
index 27ccdd8..a703ed8 100644
--- a/drivers/net/benet/be_main.c
+++ b/drivers/net/benet/be_main.c
@@ -583,7 +583,7 @@ static void be_set_multicast_list(struct net_device *netdev)
}
be_cmd_multicast_set(adapter, adapter->if_handle, netdev,
- &adapter->mc_cmd_mem);
+ &adapter->mc_cmd_mem);
done:
return;
}
@@ -1469,23 +1469,38 @@ int be_poll_rx(struct napi_struct *napi, int budget)
return work_done;
}
-void be_process_tx(struct be_adapter *adapter)
+/* As TX and MCC share the same EQ check for both TX and MCC completions.
+ * For TX/MCC we don't honour budget; consume everything
+ */
+static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
{
+ struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi);
+ struct be_adapter *adapter =
+ container_of(tx_eq, struct be_adapter, tx_eq);
struct be_queue_info *txq = &adapter->tx_obj.q;
struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
struct be_eth_tx_compl *txcp;
- u32 num_cmpl = 0;
+ int tx_compl = 0, mcc_compl, status = 0;
u16 end_idx;
while ((txcp = be_tx_compl_get(tx_cq))) {
end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl,
- wrb_index, txcp);
+ wrb_index, txcp);
be_tx_compl_process(adapter, end_idx);
- num_cmpl++;
+ tx_compl++;
}
- if (num_cmpl) {
- be_cq_notify(adapter, tx_cq->id, true, num_cmpl);
+ mcc_compl = be_process_mcc(adapter, &status);
+
+ napi_complete(napi);
+
+ if (mcc_compl) {
+ struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
+ be_cq_notify(adapter, mcc_obj->cq.id, true, mcc_compl);
+ }
+
+ if (tx_compl) {
+ be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl);
/* As Tx wrbs have been freed up, wake up netdev queue if
* it was stopped due to lack of tx wrbs.
@@ -1496,24 +1511,8 @@ void be_process_tx(struct be_adapter *adapter)
}
drvr_stats(adapter)->be_tx_events++;
- drvr_stats(adapter)->be_tx_compl += num_cmpl;
+ drvr_stats(adapter)->be_tx_compl += tx_compl;
}
-}
-
-/* As TX and MCC share the same EQ check for both TX and MCC completions.
- * For TX/MCC we don't honour budget; consume everything
- */
-static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
-{
- struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi);
- struct be_adapter *adapter =
- container_of(tx_eq, struct be_adapter, tx_eq);
-
- napi_complete(napi);
-
- be_process_tx(adapter);
-
- be_process_mcc(adapter);
return 1;
}
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH] bridge: per-cpu packet statistics
From: Eric Dumazet @ 2010-03-02 6:01 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: David Miller, netdev, bridge
In-Reply-To: <20100301161658.5a61143b@nehalam>
Le lundi 01 mars 2010 à 16:16 -0800, Stephen Hemminger a écrit :
> The shared packet statistics are a potential source of slow down
> on bridged traffic. Convert to per-cpu array, but only keep those
> statistics which change per-packet.
>
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
>
> ---
>
> net/bridge/br_device.c | 43 ++++++++++++++++++++++++++++++++++++++-----
> net/bridge/br_if.c | 6 ++++++
> net/bridge/br_input.c | 5 +++--
> net/bridge/br_private.h | 8 ++++++++
> 4 files changed, 55 insertions(+), 7 deletions(-)
>
> --- a/net/bridge/br_device.c 2010-03-01 08:22:23.476657998 -0800
> +++ b/net/bridge/br_device.c 2010-03-01 15:31:36.737227465 -0800
> @@ -26,11 +26,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *
> const unsigned char *dest = skb->data;
> struct net_bridge_fdb_entry *dst;
> struct net_bridge_mdb_entry *mdst;
> + struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
>
> - BR_INPUT_SKB_CB(skb)->brdev = dev;
> + brstats->tx_packets++;
> + brstats->tx_bytes += skb->len;
On TX path, this is not really necessary, since we already dirtied
txq->lock before calling br_dev_xmit(), we can use txq->tx_packets and
txq->tx_bytes for free ?
>
> - dev->stats.tx_packets++;
> - dev->stats.tx_bytes += skb->len;
> + BR_INPUT_SKB_CB(skb)->brdev = dev;
>
> skb_reset_mac_header(skb);
> skb_pull(skb, ETH_HLEN);
> @@ -81,6 +82,28 @@ static int br_dev_stop(struct net_device
> return 0;
> }
>
^ permalink raw reply
* Re: [RFC] ppp: Bridge Control Protocol
From: Stephen Hemminger @ 2010-03-02 4:42 UTC (permalink / raw)
To: Paul Mackerras
Cc: David, "Miller <davem", linux-ppp, bridge, netdev,
dane
In-Reply-To: <20100302033944.GL26848@brick.ozlabs.ibm.com>
On Tue, 2 Mar 2010 14:39:44 +1100
Paul Mackerras <paulus@samba.org> wrote:
> On Mon, Mar 01, 2010 at 02:59:15PM -0800, Stephen Hemminger wrote:
>
> > This is a port of an old patch to provide BCP support.
> > BCP allows bridging PPP tunnels, it useful to do bridging between
> > virtualized environments. Original patch was done by
> > Dan Eble but was for 2.4.21.
> >
> > Note: still needs testing. Does anyone have hardware to make
> > sure this interoperates with other implementations?
>
> Is there a corresponding pppd patch to handle the control protocol?
>
> Paul.
Old starting point
https://lists.linux-foundation.org/pipermail/bridge/2005-May/004523.html
^ permalink raw reply
* Re: [RFC] ppp: Bridge Control Protocol
From: Stephen Hemminger @ 2010-03-02 4:40 UTC (permalink / raw)
To: Paul Mackerras
Cc: David, "Miller <davem", linux-ppp, bridge, netdev,
dane
In-Reply-To: <20100302033944.GL26848@brick.ozlabs.ibm.com>
On Tue, 2 Mar 2010 14:39:44 +1100
Paul Mackerras <paulus@samba.org> wrote:
> On Mon, Mar 01, 2010 at 02:59:15PM -0800, Stephen Hemminger wrote:
>
> > This is a port of an old patch to provide BCP support.
> > BCP allows bridging PPP tunnels, it useful to do bridging between
> > virtualized environments. Original patch was done by
> > Dan Eble but was for 2.4.21.
> >
> > Note: still needs testing. Does anyone have hardware to make
> > sure this interoperates with other implementations?
>
> Is there a corresponding pppd patch to handle the control protocol?
>
> Paul.
Yes, I am working on that
^ permalink raw reply
* Re: Was: Re: [RFC PATCH] fix problems with NETIF_F_HIGHDMA in networking, Now: SWIOTLB dynamic allocation
From: FUJITA Tomonori @ 2010-03-02 4:40 UTC (permalink / raw)
To: hancockrwd
Cc: konrad.wilk, davem, fujita.tomonori, bzolnier, linux-kernel,
netdev, linux-usb
In-Reply-To: <51f3faa71003011312n3e7f8151x1a5e4b8bb57bb7c0@mail.gmail.com>
On Mon, 1 Mar 2010 15:12:41 -0600
Robert Hancock <hancockrwd@gmail.com> wrote:
> On Mon, Mar 1, 2010 at 10:34 AM, Konrad Rzeszutek Wilk
> <konrad.wilk@oracle.com> wrote:
> > On Sun, Feb 28, 2010 at 12:16:28AM -0800, David Miller wrote:
> >> From: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
> >> Date: Sun, 28 Feb 2010 03:38:19 +0900
> >>
> >> > When I proposed such approach (always use swiotlb) before, IIRC,
> >> > the objections were:
> >> >
> >> > - better to make allocation respect dma_mask. (I don't think that this
> >> > approach is possible since we don't know which device handles data
> >> > later when we allocate memory).
> >>
> >> And such objects might end up being processed by multiple devices with
> >> different DMA restrictions.
> >>
> >> > - swiotlb is not good for small systems since it allocates too much
> >> > memory (we can fix this though).
> >>
> >> Indeed.
> >
> > What would be a good mechanism for this? Enumerating all of the PCI
> > devices to find out which ones are 32-bit and then allocate some chunk
> > of memory based on the amount of them? say, 1MB per card?
> >
> > Or maybe a simpler one - figure out how many pages we have an allocate
> > based on some sliding rule (say, 8MB for under 512MB, 16MB between 512MB
> > and 2GB, and 32MB for 2GB to 4GB, and after that the full 64MB?)
>
> Why do we need to allocate SWIOTLB if your highest memory address is
> under 4GB? You can just disable it in that case, like x86_64 does.
Unfortunately, we need to do because the goal is removing the block
layer bounce (and other bounce mechanisms in sub systems).
For example, even if highest memory address is under 4GB, you must
provide bounce buffer for ISA devices (and some devices for strange
DMA restrictions such as under 1GB).
^ permalink raw reply
* Re: Was: Re: [RFC PATCH] fix problems with NETIF_F_HIGHDMA in networking, Now: SWIOTLB dynamic allocation
From: FUJITA Tomonori @ 2010-03-02 4:40 UTC (permalink / raw)
To: konrad.wilk
Cc: davem, fujita.tomonori, hancockrwd, bzolnier, linux-kernel,
netdev, linux-usb
In-Reply-To: <20100301163437.GE7881@phenom.dumpdata.com>
On Mon, 1 Mar 2010 11:34:37 -0500
Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote:
> On Sun, Feb 28, 2010 at 12:16:28AM -0800, David Miller wrote:
> > From: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
> > Date: Sun, 28 Feb 2010 03:38:19 +0900
> >
> > > When I proposed such approach (always use swiotlb) before, IIRC,
> > > the objections were:
> > >
> > > - better to make allocation respect dma_mask. (I don't think that this
> > > approach is possible since we don't know which device handles data
> > > later when we allocate memory).
> >
> > And such objects might end up being processed by multiple devices with
> > different DMA restrictions.
> >
> > > - swiotlb is not good for small systems since it allocates too much
> > > memory (we can fix this though).
> >
> > Indeed.
>
> What would be a good mechanism for this? Enumerating all of the PCI
> devices to find out which ones are 32-bit and then allocate some chunk
> of memory based on the amount of them? say, 1MB per card?
>
> Or maybe a simpler one - figure out how many pages we have an allocate
> based on some sliding rule (say, 8MB for under 512MB, 16MB between 512MB
> and 2GB, and 32MB for 2GB to 4GB, and after that the full 64MB?)
Hmm, have you read the above objection from embedded system people ? :)
We can't pre-allocate such lots of memory (several MB). And we don't
need to.
We have to pre-allocate some for the block layer (we have to guarantee
that we can handle one request at least), however we don't need to
pre-allocate for the rest (including the network stack).
This guarantee is a bit difficult since the dma_map_* doesn't know who
is at the upper layer; the dma_map_* cannot know if it needs to
allocate from the pre-allocated pool or not. I thought about adding
'dma attribute flag' to struct device (to be exact, struct
device_dma_parameters).
^ permalink raw reply
* Re: [PATCH] net: TCP_MSS_DEFAULT, TCP_MSS_DESIRED
From: Simon Horman @ 2010-03-02 4:16 UTC (permalink / raw)
To: William Allen Simpson; +Cc: Linux Kernel Network Developers
In-Reply-To: <4AF9C436.8060403@gmail.com>
On Tue, Nov 10, 2009 at 02:51:18PM -0500, William Allen Simpson wrote:
> Define two symbols needed in both kernel and user space.
>
> Remove old (somewhat incorrect) kernel variant that wasn't used in
> most cases. Default should apply to both RMSS and SMSS (RFC2581).
The name was somewhat incorrect in the sense that it was named
differently in the RFCs?
> Replace numeric constants with defined symbols.
That bit sounds very reasonable.
> Stand-alone patch, originally developed for TCPCT.
>
> Signed-off-by: William.Allen.Simpson@gmail.com
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
> include/linux/tcp.h | 6 ++++++
> include/net/tcp.h | 3 ---
> net/ipv4/tcp_input.c | 4 ++--
> net/ipv4/tcp_ipv4.c | 6 +++---
> net/ipv4/tcp_minisocks.c | 2 +-
> net/ipv6/tcp_ipv6.c | 2 +-
> 6 files changed, 13 insertions(+), 10 deletions(-)
> diff --git a/include/linux/tcp.h b/include/linux/tcp.h
> index eeecb85..32d7d77 100644
> --- a/include/linux/tcp.h
> +++ b/include/linux/tcp.h
> @@ -81,6 +81,12 @@ enum {
> TCP_DATA_OFFSET = __cpu_to_be32(0xF0000000)
> };
>
> +/*
> + * TCP general constants
> + */
> +#define TCP_MSS_DEFAULT 536U /* IPv4 (RFC1122, RFC2581) */
> +#define TCP_MSS_DESIRED 1220U /* IPv6 (tunneled), EDNS0 (RFC3226) */
Are you planing to use TCP_MSS_DESIRED anywhere?
> +
> /* TCP socket options */
> #define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */
> #define TCP_MAXSEG 2 /* Limit MSS */
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index 25bf3ba..a413e9f 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -62,9 +62,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
> /* Minimal accepted MSS. It is (60+60+8) - (20+20). */
> #define TCP_MIN_MSS 88U
>
> -/* Minimal RCV_MSS. */
> -#define TCP_MIN_RCVMSS 536U
> -
> /* The least MTU to use for probing */
> #define TCP_BASE_MSS 512
>
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index be0c5bf..cc306ac 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -140,7 +140,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb)
> * "len" is invariant segment length, including TCP header.
> */
> len += skb->data - skb_transport_header(skb);
> - if (len >= TCP_MIN_RCVMSS + sizeof(struct tcphdr) ||
> + if (len >= TCP_MSS_DEFAULT + sizeof(struct tcphdr) ||
> /* If PSH is not set, packet should be
> * full sized, provided peer TCP is not badly broken.
> * This observation (if it is correct 8)) allows
> @@ -411,7 +411,7 @@ void tcp_initialize_rcv_mss(struct sock *sk)
> unsigned int hint = min_t(unsigned int, tp->advmss, tp->mss_cache);
>
> hint = min(hint, tp->rcv_wnd / 2);
> - hint = min(hint, TCP_MIN_RCVMSS);
> + hint = min(hint, TCP_MSS_DEFAULT);
> hint = max(hint, TCP_MIN_MSS);
>
> inet_csk(sk)->icsk_ack.rcv_mss = hint;
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index f83ac91..0718fde 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -217,7 +217,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
> if (inet->opt)
> inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
>
> - tp->rx_opt.mss_clamp = 536;
> + tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;
>
> /* Socket identity is still unknown (sport may be zero).
> * However we set state to SYN-SENT and not releasing socket
> @@ -1270,7 +1270,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
> goto drop_and_free;
>
> tcp_clear_options(&tmp_opt);
> - tmp_opt.mss_clamp = 536;
> + tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
> tmp_opt.user_mss = tcp_sk(sk)->rx_opt.user_mss;
>
> tcp_parse_options(skb, &tmp_opt, 0, dst);
> @@ -1818,7 +1818,7 @@ static int tcp_v4_init_sock(struct sock *sk)
> */
> tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
> tp->snd_cwnd_clamp = ~0;
> - tp->mss_cache = 536;
> + tp->mss_cache = TCP_MSS_DEFAULT;
>
> tp->reordering = sysctl_tcp_reordering;
> icsk->icsk_ca_ops = &tcp_init_congestion_ops;
> diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
> index fb68bab..7a42990 100644
> --- a/net/ipv4/tcp_minisocks.c
> +++ b/net/ipv4/tcp_minisocks.c
> @@ -476,7 +476,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
> if (newtp->af_specific->md5_lookup(sk, newsk))
> newtp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
> #endif
> - if (skb->len >= TCP_MIN_RCVMSS+newtp->tcp_header_len)
> + if (skb->len >= TCP_MSS_DEFAULT + newtp->tcp_header_len)
> newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
> newtp->rx_opt.mss_clamp = req->mss;
> TCP_ECN_openreq_child(newtp, req);
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index 6951827..b528f75 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1849,7 +1849,7 @@ static int tcp_v6_init_sock(struct sock *sk)
> */
> tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
> tp->snd_cwnd_clamp = ~0;
> - tp->mss_cache = 536;
> + tp->mss_cache = TCP_MSS_DEFAULT;
>
> tp->reordering = sysctl_tcp_reordering;
>
> --
> 1.6.3.3
>
^ permalink raw reply
* Re: [RFC] ppp: Bridge Control Protocol
From: Paul Mackerras @ 2010-03-02 3:39 UTC (permalink / raw)
To: Stephen Hemminger
Cc: David, "Miller <davem", linux-ppp, bridge, netdev,
dane
In-Reply-To: <20100301145915.2ecaac89@nehalam>
On Mon, Mar 01, 2010 at 02:59:15PM -0800, Stephen Hemminger wrote:
> This is a port of an old patch to provide BCP support.
> BCP allows bridging PPP tunnels, it useful to do bridging between
> virtualized environments. Original patch was done by
> Dan Eble but was for 2.4.21.
>
> Note: still needs testing. Does anyone have hardware to make
> sure this interoperates with other implementations?
Is there a corresponding pppd patch to handle the control protocol?
Paul.
^ permalink raw reply
* Re: NETIF_F_FRAGLIST and NETIF_F_SG difference
From: David Miller @ 2010-03-02 3:01 UTC (permalink / raw)
To: bhutchings; +Cc: mekaviraj, netdev
In-Reply-To: <1267498318.2819.21.camel@localhost>
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Tue, 02 Mar 2010 02:51:58 +0000
> On Mon, 2010-03-01 at 17:40 -0800, David Miller wrote:
>> From: Ben Hutchings <bhutchings@solarflare.com>
>> Date: Mon, 01 Mar 2010 12:58:41 +0000
>>
>> > (I don't know why there are two ways of adding extra data. The latter
>> > does not seem to be used often.)
>>
>> It's the most efficient way to handle IPv4/IPv6 fragmentation and
>> reassembly.
>
> But fragmentation results in a series of packets to be transmitted
> separately (not gathered) and reassembly is only done at endpoints. So
> when would we see a fragment list on the transmit path?
If the device indicates it can take the set of fragments as a bundle,
with only one IP header at the front, instead of individual fully
header'd IP frames.
This was the original intention of what this net device feature flag
was to be used for.
The device does all of the IP header creation for the individual
frames, bumping the fragment ID and flags, etc. Just like it does
for TSO.
Some broadcom chips can do this, if I recall correctly.
But that never materialized. Instead it now simply means that
the frag list based linkage is supported, and the packet is an
entire fully formed frame.
GSO/GRO makes use of the current interpretation so that the it's
bundles can be passed around efficiently from RX to TX. Mostly the
feature bit is set by layered and virtualization devices.
^ permalink raw reply
* Re: NETIF_F_FRAGLIST and NETIF_F_SG difference
From: Ben Hutchings @ 2010-03-02 2:51 UTC (permalink / raw)
To: David Miller; +Cc: mekaviraj, netdev
In-Reply-To: <20100301.174024.214216263.davem@davemloft.net>
On Mon, 2010-03-01 at 17:40 -0800, David Miller wrote:
> From: Ben Hutchings <bhutchings@solarflare.com>
> Date: Mon, 01 Mar 2010 12:58:41 +0000
>
> > (I don't know why there are two ways of adding extra data. The latter
> > does not seem to be used often.)
>
> It's the most efficient way to handle IPv4/IPv6 fragmentation and
> reassembly.
But fragmentation results in a series of packets to be transmitted
separately (not gathered) and reassembly is only done at endpoints. So
when would we see a fragment list on the transmit path?
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: sparse checking prevented by "net: fix kmemcheck annotations"
From: David Miller @ 2010-03-02 2:39 UTC (permalink / raw)
To: stefanr; +Cc: eric.dumazet, linux-kernel, netdev
In-Reply-To: <4B8BE8BE.9070505@s5r6.in-berlin.de>
From: Stefan Richter <stefanr@s5r6.in-berlin.de>
Date: Mon, 01 Mar 2010 17:18:06 +0100
> Eric Dumazet wrote:
>> [PATCH] net: fix protocol sk_buff field
>>
>> Commit e992cd9b72a18 (kmemcheck: make bitfield annotations truly no-ops
>> when disabled) allows us to revert a workaround we did in the past to
>> not add holes in sk_buff structure.
>>
>> This patch partially reverts commit 14d18a81b5171
>> (net: fix kmemcheck annotations) so that sparse doesnt complain:
>>
>> include/linux/skbuff.h:357:41: error: invalid bitfield specifier for
>> type restricted __be16.
>
> Great, very appreciated.
Applied, thanks everyone.
^ permalink raw reply
* Re: [PATCH V2] net: add accounting for socket backlog
From: Zhu Yi @ 2010-03-02 2:34 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev@vger.kernel.org, David Miller
In-Reply-To: <1267443828.3039.7.camel@edumazet-laptop>
On Mon, 2010-03-01 at 19:43 +0800, Eric Dumazet wrote:
> Thinking again about this, doing this zero initialization at the very
> end of __release_sock() solves the problem of potential infinite loop
> in
> __release_sock(). Since producer will hit the backlog limit.
>
> Thanks
>
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 305cba4..544cf4a 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1542,6 +1542,11 @@ static void __release_sock(struct sock *sk)
>
> bh_lock_sock(sk);
> } while ((skb = sk->sk_backlog.head) != NULL);
> + /*
> + * Doing this zeroing at the end of this function guarantee we
> can not
> + * loop forever while a wild producer attempts to flood us
> + */
> + sk->sk_backlog.len = 0;
> }
Very good point. I'll add your sob in my next patch.
Thanks,
-yi
^ permalink raw reply
* Re: linux-next: Tree for March 1 (bluetooth/hci_sysfs)
From: David Miller @ 2010-03-02 2:14 UTC (permalink / raw)
To: sfr-3FnU+UHB4dNDw9hX6IcOSA
Cc: randy.dunlap-QHcLZuEGTsvQT0dZR+AlfA,
linux-next-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
marcel-kz+m5ild9QBg9hUCZPvPmw,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20100302121526.36a1ea88.sfr-3FnU+UHB4dNDw9hX6IcOSA@public.gmane.org>
From: Stephen Rothwell <sfr-3FnU+UHB4dNDw9hX6IcOSA@public.gmane.org>
Date: Tue, 2 Mar 2010 12:15:26 +1100
> Hi Randy,
>
> On Mon, 01 Mar 2010 13:08:21 -0800 Randy Dunlap <randy.dunlap-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> wrote:
>>
>> static ssize_t inquiry_cache_read(struct file *file, char __user *userbuf,
>> size_t count, loff_t *ppos)
>> {
>> struct hci_dev *hdev = file->private_data;
>> struct inquiry_cache *cache = &hdev->inq_cache;
>> struct inquiry_entry *e;
>> char buf[4096]; // <<<<<<<<<<<<<<<<<<<<<<<<<<< huh? don't do that on stack.
>> int n = 0;
>
> Dave Miller is following up on that.
This looks like a job for.... SEQ FILE! :-)
I'm testing the following fix.
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 1a79a6c..f02c2ff 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -3,6 +3,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -405,44 +406,85 @@ static struct device_type bt_host = {
.release = bt_host_release,
};
-static int inquiry_cache_open(struct inode *inode, struct file *file)
+static int inquiry_cache_show(struct seq_file *m, void *v)
{
- file->private_data = inode->i_private;
+ struct inquiry_entry *e = v;
+ struct inquiry_data *data;
+ bdaddr_t bdaddr;
+
+ data = &e->data;
+ baswap(&bdaddr, &data->bdaddr);
+
+ seq_printf(m, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+ batostr(&bdaddr),
+ data->pscan_rep_mode, data->pscan_period_mode,
+ data->pscan_mode, data->dev_class[2],
+ data->dev_class[1], data->dev_class[0],
+ __le16_to_cpu(data->clock_offset),
+ data->rssi, data->ssp_mode, e->timestamp);
+
return 0;
}
-static ssize_t inquiry_cache_read(struct file *file, char __user *userbuf,
- size_t count, loff_t *ppos)
+static void *inquiry_cache_get_idx(struct hci_dev *hdev, loff_t pos)
{
- struct hci_dev *hdev = file->private_data;
struct inquiry_cache *cache = &hdev->inq_cache;
- struct inquiry_entry *e;
- char buf[4096];
- int n = 0;
+ struct inquiry_entry *e = cache->list;
+
+ while (e && pos) {
+ e = e->next;
+ pos--;
+ }
+ return e;
+}
+
+static void *inquiry_cache_start(struct seq_file *m, loff_t *pos)
+{
+ struct hci_dev *hdev = m->private;
hci_dev_lock_bh(hdev);
- for (e = cache->list; e; e = e->next) {
- struct inquiry_data *data = &e->data;
- bdaddr_t bdaddr;
- baswap(&bdaddr, &data->bdaddr);
- n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
- batostr(&bdaddr),
- data->pscan_rep_mode, data->pscan_period_mode,
- data->pscan_mode, data->dev_class[2],
- data->dev_class[1], data->dev_class[0],
- __le16_to_cpu(data->clock_offset),
- data->rssi, data->ssp_mode, e->timestamp);
- }
+ return inquiry_cache_get_idx(hdev, *pos);
+}
+
+static void *inquiry_cache_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct inquiry_entry *e = v;
+
+ ++*pos;
+ return e->next;
+}
+
+static void inquiry_cache_stop(struct seq_file *m, void *v)
+{
+ struct hci_dev *hdev = m->private;
hci_dev_unlock_bh(hdev);
+}
- return simple_read_from_buffer(userbuf, count, ppos, buf, n);
+static const struct seq_operations inquiry_cache_ops = {
+ .start = inquiry_cache_start,
+ .next = inquiry_cache_next,
+ .stop = inquiry_cache_stop,
+ .show = inquiry_cache_show,
+};
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+ int rc = seq_open(file, &inquiry_cache_ops);
+
+ if (rc >= 0) {
+ struct seq_file *m = file->private_data;
+ m->private = inode->i_private;
+ }
+ return rc;
}
static const struct file_operations inquiry_cache_fops = {
- .open = inquiry_cache_open,
- .read = inquiry_cache_read,
+ .open = inquiry_cache_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
};
int hci_register_sysfs(struct hci_dev *hdev)
^ permalink raw reply related
* Re: [PATCH net-next-2.6] bridge: Fix build error when IGMP_SNOOPING is not enabled
From: David Miller @ 2010-03-02 2:14 UTC (permalink / raw)
To: herbert; +Cc: sri, netdev
In-Reply-To: <20100302004626.GA28274@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Tue, 2 Mar 2010 08:46:26 +0800
> On Mon, Mar 01, 2010 at 11:53:04AM -0800, Sridhar Samudrala wrote:
>> Fix the following build error when IGMP_SNOOPING is not enabled.
>> In file included from net/bridge/br.c:24:
>> net/bridge/br_private.h: In function 'br_multicast_is_router':
>> net/bridge/br_private.h:361: error: 'struct net_bridge' has no member named 'multicast_router'
>> net/bridge/br_private.h:362: error: 'struct net_bridge' has no member named 'multicast_router'
>> net/bridge/br_private.h:363: error: 'struct net_bridge' has no member named 'multicast_router_timer'
>>
>> Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
>
> Thanks for the fix.
>
> Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
>
> I'd originally left the struct data outside ifdefs and then added
> the ifdefs without testing.
Thanks everyone, I'll apply this.
^ permalink raw reply
* Re: NETIF_F_FRAGLIST and NETIF_F_SG difference
From: David Miller @ 2010-03-02 1:40 UTC (permalink / raw)
To: bhutchings; +Cc: mekaviraj, netdev
In-Reply-To: <1267448321.2819.15.camel@localhost>
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Mon, 01 Mar 2010 12:58:41 +0000
> (I don't know why there are two ways of adding extra data. The latter
> does not seem to be used often.)
It's the most efficient way to handle IPv4/IPv6 fragmentation and
reassembly.
^ permalink raw reply
* Re: BNX2: Kernel crashes with 2.6.31 and 2.6.31.9
From: Benjamin Li @ 2010-03-02 1:26 UTC (permalink / raw)
To: Bruno Prémont; +Cc: NetDEV, Michael Chan, Linux-Kernel
In-Reply-To: <20100223131508.4c6cb866@neptune.home>
Hi Bruno,
On Tue, 2010-02-23 at 04:15 -0800, Bruno Prémont wrote:
> Hi Benjamin,
>
> On Fri, 19 February 2010 "Benjamin Li" <benli@broadcom.com> wrote:
> > >From your logs it looks like the device came up using MSI, but in the
> > MSI-X poll routine was being called:
> >
> > [ 9.836673] bnx2: eth0: using MSI
> > ...
> >
> > [ 134.643459] [<ffffffffa004019e>] bnx2_poll_msix+0x3e/0xd0 [bnx2]
> > [ 134.643465] [<ffffffff8135bcd1>] netpoll_poll+0xe1/0x3c0
> >
> > which is incorrect. If we are in MSI mode, the bnx2_poll() routine
> > should be used.
> >
> > I think what is going on here is that during the bnx2x driver
> > initialization the current bnx2 driver adds all possible NAPI
> > structures that map to all the hardware vectors (BNX2_MAX_MSIX_VEC=9)
> > to the NAPI list in the net_device structure regardless if they are
> > used or not (Seen in drivers/net/bnx2.c:bnx2_init_napi()). This can
> > cause uninitialized NAPI structures to be placed on the napi_list.
> > Because this device is in MSI mode, only 1 vector is initialized.
> > Now, the problem is triggered when net/core/netpoll.c:poll_napi() is
> > called. This is because this routine will run through the entire
> > napi_list calling all the poll routines. In your particular case, it
> > is calling the poll routine on an uninitialized vector causing the
> > kernel panic.
> >
> > Please try the patch below to see if it solves your problem. Note,
> > this only have been compile tested and tested against basic traffic
> > runs. Unfortunately, I could not reproduce the kernel panic with the
> > instructions below to verify the patch.
> >
> > Thanks again for all your help in helping us track this down.
>
> I applied the patch today and tried to reproduce with my showcases.
>
> Seems that it's harder to trigger now but I still end up being able to
> crash the box. Don't know if it's the same cause or not (could also
> be the tcp-retransmit ghost)...
>
> This time I had to run a few paralell scp's (8Mb/s each) to the box and
> 'echo t > /proc/sysrq-trigger' multiple times via ssh session for it to
> happen. It didn't trigger with by netbomb though I will try some more
> and see)
>
> I don't know if it's the same reason or not (hopefully something
> reached disk as serial console is dead and pings are not
> answered anymore.
> It's probably some printk/bug/warn that triggers in network stack and
> deadlocks with netconsole.
Thanks for trying the patch. I still haven't been able to reproduce
what you are seeing here. I am able to run scp and 'echo t
> /proc/sysrq-trigger' multiple times. I was wondering if you had any
success reproducing the problem with a stack trace?
Thanks again.
-Ben
>
> Regards,
> Bruno
>
^ permalink raw reply
* Re: linux-next: Tree for March 1 (bluetooth/hci_sysfs)
From: Stephen Rothwell @ 2010-03-02 1:15 UTC (permalink / raw)
To: Randy Dunlap; +Cc: linux-next, LKML, Marcel Holtmann, linux-bluetooth, Netdev
In-Reply-To: <4B8C2CC5.8000603@oracle.com>
[-- Attachment #1: Type: text/plain, Size: 588 bytes --]
Hi Randy,
On Mon, 01 Mar 2010 13:08:21 -0800 Randy Dunlap <randy.dunlap@oracle.com> wrote:
>
> static ssize_t inquiry_cache_read(struct file *file, char __user *userbuf,
> size_t count, loff_t *ppos)
> {
> struct hci_dev *hdev = file->private_data;
> struct inquiry_cache *cache = &hdev->inq_cache;
> struct inquiry_entry *e;
> char buf[4096]; // <<<<<<<<<<<<<<<<<<<<<<<<<<< huh? don't do that on stack.
> int n = 0;
Dave Miller is following up on that.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* RE: [net-next-2.6 PATCH 1/3] ixgbe: Fix DMA mapping/unmapping issues when HWRSC is enabled on IOMMU enabled kernels
From: Chilakala, Mallikarjuna @ 2010-03-02 1:09 UTC (permalink / raw)
To: Simon Horman, Kirsher, Jeffrey T
Cc: davem@davemloft.net, netdev@vger.kernel.org, gospo@redhat.com
In-Reply-To: <20100302002912.GB19929@verge.net.au>
>-----Original Message-----
>From: Simon Horman [mailto:horms@verge.net.au]
>Sent: Monday, March 01, 2010 4:29 PM
>To: Kirsher, Jeffrey T
>Cc: davem@davemloft.net; netdev@vger.kernel.org; gospo@redhat.com;
>Chilakala, Mallikarjuna
>Subject: Re: [net-next-2.6 PATCH 1/3] ixgbe: Fix DMA mapping/unmapping
>issues when HWRSC is enabled on IOMMU enabled kernels
>
>On Fri, Feb 26, 2010 at 01:14:37AM -0800, Jeff Kirsher wrote:
>> From: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>
>>
>> Work around 82599 HW issue when HWRSC is enabled on IOMMU enabled
>> kernels. 82599 HW is updating the header information after setting the
>> descriptor to done, resulting DMA mapping/unmapping issues on IOMMU
>> enabled systems. To work around the issue delay unmapping of first packet
>> that carries the header information until end of packet is reached.
>>
>> Signed-off-by: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>
>> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
>> ---
>>
>> drivers/net/ixgbe/ixgbe_main.c | 32 +++++++++++++++++++++++++++++---
>> 1 files changed, 29 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/ixgbe/ixgbe_main.c
>b/drivers/net/ixgbe/ixgbe_main.c
>> index 3308790..4a01022 100644
>> --- a/drivers/net/ixgbe/ixgbe_main.c
>> +++ b/drivers/net/ixgbe/ixgbe_main.c
>> @@ -818,6 +818,12 @@ static inline struct sk_buff
>*ixgbe_transform_rsc_queue(struct sk_buff *skb,
>> return skb;
>> }
>>
>> +struct ixgbe_rsc_cb {
>> + dma_addr_t dma;
>> +};
>> +
>> +#define IXGBE_RSC_CB(skb) ((struct ixgbe_rsc_cb *)(skb)->cb)
>> +
>> static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
>> struct ixgbe_ring *rx_ring,
>> int *work_done, int work_to_do)
>> @@ -867,9 +873,21 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector
>*q_vector,
>> rx_buffer_info->skb = NULL;
>>
>> if (rx_buffer_info->dma) {
>> - pci_unmap_single(pdev, rx_buffer_info->dma,
>> - rx_ring->rx_buf_len,
>> - PCI_DMA_FROMDEVICE);
>> + if ((adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
>> + (!(staterr & IXGBE_RXD_STAT_EOP)) &&
>> + (!(skb->prev)))
>> + /*
>> + * When HWRSC is enabled, delay unmapping
>> + * of the first packet. It carries the
>> + * header information, HW may still
>> + * access the header after the writeback.
>> + * Only unmap it when EOP is reached
>> + */
>> + IXGBE_RSC_CB(skb)->dma = rx_buffer_info->dma;
>> + else
>> + pci_unmap_single(pdev, rx_buffer_info->dma,
>> + rx_ring->rx_buf_len,
>> + PCI_DMA_FROMDEVICE);
>> rx_buffer_info->dma = 0;
>> skb_put(skb, len);
>> }
>> @@ -917,6 +935,10 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector
>*q_vector,
>> if (skb->prev)
>> skb = ixgbe_transform_rsc_queue(skb, &(rx_ring-
>>rsc_count));
>> if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
>> + if (IXGBE_RSC_CB(skb)->dma)
>> + pci_unmap_single(pdev, IXGBE_RSC_CB(skb)->dma,
>> + rx_ring->rx_buf_len,
>> + PCI_DMA_FROMDEVICE);
>
>Does IXGBE_RSC_CB(skb)->dma need to be set to NULL here
>to avoid a double-free in ixgbe_clean_rx_ring() ?
It shouldn't happen, but it is good to set it to NULL.
I'll send out a patch to fix this issue.
Thanks for your review Horman.
>
>> if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)
>> rx_ring->rsc_count += skb_shinfo(skb)-
>>nr_frags;
>> else
>> @@ -3104,6 +3126,10 @@ static void ixgbe_clean_rx_ring(struct
>ixgbe_adapter *adapter,
>> rx_buffer_info->skb = NULL;
>> do {
>> struct sk_buff *this = skb;
>> + if (IXGBE_RSC_CB(this)->dma)
>> + pci_unmap_single(pdev, IXGBE_RSC_CB(this)-
>>dma,
>> + rx_ring->rx_buf_len,
>> + PCI_DMA_FROMDEVICE);
>> skb = skb->prev;
>> dev_kfree_skb(this);
>> } while (skb);
>>
>> --
>> 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
* Re: [PATCH net-next-2.6] bridge: Fix build error when IGMP_SNOOPING is not enabled
From: Herbert Xu @ 2010-03-02 0:46 UTC (permalink / raw)
To: Sridhar Samudrala; +Cc: David Miller, netdev
In-Reply-To: <1267473184.31728.7.camel@w-sridhar.beaverton.ibm.com>
On Mon, Mar 01, 2010 at 11:53:04AM -0800, Sridhar Samudrala wrote:
> Fix the following build error when IGMP_SNOOPING is not enabled.
> In file included from net/bridge/br.c:24:
> net/bridge/br_private.h: In function 'br_multicast_is_router':
> net/bridge/br_private.h:361: error: 'struct net_bridge' has no member named 'multicast_router'
> net/bridge/br_private.h:362: error: 'struct net_bridge' has no member named 'multicast_router'
> net/bridge/br_private.h:363: error: 'struct net_bridge' has no member named 'multicast_router_timer'
>
> Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Thanks for the fix.
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
I'd originally left the struct data outside ifdefs and then added
the ifdefs without testing.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [net-next-2.6 PATCH 1/3] ixgbe: Fix DMA mapping/unmapping issues when HWRSC is enabled on IOMMU enabled kernels
From: Simon Horman @ 2010-03-02 0:29 UTC (permalink / raw)
To: Jeff Kirsher; +Cc: davem, netdev, gospo, Mallikarjuna R Chilakala
In-Reply-To: <20100226091318.19796.70225.stgit@localhost.localdomain>
On Fri, Feb 26, 2010 at 01:14:37AM -0800, Jeff Kirsher wrote:
> From: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>
>
> Work around 82599 HW issue when HWRSC is enabled on IOMMU enabled
> kernels. 82599 HW is updating the header information after setting the
> descriptor to done, resulting DMA mapping/unmapping issues on IOMMU
> enabled systems. To work around the issue delay unmapping of first packet
> that carries the header information until end of packet is reached.
>
> Signed-off-by: Mallikarjuna R Chilakala <mallikarjuna.chilakala@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
> ---
>
> drivers/net/ixgbe/ixgbe_main.c | 32 +++++++++++++++++++++++++++++---
> 1 files changed, 29 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
> index 3308790..4a01022 100644
> --- a/drivers/net/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ixgbe/ixgbe_main.c
> @@ -818,6 +818,12 @@ static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb,
> return skb;
> }
>
> +struct ixgbe_rsc_cb {
> + dma_addr_t dma;
> +};
> +
> +#define IXGBE_RSC_CB(skb) ((struct ixgbe_rsc_cb *)(skb)->cb)
> +
> static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
> struct ixgbe_ring *rx_ring,
> int *work_done, int work_to_do)
> @@ -867,9 +873,21 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
> rx_buffer_info->skb = NULL;
>
> if (rx_buffer_info->dma) {
> - pci_unmap_single(pdev, rx_buffer_info->dma,
> - rx_ring->rx_buf_len,
> - PCI_DMA_FROMDEVICE);
> + if ((adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
> + (!(staterr & IXGBE_RXD_STAT_EOP)) &&
> + (!(skb->prev)))
> + /*
> + * When HWRSC is enabled, delay unmapping
> + * of the first packet. It carries the
> + * header information, HW may still
> + * access the header after the writeback.
> + * Only unmap it when EOP is reached
> + */
> + IXGBE_RSC_CB(skb)->dma = rx_buffer_info->dma;
> + else
> + pci_unmap_single(pdev, rx_buffer_info->dma,
> + rx_ring->rx_buf_len,
> + PCI_DMA_FROMDEVICE);
> rx_buffer_info->dma = 0;
> skb_put(skb, len);
> }
> @@ -917,6 +935,10 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
> if (skb->prev)
> skb = ixgbe_transform_rsc_queue(skb, &(rx_ring->rsc_count));
> if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
> + if (IXGBE_RSC_CB(skb)->dma)
> + pci_unmap_single(pdev, IXGBE_RSC_CB(skb)->dma,
> + rx_ring->rx_buf_len,
> + PCI_DMA_FROMDEVICE);
Does IXGBE_RSC_CB(skb)->dma need to be set to NULL here
to avoid a double-free in ixgbe_clean_rx_ring() ?
> if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)
> rx_ring->rsc_count += skb_shinfo(skb)->nr_frags;
> else
> @@ -3104,6 +3126,10 @@ static void ixgbe_clean_rx_ring(struct ixgbe_adapter *adapter,
> rx_buffer_info->skb = NULL;
> do {
> struct sk_buff *this = skb;
> + if (IXGBE_RSC_CB(this)->dma)
> + pci_unmap_single(pdev, IXGBE_RSC_CB(this)->dma,
> + rx_ring->rx_buf_len,
> + PCI_DMA_FROMDEVICE);
> skb = skb->prev;
> dev_kfree_skb(this);
> } while (skb);
>
> --
> 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] bridge: per-cpu packet statistics
From: Stephen Hemminger @ 2010-03-02 0:16 UTC (permalink / raw)
To: David Miller, netdev, bridge
The shared packet statistics are a potential source of slow down
on bridged traffic. Convert to per-cpu array, but only keep those
statistics which change per-packet.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
net/bridge/br_device.c | 43 ++++++++++++++++++++++++++++++++++++++-----
net/bridge/br_if.c | 6 ++++++
net/bridge/br_input.c | 5 +++--
net/bridge/br_private.h | 8 ++++++++
4 files changed, 55 insertions(+), 7 deletions(-)
--- a/net/bridge/br_device.c 2010-03-01 08:22:23.476657998 -0800
+++ b/net/bridge/br_device.c 2010-03-01 15:31:36.737227465 -0800
@@ -26,11 +26,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *
const unsigned char *dest = skb->data;
struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst;
+ struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
- BR_INPUT_SKB_CB(skb)->brdev = dev;
+ brstats->tx_packets++;
+ brstats->tx_bytes += skb->len;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
+ BR_INPUT_SKB_CB(skb)->brdev = dev;
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
@@ -81,6 +82,28 @@ static int br_dev_stop(struct net_device
return 0;
}
+static struct net_device_stats *br_get_stats(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned int cpu;
+
+ stats->tx_bytes = stats->tx_packets = 0;
+ stats->rx_bytes = stats->rx_packets = 0;
+
+ for_each_online_cpu(cpu) {
+ const struct br_cpu_netstats *bstats
+ = per_cpu_ptr(br->stats, cpu);
+
+ stats->tx_bytes += bstats->tx_bytes;
+ stats->tx_packets += bstats->tx_packets;
+ stats->rx_bytes += bstats->rx_bytes;
+ stats->rx_packets += bstats->rx_packets;
+ }
+
+ return stats;
+}
+
static int br_change_mtu(struct net_device *dev, int new_mtu)
{
struct net_bridge *br = netdev_priv(dev);
@@ -180,19 +203,27 @@ static const struct net_device_ops br_ne
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
.ndo_start_xmit = br_dev_xmit,
+ .ndo_get_stats = br_get_stats,
.ndo_set_mac_address = br_set_mac_address,
.ndo_set_multicast_list = br_dev_set_multicast_list,
.ndo_change_mtu = br_change_mtu,
.ndo_do_ioctl = br_dev_ioctl,
};
+static void br_dev_free(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ free_percpu(br->stats);
+}
+
void br_dev_setup(struct net_device *dev)
{
random_ether_addr(dev->dev_addr);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
- dev->destructor = free_netdev;
+ dev->destructor = br_dev_free;
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
dev->tx_queue_len = 0;
dev->priv_flags = IFF_EBRIDGE;
--- a/net/bridge/br_if.c 2010-03-01 08:22:23.476657998 -0800
+++ b/net/bridge/br_if.c 2010-03-01 15:30:47.733227819 -0800
@@ -185,6 +185,12 @@ static struct net_device *new_bridge_dev
br = netdev_priv(dev);
br->dev = dev;
+ br->stats = alloc_percpu(sizeof(struct br_cpu_netstats));
+ if (!br->stats) {
+ free_netdev(dev);
+ return NULL;
+ }
+
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
spin_lock_init(&br->hash_lock);
--- a/net/bridge/br_input.c 2010-03-01 08:22:23.476657998 -0800
+++ b/net/bridge/br_input.c 2010-03-01 15:32:45.882471626 -0800
@@ -23,9 +23,11 @@ const u8 br_group_address[ETH_ALEN] = {
static int br_pass_frame_up(struct sk_buff *skb)
{
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
+ struct net_bridge *br = netdev_priv(brdev);
+ struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
- brdev->stats.rx_packets++;
- brdev->stats.rx_bytes += skb->len;
+ brstats->rx_packets++;
+ brstats->rx_bytes += skb->len;
indev = skb->dev;
skb->dev = brdev;
--- a/net/bridge/br_private.h 2010-03-01 08:22:23.476657998 -0800
+++ b/net/bridge/br_private.h 2010-03-01 15:31:03.437228864 -0800
@@ -135,6 +135,14 @@ struct net_bridge
spinlock_t lock;
struct list_head port_list;
struct net_device *dev;
+
+ struct br_cpu_netstats __percpu {
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ } *stats;
+
spinlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
unsigned long feature_mask;
^ permalink raw reply
* [PATCH 1/1] ARC vmac ethernet driver.
From: Andreas Fenkart @ 2010-03-01 23:18 UTC (permalink / raw)
To: netdev; +Cc: amit.bhor, sameer.dhavale, Andreas Fenkart
In-Reply-To: <1267485483-15298-1-git-send-email-andreas.fenkart@streamunlimited.com>
Signed-off-by: Andreas Fenkart <andreas.fenkart@streamunlimited.com>
---
drivers/net/Kconfig | 6 +
drivers/net/Makefile | 1 +
drivers/net/arcvmac.c | 1439 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/arcvmac.h | 268 +++++++++
4 files changed, 1714 insertions(+), 0 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index dd9a09c..7972d4d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -236,6 +236,12 @@ config AX88796_93CX6
help
Select this if your platform comes with an external 93CX6 eeprom.
+config ARCVMAC
+ tristate "ARC VMAC ethernet support"
+ select MII
+ help
+ MAC present on Zoran Quatro43XX
+
config MACE
tristate "MACE (Power Mac ethernet) support"
depends on PPC_PMAC && PPC32
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 2aff98c..0095022 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o
obj-$(CONFIG_E2100) += e2100.o 8390.o
obj-$(CONFIG_ES3210) += es3210.o 8390.o
obj-$(CONFIG_LNE390) += lne390.o 8390.o
+obj-$(CONFIG_ARCVMAC) += arcvmac.o
obj-$(CONFIG_NE3210) += ne3210.o 8390.o
obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
obj-$(CONFIG_B44) += b44.o
diff --git a/drivers/net/arcvmac.c b/drivers/net/arcvmac.c
new file mode 100644
index 0000000..280d92b
--- /dev/null
+++ b/drivers/net/arcvmac.c
@@ -0,0 +1,1439 @@
+/*
+ * linux/arch/arc/drivers/arcvmac.c
+ *
+ * Copyright (C) 2003-2006 Codito Technologies, for linux-2.4 port
+ * Copyright (C) 2006-2007 Celunite Inc, for linux-2.6 port
+ * Copyright (C) 2007-2008 Sagem Communications, Fehmi HAFSI
+ * Copyright (C) 2009 Sagem Communications, Andreas Fenkart
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * external PHY support based on dnet.c
+ * ring management based on bcm63xx_enet.c
+ *
+ * Authors: amit.bhor@celunite.com, sameer.dhavale@celunite.com
+ */
+
+#undef DEBUG
+
+#include <linux/clk.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "arcvmac.h"
+
+static char *mac_addr;
+module_param(mac_addr, charp, 0644);
+MODULE_PARM_DESC(mac_addr, "MAC address as colon separated hexadecimals");
+
+static void parse_mac_addr_param(struct net_device *dev,
+ char *module_param)
+{
+ struct sockaddr saddr;
+ unsigned char *hwaddr = saddr.sa_data;
+
+ sscanf(module_param, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &hwaddr[0],
+ &hwaddr[1],
+ &hwaddr[2],
+ &hwaddr[3],
+ &hwaddr[4],
+ &hwaddr[5]);
+
+ eth_mac_addr(dev, &saddr);
+}
+
+/* Register access macros */
+#define vmac_writel(port, value, reg) \
+ writel((value), (port)->regs + reg##_OFFSET)
+#define vmac_readl(port, reg) readl((port)->regs + reg##_OFFSET)
+
+static unsigned char *read_mac_reg(struct net_device *dev,
+ unsigned char hwaddr[ETH_ALEN])
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned mac_lo, mac_hi;
+
+ WARN_ON(!hwaddr);
+ mac_lo = vmac_readl(ap, ADDRL);
+ mac_hi = vmac_readl(ap, ADDRH);
+
+ hwaddr[0] = (mac_lo >> 0) & 0xff;
+ hwaddr[1] = (mac_lo >> 8) & 0xff;
+ hwaddr[2] = (mac_lo >> 16) & 0xff;
+ hwaddr[3] = (mac_lo >> 24) & 0xff;
+ hwaddr[4] = (mac_hi >> 0) & 0xff;
+ hwaddr[5] = (mac_hi >> 8) & 0xff;
+ return hwaddr;
+}
+
+static void write_mac_reg(struct net_device *dev, unsigned char* hwaddr)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned mac_lo, mac_hi;
+
+ mac_lo = hwaddr[3] << 24 | hwaddr[2] << 16 | hwaddr[1] << 8 | hwaddr[0];
+ mac_hi = hwaddr[5] << 8 | hwaddr[4];
+
+ vmac_writel(ap, mac_lo, ADDRL);
+ vmac_writel(ap, mac_hi, ADDRH);
+}
+
+static void vmac_mdio_xmit(struct vmac_priv *ap, unsigned val)
+{
+ init_completion(&ap->mdio_complete);
+ vmac_writel(ap, val, MDIO_DATA);
+ wait_for_completion(&ap->mdio_complete);
+}
+
+static int vmac_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
+{
+ struct vmac_priv *vmac = bus->priv;
+ unsigned int val;
+ /* only 5 bits allowed for phy-addr and reg_offset */
+ WARN_ON(phy_id & ~0x1f || phy_reg & ~0x1f);
+
+ val = MDIO_BASE | MDIO_OP_READ;
+ val |= phy_id << 23 | phy_reg << 18;
+ vmac_mdio_xmit(vmac, val);
+
+ val = vmac_readl(vmac, MDIO_DATA);
+ return val & MDIO_DATA_MASK;
+}
+
+static int vmac_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg,
+ u16 value)
+{
+ struct vmac_priv *vmac = bus->priv;
+ unsigned int val;
+ /* only 5 bits allowed for phy-addr and reg_offset */
+ WARN_ON(phy_id & ~0x1f || phy_reg & ~0x1f);
+
+ val = MDIO_BASE | MDIO_OP_WRITE;
+ val |= phy_id << 23 | phy_reg << 18;
+ val |= (value & MDIO_DATA_MASK);
+ vmac_mdio_xmit(vmac, val);
+ return 0;
+}
+
+static void vmac_handle_link_change(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev = ap->phy_dev;
+ unsigned long flags;
+ int report_change = 0;
+
+ spin_lock_irqsave(&ap->lock, flags);
+
+ if (phydev->duplex != ap->duplex) {
+ unsigned tmp;
+
+ tmp = vmac_readl(ap, ENABLE);
+
+ if (phydev->duplex)
+ tmp |= ENFL_MASK;
+ else
+ tmp &= ~ENFL_MASK;
+
+ vmac_writel(ap, tmp, ENABLE);
+
+ ap->duplex = phydev->duplex;
+ report_change = 1;
+ }
+
+ if (phydev->speed != ap->speed) {
+ ap->speed = phydev->speed;
+ report_change = 1;
+ }
+
+ if (phydev->link != ap->link) {
+ ap->link = phydev->link;
+ report_change = 1;
+ }
+
+ spin_unlock_irqrestore(&ap->lock, flags);
+
+ if (report_change)
+ phy_print_status(ap->phy_dev);
+}
+
+static int __devinit vmac_mii_probe(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ struct clk *sys_clk;
+ unsigned long clock_rate;
+ int phy_addr, err;
+
+ /* find the first phy */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ if (ap->mii_bus->phy_map[phy_addr]) {
+ phydev = ap->mii_bus->phy_map[phy_addr];
+ break;
+ }
+ }
+
+ if (!phydev) {
+ dev_err(&dev->dev, "no PHY found\n");
+ return -ENODEV;
+ }
+
+ /* FIXME: add pin_irq, if avail */
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev),
+ &vmac_handle_link_change, 0,
+ PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ err = PTR_ERR(phydev);
+ dev_err(&dev->dev, "could not attach to PHY %d\n", err);
+ goto err_out;
+ }
+
+ phydev->supported &= PHY_BASIC_FEATURES;
+ phydev->supported |= SUPPORTED_Asym_Pause | SUPPORTED_Pause;
+
+ sys_clk = clk_get(&ap->pdev->dev, "arcvmac");
+ if (IS_ERR(sys_clk)) {
+ err = PTR_ERR(sys_clk);
+ goto err_disconnect;
+ }
+
+ clock_rate = clk_get_rate(sys_clk);
+ clk_put(sys_clk);
+
+ dev_dbg(&ap->pdev->dev, "clk_get: dev_name : %s %lu\n",
+ dev_name(&ap->pdev->dev),
+ clock_rate);
+
+ if (clock_rate < 25000000)
+ phydev->supported &= ~(SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full);
+
+ phydev->advertising = phydev->supported;
+
+ ap->link = 0;
+ ap->speed = 0;
+ ap->duplex = -1;
+ ap->phy_dev = phydev;
+
+ return 0;
+
+err_disconnect:
+ phy_disconnect(phydev);
+err_out:
+ return err;
+}
+
+static int __devinit vmac_mii_init(struct vmac_priv *ap)
+{
+ int err, i;
+
+ ap->mii_bus = mdiobus_alloc();
+ if (ap->mii_bus == NULL)
+ return -ENOMEM;
+
+ ap->mii_bus->name = "vmac_mii_bus";
+ ap->mii_bus->read = &vmac_mdio_read;
+ ap->mii_bus->write = &vmac_mdio_write;
+
+ snprintf(ap->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0);
+
+ ap->mii_bus->priv = ap;
+
+ err = -ENOMEM;
+ ap->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!ap->mii_bus->irq)
+ goto err_out;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ap->mii_bus->irq[i] = PHY_POLL;
+
+#if 0
+ /* FIXME: what is it used for? */
+ platform_set_drvdata(ap->dev, ap->mii_bus);
+#endif
+
+ err = mdiobus_register(ap->mii_bus);
+ if (err)
+ goto err_out_free_mdio_irq;
+
+ err = vmac_mii_probe(ap->dev);
+ if (err)
+ goto err_out_unregister_bus;
+
+ return 0;
+
+err_out_unregister_bus:
+ mdiobus_unregister(ap->mii_bus);
+err_out_free_mdio_irq:
+ kfree(ap->mii_bus->irq);
+err_out:
+ mdiobus_free(ap->mii_bus);
+ return err;
+}
+
+static void vmac_mii_exit(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+
+ if (ap->phy_dev)
+ phy_disconnect(ap->phy_dev);
+
+ mdiobus_unregister(ap->mii_bus);
+ kfree(ap->mii_bus->irq);
+ mdiobus_free(ap->mii_bus);
+}
+
+static int vmacether_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev = ap->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+static int vmacether_set_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev = ap->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+static int vmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev = ap->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phydev, if_mii(rq), cmd);
+}
+
+static void vmacether_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ snprintf(info->bus_info, sizeof(info->bus_info),
+ "platform 0x%x", ap->mem_base);
+}
+
+static int update_error_counters(struct net_device *dev, int status)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ dev_dbg(&ap->pdev->dev, "rx error counter overrun. status = 0x%x\n",
+ status);
+
+ /* programming error */
+ WARN_ON(status & TXCH_MASK);
+ WARN_ON(!(status & (MSER_MASK | RXCR_MASK | RXFR_MASK | RXFL_MASK)));
+
+ if (status & MSER_MASK)
+ ap->stats.rx_over_errors += 256; /* ran out of BD */
+ if (status & RXCR_MASK)
+ ap->stats.rx_crc_errors += 256;
+ if (status & RXFR_MASK)
+ ap->stats.rx_frame_errors += 256;
+ if (status & RXFL_MASK)
+ ap->stats.rx_fifo_errors += 256;
+
+ return 0;
+}
+
+static void update_tx_errors(struct net_device *dev, int status)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+
+ if (status & UFLO)
+ ap->stats.tx_fifo_errors++;
+
+ if (ap->duplex)
+ return;
+
+ /* half duplex flags */
+ if (status & LTCL)
+ ap->stats.tx_window_errors++;
+ if (status & RETRY_CT)
+ ap->stats.collisions += (status & RETRY_CT) >> 24;
+ if (status & DROP) /* too many retries */
+ ap->stats.tx_aborted_errors++;
+ if (status & DEFER)
+ dev_vdbg(&ap->pdev->dev, "\"defer to traffic\"\n");
+ if (status & CARLOSS)
+ ap->stats.tx_carrier_errors++;
+}
+
+static int vmac_rx_reclaim_force(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ int ct;
+
+ ct = 0;
+
+ dev_dbg(&ap->pdev->dev, "%s need to release %d rx sk_buff\n",
+ __func__, fifo_used(&ap->rx_ring));
+
+ while (!fifo_empty(&ap->rx_ring) && ct++ < ap->rx_ring.size) {
+ struct vmac_buffer_desc *desc;
+ struct sk_buff *skb;
+ int desc_idx;
+
+ desc_idx = ap->rx_ring.tail;
+ desc = &ap->rxbd[desc_idx];
+ fifo_inc_tail(&ap->rx_ring);
+
+ if (!ap->rx_skbuff[desc_idx]) {
+ dev_err(&ap->pdev->dev, "non-populated rx_skbuff found %d\n",
+ desc_idx);
+ continue;
+ }
+
+ skb = ap->rx_skbuff[desc_idx];
+ ap->rx_skbuff[desc_idx] = NULL;
+
+ dma_unmap_single(&ap->pdev->dev, desc->data, skb->len,
+ DMA_TO_DEVICE);
+
+ dev_kfree_skb(skb);
+ }
+
+ if (!fifo_empty(&ap->rx_ring)) {
+ dev_err(&ap->pdev->dev, "failed to reclaim %d rx sk_buff\n",
+ fifo_used(&ap->rx_ring));
+ }
+
+ return 0;
+}
+
+static int vmac_rx_refill(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+
+ WARN_ON(fifo_full(&ap->rx_ring));
+
+ while (!fifo_full(&ap->rx_ring)) {
+ struct vmac_buffer_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t p;
+ int desc_idx;
+
+ desc_idx = ap->rx_ring.head;
+ desc = &ap->rxbd[desc_idx];
+
+ /* make sure we read the actual descriptor status */
+ rmb();
+
+ if (ap->rx_skbuff[desc_idx]) {
+ /* dropped packet / buffer chaining */
+ fifo_inc_head(&ap->rx_ring);
+
+ /* return to DMA */
+ wmb();
+ desc->info = OWN_MASK | ap->rx_skb_size;
+ continue;
+ }
+
+ skb = netdev_alloc_skb(dev, ap->rx_skb_size + 2);
+ if (!skb) {
+ dev_info(&ap->pdev->dev, "failed to allocate rx_skb, skb's left %d\n",
+ fifo_used(&ap->rx_ring));
+ break;
+ }
+
+ /* IP header Alignment (14 byte Ethernet header) */
+ skb_reserve(skb, 2);
+ WARN_ON(skb->len != 0); /* nothing received yet */
+
+ ap->rx_skbuff[desc_idx] = skb;
+
+ /* TODO +2 okay with alignment? */
+ p = dma_map_single(&ap->pdev->dev, skb->data, ap->rx_skb_size,
+ DMA_FROM_DEVICE);
+
+ desc->data = p;
+
+ wmb();
+ desc->info = OWN_MASK | ap->rx_skb_size;
+
+ fifo_inc_head(&ap->rx_ring);
+ }
+
+ /* If rx ring is still empty, set a timer to try allocating
+ * again at a later time. */
+ if (fifo_empty(&ap->rx_ring) && netif_running(dev)) {
+ dev_warn(&ap->pdev->dev, "unable to refill rx ring\n");
+ ap->rx_timeout.expires = jiffies + HZ;
+ add_timer(&ap->rx_timeout);
+ }
+
+ return 0;
+}
+
+/*
+ * timer callback to defer refill rx queue in case we're OOM
+ */
+static void vmac_refill_rx_timer(unsigned long data)
+{
+ struct net_device *dev;
+ struct vmac_priv *ap;
+
+ dev = (struct net_device *)data;
+ ap = netdev_priv(dev);
+
+ spin_lock(&ap->rx_lock);
+ vmac_rx_refill(dev);
+ spin_unlock(&ap->rx_lock);
+}
+
+/* merge buffer chaining */
+struct sk_buff *vmac_merge_rx_buffers(struct net_device *dev,
+ struct vmac_buffer_desc *after,
+ int pkt_len) /* data */
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct sk_buff *merge_skb, *cur_skb;
+ struct dma_fifo *rx_ring;
+ struct vmac_buffer_desc *desc;
+
+ rx_ring = &ap->rx_ring;
+ desc = &ap->rxbd[rx_ring->tail];
+
+ WARN_ON(desc == after);
+
+ /* strip FCS */
+ pkt_len -= 4;
+
+ /* IP header Alignment (14 byte Ethernet header) */
+ merge_skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (!merge_skb) {
+ dev_err(&ap->pdev->dev, "failed to allocate merged rx_skb, rx skb's left %d\n",
+ fifo_used(rx_ring));
+
+ return NULL;
+ }
+
+ skb_reserve(merge_skb, 2);
+
+ while (desc != after && pkt_len) {
+ struct vmac_buffer_desc *desc;
+ int buf_len, valid;
+
+ /* desc needs wrapping */
+ desc = &ap->rxbd[rx_ring->tail];
+ cur_skb = ap->rx_skbuff[rx_ring->tail];
+ WARN_ON(!cur_skb);
+
+ dma_unmap_single(&ap->pdev->dev, desc->data, ap->rx_skb_size,
+ DMA_FROM_DEVICE);
+
+ /* do not copy FCS */
+ buf_len = desc->info & LEN_MASK;
+ valid = min(pkt_len, buf_len);
+ pkt_len -= valid;
+
+ memcpy(skb_put(merge_skb, valid), cur_skb->data, valid);
+
+ fifo_inc_tail(rx_ring);
+ }
+
+ /* merging_pressure++ */
+
+#ifdef DEBUG
+ if (pkt_len != 0)
+ dev_err(&ap->pdev->dev, "buffer chaining bytes missing %d\n", pkt_len);
+#endif
+
+ WARN_ON(desc != after);
+
+ return merge_skb;
+}
+
+int vmac_rx_receive(struct net_device *dev, int budget)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct vmac_buffer_desc *first;
+ int processed, pkt_len, pkt_err;
+ struct dma_fifo lookahead;
+
+ processed = 0;
+
+ first = NULL;
+ pkt_err = pkt_len = 0;
+
+ /* look ahead, till packet complete */
+ lookahead = ap->rx_ring;
+
+ do {
+ struct vmac_buffer_desc *desc; /* cur_ */
+ int desc_idx; /* cur_ */
+ struct sk_buff *skb; /* pkt_ */
+
+ desc_idx = lookahead.tail;
+ desc = &ap->rxbd[desc_idx];
+
+ /* make sure we read the actual descriptor status */
+ rmb();
+
+ /* break if dma ownership belongs to hw */
+ if (desc->info & OWN_MASK) {
+ ap->mac_rxring_head = vmac_readl(ap, MAC_RXRING_HEAD);
+ break;
+ }
+
+ if (desc->info & FRST_MASK) {
+ pkt_len = 0;
+ pkt_err = 0;
+
+ /* don't free current */
+ ap->rx_ring.tail = lookahead.tail;
+ first = desc;
+ }
+
+ fifo_inc_tail(&lookahead);
+
+ /* check bd */
+
+ pkt_len += desc->info & LEN_MASK;
+ pkt_err |= (desc->info & BUFF);
+
+ if (!(desc->info & LAST_MASK))
+ continue;
+
+ /* received complete packet */
+
+ if (unlikely(pkt_err || !first)) {
+ /* recycle buffers */
+ ap->rx_ring.tail = lookahead.tail;
+ continue;
+ }
+
+#ifdef DEBUG
+ WARN_ON(!(first->info & FRST_MASK) ||
+ !(desc->info & LAST_MASK));
+ WARN_ON(pkt_err);
+#endif
+
+ /* -- valid packet -- */
+
+ if (first != desc) {
+ skb = vmac_merge_rx_buffers(dev, desc, pkt_len);
+
+ if (!skb) {
+ /* kill packet */
+ ap->rx_ring.tail = lookahead.tail;
+ ap->rx_merge_error++;
+ continue;
+ }
+ } else {
+ dma_unmap_single(&ap->pdev->dev, desc->data,
+ ap->rx_skb_size, DMA_FROM_DEVICE);
+
+ skb = ap->rx_skbuff[desc_idx];
+ ap->rx_skbuff[desc_idx] = NULL;
+ /* desc->data != skb->data => desc->data DMA mapped */
+
+ /* strip FCS */
+ skb_put(skb, pkt_len - 4);
+ }
+
+ /* free buffers */
+ ap->rx_ring.tail = lookahead.tail;
+
+#ifdef DEBUG
+ WARN_ON(skb->len != pkt_len - 4);
+#endif
+ processed++;
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ ap->stats.rx_packets++;
+ ap->stats.rx_bytes += skb->len;
+ dev->last_rx = jiffies;
+ netif_rx(skb);
+
+ } while (!fifo_empty(&lookahead) && (processed < budget));
+
+ dev_vdbg(&ap->pdev->dev, "processed pkt %d, remaining rx buff %d\n",
+ processed,
+ fifo_used(&ap->rx_ring));
+
+ if (processed || fifo_empty(&ap->rx_ring))
+ vmac_rx_refill(dev);
+
+ return processed;
+}
+
+static void vmac_toggle_irqmask(struct net_device *dev, int enable, int mask)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned long tmp;
+
+ tmp = vmac_readl(ap, ENABLE);
+ if (enable)
+ tmp |= mask;
+ else
+ tmp &= ~mask;
+ vmac_writel(ap, tmp, ENABLE);
+}
+
+static void vmac_toggle_txint(struct net_device *dev, int enable)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap->lock, flags);
+ vmac_toggle_irqmask(dev, enable, TXINT_MASK);
+ spin_unlock_irqrestore(&ap->lock, flags);
+}
+
+static void vmac_toggle_rxint(struct net_device *dev, int enable)
+{
+ vmac_toggle_irqmask(dev, enable, RXINT_MASK);
+}
+
+static int vmac_poll(struct napi_struct *napi, int budget)
+{
+ struct vmac_priv *ap;
+ struct net_device *dev;
+ int rx_work_done;
+ unsigned long flags;
+
+ ap = container_of(napi, struct vmac_priv, napi);
+ dev = ap->dev;
+
+ /* ack interrupt */
+ vmac_writel(ap, RXINT_MASK, STAT);
+
+ spin_lock(&ap->rx_lock);
+ rx_work_done = vmac_rx_receive(dev, budget);
+ spin_unlock(&ap->rx_lock);
+
+ if (0 && printk_ratelimit()) {
+ dev_dbg(&ap->pdev->dev, "poll budget %d receive rx_work_done %d\n",
+ budget,
+ rx_work_done);
+ }
+
+ if (rx_work_done >= budget) {
+ /* rx queue is not yet empty/clean */
+ return rx_work_done;
+ }
+
+ /* no more packet in rx/tx queue, remove device from poll
+ * queue */
+ spin_lock_irqsave(&ap->lock, flags);
+ napi_complete(napi);
+ vmac_toggle_rxint(dev, 1);
+ spin_unlock_irqrestore(&ap->lock, flags);
+
+ return rx_work_done;
+}
+
+static int vmac_tx_reclaim(struct net_device *dev, int force);
+
+static irqreturn_t vmac_intr(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned int status;
+
+ spin_lock(&ap->lock);
+
+ status = vmac_readl(ap, STAT);
+ vmac_writel(ap, status, STAT);
+
+#ifdef DEBUG
+ if (unlikely(ap->shutdown))
+ dev_err(&ap->pdev->dev, "ISR during close\n");
+
+ if (unlikely(!status & (RXINT_MASK|MDIO_MASK|ERR_MASK)))
+ dev_err(&ap->pdev->dev, "No source of IRQ found\n");
+#endif
+
+ if ((status & RXINT_MASK) &&
+ (ap->mac_rxring_head !=
+ vmac_readl(ap, MAC_RXRING_HEAD))) {
+ vmac_toggle_rxint(dev, 0);
+ napi_schedule(&ap->napi);
+ }
+
+ if (unlikely(netif_queue_stopped(dev) && (status & TXINT_MASK)))
+ vmac_tx_reclaim(dev, 0);
+
+ if (status & MDIO_MASK)
+ complete(&ap->mdio_complete);
+
+ if (unlikely(status & ERR_MASK))
+ update_error_counters(dev, status);
+
+ spin_unlock(&ap->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int vmac_tx_reclaim(struct net_device *dev, int force)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ int released = 0;
+
+ /* buffer chaining not used, see vmac_start_xmit */
+
+ while (!fifo_empty(&ap->tx_ring)) {
+ struct vmac_buffer_desc *desc;
+ struct sk_buff *skb;
+ int desc_idx;
+
+ desc_idx = ap->tx_ring.tail;
+ desc = &ap->txbd[desc_idx];
+
+ /* ensure other field of the descriptor were not read
+ * before we checked ownership */
+ rmb();
+
+ if ((desc->info & OWN_MASK) && !force)
+ break;
+
+ if (desc->info & ERR_MSK_TX) {
+ update_tx_errors(dev, desc->info);
+ /* recycle packet, let upper level deal with it */
+ }
+
+ skb = ap->tx_skbuff[desc_idx];
+ ap->tx_skbuff[desc_idx] = NULL;
+ WARN_ON(!skb);
+
+ dma_unmap_single(&ap->pdev->dev, desc->data, skb->len,
+ DMA_TO_DEVICE);
+
+ dev_kfree_skb_any(skb);
+
+ released++;
+ fifo_inc_tail(&ap->tx_ring);
+ }
+
+ if (netif_queue_stopped(dev) && released) {
+ netif_wake_queue(dev);
+ vmac_toggle_txint(dev, 0);
+ }
+
+ if (unlikely(force && !fifo_empty(&ap->tx_ring))) {
+ dev_err(&ap->pdev->dev, "failed to reclaim %d tx sk_buff\n",
+ fifo_used(&ap->tx_ring));
+ }
+
+ return released;
+}
+
+int vmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct vmac_buffer_desc *desc;
+ unsigned int tmp;
+
+ /* running under xmit lock */
+
+ /* no scatter/gatter see features below */
+ WARN_ON(skb_shinfo(skb)->nr_frags != 0);
+ WARN_ON(skb->len > MAX_TX_BUFFER_LEN);
+
+ if (unlikely(fifo_full(&ap->tx_ring))) {
+ netif_stop_queue(dev);
+ vmac_toggle_txint(dev, 1);
+ dev_err(&ap->pdev->dev, "xmit called with no tx desc available\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (unlikely(skb->len < ETH_ZLEN)) {
+ struct sk_buff *short_skb;
+ short_skb = netdev_alloc_skb(dev, ETH_ZLEN);
+ if (!short_skb)
+ return NETDEV_TX_LOCKED;
+
+ memset(short_skb->data, 0, ETH_ZLEN);
+ memcpy(skb_put(short_skb, ETH_ZLEN), skb->data, skb->len);
+ dev_kfree_skb(skb);
+ skb = short_skb;
+ }
+
+ /* fill descriptor */
+ ap->tx_skbuff[ap->tx_ring.head] = skb;
+
+ desc = &ap->txbd[ap->tx_ring.head];
+ desc->data = dma_map_single(&ap->pdev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+
+ /* dma might already be polling */
+ wmb();
+ desc->info = OWN_MASK | FRST_MASK | LAST_MASK | skb->len;
+ wmb();
+
+ /* kick tx dma */
+ tmp = vmac_readl(ap, STAT);
+ vmac_writel(ap, tmp | TXPL_MASK, STAT);
+
+ ap->stats.tx_packets++;
+ ap->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+ fifo_inc_head(&ap->tx_ring);
+
+ /* vmac_tx_reclaim independent of vmac_tx_timeout */
+ if (fifo_used(&ap->tx_ring) > 8)
+ vmac_tx_reclaim(dev, 0);
+
+ /* stop queue if no more desc available */
+ if (fifo_full(&ap->tx_ring)) {
+ netif_stop_queue(dev);
+ vmac_toggle_txint(dev, 1);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static int alloc_buffers(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ int err = -ENOMEM;
+ int size;
+
+ fifo_init(&ap->rx_ring, RX_BDT_LEN);
+ fifo_init(&ap->tx_ring, TX_BDT_LEN);
+
+ /* initialize skb list */
+ memset(ap->rx_skbuff, 0, sizeof(ap->rx_skbuff));
+ memset(ap->tx_skbuff, 0, sizeof(ap->tx_skbuff));
+
+ /* allocate DMA received descriptors */
+ size = sizeof(*ap->rxbd) * ap->rx_ring.size;
+ ap->rxbd = dma_alloc_coherent(&ap->pdev->dev, size,
+ &ap->rxbd_dma,
+ GFP_KERNEL);
+ if (ap->rxbd == NULL)
+ goto err_out;
+
+ /* allocate DMA transmit descriptors */
+ size = sizeof(*ap->txbd) * ap->tx_ring.size;
+ ap->txbd = dma_alloc_coherent(&ap->pdev->dev, size,
+ &ap->txbd_dma,
+ GFP_KERNEL);
+ if (ap->txbd == NULL)
+ goto err_free_rxbd;
+
+ /* ensure 8-byte aligned */
+ WARN_ON(((int)ap->txbd & 0x7) || ((int)ap->rxbd & 0x7));
+
+ memset(ap->txbd, 0, sizeof(*ap->txbd) * ap->tx_ring.size);
+ memset(ap->rxbd, 0, sizeof(*ap->rxbd) * ap->rx_ring.size);
+
+ /* allocate rx skb */
+ err = vmac_rx_refill(dev);
+ if (err)
+ goto err_free_txbd;
+
+ return 0;
+
+err_free_txbd:
+ dma_free_coherent(&ap->pdev->dev, sizeof(*ap->txbd) * ap->tx_ring.size,
+ ap->txbd, ap->txbd_dma);
+err_free_rxbd:
+ dma_free_coherent(&ap->pdev->dev, sizeof(*ap->rxbd) * ap->rx_ring.size,
+ ap->rxbd, ap->rxbd_dma);
+err_out:
+ return err;
+}
+
+static int free_buffers(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+
+ /* free skbuff */
+ vmac_tx_reclaim(dev, 1);
+ vmac_rx_reclaim_force(dev);
+
+ /* free DMA ring */
+ dma_free_coherent(&ap->pdev->dev, sizeof(ap->txbd) * ap->tx_ring.size,
+ ap->txbd, ap->txbd_dma);
+ dma_free_coherent(&ap->pdev->dev, sizeof(ap->rxbd) * ap->rx_ring.size,
+ ap->rxbd, ap->rxbd_dma);
+
+ return 0;
+}
+
+static int vmac_hw_init(struct net_device *dev)
+{
+ struct vmac_priv *priv = netdev_priv(dev);
+
+ /* clear IRQ mask */
+ vmac_writel(priv, 0, ENABLE);
+
+ /* clear pending IRQ */
+ vmac_writel(priv, 0xffffffff, STAT);
+
+ /* Initialize logical address filter */
+ vmac_writel(priv, 0x0, LAFL);
+ vmac_writel(priv, 0x0, LAFH);
+
+ return 0;
+}
+
+int vmac_open(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ struct phy_device *phydev;
+ unsigned int temp;
+ int err = 0;
+
+ if (ap == NULL)
+ return -ENODEV;
+
+ ap->shutdown = 0;
+
+ vmac_hw_init(dev);
+
+ /* mac address changed? */
+ write_mac_reg(dev, dev->dev_addr);
+
+ err = alloc_buffers(dev);
+ if (err)
+ goto err_out;
+
+ err = request_irq(dev->irq, &vmac_intr, 0, dev->name, dev);
+ if (err) {
+ dev_err(&ap->pdev->dev, "Unable to request IRQ %d (error %d)\n",
+ dev->irq, err);
+ goto err_free_buffers;
+ }
+
+ /* install DMA ring pointers */
+ vmac_writel(ap, ap->rxbd_dma, RXRINGPTR);
+ vmac_writel(ap, ap->txbd_dma, TXRINGPTR);
+
+ /* set poll rate to 1 ms */
+ vmac_writel(ap, POLLRATE_TIME, POLLRATE);
+
+ /* make sure we enable napi before rx interrupt */
+ napi_enable(&ap->napi);
+
+ /* IRQ mask */
+ temp = RXINT_MASK | ERR_MASK | TXCH_MASK | MDIO_MASK;
+ vmac_writel(ap, temp, ENABLE);
+
+ /* Set control */
+ temp = (RX_BDT_LEN << 24) | (TX_BDT_LEN << 16) | TXRN_MASK | RXRN_MASK;
+ vmac_writel(ap, temp, CONTROL);
+
+ /* enable, after all other bits are set */
+ vmac_writel(ap, temp | EN_MASK, CONTROL);
+
+ netif_start_queue(dev);
+ netif_carrier_off(dev);
+
+ /* register the PHY board fixup, if needed */
+ err = vmac_mii_init(ap);
+ if (err)
+ goto err_free_irq;
+
+ /* schedule a link state check */
+ phy_start(ap->phy_dev);
+
+ phydev = ap->phy_dev;
+ dev_info(&ap->pdev->dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_free_buffers:
+ free_buffers(dev);
+err_out:
+ return err;
+}
+
+int vmac_close(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned int temp;
+
+ netif_stop_queue(dev);
+ napi_disable(&ap->napi);
+
+ /* stop running transfers */
+ temp = vmac_readl(ap, CONTROL);
+ temp &= ~(TXRN_MASK | RXRN_MASK);
+ vmac_writel(ap, temp, CONTROL);
+
+ del_timer_sync(&ap->rx_timeout);
+
+ /* disable phy */
+ phy_stop(ap->phy_dev);
+ vmac_mii_exit(dev);
+ netif_carrier_off(dev);
+
+ /* disable interrupts */
+ vmac_writel(ap, 0, ENABLE);
+ free_irq(dev->irq, dev);
+
+ /* turn off vmac */
+ vmac_writel(ap, 0, CONTROL);
+ /* vmac_reset_hw(vmac) */
+
+ ap->shutdown = 1;
+ wmb();
+
+ free_buffers(dev);
+ return 0;
+}
+
+void vmac_update_stats(struct vmac_priv *ap)
+{
+ struct net_device_stats *_stats = &ap->stats;
+ unsigned long miss, rxerr;
+ unsigned long rxfram, rxcrc, rxoflow;
+
+ /* compare with /proc/net/dev,
+ * see net/core/dev.c:dev_seq_printf_stats */
+
+ /* rx stats */
+ rxerr = vmac_readl(ap, RXERR);
+ miss = vmac_readl(ap, MISS);
+
+ rxcrc = (rxerr & RXERR_CRC);
+ rxfram = (rxerr & RXERR_FRM) >> 8;
+ rxoflow = (rxerr & RXERR_OFLO) >> 16;
+
+ _stats->rx_length_errors = 0;
+ _stats->rx_over_errors += miss;
+ _stats->rx_crc_errors += rxcrc;
+ _stats->rx_frame_errors += rxfram;
+ _stats->rx_fifo_errors += rxoflow;
+ _stats->rx_missed_errors = 0;
+
+ /* TODO check rx_dropped/rx_errors/tx_dropped/tx_errors have not
+ * been updated elsewhere */
+ _stats->rx_dropped = _stats->rx_over_errors +
+ _stats->rx_fifo_errors +
+ ap->rx_merge_error;
+
+ _stats->rx_errors = _stats->rx_length_errors + _stats->rx_crc_errors +
+ _stats->rx_frame_errors +
+ _stats->rx_missed_errors +
+ _stats->rx_dropped;
+
+ /* tx stats */
+ _stats->tx_dropped = 0; /* otherwise queue stopped */
+
+ _stats->tx_errors = _stats->tx_aborted_errors +
+ _stats->tx_carrier_errors +
+ _stats->tx_fifo_errors +
+ _stats->tx_heartbeat_errors +
+ _stats->tx_window_errors +
+ _stats->tx_dropped +
+ ap->tx_timeout_error;
+}
+
+struct net_device_stats *vmac_stats(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap->lock, flags);
+ vmac_update_stats(ap);
+ spin_unlock_irqrestore(&ap->lock, flags);
+
+ return &ap->stats;
+}
+
+void vmac_tx_timeout(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap->lock, flags);
+
+ /* queue did not progress for timeo jiffies */
+ WARN_ON(!netif_queue_stopped(dev));
+ WARN_ON(!fifo_full(&ap->tx_ring));
+
+ /* TX IRQ lost? */
+ status = vmac_readl(ap, STAT);
+ if (status & TXINT_MASK) {
+ dev_err(&ap->pdev->dev, "lost tx interrupt, IRQ mask %x\n",
+ vmac_readl(ap, ENABLE));
+ vmac_writel(ap, TXINT_MASK, STAT);
+ }
+
+ /* TODO RX/MDIO/ERR as well? */
+
+ vmac_tx_reclaim(dev, 0);
+ if (fifo_full(&ap->tx_ring))
+ dev_err(&ap->pdev->dev, "DMA state machine not active\n");
+
+ /* We can accept TX packets again */
+ ap->tx_timeout_error++;
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+
+ spin_unlock_irqrestore(&ap->lock, flags);
+}
+
+static void create_multicast_filter(struct net_device *dev,
+ unsigned long *bitmask)
+{
+ struct dev_mc_list *mc_ptr;
+ unsigned long crc;
+ char *addrs;
+
+ WARN_ON(dev->mc_count == 0);
+ WARN_ON(dev->flags & IFF_ALLMULTI);
+
+ bitmask[0] = bitmask[1] = 0;
+ for (mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
+ addrs = mc_ptr->dmi_addr;
+
+ /* skip non-multicast addresses */
+ if (!(*addrs & 1))
+ continue;
+
+ crc = ether_crc_le(ETH_ALEN, addrs);
+ set_bit(crc >> 26, bitmask);
+ }
+}
+
+static void vmac_set_multicast_list(struct net_device *dev)
+{
+ struct vmac_priv *ap = netdev_priv(dev);
+ unsigned long flags, bitmask[2];
+ int promisc, reg;
+
+ spin_lock_irqsave(&ap->lock, flags);
+
+ promisc = !!(dev->flags & IFF_PROMISC);
+ reg = vmac_readl(ap, ENABLE);
+ if (promisc != !!(reg & PROM_MASK)) {
+ reg ^= PROM_MASK;
+ vmac_writel(ap, reg, ENABLE);
+ }
+
+ if (dev->flags & IFF_ALLMULTI)
+ memset(bitmask, 1, sizeof(bitmask));
+ else if (dev->mc_count == 0)
+ memset(bitmask, 0, sizeof(bitmask));
+ else
+ create_multicast_filter(dev, bitmask);
+
+ vmac_writel(ap, bitmask[0], LAFL);
+ vmac_writel(ap, bitmask[1], LAFH);
+
+ spin_unlock_irqrestore(&ap->lock, flags);
+}
+
+static struct ethtool_ops vmac_ethtool_ops = {
+ .get_settings = vmacether_get_settings,
+ .set_settings = vmacether_set_settings,
+ .get_drvinfo = vmacether_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static const struct net_device_ops vmac_netdev_ops = {
+ .ndo_open = vmac_open,
+ .ndo_stop = vmac_close,
+ .ndo_get_stats = vmac_stats,
+ .ndo_start_xmit = vmac_start_xmit,
+ .ndo_do_ioctl = vmac_ioctl,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_tx_timeout = vmac_tx_timeout,
+ .ndo_set_multicast_list = vmac_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int __devinit vmac_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct vmac_priv *ap;
+ struct resource *res;
+ unsigned int mem_base, mem_size, irq;
+ int err;
+
+ dev = alloc_etherdev(sizeof(*ap));
+ if (!dev) {
+ dev_err(&pdev->dev, "etherdev alloc failed, aborting.\n");
+ return -ENOMEM;
+ }
+
+ ap = netdev_priv(dev);
+
+ err = -ENODEV;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mmio resource defined\n");
+ goto err_out;
+ }
+ mem_base = res->start;
+ mem_size = resource_size(res);
+ irq = platform_get_irq(pdev, 0);
+
+ err = -EBUSY;
+ if (!request_mem_region(mem_base, mem_size, DRV_NAME)) {
+ dev_err(&pdev->dev, "no memory region available\n");
+ goto err_out;
+ }
+
+ err = -ENOMEM;
+ ap->regs = ioremap(mem_base, mem_size);
+ if (!ap->regs) {
+ dev_err(&pdev->dev, "failed to map registers, aborting.\n");
+ goto err_out_release_mem;
+ }
+
+ /* no checksum support, hence no scatter/gather */
+ dev->features |= NETIF_F_HIGHDMA;
+
+ spin_lock_init(&ap->lock);
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ ap->dev = dev;
+ ap->pdev = pdev;
+
+ /* init rx timeout (used for oom) */
+ init_timer(&ap->rx_timeout);
+ ap->rx_timeout.function = vmac_refill_rx_timer;
+ ap->rx_timeout.data = (unsigned long)dev;
+
+ netif_napi_add(dev, &ap->napi, vmac_poll, 2);
+ dev->netdev_ops = &vmac_netdev_ops;
+ dev->ethtool_ops = &vmac_ethtool_ops;
+ dev->irq = irq;
+
+ dev->flags |= IFF_MULTICAST;
+
+ dev->base_addr = (unsigned long)ap->regs;
+ ap->mem_base = mem_base;
+
+ /* prevent buffer chaining, favor speed over space */
+ ap->rx_skb_size = ETH_FRAME_LEN + VMAC_BUFFER_PAD;
+
+ /* private struct functional */
+
+ /* mac address intialize, set vmac_open */
+ read_mac_reg(dev, dev->dev_addr);
+
+ if (!is_valid_ether_addr(dev->dev_addr))
+ random_ether_addr(dev->dev_addr);
+ if (mac_addr != NULL) {
+ /* overridde mac address by module parameter */
+ parse_mac_addr_param(dev, mac_addr);
+ }
+
+ err = register_netdev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
+ goto err_out_iounmap;
+ }
+
+ dev_info(&pdev->dev, "ARC VMAC at 0x%08x irq %d %pM\n", mem_base,
+ dev->irq, dev->dev_addr);
+ platform_set_drvdata(pdev, dev);
+
+ return 0;
+
+err_out_iounmap:
+ iounmap(ap->regs);
+err_out_release_mem:
+ release_mem_region(mem_base, mem_size);
+err_out:
+ free_netdev(dev);
+ return err;
+}
+
+static int __devexit vmac_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct vmac_priv *ap;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ if (!dev) {
+ dev_err(&pdev->dev, "%s no valid dev found\n", __func__);
+ return 0;
+ }
+
+ ap = netdev_priv(dev);
+
+ /* MAC */
+ unregister_netdev(dev);
+ iounmap(ap->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver arcvmac_driver = {
+ .probe = vmac_probe,
+ .remove = __devexit_p(vmac_remove),
+ .driver = {
+ .name = "arcvmac",
+ },
+};
+
+static int __init vmac_init(void)
+{
+ return platform_driver_register(&arcvmac_driver);
+}
+
+static void __exit vmac_exit(void)
+{
+ platform_driver_unregister(&arcvmac_driver);
+}
+
+module_init(vmac_init);
+module_exit(vmac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARC VMAC Ethernet driver");
+MODULE_AUTHOR("amit.bhor@celunite.com, sameer.dhavale@celunite.com");
diff --git a/drivers/net/arcvmac.h b/drivers/net/arcvmac.h
new file mode 100644
index 0000000..44c0587
--- /dev/null
+++ b/drivers/net/arcvmac.h
@@ -0,0 +1,268 @@
+/*
+ * linux/arch/arc/drivers/arcvmac.h
+ *
+ * Copyright (C) 2003-2006 Codito Technologies, for linux-2.4 port
+ * Copyright (C) 2006-2007 Celunite Inc, for linux-2.6 port
+ * Copyright (C) 2007-2008 Sagem Communications, Fehmi HAFSI
+ * Copyright (C) 2009 Sagem Communications, Andreas Fenkart
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors: amit.bhor@celunite.com, sameer.dhavale@celunite.com
+ */
+
+#ifndef _ARCVMAC_H
+#define _ARCVMAC_H
+
+#define DRV_NAME "arcvmac"
+#define DRV_VERSION "1.0"
+
+/* Buffer descriptors */
+#define TX_BDT_LEN 16 /* Number of receive BD's */
+#define RX_BDT_LEN 256 /* Number of transmit BD's */
+
+/* BD poll rate, in 1024 cycles. @100Mhz: x * 1024 cy * 10ns = 1ms */
+#define POLLRATE_TIME 200
+
+/* next power of two, bigger than ETH_FRAME_LEN + VLAN */
+#define MAX_RX_BUFFER_LEN 0x800 /* 2^11 = 2048 = 0x800 */
+#define MAX_TX_BUFFER_LEN 0x800 /* 2^11 = 2048 = 0x800 */
+
+/* 14 bytes of ethernet header, 4 bytes VLAN, FCS,
+ * plus extra pad to prevent buffer chaining of
+ * maximum sized ethernet packets (1514 bytes) */
+#define VMAC_BUFFER_PAD (ETH_HLEN + 4 + ETH_FCS_LEN + 4)
+
+/* VMAC register definitions, offsets in the ref manual are in bytes */
+#define ID_OFFSET (0x00/0x4)
+#define STAT_OFFSET (0x04/0x4)
+#define ENABLE_OFFSET (0x08/0x4)
+#define CONTROL_OFFSET (0x0c/0x4)
+#define POLLRATE_OFFSET (0x10/0x4)
+#define RXERR_OFFSET (0x14/0x4)
+#define MISS_OFFSET (0x18/0x4)
+#define TXRINGPTR_OFFSET (0x1c/0x4)
+#define RXRINGPTR_OFFSET (0x20/0x4)
+#define ADDRL_OFFSET (0x24/0x4)
+#define ADDRH_OFFSET (0x28/0x4)
+#define LAFL_OFFSET (0x2c/0x4)
+#define LAFH_OFFSET (0x30/0x4)
+#define MDIO_DATA_OFFSET (0x34/0x4)
+#define MAC_TXRING_HEAD_OFFSET (0x38/0x4)
+#define MAC_RXRING_HEAD_OFFSET (0x3C/0x4)
+
+/* STATUS and ENABLE register bit masks */
+#define TXINT_MASK (1<<0) /* Transmit interrupt */
+#define RXINT_MASK (1<<1) /* Receive interrupt */
+#define ERR_MASK (1<<2) /* Error interrupt */
+#define TXCH_MASK (1<<3) /* Transmit chaining error interrupt */
+#define MSER_MASK (1<<4) /* Missed packet counter error */
+#define RXCR_MASK (1<<8) /* RXCRCERR counter rolled over */
+#define RXFR_MASK (1<<9) /* RXFRAMEERR counter rolled over */
+#define RXFL_MASK (1<<10) /* RXOFLOWERR counter rolled over */
+#define MDIO_MASK (1<<12) /* MDIO complete */
+#define TXPL_MASK (1<<31) /* TXPOLL */
+
+/* CONTROL register bitmasks */
+#define EN_MASK (1<<0) /* VMAC enable */
+#define TXRN_MASK (1<<3) /* TX enable */
+#define RXRN_MASK (1<<4) /* RX enable */
+#define DSBC_MASK (1<<8) /* Disable receive broadcast */
+#define ENFL_MASK (1<<10) /* Enable Full Duplex */
+#define PROM_MASK (1<<11) /* Promiscuous mode */
+
+/* RXERR register bitmasks */
+#define RXERR_CRC 0x000000ff
+#define RXERR_FRM 0x0000ff00
+#define RXERR_OFLO 0x00ff0000 /* fifo overflow */
+
+/* MDIO data register bit masks */
+#define MDIO_SFD 0xC0000000
+#define MDIO_OP 0x30000000
+#define MDIO_ID_MASK 0x0F800000
+#define MDIO_REG_MASK 0x007C0000
+#define MDIO_TA 0x00030000
+#define MDIO_DATA_MASK 0x0000FFFF
+
+#define MDIO_BASE 0x40020000
+#define MDIO_OP_READ 0x20000000
+#define MDIO_OP_WRITE 0x10000000
+
+/* Buffer descriptor INFO bit masks */
+#define OWN_MASK (1<<31) /* ownership of buffer, 0 CPU, 1 DMA */
+#define BUFF (1<<30) /* buffer invalid, rx */
+#define UFLO (1<<29) /* underflow, tx */
+#define LTCL (1<<28) /* late collision, tx */
+#define RETRY_CT (0xf<<24) /* tx */
+#define DROP (1<<23) /* drop, more than 16 retries, tx */
+#define DEFER (1<<22) /* traffic on the wire, tx */
+#define CARLOSS (1<<21) /* carrier loss while transmission, tx, rx? */
+/* 20:19 reserved */
+#define ADCR (1<<18) /* add crc, ignored if not disaddcrc */
+#define LAST_MASK (1<<17) /* Last buffer in chain */
+#define FRST_MASK (1<<16) /* First buffer in chain */
+/* 15:11 reserved */
+#define LEN_MASK 0x000007FF
+
+#define ERR_MSK_TX 0x3fe00000 /* UFLO | LTCL | RTRY | DROP | DEFER | CRLS */
+
+
+/* arcvmac private data structures */
+struct vmac_buffer_desc {
+ unsigned int info;
+ dma_addr_t data;
+};
+
+struct dma_fifo {
+ int head; /* head */
+ int tail; /* tail */
+ int size;
+};
+
+struct vmac_priv {
+ struct net_device *dev;
+ struct platform_device *pdev;
+ struct net_device_stats stats;
+
+ spinlock_t lock; /* TODO revisit */
+ struct completion mdio_complete;
+
+ /* base address of register set */
+ int *regs;
+ unsigned int mem_base;
+
+ /* DMA ring buffers */
+ struct vmac_buffer_desc *rxbd;
+ dma_addr_t rxbd_dma;
+
+ struct vmac_buffer_desc *txbd;
+ dma_addr_t txbd_dma;
+
+ /* socket buffers */
+ struct sk_buff *rx_skbuff[RX_BDT_LEN];
+ struct sk_buff *tx_skbuff[TX_BDT_LEN];
+ int rx_skb_size;
+
+ /* skb / dma desc managing */
+ struct dma_fifo rx_ring;
+ struct dma_fifo tx_ring;
+
+ /* descriptor last polled/processed by the VMAC */
+ unsigned long mac_rxring_head;
+ /* used when rx skb allocation failed, so we defer rx queue
+ * refill */
+ struct timer_list rx_timeout;
+
+ /* lock rx_timeout against rx normal operation */
+ spinlock_t rx_lock;
+
+ struct napi_struct napi;
+
+ /* rx buffer chaining */
+ int rx_merge_error;
+ int tx_timeout_error;
+
+ /* PHY stuff */
+ struct mii_bus *mii_bus;
+ struct phy_device *phy_dev;
+
+ int link;
+ int speed;
+ int duplex;
+
+ /* debug */
+ int shutdown;
+};
+
+/* DMA ring management */
+
+/* for a fifo with size n,
+ * - [0..n] fill levels are n + 1 states
+ * - there are only n different deltas (head - tail) values
+ * => not all fill levels can be represented with head, tail
+ * pointers only
+ * we give up the n fill level, aka fifo full */
+
+/* sacrifice one elt as a sentinel */
+static inline int fifo_used(struct dma_fifo *f);
+static inline int fifo_inc_ct(int ct, int size);
+static inline void fifo_dump(struct dma_fifo *fifo);
+
+static inline int fifo_empty(struct dma_fifo *f)
+{
+ return (f->head == f->tail);
+}
+
+static inline int fifo_free(struct dma_fifo *f)
+{
+ int free;
+
+ free = f->tail - f->head;
+ if (free <= 0)
+ free += f->size;
+
+ return free;
+}
+
+static inline int fifo_used(struct dma_fifo *f)
+{
+ int used;
+
+ used = f->head - f->tail;
+ if (used < 0)
+ used += f->size;
+
+ return used;
+}
+
+static inline int fifo_full(struct dma_fifo *f)
+{
+ return (fifo_used(f) + 1) == f->size;
+}
+
+/* manipulate */
+static inline void fifo_init(struct dma_fifo *fifo, int size)
+{
+ fifo->size = size;
+ fifo->head = fifo->tail = 0; /* empty */
+}
+
+static inline void fifo_inc_head(struct dma_fifo *fifo)
+{
+ BUG_ON(fifo_full(fifo));
+ fifo->head = fifo_inc_ct(fifo->head, fifo->size);
+}
+
+static inline void fifo_inc_tail(struct dma_fifo *fifo)
+{
+ BUG_ON(fifo_empty(fifo));
+ fifo->tail = fifo_inc_ct(fifo->tail, fifo->size);
+}
+
+/* internal funcs */
+static inline void fifo_dump(struct dma_fifo *fifo)
+{
+ printk(KERN_INFO "fifo: head %d, tail %d, size %d\n", fifo->head,
+ fifo->tail,
+ fifo->size);
+}
+
+static inline int fifo_inc_ct(int ct, int size)
+{
+ return (++ct == size) ? 0 : ct;
+}
+
+#endif /* _ARCVMAC_H */
--
1.6.6.1
^ permalink raw reply related
* [PATCH 0/5] ARC vmac ethernet driver.
From: Andreas Fenkart @ 2010-03-01 23:18 UTC (permalink / raw)
To: netdev; +Cc: amit.bhor, sameer.dhavale
In-Reply-To: <20100222.154010.149123575.davem@davemloft.net>
This is a resend of previous submission. It contains the following
changes:
* Removed arcvmac_fifo.h, moved content to arcvmac.h
* Replaced BUG_ON with WARN_ON
* Added missing Kconfig entry
^ 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