* Re: [Lsf-pc] [LSF/MM TOPIC] Generic page-pool recycle facility?
From: Christoph Hellwig @ 2016-04-07 14:38 UTC (permalink / raw)
To: Jesper Dangaard Brouer
Cc: lsf, linux-mm, netdev@vger.kernel.org, Brenden Blanco,
James Bottomley, Tom Herbert, lsf-pc, Alexei Starovoitov
In-Reply-To: <20160407161715.52635cac@redhat.com>
This is also very interesting for storage targets, which face the same
issue. SCST has a mode where it caches some fully constructed SGLs,
which is probably very similar to what NICs want to do.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: Boot failure when using NFS on OMAP based evms
From: Willem de Bruijn @ 2016-04-07 14:39 UTC (permalink / raw)
To: Franklin S Cooper Jr.
Cc: Willem de Bruijn, Sam Kumar, Eric Dumazet, tony, mugunthanvnm,
Network Development, LKML, David Miller, Alexey Kuznetsov,
jmorris, yoshfuji, Patrick McHardy, nsekhar, linux-omap
In-Reply-To: <57067089.4080406@ti.com>
On Thu, Apr 7, 2016 at 10:36 AM, Franklin S Cooper Jr. <fcooper@ti.com> wrote:
>
>
> On 04/06/2016 09:22 PM, Willem de Bruijn wrote:
>> On Wed, Apr 6, 2016 at 7:12 PM, Franklin S Cooper Jr. <fcooper@ti.com> wrote:
>>> Hi All,
>>>
>>> Currently linux-next is failing to boot via NFS on my AM335x GP evm,
>>> AM437x GP evm and Beagle X15. I bisected the problem down to the commit
>>> "udp: remove headers from UDP packets before queueing".
>>>
>>> I had to revert the following three commits to get things working again:
>>>
>>> e6afc8ace6dd5cef5e812f26c72579da8806f5ac
>>> udp: remove headers from UDP packets before queueing
>>>
>>> 627d2d6b550094d88f9e518e15967e7bf906ebbf
>>> udp: enable MSG_PEEK at non-zero offset
>>>
>>> b9bb53f3836f4eb2bdeb3447be11042bd29c2408
>>> sock: convert sk_peek_offset functions to WRITE_ONCE
>>>
>>
>> Thanks for the report, and apologies for breaking your configuration.
>> I had missed that sunrpc can dequeue skbs from a udp receive
>> queue and makes assumptions about the layout of those packets. rxrpc
>> does the same. From what I can tell so far, those are the only two
>> protocols that do this. I have verified that the following fixes rxrpc for me
>>
>> --- a/net/rxrpc/ar-input.c
>> +++ b/net/rxrpc/ar-input.c
>> @@ -612,9 +612,9 @@ int rxrpc_extract_header(struct rxrpc_skb_priv
>> *sp, struct sk_buff *skb)
>> struct rxrpc_wire_header whdr;
>>
>> /* dig out the RxRPC connection details */
>> - if (skb_copy_bits(skb, sizeof(struct udphdr), &whdr, sizeof(whdr)) < 0)
>> + if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0)
>> return -EBADMSG;
>> - if (!pskb_pull(skb, sizeof(struct udphdr) + sizeof(whdr)))
>> + if (!pskb_pull(skb, sizeof(whdr)))
>> BUG();
>>
>> I have not yet been able to reproduce the sunrpc/nfs issue, but I
>> suspect that the following might fix it. I will try to create an NFS
>> setup.
>>
>> diff --git a/net/sunrpc/socklib.c b/net/sunrpc/socklib.c
>> index 2df87f7..8ab40ba 100644
>> --- a/net/sunrpc/socklib.c
>> +++ b/net/sunrpc/socklib.c
>> @@ -155,7 +155,7 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr,
>> struct sk_buff *skb)
>> struct xdr_skb_reader desc;
>>
>> desc.skb = skb;
>> - desc.offset = sizeof(struct udphdr);
>> + desc.offset = 0;
>> desc.count = skb->len - desc.offset;
>>
>> if (skb_csum_unnecessary(skb))
>> diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
>> index 1413cdc..71d6072 100644
>> --- a/net/sunrpc/svcsock.c
>> +++ b/net/sunrpc/svcsock.c
>> @@ -617,7 +617,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
>> svsk->sk_sk->sk_stamp = skb->tstamp;
>> set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be
>> more data... */
>>
>> - len = skb->len - sizeof(struct udphdr);
>> + len = skb->len;
>> rqstp->rq_arg.len = len;
>>
>> rqstp->rq_prot = IPPROTO_UDP;
>> @@ -641,8 +641,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
>> skb_free_datagram_locked(svsk->sk_sk, skb);
>> } else {
>> /* we can use it in-place */
>> - rqstp->rq_arg.head[0].iov_base = skb->data +
>> - sizeof(struct udphdr);
>> + rqstp->rq_arg.head[0].iov_base = skb->data;
>> rqstp->rq_arg.head[0].iov_len = len;
>> if (skb_checksum_complete(skb))
>> goto out_free;
>> diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
>> index 65e7595..c1fc7b2 100644
>> --- a/net/sunrpc/xprtsock.c
>> +++ b/net/sunrpc/xprtsock.c
>> @@ -995,15 +995,14 @@ static void xs_udp_data_read_skb(struct rpc_xprt *xprt,
>> u32 _xid;
>> __be32 *xp;
>>
>> - repsize = skb->len - sizeof(struct udphdr);
>> + repsize = skb->len;
>> if (repsize < 4) {
>> dprintk("RPC: impossible RPC reply size %d!\n", repsize);
>> return;
>> }
>>
>>
>>
>> /* Copy the XID from the skb... */
>> - xp = skb_header_pointer(skb, sizeof(struct udphdr),
>> - sizeof(_xid), &_xid);
>> + xp = skb_header_pointer(skb, 0, sizeof(_xid), &_xid);
>> if (xp == NULL)
>> return;
>>
>
>
> Thank you for your quick response. I verified with all of the above
> suggested changes that NFS works again on my 3 evms.
Thanks a lot for testing, Franklin. I will send out the two patches.
^ permalink raw reply
* [PATCH net 0/2] tipc: name distributor pernet queue
From: Jon Maloy @ 2016-04-07 14:40 UTC (permalink / raw)
To: davem
Cc: netdev, Paul Gortmaker, parthasarathy.bhuvaragan, richard.alpe,
ying.xue, maloy, tipc-discussion, Jon Maloy
Commit #1 fixes a potential issue with deferred binding table
updates being pushed to the wrong namespace.
Commit #2 solves a problem with deferred binding table updates
remaining in the the defer queue after the issuing node has gone
down.
Erik Hugne (2):
tipc: make dist queue pernet
tipc: purge deferred updates from dead nodes
net/tipc/core.c | 1 +
net/tipc/core.h | 3 +++
net/tipc/name_distr.c | 35 ++++++++++++++++++++++++++---------
3 files changed, 30 insertions(+), 9 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH net 1/2] tipc: make dist queue pernet
From: Jon Maloy @ 2016-04-07 14:40 UTC (permalink / raw)
To: davem
Cc: netdev, Paul Gortmaker, parthasarathy.bhuvaragan, richard.alpe,
ying.xue, maloy, tipc-discussion, Erik Hugne, Jon Maloy
In-Reply-To: <1460040044-30770-1-git-send-email-jon.maloy@ericsson.com>
From: Erik Hugne <erik.hugne@gmail.com>
Nametable updates received from the network that cannot be applied
immediately are placed on a defer queue. This queue is global to the
TIPC module, which might cause problems when using TIPC in containers.
To prevent nametable updates from escaping into the wrong namespace,
we make the queue pernet instead.
Signed-off-by: Erik Hugne <erik.hugne@gmail.com>
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
---
net/tipc/core.c | 1 +
net/tipc/core.h | 3 +++
net/tipc/name_distr.c | 16 +++++++---------
3 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/net/tipc/core.c b/net/tipc/core.c
index 03a8428..e2bdb07a 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -69,6 +69,7 @@ static int __net_init tipc_init_net(struct net *net)
if (err)
goto out_nametbl;
+ INIT_LIST_HEAD(&tn->dist_queue);
err = tipc_topsrv_start(net);
if (err)
goto out_subscr;
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 5504d63..eff58dc 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -103,6 +103,9 @@ struct tipc_net {
spinlock_t nametbl_lock;
struct name_table *nametbl;
+ /* Name dist queue */
+ struct list_head dist_queue;
+
/* Topology subscription server */
struct tipc_server *topsrv;
atomic_t subscription_count;
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index ebe9d0f..4f4f581 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -40,11 +40,6 @@
int sysctl_tipc_named_timeout __read_mostly = 2000;
-/**
- * struct tipc_dist_queue - queue holding deferred name table updates
- */
-static struct list_head tipc_dist_queue = LIST_HEAD_INIT(tipc_dist_queue);
-
struct distr_queue_item {
struct distr_item i;
u32 dtype;
@@ -279,9 +274,11 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
* tipc_named_add_backlog - add a failed name table update to the backlog
*
*/
-static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node)
+static void tipc_named_add_backlog(struct net *net, struct distr_item *i,
+ u32 type, u32 node)
{
struct distr_queue_item *e;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
unsigned long now = get_jiffies_64();
e = kzalloc(sizeof(*e), GFP_ATOMIC);
@@ -291,7 +288,7 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node)
e->node = node;
e->expires = now + msecs_to_jiffies(sysctl_tipc_named_timeout);
memcpy(e, i, sizeof(*i));
- list_add_tail(&e->next, &tipc_dist_queue);
+ list_add_tail(&e->next, &tn->dist_queue);
}
/**
@@ -301,10 +298,11 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node)
void tipc_named_process_backlog(struct net *net)
{
struct distr_queue_item *e, *tmp;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
char addr[16];
unsigned long now = get_jiffies_64();
- list_for_each_entry_safe(e, tmp, &tipc_dist_queue, next) {
+ list_for_each_entry_safe(e, tmp, &tn->dist_queue, next) {
if (time_after(e->expires, now)) {
if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype))
continue;
@@ -344,7 +342,7 @@ void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq)
node = msg_orignode(msg);
while (count--) {
if (!tipc_update_nametbl(net, item, node, mtype))
- tipc_named_add_backlog(item, mtype, node);
+ tipc_named_add_backlog(net, item, mtype, node);
item++;
}
kfree_skb(skb);
--
1.9.1
^ permalink raw reply related
* [PATCH net 2/2] tipc: purge deferred updates from dead nodes
From: Jon Maloy @ 2016-04-07 14:40 UTC (permalink / raw)
To: davem
Cc: netdev, Paul Gortmaker, parthasarathy.bhuvaragan, richard.alpe,
ying.xue, maloy, tipc-discussion, Erik Hugne, Jon Maloy
In-Reply-To: <1460040044-30770-1-git-send-email-jon.maloy@ericsson.com>
From: Erik Hugne <erik.hugne@gmail.com>
If a peer node becomes unavailable, in addition to removing the
nametable entries from this node we also need to purge all deferred
updates associated with this node.
Signed-off-by: Erik Hugne <erik.hugne@gmail.com>
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
---
net/tipc/name_distr.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 4f4f581..6b626a6 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -224,12 +224,31 @@ static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr)
kfree_rcu(p, rcu);
}
+/**
+ * tipc_dist_queue_purge - remove deferred updates from a node that went down
+ */
+static void tipc_dist_queue_purge(struct net *net, u32 addr)
+{
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct distr_queue_item *e, *tmp;
+
+ spin_lock_bh(&tn->nametbl_lock);
+ list_for_each_entry_safe(e, tmp, &tn->dist_queue, next) {
+ if (e->node != addr)
+ continue;
+ list_del(&e->next);
+ kfree(e);
+ }
+ spin_unlock_bh(&tn->nametbl_lock);
+}
+
void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr)
{
struct publication *publ, *tmp;
list_for_each_entry_safe(publ, tmp, nsub_list, nodesub_list)
tipc_publ_purge(net, publ, addr);
+ tipc_dist_queue_purge(net, addr);
}
/**
--
1.9.1
^ permalink raw reply related
* Re: Backport Security Fix for CVE-2015-8787 to v4.1
From: Pablo Neira Ayuso @ 2016-04-07 14:46 UTC (permalink / raw)
To: Yuki Machida; +Cc: davem, netdev, kamatam
In-Reply-To: <570600DE.2030001@jp.fujitsu.com>
On Thu, Apr 07, 2016 at 03:40:30PM +0900, Yuki Machida wrote:
> Hi David,
>
> I conformed that a patch of CVE-2015-8787 not applied at v4.1.21.
> Could you please apply a patch for 4.1-stable ?
>
> CVE-2015-8787
> Upstream commit 94f9cd81436c85d8c3a318ba92e236ede73752fc
I'll request again, this time to Sasha Levin to include this in
-stable 4.1.
Thanks.
^ permalink raw reply
* Re: [PATCH RFC] net: decrease the length of backlog queue immediately after it's detached from sk
From: Eric Dumazet @ 2016-04-07 14:51 UTC (permalink / raw)
To: Yang Yingliang; +Cc: netdev, davem, Ding Tianhong
In-Reply-To: <1460024518.6473.389.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2016-04-07 at 03:21 -0700, Eric Dumazet wrote:
> Please do not send patches before really understanding the issue you
> have.
>
> Having a backlog of 12506206 bytes is ridiculous. Dropping packets is
> absolutely fine if this ever happens.
>
> Something is really wrong on your host, or the sender simply does not
> comply with TCP protocol (not caring of receiver window at all)
>
> Since you added a trace of truesize, please also trace skb->len
>
BTW, have you played with /proc/sys/net/ipv4/tcp_adv_win_scale ?
^ permalink raw reply
* [PATCH net] vxlan: synchronously and race-free destruction of vxlan sockets
From: Hannes Frederic Sowa @ 2016-04-07 14:57 UTC (permalink / raw)
To: netdev; +Cc: Jiri Benc
Due to the fact that the udp socket is destructed asynchronously in a
work queue, we have some nondeterministic behavior during shutdown of
vxlan tunnels and creating new ones. Fix this by keeping the destruction
process synchronous in regards to the user space process so IFF_UP can
be reliably set.
udp_tunnel_sock_release destroys vs->sock->sk if reference counter
indicates so. We expect to have the same lifetime of vxlan_sock and
vxlan_sock->sock->sk even in fast paths with only rcu locks held. So
only destruct the whole socket after we can be sure it cannot be found
by searching vxlan_net->sock_list.
Cc: Jiri Benc <jbenc@redhat.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
---
drivers/net/vxlan.c | 20 +++-----------------
include/net/vxlan.h | 2 --
2 files changed, 3 insertions(+), 19 deletions(-)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 1c0fa364323e28..487e48b7a53090 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -98,7 +98,6 @@ struct vxlan_fdb {
/* salt for hash table */
static u32 vxlan_salt __read_mostly;
-static struct workqueue_struct *vxlan_wq;
static inline bool vxlan_collect_metadata(struct vxlan_sock *vs)
{
@@ -1065,7 +1064,9 @@ static void __vxlan_sock_release(struct vxlan_sock *vs)
vxlan_notify_del_rx_port(vs);
spin_unlock(&vn->sock_lock);
- queue_work(vxlan_wq, &vs->del_work);
+ synchronize_rcu();
+ udp_tunnel_sock_release(vs->sock);
+ kfree(vs);
}
static void vxlan_sock_release(struct vxlan_dev *vxlan)
@@ -2574,13 +2575,6 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
};
-static void vxlan_del_work(struct work_struct *work)
-{
- struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
- udp_tunnel_sock_release(vs->sock);
- kfree_rcu(vs, rcu);
-}
-
static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
__be16 port, u32 flags)
{
@@ -2626,8 +2620,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
for (h = 0; h < VNI_HASH_SIZE; ++h)
INIT_HLIST_HEAD(&vs->vni_list[h]);
- INIT_WORK(&vs->del_work, vxlan_del_work);
-
sock = vxlan_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) {
pr_info("Cannot bind port %d, err=%ld\n", ntohs(port),
@@ -3218,10 +3210,6 @@ static int __init vxlan_init_module(void)
{
int rc;
- vxlan_wq = alloc_workqueue("vxlan", 0, 0);
- if (!vxlan_wq)
- return -ENOMEM;
-
get_random_bytes(&vxlan_salt, sizeof(vxlan_salt));
rc = register_pernet_subsys(&vxlan_net_ops);
@@ -3242,7 +3230,6 @@ out3:
out2:
unregister_pernet_subsys(&vxlan_net_ops);
out1:
- destroy_workqueue(vxlan_wq);
return rc;
}
late_initcall(vxlan_init_module);
@@ -3251,7 +3238,6 @@ static void __exit vxlan_cleanup_module(void)
{
rtnl_link_unregister(&vxlan_link_ops);
unregister_netdevice_notifier(&vxlan_notifier_block);
- destroy_workqueue(vxlan_wq);
unregister_pernet_subsys(&vxlan_net_ops);
/* rcu_barrier() is called by netns */
}
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 73ed2e951c020d..2113f808e905a4 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -126,9 +126,7 @@ struct vxlan_metadata {
/* per UDP socket information */
struct vxlan_sock {
struct hlist_node hlist;
- struct work_struct del_work;
struct socket *sock;
- struct rcu_head rcu;
struct hlist_head vni_list[VNI_HASH_SIZE];
atomic_t refcnt;
struct udp_offload udp_offloads;
--
2.5.5
^ permalink raw reply related
* Re: [Lsf] [Lsf-pc] [LSF/MM TOPIC] Generic page-pool recycle facility?
From: Bart Van Assche @ 2016-04-07 15:11 UTC (permalink / raw)
To: Christoph Hellwig, Jesper Dangaard Brouer
Cc: James Bottomley, Tom Herbert, Brenden Blanco,
lsf@lists.linux-foundation.org, linux-mm, netdev@vger.kernel.org,
lsf-pc@lists.linux-foundation.org, Alexei Starovoitov
In-Reply-To: <20160407143854.GA7685@infradead.org>
On 04/07/16 07:38, Christoph Hellwig wrote:
> This is also very interesting for storage targets, which face the same
> issue. SCST has a mode where it caches some fully constructed SGLs,
> which is probably very similar to what NICs want to do.
I think a cached allocator for page sets + the scatterlists that
describe these page sets would not only be useful for SCSI target
implementations but also for the Linux SCSI initiator. Today the scsi-mq
code reserves space in each scsi_cmnd for a scatterlist of
SCSI_MAX_SG_SEGMENTS. If scatterlists would be cached together with page
sets less memory would be needed per scsi_cmnd. See also
scsi_mq_setup_tags() and scsi_alloc_sgtable().
Bart.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: veth regression with "don’t modify ip_summed; doing so treats packets with bad checksums as good."
From: Vijay Pandurangan @ 2016-04-07 15:11 UTC (permalink / raw)
To: Ben Greear; +Cc: Cong Wang, netdev, Evan Jones, Cong Wang
In-Reply-To: <56F5CDE4.7010306@candelatech.com>
On Fri, Mar 25, 2016 at 7:46 PM, Ben Greear <greearb@candelatech.com> wrote:
> A real NIC can either do hardware checksums, or it cannot. If it
> cannot, then the host must do it on the CPU for both transmit and
> receive.
>
> Veth is not a real NIC, and it cannot do hardware checksum offloading.
>
> So, we either lie and pretend it does, or we eat massive amounts
> of CPU usage to calculate and check checksums when sending across
> a veth pair.
>
That's a good point. Does anyone know what the overhead actually is these days?
>>> Any frame sent from a socket can be considered to be a local packet in my
>>> opinion.
>>
>>
>> I'm not sure that's totally right. Your bridge is adding a delay to
>> your packets; it could just as easily be simulating corruption by
>> corrupting 5% of packets going through it. If this change allows
>> corrupt packets to be delivered to an application when they could not
>> be delivered if the packets were routed via physical eths, I think
>> that is a bug.
>
>
> I actually do support corrupting the frame, but what I normally do is
> corrupt the contents
> of the packet, and then recalculate the IP checksum (and TCP if it applies)
> and send it on its way. The receiving NIC and stack will pass the frame up
> to
> the application since the checksums match, and it would be up the
> application
> to deal with it. So, I can easily cause an application to receive corrupted
> frames over physical eths.
>
> I can also corrupt without updating the checksums in case you want to
> test another systems NIC and/or stack.
>
> But, if I am purposely corrupting a frame destined for veth, then the only
> reason
> I would want the stack to check the checksums is if I were testing my own
> stack's checksum logic, and that seems to be a pretty limited use.
In the common case you're 100% right. OTOH, there's something
disconcerting about an abstraction layer lying and behaving
unexpectedly. Most traffic that originates on a machine can have its
checksums safely ignored. Whatever the reason is (maybe, as you say
you're testing checksums – on the other hand maybe there's a bug in
your code somewhere), I really feel like we should try to figure out a
way to ensure that this optimization is at the very least opt-in…
>
>
> Thanks,
> Ben
>
> --
> Ben Greear <greearb@candelatech.com>
> Candela Technologies Inc http://www.candelatech.com
>
^ permalink raw reply
* Re: [LSF/MM TOPIC] Generic page-pool recycle facility?
From: Eric Dumazet @ 2016-04-07 15:18 UTC (permalink / raw)
To: Jesper Dangaard Brouer
Cc: lsf, linux-mm, James Bottomley, netdev@vger.kernel.org,
Tom Herbert, Alexei Starovoitov, Brenden Blanco, lsf-pc
In-Reply-To: <20160407161715.52635cac@redhat.com>
On Thu, 2016-04-07 at 16:17 +0200, Jesper Dangaard Brouer wrote:
> (Topic proposal for MM-summit)
>
> Network Interface Cards (NIC) drivers, and increasing speeds stress
> the page-allocator (and DMA APIs). A number of driver specific
> open-coded approaches exists that work-around these bottlenecks in the
> page allocator and DMA APIs. E.g. open-coded recycle mechanisms, and
> allocating larger pages and handing-out page "fragments".
>
> I'm proposing a generic page-pool recycle facility, that can cover the
> driver use-cases, increase performance and open up for zero-copy RX.
>
>
> The basic performance problem is that pages (containing packets at RX)
> are cycled through the page allocator (freed at TX DMA completion
> time). While a system in a steady state, could avoid calling the page
> allocator, when having a pool of pages equal to the size of the RX
> ring plus the number of outstanding frames in the TX ring (waiting for
> DMA completion).
We certainly used this at Google for quite a while.
The thing is : in steady state, the number of pages being 'in tx queues'
is lower than number of pages that were allocated for RX queues.
The page allocator is hardly hit, once you have big enough RX ring
buffers. (Nothing fancy, simply the default number of slots)
The 'hard coded´ code is quite small actually
if (page_count(page) != 1) {
free the page and allocate another one,
since we are not the exclusive owner.
Prefer __GFP_COLD pages btw.
}
page_ref_inc(page);
Problem of a 'pool' is that it matches a router workload, not host one.
With existing code, new pages are automatically allocated on demand, if
say previous pages are still used by skb stored in sockets receive
queues and consumers are slow to react to the presence of this data.
But in most cases (steady state), the refcount on the page is released
by the application reading the data before the driver cycled through the
RX ring buffer and drivers only increments the page count.
I also played with grouping pages into the same 2MB pages, but got mixed
results.
^ permalink raw reply
* Re: [PATCH net-next V3 00/16] net: fec: cleanup and fixes
From: Troy Kisky @ 2016-04-07 15:35 UTC (permalink / raw)
To: David Miller
Cc: netdev, fugang.duan, lznuaa, fabio.estevam, l.stach, andrew,
tremyfr, gerg, linux-arm-kernel, johannes, stillcompiling,
sergei.shtylyov, arnd
In-Reply-To: <20160406.235804.2223760831702758803.davem@davemloft.net>
On 4/6/2016 8:58 PM, David Miller wrote:
> From: Troy Kisky <troy.kisky@boundarydevices.com>
> Date: Wed, 6 Apr 2016 18:09:17 -0700
>
>> On 4/6/2016 2:20 PM, David Miller wrote:
>>>
>>> This is a way too large patch series.
>>>
>>> Please split it up into smaller, more logical, pieces.
>>>
>>> Thanks.
>>>
>>
>> If you apply the 1st 3 that have been acked, I'll be at 13.
>>
>> Would I then send the next 5 for V4, and when that is applied
>> send another V4 with the next 8 that have been already been acked?
>
> What other reasonable option is there? I can't think of any.
>
A V1 for the next 8 would not be too unreasonable.
^ permalink raw reply
* [PATCH net-next 0/2] fix udp pull header breakage
From: Willem de Bruijn @ 2016-04-07 15:44 UTC (permalink / raw)
To: netdev; +Cc: davem, fcooper, samanthakumar, Willem de Bruijn
From: Willem de Bruijn <willemb@google.com>
Commit e6afc8ace6dd ("udp: remove headers from UDP packets before
queueing") modified udp receive processing to pull headers before
enqueue and to not expect them on dequeue.
The patch missed protocols on top of udp with in-kernel
implementations that have their own skb_recv_datagram calls and
dequeue logic. Modify these datapaths to also no longer expect
a udp header at skb->data.
Sunrpc and rxrpc are the only two protocols that call this
function and contain references to udphr (some others, like tipc,
are based on encap_rcv, which acts before enqueue, before the
the header pull).
Willem de Bruijn (2):
sunrpc: do not pull udp headers on receive
rxrpc: do not pull udp headers on receive
net/rxrpc/ar-input.c | 4 ++--
net/sunrpc/socklib.c | 2 +-
net/sunrpc/svcsock.c | 5 ++---
net/sunrpc/xprtsock.c | 5 ++---
4 files changed, 7 insertions(+), 9 deletions(-)
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply
* [PATCH net-next 1/2] sunrpc: do not pull udp headers on receive
From: Willem de Bruijn @ 2016-04-07 15:44 UTC (permalink / raw)
To: netdev; +Cc: davem, fcooper, samanthakumar, Willem de Bruijn
In-Reply-To: <1460043899-56894-1-git-send-email-willemdebruijn.kernel@gmail.com>
From: Willem de Bruijn <willemb@google.com>
Commit e6afc8ace6dd modified the udp receive path by pulling the udp
header before queuing an skbuff onto the receive queue.
Sunrpc also calls skb_recv_datagram to dequeue an skb from a udp
socket. Modify this receive path to also no longer expect udp
headers.
Fixes: e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")
Reported-by: Franklin S Cooper Jr. <fcooper@ti.com>
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
net/sunrpc/socklib.c | 2 +-
net/sunrpc/svcsock.c | 5 ++---
net/sunrpc/xprtsock.c | 5 ++---
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/net/sunrpc/socklib.c b/net/sunrpc/socklib.c
index 2df87f7..8ab40ba 100644
--- a/net/sunrpc/socklib.c
+++ b/net/sunrpc/socklib.c
@@ -155,7 +155,7 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
struct xdr_skb_reader desc;
desc.skb = skb;
- desc.offset = sizeof(struct udphdr);
+ desc.offset = 0;
desc.count = skb->len - desc.offset;
if (skb_csum_unnecessary(skb))
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 1413cdc..71d6072 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -617,7 +617,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
svsk->sk_sk->sk_stamp = skb->tstamp;
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be more data... */
- len = skb->len - sizeof(struct udphdr);
+ len = skb->len;
rqstp->rq_arg.len = len;
rqstp->rq_prot = IPPROTO_UDP;
@@ -641,8 +641,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
skb_free_datagram_locked(svsk->sk_sk, skb);
} else {
/* we can use it in-place */
- rqstp->rq_arg.head[0].iov_base = skb->data +
- sizeof(struct udphdr);
+ rqstp->rq_arg.head[0].iov_base = skb->data;
rqstp->rq_arg.head[0].iov_len = len;
if (skb_checksum_complete(skb))
goto out_free;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 65e7595..c1fc7b2 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -995,15 +995,14 @@ static void xs_udp_data_read_skb(struct rpc_xprt *xprt,
u32 _xid;
__be32 *xp;
- repsize = skb->len - sizeof(struct udphdr);
+ repsize = skb->len;
if (repsize < 4) {
dprintk("RPC: impossible RPC reply size %d!\n", repsize);
return;
}
/* Copy the XID from the skb... */
- xp = skb_header_pointer(skb, sizeof(struct udphdr),
- sizeof(_xid), &_xid);
+ xp = skb_header_pointer(skb, 0, sizeof(_xid), &_xid);
if (xp == NULL)
return;
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH net-next 2/2] rxrpc: do not pull udp headers on receive
From: Willem de Bruijn @ 2016-04-07 15:44 UTC (permalink / raw)
To: netdev; +Cc: davem, fcooper, samanthakumar, Willem de Bruijn
In-Reply-To: <1460043899-56894-1-git-send-email-willemdebruijn.kernel@gmail.com>
From: Willem de Bruijn <willemb@google.com>
Commit e6afc8ace6dd modified the udp receive path by pulling the udp
header before queuing an skbuff onto the receive queue.
Rxrpc also calls skb_recv_datagram to dequeue an skb from a udp
socket. Modify this receive path to also no longer expect udp
headers.
Fixes: e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
net/rxrpc/ar-input.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c
index 63ed75c..4824a82 100644
--- a/net/rxrpc/ar-input.c
+++ b/net/rxrpc/ar-input.c
@@ -612,9 +612,9 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb)
struct rxrpc_wire_header whdr;
/* dig out the RxRPC connection details */
- if (skb_copy_bits(skb, sizeof(struct udphdr), &whdr, sizeof(whdr)) < 0)
+ if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0)
return -EBADMSG;
- if (!pskb_pull(skb, sizeof(struct udphdr) + sizeof(whdr)))
+ if (!pskb_pull(skb, sizeof(whdr)))
BUG();
memset(sp, 0, sizeof(*sp));
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* Re: [Lsf] [Lsf-pc] [LSF/MM TOPIC] Generic page-pool recycle facility?
From: Chuck Lever @ 2016-04-07 15:48 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jesper Dangaard Brouer, James Bottomley, Tom Herbert,
Brenden Blanco, lsf, linux-mm, netdev@vger.kernel.org, lsf-pc,
Alexei Starovoitov
In-Reply-To: <20160407143854.GA7685@infradead.org>
> On Apr 7, 2016, at 7:38 AM, Christoph Hellwig <hch@infradead.org> wrote:
>
> This is also very interesting for storage targets, which face the same
> issue. SCST has a mode where it caches some fully constructed SGLs,
> which is probably very similar to what NICs want to do.
+1 for NFS server.
--
Chuck Lever
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH net-next V3 05/16] net: fec: reduce interrupts
From: Troy Kisky @ 2016-04-07 15:50 UTC (permalink / raw)
To: David Miller
Cc: netdev, fugang.duan, lznuaa, fabio.estevam, l.stach, andrew,
tremyfr, gerg, linux-arm-kernel, johannes, stillcompiling,
sergei.shtylyov, arnd
In-Reply-To: <20160406.235731.57253886354009409.davem@davemloft.net>
On 4/6/2016 8:57 PM, David Miller wrote:
> From: Troy Kisky <troy.kisky@boundarydevices.com>
> Date: Wed, 6 Apr 2016 17:42:47 -0700
>
>> Sure, that's an easy change. But if a TX int is what caused the
>> interrupt and masks them, and then a RX packet happens before napi
>> runs, do you want the TX serviced 1st, or RX ?
>
> If you properly split your driver up into seperate interrupt/poll
> instances, one for TX one for RX, you wouldn't need to ask me
> that question now would you?
>
> :-)
>
I absolutely claim no ownership :-)
^ permalink raw reply
* Re: [net-next 0/8][pull request] 1GbE Intel Wired LAN Driver Updates 2016-04-06
From: David Miller @ 2016-04-07 15:50 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, nhorman, sassmann, jogreene, john.ronciak
In-Reply-To: <1460003853-133949-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Wed, 6 Apr 2016 21:37:25 -0700
> This series contains updates to e1000, e1000e, igb and Kconfig.
All looks sane, pulled, thanks Jeff.
^ permalink raw reply
* Re: Boot failure when using NFS on OMAP based evms
From: Willem de Bruijn @ 2016-04-07 15:52 UTC (permalink / raw)
To: Willem de Bruijn
Cc: Franklin S Cooper Jr., Sam Kumar, Eric Dumazet, Tony Lindgren,
mugunthanvnm, Network Development, LKML, David Miller,
Alexey Kuznetsov, jmorris, yoshfuji, Patrick McHardy, nsekhar,
linux-omap
In-Reply-To: <CA+FuTSeNJZPN70S_4UWt_68Wjo5Oks0swKRwVdwLLtYvgWDk+A@mail.gmail.com>
>>>> Currently linux-next is failing to boot via NFS on my AM335x GP evm,
>>>> AM437x GP evm and Beagle X15. I bisected the problem down to the commit
>>>> "udp: remove headers from UDP packets before queueing".
>>>
>>> Thanks for the report, and apologies for breaking your configuration.
>>> I had missed that sunrpc can dequeue skbs from a udp receive
>>> queue and makes assumptions about the layout of those packets. rxrpc
>>> does the same. From what I can tell so far, those are the only two
>>> protocols that do this. I have verified that the following fixes rxrpc for me
>>>
>>
>> Thank you for your quick response. I verified with all of the above
>> suggested changes that NFS works again on my 3 evms.
>
> Thanks a lot for testing, Franklin. I will send out the two patches.
Patches sent to netdev. I'll do another scan to verify that there are
no additional protocols that dequeue skbs from udp receive queues
and expect udp headers present.
^ permalink raw reply
* Re: [PATCH net-next] tcp/dccp: fix inet_reuseport_add_sock()
From: David Miller @ 2016-04-07 16:02 UTC (permalink / raw)
To: eric.dumazet; +Cc: dsa, netdev, edumazet
In-Reply-To: <1460005654.6473.377.camel@edumazet-glaptop3.roam.corp.google.com>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 06 Apr 2016 22:07:34 -0700
> From: Eric Dumazet <edumazet@google.com>
>
> David Ahern reported panics in __inet_hash() caused by my recent commit.
>
> The reason is inet_reuseport_add_sock() was still using
> sk_nulls_for_each_rcu() instead of sk_for_each_rcu().
> SO_REUSEPORT enabled listeners were causing an instant crash.
>
> While chasing this bug, I found that I forgot to clear SOCK_RCU_FREE
> flag, as it is inherited from the parent at clone time.
>
> Fixes: 3b24d854cb35 ("tcp/dccp: do not touch listener sk_refcnt under synflood")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: David Ahern <dsa@cumulusnetworks.com>
Applied, thanks Eric.
^ permalink raw reply
* Re: Backport Security Fix for CVE-2015-8787 to v4.1
From: David Miller @ 2016-04-07 16:04 UTC (permalink / raw)
To: machida.yuki; +Cc: netdev, kamatam, pablo
In-Reply-To: <570600DE.2030001@jp.fujitsu.com>
From: Yuki Machida <machida.yuki@jp.fujitsu.com>
Date: Thu, 7 Apr 2016 15:40:30 +0900
> Hi David,
>
> I conformed that a patch of CVE-2015-8787 not applied at v4.1.21.
> Could you please apply a patch for 4.1-stable ?
>
> CVE-2015-8787
> Upstream commit 94f9cd81436c85d8c3a318ba92e236ede73752fc
I do not handle netfilter -stable submissions, the netfilter team does.
^ permalink raw reply
* Re: [PATCH v2] packet: uses kfree_skb() for drops or errors.
From: Willem de Bruijn @ 2016-04-07 16:06 UTC (permalink / raw)
To: Weongyo Jeong; +Cc: Network Development, David S. Miller, Willem de Bruijn
In-Reply-To: <1459977298-16670-1-git-send-email-weongyo.linux@gmail.com>
On Wed, Apr 6, 2016 at 5:14 PM, Weongyo Jeong <weongyo.linux@gmail.com> wrote:
> consume_skb() isn't for drop or error cases
for drop or error -> for error
> that kfree_skb() is more proper
> one. At this patch, it fixed tpacket_rcv() and packet_rcv() to be
> consistent for error or non-error cases letting perf trace its event
> properly.
>
> Signed-off-by: Weongyo Jeong <weongyo.linux@gmail.com>
Don't forget to add the target to your subject line: PATCH net-next v3.
> ---
> net/packet/af_packet.c | 16 ++++++++++++----
> 1 file changed, 12 insertions(+), 4 deletions(-)
>
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 1ecfa71..cd100cf 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -2040,7 +2040,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
> struct sockaddr_ll *sll;
> struct packet_sock *po;
> u8 *skb_head = skb->data;
> - int skb_len = skb->len;
> + int err = 0, skb_len = skb->len;
bool
Otherwise looks good.
^ permalink raw reply
* Re: [Lsf-pc] [Lsf] [LSF/MM TOPIC] Generic page-pool recycle facility?
From: Rik van Riel @ 2016-04-07 16:14 UTC (permalink / raw)
To: Chuck Lever, Christoph Hellwig
Cc: lsf, Tom Herbert, Brenden Blanco, James Bottomley, linux-mm,
netdev@vger.kernel.org, Jesper Dangaard Brouer, lsf-pc,
Alexei Starovoitov
In-Reply-To: <2816CC0C-686E-43CA-8689-027085255703@oracle.com>
[-- Attachment #1: Type: text/plain, Size: 655 bytes --]
On Thu, 2016-04-07 at 08:48 -0700, Chuck Lever wrote:
> >
> > On Apr 7, 2016, at 7:38 AM, Christoph Hellwig <hch@infradead.org>
> > wrote:
> >
> > This is also very interesting for storage targets, which face the
> > same
> > issue. SCST has a mode where it caches some fully constructed
> > SGLs,
> > which is probably very similar to what NICs want to do.
> +1 for NFS server.
I have swapped around my slot (into the MM track)
with Jesper's slot (now a plenary session), since
there seems to be a fair amount of interest in
Jesper's proposal from IO and FS people, and my
topic is more MM specific.
--
All Rights Reversed.
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* [PATCH v2 1/5] net: w5100: move mmiowb into register access callbacks
From: Akinobu Mita @ 2016-04-07 16:16 UTC (permalink / raw)
To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller
Instead of sprinkle mmiowb over the driver code, move it into primary
register write callbacks. (w5100_write, w5100_write16, w5100_writebuf)
This is a preparation for supporting SPI interface which doesn't use
MMIO for accessing w5100 registers.
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
* No changes from v1
drivers/net/ethernet/wiznet/w5100.c | 44 +++++++++++++------------------------
1 file changed, 15 insertions(+), 29 deletions(-)
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 8b282d0..f4b7200 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -122,10 +122,17 @@ static inline u8 w5100_read_direct(struct w5100_priv *priv, u16 addr)
return ioread8(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
}
+static inline void __w5100_write_direct(struct w5100_priv *priv, u16 addr,
+ u8 data)
+{
+ iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
static inline void w5100_write_direct(struct w5100_priv *priv,
u16 addr, u8 data)
{
- iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+ __w5100_write_direct(priv, addr, data);
+ mmiowb();
}
static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
@@ -138,8 +145,9 @@ static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
static void w5100_write16_direct(struct w5100_priv *priv, u16 addr, u16 data)
{
- w5100_write_direct(priv, addr, data >> 8);
- w5100_write_direct(priv, addr + 1, data);
+ __w5100_write_direct(priv, addr, data >> 8);
+ __w5100_write_direct(priv, addr + 1, data);
+ mmiowb();
}
static void w5100_readbuf_direct(struct w5100_priv *priv,
@@ -164,8 +172,9 @@ static void w5100_writebuf_direct(struct w5100_priv *priv,
for (i = 0; i < len; i++, addr++) {
if (unlikely(addr > W5100_TX_MEM_END))
addr = W5100_TX_MEM_START;
- w5100_write_direct(priv, addr, *buf++);
+ __w5100_write_direct(priv, addr, *buf++);
}
+ mmiowb();
}
/*
@@ -186,7 +195,6 @@ static u8 w5100_read_indirect(struct w5100_priv *priv, u16 addr)
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
data = w5100_read_direct(priv, W5100_IDM_DR);
spin_unlock_irqrestore(&priv->reg_lock, flags);
@@ -199,9 +207,7 @@ static void w5100_write_indirect(struct w5100_priv *priv, u16 addr, u8 data)
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
w5100_write_direct(priv, W5100_IDM_DR, data);
- mmiowb();
spin_unlock_irqrestore(&priv->reg_lock, flags);
}
@@ -212,7 +218,6 @@ static u16 w5100_read16_indirect(struct w5100_priv *priv, u16 addr)
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
data = w5100_read_direct(priv, W5100_IDM_DR) << 8;
data |= w5100_read_direct(priv, W5100_IDM_DR);
spin_unlock_irqrestore(&priv->reg_lock, flags);
@@ -226,10 +231,8 @@ static void w5100_write16_indirect(struct w5100_priv *priv, u16 addr, u16 data)
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
- w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
+ __w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
w5100_write_direct(priv, W5100_IDM_DR, data);
- mmiowb();
spin_unlock_irqrestore(&priv->reg_lock, flags);
}
@@ -242,13 +245,11 @@ static void w5100_readbuf_indirect(struct w5100_priv *priv,
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
for (i = 0; i < len; i++, addr++) {
if (unlikely(addr > W5100_RX_MEM_END)) {
addr = W5100_RX_MEM_START;
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
}
*buf++ = w5100_read_direct(priv, W5100_IDM_DR);
}
@@ -265,15 +266,13 @@ static void w5100_writebuf_indirect(struct w5100_priv *priv,
spin_lock_irqsave(&priv->reg_lock, flags);
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
for (i = 0; i < len; i++, addr++) {
if (unlikely(addr > W5100_TX_MEM_END)) {
addr = W5100_TX_MEM_START;
w5100_write16_direct(priv, W5100_IDM_AR, addr);
- mmiowb();
}
- w5100_write_direct(priv, W5100_IDM_DR, *buf++);
+ __w5100_write_direct(priv, W5100_IDM_DR, *buf++);
}
mmiowb();
spin_unlock_irqrestore(&priv->reg_lock, flags);
@@ -309,7 +308,6 @@ static int w5100_command(struct w5100_priv *priv, u16 cmd)
unsigned long timeout = jiffies + msecs_to_jiffies(100);
w5100_write(priv, W5100_S0_CR, cmd);
- mmiowb();
while (w5100_read(priv, W5100_S0_CR) != 0) {
if (time_after(jiffies, timeout))
@@ -327,18 +325,15 @@ static void w5100_write_macaddr(struct w5100_priv *priv)
for (i = 0; i < ETH_ALEN; i++)
w5100_write(priv, W5100_SHAR + i, ndev->dev_addr[i]);
- mmiowb();
}
static void w5100_hw_reset(struct w5100_priv *priv)
{
w5100_write_direct(priv, W5100_MR, MR_RST);
- mmiowb();
mdelay(5);
w5100_write_direct(priv, W5100_MR, priv->indirect ?
MR_PB | MR_AI | MR_IND :
MR_PB);
- mmiowb();
w5100_write(priv, W5100_IMR, 0);
w5100_write_macaddr(priv);
@@ -347,23 +342,19 @@ static void w5100_hw_reset(struct w5100_priv *priv)
*/
w5100_write(priv, W5100_RMSR, 0x03);
w5100_write(priv, W5100_TMSR, 0x03);
- mmiowb();
}
static void w5100_hw_start(struct w5100_priv *priv)
{
w5100_write(priv, W5100_S0_MR, priv->promisc ?
S0_MR_MACRAW : S0_MR_MACRAW_MF);
- mmiowb();
w5100_command(priv, S0_CR_OPEN);
w5100_write(priv, W5100_IMR, IR_S0);
- mmiowb();
}
static void w5100_hw_close(struct w5100_priv *priv)
{
w5100_write(priv, W5100_IMR, 0);
- mmiowb();
w5100_command(priv, S0_CR_CLOSE);
}
@@ -447,7 +438,6 @@ static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
offset = w5100_read16(priv, W5100_S0_TX_WR);
w5100_writebuf(priv, offset, skb->data, skb->len);
w5100_write16(priv, W5100_S0_TX_WR, offset + skb->len);
- mmiowb();
ndev->stats.tx_bytes += skb->len;
ndev->stats.tx_packets++;
dev_kfree_skb(skb);
@@ -488,7 +478,6 @@ static int w5100_napi_poll(struct napi_struct *napi, int budget)
skb_put(skb, rx_len);
w5100_readbuf(priv, offset + 2, skb->data, rx_len);
w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
- mmiowb();
w5100_command(priv, S0_CR_RECV);
skb->protocol = eth_type_trans(skb, ndev);
@@ -500,7 +489,6 @@ static int w5100_napi_poll(struct napi_struct *napi, int budget)
if (rx_count < budget) {
napi_complete(napi);
w5100_write(priv, W5100_IMR, IR_S0);
- mmiowb();
}
return rx_count;
@@ -515,7 +503,6 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
if (!ir)
return IRQ_NONE;
w5100_write(priv, W5100_S0_IR, ir);
- mmiowb();
if (ir & S0_IR_SENDOK) {
netif_dbg(priv, tx_done, ndev, "tx done\n");
@@ -525,7 +512,6 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
if (ir & S0_IR_RECV) {
if (napi_schedule_prep(&priv->napi)) {
w5100_write(priv, W5100_IMR, 0);
- mmiowb();
__napi_schedule(&priv->napi);
}
}
--
2.5.0
^ permalink raw reply related
* [PATCH v2 2/5] net: w5100: add ability to support other bus interface
From: Akinobu Mita @ 2016-04-07 16:16 UTC (permalink / raw)
To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller
In-Reply-To: <1460045792-30594-1-git-send-email-akinobu.mita@gmail.com>
The w5100 driver currently only supports direct and indirect bus
interface mode which use MMIO space for accessing w5100 registers.
In order to support SPI interface mode which is supported by W5100 chip,
this makes the bus interface abstraction layer more generic so that
separated w5100-spi driver can use w5100 driver as core module.
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
* No changes from v1
drivers/net/ethernet/wiznet/w5100.c | 588 ++++++++++++++++++++++++------------
drivers/net/ethernet/wiznet/w5100.h | 26 ++
2 files changed, 415 insertions(+), 199 deletions(-)
create mode 100644 drivers/net/ethernet/wiznet/w5100.h
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index f4b7200..7290096 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -27,6 +27,8 @@
#include <linux/irq.h>
#include <linux/gpio.h>
+#include "w5100.h"
+
#define DRV_NAME "w5100"
#define DRV_VERSION "2012-04-04"
@@ -76,25 +78,21 @@ MODULE_LICENSE("GPL");
#define W5100_S0_REGS_LEN 0x0040
#define W5100_TX_MEM_START 0x4000
-#define W5100_TX_MEM_END 0x5fff
-#define W5100_TX_MEM_MASK 0x1fff
+#define W5100_TX_MEM_SIZE 0x2000
#define W5100_RX_MEM_START 0x6000
-#define W5100_RX_MEM_END 0x7fff
-#define W5100_RX_MEM_MASK 0x1fff
+#define W5100_RX_MEM_SIZE 0x2000
/*
* Device driver private data structure
*/
-struct w5100_priv {
+
+struct w5100_mmio_priv {
void __iomem *base;
spinlock_t reg_lock;
- bool indirect;
- u8 (*read)(struct w5100_priv *priv, u16 addr);
- void (*write)(struct w5100_priv *priv, u16 addr, u8 data);
- u16 (*read16)(struct w5100_priv *priv, u16 addr);
- void (*write16)(struct w5100_priv *priv, u16 addr, u16 data);
- void (*readbuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
- void (*writebuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
+};
+
+struct w5100_priv {
+ const struct w5100_ops *ops;
int irq;
int link_irq;
int link_gpio;
@@ -103,6 +101,8 @@ struct w5100_priv {
struct net_device *ndev;
bool promisc;
u32 msg_enable;
+
+ struct w5100_mmio_priv mmio_priv;
};
/************************************************************************
@@ -111,72 +111,117 @@ struct w5100_priv {
*
***********************************************************************/
+static inline struct w5100_mmio_priv *to_w5100_mmio_priv(struct net_device *dev)
+{
+ struct w5100_priv *priv = netdev_priv(dev);
+
+ return &priv->mmio_priv;
+}
+
+static inline void __iomem *w5100_mmio(struct net_device *ndev)
+{
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
+
+ return mmio_priv->base;
+}
+
/*
* In direct address mode host system can directly access W5100 registers
* after mapping to Memory-Mapped I/O space.
*
* 0x8000 bytes are required for memory space.
*/
-static inline u8 w5100_read_direct(struct w5100_priv *priv, u16 addr)
+static inline int w5100_read_direct(struct net_device *ndev, u16 addr)
{
- return ioread8(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+ return ioread8(w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
}
-static inline void __w5100_write_direct(struct w5100_priv *priv, u16 addr,
- u8 data)
+static inline int __w5100_write_direct(struct net_device *ndev, u16 addr,
+ u8 data)
{
- iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+ iowrite8(data, w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
+
+ return 0;
}
-static inline void w5100_write_direct(struct w5100_priv *priv,
- u16 addr, u8 data)
+static inline int w5100_write_direct(struct net_device *ndev, u16 addr, u8 data)
{
- __w5100_write_direct(priv, addr, data);
+ __w5100_write_direct(ndev, addr, data);
mmiowb();
+
+ return 0;
}
-static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
+static int w5100_read16_direct(struct net_device *ndev, u16 addr)
{
u16 data;
- data = w5100_read_direct(priv, addr) << 8;
- data |= w5100_read_direct(priv, addr + 1);
+ data = w5100_read_direct(ndev, addr) << 8;
+ data |= w5100_read_direct(ndev, addr + 1);
return data;
}
-static void w5100_write16_direct(struct w5100_priv *priv, u16 addr, u16 data)
+static int w5100_write16_direct(struct net_device *ndev, u16 addr, u16 data)
{
- __w5100_write_direct(priv, addr, data >> 8);
- __w5100_write_direct(priv, addr + 1, data);
+ __w5100_write_direct(ndev, addr, data >> 8);
+ __w5100_write_direct(ndev, addr + 1, data);
mmiowb();
+
+ return 0;
}
-static void w5100_readbuf_direct(struct w5100_priv *priv,
- u16 offset, u8 *buf, int len)
+static int w5100_readbulk_direct(struct net_device *ndev, u16 addr, u8 *buf,
+ int len)
{
- u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
int i;
- for (i = 0; i < len; i++, addr++) {
- if (unlikely(addr > W5100_RX_MEM_END))
- addr = W5100_RX_MEM_START;
- *buf++ = w5100_read_direct(priv, addr);
- }
+ for (i = 0; i < len; i++, addr++)
+ *buf++ = w5100_read_direct(ndev, addr);
+
+ return 0;
}
-static void w5100_writebuf_direct(struct w5100_priv *priv,
- u16 offset, u8 *buf, int len)
+static int w5100_writebulk_direct(struct net_device *ndev, u16 addr,
+ const u8 *buf, int len)
{
- u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
int i;
- for (i = 0; i < len; i++, addr++) {
- if (unlikely(addr > W5100_TX_MEM_END))
- addr = W5100_TX_MEM_START;
- __w5100_write_direct(priv, addr, *buf++);
- }
+ for (i = 0; i < len; i++, addr++)
+ __w5100_write_direct(ndev, addr, *buf++);
+
mmiowb();
+
+ return 0;
}
+static int w5100_mmio_init(struct net_device *ndev)
+{
+ struct platform_device *pdev = to_platform_device(ndev->dev.parent);
+ struct w5100_priv *priv = netdev_priv(ndev);
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
+ struct resource *mem;
+
+ spin_lock_init(&mmio_priv->reg_lock);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio_priv->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(mmio_priv->base))
+ return PTR_ERR(mmio_priv->base);
+
+ netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, priv->irq);
+
+ return 0;
+}
+
+static const struct w5100_ops w5100_mmio_direct_ops = {
+ .read = w5100_read_direct,
+ .write = w5100_write_direct,
+ .read16 = w5100_read16_direct,
+ .write16 = w5100_write16_direct,
+ .readbulk = w5100_readbulk_direct,
+ .writebulk = w5100_writebulk_direct,
+ .init = w5100_mmio_init,
+};
+
/*
* In indirect address mode host system indirectly accesses registers by
* using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
@@ -188,121 +233,276 @@ static void w5100_writebuf_direct(struct w5100_priv *priv,
#define W5100_IDM_AR 0x01 /* Indirect Mode Address Register */
#define W5100_IDM_DR 0x03 /* Indirect Mode Data Register */
-static u8 w5100_read_indirect(struct w5100_priv *priv, u16 addr)
+static int w5100_read_indirect(struct net_device *ndev, u16 addr)
{
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
u8 data;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- data = w5100_read_direct(priv, W5100_IDM_DR);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+ data = w5100_read_direct(ndev, W5100_IDM_DR);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
return data;
}
-static void w5100_write_indirect(struct w5100_priv *priv, u16 addr, u8 data)
+static int w5100_write_indirect(struct net_device *ndev, u16 addr, u8 data)
{
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- w5100_write_direct(priv, W5100_IDM_DR, data);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+ w5100_write_direct(ndev, W5100_IDM_DR, data);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+ return 0;
}
-static u16 w5100_read16_indirect(struct w5100_priv *priv, u16 addr)
+static int w5100_read16_indirect(struct net_device *ndev, u16 addr)
{
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
u16 data;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- data = w5100_read_direct(priv, W5100_IDM_DR) << 8;
- data |= w5100_read_direct(priv, W5100_IDM_DR);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+ data = w5100_read_direct(ndev, W5100_IDM_DR) << 8;
+ data |= w5100_read_direct(ndev, W5100_IDM_DR);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
return data;
}
-static void w5100_write16_indirect(struct w5100_priv *priv, u16 addr, u16 data)
+static int w5100_write16_indirect(struct net_device *ndev, u16 addr, u16 data)
{
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- __w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
- w5100_write_direct(priv, W5100_IDM_DR, data);
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+ __w5100_write_direct(ndev, W5100_IDM_DR, data >> 8);
+ w5100_write_direct(ndev, W5100_IDM_DR, data);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+ return 0;
}
-static void w5100_readbuf_indirect(struct w5100_priv *priv,
- u16 offset, u8 *buf, int len)
+static int w5100_readbulk_indirect(struct net_device *ndev, u16 addr, u8 *buf,
+ int len)
{
- u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
int i;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+
+ for (i = 0; i < len; i++)
+ *buf++ = w5100_read_direct(ndev, W5100_IDM_DR);
- for (i = 0; i < len; i++, addr++) {
- if (unlikely(addr > W5100_RX_MEM_END)) {
- addr = W5100_RX_MEM_START;
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- }
- *buf++ = w5100_read_direct(priv, W5100_IDM_DR);
- }
mmiowb();
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+ return 0;
}
-static void w5100_writebuf_indirect(struct w5100_priv *priv,
- u16 offset, u8 *buf, int len)
+static int w5100_writebulk_indirect(struct net_device *ndev, u16 addr,
+ const u8 *buf, int len)
{
- u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
+ struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
unsigned long flags;
int i;
- spin_lock_irqsave(&priv->reg_lock, flags);
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+ w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+
+ for (i = 0; i < len; i++)
+ __w5100_write_direct(ndev, W5100_IDM_DR, *buf++);
- for (i = 0; i < len; i++, addr++) {
- if (unlikely(addr > W5100_TX_MEM_END)) {
- addr = W5100_TX_MEM_START;
- w5100_write16_direct(priv, W5100_IDM_AR, addr);
- }
- __w5100_write_direct(priv, W5100_IDM_DR, *buf++);
- }
mmiowb();
- spin_unlock_irqrestore(&priv->reg_lock, flags);
+ spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+ return 0;
}
+static int w5100_reset_indirect(struct net_device *ndev)
+{
+ w5100_write_direct(ndev, W5100_MR, MR_RST);
+ mdelay(5);
+ w5100_write_direct(ndev, W5100_MR, MR_PB | MR_AI | MR_IND);
+
+ return 0;
+}
+
+static const struct w5100_ops w5100_mmio_indirect_ops = {
+ .read = w5100_read_indirect,
+ .write = w5100_write_indirect,
+ .read16 = w5100_read16_indirect,
+ .write16 = w5100_write16_indirect,
+ .readbulk = w5100_readbulk_indirect,
+ .writebulk = w5100_writebulk_indirect,
+ .init = w5100_mmio_init,
+ .reset = w5100_reset_indirect,
+};
+
#if defined(CONFIG_WIZNET_BUS_DIRECT)
-#define w5100_read w5100_read_direct
-#define w5100_write w5100_write_direct
-#define w5100_read16 w5100_read16_direct
-#define w5100_write16 w5100_write16_direct
-#define w5100_readbuf w5100_readbuf_direct
-#define w5100_writebuf w5100_writebuf_direct
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+ return w5100_read_direct(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+ return w5100_write_direct(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+ return w5100_read16_direct(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+ return w5100_write16_direct(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+ return w5100_readbulk_direct(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+ int len)
+{
+ return w5100_writebulk_direct(priv->ndev, addr, buf, len);
+}
#elif defined(CONFIG_WIZNET_BUS_INDIRECT)
-#define w5100_read w5100_read_indirect
-#define w5100_write w5100_write_indirect
-#define w5100_read16 w5100_read16_indirect
-#define w5100_write16 w5100_write16_indirect
-#define w5100_readbuf w5100_readbuf_indirect
-#define w5100_writebuf w5100_writebuf_indirect
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+ return w5100_read_indirect(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+ return w5100_write_indirect(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+ return w5100_read16_indirect(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+ return w5100_write16_indirect(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+ return w5100_readbulk_indirect(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+ int len)
+{
+ return w5100_writebulk_indirect(priv->ndev, addr, buf, len);
+}
#else /* CONFIG_WIZNET_BUS_ANY */
-#define w5100_read priv->read
-#define w5100_write priv->write
-#define w5100_read16 priv->read16
-#define w5100_write16 priv->write16
-#define w5100_readbuf priv->readbuf
-#define w5100_writebuf priv->writebuf
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+ return priv->ops->read(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+ return priv->ops->write(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+ return priv->ops->read16(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+ return priv->ops->write16(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+ return priv->ops->readbulk(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+ int len)
+{
+ return priv->ops->writebulk(priv->ndev, addr, buf, len);
+}
+
#endif
+static int w5100_readbuf(struct w5100_priv *priv, u16 offset, u8 *buf, int len)
+{
+ u16 addr;
+ int remain = 0;
+ int ret;
+
+ offset %= W5100_RX_MEM_SIZE;
+ addr = W5100_RX_MEM_START + offset;
+
+ if (offset + len > W5100_RX_MEM_SIZE) {
+ remain = (offset + len) % W5100_RX_MEM_SIZE;
+ len = W5100_RX_MEM_SIZE - offset;
+ }
+
+ ret = w5100_readbulk(priv, addr, buf, len);
+ if (ret || !remain)
+ return ret;
+
+ return w5100_readbulk(priv, W5100_RX_MEM_START, buf + len, remain);
+}
+
+static int w5100_writebuf(struct w5100_priv *priv, u16 offset, const u8 *buf,
+ int len)
+{
+ u16 addr;
+ int ret;
+ int remain = 0;
+
+ offset %= W5100_TX_MEM_SIZE;
+ addr = W5100_TX_MEM_START + offset;
+
+ if (offset + len > W5100_TX_MEM_SIZE) {
+ remain = (offset + len) % W5100_TX_MEM_SIZE;
+ len = W5100_TX_MEM_SIZE - offset;
+ }
+
+ ret = w5100_writebulk(priv, addr, buf, len);
+ if (ret || !remain)
+ return ret;
+
+ return w5100_writebulk(priv, W5100_TX_MEM_START, buf + len, remain);
+}
+
+static int w5100_reset(struct w5100_priv *priv)
+{
+ if (priv->ops->reset)
+ return priv->ops->reset(priv->ndev);
+
+ w5100_write(priv, W5100_MR, MR_RST);
+ mdelay(5);
+ w5100_write(priv, W5100_MR, MR_PB);
+
+ return 0;
+}
+
static int w5100_command(struct w5100_priv *priv, u16 cmd)
{
unsigned long timeout = jiffies + msecs_to_jiffies(100);
@@ -321,19 +521,14 @@ static int w5100_command(struct w5100_priv *priv, u16 cmd)
static void w5100_write_macaddr(struct w5100_priv *priv)
{
struct net_device *ndev = priv->ndev;
- int i;
- for (i = 0; i < ETH_ALEN; i++)
- w5100_write(priv, W5100_SHAR + i, ndev->dev_addr[i]);
+ w5100_writebulk(priv, W5100_SHAR, ndev->dev_addr, ETH_ALEN);
}
static void w5100_hw_reset(struct w5100_priv *priv)
{
- w5100_write_direct(priv, W5100_MR, MR_RST);
- mdelay(5);
- w5100_write_direct(priv, W5100_MR, priv->indirect ?
- MR_PB | MR_AI | MR_IND :
- MR_PB);
+ w5100_reset(priv);
+
w5100_write(priv, W5100_IMR, 0);
w5100_write_macaddr(priv);
@@ -403,17 +598,14 @@ static int w5100_get_regs_len(struct net_device *ndev)
}
static void w5100_get_regs(struct net_device *ndev,
- struct ethtool_regs *regs, void *_buf)
+ struct ethtool_regs *regs, void *buf)
{
struct w5100_priv *priv = netdev_priv(ndev);
- u8 *buf = _buf;
- u16 i;
regs->version = 1;
- for (i = 0; i < W5100_COMMON_REGS_LEN; i++)
- *buf++ = w5100_read(priv, W5100_COMMON_REGS + i);
- for (i = 0; i < W5100_S0_REGS_LEN; i++)
- *buf++ = w5100_read(priv, W5100_S0_REGS + i);
+ w5100_readbulk(priv, W5100_COMMON_REGS, buf, W5100_COMMON_REGS_LEN);
+ buf += W5100_COMMON_REGS_LEN;
+ w5100_readbulk(priv, W5100_S0_REGS, buf, W5100_S0_REGS_LEN);
}
static void w5100_tx_timeout(struct net_device *ndev)
@@ -606,79 +798,38 @@ static const struct net_device_ops w5100_netdev_ops = {
.ndo_change_mtu = eth_change_mtu,
};
-static int w5100_hw_probe(struct platform_device *pdev)
+static int w5100_mmio_probe(struct platform_device *pdev)
{
struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct w5100_priv *priv = netdev_priv(ndev);
- const char *name = netdev_name(ndev);
+ u8 *mac_addr = NULL;
struct resource *mem;
- int mem_size;
+ const struct w5100_ops *ops;
int irq;
- int ret;
- if (data && is_valid_ether_addr(data->mac_addr)) {
- memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN);
- } else {
- eth_hw_addr_random(ndev);
- }
+ if (data && is_valid_ether_addr(data->mac_addr))
+ mac_addr = data->mac_addr;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
- mem_size = resource_size(mem);
-
- spin_lock_init(&priv->reg_lock);
- priv->indirect = mem_size < W5100_BUS_DIRECT_SIZE;
- if (priv->indirect) {
- priv->read = w5100_read_indirect;
- priv->write = w5100_write_indirect;
- priv->read16 = w5100_read16_indirect;
- priv->write16 = w5100_write16_indirect;
- priv->readbuf = w5100_readbuf_indirect;
- priv->writebuf = w5100_writebuf_indirect;
- } else {
- priv->read = w5100_read_direct;
- priv->write = w5100_write_direct;
- priv->read16 = w5100_read16_direct;
- priv->write16 = w5100_write16_direct;
- priv->readbuf = w5100_readbuf_direct;
- priv->writebuf = w5100_writebuf_direct;
- }
-
- w5100_hw_reset(priv);
- if (w5100_read16(priv, W5100_RTR) != RTR_DEFAULT)
- return -ENODEV;
+ if (resource_size(mem) < W5100_BUS_DIRECT_SIZE)
+ ops = &w5100_mmio_indirect_ops;
+ else
+ ops = &w5100_mmio_direct_ops;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- ret = request_irq(irq, w5100_interrupt,
- IRQ_TYPE_LEVEL_LOW, name, ndev);
- if (ret < 0)
- return ret;
- priv->irq = irq;
- priv->link_gpio = data ? data->link_gpio : -EINVAL;
- if (gpio_is_valid(priv->link_gpio)) {
- char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL);
- if (!link_name)
- return -ENOMEM;
- snprintf(link_name, 16, "%s-link", name);
- priv->link_irq = gpio_to_irq(priv->link_gpio);
- if (request_any_context_irq(priv->link_irq, w5100_detect_link,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- link_name, priv->ndev) < 0)
- priv->link_gpio = -EINVAL;
- }
+ return w5100_probe(&pdev->dev, ops, mac_addr, irq,
+ data ? data->link_gpio : -EINVAL);
+}
- netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq);
- return 0;
+static int w5100_mmio_remove(struct platform_device *pdev)
+{
+ return w5100_remove(&pdev->dev);
}
-static int w5100_probe(struct platform_device *pdev)
+int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
+ int irq, int link_gpio)
{
struct w5100_priv *priv;
struct net_device *ndev;
@@ -687,10 +838,13 @@ static int w5100_probe(struct platform_device *pdev)
ndev = alloc_etherdev(sizeof(*priv));
if (!ndev)
return -ENOMEM;
- SET_NETDEV_DEV(ndev, &pdev->dev);
- platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, dev);
+ dev_set_drvdata(dev, ndev);
priv = netdev_priv(ndev);
priv->ndev = ndev;
+ priv->ops = ops;
+ priv->irq = irq;
+ priv->link_gpio = link_gpio;
ndev->netdev_ops = &w5100_netdev_ops;
ndev->ethtool_ops = &w5100_ethtool_ops;
@@ -706,22 +860,59 @@ static int w5100_probe(struct platform_device *pdev)
if (err < 0)
goto err_register;
- err = w5100_hw_probe(pdev);
- if (err < 0)
- goto err_hw_probe;
+ if (mac_addr)
+ memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+ else
+ eth_hw_addr_random(ndev);
+
+ if (priv->ops->init) {
+ err = priv->ops->init(priv->ndev);
+ if (err)
+ goto err_hw;
+ }
+
+ w5100_hw_reset(priv);
+ if (w5100_read16(priv, W5100_RTR) != RTR_DEFAULT) {
+ err = -ENODEV;
+ goto err_hw;
+ }
+
+ err = request_irq(priv->irq, w5100_interrupt, IRQF_TRIGGER_LOW,
+ netdev_name(ndev), ndev);
+ if (err)
+ goto err_hw;
+
+ if (gpio_is_valid(priv->link_gpio)) {
+ char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL);
+
+ if (!link_name) {
+ err = -ENOMEM;
+ goto err_gpio;
+ }
+ snprintf(link_name, 16, "%s-link", netdev_name(ndev));
+ priv->link_irq = gpio_to_irq(priv->link_gpio);
+ if (request_any_context_irq(priv->link_irq, w5100_detect_link,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ link_name, priv->ndev) < 0)
+ priv->link_gpio = -EINVAL;
+ }
return 0;
-err_hw_probe:
+err_gpio:
+ free_irq(priv->irq, ndev);
+err_hw:
unregister_netdev(ndev);
err_register:
free_netdev(ndev);
return err;
}
+EXPORT_SYMBOL_GPL(w5100_probe);
-static int w5100_remove(struct platform_device *pdev)
+int w5100_remove(struct device *dev)
{
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct w5100_priv *priv = netdev_priv(ndev);
w5100_hw_reset(priv);
@@ -733,12 +924,12 @@ static int w5100_remove(struct platform_device *pdev)
free_netdev(ndev);
return 0;
}
+EXPORT_SYMBOL_GPL(w5100_remove);
#ifdef CONFIG_PM_SLEEP
static int w5100_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct w5100_priv *priv = netdev_priv(ndev);
if (netif_running(ndev)) {
@@ -752,8 +943,7 @@ static int w5100_suspend(struct device *dev)
static int w5100_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct w5100_priv *priv = netdev_priv(ndev);
if (netif_running(ndev)) {
@@ -769,15 +959,15 @@ static int w5100_resume(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
+SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
+EXPORT_SYMBOL_GPL(w5100_pm_ops);
-static struct platform_driver w5100_driver = {
+static struct platform_driver w5100_mmio_driver = {
.driver = {
.name = DRV_NAME,
.pm = &w5100_pm_ops,
},
- .probe = w5100_probe,
- .remove = w5100_remove,
+ .probe = w5100_mmio_probe,
+ .remove = w5100_mmio_remove,
};
-
-module_platform_driver(w5100_driver);
+module_platform_driver(w5100_mmio_driver);
diff --git a/drivers/net/ethernet/wiznet/w5100.h b/drivers/net/ethernet/wiznet/w5100.h
new file mode 100644
index 0000000..368862d
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5100.h
@@ -0,0 +1,26 @@
+/*
+ * Ethernet driver for the WIZnet W5100 chip.
+ *
+ * Copyright (C) 2006-2008 WIZnet Co.,Ltd.
+ * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+struct w5100_ops {
+ int (*read)(struct net_device *ndev, u16 addr);
+ int (*write)(struct net_device *ndev, u16 addr, u8 data);
+ int (*read16)(struct net_device *ndev, u16 addr);
+ int (*write16)(struct net_device *ndev, u16 addr, u16 data);
+ int (*readbulk)(struct net_device *ndev, u16 addr, u8 *buf, int len);
+ int (*writebulk)(struct net_device *ndev, u16 addr, const u8 *buf,
+ int len);
+ int (*reset)(struct net_device *ndev);
+ int (*init)(struct net_device *ndev);
+};
+
+int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
+ int irq, int link_gpio);
+int w5100_remove(struct device *dev);
+
+extern const struct dev_pm_ops w5100_pm_ops;
--
2.5.0
^ permalink raw reply related
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