* Re: [PATCH net] net: arc_emac: replace dev_kfree_skb_irq by dev_consume_skb_irq for drop profiles
From: David Miller @ 2019-02-14 17:43 UTC (permalink / raw)
To: albin_yang; +Cc: netdev, andrew, yang.wei9
In-Reply-To: <1550156010-4458-1-git-send-email-albin_yang@163.com>
From: Yang Wei <albin_yang@163.com>
Date: Thu, 14 Feb 2019 22:53:30 +0800
> From: Yang Wei <yang.wei9@zte.com.cn>
>
> dev_consume_skb_irq() should be called in arc_emac_tx_clean() when
> skb xmit done. It makes drop profiles(dropwatch, perf) more friendly.
>
> Signed-off-by: Yang Wei <yang.wei9@zte.com.cn>
Applied.
^ permalink raw reply
* Re: [PATCH net] net: 3com: replace dev_kfree_skb_irq by dev_consume_skb_irq for drop profiles
From: David Miller @ 2019-02-14 17:43 UTC (permalink / raw)
To: albin_yang; +Cc: netdev, klassert, anna-maria, bigeasy, yang.wei9
In-Reply-To: <1550156114-4518-1-git-send-email-albin_yang@163.com>
From: Yang Wei <albin_yang@163.com>
Date: Thu, 14 Feb 2019 22:55:14 +0800
> From: Yang Wei <yang.wei9@zte.com.cn>
>
> dev_consume_skb_irq() should be called when skb xmit done. It makes
> drop profiles(dropwatch, perf) more friendly.
>
> Signed-off-by: Yang Wei <yang.wei9@zte.com.cn>
Applied.
^ permalink raw reply
* Re: [PATCH net] net: adaptec: starfire: replace dev_kfree_skb_irq by dev_consume_skb_irq for drop profiles
From: David Miller @ 2019-02-14 17:43 UTC (permalink / raw)
To: albin_yang; +Cc: netdev, ionut, yang.wei9
In-Reply-To: <1550156800-4821-1-git-send-email-albin_yang@163.com>
From: Yang Wei <albin_yang@163.com>
Date: Thu, 14 Feb 2019 23:06:40 +0800
> From: Yang Wei <yang.wei9@zte.com.cn>
>
> dev_consume_skb_irq() should be called in intr_handler() when skb
> xmit done. It makes drop profiles(dropwatch, perf) more friendly.
>
> Signed-off-by: Yang Wei <yang.wei9@zte.com.cn>
Applied.
^ permalink raw reply
* Re: [PATCH] net: ethernet: freescale: set FEC ethtool regs version
From: David Miller @ 2019-02-14 17:46 UTC (permalink / raw)
To: vivien.didelot; +Cc: netdev, vivien.didelot, fugang.duan
In-Reply-To: <20190214161536.19646-1-vivien.didelot@gmail.com>
From: Vivien Didelot <vivien.didelot@gmail.com>
Date: Thu, 14 Feb 2019 11:15:35 -0500
> From: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
>
> Currently the ethtool_regs version is set to 0 for FEC devices.
>
> Use this field to store the register dump version exposed by the
> kernel. The choosen version 2 corresponds to the kernel compile test:
>
> #if defined(CONFIG_M523x) || defined(CONFIG_M527x)
> || defined(CONFIG_M528x) || defined(CONFIG_M520x)
> || defined(CONFIG_M532x) || defined(CONFIG_ARM)
> || defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
>
> and version 1 corresponds to the opposite. Binaries of ethtool unaware
> of this version will dump the whole set as usual.
>
> Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
Applied, thanks Vivien.
^ permalink raw reply
* Re: [PATCH iproute2] lib/libnetlink: ensure a minimum of 32KB for the buffer used in rtnl_recvmsg()
From: Phil Sutter @ 2019-02-14 17:47 UTC (permalink / raw)
To: David Ahern
Cc: Michal Kubecek, netdev, Eric Dumazet, Stephen Hemminger,
Eric Dumazet, Hangbin Liu
In-Reply-To: <310f4861-1a65-cdcd-515c-b1f6da04b7d7@gmail.com>
On Thu, Feb 14, 2019 at 10:34:06AM -0700, David Ahern wrote:
> On 2/14/19 6:49 AM, Michal Kubecek wrote:
> > On Tue, Feb 12, 2019 at 07:04:17PM -0700, David Ahern wrote:
> >>
> >> Do we know of any single message sizes > 32k? 2d34851cd341 cites
> >> increasing VF's but at some point there is a limit. If not, the whole
> >> PEEK thing should go away and we just malloc 32k (or 64k) buffers for
> >> each recvmsg.
> >
> > IFLA_VF_LIST is by far the biggest thing I have seen so far. I don't
> > remember exact numbers but the issue with 32KB buffer (for the whole
> > RTM_NELINK message) was encountered by some of our customers with NICs
> > having 120 or 128 VFs.
> >
> > There is a bigger issue with IFLA_VFINFO_LIST, though, as it's an
> > attribute so that netlink limits its size to 64 KB. IIRC with current
> > size of IFLA_VF_INFO this would be reached with 270-280 VFs (I'm sure
> > the number was higer than 256 but not too much higher.)
Using netdevsim, 'ip link show' becomes unusable after enabling more
than 244 VFs. I guess it depends on how much info per VF is available.
> > This would mean unless we let something else grow too much, the whole
> > message shouldn't get much bigger than 64 KB. And if we can find some
> > other solution (e.g. passing VF information in separate messages if
> > client declares support), even 32 KB would be more than enough.
>
> That's what I was asking, thanks. So 32kB today is sufficient, 64kB has
> future buffer. So this whole PEEK and allocate the message size is
> overkill. It could just as easily been bumped from 32kB to 64kB in the
> original patch and been good for a while.
Yes, I think the real problem is how VF-related messages are structured
currently.
Cheers, Phil
^ permalink raw reply
* Re: [PATCH net-next] sfc: ensure recovery after allocation failures
From: David Miller @ 2019-02-14 17:47 UTC (permalink / raw)
To: bkenward; +Cc: linux-net-drivers, netdev, rstonehouse
In-Reply-To: <6beed7df-01e6-a15e-0e19-d40bf0ff945c@solarflare.com>
From: Bert Kenward <bkenward@solarflare.com>
Date: Thu, 14 Feb 2019 17:27:43 +0000
> From: Robert Stonehouse <rstonehouse@solarflare.com>
>
> After failing to allocate a receive buffer the driver may fail to ever
> request additional allocations. EF10 NICs require new receive buffers to
> be pushed in batches of eight or more. The test for whether a slow fill
> should be scheduled failed to take account of this. There is little
> downside to *always* requesting a slow fill if we failed to allocate a
> buffer, so the condition has been removed completely. The timer that
> triggers the request for a refill has also been shortened.
>
> Signed-off-by: Robert Stonehouse <rstonehouse@solarflare.com>
> Signed-off-by: Bert Kenward <bkenward@solarflare.com>
Applied, thanks Bert.
^ permalink raw reply
* Re: [PATCH] NETWORKING: avoid use IPCB in cipso_v4_error
From: Nazarov Sergey @ 2019-02-14 18:00 UTC (permalink / raw)
To: Paul Moore
Cc: netdev@vger.kernel.org, linux-security-module@vger.kernel.org,
davem@davemloft.net, kuznet@ms2.inr.ac.ru,
yoshfuji@linux-ipv6.org
In-Reply-To: <CAHC9VhQ4ooT-_iBoTiYgPV7oBV5RAgN1_HqDS2LGrGBY6erMmQ@mail.gmail.com>
Hi, Paul!
I've found the problem and testing it with some very specific custom lsm module. The test case was simple:
standard TCP/IP client-server application, where server opens CIPSO labeled TCP socket, and client connecting
to this socket with forbidden labels. After several connections kernel crashing with general memory protection or
kernel cache inconsistent error.
I think, the similar behaviour should be with selinux or smack in the same conditions. But I don't know them
so good to reproduce situation.
After applying patch, I haven't kernel crashes.
But now I've made additional checks and found no response icmp packets. The ip_options_compile requires
CAP_NET_RAW capability when CIPSO option compiling, if skb is NULL. I have no other ideas than returning to
the early patch version with ip_options_compile modified. What do you think about that?
14.02.2019, 00:42, "Paul Moore" <paul@paul-moore.com>:
> On Tue, Feb 12, 2019 at 10:10 AM Nazarov Sergey <s-nazarov@yandex.ru> wrote:
>> Since cipso_v4_error might be called from different network stack layers, we can't safely use icmp_send there.
>> icmp_send copies IP options with ip_option_echo, which uses IPCB to take access to IP header compiled data.
>> But after commit 971f10ec ("tcp: better TCP_SKB_CB layout to reduce cache line misses"), IPCB can't be used
>> above IP layer.
>> This patch fixes the problem by creating in cipso_v4_error a local copy of compiled IP options and using it with
>> introduced __icmp_send function. This looks some overloaded, but in quite rare error conditions only.
>>
>> The original discussion is here:
>> https://lore.kernel.org/linux-security-module/16659801547571984@sas1-890ba5c2334a.qloud-c.yandex.net/
>>
>> Signed-off-by: Sergey Nazarov <s-nazarov@yandex.ru>
>> ---
>> include/net/icmp.h | 9 ++++++++-
>> net/ipv4/cipso_ipv4.c | 18 ++++++++++++++++--
>> net/ipv4/icmp.c | 7 ++++---
>> 3 files changed, 28 insertions(+), 6 deletions(-)
>
> Hi Sergey,
>
> Thanks for your work on finding this and putting a fix together. As
> we discussed previously, I think this looks good, but can you describe
> the testing you did to verify that this works correctly?
>
>> diff --git a/include/net/icmp.h b/include/net/icmp.h
>> index 6ac3a5b..e0f709d 100644
>> --- a/include/net/icmp.h
>> +++ b/include/net/icmp.h
>> @@ -22,6 +22,7 @@
>>
>> #include <net/inet_sock.h>
>> #include <net/snmp.h>
>> +#include <net/ip.h>
>>
>> struct icmp_err {
>> int errno;
>> @@ -39,7 +40,13 @@ struct icmp_err {
>> struct sk_buff;
>> struct net;
>>
>> -void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
>> +void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
>> + const struct ip_options *opt);
>> +static inline void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>> +{
>> + __icmp_send(skb_in, type, code, info, &IPCB(skb_in)->opt);
>> +}
>> +
>> int icmp_rcv(struct sk_buff *skb);
>> int icmp_err(struct sk_buff *skb, u32 info);
>> int icmp_init(void);
>> diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
>> index 777fa3b..234d12e 100644
>> --- a/net/ipv4/cipso_ipv4.c
>> +++ b/net/ipv4/cipso_ipv4.c
>> @@ -1735,13 +1735,27 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
>> */
>> void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
>> {
>> + unsigned char optbuf[sizeof(struct ip_options) + 40];
>> + struct ip_options *opt = (struct ip_options *)optbuf;
>> +
>> if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
>> return;
>>
>> + /*
>> + * We might be called above the IP layer,
>> + * so we can not use icmp_send and IPCB here.
>> + */
>> +
>> + memset(opt, 0, sizeof(struct ip_options));
>> + opt->optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
>> + memcpy(opt->__data, (unsigned char *)&(ip_hdr(skb)[1]), opt->optlen);
>> + if (ip_options_compile(dev_net(skb->dev), opt, NULL))
>> + return;
>> +
>> if (gateway)
>> - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
>> + __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0, opt);
>> else
>> - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
>> + __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0, opt);
>> }
>>
>> /**
>> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
>> index 065997f..3f24414 100644
>> --- a/net/ipv4/icmp.c
>> +++ b/net/ipv4/icmp.c
>> @@ -570,7 +570,8 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
>> * MUST reply to only the first fragment.
>> */
>>
>> -void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>> +void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
>> + const struct ip_options *opt)
>> {
>> struct iphdr *iph;
>> int room;
>> @@ -691,7 +692,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>> iph->tos;
>> mark = IP4_REPLY_MARK(net, skb_in->mark);
>>
>> - if (ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in))
>> + if (__ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in, opt))
>> goto out_unlock;
>>
>> @@ -742,7 +743,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>> local_bh_enable();
>> out:;
>> }
>> -EXPORT_SYMBOL(icmp_send);
>> +EXPORT_SYMBOL(__icmp_send);
>>
>> static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
>> --
>
> --
> paul moore
> www.paul-moore.com
^ permalink raw reply
* Re: [net-next] net: stmmac: handle endianness in dwmac4_get_timestamp
From: Florian Fainelli @ 2019-02-14 18:09 UTC (permalink / raw)
To: Alexandre Torgue, Giuseppe Cavallaro, Jose Abreu, davem
Cc: netdev, linux-stm32, linux-arm-kernel, linux-kernel
In-Reply-To: <1550165176-10099-1-git-send-email-alexandre.torgue@st.com>
On 2/14/19 9:26 AM, Alexandre Torgue wrote:
> GMAC IP is little-endian and used on several kind of CPU (big or little
> endian). Main callbacks functions of the stmmac drivers take care about
> it. It was not the case for dwmac4_get_timestamp function.
This is clearly a bugfix, so you should be targeting the 'net' tree and
provide a Fixes: tag so this can be backported to relevant stable kernels.
>
> Signed-off-by: Alexandre Torgue <alexandre.torgue@st.com>
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
> index 20299f6..736e296 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
> @@ -241,15 +241,18 @@ static inline void dwmac4_get_timestamp(void *desc, u32 ats, u64 *ts)
> static int dwmac4_rx_check_timestamp(void *desc)
> {
> struct dma_desc *p = (struct dma_desc *)desc;
> + unsigned int rdes0 = le32_to_cpu(p->des0);
> + unsigned int rdes1 = le32_to_cpu(p->des1);
> + unsigned int rdes3 = le32_to_cpu(p->des3);
> u32 own, ctxt;
> int ret = 1;
>
> - own = p->des3 & RDES3_OWN;
> - ctxt = ((p->des3 & RDES3_CONTEXT_DESCRIPTOR)
> + own = rdes3 & RDES3_OWN;
> + ctxt = ((rdes3 & RDES3_CONTEXT_DESCRIPTOR)
> >> RDES3_CONTEXT_DESCRIPTOR_SHIFT);
>
> if (likely(!own && ctxt)) {
> - if ((p->des0 == 0xffffffff) && (p->des1 == 0xffffffff))
> + if ((rdes0 == 0xffffffff) && (rdes1 == 0xffffffff))
> /* Corrupted value */
> ret = -EINVAL;
> else
>
--
Florian
^ permalink raw reply
* Re: [PATCH bpf-next] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: David Ahern @ 2019-02-14 18:11 UTC (permalink / raw)
To: Peter Oskolkov, Alexei Starovoitov, Daniel Borkmann, netdev
Cc: Peter Oskolkov, Willem de Bruijn
In-Reply-To: <20190214060939.101851-1-posk@google.com>
On 2/13/19 11:09 PM, Peter Oskolkov wrote:
> On error the skb should be freed. Tested with diff/steps
> provided by David Ahern.
>
> Reported-by: David Ahern <dsahern@gmail.com>
> Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c")
> Signed-off-by: Peter Oskolkov <posk@google.com>
> ---
> net/core/lwt_bpf.c | 24 ++++++++++++++++--------
> 1 file changed, 16 insertions(+), 8 deletions(-)
>
> diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
> index 32251f3fcda0..f3273cbb6b22 100644
> --- a/net/core/lwt_bpf.c
> +++ b/net/core/lwt_bpf.c
> @@ -179,18 +179,19 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> struct net_device *l3mdev = l3mdev_master_dev_rcu(skb_dst(skb)->dev);
> int oif = l3mdev ? l3mdev->ifindex : 0;
> struct dst_entry *dst = NULL;
> + int err = -EAFNOSUPPORT;
> struct sock *sk;
> struct net *net;
> bool ipv4;
> - int err;
>
> if (skb->protocol == htons(ETH_P_IP))
> ipv4 = true;
> else if (skb->protocol == htons(ETH_P_IPV6))
> ipv4 = false;
> else
> - return -EAFNOSUPPORT;
> + goto err;
>
> + err = -EINVAL;
> sk = sk_to_full_sk(skb->sk);
> if (sk) {
> if (sk->sk_bound_dev_if)
> @@ -216,7 +217,7 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
>
> rt = ip_route_output_key(net, &fl4);
> if (IS_ERR(rt))
> - return -EINVAL;
> + goto err;
> dst = &rt->dst;
> } else {
> struct ipv6hdr *iph6 = ipv6_hdr(skb);
> @@ -231,12 +232,15 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> fl6.saddr = iph6->saddr;
>
> err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
> - if (err || IS_ERR(dst))
> - return -EINVAL;
> + if (err || IS_ERR(dst)) {
> + err = -EINVAL;
> + goto err;
> + }
> }
> if (unlikely(dst->error)) {
> dst_release(dst);
> - return -EINVAL;
> + err = -EINVAL;
> + goto err;
> }
>
> /* Although skb header was reserved in bpf_lwt_push_ip_encap(), it
EINVAL is a confusing return code; it is not an EINVAL problem, it is a
routing problem:
...
starting egress IPv4 encap test
ping: sendmsg: Invalid argument
FAIL: test_ping: 1
Versus returning the error from the lookup:
...
starting egress IPv4 encap test
ping: sendmsg: No route to host
FAIL: test_ping: 1
diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
index f3273cbb6b22..a1901ba319fc 100644
--- a/net/core/lwt_bpf.c
+++ b/net/core/lwt_bpf.c
@@ -191,7 +191,6 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
else
goto err;
- err = -EINVAL;
sk = sk_to_full_sk(skb->sk);
if (sk) {
if (sk->sk_bound_dev_if)
@@ -216,8 +215,10 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
fl4.saddr = iph->saddr;
rt = ip_route_output_key(net, &fl4);
- if (IS_ERR(rt))
+ if (IS_ERR(rt)) {
+ err = PTR_ERR(rt);
goto err;
+ }
dst = &rt->dst;
} else {
struct ipv6hdr *iph6 = ipv6_hdr(skb);
@@ -232,14 +233,12 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
fl6.saddr = iph6->saddr;
err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
- if (err || IS_ERR(dst)) {
- err = -EINVAL;
+ if (err || IS_ERR(dst))
goto err;
- }
}
if (unlikely(dst->error)) {
dst_release(dst);
- err = -EINVAL;
+ err = dst->error;
goto err;
}
> @@ -246,17 +250,21 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> */
> err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
> if (unlikely(err))
> - return err;
> + goto err;
>
> skb_dst_drop(skb);
> skb_dst_set(skb, dst);
>
> err = dst_output(dev_net(skb_dst(skb)->dev), skb->sk, skb);
> if (unlikely(err))
> - return err;
> + goto err;
>
> /* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
> return LWTUNNEL_XMIT_DONE;
> +
> +err:
> + kfree_skb(skb);
> + return err;
> }
>
> static int bpf_xmit(struct sk_buff *skb)
>
I figured it was a leaked skb.
Also, the test script needs to be updated as well with the negative
tests -- ie., toggle the route from a dev/gateway to a reject
(e.g.,unreachable) and back.
Also, don't exit on the first failure - run all of them.
Having the result line up is more user friendly. e.g.,
# ./fib_tests.sh
Single path route test
Start point
TEST: IPv4 fibmatch [ OK ]
TEST: IPv6 fibmatch [ OK ]
Nexthop device deleted
TEST: IPv4 fibmatch - no route [ OK ]
TEST: IPv6 fibmatch - no route [ OK ]
...
^ permalink raw reply related
* Re: [PATCH] NETWORKING: avoid use IPCB in cipso_v4_error
From: Nazarov Sergey @ 2019-02-14 18:14 UTC (permalink / raw)
To: David Miller
Cc: netdev@vger.kernel.org, linux-security-module@vger.kernel.org,
kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org,
paul@paul-moore.com
In-Reply-To: <20190214.084343.1138362153341500718.davem@davemloft.net>
Now the problem comes from TCP layer only. Is the IP over ATM operates over IP layer?
14.02.2019, 19:43, "David Miller" <davem@davemloft.net>:
> From: Nazarov Sergey <s-nazarov@yandex.ru>
> Date: Tue, 12 Feb 2019 18:10:03 +0300
>
>> Since cipso_v4_error might be called from different network stack layers, we can't safely use icmp_send there.
>> icmp_send copies IP options with ip_option_echo, which uses IPCB to take access to IP header compiled data.
>> But after commit 971f10ec ("tcp: better TCP_SKB_CB layout to reduce cache line misses"), IPCB can't be used
>> above IP layer.
>> This patch fixes the problem by creating in cipso_v4_error a local copy of compiled IP options and using it with
>> introduced __icmp_send function. This looks some overloaded, but in quite rare error conditions only.
>>
>> The original discussion is here:
>> https://lore.kernel.org/linux-security-module/16659801547571984@sas1-890ba5c2334a.qloud-c.yandex.net/
>>
>> Signed-off-by: Sergey Nazarov <s-nazarov@yandex.ru>
>
> This problem is not unique to Cipso, net/atm/clip.c's error handler
> has the same exact issue.
>
> I didn't scan more of the tree, there are probably a couple more
> locations as well.
^ permalink raw reply
* RE: [rdma-rc PATCH 2/2] iw_cxgb4: cq/qp mask depends on bar2 pages in a host page
From: Steve Wise @ 2019-02-14 17:54 UTC (permalink / raw)
To: 'Raju Rangoju', 'Jason Gunthorpe'
Cc: davem, linux-rdma, netdev
In-Reply-To: <20190214172801.GA12408@chelsio.com>
> -----Original Message-----
> From: linux-rdma-owner@vger.kernel.org <linux-rdma-
> owner@vger.kernel.org> On Behalf Of Raju Rangoju
> Sent: Thursday, February 14, 2019 11:28 AM
> To: Jason Gunthorpe <jgg@mellanox.com>
> Cc: davem@davemloft.net; linux-rdma@vger.kernel.org;
> netdev@vger.kernel.org; swise@opengridcomputing.com
> Subject: Re: [rdma-rc PATCH 2/2] iw_cxgb4: cq/qp mask depends on bar2
> pages in a host page
>
> On Thursday, February 02/14/19, 2019 at 15:41:34 +0000, Jason Gunthorpe
> wrote:
> > On Thu, Feb 14, 2019 at 05:40:54PM +0530, Raju Rangoju wrote:
> > > Adjust the cq/qp mask based on no.of bar2 pages in a host page.
> > >
> > > For user-mode rdma, the granularity of the BAR2 memory mapped
> > > to a user rdma process during queue allocation must be based
> > > on the host page size. The lld attributes udb_density and
> > > ucq_density are used to figure out how many sge contexts are
> > > in a bar2 page. So the rdev->qpmask and rdev->cqmask in
> > > iw_cxgb4 need to now be adjusted based on how many sge bar2
> > > pages are in a host page.
> >
> > Why is this rc? Do certain arches fail to work or something?
> >
>
> Yes, this series fixes a regression that was introduced by commit
> 2391b0030e (v5.0-rc1~129^2~272)
>
> > Jason
Rdma over cxgb4 on arches with a non-4K page size are busted w/o this fix.
That was the motivation for -rc.
^ permalink raw reply
* Re: [PATCH net-next v4 07/17] net: sched: protect filter_chain list with filter_chain_lock mutex
From: Ido Schimmel @ 2019-02-14 18:24 UTC (permalink / raw)
To: Vlad Buslov; +Cc: netdev, jhs, xiyou.wangcong, jiri, davem, ast, daniel
In-Reply-To: <20190211085548.7190-8-vladbu@mellanox.com>
On Mon, Feb 11, 2019 at 10:55:38AM +0200, Vlad Buslov wrote:
> Extend tcf_chain with new filter_chain_lock mutex. Always lock the chain
> when accessing filter_chain list, instead of relying on rtnl lock.
> Dereference filter_chain with tcf_chain_dereference() lockdep macro to
> verify that all users of chain_list have the lock taken.
>
> Rearrange tp insert/remove code in tc_new_tfilter/tc_del_tfilter to execute
> all necessary code while holding chain lock in order to prevent
> invalidation of chain_info structure by potential concurrent change. This
> also serializes calls to tcf_chain0_head_change(), which allows head change
> callbacks to rely on filter_chain_lock for synchronization instead of rtnl
> mutex.
>
> Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
> Acked-by: Jiri Pirko <jiri@mellanox.com>
With this sequence [1] I get the following trace [2]. Bisected it to
this patch. Note that second filter is rejected by the device driver
(that's the intention). I guess it is not properly removed from the
filter chain in the error path?
Thanks
[1]
# tc qdisc add dev swp3 clsact
# tc filter add dev swp3 ingress pref 1 matchall skip_sw \
action mirred egress mirror dev swp7
# tc filter add dev swp3 ingress pref 2 matchall skip_sw \
action mirred egress mirror dev swp7
RTNETLINK answers: File exists
We have an error talking to the kernel, -1
# tc qdisc del dev swp3 clsact
[2]
[ 70.545131] kasan: GPF could be caused by NULL-ptr deref or user memory access
[ 70.553394] general protection fault: 0000 [#1] PREEMPT SMP KASAN PTI
[ 70.560618] CPU: 2 PID: 2268 Comm: tc Not tainted 5.0.0-rc5-custom-02103-g415d39427317 #1304
[ 70.570065] Hardware name: Mellanox Technologies Ltd. MSN2100-CB2FO/SA001017, BIOS 5.6.5 06/07/2016
[ 70.580204] RIP: 0010:mall_reoffload+0x14a/0x760
[ 70.585382] Code: c1 0f 85 ba 05 00 00 31 c0 4d 8d 6c 24 34 b9 06 00 00 00 4c 89 ff f3 48 ab 4c 89 ea 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 <0f> b6 14 02 4c 89 e8 83
e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 bd
[ 70.606382] RSP: 0018:ffff888231faefc0 EFLAGS: 00010207
[ 70.612235] RAX: dffffc0000000000 RBX: 1ffff110463f5dfe RCX: 0000000000000000
[ 70.620220] RDX: 0000000000000006 RSI: 1ffff110463f5e01 RDI: ffff888231faf040
[ 70.628206] RBP: ffff8881ef151a00 R08: 0000000000000000 R09: ffffed10463f5dfa
[ 70.636192] R10: ffffed10463f5dfa R11: 0000000000000003 R12: 0000000000000000
[ 70.644177] R13: 0000000000000034 R14: 0000000000000000 R15: ffff888231faf010
[ 70.652163] FS: 00007f46b5bf0800(0000) GS:ffff888236c00000(0000) knlGS:0000000000000000
[ 70.661218] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 70.667649] CR2: 0000000001d590a8 CR3: 0000000231c3c000 CR4: 00000000001006e0
[ 70.675633] Call Trace:
[ 70.693046] tcf_block_playback_offloads+0x94/0x230
[ 70.710617] __tcf_block_cb_unregister+0xf7/0x2d0
[ 70.734143] mlxsw_sp_setup_tc+0x20f/0x660
[ 70.738739] tcf_block_offload_unbind+0x22b/0x350
[ 70.748898] __tcf_block_put+0x203/0x630
[ 70.769700] tcf_block_put_ext+0x2f/0x40
[ 70.774098] clsact_destroy+0x7a/0xb0
[ 70.782604] qdisc_destroy+0x11a/0x5c0
[ 70.786810] qdisc_put+0x5a/0x70
[ 70.790435] notify_and_destroy+0x8e/0xa0
[ 70.794933] qdisc_graft+0xbb7/0xef0
[ 70.809009] tc_get_qdisc+0x518/0xa30
[ 70.821530] rtnetlink_rcv_msg+0x397/0xa20
[ 70.835510] netlink_rcv_skb+0x132/0x380
[ 70.848419] netlink_unicast+0x4c0/0x690
[ 70.866019] netlink_sendmsg+0x929/0xe10
[ 70.882134] sock_sendmsg+0xc8/0x110
[ 70.886144] ___sys_sendmsg+0x77a/0x8f0
[ 70.924064] __sys_sendmsg+0xf7/0x250
[ 70.955727] do_syscall_64+0x14d/0x610
^ permalink raw reply
* [PATCH v5 3/5] can: m_can: Rename m_can_priv to m_can_classdev
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
In-Reply-To: <20190214182754.30721-1-dmurphy@ti.com>
Rename the common m_can_priv class structure to
m_can_classdev as this is more descriptive.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
v5 - New patch per comment in v4 - https://lore.kernel.org/patchwork/patch/1033095/
drivers/net/can/m_can/m_can.c | 95 +++++++++++++-------------
drivers/net/can/m_can/m_can.h | 28 ++++----
drivers/net/can/m_can/m_can_platform.c | 16 ++---
3 files changed, 69 insertions(+), 70 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 2ceccb870557..cc51e385fd83 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -269,7 +269,7 @@
#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT
#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT)
-static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
+static u32 m_can_read(struct m_can_classdev *priv, enum m_can_reg reg)
{
u32 ret = -EINVAL;
@@ -279,7 +279,7 @@ static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
return ret;
}
-static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
+static int m_can_write(struct m_can_classdev *priv, enum m_can_reg reg, u32 val)
{
int ret = -EINVAL;
@@ -289,7 +289,7 @@ static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
return ret;
}
-static u32 m_can_fifo_read(struct m_can_priv *priv,
+static u32 m_can_fifo_read(struct m_can_classdev *priv,
u32 fgi, unsigned int offset)
{
u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE + offset;
@@ -301,7 +301,7 @@ static u32 m_can_fifo_read(struct m_can_priv *priv,
return ret;
}
-static u32 m_can_fifo_write(struct m_can_priv *priv,
+static u32 m_can_fifo_write(struct m_can_classdev *priv,
u32 fpi, unsigned int offset, u32 val)
{
u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE + offset;
@@ -313,7 +313,7 @@ static u32 m_can_fifo_write(struct m_can_priv *priv,
return ret;
}
-static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
+static u32 m_can_fifo_write_no_off(struct m_can_classdev *priv,
u32 fpi, u32 val)
{
u32 ret = 0;
@@ -324,7 +324,7 @@ static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
return ret;
}
-static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
+static u32 m_can_txe_fifo_read(struct m_can_classdev *priv, u32 fgi, u32 offset)
{
u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE + offset;
u32 ret = -EINVAL;
@@ -335,12 +335,12 @@ static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
return ret;
}
-static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
+static inline bool m_can_tx_fifo_full(struct m_can_classdev *priv)
{
return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
}
-void m_can_config_endisable(struct m_can_priv *priv, bool enable)
+void m_can_config_endisable(struct m_can_classdev *priv, bool enable)
{
u32 cccr = m_can_read(priv, M_CAN_CCCR);
u32 timeout = 10;
@@ -373,13 +373,13 @@ void m_can_config_endisable(struct m_can_priv *priv, bool enable)
}
}
-static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
+static inline void m_can_enable_all_interrupts(struct m_can_classdev *priv)
{
/* Only interrupt line 0 is used in this driver */
m_can_write(priv, M_CAN_ILE, ILE_EINT0);
}
-static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
+static inline void m_can_disable_all_interrupts(struct m_can_classdev *priv)
{
m_can_write(priv, M_CAN_ILE, 0x0);
}
@@ -387,7 +387,7 @@ static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
{
struct net_device_stats *stats = &dev->stats;
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
struct canfd_frame *cf;
struct sk_buff *skb;
u32 id, fgi, dlc;
@@ -444,7 +444,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
static int m_can_do_rx_poll(struct net_device *dev, int quota)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
u32 pkts = 0;
u32 rxfs;
@@ -497,7 +497,7 @@ static int m_can_handle_lost_msg(struct net_device *dev)
static int m_can_handle_lec_err(struct net_device *dev,
enum m_can_lec_type lec_type)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct can_frame *cf;
struct sk_buff *skb;
@@ -554,7 +554,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
static int __m_can_get_berr_counter(const struct net_device *dev,
struct can_berr_counter *bec)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
unsigned int ecr;
ecr = m_can_read(priv, M_CAN_ECR);
@@ -564,7 +564,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev,
return 0;
}
-static int m_can_clk_start(struct m_can_priv *priv)
+static int m_can_clk_start(struct m_can_classdev *priv)
{
int err;
@@ -580,7 +580,7 @@ static int m_can_clk_start(struct m_can_priv *priv)
return 0;
}
-static void m_can_clk_stop(struct m_can_priv *priv)
+static void m_can_clk_stop(struct m_can_classdev *priv)
{
if (priv->pm_clock_support)
pm_runtime_put_sync(priv->dev);
@@ -589,7 +589,7 @@ static void m_can_clk_stop(struct m_can_priv *priv)
static int m_can_get_berr_counter(const struct net_device *dev,
struct can_berr_counter *bec)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int err;
err = m_can_clk_start(priv);
@@ -606,7 +606,7 @@ static int m_can_get_berr_counter(const struct net_device *dev,
static int m_can_handle_state_change(struct net_device *dev,
enum can_state new_state)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct can_frame *cf;
struct sk_buff *skb;
@@ -680,7 +680,7 @@ static int m_can_handle_state_change(struct net_device *dev,
static int m_can_handle_state_errors(struct net_device *dev, u32 psr)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int work_done = 0;
if ((psr & PSR_EW) &&
@@ -733,7 +733,7 @@ static inline bool is_lec_err(u32 psr)
static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
u32 psr)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int work_done = 0;
if (irqstatus & IR_RF0L)
@@ -752,7 +752,7 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
static int m_can_rx_handler(struct net_device *dev, int quota)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int work_done = 0;
u32 irqstatus, psr;
@@ -775,7 +775,7 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
static int m_can_rx_peripherial(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
m_can_rx_handler(dev, 1);
@@ -787,7 +787,7 @@ static int m_can_rx_peripherial(struct net_device *dev)
static int m_can_poll(struct napi_struct *napi, int quota)
{
struct net_device *dev = napi->dev;
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int work_done = 0;
work_done = m_can_rx_handler(dev, quota);
@@ -807,7 +807,7 @@ static void m_can_echo_tx_event(struct net_device *dev)
int i = 0;
unsigned int msg_mark;
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
/* read tx event fifo status */
@@ -840,7 +840,7 @@ static void m_can_echo_tx_event(struct net_device *dev)
static irqreturn_t m_can_isr(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
u32 ir;
@@ -941,7 +941,7 @@ static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
static int m_can_set_bittiming(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
const struct can_bittiming *bt = &priv->can.bittiming;
const struct can_bittiming *dbt = &priv->can.data_bittiming;
u16 brp, sjw, tseg1, tseg2;
@@ -1014,7 +1014,7 @@ static int m_can_set_bittiming(struct net_device *dev)
*/
static void m_can_chip_config(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
u32 cccr, test;
m_can_config_endisable(priv, true);
@@ -1126,7 +1126,7 @@ static void m_can_chip_config(struct net_device *dev)
static void m_can_start(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
/* basic m_can configuration */
m_can_chip_config(dev);
@@ -1155,7 +1155,7 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
* else it returns the release and step coded as:
* return value = 10 * <release> + 1 * <step>
*/
-static int m_can_check_core_release(struct m_can_priv *priv)
+static int m_can_check_core_release(struct m_can_classdev *priv)
{
u32 crel_reg;
u8 rel;
@@ -1183,7 +1183,7 @@ static int m_can_check_core_release(struct m_can_priv *priv)
/* Selectable Non ISO support only in version 3.2.x
* This function checks if the bit is writable.
*/
-static bool m_can_niso_supported(struct m_can_priv *priv)
+static bool m_can_niso_supported(struct m_can_classdev *priv)
{
u32 cccr_reg, cccr_poll = 0;
int niso_timeout = -ETIMEDOUT;
@@ -1210,7 +1210,7 @@ static bool m_can_niso_supported(struct m_can_priv *priv)
return !niso_timeout;
}
-static int m_can_dev_setup(struct m_can_priv *m_can_dev)
+static int m_can_dev_setup(struct m_can_classdev *m_can_dev)
{
struct net_device *dev = m_can_dev->net;
int m_can_version;
@@ -1303,7 +1303,7 @@ static int m_can_dev_setup(struct m_can_priv *m_can_dev)
static void m_can_stop(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
/* disable all interrupts */
m_can_disable_all_interrupts(priv);
@@ -1314,7 +1314,7 @@ static void m_can_stop(struct net_device *dev)
static int m_can_close(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
netif_stop_queue(dev);
if (!priv->is_peripherial)
@@ -1336,7 +1336,7 @@ static int m_can_close(struct net_device *dev)
static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
/*get wrap around for loopback skb index */
unsigned int wrap = priv->can.echo_skb_max;
int next_idx;
@@ -1348,7 +1348,7 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
return !!priv->can.echo_skb[next_idx];
}
-static void m_can_tx_handler(struct m_can_priv *priv)
+static void m_can_tx_handler(struct m_can_classdev *priv)
{
struct canfd_frame *cf = (struct canfd_frame *)priv->skb->data;
struct net_device *dev = priv->net;
@@ -1460,7 +1460,7 @@ static void m_can_tx_handler(struct m_can_priv *priv)
static void m_can_tx_work_queue(struct work_struct *ws)
{
- struct m_can_priv *priv = container_of(ws, struct m_can_priv,
+ struct m_can_classdev *priv = container_of(ws, struct m_can_classdev,
tx_work);
m_can_tx_handler(priv);
}
@@ -1468,8 +1468,7 @@ static void m_can_tx_work_queue(struct work_struct *ws)
static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
-
+ struct m_can_classdev *priv = netdev_priv(dev);
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
@@ -1487,7 +1486,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
static int m_can_open(struct net_device *dev)
{
- struct m_can_priv *priv = netdev_priv(dev);
+ struct m_can_classdev *priv = netdev_priv(dev);
int err;
err = m_can_clk_start(priv);
@@ -1562,7 +1561,7 @@ static int register_m_can_dev(struct net_device *dev)
return register_candev(dev);
}
-static void m_can_of_parse_mram(struct m_can_priv *priv,
+static void m_can_of_parse_mram(struct m_can_classdev *priv,
const u32 *mram_config_vals)
{
priv->mcfg[MRAM_SIDF].off = mram_config_vals[0];
@@ -1600,7 +1599,7 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
}
-void m_can_init_ram(struct m_can_priv *priv)
+void m_can_init_ram(struct m_can_classdev *priv)
{
int end, i, start;
@@ -1616,7 +1615,7 @@ void m_can_init_ram(struct m_can_priv *priv)
}
EXPORT_SYMBOL_GPL(m_can_init_ram);
-int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
+int m_can_class_get_clocks(struct m_can_classdev *m_can_dev)
{
int ret = 0;
@@ -1632,9 +1631,9 @@ int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
}
EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
-struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
+struct m_can_classdev *m_can_class_allocate_dev(struct device *dev)
{
- struct m_can_priv *class_dev = NULL;
+ struct m_can_classdev *class_dev = NULL;
u32 mram_config_vals[MRAM_CFG_LEN];
struct net_device *net_dev;
u32 tx_fifo_size;
@@ -1677,7 +1676,7 @@ struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
}
EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
-int m_can_class_register(struct m_can_priv *m_can_dev)
+int m_can_class_register(struct m_can_classdev *m_can_dev)
{
int ret;
@@ -1725,7 +1724,7 @@ EXPORT_SYMBOL_GPL(m_can_class_register);
int m_can_class_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *priv = netdev_priv(ndev);
+ struct m_can_classdev *priv = netdev_priv(ndev);
if (netif_running(ndev)) {
netif_stop_queue(ndev);
@@ -1745,7 +1744,7 @@ EXPORT_SYMBOL_GPL(m_can_class_suspend);
int m_can_class_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *priv = netdev_priv(ndev);
+ struct m_can_classdev *priv = netdev_priv(ndev);
pinctrl_pm_select_default_state(dev);
@@ -1768,7 +1767,7 @@ int m_can_class_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(m_can_class_resume);
-void m_can_class_unregister(struct m_can_priv *m_can_dev)
+void m_can_class_unregister(struct m_can_classdev *m_can_dev)
{
unregister_candev(m_can_dev->net);
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index 36b1b833d41b..8d1f0f7f5b43 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -107,18 +107,18 @@ struct mram_cfg {
u8 num;
};
-struct m_can_priv;
+struct m_can_classdev;
struct m_can_ops {
/* Device specific call backs */
- int (*clr_dev_interrupts) (struct m_can_priv *m_can_class);
- u32 (*read_reg) (struct m_can_priv *m_can_class, int reg);
- int (*write_reg) (struct m_can_priv *m_can_class, int reg, int val);
- u32 (*read_fifo) (struct m_can_priv *m_can_class, int addr_offset);
- int (*write_fifo) (struct m_can_priv *m_can_class, int addr_offset, int val);
- int (*device_init) (struct m_can_priv *m_can_class);
+ int (*clr_dev_interrupts) (struct m_can_classdev *m_can_class);
+ u32 (*read_reg) (struct m_can_classdev *m_can_class, int reg);
+ int (*write_reg) (struct m_can_classdev *m_can_class, int reg, int val);
+ u32 (*read_fifo) (struct m_can_classdev *m_can_class, int addr_offset);
+ int (*write_fifo) (struct m_can_classdev *m_can_class, int addr_offset, int val);
+ int (*device_init) (struct m_can_classdev *m_can_class);
};
-struct m_can_priv {
+struct m_can_classdev {
struct can_priv can;
struct napi_struct napi;
struct net_device *net;
@@ -147,12 +147,12 @@ struct m_can_priv {
struct mram_cfg mcfg[MRAM_CFG_NUM];
};
-struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
-int m_can_class_register(struct m_can_priv *m_can_dev);
-void m_can_class_unregister(struct m_can_priv *m_can_dev);
-int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
-void m_can_init_ram(struct m_can_priv *priv);
-void m_can_config_endisable(struct m_can_priv *priv, bool enable);
+struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
+int m_can_class_register(struct m_can_classdev *m_can_dev);
+void m_can_class_unregister(struct m_can_classdev *m_can_dev);
+int m_can_class_get_clocks(struct m_can_classdev *m_can_dev);
+void m_can_init_ram(struct m_can_classdev *priv);
+void m_can_config_endisable(struct m_can_classdev *priv, bool enable);
int m_can_class_suspend(struct device *dev);
int m_can_class_resume(struct device *dev);
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index d8d51bd64205..bc9143764ac8 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -14,21 +14,21 @@ struct m_can_plat_priv {
void __iomem *mram_base;
};
-static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
+static u32 iomap_read_reg(struct m_can_classdev *m_can_class, int reg)
{
struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
return readl(priv->base + reg);
}
-static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
+static u32 iomap_read_fifo(struct m_can_classdev *m_can_class, int offset)
{
struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
return readl(priv->mram_base + offset);
}
-static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
+static int iomap_write_reg(struct m_can_classdev *m_can_class, int reg, int val)
{
struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
@@ -37,7 +37,7 @@ static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
return 0;
}
-static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
+static int iomap_write_fifo(struct m_can_classdev *m_can_class, int offset, int val)
{
struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
@@ -55,7 +55,7 @@ static struct m_can_ops m_can_plat_ops = {
static int m_can_plat_probe(struct platform_device *pdev)
{
- struct m_can_priv *mcan_class;
+ struct m_can_classdev *mcan_class;
struct m_can_plat_priv *priv;
struct resource *res;
void __iomem *addr;
@@ -127,7 +127,7 @@ static __maybe_unused int m_can_resume(struct device *dev)
static int m_can_plat_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
- struct m_can_priv *mcan_class = netdev_priv(dev);
+ struct m_can_classdev *mcan_class = netdev_priv(dev);
m_can_class_unregister(mcan_class);
@@ -139,7 +139,7 @@ static int m_can_plat_remove(struct platform_device *pdev)
static int __maybe_unused m_can_runtime_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *mcan_class = netdev_priv(ndev);
+ struct m_can_classdev *mcan_class = netdev_priv(ndev);
m_can_class_suspend(dev);
@@ -152,7 +152,7 @@ static int __maybe_unused m_can_runtime_suspend(struct device *dev)
static int __maybe_unused m_can_runtime_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *mcan_class = netdev_priv(ndev);
+ struct m_can_classdev *mcan_class = netdev_priv(ndev);
int err;
err = clk_prepare_enable(mcan_class->hclk);
--
2.20.1.390.gb5101f9297
^ permalink raw reply related
* [PATCH v5 5/5] can: tcan4x5x: Add tcan4x5x driver to the kernel
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
In-Reply-To: <20190214182754.30721-1-dmurphy@ti.com>
Add the TCAN4x5x SPI CAN driver. This device
uses the Bosch MCAN IP core along with a SPI
interface map. Leverage the MCAN common core
code to manage the MCAN IP.
This device has a special method to indicate a
write/read operation on the data payload.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
v5 - Changes to accomodate previous patches for functionality - https://lore.kernel.org/patchwork/patch/1033096/
drivers/net/can/m_can/Kconfig | 6 +
drivers/net/can/m_can/Makefile | 1 +
drivers/net/can/m_can/tcan4x5x.c | 531 +++++++++++++++++++++++++++++++
3 files changed, 538 insertions(+)
create mode 100644 drivers/net/can/m_can/tcan4x5x.c
diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
index 66e31022a5fa..6c0ab4703fb7 100644
--- a/drivers/net/can/m_can/Kconfig
+++ b/drivers/net/can/m_can/Kconfig
@@ -9,3 +9,9 @@ config CAN_M_CAN_PLATFORM
depends on CAN_M_CAN
---help---
Say Y here if you want to support for Bosch M_CAN controller.
+
+config CAN_M_CAN_TCAN4X5X
+ depends on CAN_M_CAN
+ tristate "TCAN4X5X M_CAN device"
+ ---help---
+ Say Y here if you want to support for TI M_CAN controller.
diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
index 057bbcdb3c74..e77f0eccff97 100644
--- a/drivers/net/can/m_can/Makefile
+++ b/drivers/net/can/m_can/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_CAN_M_CAN) += m_can.o
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
+obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
new file mode 100644
index 000000000000..606cd1925009
--- /dev/null
+++ b/drivers/net/can/m_can/tcan4x5x.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPI to CAN driver for the Texas Instruments TCAN4x5x
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+
+#include "m_can.h"
+
+#define DEVICE_NAME "tcan4x5x"
+#define TCAN4X5X_EXT_CLK_DEF 40000000
+
+#define TCAN4X5X_DEV_ID0 0x00
+#define TCAN4X5X_DEV_ID1 0x04
+#define TCAN4X5X_REV 0x08
+#define TCAN4X5X_STATUS 0x0C
+#define TCAN4X5X_ERROR_STATUS 0x10
+#define TCAN4X5X_CONTROL 0x14
+
+#define TCAN4X5X_CONFIG 0x800
+#define TCAN4X5X_TS_PRESCALE 0x804
+#define TCAN4X5X_TEST_REG 0x808
+#define TCAN4X5X_INT_FLAGS 0x820
+#define TCAN4X5X_MCAN_INT_REG 0x824
+#define TCAN4X5X_INT_EN 0x830
+
+
+/* Interrupt bits */
+#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
+#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
+#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
+#define TCAN4X5X_CANLGND_INT_EN BIT(27)
+#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
+#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
+#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
+#define TCAN4X5X_UVSUP_INT_EN BIT(22)
+#define TCAN4X5X_UVIO_INT_EN BIT(21)
+#define TCAN4X5X_TSD_INT_EN BIT(19)
+#define TCAN4X5X_ECCERR_INT_EN BIT(16)
+#define TCAN4X5X_CANINT_INT_EN BIT(15)
+#define TCAN4X5X_LWU_INT_EN BIT(14)
+#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
+#define TCAN4X5X_CANDOM_INT_EN BIT(8)
+#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
+#define TCAN4X5X_BUS_FAULT BIT(4)
+#define TCAN4X5X_MCAN_INT BIT(1)
+#define TCAN4X5X_ENABLE_TCAN_INT (TCAN4X5X_MCAN_INT | \
+ TCAN4X5X_BUS_FAULT | \
+ TCAN4X5X_CANBUS_ERR_INT_EN | \
+ TCAN4X5X_CANINT_INT_EN)
+
+/* MCAN Interrupt bits */
+#define TCAN4X5X_MCAN_IR_ARA BIT(29)
+#define TCAN4X5X_MCAN_IR_PED BIT(28)
+#define TCAN4X5X_MCAN_IR_PEA BIT(27)
+#define TCAN4X5X_MCAN_IR_WD BIT(26)
+#define TCAN4X5X_MCAN_IR_BO BIT(25)
+#define TCAN4X5X_MCAN_IR_EW BIT(24)
+#define TCAN4X5X_MCAN_IR_EP BIT(23)
+#define TCAN4X5X_MCAN_IR_ELO BIT(22)
+#define TCAN4X5X_MCAN_IR_BEU BIT(21)
+#define TCAN4X5X_MCAN_IR_BEC BIT(20)
+#define TCAN4X5X_MCAN_IR_DRX BIT(19)
+#define TCAN4X5X_MCAN_IR_TOO BIT(18)
+#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
+#define TCAN4X5X_MCAN_IR_TSW BIT(16)
+#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
+#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
+#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
+#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
+#define TCAN4X5X_MCAN_IR_TFE BIT(11)
+#define TCAN4X5X_MCAN_IR_TCF BIT(10)
+#define TCAN4X5X_MCAN_IR_TC BIT(9)
+#define TCAN4X5X_MCAN_IR_HPM BIT(8)
+#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
+#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
+#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
+#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
+#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
+#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
+#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
+#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
+#define TCAN4X5X_ENABLE_MCAN_INT (TCAN4X5X_MCAN_IR_TC | \
+ TCAN4X5X_MCAN_IR_RF0N | \
+ TCAN4X5X_MCAN_IR_RF1N | \
+ TCAN4X5X_MCAN_IR_RF0F | \
+ TCAN4X5X_MCAN_IR_RF1F)
+#define TCAN4X5X_MRAM_START 0x8000
+#define TCAN4X5X_MCAN_OFFSET 0x1000
+#define TCAN4X5X_MAX_REGISTER 0x8fff
+
+#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
+#define TCAN4X5X_SET_ALL_INT 0xffffffff
+
+#define TCAN4X5X_WRITE_CMD (0x61 << 24)
+#define TCAN4X5X_READ_CMD (0x41 << 24)
+
+#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
+#define TCAN4X5X_MODE_SLEEP 0x00
+#define TCAN4X5X_MODE_STANDBY BIT(6)
+#define TCAN4X5X_MODE_NORMAL BIT(7)
+
+#define TCAN4X5X_SW_RESET BIT(2)
+
+#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
+#define TCAN4X5X_WATCHDOG_EN BIT(3)
+#define TCAN4X5X_WD_60_MS_TIMER 0
+#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
+#define TCAN4X5X_WD_3_S_TIMER BIT(29)
+#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
+
+struct tcan4x5x_priv {
+ struct regmap *regmap;
+ struct spi_device *spi;
+ struct mutex tcan4x5x_lock; /* SPI device lock */
+
+ struct m_can_classdev *mcan_dev;
+
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *interrupt_gpio;
+ struct gpio_desc *device_wake_gpio;
+ struct gpio_desc *device_state_gpio;
+ struct regulator *power;
+
+ /* Register based ip */
+ int mram_start;
+ int reg_offset;
+};
+
+static struct can_bittiming_const tcan4x5x_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 31,
+ .tseg2_min = 2,
+ .tseg2_max = 16,
+ .sjw_max = 16,
+ .brp_min = 1,
+ .brp_max = 32,
+ .brp_inc = 1,
+};
+
+static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = 32,
+ .tseg2_min = 1,
+ .tseg2_max = 16,
+ .sjw_max = 16,
+ .brp_min = 1,
+ .brp_max = 32,
+ .brp_inc = 1,
+};
+
+static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
+{
+ int wake_state = 0;
+
+ if (priv->device_state_gpio)
+ wake_state = gpiod_get_value(priv->device_state_gpio);
+
+ if (priv->device_wake_gpio && wake_state) {
+ gpiod_set_value(priv->device_wake_gpio, 1);
+ udelay(100);
+ gpiod_set_value(priv->device_wake_gpio, 0);
+ udelay(100);
+ gpiod_set_value(priv->device_wake_gpio, 1);
+ }
+}
+
+static int regmap_spi_gather_write(void *context, const void *reg,
+ size_t reg_len, const void *val,
+ size_t val_len)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_message m;
+ u32 addr;
+ struct spi_transfer t[2] = {{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
+ { .tx_buf = val, .len = val_len, },};
+
+ addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ return spi_sync(spi, &m);
+}
+
+static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
+{
+ u16 *reg = (u16 *)(data);
+ const u32 *val = data + 4;
+
+ return regmap_spi_gather_write(context, reg, 4, val, count);
+}
+
+static int regmap_spi_async_write(void *context,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len,
+ struct regmap_async *a)
+{
+ return -ENOTSUPP;
+}
+
+static struct regmap_async *regmap_spi_async_alloc(void)
+{
+ return NULL;
+}
+
+static int tcan4x5x_regmap_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+
+ u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
+
+ return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
+}
+
+static struct regmap_bus tcan4x5x_bus = {
+ .write = tcan4x5x_regmap_write,
+ .gather_write = regmap_spi_gather_write,
+ .async_write = regmap_spi_async_write,
+ .async_alloc = regmap_spi_async_alloc,
+ .read = tcan4x5x_regmap_read,
+ .read_flag_mask = 0x00,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+static u32 tcan4x5x_read_reg(struct m_can_classdev *m_can_class, int reg)
+{
+ struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+ u32 val;
+
+ tcan4x5x_check_wake(priv);
+
+ regmap_read(priv->regmap, priv->reg_offset + reg, &val);
+
+ return val;
+}
+
+static u32 tcan4x5x_read_fifo(struct m_can_classdev *m_can_class,
+ int addr_offset)
+{
+ struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+ u32 val;
+
+ tcan4x5x_check_wake(priv);
+
+ regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
+
+ return val;
+}
+
+static int tcan4x5x_write_reg(struct m_can_classdev *m_can_class,
+ int reg, int val)
+{
+ struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+ tcan4x5x_check_wake(priv);
+
+ return regmap_write(priv->regmap, priv->reg_offset + reg, val);
+}
+
+static int tcan4x5x_write_fifo(struct m_can_classdev *m_can_class,
+ int addr_offset, int val)
+{
+ struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+ tcan4x5x_check_wake(priv);
+
+ return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
+}
+
+static int tcan4x5x_power_enable(struct regulator *reg, int enable)
+{
+ if (IS_ERR_OR_NULL(reg))
+ return 0;
+
+ if (enable)
+ return regulator_enable(reg);
+ else
+ return regulator_disable(reg);
+}
+
+static int tcan4x5x_write_tcan_reg(struct m_can_classdev *m_can_class,
+ int reg, int val)
+{
+ struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+ tcan4x5x_check_wake(priv);
+
+ return regmap_write(priv->regmap, reg, val);
+}
+
+static int tcan4x5x_clear_interrupts(struct m_can_classdev *class_dev)
+{
+ struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+ int ret;
+
+ tcan4x5x_check_wake(tcan4x5x);
+
+ ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_STATUS,
+ TCAN4X5X_CLEAR_ALL_INT);
+ if (ret)
+ return -EIO;
+
+ ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_MCAN_INT_REG,
+ TCAN4X5X_ENABLE_MCAN_INT);
+ if (ret)
+ return -EIO;
+
+ ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_FLAGS,
+ TCAN4X5X_CLEAR_ALL_INT);
+ if (ret)
+ return -EIO;
+
+
+ ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_ERROR_STATUS,
+ TCAN4X5X_CLEAR_ALL_INT);
+ if (ret)
+ return -EIO;
+
+ return ret;
+}
+
+static int tcan4x5x_init(struct m_can_classdev *class_dev)
+{
+ struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+ int ret;
+
+ tcan4x5x_check_wake(tcan4x5x);
+
+ ret = tcan4x5x_clear_interrupts(class_dev);
+ if (ret)
+ return ret;
+
+ ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_EN,
+ TCAN4X5X_ENABLE_TCAN_INT);
+ if (ret)
+ return -EIO;
+
+ ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
+ TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
+ if (ret)
+ return -EIO;
+
+ /* Zero out the MCAN buffers */
+ m_can_init_ram(class_dev);
+
+ return ret;
+}
+
+static int tcan4x5x_parse_config(struct m_can_classdev *class_dev)
+{
+ struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+
+ tcan4x5x->reset_gpio = devm_gpiod_get_optional(class_dev->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(tcan4x5x->reset_gpio))
+ tcan4x5x->reset_gpio = NULL;
+
+ tcan4x5x->device_wake_gpio = devm_gpiod_get_optional(class_dev->dev,
+ "device-wake",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tcan4x5x->device_wake_gpio))
+ tcan4x5x->device_wake_gpio = NULL;
+
+ tcan4x5x->device_state_gpio = devm_gpiod_get_optional(class_dev->dev,
+ "device-state",
+ GPIOD_IN);
+ if (IS_ERR(tcan4x5x->device_state_gpio))
+ tcan4x5x->device_state_gpio = NULL;
+
+ tcan4x5x->interrupt_gpio = devm_gpiod_get(class_dev->dev,
+ "data-ready", GPIOD_IN);
+ if (IS_ERR(tcan4x5x->interrupt_gpio)) {
+ dev_err(class_dev->dev, "data-ready gpio not defined\n");
+ return -EINVAL;
+ }
+
+ class_dev->net->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
+
+ tcan4x5x->power = devm_regulator_get_optional(class_dev->dev,
+ "vsup");
+ if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static const struct regmap_config tcan4x5x_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .cache_type = REGCACHE_NONE,
+ .max_register = TCAN4X5X_MAX_REGISTER,
+};
+
+static struct m_can_ops tcan4x5x_ops = {
+ .device_init = tcan4x5x_init,
+ .read_reg = tcan4x5x_read_reg,
+ .write_reg = tcan4x5x_write_reg,
+ .write_fifo = tcan4x5x_write_fifo,
+ .read_fifo = tcan4x5x_read_fifo,
+ .clr_dev_interrupts = tcan4x5x_clear_interrupts,
+};
+
+static int tcan4x5x_can_probe(struct spi_device *spi)
+{
+ struct tcan4x5x_priv *priv;
+ struct m_can_classdev *mcan_class;
+ int freq, ret;
+
+ mcan_class = m_can_class_allocate_dev(&spi->dev);
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mcan_class->device_data = priv;
+
+ m_can_class_get_clocks(mcan_class);
+ if (IS_ERR(mcan_class->cclk)) {
+ dev_err(&spi->dev, "no CAN clock source defined\n");
+ freq = TCAN4X5X_EXT_CLK_DEF;
+ } else {
+ freq = clk_get_rate(mcan_class->cclk);
+ }
+
+ /* Sanity check */
+ if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
+ return -ERANGE;
+
+ priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
+ priv->mram_start = TCAN4X5X_MRAM_START;
+ priv->spi = spi;
+ priv->mcan_dev = mcan_class;
+
+ mcan_class->pm_clock_support = 0;
+ mcan_class->can.clock.freq = freq;
+ mcan_class->dev = &spi->dev;
+ mcan_class->ops = &tcan4x5x_ops;
+ mcan_class->is_peripherial = true;
+ mcan_class->bit_timing = &tcan4x5x_bittiming_const;
+ mcan_class->data_timing = &tcan4x5x_data_bittiming_const;
+
+ spi_set_drvdata(spi, priv);
+
+ ret = tcan4x5x_parse_config(mcan_class);
+ if (ret)
+ goto out_clk;
+
+ /* Configure the SPI bus */
+ spi->bits_per_word = 32;
+ ret = spi_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
+ &spi->dev, &tcan4x5x_regmap);
+
+ mutex_init(&priv->tcan4x5x_lock);
+
+ tcan4x5x_power_enable(priv->power, 1);
+
+ ret = m_can_class_register(mcan_class);
+ if (ret)
+ goto reg_err;
+
+ netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
+ return 0;
+
+reg_err:
+ tcan4x5x_power_enable(priv->power, 0);
+out_clk:
+ if (!IS_ERR(mcan_class->cclk)) {
+ clk_disable_unprepare(mcan_class->cclk);
+ clk_disable_unprepare(mcan_class->hclk);
+ }
+
+ dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+ return ret;
+}
+
+static int tcan4x5x_can_remove(struct spi_device *spi)
+{
+ struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
+
+ tcan4x5x_power_enable(priv->power, 0);
+
+ m_can_class_unregister(priv->mcan_dev);
+
+ return 0;
+}
+
+static const struct of_device_id tcan4x5x_of_match[] = {
+ { .compatible = "ti,tcan4x5x", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
+
+static const struct spi_device_id tcan4x5x_id_table[] = {
+ {
+ .name = "tcan4x5x",
+ .driver_data = 0,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
+
+static struct spi_driver tcan4x5x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = tcan4x5x_of_match,
+ .pm = NULL,
+ },
+ .id_table = tcan4x5x_id_table,
+ .probe = tcan4x5x_can_probe,
+ .remove = tcan4x5x_can_remove,
+};
+module_spi_driver(tcan4x5x_can_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
+MODULE_LICENSE("GPL v2");
--
2.20.1.390.gb5101f9297
^ permalink raw reply related
* [PATCH v5 4/5] dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
In-Reply-To: <20190214182754.30721-1-dmurphy@ti.com>
DT binding documentation for TI TCAN4x5x driver.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
v5 - No changes - https://lore.kernel.org/patchwork/patch/1033097/
.../devicetree/bindings/net/can/tcan4x5x.txt | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/tcan4x5x.txt
diff --git a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt
new file mode 100644
index 000000000000..781c19887538
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt
@@ -0,0 +1,37 @@
+Texas Instruments TCAN4x5x CAN Controller
+================================================
+
+This file provides device node information for the TCAN4x5x interface contains.
+
+Required properties:
+ - compatible: "ti,tcan4x5x"
+ - reg: 0
+ - #address-cells: 1
+ - #size-cells: 0
+ - spi-max-frequency: Maximum frequency of the SPI bus the chip can
+ operate at should be less than or equal to 18 MHz.
+ - data-ready-gpios: Interrupt GPIO for data and error reporting.
+ - device-wake-gpios: Wake up GPIO to wake up the TCAN device.
+ - device-state-gpios: Input GPIO that indicates if the device is in
+ a sleep state or if the device is active.
+
+See Documentation/devicetree/bindings/net/can/m_can.txt for additional
+required property details.
+
+Optional properties:
+ - reset-gpios: Hardwired output GPIO. If not defined then software
+ reset.
+
+Example:
+tcan4x5x: tcan4x5x@0 {
+ compatible = "ti,tcan4x5x";
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <10000000>;
+ bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
+ data-ready-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
+ device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
+ device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
+};
--
2.20.1.390.gb5101f9297
^ permalink raw reply related
* [PATCH v5 2/5] can: m_can: Migrate the m_can code to use the framework
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
In-Reply-To: <20190214182754.30721-1-dmurphy@ti.com>
Migrate the m_can code to use the m_can_platform framework
code.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
v5 - Updated copyright, change m_can_classdev to m_can_priv, removed extra
KCONFIG flag - https://lore.kernel.org/patchwork/patch/1033095/
drivers/net/can/m_can/Kconfig | 8 +-
drivers/net/can/m_can/Makefile | 1 +
drivers/net/can/m_can/m_can.c | 745 ++++++++++++++++-----------------
3 files changed, 367 insertions(+), 387 deletions(-)
diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
index 04f20dd39007..66e31022a5fa 100644
--- a/drivers/net/can/m_can/Kconfig
+++ b/drivers/net/can/m_can/Kconfig
@@ -1,5 +1,11 @@
config CAN_M_CAN
+ tristate "Bosch M_CAN support"
+ ---help---
+ Say Y here if you want to support for Bosch M_CAN controller.
+
+config CAN_M_CAN_PLATFORM
+ tristate "Bosch M_CAN support for io-mapped devices"
depends on HAS_IOMEM
- tristate "Bosch M_CAN devices"
+ depends on CAN_M_CAN
---help---
Say Y here if you want to support for Bosch M_CAN controller.
diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
index 8bbd7f24f5be..057bbcdb3c74 100644
--- a/drivers/net/can/m_can/Makefile
+++ b/drivers/net/can/m_can/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_CAN_M_CAN) += m_can.o
+obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 9b449400376b..2ceccb870557 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1,20 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+// CAN bus driver for Bosch M_CAN controller
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+// Dong Aisheng <b29396@freescale.com>
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
/*
- * CAN bus driver for Bosch M_CAN controller
- *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
- * Dong Aisheng <b29396@freescale.com>
- *
* Bosch M_CAN user manual can be obtained from:
* http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/
* mcan_users_manual_v302.pdf
*
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
-#include <linux/clk.h>
-#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -28,87 +24,14 @@
#include <linux/can/dev.h>
#include <linux/pinctrl/consumer.h>
+#include "m_can.h"
+
/* napi related */
#define M_CAN_NAPI_WEIGHT 64
/* message ram configuration data length */
#define MRAM_CFG_LEN 8
-/* registers definition */
-enum m_can_reg {
- M_CAN_CREL = 0x0,
- M_CAN_ENDN = 0x4,
- M_CAN_CUST = 0x8,
- M_CAN_DBTP = 0xc,
- M_CAN_TEST = 0x10,
- M_CAN_RWD = 0x14,
- M_CAN_CCCR = 0x18,
- M_CAN_NBTP = 0x1c,
- M_CAN_TSCC = 0x20,
- M_CAN_TSCV = 0x24,
- M_CAN_TOCC = 0x28,
- M_CAN_TOCV = 0x2c,
- M_CAN_ECR = 0x40,
- M_CAN_PSR = 0x44,
-/* TDCR Register only available for version >=3.1.x */
- M_CAN_TDCR = 0x48,
- M_CAN_IR = 0x50,
- M_CAN_IE = 0x54,
- M_CAN_ILS = 0x58,
- M_CAN_ILE = 0x5c,
- M_CAN_GFC = 0x80,
- M_CAN_SIDFC = 0x84,
- M_CAN_XIDFC = 0x88,
- M_CAN_XIDAM = 0x90,
- M_CAN_HPMS = 0x94,
- M_CAN_NDAT1 = 0x98,
- M_CAN_NDAT2 = 0x9c,
- M_CAN_RXF0C = 0xa0,
- M_CAN_RXF0S = 0xa4,
- M_CAN_RXF0A = 0xa8,
- M_CAN_RXBC = 0xac,
- M_CAN_RXF1C = 0xb0,
- M_CAN_RXF1S = 0xb4,
- M_CAN_RXF1A = 0xb8,
- M_CAN_RXESC = 0xbc,
- M_CAN_TXBC = 0xc0,
- M_CAN_TXFQS = 0xc4,
- M_CAN_TXESC = 0xc8,
- M_CAN_TXBRP = 0xcc,
- M_CAN_TXBAR = 0xd0,
- M_CAN_TXBCR = 0xd4,
- M_CAN_TXBTO = 0xd8,
- M_CAN_TXBCF = 0xdc,
- M_CAN_TXBTIE = 0xe0,
- M_CAN_TXBCIE = 0xe4,
- M_CAN_TXEFC = 0xf0,
- M_CAN_TXEFS = 0xf4,
- M_CAN_TXEFA = 0xf8,
-};
-
-/* m_can lec values */
-enum m_can_lec_type {
- LEC_NO_ERROR = 0,
- LEC_STUFF_ERROR,
- LEC_FORM_ERROR,
- LEC_ACK_ERROR,
- LEC_BIT1_ERROR,
- LEC_BIT0_ERROR,
- LEC_CRC_ERROR,
- LEC_UNUSED,
-};
-
-enum m_can_mram_cfg {
- MRAM_SIDF = 0,
- MRAM_XIDF,
- MRAM_RXF0,
- MRAM_RXF1,
- MRAM_RXB,
- MRAM_TXE,
- MRAM_TXB,
- MRAM_CFG_NUM,
-};
-
/* Core Release Register (CREL) */
#define CREL_REL_SHIFT 28
#define CREL_REL_MASK (0xF << CREL_REL_SHIFT)
@@ -343,77 +266,89 @@ enum m_can_mram_cfg {
#define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT)
/* Tx event FIFO Element */
-/* E1 */
#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT
#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT)
-/* address offset and element number for each FIFO/Buffer in the Message RAM */
-struct mram_cfg {
- u16 off;
- u8 num;
-};
+static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
+{
+ u32 ret = -EINVAL;
-/* m_can private data structure */
-struct m_can_priv {
- struct can_priv can; /* must be the first member */
- struct napi_struct napi;
- struct net_device *dev;
- struct device *device;
- struct clk *hclk;
- struct clk *cclk;
- void __iomem *base;
- u32 irqstatus;
- int version;
-
- /* message ram configuration */
- void __iomem *mram_base;
- struct mram_cfg mcfg[MRAM_CFG_NUM];
-};
+ if (priv->ops->read_reg)
+ ret = priv->ops->read_reg(priv, reg);
+
+ return ret;
+}
-static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg)
+static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
{
- return readl(priv->base + reg);
+ int ret = -EINVAL;
+
+ if (priv->ops->write_reg)
+ ret = priv->ops->write_reg(priv, reg, val);
+
+ return ret;
}
-static inline void m_can_write(const struct m_can_priv *priv,
- enum m_can_reg reg, u32 val)
+static u32 m_can_fifo_read(struct m_can_priv *priv,
+ u32 fgi, unsigned int offset)
{
- writel(val, priv->base + reg);
+ u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE + offset;
+ u32 ret = -EINVAL;
+
+ if (priv->ops->read_fifo)
+ ret = priv->ops->read_fifo(priv, addr_offset);
+
+ return ret;
}
-static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
- u32 fgi, unsigned int offset)
+static u32 m_can_fifo_write(struct m_can_priv *priv,
+ u32 fpi, unsigned int offset, u32 val)
{
- return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
- fgi * RXF0_ELEMENT_SIZE + offset);
+ u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE + offset;
+ u32 ret = -EINVAL;
+
+ if (priv->ops->write_fifo)
+ ret = priv->ops->write_fifo(priv, addr_offset, val);
+
+ return ret;
}
-static inline void m_can_fifo_write(const struct m_can_priv *priv,
- u32 fpi, unsigned int offset, u32 val)
+static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
+ u32 fpi, u32 val)
{
- writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
- fpi * TXB_ELEMENT_SIZE + offset);
+ u32 ret = 0;
+
+ if (priv->ops->write_fifo)
+ ret = priv->ops->write_fifo(priv, fpi, val);
+
+ return ret;
}
-static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
- u32 fgi,
- u32 offset) {
- return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
- fgi * TXE_ELEMENT_SIZE + offset);
+static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
+{
+ u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE + offset;
+ u32 ret = -EINVAL;
+
+ if (priv->ops->read_fifo)
+ ret = priv->ops->read_fifo(priv, addr_offset);
+
+ return ret;
}
-static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
+static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
{
return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
}
-static inline void m_can_config_endisable(const struct m_can_priv *priv,
- bool enable)
+void m_can_config_endisable(struct m_can_priv *priv, bool enable)
{
u32 cccr = m_can_read(priv, M_CAN_CCCR);
u32 timeout = 10;
u32 val = 0;
+ if (cccr & CCCR_CSR)
+ cccr &= ~CCCR_CSR;
+
if (enable) {
/* enable m_can configuration */
m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
@@ -430,7 +365,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
if (timeout == 0) {
- netdev_warn(priv->dev, "Failed to init module\n");
+ netdev_warn(priv->net, "Failed to init module\n");
return;
}
timeout--;
@@ -438,13 +373,13 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
}
}
-static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
+static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
{
/* Only interrupt line 0 is used in this driver */
m_can_write(priv, M_CAN_ILE, ILE_EINT0);
}
-static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
+static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
{
m_can_write(priv, M_CAN_ILE, 0x0);
}
@@ -633,9 +568,12 @@ static int m_can_clk_start(struct m_can_priv *priv)
{
int err;
- err = pm_runtime_get_sync(priv->device);
+ if (priv->pm_clock_support == 0)
+ return 0;
+
+ err = pm_runtime_get_sync(priv->dev);
if (err < 0) {
- pm_runtime_put_noidle(priv->device);
+ pm_runtime_put_noidle(priv->dev);
return err;
}
@@ -644,7 +582,8 @@ static int m_can_clk_start(struct m_can_priv *priv)
static void m_can_clk_stop(struct m_can_priv *priv)
{
- pm_runtime_put_sync(priv->device);
+ if (priv->pm_clock_support)
+ pm_runtime_put_sync(priv->dev);
}
static int m_can_get_berr_counter(const struct net_device *dev,
@@ -811,9 +750,8 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
return work_done;
}
-static int m_can_poll(struct napi_struct *napi, int quota)
+static int m_can_rx_handler(struct net_device *dev, int quota)
{
- struct net_device *dev = napi->dev;
struct m_can_priv *priv = netdev_priv(dev);
int work_done = 0;
u32 irqstatus, psr;
@@ -831,13 +769,33 @@ static int m_can_poll(struct napi_struct *napi, int quota)
if (irqstatus & IR_RF0N)
work_done += m_can_do_rx_poll(dev, (quota - work_done));
+end:
+ return work_done;
+}
+
+static int m_can_rx_peripherial(struct net_device *dev)
+{
+ struct m_can_priv *priv = netdev_priv(dev);
+
+ m_can_rx_handler(dev, 1);
+
+ m_can_enable_all_interrupts(priv);
+
+ return 0;
+}
+
+static int m_can_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *dev = napi->dev;
+ struct m_can_priv *priv = netdev_priv(dev);
+ int work_done = 0;
+ work_done = m_can_rx_handler(dev, quota);
if (work_done < quota) {
napi_complete_done(napi, work_done);
m_can_enable_all_interrupts(priv);
}
-end:
return work_done;
}
@@ -902,7 +860,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
priv->irqstatus = ir;
m_can_disable_all_interrupts(priv);
- napi_schedule(&priv->napi);
+ if (!priv->is_peripherial)
+ napi_schedule(&priv->napi);
+ else
+ m_can_rx_peripherial(dev);
}
if (priv->version == 30) {
@@ -924,6 +885,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
}
}
+ if (priv->ops->clr_dev_interrupts)
+ priv->ops->clr_dev_interrupts(priv);
+
return IRQ_HANDLED;
}
@@ -1155,6 +1119,9 @@ static void m_can_chip_config(struct net_device *dev)
m_can_set_bittiming(dev);
m_can_config_endisable(priv, false);
+
+ if (priv->ops->device_init)
+ priv->ops->device_init(priv);
}
static void m_can_start(struct net_device *dev)
@@ -1188,20 +1155,17 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
* else it returns the release and step coded as:
* return value = 10 * <release> + 1 * <step>
*/
-static int m_can_check_core_release(void __iomem *m_can_base)
+static int m_can_check_core_release(struct m_can_priv *priv)
{
u32 crel_reg;
u8 rel;
u8 step;
int res;
- struct m_can_priv temp_priv = {
- .base = m_can_base
- };
/* Read Core Release Version and split into version number
* Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
*/
- crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
+ crel_reg = m_can_read(priv, M_CAN_CREL);
rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
@@ -1219,18 +1183,22 @@ static int m_can_check_core_release(void __iomem *m_can_base)
/* Selectable Non ISO support only in version 3.2.x
* This function checks if the bit is writable.
*/
-static bool m_can_niso_supported(const struct m_can_priv *priv)
+static bool m_can_niso_supported(struct m_can_priv *priv)
{
- u32 cccr_reg, cccr_poll;
- int niso_timeout;
+ u32 cccr_reg, cccr_poll = 0;
+ int niso_timeout = -ETIMEDOUT;
+ int i;
m_can_config_endisable(priv, true);
cccr_reg = m_can_read(priv, M_CAN_CCCR);
cccr_reg |= CCCR_NISO;
m_can_write(priv, M_CAN_CCCR, cccr_reg);
- niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
- (cccr_poll == cccr_reg), 0, 10);
+ for (i = 0; i <= 10; i++) {
+ cccr_poll = m_can_read(priv, M_CAN_CCCR);
+ if (cccr_poll == cccr_reg)
+ niso_timeout = 0;
+ }
/* Clear NISO */
cccr_reg &= ~(CCCR_NISO);
@@ -1242,107 +1210,95 @@ static bool m_can_niso_supported(const struct m_can_priv *priv)
return !niso_timeout;
}
-static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
- void __iomem *addr)
+static int m_can_dev_setup(struct m_can_priv *m_can_dev)
{
- struct m_can_priv *priv;
+ struct net_device *dev = m_can_dev->net;
int m_can_version;
- m_can_version = m_can_check_core_release(addr);
+ m_can_version = m_can_check_core_release(m_can_dev);
/* return if unsupported version */
if (!m_can_version) {
- dev_err(&pdev->dev, "Unsupported version number: %2d",
+ dev_err(m_can_dev->dev, "Unsupported version number: %2d",
m_can_version);
return -EINVAL;
}
- priv = netdev_priv(dev);
- netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
+ if (!m_can_dev->is_peripherial)
+ netif_napi_add(dev, &m_can_dev->napi,
+ m_can_poll, M_CAN_NAPI_WEIGHT);
/* Shared properties of all M_CAN versions */
- priv->version = m_can_version;
- priv->dev = dev;
- priv->base = addr;
- priv->can.do_set_mode = m_can_set_mode;
- priv->can.do_get_berr_counter = m_can_get_berr_counter;
+ m_can_dev->version = m_can_version;
+ m_can_dev->can.do_set_mode = m_can_set_mode;
+ m_can_dev->can.do_get_berr_counter = m_can_get_berr_counter;
/* Set M_CAN supported operations */
- priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+ m_can_dev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_FD;
/* Set properties depending on M_CAN version */
- switch (priv->version) {
+ switch (m_can_dev->version) {
case 30:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
- priv->can.bittiming_const = &m_can_bittiming_const_30X;
- priv->can.data_bittiming_const =
+ if (m_can_dev->bit_timing)
+ m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+ else
+ m_can_dev->can.bittiming_const =
+ &m_can_bittiming_const_30X;
+ if (m_can_dev->data_timing)
+ m_can_dev->can.data_bittiming_const =
+ m_can_dev->data_timing;
+ else
+ m_can_dev->can.data_bittiming_const =
&m_can_data_bittiming_const_30X;
break;
case 31:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
- priv->can.bittiming_const = &m_can_bittiming_const_31X;
- priv->can.data_bittiming_const =
+ if (m_can_dev->bit_timing)
+ m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+ else
+ m_can_dev->can.bittiming_const =
+ &m_can_bittiming_const_31X;
+ if (m_can_dev->data_timing)
+ m_can_dev->can.data_bittiming_const =
+ m_can_dev->data_timing;
+ else
+ m_can_dev->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
break;
case 32:
- priv->can.bittiming_const = &m_can_bittiming_const_31X;
- priv->can.data_bittiming_const =
+ if (m_can_dev->bit_timing)
+ m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+ else
+ m_can_dev->can.bittiming_const =
+ &m_can_bittiming_const_31X;
+
+ if (m_can_dev->data_timing)
+ m_can_dev->can.data_bittiming_const =
+ m_can_dev->data_timing;
+ else
+ m_can_dev->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
- priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
+
+ m_can_dev->can.ctrlmode_supported |=
+ (m_can_niso_supported(m_can_dev)
? CAN_CTRLMODE_FD_NON_ISO
: 0);
break;
default:
- dev_err(&pdev->dev, "Unsupported version number: %2d",
- priv->version);
+ dev_err(m_can_dev->dev, "Unsupported version number: %2d",
+ m_can_dev->version);
return -EINVAL;
}
- return 0;
-}
-
-static int m_can_open(struct net_device *dev)
-{
- struct m_can_priv *priv = netdev_priv(dev);
- int err;
-
- err = m_can_clk_start(priv);
- if (err)
- return err;
-
- /* open the can device */
- err = open_candev(dev);
- if (err) {
- netdev_err(dev, "failed to open can device\n");
- goto exit_disable_clks;
- }
-
- /* register interrupt handler */
- err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
- dev);
- if (err < 0) {
- netdev_err(dev, "failed to request interrupt\n");
- goto exit_irq_fail;
- }
-
- /* start the m_can controller */
- m_can_start(dev);
-
- can_led_event(dev, CAN_LED_EVENT_OPEN);
- napi_enable(&priv->napi);
- netif_start_queue(dev);
+ if (m_can_dev->ops->device_init)
+ m_can_dev->ops->device_init(m_can_dev);
return 0;
-
-exit_irq_fail:
- close_candev(dev);
-exit_disable_clks:
- m_can_clk_stop(priv);
- return err;
}
static void m_can_stop(struct net_device *dev)
@@ -1361,10 +1317,17 @@ static int m_can_close(struct net_device *dev)
struct m_can_priv *priv = netdev_priv(dev);
netif_stop_queue(dev);
- napi_disable(&priv->napi);
+ if (!priv->is_peripherial)
+ napi_disable(&priv->napi);
m_can_stop(dev);
m_can_clk_stop(priv);
free_irq(dev->irq, dev);
+
+ if (priv->is_peripherial) {
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+ }
+
close_candev(dev);
can_led_event(dev, CAN_LED_EVENT_STOP);
@@ -1385,18 +1348,15 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
return !!priv->can.echo_skb[next_idx];
}
-static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static void m_can_tx_handler(struct m_can_priv *priv)
{
- struct m_can_priv *priv = netdev_priv(dev);
- struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ struct canfd_frame *cf = (struct canfd_frame *)priv->skb->data;
+ struct net_device *dev = priv->net;
+ struct sk_buff *skb = priv->skb;
u32 id, cccr, fdflags;
int i;
int putidx;
- if (can_dropped_invalid_skb(dev, skb))
- return NETDEV_TX_OK;
-
/* Generate ID field for TX buffer Element */
/* Common to all supported M_CAN versions */
if (cf->can_id & CAN_EFF_FLAG) {
@@ -1451,7 +1411,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
netif_stop_queue(dev);
netdev_warn(dev,
"TX queue active although FIFO is full.");
- return NETDEV_TX_BUSY;
+ return;
}
/* get put index for frame */
@@ -1492,14 +1452,101 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
m_can_write(priv, M_CAN_TXBAR, (1 << putidx));
/* stop network queue if fifo full */
- if (m_can_tx_fifo_full(priv) ||
- m_can_next_echo_skb_occupied(dev, putidx))
- netif_stop_queue(dev);
+ if (m_can_tx_fifo_full(priv) ||
+ m_can_next_echo_skb_occupied(dev, putidx))
+ netif_stop_queue(dev);
+ }
+}
+
+static void m_can_tx_work_queue(struct work_struct *ws)
+{
+ struct m_can_priv *priv = container_of(ws, struct m_can_priv,
+ tx_work);
+ m_can_tx_handler(priv);
+}
+
+static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct m_can_priv *priv = netdev_priv(dev);
+
+
+ if (can_dropped_invalid_skb(dev, skb))
+ return NETDEV_TX_OK;
+
+ priv->skb = skb;
+ if (priv->is_peripherial) {
+ netif_stop_queue(priv->net);
+ queue_work(priv->wq, &priv->tx_work);
+ } else {
+ m_can_tx_handler(priv);
}
return NETDEV_TX_OK;
}
+static int m_can_open(struct net_device *dev)
+{
+ struct m_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = m_can_clk_start(priv);
+ if (err)
+ return err;
+
+ /* open the can device */
+ err = open_candev(dev);
+ if (err) {
+ netdev_err(dev, "failed to open can device\n");
+ goto exit_disable_clks;
+ }
+
+ /* register interrupt handler */
+ if (priv->is_peripherial) {
+ priv->wq = alloc_workqueue("mcan_wq",
+ WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
+ if (!priv->wq) {
+ err = -ENOMEM;
+ goto out_wq_fail;
+ }
+
+ INIT_WORK(&priv->tx_work, m_can_tx_work_queue);
+
+ err = request_threaded_irq(dev->irq, NULL, m_can_isr,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ dev->name, dev);
+ } else {
+ err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
+ dev);
+ }
+
+ if (err < 0) {
+ netdev_err(dev, "failed to request interrupt\n");
+ goto exit_irq_fail;
+ }
+
+ /* start the m_can controller */
+ m_can_start(dev);
+
+ can_led_event(dev, CAN_LED_EVENT_OPEN);
+
+ if (!priv->is_peripherial)
+ napi_enable(&priv->napi);
+
+ netif_start_queue(dev);
+
+ return 0;
+
+exit_irq_fail:
+ if (priv->is_peripherial)
+ destroy_workqueue(priv->wq);
+out_wq_fail:
+ close_candev(dev);
+exit_disable_clks:
+ m_can_clk_stop(priv);
+ return err;
+}
+
static const struct net_device_ops m_can_netdev_ops = {
.ndo_open = m_can_open,
.ndo_stop = m_can_close,
@@ -1515,20 +1562,6 @@ static int register_m_can_dev(struct net_device *dev)
return register_candev(dev);
}
-static void m_can_init_ram(struct m_can_priv *priv)
-{
- int end, i, start;
-
- /* initialize the entire Message RAM in use to avoid possible
- * ECC/parity checksum errors when reading an uninitialized buffer
- */
- start = priv->mcfg[MRAM_SIDF].off;
- end = priv->mcfg[MRAM_TXB].off +
- priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
- for (i = start; i < end; i += 4)
- writel(0x0, priv->mram_base + i);
-}
-
static void m_can_of_parse_mram(struct m_can_priv *priv,
const u32 *mram_config_vals)
{
@@ -1556,9 +1589,8 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
- dev_dbg(priv->device,
- "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
- priv->mram_base,
+ dev_dbg(priv->dev,
+ "sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
priv->mcfg[MRAM_RXF0].off, priv->mcfg[MRAM_RXF0].num,
@@ -1566,63 +1598,55 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
priv->mcfg[MRAM_RXB].off, priv->mcfg[MRAM_RXB].num,
priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
-
- m_can_init_ram(priv);
}
-static int m_can_plat_probe(struct platform_device *pdev)
+void m_can_init_ram(struct m_can_priv *priv)
{
- struct net_device *dev;
- struct m_can_priv *priv;
- struct resource *res;
- void __iomem *addr;
- void __iomem *mram_addr;
- struct clk *hclk, *cclk;
- int irq, ret;
- struct device_node *np;
- u32 mram_config_vals[MRAM_CFG_LEN];
- u32 tx_fifo_size;
-
- np = pdev->dev.of_node;
+ int end, i, start;
- hclk = devm_clk_get(&pdev->dev, "hclk");
- cclk = devm_clk_get(&pdev->dev, "cclk");
+ /* initialize the entire Message RAM in use to avoid possible
+ * ECC/parity checksum errors when reading an uninitialized buffer
+ */
+ start = priv->mcfg[MRAM_SIDF].off;
+ end = priv->mcfg[MRAM_TXB].off +
+ priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
- if (IS_ERR(hclk) || IS_ERR(cclk)) {
- dev_err(&pdev->dev, "no clock found\n");
- ret = -ENODEV;
- goto failed_ret;
- }
+ for (i = start; i < end; i += 4)
+ m_can_fifo_write_no_off(priv, i, 0x0);
+}
+EXPORT_SYMBOL_GPL(m_can_init_ram);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
- addr = devm_ioremap_resource(&pdev->dev, res);
- irq = platform_get_irq_byname(pdev, "int0");
+int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
+{
+ int ret = 0;
- if (IS_ERR(addr) || irq < 0) {
- ret = -EINVAL;
- goto failed_ret;
- }
+ m_can_dev->hclk = devm_clk_get(m_can_dev->dev, "hclk");
+ m_can_dev->cclk = devm_clk_get(m_can_dev->dev, "cclk");
- /* message ram could be shared */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
- if (!res) {
+ if (IS_ERR(m_can_dev->cclk)) {
+ dev_err(m_can_dev->dev, "no clock found\n");
ret = -ENODEV;
- goto failed_ret;
}
- mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!mram_addr) {
- ret = -ENOMEM;
- goto failed_ret;
- }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
+
+struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
+{
+ struct m_can_priv *class_dev = NULL;
+ u32 mram_config_vals[MRAM_CFG_LEN];
+ struct net_device *net_dev;
+ u32 tx_fifo_size;
+ int ret;
- /* get message ram configuration */
- ret = of_property_read_u32_array(np, "bosch,mram-cfg",
- mram_config_vals,
- sizeof(mram_config_vals) / 4);
+ ret = fwnode_property_read_u32_array(dev_fwnode(dev),
+ "bosch,mram-cfg",
+ mram_config_vals,
+ sizeof(mram_config_vals) / 4);
if (ret) {
- dev_err(&pdev->dev, "Could not get Message RAM configuration.");
- goto failed_ret;
+ dev_err(dev, "Could not get Message RAM configuration.");
+ goto out;
}
/* Get TX FIFO size
@@ -1631,66 +1655,74 @@ static int m_can_plat_probe(struct platform_device *pdev)
tx_fifo_size = mram_config_vals[7];
/* allocate the m_can device */
- dev = alloc_candev(sizeof(*priv), tx_fifo_size);
- if (!dev) {
- ret = -ENOMEM;
- goto failed_ret;
+ net_dev = alloc_candev(sizeof(*class_dev), tx_fifo_size);
+ if (!net_dev) {
+ dev_err(dev, "Failed to allocate CAN device");
+ goto out;
}
- priv = netdev_priv(dev);
- dev->irq = irq;
- priv->device = &pdev->dev;
- priv->hclk = hclk;
- priv->cclk = cclk;
- priv->can.clock.freq = clk_get_rate(cclk);
- priv->mram_base = mram_addr;
+ class_dev = netdev_priv(net_dev);
+ if (!class_dev) {
+ dev_err(dev, "Failed to init netdev private");
+ goto out;
+ }
- platform_set_drvdata(pdev, dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
+ class_dev->net = net_dev;
+ class_dev->dev = dev;
+ SET_NETDEV_DEV(net_dev, dev);
- /* Enable clocks. Necessary to read Core Release in order to determine
- * M_CAN version
- */
- pm_runtime_enable(&pdev->dev);
- ret = m_can_clk_start(priv);
- if (ret)
- goto pm_runtime_fail;
+ m_can_of_parse_mram(class_dev, mram_config_vals);
+out:
+ return class_dev;
+}
+EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
+
+int m_can_class_register(struct m_can_priv *m_can_dev)
+{
+ int ret;
- ret = m_can_dev_setup(pdev, dev, addr);
+ if (m_can_dev->pm_clock_support) {
+ pm_runtime_enable(m_can_dev->dev);
+ ret = m_can_clk_start(m_can_dev);
+ if (ret)
+ goto pm_runtime_fail;
+ }
+
+ ret = m_can_dev_setup(m_can_dev);
if (ret)
goto clk_disable;
- ret = register_m_can_dev(dev);
+ ret = register_m_can_dev(m_can_dev->net);
if (ret) {
- dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
- KBUILD_MODNAME, ret);
+ dev_err(m_can_dev->dev, "registering %s failed (err=%d)\n",
+ m_can_dev->net->name, ret);
goto clk_disable;
}
- m_can_of_parse_mram(priv, mram_config_vals);
-
- devm_can_led_init(dev);
+ devm_can_led_init(m_can_dev->net);
- of_can_transceiver(dev);
+ of_can_transceiver(m_can_dev->net);
- dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
- KBUILD_MODNAME, dev->irq, priv->version);
+ dev_info(m_can_dev->dev, "%s device registered (irq=%d, version=%d)\n",
+ KBUILD_MODNAME, m_can_dev->net->irq, m_can_dev->version);
/* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened
*/
clk_disable:
- m_can_clk_stop(priv);
+ m_can_clk_stop(m_can_dev);
pm_runtime_fail:
if (ret) {
- pm_runtime_disable(&pdev->dev);
- free_candev(dev);
+ if (m_can_dev->pm_clock_support)
+ pm_runtime_disable(m_can_dev->dev);
+ free_candev(m_can_dev->net);
}
-failed_ret:
+
return ret;
}
+EXPORT_SYMBOL_GPL(m_can_class_register);
-static __maybe_unused int m_can_suspend(struct device *dev)
+int m_can_class_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct m_can_priv *priv = netdev_priv(ndev);
@@ -1708,8 +1740,9 @@ static __maybe_unused int m_can_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(m_can_class_suspend);
-static __maybe_unused int m_can_resume(struct device *dev)
+int m_can_class_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct m_can_priv *priv = netdev_priv(ndev);
@@ -1733,79 +1766,19 @@ static __maybe_unused int m_can_resume(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(m_can_class_resume);
-static void unregister_m_can_dev(struct net_device *dev)
-{
- unregister_candev(dev);
-}
-
-static int m_can_plat_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
-
- unregister_m_can_dev(dev);
-
- pm_runtime_disable(&pdev->dev);
-
- platform_set_drvdata(pdev, NULL);
-
- free_candev(dev);
-
- return 0;
-}
-
-static int __maybe_unused m_can_runtime_suspend(struct device *dev)
-{
- struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *priv = netdev_priv(ndev);
-
- clk_disable_unprepare(priv->cclk);
- clk_disable_unprepare(priv->hclk);
-
- return 0;
-}
-
-static int __maybe_unused m_can_runtime_resume(struct device *dev)
+void m_can_class_unregister(struct m_can_priv *m_can_dev)
{
- struct net_device *ndev = dev_get_drvdata(dev);
- struct m_can_priv *priv = netdev_priv(ndev);
- int err;
-
- err = clk_prepare_enable(priv->hclk);
- if (err)
- return err;
+ unregister_candev(m_can_dev->net);
- err = clk_prepare_enable(priv->cclk);
- if (err)
- clk_disable_unprepare(priv->hclk);
+ m_can_clk_stop(m_can_dev);
- return err;
+ free_candev(m_can_dev->net);
}
-
-static const struct dev_pm_ops m_can_pmops = {
- SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
- m_can_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
-};
-
-static const struct of_device_id m_can_of_table[] = {
- { .compatible = "bosch,m_can", .data = NULL },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, m_can_of_table);
-
-static struct platform_driver m_can_plat_driver = {
- .driver = {
- .name = KBUILD_MODNAME,
- .of_match_table = m_can_of_table,
- .pm = &m_can_pmops,
- },
- .probe = m_can_plat_probe,
- .remove = m_can_plat_remove,
-};
-
-module_platform_driver(m_can_plat_driver);
+EXPORT_SYMBOL_GPL(m_can_class_unregister);
MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");
--
2.20.1.390.gb5101f9297
^ permalink raw reply related
* [PATCH v5 1/5] can: m_can: Create a m_can platform framework
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
In-Reply-To: <20190214182754.30721-1-dmurphy@ti.com>
Create a m_can platform framework that peripherial
devices can register to and use common code and register sets.
The peripherial devices may provide read/write and configuration
support of the IP.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
v5 - Created ops struct, renamed header to m_can.h, updated license and copyright
added MODULE_AUTHOR and removed unneeded changes - https://lore.kernel.org/patchwork/patch/1033094/
drivers/net/can/m_can/m_can.h | 159 ++++++++++++++++++++
drivers/net/can/m_can/m_can_platform.c | 198 +++++++++++++++++++++++++
2 files changed, 357 insertions(+)
create mode 100644 drivers/net/can/m_can/m_can.h
create mode 100644 drivers/net/can/m_can/m_can_platform.c
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
new file mode 100644
index 000000000000..36b1b833d41b
--- /dev/null
+++ b/drivers/net/can/m_can/m_can.h
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#ifndef _CAN_M_CAN_H_
+#define _CAN_M_CAN_H_
+
+#include <linux/can/core.h>
+#include <linux/can/led.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/can/dev.h>
+#include <linux/pinctrl/consumer.h>
+
+/* m_can lec values */
+enum m_can_lec_type {
+ LEC_NO_ERROR = 0,
+ LEC_STUFF_ERROR,
+ LEC_FORM_ERROR,
+ LEC_ACK_ERROR,
+ LEC_BIT1_ERROR,
+ LEC_BIT0_ERROR,
+ LEC_CRC_ERROR,
+ LEC_UNUSED,
+};
+
+enum m_can_mram_cfg {
+ MRAM_SIDF = 0,
+ MRAM_XIDF,
+ MRAM_RXF0,
+ MRAM_RXF1,
+ MRAM_RXB,
+ MRAM_TXE,
+ MRAM_TXB,
+ MRAM_CFG_NUM,
+};
+
+/* registers definition */
+enum m_can_reg {
+ M_CAN_CREL = 0x0,
+ M_CAN_ENDN = 0x4,
+ M_CAN_CUST = 0x8,
+ M_CAN_DBTP = 0xc,
+ M_CAN_TEST = 0x10,
+ M_CAN_RWD = 0x14,
+ M_CAN_CCCR = 0x18,
+ M_CAN_NBTP = 0x1c,
+ M_CAN_TSCC = 0x20,
+ M_CAN_TSCV = 0x24,
+ M_CAN_TOCC = 0x28,
+ M_CAN_TOCV = 0x2c,
+ M_CAN_ECR = 0x40,
+ M_CAN_PSR = 0x44,
+/* TDCR Register only available for version >=3.1.x */
+ M_CAN_TDCR = 0x48,
+ M_CAN_IR = 0x50,
+ M_CAN_IE = 0x54,
+ M_CAN_ILS = 0x58,
+ M_CAN_ILE = 0x5c,
+ M_CAN_GFC = 0x80,
+ M_CAN_SIDFC = 0x84,
+ M_CAN_XIDFC = 0x88,
+ M_CAN_XIDAM = 0x90,
+ M_CAN_HPMS = 0x94,
+ M_CAN_NDAT1 = 0x98,
+ M_CAN_NDAT2 = 0x9c,
+ M_CAN_RXF0C = 0xa0,
+ M_CAN_RXF0S = 0xa4,
+ M_CAN_RXF0A = 0xa8,
+ M_CAN_RXBC = 0xac,
+ M_CAN_RXF1C = 0xb0,
+ M_CAN_RXF1S = 0xb4,
+ M_CAN_RXF1A = 0xb8,
+ M_CAN_RXESC = 0xbc,
+ M_CAN_TXBC = 0xc0,
+ M_CAN_TXFQS = 0xc4,
+ M_CAN_TXESC = 0xc8,
+ M_CAN_TXBRP = 0xcc,
+ M_CAN_TXBAR = 0xd0,
+ M_CAN_TXBCR = 0xd4,
+ M_CAN_TXBTO = 0xd8,
+ M_CAN_TXBCF = 0xdc,
+ M_CAN_TXBTIE = 0xe0,
+ M_CAN_TXBCIE = 0xe4,
+ M_CAN_TXEFC = 0xf0,
+ M_CAN_TXEFS = 0xf4,
+ M_CAN_TXEFA = 0xf8,
+};
+
+/* address offset and element number for each FIFO/Buffer in the Message RAM */
+struct mram_cfg {
+ u16 off;
+ u8 num;
+};
+
+struct m_can_priv;
+struct m_can_ops {
+ /* Device specific call backs */
+ int (*clr_dev_interrupts) (struct m_can_priv *m_can_class);
+ u32 (*read_reg) (struct m_can_priv *m_can_class, int reg);
+ int (*write_reg) (struct m_can_priv *m_can_class, int reg, int val);
+ u32 (*read_fifo) (struct m_can_priv *m_can_class, int addr_offset);
+ int (*write_fifo) (struct m_can_priv *m_can_class, int addr_offset, int val);
+ int (*device_init) (struct m_can_priv *m_can_class);
+};
+
+struct m_can_priv {
+ struct can_priv can;
+ struct napi_struct napi;
+ struct net_device *net;
+ struct device *dev;
+ struct clk *hclk;
+ struct clk *cclk;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct sk_buff *skb;
+
+ struct can_bittiming_const *bit_timing;
+ struct can_bittiming_const *data_timing;
+
+ struct m_can_ops *ops;
+
+ void *device_data;
+
+ int version;
+ int freq;
+ u32 irqstatus;
+
+ int pm_clock_support;
+ bool is_peripherial;
+
+ struct mram_cfg mcfg[MRAM_CFG_NUM];
+};
+
+struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
+int m_can_class_register(struct m_can_priv *m_can_dev);
+void m_can_class_unregister(struct m_can_priv *m_can_dev);
+int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
+void m_can_init_ram(struct m_can_priv *priv);
+void m_can_config_endisable(struct m_can_priv *priv, bool enable);
+
+int m_can_class_suspend(struct device *dev);
+int m_can_class_resume(struct device *dev);
+#endif /* _CAN_M_H_ */
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
new file mode 100644
index 000000000000..d8d51bd64205
--- /dev/null
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+// IOMapped CAN bus driver for Bosch M_CAN controller
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+// Dong Aisheng <b29396@freescale.com>
+//
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/platform_device.h>
+
+#include "m_can.h"
+
+struct m_can_plat_priv {
+ void __iomem *base;
+ void __iomem *mram_base;
+};
+
+static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
+{
+ struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+ return readl(priv->base + reg);
+}
+
+static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
+{
+ struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+ return readl(priv->mram_base + offset);
+}
+
+static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
+{
+ struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+ writel(val, priv->base + reg);
+
+ return 0;
+}
+
+static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
+{
+ struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+ writel(val, priv->mram_base + offset);
+
+ return 0;
+}
+
+static struct m_can_ops m_can_plat_ops = {
+ .read_reg = iomap_read_reg,
+ .write_reg = iomap_write_reg,
+ .write_fifo = iomap_write_fifo,
+ .read_fifo = iomap_read_fifo,
+};
+
+static int m_can_plat_probe(struct platform_device *pdev)
+{
+ struct m_can_priv *mcan_class;
+ struct m_can_plat_priv *priv;
+ struct resource *res;
+ void __iomem *addr;
+ void __iomem *mram_addr;
+ int irq, ret = 0;
+
+ mcan_class = m_can_class_allocate_dev(&pdev->dev);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mcan_class->device_data = priv;
+
+ m_can_class_get_clocks(mcan_class);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ irq = platform_get_irq_byname(pdev, "int0");
+ if (IS_ERR(addr) || irq < 0) {
+ ret = -EINVAL;
+ goto failed_ret;
+ }
+
+ /* message ram could be shared */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
+ if (!res) {
+ ret = -ENODEV;
+ goto failed_ret;
+ }
+
+ mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!mram_addr) {
+ ret = -ENOMEM;
+ goto failed_ret;
+ }
+
+ priv->base = addr;
+ priv->mram_base = mram_addr;
+
+ mcan_class->net->irq = irq;
+ mcan_class->pm_clock_support = 1;
+ mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
+ mcan_class->dev = &pdev->dev;
+
+ mcan_class->ops = &m_can_plat_ops;
+
+ mcan_class->is_peripherial = false;
+
+ platform_set_drvdata(pdev, mcan_class->dev);
+
+ m_can_init_ram(mcan_class);
+
+ ret = m_can_class_register(mcan_class);
+
+failed_ret:
+ return ret;
+}
+
+static __maybe_unused int m_can_suspend(struct device *dev)
+{
+ return m_can_class_suspend(dev);
+}
+
+static __maybe_unused int m_can_resume(struct device *dev)
+{
+ return m_can_class_resume(dev);
+}
+
+static int m_can_plat_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct m_can_priv *mcan_class = netdev_priv(dev);
+
+ m_can_class_unregister(mcan_class);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static int __maybe_unused m_can_runtime_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct m_can_priv *mcan_class = netdev_priv(ndev);
+
+ m_can_class_suspend(dev);
+
+ clk_disable_unprepare(mcan_class->cclk);
+ clk_disable_unprepare(mcan_class->hclk);
+
+ return 0;
+}
+
+static int __maybe_unused m_can_runtime_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct m_can_priv *mcan_class = netdev_priv(ndev);
+ int err;
+
+ err = clk_prepare_enable(mcan_class->hclk);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(mcan_class->cclk);
+ if (err)
+ clk_disable_unprepare(mcan_class->hclk);
+
+ m_can_class_resume(dev);
+
+ return err;
+}
+
+static const struct dev_pm_ops m_can_pmops = {
+ SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
+ m_can_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
+};
+
+static const struct of_device_id m_can_of_table[] = {
+ { .compatible = "bosch,m_can", .data = NULL },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, m_can_of_table);
+
+static struct platform_driver m_can_plat_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = m_can_of_table,
+ .pm = &m_can_pmops,
+ },
+ .probe = m_can_plat_probe,
+ .remove = m_can_plat_remove,
+};
+
+module_platform_driver(m_can_plat_driver);
+
+MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");
--
2.20.1.390.gb5101f9297
^ permalink raw reply related
* [PATCH v5 0/5] M_CAN Framework re-write
From: Dan Murphy @ 2019-02-14 18:27 UTC (permalink / raw)
To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy
Hello
OK I did not give up on this patch series just got a little preoccupied with
some other kernel work. But here is the update per the comments.
It should be understood I broke these out for reviewability.
For instance the first patch does not compile on its own as including this
patch should not change the current functionality and it pulls all the io-mapped
code from the m_can base file to a platfrom file.
The next patch "Migrate the m_can code to use the framework"
is the change to the kernel for the io-mapped conversion from a flat file to use
the framework. Finally the rename patch just renames the m_can_priv to
m_can_classdev. I broke this change out specifically for readability of the
migration patch per comments on the code.
AFAIC the first 3 patches can all be squashed into a single patch. Or the
first 2 patches in the series can be re-arranged but then m_can functionality is
affected in the migration patch.
Again the first 3 patches here are all just for readability and review purposes.
Dan
Dan Murphy (5):
can: m_can: Create a m_can platform framework
can: m_can: Migrate the m_can code to use the framework
can: m_can: Rename m_can_priv to m_can_classdev
dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver
can: tcan4x5x: Add tcan4x5x driver to the kernel
.../devicetree/bindings/net/can/tcan4x5x.txt | 37 +
drivers/net/can/m_can/Kconfig | 14 +-
drivers/net/can/m_can/Makefile | 2 +
drivers/net/can/m_can/m_can.c | 788 +++++++++---------
drivers/net/can/m_can/m_can.h | 159 ++++
drivers/net/can/m_can/m_can_platform.c | 198 +++++
drivers/net/can/m_can/tcan4x5x.c | 531 ++++++++++++
7 files changed, 1320 insertions(+), 409 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/can/tcan4x5x.txt
create mode 100644 drivers/net/can/m_can/m_can.h
create mode 100644 drivers/net/can/m_can/m_can_platform.c
create mode 100644 drivers/net/can/m_can/tcan4x5x.c
--
2.20.1.390.gb5101f9297
^ permalink raw reply
* [PATCH bpf-next v2] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: Peter Oskolkov @ 2019-02-14 18:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, netdev
Cc: Peter Oskolkov, David Ahern, Willem de Bruijn, Peter Oskolkov
On error the skb should be freed. Tested with diff/steps
provided by David Ahern.
v2: surface routing errors to the user instead of a generic EINVAL,
as suggested by David Ahern.
Reported-by: David Ahern <dsahern@gmail.com>
Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c")
Signed-off-by: Peter Oskolkov <posk@google.com>
---
net/core/lwt_bpf.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
index 32251f3fcda0..a5c8c79d468a 100644
--- a/net/core/lwt_bpf.c
+++ b/net/core/lwt_bpf.c
@@ -179,17 +179,17 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
struct net_device *l3mdev = l3mdev_master_dev_rcu(skb_dst(skb)->dev);
int oif = l3mdev ? l3mdev->ifindex : 0;
struct dst_entry *dst = NULL;
+ int err = -EAFNOSUPPORT;
struct sock *sk;
struct net *net;
bool ipv4;
- int err;
if (skb->protocol == htons(ETH_P_IP))
ipv4 = true;
else if (skb->protocol == htons(ETH_P_IPV6))
ipv4 = false;
else
- return -EAFNOSUPPORT;
+ goto err;
sk = sk_to_full_sk(skb->sk);
if (sk) {
@@ -215,8 +215,10 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
fl4.saddr = iph->saddr;
rt = ip_route_output_key(net, &fl4);
- if (IS_ERR(rt))
- return -EINVAL;
+ if (IS_ERR(rt)) {
+ err = PTR_ERR(rt);
+ goto err;
+ }
dst = &rt->dst;
} else {
struct ipv6hdr *iph6 = ipv6_hdr(skb);
@@ -231,12 +233,17 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
fl6.saddr = iph6->saddr;
err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
- if (err || IS_ERR(dst))
- return -EINVAL;
+ if (unlikely(err))
+ goto err;
+ if (IS_ERR(dst)) {
+ err = PTR_ERR(dst);
+ goto err;
+ }
}
if (unlikely(dst->error)) {
+ err = dst->error;
dst_release(dst);
- return -EINVAL;
+ goto err;
}
/* Although skb header was reserved in bpf_lwt_push_ip_encap(), it
@@ -246,17 +253,21 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
*/
err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
if (unlikely(err))
- return err;
+ goto err;
skb_dst_drop(skb);
skb_dst_set(skb, dst);
err = dst_output(dev_net(skb_dst(skb)->dev), skb->sk, skb);
if (unlikely(err))
- return err;
+ goto err;
/* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
return LWTUNNEL_XMIT_DONE;
+
+err:
+ kfree_skb(skb);
+ return err;
}
static int bpf_xmit(struct sk_buff *skb)
--
2.21.0.rc0.258.g878e2cd30e-goog
^ permalink raw reply related
* Re: [PATCH bpf-next] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: Peter Oskolkov @ 2019-02-14 18:42 UTC (permalink / raw)
To: David Ahern
Cc: Peter Oskolkov, Alexei Starovoitov, Daniel Borkmann, netdev,
Willem de Bruijn
In-Reply-To: <733c9f8e-2262-dbff-6aa6-f960983812ab@gmail.com>
On Thu, Feb 14, 2019 at 10:11 AM David Ahern <dsahern@gmail.com> wrote:
>
> On 2/13/19 11:09 PM, Peter Oskolkov wrote:
> > On error the skb should be freed. Tested with diff/steps
> > provided by David Ahern.
> >
> > Reported-by: David Ahern <dsahern@gmail.com>
> > Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c")
> > Signed-off-by: Peter Oskolkov <posk@google.com>
> > ---
> > net/core/lwt_bpf.c | 24 ++++++++++++++++--------
> > 1 file changed, 16 insertions(+), 8 deletions(-)
> >
> > diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
> > index 32251f3fcda0..f3273cbb6b22 100644
> > --- a/net/core/lwt_bpf.c
> > +++ b/net/core/lwt_bpf.c
> > @@ -179,18 +179,19 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> > struct net_device *l3mdev = l3mdev_master_dev_rcu(skb_dst(skb)->dev);
> > int oif = l3mdev ? l3mdev->ifindex : 0;
> > struct dst_entry *dst = NULL;
> > + int err = -EAFNOSUPPORT;
> > struct sock *sk;
> > struct net *net;
> > bool ipv4;
> > - int err;
> >
> > if (skb->protocol == htons(ETH_P_IP))
> > ipv4 = true;
> > else if (skb->protocol == htons(ETH_P_IPV6))
> > ipv4 = false;
> > else
> > - return -EAFNOSUPPORT;
> > + goto err;
> >
> > + err = -EINVAL;
> > sk = sk_to_full_sk(skb->sk);
> > if (sk) {
> > if (sk->sk_bound_dev_if)
> > @@ -216,7 +217,7 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> >
> > rt = ip_route_output_key(net, &fl4);
> > if (IS_ERR(rt))
> > - return -EINVAL;
> > + goto err;
> > dst = &rt->dst;
> > } else {
> > struct ipv6hdr *iph6 = ipv6_hdr(skb);
> > @@ -231,12 +232,15 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> > fl6.saddr = iph6->saddr;
> >
> > err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
> > - if (err || IS_ERR(dst))
> > - return -EINVAL;
> > + if (err || IS_ERR(dst)) {
> > + err = -EINVAL;
> > + goto err;
> > + }
> > }
> > if (unlikely(dst->error)) {
> > dst_release(dst);
> > - return -EINVAL;
> > + err = -EINVAL;
> > + goto err;
> > }
> >
> > /* Although skb header was reserved in bpf_lwt_push_ip_encap(), it
>
> EINVAL is a confusing return code; it is not an EINVAL problem, it is a
> routing problem:
Thanks, David! Sent a v2 of the patch.
>
> ...
> starting egress IPv4 encap test
> ping: sendmsg: Invalid argument
> FAIL: test_ping: 1
>
>
> Versus returning the error from the lookup:
> ...
> starting egress IPv4 encap test
> ping: sendmsg: No route to host
> FAIL: test_ping: 1
>
>
> diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
> index f3273cbb6b22..a1901ba319fc 100644
> --- a/net/core/lwt_bpf.c
> +++ b/net/core/lwt_bpf.c
> @@ -191,7 +191,6 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> else
> goto err;
>
> - err = -EINVAL;
> sk = sk_to_full_sk(skb->sk);
> if (sk) {
> if (sk->sk_bound_dev_if)
> @@ -216,8 +215,10 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> fl4.saddr = iph->saddr;
>
> rt = ip_route_output_key(net, &fl4);
> - if (IS_ERR(rt))
> + if (IS_ERR(rt)) {
> + err = PTR_ERR(rt);
> goto err;
> + }
> dst = &rt->dst;
> } else {
> struct ipv6hdr *iph6 = ipv6_hdr(skb);
> @@ -232,14 +233,12 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> fl6.saddr = iph6->saddr;
>
> err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
> - if (err || IS_ERR(dst)) {
> - err = -EINVAL;
> + if (err || IS_ERR(dst))
> goto err;
> - }
> }
> if (unlikely(dst->error)) {
> dst_release(dst);
> - err = -EINVAL;
> + err = dst->error;
> goto err;
> }
>
>
>
>
> > @@ -246,17 +250,21 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
> > */
> > err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
> > if (unlikely(err))
> > - return err;
> > + goto err;
> >
> > skb_dst_drop(skb);
> > skb_dst_set(skb, dst);
> >
> > err = dst_output(dev_net(skb_dst(skb)->dev), skb->sk, skb);
> > if (unlikely(err))
> > - return err;
> > + goto err;
> >
> > /* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
> > return LWTUNNEL_XMIT_DONE;
> > +
> > +err:
> > + kfree_skb(skb);
> > + return err;
> > }
> >
> > static int bpf_xmit(struct sk_buff *skb)
> >
>
> I figured it was a leaked skb.
>
> Also, the test script needs to be updated as well with the negative
> tests -- ie., toggle the route from a dev/gateway to a reject
> (e.g.,unreachable) and back.
>
> Also, don't exit on the first failure - run all of them.
I'll refactor the test as you suggest here
when I add VRF and GRO tests in a couple of weeks, if this is OK.
>
> Having the result line up is more user friendly. e.g.,
>
> # ./fib_tests.sh
>
> Single path route test
> Start point
> TEST: IPv4 fibmatch [ OK ]
> TEST: IPv6 fibmatch [ OK ]
> Nexthop device deleted
> TEST: IPv4 fibmatch - no route [ OK ]
> TEST: IPv6 fibmatch - no route [ OK ]
> ...
^ permalink raw reply
* Re: [PATCH bpf-next v2] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: David Ahern @ 2019-02-14 19:04 UTC (permalink / raw)
To: Peter Oskolkov, Alexei Starovoitov, Daniel Borkmann, netdev
Cc: Peter Oskolkov, Willem de Bruijn
In-Reply-To: <20190214183931.20293-1-posk@google.com>
On 2/14/19 11:39 AM, Peter Oskolkov wrote:
> On error the skb should be freed. Tested with diff/steps
> provided by David Ahern.
>
> v2: surface routing errors to the user instead of a generic EINVAL,
> as suggested by David Ahern.
>
> Reported-by: David Ahern <dsahern@gmail.com>
> Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c")
> Signed-off-by: Peter Oskolkov <posk@google.com>
> ---
> net/core/lwt_bpf.c | 29 ++++++++++++++++++++---------
> 1 file changed, 20 insertions(+), 9 deletions(-)
>
Reviewed-by: David Ahern <dsahern@gmail.com>
^ permalink raw reply
* Re: [PATCH bpf-next] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: David Ahern @ 2019-02-14 19:10 UTC (permalink / raw)
To: Peter Oskolkov
Cc: Peter Oskolkov, Alexei Starovoitov, Daniel Borkmann, netdev,
Willem de Bruijn
In-Reply-To: <CAFTs51ULXLnGoBhA_JU4L86RpZifykJtsseawHKvcTUYN+Ar0g@mail.gmail.com>
On 2/14/19 11:42 AM, Peter Oskolkov wrote:
> I'll refactor the test as you suggest here
> when I add VRF and GRO tests in a couple of weeks, if this is OK.
IMO, the tests should go in with the feature, not a release later. If we
are at -rc6 this week then you might get next week as well.
The unreachable toggle is a fairly quick integration. GRO really should
also get in the same cycle as the feature. Preferably VRF tests as well
since you have the commands.
The pretty printing cleanup can be done later.
^ permalink raw reply
* Re: [PATCH] NETWORKING: avoid use IPCB in cipso_v4_error
From: Stephen Smalley @ 2019-02-14 18:59 UTC (permalink / raw)
To: Nazarov Sergey, Paul Moore
Cc: netdev@vger.kernel.org, linux-security-module@vger.kernel.org,
davem@davemloft.net, kuznet@ms2.inr.ac.ru,
yoshfuji@linux-ipv6.org
In-Reply-To: <258621550167251@sas1-46c84f197234.qloud-c.yandex.net>
On 2/14/19 1:00 PM, Nazarov Sergey wrote:
> Hi, Paul!
> I've found the problem and testing it with some very specific custom lsm module. The test case was simple:
> standard TCP/IP client-server application, where server opens CIPSO labeled TCP socket, and client connecting
> to this socket with forbidden labels. After several connections kernel crashing with general memory protection or
> kernel cache inconsistent error.
> I think, the similar behaviour should be with selinux or smack in the same conditions. But I don't know them
> so good to reproduce situation.
For SELinux, you can use
https://github.com/SELinuxProject/selinux-testsuite
That includes testing of CIPSO, both connecting from a client with an
authorized level and from a client with an unauthorized level.
Not sure about Smack; there were some tests in LTP but I don't know if
they would exercise it.
> After applying patch, I haven't kernel crashes.
> But now I've made additional checks and found no response icmp packets. The ip_options_compile requires
> CAP_NET_RAW capability when CIPSO option compiling, if skb is NULL. I have no other ideas than returning to
> the early patch version with ip_options_compile modified. What do you think about that?
>
> 14.02.2019, 00:42, "Paul Moore" <paul@paul-moore.com>:
>> On Tue, Feb 12, 2019 at 10:10 AM Nazarov Sergey <s-nazarov@yandex.ru> wrote:
>>> Since cipso_v4_error might be called from different network stack layers, we can't safely use icmp_send there.
>>> icmp_send copies IP options with ip_option_echo, which uses IPCB to take access to IP header compiled data.
>>> But after commit 971f10ec ("tcp: better TCP_SKB_CB layout to reduce cache line misses"), IPCB can't be used
>>> above IP layer.
>>> This patch fixes the problem by creating in cipso_v4_error a local copy of compiled IP options and using it with
>>> introduced __icmp_send function. This looks some overloaded, but in quite rare error conditions only.
>>>
>>> The original discussion is here:
>>> https://lore.kernel.org/linux-security-module/16659801547571984@sas1-890ba5c2334a.qloud-c.yandex.net/
>>>
>>> Signed-off-by: Sergey Nazarov <s-nazarov@yandex.ru>
>>> ---
>>> include/net/icmp.h | 9 ++++++++-
>>> net/ipv4/cipso_ipv4.c | 18 ++++++++++++++++--
>>> net/ipv4/icmp.c | 7 ++++---
>>> 3 files changed, 28 insertions(+), 6 deletions(-)
>>
>> Hi Sergey,
>>
>> Thanks for your work on finding this and putting a fix together. As
>> we discussed previously, I think this looks good, but can you describe
>> the testing you did to verify that this works correctly?
>>
>>> diff --git a/include/net/icmp.h b/include/net/icmp.h
>>> index 6ac3a5b..e0f709d 100644
>>> --- a/include/net/icmp.h
>>> +++ b/include/net/icmp.h
>>> @@ -22,6 +22,7 @@
>>>
>>> #include <net/inet_sock.h>
>>> #include <net/snmp.h>
>>> +#include <net/ip.h>
>>>
>>> struct icmp_err {
>>> int errno;
>>> @@ -39,7 +40,13 @@ struct icmp_err {
>>> struct sk_buff;
>>> struct net;
>>>
>>> -void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
>>> +void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
>>> + const struct ip_options *opt);
>>> +static inline void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>>> +{
>>> + __icmp_send(skb_in, type, code, info, &IPCB(skb_in)->opt);
>>> +}
>>> +
>>> int icmp_rcv(struct sk_buff *skb);
>>> int icmp_err(struct sk_buff *skb, u32 info);
>>> int icmp_init(void);
>>> diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
>>> index 777fa3b..234d12e 100644
>>> --- a/net/ipv4/cipso_ipv4.c
>>> +++ b/net/ipv4/cipso_ipv4.c
>>> @@ -1735,13 +1735,27 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
>>> */
>>> void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
>>> {
>>> + unsigned char optbuf[sizeof(struct ip_options) + 40];
>>> + struct ip_options *opt = (struct ip_options *)optbuf;
>>> +
>>> if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
>>> return;
>>>
>>> + /*
>>> + * We might be called above the IP layer,
>>> + * so we can not use icmp_send and IPCB here.
>>> + */
>>> +
>>> + memset(opt, 0, sizeof(struct ip_options));
>>> + opt->optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
>>> + memcpy(opt->__data, (unsigned char *)&(ip_hdr(skb)[1]), opt->optlen);
>>> + if (ip_options_compile(dev_net(skb->dev), opt, NULL))
>>> + return;
>>> +
>>> if (gateway)
>>> - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
>>> + __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0, opt);
>>> else
>>> - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
>>> + __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0, opt);
>>> }
>>>
>>> /**
>>> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
>>> index 065997f..3f24414 100644
>>> --- a/net/ipv4/icmp.c
>>> +++ b/net/ipv4/icmp.c
>>> @@ -570,7 +570,8 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
>>> * MUST reply to only the first fragment.
>>> */
>>>
>>> -void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>>> +void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
>>> + const struct ip_options *opt)
>>> {
>>> struct iphdr *iph;
>>> int room;
>>> @@ -691,7 +692,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>>> iph->tos;
>>> mark = IP4_REPLY_MARK(net, skb_in->mark);
>>>
>>> - if (ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in))
>>> + if (__ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in, opt))
>>> goto out_unlock;
>>>
>>> @@ -742,7 +743,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
>>> local_bh_enable();
>>> out:;
>>> }
>>> -EXPORT_SYMBOL(icmp_send);
>>> +EXPORT_SYMBOL(__icmp_send);
>>>
>>> static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
>>> --
>>
>> --
>> paul moore
>> www.paul-moore.com
^ permalink raw reply
* RE: [PATCH v1 net-next 2/4] net: dsa: microchip: add MIB counter reading support
From: Tristram.Ha @ 2019-02-14 19:26 UTC (permalink / raw)
To: f.fainelli; +Cc: sergio.paracuellos, pavel, UNGLinuxDriver, netdev, andrew
In-Reply-To: <BC575D76-0419-4318-B924-81E29858B8E9@gmail.com>
> >> > + /* read only dropped counters when link is not up */
> >> > + if (p->link_just_down)
> >> > + p->link_just_down = 0;
> >> > + else if (!p->phydev.link)
> >> > + mib->cnt_ptr = dev->reg_mib_cnt;
> >>
> >> This link_just_down stuff is not clear at all. Why can the drop
> >> counters not be read when the link is up?
> >
> >All of the MIB counters, except some that may be marked by driver, do
> >not get updated when the link is down, so it is a waste of time to read
> >them.
>
> Can you use netif_running() to determine that condition? Maintaining your
> own set of variables when the PHY state machine should already determine
> the link state sounds redundant if not error prone.
>
The driver can store the PHY device pointer passed to it when the port is enabled. But I am a little worried that pointer can be changed or completely gone as it is out of control of the driver.
> >My intention is the driver eventually reads the MIB counters at least
> >every second or faster so that the ethtool API called to show MIB
> >counters gets them from memory rather than starting a read operation.
> >For now that API is called from user space with the ethtool utility, so
> >it may not be called too often and too fast. But theoretically that
> >API can be called from a program continually.
> >
> >For simple switches that do not need to do anything special the MIB
> >read operation does not cause any issue except CPU load, for more
> >complicate switches that need to do some background operations too many
> >read operation can affect some critical functions.
>
> Some switches have a MIB autocast feature taking a snapshot which AFAIR is
> internally implemented as a fast read register with no contention on other
> registers internally, do you have something similar?
There is no such function in the switch. Every MIB counter read has to go through a single SPI transfer using indirect access. There are no table-like stored values that a single SPI transfer can retrieve all.
For i2C the access is even slower, but then I do not expect this access mechanism is used when the switch can do more complex things.
^ permalink raw reply
* Re: [PATCH bpf-next] bpf: fix memory leak in bpf_lwt_xmit_reroute
From: Peter Oskolkov @ 2019-02-14 19:28 UTC (permalink / raw)
To: David Ahern
Cc: Peter Oskolkov, Alexei Starovoitov, Daniel Borkmann, netdev,
Willem de Bruijn
In-Reply-To: <8f52efda-b502-2bfb-c881-8833bac461c2@gmail.com>
On Thu, Feb 14, 2019 at 11:10 AM David Ahern <dsahern@gmail.com> wrote:
>
> On 2/14/19 11:42 AM, Peter Oskolkov wrote:
> > I'll refactor the test as you suggest here
> > when I add VRF and GRO tests in a couple of weeks, if this is OK.
>
> IMO, the tests should go in with the feature, not a release later. If we
> are at -rc6 this week then you might get next week as well.
>
> The unreachable toggle is a fairly quick integration. GRO really should
> also get in the same cycle as the feature. Preferably VRF tests as well
> since you have the commands.
OK, I'll work on the negative tests and GRO first.
>
> The pretty printing cleanup can be done later.
^ 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