* Re: [RFC PATCH 3/5] sctp: Add LSM hooks
From: Richard Haines @ 2017-10-24 20:27 UTC (permalink / raw)
To: Xin Long
Cc: Neil Horman, selinux, network dev, linux-sctp,
linux-security-module
In-Reply-To: <CADvbK_cLGA4PH_c4FB0eSUAMoV0XgtyA++cTFsGDkb=nvtR68g@mail.gmail.com>
On Fri, 2017-10-20 at 21:14 +0800, Xin Long wrote:
> On Fri, Oct 20, 2017 at 8:04 PM, Richard Haines
> <richard_c_haines@btinternet.com> wrote:
> > On Fri, 2017-10-20 at 07:16 -0400, Neil Horman wrote:
> > > On Wed, Oct 18, 2017 at 11:05:09PM +0800, Xin Long wrote:
> > > > On Tue, Oct 17, 2017 at 9:58 PM, Richard Haines
> > > > <richard_c_haines@btinternet.com> wrote:
> > > > > Add security hooks to allow security modules to exercise
> > > > > access
> > > > > control
> > > > > over SCTP.
> > > > >
> > > > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.co
> > > > > m>
> > > > > ---
> > > > > include/net/sctp/structs.h | 10 ++++++++
> > > > > include/uapi/linux/sctp.h | 1 +
> > > > > net/sctp/sm_make_chunk.c | 12 +++++++++
> > > > > net/sctp/sm_statefuns.c | 14 ++++++++++-
> > > > > net/sctp/socket.c | 61
> > > > > +++++++++++++++++++++++++++++++++++++++++++++-
> > > > > 5 files changed, 96 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/include/net/sctp/structs.h
> > > > > b/include/net/sctp/structs.h
> > > > > index 7767577..6e72e3e 100644
> > > > > --- a/include/net/sctp/structs.h
> > > > > +++ b/include/net/sctp/structs.h
> > > > > @@ -1270,6 +1270,16 @@ struct sctp_endpoint {
> > > > > reconf_enable:1;
> > > > >
> > > > > __u8 strreset_enable;
> > > > > +
> > > > > + /* Security identifiers from incoming (INIT). These
> > > > > are
> > > > > set by
> > > > > + * security_sctp_assoc_request(). These will only be
> > > > > used
> > > > > by
> > > > > + * SCTP TCP type sockets and peeled off connections
> > > > > as
> > > > > they
> > > > > + * cause a new socket to be generated.
> > > > > security_sctp_sk_clone()
> > > > > + * will then plug these into the new socket.
> > > > > + */
> > > > > +
> > > > > + u32 secid;
> > > > > + u32 peer_secid;
> > > > > };
> > > > >
> > > > > /* Recover the outter endpoint structure. */
> > > > > diff --git a/include/uapi/linux/sctp.h
> > > > > b/include/uapi/linux/sctp.h
> > > > > index 6217ff8..c04812f 100644
> > > > > --- a/include/uapi/linux/sctp.h
> > > > > +++ b/include/uapi/linux/sctp.h
> > > > > @@ -122,6 +122,7 @@ typedef __s32 sctp_assoc_t;
> > > > > #define SCTP_RESET_ASSOC 120
> > > > > #define SCTP_ADD_STREAMS 121
> > > > > #define SCTP_SOCKOPT_PEELOFF_FLAGS 122
> > > > > +#define SCTP_SENDMSG_CONNECT 123
> > > > >
> > > > > /* PR-SCTP policies */
> > > > > #define SCTP_PR_SCTP_NONE 0x0000
> > > > > diff --git a/net/sctp/sm_make_chunk.c
> > > > > b/net/sctp/sm_make_chunk.c
> > > > > index 6110447..ca4705b 100644
> > > > > --- a/net/sctp/sm_make_chunk.c
> > > > > +++ b/net/sctp/sm_make_chunk.c
> > > > > @@ -3059,6 +3059,12 @@ static __be16
> > > > > sctp_process_asconf_param(struct sctp_association *asoc,
> > > > > if (af->is_any(&addr))
> > > > > memcpy(&addr, &asconf->source,
> > > > > sizeof(addr));
> > > > >
> > > > > + if (security_sctp_bind_connect(asoc->ep-
> > > > > >base.sk,
> > > > > + SCTP_PARAM_ADD
> > > > > _IP,
> > > > > + (struct
> > > > > sockaddr
> > > > > *)&addr,
> > > > > + af-
> > > > > >sockaddr_len))
> > > > > + return SCTP_ERROR_REQ_REFUSED;
> > > > > +
> > > > > /* ADDIP 4.3 D9) If an endpoint receives an
> > > > > ADD
> > > > > IP address
> > > > > * request and does not have the local
> > > > > resources
> > > > > to add this
> > > > > * new address to the association, it MUST
> > > > > return
> > > > > an Error
> > > > > @@ -3125,6 +3131,12 @@ static __be16
> > > > > sctp_process_asconf_param(struct sctp_association *asoc,
> > > > > if (af->is_any(&addr))
> > > > > memcpy(&addr.v4, sctp_source(asconf),
> > > > > sizeof(addr));
> > > > >
> > > > > + if (security_sctp_bind_connect(asoc->ep-
> > > > > >base.sk,
> > > > > + SCTP_PARAM_SET
> > > > > _PRI
> > > > > MARY,
> > > > > + (struct
> > > > > sockaddr
> > > > > *)&addr,
> > > > > + af-
> > > > > >sockaddr_len))
> > > > > + return SCTP_ERROR_REQ_REFUSED;
> > > > > +
> > > > > peer = sctp_assoc_lookup_paddr(asoc, &addr);
> > > > > if (!peer)
> > > > > return SCTP_ERROR_DNS_FAILED;
> > > > > diff --git a/net/sctp/sm_statefuns.c
> > > > > b/net/sctp/sm_statefuns.c
> > > > > index b2a74c3..4ba5805 100644
> > > > > --- a/net/sctp/sm_statefuns.c
> > > > > +++ b/net/sctp/sm_statefuns.c
> > > > > @@ -314,6 +314,11 @@ sctp_disposition_t
> > > > > sctp_sf_do_5_1B_init(struct net *net,
> > > > > sctp_unrecognized_param_t *unk_param;
> > > > > int len;
> > > > >
> > > > > + /* Update socket peer label if first association. */
> > > > > + if (security_sctp_assoc_request((struct sctp_endpoint
> > > > > *)ep,
> > > > > + chunk->skb,
> > > > > SCTP_CID_INIT))
> > > > > + return sctp_sf_pdiscard(net, ep, asoc, type,
> > > > > arg,
> > > > > commands);
> > > > > +
> > > > > /* 6.10 Bundling
> > > > > * An endpoint MUST NOT bundle INIT, INIT ACK or
> > > > > * SHUTDOWN COMPLETE with any other chunks.
> > > > > @@ -446,7 +451,6 @@ sctp_disposition_t
> > > > > sctp_sf_do_5_1B_init(struct net *net,
> > > > > }
> > > > >
> > > > > sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC,
> > > > > SCTP_ASOC(new_asoc));
> > > > > -
> > > > > sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
> > > > > SCTP_CHUNK(repl));
> > > > >
> > > > > /*
> > > > > @@ -507,6 +511,11 @@ sctp_disposition_t
> > > > > sctp_sf_do_5_1C_ack(struct net *net,
> > > > > struct sctp_chunk *err_chunk;
> > > > > struct sctp_packet *packet;
> > > > >
> > > > > + /* Update socket peer label if first association. */
> > > > > + if (security_sctp_assoc_request((struct sctp_endpoint
> > > > > *)ep,
> > > > > + chunk->skb,
> > > > > SCTP_CID_INIT_ACK))
> > > > > + return sctp_sf_pdiscard(net, ep, asoc, type,
> > > > > arg,
> > > > > commands);
> > > > > +
> > > >
> > > > Just thinking security_sctp_assoc_request hook should also be
> > > > in
> > > > sctp_sf_do_unexpected_init() and sctp_sf_do_5_2_4_dupcook() ?
> > >
> > > I agree, i think if this hook is gating on the creation of a new
> > > association,
> > > they should be in all the locations where that happens
> > > Neil
> >
> > Thanks for the comments. I've just added the hook as requested for
> > my
> > next update, however I have not managed to trigger these areas
> > using
> > the lksctp-tools tests. Can you suggest any suitable tools for
> > testing
> > these scenarios.
>
> It's all a matter of timing:
>
> sctp_sf_do_5_2_2_dupinit():
> Case A:
>
> Endpoint A Endpoint B ULP
> (CLOSED) (CLOSED)
>
> INIT ----------------->
>
> <----------------- INIT-ACK
>
> COOKIE-ECHO ----------------->
>
> <----------------- COOKIE-ACK
> Communication Up ---------->
> INIT ----------------->
> (Different INIT-TAG)
> <----------------- INIT-ACK
>
> COOKIE-ECHO ----------------->
>
> <----------------- COOKIE-ACK
>
> DATA ----------------->
>
> <----------------- SACK
>
>
> sctp_sf_do_5_2_1_siminit():
> Case B:
>
> Endpoint A Endpoint B ULP
> (CLOSED) (CLOSED)
>
> <--
> --- Associate
> <----------------- INIT
>
> INIT ----------------->
>
> <----------------- INIT-ACK
>
> COOKIE-ECHO ----------------->
>
> <----------------- COOKIE-ACK
> Communication Up ---------->
>
>
> sctp_sf_do_5_2_4_dupcook():
> Case D:
>
> Endpoint A Endpoint B ULP
> (CLOSED) (CLOSED)
>
> <--
> --- Associate
> INIT ----------------->
>
> <----------------- INIT-ACK
>
> COOKIE-ECHO ----------------->
>
> <----------------- COOKIE-ACK
> Communication Up ---------->
> COOKIE-ECHO ----------------->
>
> <----------------- COOKIE-ACK
>
> I think scapy could help with 4-shake stuff:
> # iptables -A OUTPUT -p sctp -d 192.0.0.2 --chunk-type only abort -o
> eth1 -j DROP
> and
> something like:
> def start_assoc(self, target, local):
> target_host, target_port = target
> local_host, local_port = local
>
> # init snd
> self._tsn = 2017
> self._cnt = 15
>
> SCTP_HEADER = (IP(dst=target_host, flags="DF") /
> SCTP(sport=local_port, dport=target_port, tag=0))
> INIT = (SCTP_HEADER / SCTPChunkInit(init_tag=1,
> a_rwnd=106496, n_out_streams=self._cnt, n_in_streams=self._cnt,
> init_tsn=self._tsn,
>
> params=[SCTPParamSupport(types=[64])]))
> INIT_ACK = sr1(INIT, timeout=3, verbose=0)
> if INIT_ACK == None or not
> INIT_ACK.haslayer(SCTPChunkInitAck):
> return False
>
> # cookie echo snd
> SCTP_HEADER[SCTP].tag =
> INIT_ACK[SCTPChunkInitAck].init_tag
> COOKIE_ECHO = (SCTP_HEADER /
> SCTPChunkCookieEcho(cookie=INIT_ACK[SCTPChunkParamStateCookie].cookie
> ))
> COOKIE_ACK = sr1(COOKIE_ECHO, timeout=3, verbose=0)
> if COOKIE_ACK == None or not
> COOKIE_ACK.haslayer(SCTPChunkCookieAck):
> return False
That looks a bit complicated for me so I found some SCTP Conformance
Test Tools at: https://github.com/nplab
I added the required hooks as suggested above and then built and ran
"ETSI-SCTP-Conformance-Testsuite" and "sctp-tests" with the following
specific tests for the above scenarios according to RFC2960 sections
5.2.2 and 5.2.4:
sctp-dm-o-4-8
sctp-as-o-1-9-1
sctp-as-o-1-9-2
sctp-dm-o-4-2-1
They all passed except when running:
"sctp-tests" runsctptest sctp-as-o-1-9-2 - TIMEOUT
This is because the SUT needs to reply with a new IP address that
required a modified test server (I just used a simple sctp server),
however the ETSI-SCTP-Conformance-Testsuite did pass as I guess that
provided the required IP address.
Are these tests okay ??
Does anyone on the list use these conformance tools ???
> --
> To unsubscribe from this list: send the line "unsubscribe linux-
> security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: KASAN: use-after-free Read in tipc_group_self
From: Cong Wang @ 2017-10-24 20:30 UTC (permalink / raw)
To: syzbot
Cc: David Miller, jon.maloy, LKML, Linux Kernel Network Developers,
syzkaller-bugs, tipc-discussion, ying.xue
In-Reply-To: <001a113f327c9352cd055c4c63c8@google.com>
On Tue, Oct 24, 2017 at 8:14 AM, syzbot
<bot+dbf0bb71d5568dfcd9d9cfddad8414c0d4f294c3@syzkaller.appspotmail.com>
wrote:
> Hello,
>
> syzkaller hit the following crash on
> 65302eba00aea2f1bc715f0438afb63470ac138b
> git://git.cmpxchg.org/linux-mmots.git/master
> compiler: gcc (GCC) 7.1.1 20170620
> .config is attached
> Raw console output is attached.
> C reproducer is attached
> syzkaller reproducer is attached. See https://goo.gl/kgGztJ
> for information about syzkaller reproducers
>
>
> BUG: KASAN: use-after-free in tipc_group_self+0x1a2/0x1b0
> net/tipc/group.c:335
> Read of size 4 at addr ffff8801d805726c by task syzkaller195348/2990
>
> CPU: 0 PID: 2990 Comm: syzkaller195348 Not tainted 4.14.0-rc5-mm1+ #19
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
> Google 01/01/2011
> Call Trace:
> __dump_stack lib/dump_stack.c:16 [inline]
> dump_stack+0x194/0x257 lib/dump_stack.c:52
> print_address_description+0x73/0x250 mm/kasan/report.c:252
> kasan_report_error mm/kasan/report.c:351 [inline]
> kasan_report+0x25b/0x340 mm/kasan/report.c:409
> __asan_report_load4_noabort+0x14/0x20 mm/kasan/report.c:429
> tipc_group_self+0x1a2/0x1b0 net/tipc/group.c:335
> tipc_sk_leave+0xfc/0x200 net/tipc/socket.c:2781
> tipc_release+0x154/0xfe0 net/tipc/socket.c:575
> sock_release+0x8d/0x1e0 net/socket.c:596
> sock_close+0x16/0x20 net/socket.c:1125
> __fput+0x327/0x7e0 fs/file_table.c:210
> ____fput+0x15/0x20 fs/file_table.c:244
> task_work_run+0x199/0x270 kernel/task_work.c:112
> exit_task_work include/linux/task_work.h:21 [inline]
> do_exit+0x9b5/0x1ad0 kernel/exit.c:869
> do_group_exit+0x149/0x400 kernel/exit.c:972
> SYSC_exit_group kernel/exit.c:983 [inline]
> SyS_exit_group+0x1d/0x20 kernel/exit.c:981
> entry_SYSCALL_64_fastpath+0x1f/0xbe
> RIP: 0033:0x43e978
> RSP: 002b:00007ffcae951f88 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
> RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 000000000043e978
> RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000
> RBP: 0000000000000082 R08: 00000000000000e7 R09: ffffffffffffffd0
> R10: 000000002010e000 R11: 0000000000000246 R12: 00000000006ca858
> R13: 00000000006ca858 R14: 0000000000000000 R15: 0000000000002710
>
> Allocated by task 2990:
> save_stack+0x43/0xd0 mm/kasan/kasan.c:447
> set_track mm/kasan/kasan.c:459 [inline]
> kasan_kmalloc+0xad/0xe0 mm/kasan/kasan.c:551
> kmem_cache_alloc_trace+0x136/0x750 mm/slab.c:3614
> kmalloc include/linux/slab.h:499 [inline]
> kzalloc include/linux/slab.h:688 [inline]
> tipc_group_create+0x116/0x9c0 net/tipc/group.c:167
> tipc_sk_join net/tipc/socket.c:2751 [inline]
> tipc_setsockopt+0x25e/0xc00 net/tipc/socket.c:2863
> SYSC_setsockopt net/socket.c:1851 [inline]
> SyS_setsockopt+0x189/0x360 net/socket.c:1830
> entry_SYSCALL_64_fastpath+0x1f/0xbe
>
> Freed by task 2990:
> save_stack+0x43/0xd0 mm/kasan/kasan.c:447
> set_track mm/kasan/kasan.c:459 [inline]
> kasan_slab_free+0x71/0xc0 mm/kasan/kasan.c:524
> __cache_free mm/slab.c:3492 [inline]
> kfree+0xca/0x250 mm/slab.c:3807
> tipc_group_delete+0x2c0/0x3c0 net/tipc/group.c:206
> tipc_sk_join net/tipc/socket.c:2764 [inline]
> tipc_setsockopt+0xb33/0xc00 net/tipc/socket.c:2863
> SYSC_setsockopt net/socket.c:1851 [inline]
> SyS_setsockopt+0x189/0x360 net/socket.c:1830
> entry_SYSCALL_64_fastpath+0x1f/0xbe
Should be fixed by:
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index b3b72d8e9543..ea61c32f6b80 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -2756,8 +2756,10 @@ static int tipc_sk_join(struct tipc_sock *tsk,
struct tipc_group_req *mreq)
seq.upper = seq.lower;
tipc_nametbl_build_group(net, grp, mreq->type, domain);
rc = tipc_sk_publish(tsk, mreq->scope, &seq);
- if (rc)
+ if (rc) {
tipc_group_delete(net, grp);
+ tsk->group = NULL;
+ }
/* Eliminate any risk that a broadcast overtakes the sent JOIN */
tsk->mc_method.rcast = true;
^ permalink raw reply related
* [PATCH net] net: dsa: check master device before put
From: Vivien Didelot @ 2017-10-24 20:37 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
Andrew Lunn, Vivien Didelot
In the case of pdata, the dsa_cpu_parse function calls dev_put() before
making sure it isn't NULL. Fix this.
Fixes: 71e0bbde0d88 ("net: dsa: Add support for platform data")
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
net/dsa/dsa2.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 873af0108e24..045d8a176279 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -496,14 +496,15 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index,
if (!ethernet)
return -EINVAL;
ethernet_dev = of_find_net_device_by_node(ethernet);
+ if (!ethernet_dev)
+ return -EPROBE_DEFER;
} else {
ethernet_dev = dsa_dev_to_net_device(ds->cd->netdev[index]);
+ if (!ethernet_dev)
+ return -EPROBE_DEFER;
dev_put(ethernet_dev);
}
- if (!ethernet_dev)
- return -EPROBE_DEFER;
-
if (!dst->cpu_dp) {
dst->cpu_dp = port;
dst->cpu_dp->netdev = ethernet_dev;
--
2.14.2
^ permalink raw reply related
* Re: [PATCH net 0/6] rtnetlink: a bunch of fixes for userspace notifications in changing dev properties
From: Stefano Brivio @ 2017-10-24 20:44 UTC (permalink / raw)
To: davem; +Cc: Xin Long, network dev, David Ahern, hannes
In-Reply-To: <cover.1508062280.git.lucien.xin@gmail.com>
On Sun, 15 Oct 2017 18:13:40 +0800
Xin Long <lucien.xin@gmail.com> wrote:
> Whenever any property of a link, address, route, etc. changes by whatever way,
> kernel should notify the programs that listen for such events in userspace.
>
> The patchet "rtnetlink: Cleanup user notifications for netdev events" tried to
> fix a redundant notifications issue, but it also introduced a side effect.
>
> After that, user notifications could only be sent when changing dev properties
> via netlink api. As it removed some events process in rtnetlink_event where
> the notifications was sent to users.
>
> It resulted in no notification generated when dev properties are changed via
> other ways, like ioctl, sysfs, etc. It may cause some user programs doesn't
> work as expected because of the missing notifications.
>
> This patchset will fix it by bringing some of these netdev events back and
> also fix the old redundant notifications issue with a proper way.
>
> Xin Long (6):
> rtnetlink: bring NETDEV_CHANGEMTU event process back in
> rtnetlink_event
> rtnetlink: bring NETDEV_CHANGE_TX_QUEUE_LEN event process back in
> rtnetlink_event
> rtnetlink: bring NETDEV_POST_TYPE_CHANGE event process back in
> rtnetlink_event
> rtnetlink: bring NETDEV_CHANGEUPPER event process back in
> rtnetlink_event
> rtnetlink: check DO_SETLINK_NOTIFY correctly in do_setlink
> rtnetlink: do not set notification for tx_queue_len in do_setlink
I guess this should be considered for -stable (back to 4.12), as it
fixes quite a few critical issues in notification behaviours userspace
might rely on.
--
Stefano
^ permalink raw reply
* Re: [PATCH net-next v3 06/10] bnxt: Add devlink support for config get/set
From: Michael Chan @ 2017-10-24 20:52 UTC (permalink / raw)
To: Steve Lin
Cc: Netdev, jiri, David Miller, John W. Linville, Andy Gospodarek,
yuvalm
In-Reply-To: <1508875962-39233-7-git-send-email-steven.lin1@broadcom.com>
On Tue, Oct 24, 2017 at 1:12 PM, Steve Lin <steven.lin1@broadcom.com> wrote:
> Implements get and set of configuration parameters using new devlink
> config get/set API. Parameters themselves defined in later patches.
>
> Signed-off-by: Steve Lin <steven.lin1@broadcom.com>
> Acked-by: Andy Gospodarek <gospo@broadcom.com>
> ---
> drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c | 262 +++++++++++++++++++++-
> drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h | 17 ++
> drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 100 +++++++++
> 3 files changed, 373 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
> index f3f6aa8..81ab77e 100644
> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
> @@ -14,11 +14,261 @@
> #include "bnxt_vfr.h"
> #include "bnxt_devlink.h"
>
> -static const struct devlink_ops bnxt_dl_ops = {
> +struct bnxt_drv_cfgparam bnxt_drv_cfgparam_list[] = {
> +};
> +
> +#define BNXT_NUM_DRV_CFGPARAM ARRAY_SIZE(bnxt_drv_cfgparam_list)
> +
> +static int bnxt_nvm_read(struct bnxt *bp, int nvm_param, int idx,
> + void *buf, int size)
> +{
> + struct hwrm_nvm_get_variable_input req = {0};
> + dma_addr_t dest_data_dma_addr;
> + void *dest_data_addr = NULL;
> + int bytesize;
> + int rc;
> +
> + bytesize = (size + 7) / BITS_PER_BYTE;
> + dest_data_addr = dma_alloc_coherent(&bp->pdev->dev, bytesize,
> + &dest_data_dma_addr, GFP_KERNEL);
> + if (!dest_data_addr) {
> + netdev_err(bp->dev, "dma_alloc_coherent failure\n");
> + return -ENOMEM;
> + }
> +
> + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_GET_VARIABLE, -1, -1);
> + req.dest_data_addr = cpu_to_le64(dest_data_dma_addr);
> + req.data_len = cpu_to_le16(size);
> + req.option_num = cpu_to_le16(nvm_param);
> + req.index_0 = cpu_to_le16(idx);
> + if (idx != 0)
> + req.dimensions = cpu_to_le16(1);
> +
> + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
This won't have the proper mutex protection. You should call
hwrm_send_message() instead.
^ permalink raw reply
* RE: [PATCH] wimax/i2400m: Remove VLAIS
From: Perez-Gonzalez, Inaky @ 2017-10-24 21:00 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: linux-wimax, netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Behan Webster, Mark Charlebois, Arnd Bergmann, Guenter Roeck
In-Reply-To: <20171024194659.GC96615@google.com>
> ping
> any comments on this?
None from me; I don't have access to this HW anymore, so I can't validate
if the change would work or not.
^ permalink raw reply
* RE: [PATCH net-next v3 01/10] devlink: Add permanent config parameter get/set operations
From: Yuval Mintz @ 2017-10-24 21:22 UTC (permalink / raw)
To: Steve Lin
Cc: Jiri Pirko, davem@davemloft.net, michael.chan@broadcom.com,
linville@tuxdriver.com, gospo@broadcom.com,
netdev@vger.kernel.org
In-Reply-To: <1508875962-39233-2-git-send-email-steven.lin1@broadcom.com>
> Add support for permanent config parameter get/set commands. Used
> for persistent device configuration parameters.
>
...
> + int (*perm_config_get)(struct devlink *devlink,
> + enum devlink_perm_config_param param, u8
> type,
> + void *value);
> + int (*perm_config_set)(struct devlink *devlink,
> + enum devlink_perm_config_param param, u8
> type,
> + void *value, u8 *restart_reqd);
> };
> +static int devlink_nl_single_param_get(struct sk_buff *msg,
> + struct devlink *devlink,
> + u32 param, u8 type)
> +{
> + const struct devlink_ops *ops = devlink->ops;
> + struct nlattr *param_attr;
> + void *value;
> + u32 val;
> + int err;
> +
> + /* Allocate buffer for parameter value */
> + switch (type) {
> + case NLA_U8:
> + value = kmalloc(sizeof(u8), GFP_KERNEL);
> + break;
> + case NLA_U16:
> + value = kmalloc(sizeof(u16), GFP_KERNEL);
> + break;
> + case NLA_U32:
> + value = kmalloc(sizeof(u32), GFP_KERNEL);
> + break;
> + default:
> + return -EINVAL; /* Unsupported Type */
> + }
> +
> + if (!value)
> + return -ENOMEM;
> +
> + err = ops->perm_config_get(devlink, param, type, value);
> + if (err)
> + return err;
I suspect this logic might be risky - its dependent on the driver to cast the
'value' into the proper type or else, E.g., the following switch might break
for BE platforms.
Is there any reason to have the devlink <-> driver API be based on void*
and not on some typed data [of sufficient size]?
...
> + switch (type) {
> + case NLA_U8:
> + val = *((u8 *)value);
> + if (nla_put_u8(msg, DEVLINK_ATTR_PERM_CONFIG_VALUE,
> val))
> + goto nest_err;
> + break;
> + case NLA_U16:
> + val = *((u16 *)value);
> + if (nla_put_u16(msg,
> DEVLINK_ATTR_PERM_CONFIG_VALUE, val))
> + goto nest_err;
> + break;
> + case NLA_U32:
> + val = *((u32 *)value);
> + if (nla_put_u32(msg,
> DEVLINK_ATTR_PERM_CONFIG_VALUE, val))
> + goto nest_err;
> + break;
> + }
...
> +static int devlink_nl_single_param_set(struct sk_buff *msg,
> + struct devlink *devlink,
> + u32 param, u8 type, void *value)
> +{
> + const struct devlink_ops *ops = devlink->ops;
> + struct nlattr *cfgparam_attr;
> + u8 need_restart;
> + int err;
> +
> + /* Now set parameter */
> + err = ops->perm_config_set(devlink, param, type, value,
> &need_restart);
> + if (err)
> + return err;
Likewise
^ permalink raw reply
* [RFC] net/unix_diag: Provide UDIAG_SHOW_VFS2 attribute to fetch complete inode number
From: Cyrill Gorcunov @ 2017-10-24 21:48 UTC (permalink / raw)
To: NETDEV; +Cc: LKML, Andrey Vagin, David S. Miller, Pavel Emelyanov
Currently unix_diag_vfs structure reports unix socket inode
as u32 value which of course doesn't fit to ino_t type and
the number may be trimmed. Lets rather deprecate old UDIAG_SHOW_VFS
interface and provide UDIAG_SHOW_VFS2 (with one field "__zero" reserved
which we could extend in future).
CC: Andrey Vagin <avagin@openvz.org>
CC: David S. Miller <davem@davemloft.net>
CC: Pavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
---
I build-tested it only thus not for inclusion yet, but rather
to discuss if there some better way to handle this potential
problem.
include/uapi/linux/unix_diag.h | 8 ++++++++
net/unix/diag.c | 25 ++++++++++++++++---------
2 files changed, 24 insertions(+), 9 deletions(-)
Index: linux-ml.git/include/uapi/linux/unix_diag.h
===================================================================
--- linux-ml.git.orig/include/uapi/linux/unix_diag.h
+++ linux-ml.git/include/uapi/linux/unix_diag.h
@@ -19,6 +19,7 @@ struct unix_diag_req {
#define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */
#define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */
#define UDIAG_SHOW_MEMINFO 0x00000020 /* show memory info of a socket */
+#define UDIAG_SHOW_VFS2 0x00000040 /* show VFS inode info v2 */
struct unix_diag_msg {
__u8 udiag_family;
@@ -39,6 +40,7 @@ enum {
UNIX_DIAG_RQLEN,
UNIX_DIAG_MEMINFO,
UNIX_DIAG_SHUTDOWN,
+ UNIX_DIAG_VFS2,
__UNIX_DIAG_MAX,
};
@@ -50,6 +52,12 @@ struct unix_diag_vfs {
__u32 udiag_vfs_dev;
};
+struct unix_diag_vfs2 {
+ __u64 udiag_vfs_ino;
+ __u32 udiag_vfs_dev;
+ __u32 __zero; /* Reserve for future use */
+};
+
struct unix_diag_rqlen {
__u32 udiag_rqueue;
__u32 udiag_wqueue;
Index: linux-ml.git/net/unix/diag.c
===================================================================
--- linux-ml.git.orig/net/unix/diag.c
+++ linux-ml.git/net/unix/diag.c
@@ -19,17 +19,24 @@ static int sk_diag_dump_name(struct sock
addr->name->sun_path);
}
-static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
+static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb, unsigned int flags)
{
struct dentry *dentry = unix_sk(sk)->path.dentry;
if (dentry) {
- struct unix_diag_vfs uv = {
- .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
- .udiag_vfs_dev = dentry->d_sb->s_dev,
- };
-
- return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
+ if (flags & UDIAG_SHOW_VFS2) {
+ struct unix_diag_vfs uv = {
+ .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
+ .udiag_vfs_dev = dentry->d_sb->s_dev,
+ };
+ return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
+ } else {
+ struct unix_diag_vfs2 uv = {
+ .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
+ .udiag_vfs_dev = dentry->d_sb->s_dev,
+ };
+ return nla_put(nlskb, UDIAG_SHOW_VFS2, sizeof(uv), &uv);
+ }
}
return 0;
@@ -132,8 +139,8 @@ static int sk_diag_fill(struct sock *sk,
sk_diag_dump_name(sk, skb))
goto out_nlmsg_trim;
- if ((req->udiag_show & UDIAG_SHOW_VFS) &&
- sk_diag_dump_vfs(sk, skb))
+ if ((req->udiag_show & (UDIAG_SHOW_VFS | UDIAG_SHOW_VFS2)) &&
+ sk_diag_dump_vfs(sk, skb, req->udiag_show))
goto out_nlmsg_trim;
if ((req->udiag_show & UDIAG_SHOW_PEER) &&
^ permalink raw reply
* [PATCH] tap: double-free in error path in tap_open()
From: Girish Moodalbail @ 2017-10-24 21:41 UTC (permalink / raw)
To: netdev, davem, jasowang
Double free of skb_array in tap module is causing kernel panic. When
tap_set_queue() fails we free skb_array right away by calling
skb_array_cleanup(). However, later on skb_array_cleanup() is called
again by tap_sock_destruct through sock_put(). This patch fixes that
issue.
Fixes: 362899b8725b35e3 (macvtap: switch to use skb array)
Signed-off-by: Girish Moodalbail <girish.moodalbail@oracle.com>
---
drivers/net/tap.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 21b71ae..878520b 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -542,20 +542,20 @@ static int tap_open(struct inode *inode, struct file *file)
err = -ENOMEM;
if (skb_array_init(&q->skb_array, tap->dev->tx_queue_len, GFP_KERNEL))
- goto err_array;
+ goto err_put;
err = tap_set_queue(tap, file, q);
- if (err)
- goto err_queue;
+ if (err) {
+ /* tap_sock_destruct() will take care of freeing skb_array */
+ goto err_put;
+ }
dev_put(tap->dev);
rtnl_unlock();
return err;
-err_queue:
- skb_array_cleanup(&q->skb_array);
-err_array:
+err_put:
sock_put(&q->sk);
err:
if (tap)
--
1.8.3.1
^ permalink raw reply related
* [Patch net-next] vsock: always call vsock_init_tables()
From: Cong Wang @ 2017-10-24 22:30 UTC (permalink / raw)
To: netdev; +Cc: Cong Wang, Stefan Hajnoczi, Jorgen Hansen
Although CONFIG_VSOCKETS_DIAG depends on CONFIG_VSOCKETS,
vsock_init_tables() is not always called, it is called only
if other modules call its caller. Therefore if we only
enable CONFIG_VSOCKETS_DIAG, it would crash kernel on uninitialized
vsock_bind_table.
This patch fixes it by moving vsock_init_tables() to its own
module_init().
Fixes: 413a4317aca7 ("VSOCK: add sock_diag interface")
Reported-by: syzkaller bot
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jorgen Hansen <jhansen@vmware.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
net/vmw_vsock/af_vsock.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 98359c19522f..5d28abf87fbf 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -195,7 +195,7 @@ static int vsock_auto_bind(struct vsock_sock *vsk)
return __vsock_bind(sk, &local_addr);
}
-static void vsock_init_tables(void)
+static int __init vsock_init_tables(void)
{
int i;
@@ -204,6 +204,7 @@ static void vsock_init_tables(void)
for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++)
INIT_LIST_HEAD(&vsock_connected_table[i]);
+ return 0;
}
static void __vsock_insert_bound(struct list_head *list,
@@ -1957,8 +1958,6 @@ int __vsock_core_init(const struct vsock_transport *t, struct module *owner)
vsock_proto.owner = owner;
transport = t;
- vsock_init_tables();
-
vsock_device.minor = MISC_DYNAMIC_MINOR;
err = misc_register(&vsock_device);
if (err) {
@@ -2019,6 +2018,8 @@ const struct vsock_transport *vsock_core_get_transport(void)
}
EXPORT_SYMBOL_GPL(vsock_core_get_transport);
+module_init(vsock_init_tables);
+
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Virtual Socket Family");
MODULE_VERSION("1.0.2.0-k");
--
2.13.0
^ permalink raw reply related
* [Patch net-next] tipc: fix a dangling pointer
From: Cong Wang @ 2017-10-24 22:44 UTC (permalink / raw)
To: netdev; +Cc: Cong Wang, Jon Maloy, Ying Xue
tsk->group is set to grp earlier, but we forget to unset it
after grp is freed.
Fixes: 75da2163dbb6 ("tipc: introduce communication groups")
Reported-by: syzkaller bot
Cc: Jon Maloy <jon.maloy@ericsson.com>
Cc: Ying Xue <ying.xue@windriver.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
net/tipc/socket.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index b3b72d8e9543..ea61c32f6b80 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -2756,8 +2756,10 @@ static int tipc_sk_join(struct tipc_sock *tsk, struct tipc_group_req *mreq)
seq.upper = seq.lower;
tipc_nametbl_build_group(net, grp, mreq->type, domain);
rc = tipc_sk_publish(tsk, mreq->scope, &seq);
- if (rc)
+ if (rc) {
tipc_group_delete(net, grp);
+ tsk->group = NULL;
+ }
/* Eliminate any risk that a broadcast overtakes the sent JOIN */
tsk->mc_method.rcast = true;
--
2.13.0
^ permalink raw reply related
* [PATCH net] tcp: call tcp_rate_skb_sent() when retransmit with unaligned skb->data
From: Yousuk Seung @ 2017-10-24 23:44 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Yousuk Seung, Neal Cardwell, Soheil Hassas Yeganeh
Current implementation calls tcp_rate_skb_sent() when tcp_transmit_skb()
is called when it clones skb only. Not calling tcp_rate_skb_sent() is OK
for all such code paths except from __tcp_retransmit_skb() which happens
when skb->data address is not aligned. This may rarely happen e.g. when
small amount of data is sent initially and the receiver partially acks
odd number of bytes for some reason, possibly malicious.
Signed-off-by: Yousuk Seung <ysseung@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Soheil Hassas Yeganeh <soheil@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_output.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 973befc36fd4..1151870018e3 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2843,8 +2843,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
-ENOBUFS;
- if (!err)
+ if (!err) {
skb->skb_mstamp = tp->tcp_mstamp;
+ tcp_rate_skb_sent(sk, skb);
+ }
} else {
err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
}
--
2.15.0.rc0.271.g36b669edcc-goog
^ permalink raw reply related
* [PATCH net-next] tcp: add tracepoint trace_tcp_retransmit_synack()
From: Song Liu @ 2017-10-24 23:57 UTC (permalink / raw)
To: alexei.starovoitov, kafai, ast, netdev; +Cc: liu.song.a23, Song Liu
This tracepoint can be used to trace synack retransmits. It maintains
pointer to struct request_sock.
We cannot simply reuse trace_tcp_retransmit_skb() here, because the
sk here is the LISTEN socket. The IP addresses and ports should be
extracted from struct request_sock.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
include/trace/events/tcp.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv4/tcp_output.c | 1 +
2 files changed, 57 insertions(+)
diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h
index 03699ba..07cccca 100644
--- a/include/trace/events/tcp.h
+++ b/include/trace/events/tcp.h
@@ -237,6 +237,62 @@ TRACE_EVENT(tcp_set_state,
show_tcp_state_name(__entry->newstate))
);
+TRACE_EVENT(tcp_retransmit_synack,
+
+ TP_PROTO(const struct sock *sk, const struct request_sock *req),
+
+ TP_ARGS(sk, req),
+
+ TP_STRUCT__entry(
+ __field(const void *, skaddr)
+ __field(const void *, req)
+ __field(__u16, sport)
+ __field(__u16, dport)
+ __array(__u8, saddr, 4)
+ __array(__u8, daddr, 4)
+ __array(__u8, saddr_v6, 16)
+ __array(__u8, daddr_v6, 16)
+ ),
+
+ TP_fast_assign(
+ struct inet_request_sock *ireq = inet_rsk(req);
+ struct in6_addr *pin6;
+ __be32 *p32;
+
+ __entry->skaddr = sk;
+ __entry->req = req;
+
+ __entry->sport = ireq->ir_num;
+ __entry->dport = ntohs(ireq->ir_rmt_port);
+
+ p32 = (__be32 *) __entry->saddr;
+ *p32 = ireq->ir_loc_addr;
+
+ p32 = (__be32 *) __entry->daddr;
+ *p32 = ireq->ir_rmt_addr;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6) {
+ pin6 = (struct in6_addr *)__entry->saddr_v6;
+ *pin6 = ireq->ir_v6_loc_addr;
+ pin6 = (struct in6_addr *)__entry->daddr_v6;
+ *pin6 = ireq->ir_v6_rmt_addr;
+ } else
+#endif
+ {
+ pin6 = (struct in6_addr *)__entry->saddr_v6;
+ ipv6_addr_set_v4mapped(ireq->ir_loc_addr, pin6);
+ pin6 = (struct in6_addr *)__entry->daddr_v6;
+ ipv6_addr_set_v4mapped(ireq->ir_rmt_addr, pin6);
+ }
+ ),
+
+ TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c",
+ __entry->sport, __entry->dport,
+ __entry->saddr, __entry->daddr,
+ __entry->saddr_v6, __entry->daddr_v6)
+);
+
#endif /* _TRACE_TCP_H */
/* This part must be outside protection */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 1f01f4c..6a728a5 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3735,6 +3735,7 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req)
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
if (unlikely(tcp_passive_fastopen(sk)))
tcp_sk(sk)->total_retrans++;
+ trace_tcp_retransmit_synack(sk, req);
}
return res;
}
--
2.9.5
^ permalink raw reply related
* [iproute PATCH] ss: add detail explains of -m, -o, -e and -i options in ss man page
From: peng yu @ 2017-10-25 0:13 UTC (permalink / raw)
To: netdev; +Cc: stephen, phil
commit 340a45f79395144bd14fdf9be1904c0036456b6e
Author: yupeng <yupeng0921@gmail.com>
Date: Tue Oct 24 23:55:29 2017 +0000
add additional explain in ss man page
Add detail explains of -m, -o, -e and -i options, which are not
documented anywhere
Signed-off-by: Peng Yu <yupeng0921@gmail.com>
diff --git a/man/man8/ss.8 b/man/man8/ss.8
index 3bec97f..4597733 100644
--- a/man/man8/ss.8
+++ b/man/man8/ss.8
@@ -176,6 +176,147 @@ states except for
- opposite to
.B bucket
+.SH Additional explain of -m, -o, -e and -i options
+Some fields may have different meanings if the netowrk protocl is
different. Below explain focus on tcp protocol.
+.TP
+.B -m option
+skmem:(r<rmem_alloc>,rb<rcv_buf>,t<wmem_alloc>,tb<snd_buf>,f<fwd_alloc>,w<wmem_queued>,o<opt_mem>,bl<back_log>)
+
+.B <rmem_alloc>
+the memory allocated for receiving package
+
+.B <rcv_buf>
+the total memory can be allocated for receiving package
+
+.B <wmem_alloc>
+the memory used for sending package (which has been sent to layer 3)
+
+.B <snd_buf>
+the total memory can be allocated for sending package
+
+.B <fwd_alloc>
+the memory allocated by the socket as cache, but not used for
receiving/sending pacakge yet. If need memory to send/receive package,
the memory in this cache will be used before allocate additional
memory.
+
+.B <wmem_queued>
+The memory allocated for sending package (which has not been sent to layer 3)
+
+.B <opt_mem>
+The memory used for storing socket option, e.g., the key for TCP MD5 signature
+
+.B <back_log>
+The memory used for the sk backlog queue. On a process context, if
the process is receving package, and a new package is received, it
will be put into the sk backlog queue, so it can be received by the
process immediately
+.TP
+.B -o option
+timer:(<timer_name>,<expire_time>,<retrans>)
+
+.B <timer_name>
+the name of the timer, there are five kind of timer names:
+
+.BR on ": means one of these timers: tcp retrans timer, tcp early
retrans timer and tail loss probe timer"
+
+.BR keepalive ": tcp keep alive timer"
+
+.BR timewait ": timewait stage timer"
+
+.BR persist ": zero window probe timer"
+
+.BR unknown ": none of the above timers"
+
+.B <expire_time>
+how long time the timer will expire
+
+.B <retrans>
+how many times the retran occurs
+.TP
+
+.B -e option
+uid:<uid_number> ino:<inode_number> sk:<cookie>
+
+.B <uid_number>
+the user id the socket belongs to
+
+.B <inode_number>
+the socket's inode number in VFS
+
+.B <cookie>
+an uuid of the socket
+
+.TP
+.B -i option
+show tcp internal information
+
+.B ts
+show string "ts" if the timestamp option is set
+
+.B sack
+show string "sack" if the sack option is set
+
+.B ecn
+show string "ecn" if the explicit congestion notification option is set
+
+.B ecnseen
+show string "ecnseen" if the saw ecn flag is found in received packages
+
+.B fastopen
+show string "fastopen" if the fastopen option is set
+
+.B cong_alg
+the congestion algorithm name, the default congestion algorithm is "cubic"
+
+.B wscale:<snd_wscale>:<rcv_wscale>
+if window scale option is used, this field shows the send scale
factory and receive scale factory
+
+.B rto:<icsk_rto>
+tcp retransmission timeout value, the unit is millisecond
+
+.B backoff:<icsk_backoff>
+used for exponential backoff retransmission, the actual
retransmission timeout vaule is icsk_rto << icsk_backoff
+
+.B rtt:<rtt>/<rttvar>
+rtt is the average round trip time, rttvar is the mean deviation of
rtt, their units are millisecond
+
+.B ato:<ato>
+ack timeout, unit is millisecond, used for delay ack mode
+
+.B mss:<mss>
+max segment size
+
+.B cwnd:<cwnd>
+congestion window size
+
+.B ssthresh:<ssthresh>
+tcp congestion window slow start threshold
+
+.B bytes_acked:<bytes_acked>
+bytes acked
+
+.B bytes_received:<bytes_received>
+bytes received
+
+.B segs_out:<segs_out>
+segments sent out
+
+.B segs_in:<segs_in>
+segments received
+
+.B send <send_bps>bps
+egress bps
+
+.B lastsnd:<lastsnd>
+how long time since the last package sent, the unit is millisecond
+
+.B lastrcv:<lastrcv>
+how long time since the last package received, the unit is millisecond
+
+.B lastack:<lastack>
+how long time since the last ack received, the unit is millisecond
+
+.B pacing_rate <pacing_rate>bps/<max_pacing_rate>bps
+the pacing rate and max pacing rate
+
+.B rcv_space:<rcv_space>
+a helper variable for TCP internal auto tunning socket receive buffer
+
.SH USAGE EXAMPLES
.TP
.B ss -t -a
^ permalink raw reply related
* Re: [RFC] net/unix_diag: Provide UDIAG_SHOW_VFS2 attribute to fetch complete inode number
From: Andrei Vagin @ 2017-10-25 0:25 UTC (permalink / raw)
To: Cyrill Gorcunov
Cc: NETDEV, LKML, Andrey Vagin, David S. Miller, Pavel Emelyanov
In-Reply-To: <20171024214814.GO5343@uranus>
On Wed, Oct 25, 2017 at 12:48:14AM +0300, Cyrill Gorcunov wrote:
> Currently unix_diag_vfs structure reports unix socket inode
> as u32 value which of course doesn't fit to ino_t type and
BTW: As far as I understand, it is not a problem right now, because
get_next_ino returns int. And I'm agree that it maybe a problem in a
future and it is better to be ready.
> the number may be trimmed. Lets rather deprecate old UDIAG_SHOW_VFS
> interface and provide UDIAG_SHOW_VFS2 (with one field "__zero" reserved
> which we could extend in future).
There is one more place where we return ino as u32:
static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb)
....
return nla_put_u32(nlskb, UNIX_DIAG_PEER, ino);
>
> CC: Andrey Vagin <avagin@openvz.org>
> CC: David S. Miller <davem@davemloft.net>
> CC: Pavel Emelyanov <xemul@virtuozzo.com>
> Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
> ---
>
> I build-tested it only thus not for inclusion yet, but rather
> to discuss if there some better way to handle this potential
> problem.
>
> include/uapi/linux/unix_diag.h | 8 ++++++++
> net/unix/diag.c | 25 ++++++++++++++++---------
> 2 files changed, 24 insertions(+), 9 deletions(-)
>
> Index: linux-ml.git/include/uapi/linux/unix_diag.h
> ===================================================================
> --- linux-ml.git.orig/include/uapi/linux/unix_diag.h
> +++ linux-ml.git/include/uapi/linux/unix_diag.h
> @@ -19,6 +19,7 @@ struct unix_diag_req {
> #define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */
> #define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */
> #define UDIAG_SHOW_MEMINFO 0x00000020 /* show memory info of a socket */
> +#define UDIAG_SHOW_VFS2 0x00000040 /* show VFS inode info v2 */
>
> struct unix_diag_msg {
> __u8 udiag_family;
> @@ -39,6 +40,7 @@ enum {
> UNIX_DIAG_RQLEN,
> UNIX_DIAG_MEMINFO,
> UNIX_DIAG_SHUTDOWN,
> + UNIX_DIAG_VFS2,
>
> __UNIX_DIAG_MAX,
> };
> @@ -50,6 +52,12 @@ struct unix_diag_vfs {
> __u32 udiag_vfs_dev;
> };
>
> +struct unix_diag_vfs2 {
> + __u64 udiag_vfs_ino;
> + __u32 udiag_vfs_dev;
> + __u32 __zero; /* Reserve for future use */
How can a user understand whether this field is used or not?
Each netlink attribute has its size in a header. Any attribute can be
extended, and users can understand which fields are filled by
a size of an attribute.
> +};
> +
> struct unix_diag_rqlen {
> __u32 udiag_rqueue;
> __u32 udiag_wqueue;
> Index: linux-ml.git/net/unix/diag.c
> ===================================================================
> --- linux-ml.git.orig/net/unix/diag.c
> +++ linux-ml.git/net/unix/diag.c
> @@ -19,17 +19,24 @@ static int sk_diag_dump_name(struct sock
> addr->name->sun_path);
> }
>
> -static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
> +static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb, unsigned int flags)
> {
> struct dentry *dentry = unix_sk(sk)->path.dentry;
>
> if (dentry) {
> - struct unix_diag_vfs uv = {
> - .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
> - .udiag_vfs_dev = dentry->d_sb->s_dev,
> - };
> -
> - return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
> + if (flags & UDIAG_SHOW_VFS2) {
> + struct unix_diag_vfs uv = {
> + .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
> + .udiag_vfs_dev = dentry->d_sb->s_dev,
> + };
> + return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
> + } else {
> + struct unix_diag_vfs2 uv = {
> + .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
> + .udiag_vfs_dev = dentry->d_sb->s_dev,
> + };
> + return nla_put(nlskb, UDIAG_SHOW_VFS2, sizeof(uv), &uv);
> + }
> }
>
> return 0;
> @@ -132,8 +139,8 @@ static int sk_diag_fill(struct sock *sk,
> sk_diag_dump_name(sk, skb))
> goto out_nlmsg_trim;
>
> - if ((req->udiag_show & UDIAG_SHOW_VFS) &&
> - sk_diag_dump_vfs(sk, skb))
> + if ((req->udiag_show & (UDIAG_SHOW_VFS | UDIAG_SHOW_VFS2)) &&
> + sk_diag_dump_vfs(sk, skb, req->udiag_show))
> goto out_nlmsg_trim;
>
> if ((req->udiag_show & UDIAG_SHOW_PEER) &&
^ permalink raw reply
* Re: [PATCH V2 net-next 2/3] tcp: TCP experimental option for SMC
From: David Miller @ 2017-10-25 0:55 UTC (permalink / raw)
To: ubraun; +Cc: netdev, linux-s390, jwi, schwidefsky, heiko.carstens, raspl
In-Reply-To: <20171018143000.23884-3-ubraun@linux.vnet.ibm.com>
From: Ursula Braun <ubraun@linux.vnet.ibm.com>
Date: Wed, 18 Oct 2017 16:29:59 +0200
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index 3b3b9b968e2d..a2e87ac8cdc0 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -45,6 +45,7 @@
>
> #include <linux/seq_file.h>
> #include <linux/memcontrol.h>
> +#include <linux/unaligned/access_ok.h>
Only tcp_input.c needs this include. If you include it here, then the
rest of the kernel pays the price of this include.
^ permalink raw reply
* Re: [PATCH net-next] ipv6: update use count on dst_check()
From: Martin KaFai Lau @ 2017-10-25 1:00 UTC (permalink / raw)
To: Paolo Abeni; +Cc: netdev, David S. Miller, Wei Wang, Hannes Frederic Sowa
In-Reply-To: <f9c3808ba0aa553eaaa47c1507ba417366f015de.1508841703.git.pabeni@redhat.com>
On Tue, Oct 24, 2017 at 10:42:57AM +0000, Paolo Abeni wrote:
> Currently, the dst 'lastuse' field is not updated in a reliable way
> for dst entries only accessed via socket cache.
>
> This may fool the ipv6 dst gc, especially since we really age out unused
> entries, either via the commit 1859bac04fb6 ("ipv6: remove from fib tree
> aged out RTF_CACHE dst") or via the commit 1e2ea8ad37be ("ipv6: set
> dst.obsolete when a cached route has expired").
There is already an exception table and it already has a different way
of purging which is more inline with IPv4. It purges based on
'When was the last time the ICMPv6 too-big/redirect received' and
we purge the one that will expire sooner.
As we are moving away from the IPv6's gc,
I don't think we need to further depend on lastuse to purge/age
the RTF_CACHE on top of the purging being done in the exception
table.
Beside, lastuse is only useful for RTF_CACHE route (redirect
route in particular since it has no expire which we should give
it one imo). However, rt6_check() is called for everything,
e.g. non RTF_CACHE route which is the common case. Even though
dst_use_noref() is cheap to do now, it is still unnecessary for
the common fast path.
>
> Updating such field is now very cheap, thanks to commit 0da4af00b2ed
> ("ipv6: only update __use and lastusetime once per jiffy at most"),
> so we can address the issue updating 'lastuse' in ip6_dst_check(),
> if successful.
>
> Fixes: 1859bac04fb6 ("ipv6: remove from fib tree aged out RTF_CACHE dst")
> Fixes: 1e2ea8ad37be ("ipv6: set dst.obsolete when a cached route has expired")
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
> net/ipv6/route.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 87a15cbd0e8b..8f8c7fb83b9b 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -1952,6 +1952,7 @@ static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
> if (rt6_check_expired(rt))
> return NULL;
>
> + dst_use_noref(&rt->dst, jiffies);
> return &rt->dst;
> }
>
> --
> 2.13.6
>
^ permalink raw reply
* [PATCH net-next v3 0/2] add UniPhier AVE ethernet support
From: Kunihiko Hayashi @ 2017-10-25 1:07 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA, Andrew Lunn, Florian Fainelli
Cc: Rob Herring, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Masahiro Yamada,
Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi
This series adds support for Socionext AVE ethernet controller implemented
on UniPhier SoCs. This driver supports RGMII/RMII modes.
v1: http://www.spinics.net/lists/netdev/msg454292.html
v2: http://www.spinics.net/lists/netdev/msg460109.html
The PHY patch included in v1 has already separated in:
http://www.spinics.net/lists/netdev/msg454595.html
Changes since v2:
- replace clk_get() with devm_clk_get()
- replace reset_control_get() with devm_reset_control_get_optional_shared()
- add error return when the error occurs on the above *_get functions
- sort soc data and compatible strings
- remove clearly obvious comments
- modify dt-bindings document consistent with these modifications
Changes since v1:
- add/remove devicetree properties and sub-node
- remove "internal-phy-interrupt" and "desc-bits" property
- add SoC data structures based on compatible strings
- add node operation to apply "mdio" sub-node
- add support for features
- add support for {get,set}_pauseparam and pause frame operations
- add support for ndo_get_stats64 instead of ndo_get_stats
- replace with desiable functions
- replace check for valid phy_mode with phy_interface{_mode}_is_rgmii()
- replace phy attach message with phy_attached_info()
- replace 32bit operation with {upper,lower}_32_bits() on ave_wdesc_addr()
- replace nway_reset and get_link with generic functions
- move operations to proper functions
- move phy_start_aneg() to ndo_open,
and remove unnecessary PHY interrupt operations
See http://www.spinics.net/lists/netdev/msg454590.html
- move irq initialization and descriptor memory allocation to ndo_open
- move initialization of reset and clock and mdiobus to ndo_init
- fix skbuffer operations
- fix skb alignment operations and add Rx buffer adjustment for descriptor
See http://www.spinics.net/lists/netdev/msg456014.html
- add error returns when dma_map_single() failed
- clean up code structures
- clean up wait-loop and wake-queue conditions
- add ave_wdesc_addr() and offset definitions
- add ave_macaddr_init() to clean up mac-address operation
- fix checking whether Tx entry is not enough
- fix supported features of phydev
- add necessary free/disable operations
- add phydev check on ave_{get,set}_wol()
- remove netif_carrier functions, phydev initializer, and Tx budget check
- change obsolate codes
- replace ndev->{base_addr,irq} with the members of ave_private
- rename goto labels and mask definitions, and remove unused codes
Kunihiko Hayashi (2):
dt-bindings: net: add DT bindings for Socionext UniPhier AVE
net: ethernet: socionext: add AVE ethernet driver
.../bindings/net/socionext,uniphier-ave4.txt | 48 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/socionext/Kconfig | 22 +
drivers/net/ethernet/socionext/Makefile | 4 +
drivers/net/ethernet/socionext/sni_ave.c | 1746 ++++++++++++++++++++
6 files changed, 1822 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt
create mode 100644 drivers/net/ethernet/socionext/Kconfig
create mode 100644 drivers/net/ethernet/socionext/Makefile
create mode 100644 drivers/net/ethernet/socionext/sni_ave.c
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH net-next v3 1/2] dt-bindings: net: add DT bindings for Socionext UniPhier AVE
From: Kunihiko Hayashi @ 2017-10-25 1:07 UTC (permalink / raw)
To: netdev, Andrew Lunn, Florian Fainelli
Cc: Mark Rutland, devicetree, Kunihiko Hayashi, Masami Hiramatsu,
Jassi Brar, linux-kernel, Masahiro Yamada, Rob Herring,
linux-arm-kernel
In-Reply-To: <1508893679-11727-1-git-send-email-hayashi.kunihiko@socionext.com>
DT bindings for the AVE ethernet controller found on Socionext's
UniPhier platforms.
Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
.../bindings/net/socionext,uniphier-ave4.txt | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt
diff --git a/Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt b/Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt
new file mode 100644
index 0000000..4b7eca8
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/socionext,uniphier-ave4.txt
@@ -0,0 +1,48 @@
+* Socionext AVE ethernet controller
+
+This describes the devicetree bindings for AVE ethernet controller
+implemented on Socionext UniPhier SoCs.
+
+Required properties:
+ - compatible: Should be
+ - "socionext,uniphier-pro4-ave4" : for Pro4 SoC
+ - "socionext,uniphier-pxs2-ave4" : for PXs2 SoC
+ - "socionext,uniphier-ld11-ave4" : for LD11 SoC
+ - "socionext,uniphier-ld20-ave4" : for LD20 SoC
+ - reg: Address where registers are mapped and size of region.
+ - interrupts: Should contain the MAC interrupt.
+ - phy-mode: See ethernet.txt in the same directory. Allow to choose
+ "rgmii", "rmii", or "mii" according to the PHY.
+ - phy-handle: Should point to the external phy device.
+ See ethernet.txt file in the same directory.
+ - clocks: A phandle to the clock for the MAC.
+
+Optional properties:
+ - resets: A phandle to the reset control for the MAC.
+ - local-mac-address: See ethernet.txt in the same directory.
+
+Required subnode:
+ - mdio: Device tree subnode with the following required properties:
+ - #address-cells: Must be <1>.
+ - #size-cells: Must be <0>.
+ - reg: phy ID number, usually a small integer.
+
+Example:
+
+ ether: ethernet@65000000 {
+ compatible = "socionext,uniphier-ld20-ave4";
+ reg = <0x65000000 0x8500>;
+ interrupts = <0 66 4>;
+ phy-mode = "rgmii";
+ phy-handle = <ðphy>;
+ clocks = <&sys_clk 6>;
+ resets = <&sys_rst 6>;
+ local-mac-address = [00 00 00 00 00 00];
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ethphy: ethphy@1 {
+ reg = <1>;
+ };
+ };
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH net-next v3 2/2] net: ethernet: socionext: add AVE ethernet driver
From: Kunihiko Hayashi @ 2017-10-25 1:07 UTC (permalink / raw)
To: netdev, Andrew Lunn, Florian Fainelli
Cc: Rob Herring, Mark Rutland, linux-arm-kernel, linux-kernel,
devicetree, Masahiro Yamada, Masami Hiramatsu, Jassi Brar,
Kunihiko Hayashi
In-Reply-To: <1508893679-11727-1-git-send-email-hayashi.kunihiko@socionext.com>
The UniPhier platform from Socionext provides the AVE ethernet
controller that includes MAC and MDIO bus supporting RGMII/RMII
modes. The controller is named AVE.
Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/socionext/Kconfig | 22 +
drivers/net/ethernet/socionext/Makefile | 4 +
drivers/net/ethernet/socionext/sni_ave.c | 1746 ++++++++++++++++++++++++++++++
5 files changed, 1774 insertions(+)
create mode 100644 drivers/net/ethernet/socionext/Kconfig
create mode 100644 drivers/net/ethernet/socionext/Makefile
create mode 100644 drivers/net/ethernet/socionext/sni_ave.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c604213..d50519e 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -170,6 +170,7 @@ source "drivers/net/ethernet/sis/Kconfig"
source "drivers/net/ethernet/sfc/Kconfig"
source "drivers/net/ethernet/sgi/Kconfig"
source "drivers/net/ethernet/smsc/Kconfig"
+source "drivers/net/ethernet/socionext/Kconfig"
source "drivers/net/ethernet/stmicro/Kconfig"
source "drivers/net/ethernet/sun/Kconfig"
source "drivers/net/ethernet/tehuti/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index a0a03d4..9f55b36 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_SFC_FALCON) += sfc/falcon/
obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
+obj-$(CONFIG_NET_VENDOR_SOCIONEXT) += socionext/
obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
obj-$(CONFIG_NET_VENDOR_SUN) += sun/
obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
diff --git a/drivers/net/ethernet/socionext/Kconfig b/drivers/net/ethernet/socionext/Kconfig
new file mode 100644
index 0000000..3a1829e
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Kconfig
@@ -0,0 +1,22 @@
+config NET_VENDOR_SOCIONEXT
+ bool "Socionext ethernet drivers"
+ default y
+ ---help---
+ Option to select ethernet drivers for Socionext platforms.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Socionext devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_SOCIONEXT
+
+config SNI_AVE
+ tristate "Socionext AVE ethernet support"
+ depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+ select PHYLIB
+ ---help---
+ Driver for gigabit ethernet MACs, called AVE, in the
+ Socionext UniPhier family.
+
+endif #NET_VENDOR_SOCIONEXT
diff --git a/drivers/net/ethernet/socionext/Makefile b/drivers/net/ethernet/socionext/Makefile
new file mode 100644
index 0000000..0356341
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for all ethernet ip drivers on Socionext platforms
+#
+obj-$(CONFIG_SNI_AVE) += sni_ave.o
diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
new file mode 100644
index 0000000..ed0f4a3
--- /dev/null
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -0,0 +1,1746 @@
+/**
+ * sni_ave.c - Socionext UniPhier AVE ethernet driver
+ *
+ * Copyright 2014 Panasonic Corporation
+ * Copyright 2015-2017 Socionext Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <linux/u64_stats_sync.h>
+
+/* General Register Group */
+#define AVE_IDR 0x0 /* ID */
+#define AVE_VR 0x4 /* Version */
+#define AVE_GRR 0x8 /* Global Reset */
+#define AVE_CFGR 0xc /* Configuration */
+
+/* Interrupt Register Group */
+#define AVE_GIMR 0x100 /* Global Interrupt Mask */
+#define AVE_GISR 0x104 /* Global Interrupt Status */
+
+/* MAC Register Group */
+#define AVE_TXCR 0x200 /* TX Setup */
+#define AVE_RXCR 0x204 /* RX Setup */
+#define AVE_RXMAC1R 0x208 /* MAC address (lower) */
+#define AVE_RXMAC2R 0x20c /* MAC address (upper) */
+#define AVE_MDIOCTR 0x214 /* MDIO Control */
+#define AVE_MDIOAR 0x218 /* MDIO Address */
+#define AVE_MDIOWDR 0x21c /* MDIO Data */
+#define AVE_MDIOSR 0x220 /* MDIO Status */
+#define AVE_MDIORDR 0x224 /* MDIO Rd Data */
+
+/* Descriptor Control Register Group */
+#define AVE_DESCC 0x300 /* Descriptor Control */
+#define AVE_TXDC 0x304 /* TX Descriptor Configuration */
+#define AVE_RXDC0 0x308 /* RX Descriptor Ring0 Configuration */
+#define AVE_IIRQC 0x34c /* Interval IRQ Control */
+
+/* Packet Filter Register Group */
+#define AVE_PKTF_BASE 0x800 /* PF Base Address */
+#define AVE_PFMBYTE_BASE 0xd00 /* PF Mask Byte Base Address */
+#define AVE_PFMBIT_BASE 0xe00 /* PF Mask Bit Base Address */
+#define AVE_PFSEL_BASE 0xf00 /* PF Selector Base Address */
+#define AVE_PFEN 0xffc /* Packet Filter Enable */
+#define AVE_PKTF(ent) (AVE_PKTF_BASE + (ent) * 0x40)
+#define AVE_PFMBYTE(ent) (AVE_PFMBYTE_BASE + (ent) * 8)
+#define AVE_PFMBIT(ent) (AVE_PFMBIT_BASE + (ent) * 4)
+#define AVE_PFSEL(ent) (AVE_PFSEL_BASE + (ent) * 4)
+
+/* 64bit descriptor memory */
+#define AVE_DESC_SIZE_64 12 /* Descriptor Size */
+
+#define AVE_TXDM_64 0x1000 /* Tx Descriptor Memory */
+#define AVE_RXDM_64 0x1c00 /* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_64 0x0ba0 /* Tx Descriptor Memory Size 3KB */
+#define AVE_RXDM_SIZE_64 0x6000 /* Rx Descriptor Memory Size 24KB */
+
+/* 32bit descriptor memory */
+#define AVE_DESC_SIZE_32 8 /* Descriptor Size */
+
+#define AVE_TXDM_32 0x1000 /* Tx Descriptor Memory */
+#define AVE_RXDM_32 0x1800 /* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_32 0x07c0 /* Tx Descriptor Memory Size 2KB */
+#define AVE_RXDM_SIZE_32 0x4000 /* Rx Descriptor Memory Size 16KB */
+
+/* RMII Bridge Register Group */
+#define AVE_RSTCTRL 0x8028 /* Reset control */
+#define AVE_RSTCTRL_RMIIRST BIT(16)
+#define AVE_LINKSEL 0x8034 /* Link speed setting */
+#define AVE_LINKSEL_100M BIT(0)
+
+/* AVE_GRR */
+#define AVE_GRR_RXFFR BIT(5) /* Reset RxFIFO */
+#define AVE_GRR_PHYRST BIT(4) /* Reset external PHY */
+#define AVE_GRR_GRST BIT(0) /* Reset all MAC */
+
+/* AVE_GISR (common with GIMR) */
+#define AVE_GI_PHY BIT(24) /* PHY interrupt */
+#define AVE_GI_TX BIT(16) /* Tx complete */
+#define AVE_GI_RXERR BIT(8) /* Receive frame more than max size */
+#define AVE_GI_RXOVF BIT(7) /* Overflow at the RxFIFO */
+#define AVE_GI_RXDROP BIT(6) /* Drop packet */
+#define AVE_GI_RXIINT BIT(5) /* Interval interrupt */
+
+/* AVE_TXCR */
+#define AVE_TXCR_FLOCTR BIT(18) /* Flow control */
+#define AVE_TXCR_TXSPD_1G BIT(17)
+#define AVE_TXCR_TXSPD_100 BIT(16)
+
+/* AVE_RXCR */
+#define AVE_RXCR_RXEN BIT(30) /* Rx enable */
+#define AVE_RXCR_FDUPEN BIT(22) /* Interface mode */
+#define AVE_RXCR_FLOCTR BIT(21) /* Flow control */
+#define AVE_RXCR_AFEN BIT(19) /* MAC address filter */
+#define AVE_RXCR_DRPEN BIT(18) /* Drop pause frame */
+#define AVE_RXCR_MPSIZ_MASK GENMASK(10, 0)
+
+/* AVE_MDIOCTR */
+#define AVE_MDIOCTR_RREQ BIT(3) /* Read request */
+#define AVE_MDIOCTR_WREQ BIT(2) /* Write request */
+
+/* AVE_MDIOSR */
+#define AVE_MDIOSR_STS BIT(0) /* access status */
+
+/* AVE_DESCC */
+#define AVE_DESCC_TD BIT(0) /* Enable Tx descriptor */
+#define AVE_DESCC_RDSTP BIT(4) /* Pause Rx descriptor */
+#define AVE_DESCC_RD0 BIT(8) /* Enable Rx descriptor Ring0 */
+#define AVE_DESCC_STATUS_MASK GENMASK(31, 16)
+
+/* AVE_TXDC */
+#define AVE_TXDC_SIZE GENMASK(27, 16) /* Size of Tx descriptor */
+#define AVE_TXDC_ADDR GENMASK(11, 0) /* Start address */
+#define AVE_TXDC_ADDR_START 0
+
+/* AVE_RXDC0 */
+#define AVE_RXDC0_SIZE GENMASK(30, 16) /* Size of Rx descriptor */
+#define AVE_RXDC0_ADDR GENMASK(14, 0) /* Start address */
+#define AVE_RXDC0_ADDR_START 0
+
+/* AVE_IIRQC */
+#define AVE_IIRQC_EN0 BIT(27) /* Enable interval interrupt Ring0 */
+#define AVE_IIRQC_BSCK GENMASK(15, 0) /* Interval count unit */
+
+/* Command status for Descriptor */
+#define AVE_STS_OWN BIT(31) /* Descriptor ownership */
+#define AVE_STS_INTR BIT(29) /* Request for interrupt */
+#define AVE_STS_OK BIT(27) /* Normal transmit */
+/* TX */
+#define AVE_STS_NOCSUM BIT(28) /* No use HW checksum */
+#define AVE_STS_1ST BIT(26) /* Head of buffer chain */
+#define AVE_STS_LAST BIT(25) /* Tail of buffer chain */
+#define AVE_STS_OWC BIT(21) /* Out of window,Late Collision */
+#define AVE_STS_EC BIT(20) /* Excess collision occurred */
+#define AVE_STS_PKTLEN_TX_MASK GENMASK(15, 0)
+/* RX */
+#define AVE_STS_CSSV BIT(21) /* Checksum check performed */
+#define AVE_STS_CSER BIT(20) /* Checksum error detected */
+#define AVE_STS_PKTLEN_RX_MASK GENMASK(10, 0)
+
+/* AVE_CFGR */
+#define AVE_CFGR_FLE BIT(31) /* Filter Function */
+#define AVE_CFGR_CHE BIT(30) /* Checksum Function */
+#define AVE_CFGR_MII BIT(27) /* Func mode (1:MII/RMII, 0:RGMII) */
+#define AVE_CFGR_IPFCEN BIT(24) /* IP fragment sum Enable */
+
+#define AVE_MAX_ETHFRAME 1518
+
+/* Packet filter size */
+#define AVE_PF_SIZE 17 /* Number of all packet filter */
+#define AVE_PF_MULTICAST_SIZE 7 /* Number of multicast filter */
+
+/* Packet filter definition */
+#define AVE_PFNUM_FILTER 0 /* No.0 */
+#define AVE_PFNUM_UNICAST 1 /* No.1 */
+#define AVE_PFNUM_BROADCAST 2 /* No.2 */
+#define AVE_PFNUM_MULTICAST 11 /* No.11-17 */
+
+/* NETIF Message control */
+#define AVE_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+/* Parameter for descriptor */
+#define AVE_NR_TXDESC 32 /* Tx descriptor */
+#define AVE_NR_RXDESC 64 /* Rx descriptor */
+
+#define AVE_DESC_OFS_CMDSTS 0
+#define AVE_DESC_OFS_ADDRL 4
+#define AVE_DESC_OFS_ADDRU 8
+
+/* Parameter for interrupt */
+#define AVE_INTM_COUNT 20
+#define AVE_FORCE_TXINTCNT 1
+
+#define IS_DESC_64BIT(p) ((p)->data->is_desc_64bit)
+
+enum desc_id {
+ AVE_DESCID_TX = 0,
+ AVE_DESCID_RX,
+};
+
+enum desc_state {
+ AVE_DESC_STOP = 0,
+ AVE_DESC_START,
+ AVE_DESC_RX_SUSPEND,
+ AVE_DESC_RX_PERMIT,
+};
+
+struct ave_desc {
+ struct sk_buff *skbs;
+ dma_addr_t skbs_dma;
+ size_t skbs_dmalen;
+};
+
+struct ave_desc_info {
+ u32 ndesc; /* number of descriptor */
+ u32 daddr; /* start address of descriptor */
+ u32 proc_idx; /* index of processing packet */
+ u32 done_idx; /* index of processed packet */
+ struct ave_desc *desc; /* skb info related descriptor */
+};
+
+struct ave_soc_data {
+ bool is_desc_64bit;
+};
+
+struct ave_stats {
+ struct u64_stats_sync syncp;
+ u64 packets;
+ u64 bytes;
+ u64 errors;
+ u64 dropped;
+ u64 collisions;
+ u64 fifo_errors;
+};
+
+struct ave_private {
+ void __iomem *base;
+ int irq;
+ int phy_id;
+ unsigned int desc_size;
+ u32 msg_enable;
+ struct clk *clk;
+ struct reset_control *rst;
+ phy_interface_t phy_mode;
+ struct phy_device *phydev;
+ struct mii_bus *mdio;
+
+ /* stats */
+ struct ave_stats stats_rx;
+ struct ave_stats stats_tx;
+
+ /* NAPI support */
+ struct net_device *ndev;
+ struct napi_struct napi_rx;
+ struct napi_struct napi_tx;
+
+ /* descriptor */
+ struct ave_desc_info rx;
+ struct ave_desc_info tx;
+
+ /* flow control */
+ int pause_auto;
+ int pause_rx;
+ int pause_tx;
+
+ const struct ave_soc_data *data;
+};
+
+static inline u32 ave_r32(struct net_device *ndev, u32 offset)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ return readl_relaxed(priv->base + offset);
+}
+
+static inline void ave_w32(struct net_device *ndev, u32 offset, u32 value)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ writel_relaxed(value, priv->base + offset);
+}
+
+static inline u32 ave_rdesc(struct net_device *ndev, enum desc_id id,
+ int entry, int offset)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 addr = (id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr;
+
+ addr += entry * priv->desc_size + offset;
+
+ return ave_r32(ndev, addr);
+}
+
+static inline u32 ave_rdesc_cmdsts(struct net_device *ndev, enum desc_id id,
+ int entry)
+{
+ return ave_rdesc(ndev, id, entry, AVE_DESC_OFS_CMDSTS);
+}
+
+static inline void ave_wdesc(struct net_device *ndev, enum desc_id id,
+ int entry, int offset, u32 val)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 addr = (id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr;
+
+ addr += entry * priv->desc_size + offset;
+
+ ave_w32(ndev, addr, val);
+}
+
+static inline void ave_wdesc_cmdsts(struct net_device *ndev, enum desc_id id,
+ int entry, u32 val)
+{
+ ave_wdesc(ndev, id, entry, AVE_DESC_OFS_CMDSTS, val);
+}
+
+static void ave_wdesc_addr(struct net_device *ndev, enum desc_id id,
+ int entry, dma_addr_t paddr)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ ave_wdesc(ndev, id, entry, AVE_DESC_OFS_ADDRL, lower_32_bits(paddr));
+ if (IS_DESC_64BIT(priv))
+ ave_wdesc(ndev, id,
+ entry, AVE_DESC_OFS_ADDRU, upper_32_bits(paddr));
+ else if ((u64)paddr > (u64)U32_MAX)
+ netdev_warn(ndev, "DMA address exceeds the address space\n");
+}
+
+static void ave_get_fwversion(struct net_device *ndev, char *buf, int len)
+{
+ u32 major, minor;
+
+ major = (ave_r32(ndev, AVE_VR) & GENMASK(15, 8)) >> 8;
+ minor = (ave_r32(ndev, AVE_VR) & GENMASK(7, 0));
+ snprintf(buf, len, "v%u.%u", major, minor);
+}
+
+static void ave_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ struct device *dev = ndev->dev.parent;
+
+ strlcpy(info->driver, dev->driver->name, sizeof(info->driver));
+ strlcpy(info->bus_info, dev_name(dev), sizeof(info->bus_info));
+ ave_get_fwversion(ndev, info->fw_version, sizeof(info->fw_version));
+}
+
+static u32 ave_get_msglevel(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ return priv->msg_enable;
+}
+
+static void ave_set_msglevel(struct net_device *ndev, u32 val)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ priv->msg_enable = val;
+}
+
+static void ave_get_wol(struct net_device *ndev,
+ struct ethtool_wolinfo *wol)
+{
+ wol->supported = 0;
+ wol->wolopts = 0;
+
+ if (ndev->phydev)
+ phy_ethtool_get_wol(ndev->phydev, wol);
+}
+
+static int ave_set_wol(struct net_device *ndev,
+ struct ethtool_wolinfo *wol)
+{
+ int ret;
+
+ if (!ndev->phydev ||
+ (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)))
+ return -EOPNOTSUPP;
+
+ ret = phy_ethtool_set_wol(ndev->phydev, wol);
+ if (!ret)
+ device_set_wakeup_enable(&ndev->dev, !!wol->wolopts);
+
+ return ret;
+}
+
+static void ave_get_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ pause->autoneg = priv->pause_auto;
+ pause->rx_pause = priv->pause_rx;
+ pause->tx_pause = priv->pause_tx;
+}
+
+static int ave_set_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ struct phy_device *phydev = ndev->phydev;
+
+ if (!phydev)
+ return -EINVAL;
+
+ priv->pause_auto = pause->autoneg;
+ priv->pause_rx = pause->rx_pause;
+ priv->pause_tx = pause->tx_pause;
+
+ phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+ if (pause->rx_pause)
+ phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+ if (pause->tx_pause)
+ phydev->advertising ^= ADVERTISED_Asym_Pause;
+
+ if (pause->autoneg) {
+ if (netif_running(ndev))
+ phy_start_aneg(phydev);
+ }
+
+ return 0;
+}
+
+static const struct ethtool_ops ave_ethtool_ops = {
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_drvinfo = ave_get_drvinfo,
+ .nway_reset = phy_ethtool_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = ave_get_msglevel,
+ .set_msglevel = ave_set_msglevel,
+ .get_wol = ave_get_wol,
+ .set_wol = ave_set_wol,
+ .get_pauseparam = ave_get_pauseparam,
+ .set_pauseparam = ave_set_pauseparam,
+};
+
+static int ave_pfsel_start(struct net_device *ndev, unsigned int entry)
+{
+ u32 val;
+
+ if (WARN_ON(entry > AVE_PF_SIZE))
+ return -EINVAL;
+
+ val = ave_r32(ndev, AVE_PFEN);
+ ave_w32(ndev, AVE_PFEN, val | BIT(entry));
+
+ return 0;
+}
+
+static int ave_pfsel_stop(struct net_device *ndev, unsigned int entry)
+{
+ u32 val;
+
+ if (WARN_ON(entry > AVE_PF_SIZE))
+ return -EINVAL;
+
+ val = ave_r32(ndev, AVE_PFEN);
+ ave_w32(ndev, AVE_PFEN, val & ~BIT(entry));
+
+ return 0;
+}
+
+static int ave_pfsel_macaddr_set(struct net_device *ndev,
+ unsigned int entry, unsigned char *mac_addr,
+ unsigned int set_size)
+{
+ u32 val;
+
+ if (WARN_ON(entry > AVE_PF_SIZE))
+ return -EINVAL;
+ if (WARN_ON(set_size > 6))
+ return -EINVAL;
+
+ ave_pfsel_stop(ndev, entry);
+
+ /* set MAC address for the filter */
+ val = mac_addr[0] | (mac_addr[1] << 8) | (mac_addr[2] << 16)
+ | (mac_addr[3] << 24);
+ ave_w32(ndev, AVE_PKTF(entry), val);
+ val = mac_addr[4] | (mac_addr[5] << 8);
+ ave_w32(ndev, AVE_PKTF(entry) + 4, val);
+
+ /* set byte mask */
+ ave_w32(ndev, AVE_PFMBYTE(entry), GENMASK(31, set_size) & ~0xc0);
+ ave_w32(ndev, AVE_PFMBYTE(entry) + 4, 0xFFFFFFFF);
+
+ /* set bit mask filter */
+ ave_w32(ndev, AVE_PFMBIT(entry), 0x0000FFFF);
+
+ /* set selector to ring 0 */
+ ave_w32(ndev, AVE_PFSEL(entry), 0);
+
+ /* restart filter */
+ ave_pfsel_start(ndev, entry);
+
+ return 0;
+}
+
+static int ave_mdio_busywait(struct net_device *ndev)
+{
+ int ret = 0, loop = 100;
+ u32 mdiosr;
+
+ /* wait until completion */
+ while (--loop) {
+ mdiosr = ave_r32(ndev, AVE_MDIOSR);
+ if (!(mdiosr & AVE_MDIOSR_STS))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (!loop) {
+ netdev_err(ndev,
+ "failed to read from MDIO (status:0x%08x)\n",
+ mdiosr);
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static int ave_mdiobus_read(struct mii_bus *bus, int phyid, int regnum)
+{
+ struct net_device *ndev = bus->priv;
+ u32 mdioctl;
+ int ret;
+
+ /* write address */
+ ave_w32(ndev, AVE_MDIOAR, (phyid << 8) | regnum);
+
+ /* read request */
+ mdioctl = ave_r32(ndev, AVE_MDIOCTR);
+ ave_w32(ndev, AVE_MDIOCTR,
+ (mdioctl | AVE_MDIOCTR_RREQ) & ~AVE_MDIOCTR_WREQ);
+
+ ret = ave_mdio_busywait(ndev);
+ if (ret) {
+ netdev_err(ndev, "phy-%d reg-%x read failed\n",
+ phyid, regnum);
+ return ret;
+ }
+
+ return ave_r32(ndev, AVE_MDIORDR) & GENMASK(15, 0);
+}
+
+static int ave_mdiobus_write(struct mii_bus *bus,
+ int phyid, int regnum, u16 val)
+{
+ struct net_device *ndev = bus->priv;
+ u32 mdioctl;
+ int ret;
+
+ /* write address */
+ ave_w32(ndev, AVE_MDIOAR, (phyid << 8) | regnum);
+
+ /* write data */
+ ave_w32(ndev, AVE_MDIOWDR, val);
+
+ /* write request */
+ mdioctl = ave_r32(ndev, AVE_MDIOCTR);
+ ave_w32(ndev, AVE_MDIOCTR,
+ (mdioctl | AVE_MDIOCTR_WREQ) & ~AVE_MDIOCTR_RREQ);
+
+ ret = ave_mdio_busywait(ndev);
+ if (ret)
+ netdev_err(ndev, "phy-%d reg-%x write failed\n",
+ phyid, regnum);
+
+ return ret;
+}
+
+static dma_addr_t ave_dma_map(struct net_device *ndev, struct ave_desc *desc,
+ void *ptr, size_t len,
+ enum dma_data_direction dir)
+{
+ dma_addr_t paddr;
+
+ paddr = dma_map_single(ndev->dev.parent, ptr, len, dir);
+ if (unlikely(dma_mapping_error(ndev->dev.parent, paddr))) {
+ paddr = (dma_addr_t)-ENOMEM;
+ } else {
+ desc->skbs_dma = paddr;
+ desc->skbs_dmalen = len;
+ }
+
+ return paddr;
+}
+
+static void ave_dma_unmap(struct net_device *ndev, struct ave_desc *desc,
+ enum dma_data_direction dir)
+{
+ if (!desc->skbs_dma)
+ return;
+
+ dma_unmap_single(ndev->dev.parent,
+ desc->skbs_dma, desc->skbs_dmalen, dir);
+ desc->skbs_dma = 0;
+}
+
+/* Set Rx descriptor and memory */
+static int ave_set_rxdesc(struct net_device *ndev, int entry)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+ int ret = 0;
+
+ skb = priv->rx.desc[entry].skbs;
+ if (!skb) {
+ skb = netdev_alloc_skb_ip_align(ndev,
+ AVE_MAX_ETHFRAME);
+ if (!skb) {
+ netdev_err(ndev, "can't allocate skb for Rx\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* set disable to cmdsts */
+ ave_wdesc_cmdsts(ndev, AVE_DESCID_RX, entry,
+ AVE_STS_INTR | AVE_STS_OWN);
+
+ /* map Rx buffer
+ * Rx buffer set to the Rx descriptor has two restrictions:
+ * - Rx buffer address is 4 byte aligned.
+ * - Rx buffer begins with 2 byte headroom, and data will be put from
+ * (buffer + 2).
+ * To satisfy this, specify the address to put back the buffer
+ * pointer advanced by NET_IP_ALIGN by netdev_alloc_skb_ip_align(),
+ * and expand the map size by NET_IP_ALIGN.
+ */
+ paddr = ave_dma_map(ndev, &priv->rx.desc[entry],
+ skb->data - NET_IP_ALIGN,
+ AVE_MAX_ETHFRAME + NET_IP_ALIGN, DMA_FROM_DEVICE);
+ if (paddr == -ENOMEM) {
+ netdev_err(ndev, "can't map skb for Rx\n");
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+ priv->rx.desc[entry].skbs = skb;
+
+ /* set buffer pointer */
+ ave_wdesc_addr(ndev, AVE_DESCID_RX, entry, paddr);
+
+ /* set enable to cmdsts */
+ ave_wdesc_cmdsts(ndev, AVE_DESCID_RX, entry,
+ AVE_STS_INTR | AVE_MAX_ETHFRAME);
+
+ return ret;
+}
+
+/* Switch state of descriptor */
+static int ave_desc_switch(struct net_device *ndev, enum desc_state state)
+{
+ int counter;
+ int ret = 0;
+ u32 val;
+
+ switch (state) {
+ case AVE_DESC_START:
+ ave_w32(ndev, AVE_DESCC, AVE_DESCC_TD | AVE_DESCC_RD0);
+ break;
+
+ case AVE_DESC_STOP:
+ ave_w32(ndev, AVE_DESCC, 0);
+ counter = 100;
+ while (--counter) {
+ usleep_range(100, 150);
+ if (!ave_r32(ndev, AVE_DESCC))
+ break;
+ }
+ if (!counter) {
+ netdev_err(ndev, "can't stop descriptor\n");
+ ret = -EBUSY;
+ }
+ break;
+
+ case AVE_DESC_RX_SUSPEND:
+ val = ave_r32(ndev, AVE_DESCC);
+ val |= AVE_DESCC_RDSTP;
+ val &= ~AVE_DESCC_STATUS_MASK;
+ ave_w32(ndev, AVE_DESCC, val);
+
+ counter = 1000;
+ while (--counter) {
+ usleep_range(100, 150);
+ val = ave_r32(ndev, AVE_DESCC);
+ if (val & (AVE_DESCC_RDSTP << 16))
+ break;
+ }
+ if (!counter) {
+ netdev_err(ndev, "can't suspend descriptor\n");
+ ret = -EBUSY;
+ }
+ break;
+
+ case AVE_DESC_RX_PERMIT:
+ val = ave_r32(ndev, AVE_DESCC);
+ val &= ~AVE_DESCC_RDSTP;
+ val &= ~AVE_DESCC_STATUS_MASK;
+ ave_w32(ndev, AVE_DESCC, val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int ave_tx_completion(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 proc_idx, done_idx, ndesc, cmdsts;
+ unsigned int nr_freebuf = 0;
+ unsigned int tx_packets = 0;
+ unsigned int tx_bytes = 0;
+
+ proc_idx = priv->tx.proc_idx;
+ done_idx = priv->tx.done_idx;
+ ndesc = priv->tx.ndesc;
+
+ /* free pre-stored skb from done_idx to proc_idx */
+ while (proc_idx != done_idx) {
+ cmdsts = ave_rdesc_cmdsts(ndev, AVE_DESCID_TX, done_idx);
+
+ /* do nothing if owner is HW (==1 for Tx) */
+ if (cmdsts & AVE_STS_OWN)
+ break;
+
+ /* check Tx status and updates statistics */
+ if (cmdsts & AVE_STS_OK) {
+ tx_bytes += cmdsts & AVE_STS_PKTLEN_TX_MASK;
+ /* success */
+ if (cmdsts & AVE_STS_LAST)
+ tx_packets++;
+ } else {
+ /* error */
+ if (cmdsts & AVE_STS_LAST) {
+ priv->stats_tx.errors++;
+ if (cmdsts & (AVE_STS_OWC | AVE_STS_EC))
+ priv->stats_tx.collisions++;
+ }
+ }
+
+ /* release skb */
+ if (priv->tx.desc[done_idx].skbs) {
+ ave_dma_unmap(ndev, &priv->tx.desc[done_idx],
+ DMA_TO_DEVICE);
+ dev_consume_skb_any(priv->tx.desc[done_idx].skbs);
+ priv->tx.desc[done_idx].skbs = NULL;
+ nr_freebuf++;
+ }
+ done_idx = (done_idx + 1) % ndesc;
+ }
+
+ priv->tx.done_idx = done_idx;
+
+ /* update stats */
+ u64_stats_update_begin(&priv->stats_tx.syncp);
+ priv->stats_tx.packets += tx_packets;
+ priv->stats_tx.bytes += tx_bytes;
+ u64_stats_update_end(&priv->stats_tx.syncp);
+
+ /* wake queue for freeing buffer */
+ if (unlikely(netif_queue_stopped(ndev)) && nr_freebuf)
+ netif_wake_queue(ndev);
+
+ return nr_freebuf;
+}
+
+static int ave_rx(struct net_device *ndev, int num)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ struct sk_buff *skb;
+ u32 proc_idx, done_idx, ndesc, cmdsts;
+ int restpkt, npkts;
+ unsigned int pktlen;
+ unsigned int rx_packets = 0;
+ unsigned int rx_bytes = 0;
+
+ proc_idx = priv->rx.proc_idx;
+ done_idx = priv->rx.done_idx;
+ ndesc = priv->rx.ndesc;
+ restpkt = ((proc_idx + ndesc - 1) - done_idx) % ndesc;
+
+ for (npkts = 0; npkts < num; npkts++) {
+ /* we can't receive more packet, so fill desc quickly */
+ if (--restpkt < 0)
+ break;
+
+ cmdsts = ave_rdesc_cmdsts(ndev, AVE_DESCID_RX, proc_idx);
+
+ /* do nothing if owner is HW (==0 for Rx) */
+ if (!(cmdsts & AVE_STS_OWN))
+ break;
+
+ if (!(cmdsts & AVE_STS_OK)) {
+ priv->stats_rx.errors++;
+ proc_idx = (proc_idx + 1) % ndesc;
+ continue;
+ }
+
+ pktlen = cmdsts & AVE_STS_PKTLEN_RX_MASK;
+
+ /* get skbuff for rx */
+ skb = priv->rx.desc[proc_idx].skbs;
+ priv->rx.desc[proc_idx].skbs = NULL;
+
+ ave_dma_unmap(ndev, &priv->rx.desc[proc_idx], DMA_FROM_DEVICE);
+
+ skb->dev = ndev;
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ if ((cmdsts & AVE_STS_CSSV) && (!(cmdsts & AVE_STS_CSER)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ rx_packets++;
+ rx_bytes += pktlen;
+
+ netif_receive_skb(skb);
+
+ proc_idx = (proc_idx + 1) % ndesc;
+ }
+
+ priv->rx.proc_idx = proc_idx;
+
+ /* update stats */
+ u64_stats_update_begin(&priv->stats_rx.syncp);
+ priv->stats_rx.packets += rx_packets;
+ priv->stats_rx.bytes += rx_bytes;
+ u64_stats_update_end(&priv->stats_rx.syncp);
+
+ /* refill the Rx buffers */
+ while (proc_idx != done_idx) {
+ if (ave_set_rxdesc(ndev, done_idx))
+ break;
+ done_idx = (done_idx + 1) % ndesc;
+ }
+
+ priv->rx.done_idx = done_idx;
+
+ return npkts;
+}
+
+static inline u32 ave_irq_disable_all(struct net_device *ndev)
+{
+ u32 ret;
+
+ ret = ave_r32(ndev, AVE_GIMR);
+ ave_w32(ndev, AVE_GIMR, 0);
+
+ return ret;
+}
+
+static inline void ave_irq_restore(struct net_device *ndev, u32 val)
+{
+ ave_w32(ndev, AVE_GIMR, val);
+}
+
+static inline void ave_irq_enable(struct net_device *ndev, u32 bitflag)
+{
+ ave_w32(ndev, AVE_GIMR, ave_r32(ndev, AVE_GIMR) | bitflag);
+ ave_w32(ndev, AVE_GISR, bitflag);
+}
+
+static inline void ave_irq_disable(struct net_device *ndev, u32 bitflag)
+{
+ ave_w32(ndev, AVE_GIMR, ave_r32(ndev, AVE_GIMR) & ~bitflag);
+}
+
+static void ave_global_reset(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 val;
+
+ /* set config register */
+ val = AVE_CFGR_FLE | AVE_CFGR_IPFCEN | AVE_CFGR_CHE;
+ if (!phy_interface_mode_is_rgmii(priv->phy_mode))
+ val |= AVE_CFGR_MII;
+ ave_w32(ndev, AVE_CFGR, val);
+
+ /* reset RMII register */
+ val = ave_r32(ndev, AVE_RSTCTRL);
+ val &= ~AVE_RSTCTRL_RMIIRST;
+ ave_w32(ndev, AVE_RSTCTRL, val);
+
+ /* assert reset */
+ ave_w32(ndev, AVE_GRR, AVE_GRR_GRST | AVE_GRR_PHYRST);
+ msleep(20);
+
+ /* 1st, negate PHY reset only */
+ ave_w32(ndev, AVE_GRR, AVE_GRR_GRST);
+ msleep(40);
+
+ /* negate reset */
+ ave_w32(ndev, AVE_GRR, 0);
+ msleep(40);
+
+ /* negate RMII register */
+ val = ave_r32(ndev, AVE_RSTCTRL);
+ val |= AVE_RSTCTRL_RMIIRST;
+ ave_w32(ndev, AVE_RSTCTRL, val);
+
+ ave_irq_disable_all(ndev);
+}
+
+static void ave_rxf_reset(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 rxcr_org;
+
+ /* save and disable MAC receive op */
+ rxcr_org = ave_r32(ndev, AVE_RXCR);
+ ave_w32(ndev, AVE_RXCR, rxcr_org & (~AVE_RXCR_RXEN));
+
+ /* suspend Rx descriptor */
+ ave_desc_switch(ndev, AVE_DESC_RX_SUSPEND);
+
+ /* receive all packets before descriptor starts */
+ ave_rx(ndev, priv->rx.ndesc);
+
+ /* assert reset */
+ ave_w32(ndev, AVE_GRR, AVE_GRR_RXFFR);
+ usleep_range(40, 50);
+
+ /* negate reset */
+ ave_w32(ndev, AVE_GRR, 0);
+ usleep_range(10, 20);
+
+ /* negate interrupt status */
+ ave_w32(ndev, AVE_GISR, AVE_GI_RXOVF);
+
+ /* permit descriptor */
+ ave_desc_switch(ndev, AVE_DESC_RX_PERMIT);
+
+ /* restore MAC reccieve op */
+ ave_w32(ndev, AVE_RXCR, rxcr_org);
+}
+
+static irqreturn_t ave_interrupt(int irq, void *netdev)
+{
+ struct net_device *ndev = (struct net_device *)netdev;
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 gimr_val, gisr_val;
+
+ gimr_val = ave_irq_disable_all(ndev);
+
+ /* get interrupt status */
+ gisr_val = ave_r32(ndev, AVE_GISR);
+
+ /* PHY */
+ if (gisr_val & AVE_GI_PHY)
+ ave_w32(ndev, AVE_GISR, AVE_GI_PHY);
+
+ /* check exceeding packet */
+ if (gisr_val & AVE_GI_RXERR) {
+ ave_w32(ndev, AVE_GISR, AVE_GI_RXERR);
+ netdev_err(ndev, "receive a packet exceeding frame buffer\n");
+ }
+
+ gisr_val &= gimr_val;
+ if (!gisr_val)
+ goto exit_isr;
+
+ /* RxFIFO overflow */
+ if (gisr_val & AVE_GI_RXOVF) {
+ priv->stats_rx.fifo_errors++;
+ ave_rxf_reset(ndev);
+ goto exit_isr;
+ }
+
+ /* Rx drop */
+ if (gisr_val & AVE_GI_RXDROP) {
+ priv->stats_rx.dropped++;
+ ave_w32(ndev, AVE_GISR, AVE_GI_RXDROP);
+ }
+
+ /* Rx interval */
+ if (gisr_val & AVE_GI_RXIINT) {
+ napi_schedule(&priv->napi_rx);
+ /* still force to disable Rx interrupt until NAPI finishes */
+ gimr_val &= ~AVE_GI_RXIINT;
+ }
+
+ /* Tx completed */
+ if (gisr_val & AVE_GI_TX) {
+ napi_schedule(&priv->napi_tx);
+ /* still force to disable Tx interrupt until NAPI finishes */
+ gimr_val &= ~AVE_GI_TX;
+ }
+
+exit_isr:
+ ave_irq_restore(ndev, gimr_val);
+
+ return IRQ_HANDLED;
+}
+
+static int ave_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct ave_private *priv;
+ struct net_device *ndev;
+ int num;
+
+ priv = container_of(napi, struct ave_private, napi_rx);
+ ndev = priv->ndev;
+
+ num = ave_rx(ndev, budget);
+ if (num < budget) {
+ napi_complete_done(napi, num);
+
+ /* enable Rx interrupt when NAPI finishes */
+ ave_irq_enable(ndev, AVE_GI_RXIINT);
+ }
+
+ return num;
+}
+
+static int ave_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct ave_private *priv;
+ struct net_device *ndev;
+ int num;
+
+ priv = container_of(napi, struct ave_private, napi_tx);
+ ndev = priv->ndev;
+
+ num = ave_tx_completion(ndev);
+ napi_complete(napi);
+
+ /* enable Tx interrupt when NAPI finishes */
+ ave_irq_enable(ndev, AVE_GI_TX);
+
+ return num;
+}
+
+static void ave_pfsel_promisc_set(struct net_device *ndev,
+ unsigned int entry, u32 rxring)
+{
+ if (WARN_ON(entry > AVE_PF_SIZE))
+ return;
+
+ ave_pfsel_stop(ndev, entry);
+
+ /* set byte mask */
+ ave_w32(ndev, AVE_PFMBYTE(entry), 0xFFFFFFFF);
+ ave_w32(ndev, AVE_PFMBYTE(entry) + 4, 0xFFFFFFFF);
+
+ /* set bit mask filter */
+ ave_w32(ndev, AVE_PFMBIT(entry), 0x0000FFFF);
+
+ /* set selector to rxring */
+ ave_w32(ndev, AVE_PFSEL(entry), rxring);
+
+ ave_pfsel_start(ndev, entry);
+}
+
+static void ave_pfsel_init(struct net_device *ndev)
+{
+ int i;
+ unsigned char bcast_mac[ETH_ALEN];
+
+ eth_broadcast_addr(bcast_mac);
+
+ for (i = 0; i < AVE_PF_SIZE; i++)
+ ave_pfsel_stop(ndev, i);
+
+ /* promiscious entry, select ring 0 */
+ ave_pfsel_promisc_set(ndev, AVE_PFNUM_FILTER, 0);
+
+ /* unicast entry */
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+
+ /* broadcast entry */
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_BROADCAST, bcast_mac, 6);
+}
+
+static void ave_adjust_link(struct net_device *ndev)
+{
+ struct phy_device *phydev = ndev->phydev;
+ u32 val, txcr, rxcr, rxcr_org;
+ u16 rmt_adv = 0, lcl_adv = 0;
+ u8 cap;
+
+ /* set RGMII speed */
+ val = ave_r32(ndev, AVE_TXCR);
+ val &= ~(AVE_TXCR_TXSPD_100 | AVE_TXCR_TXSPD_1G);
+
+ if (phy_interface_is_rgmii(phydev) && phydev->speed == SPEED_1000)
+ val |= AVE_TXCR_TXSPD_1G;
+ else if (phydev->speed == SPEED_100)
+ val |= AVE_TXCR_TXSPD_100;
+
+ ave_w32(ndev, AVE_TXCR, val);
+
+ /* set RMII speed (100M/10M only) */
+ if (!phy_interface_is_rgmii(phydev)) {
+ val = ave_r32(ndev, AVE_LINKSEL);
+ if (phydev->speed == SPEED_10)
+ val &= ~AVE_LINKSEL_100M;
+ else
+ val |= AVE_LINKSEL_100M;
+ ave_w32(ndev, AVE_LINKSEL, val);
+ }
+
+ /* check current RXCR/TXCR */
+ rxcr = ave_r32(ndev, AVE_RXCR);
+ txcr = ave_r32(ndev, AVE_TXCR);
+ rxcr_org = rxcr;
+
+ if (phydev->duplex) {
+ rxcr |= AVE_RXCR_FDUPEN;
+
+ if (phydev->pause)
+ rmt_adv |= LPA_PAUSE_CAP;
+ if (phydev->asym_pause)
+ rmt_adv |= LPA_PAUSE_ASYM;
+ if (phydev->advertising & ADVERTISED_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_CAP;
+ if (phydev->advertising & ADVERTISED_Asym_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+ cap = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+ if (cap & FLOW_CTRL_TX)
+ txcr |= AVE_TXCR_FLOCTR;
+ else
+ txcr &= ~AVE_TXCR_FLOCTR;
+ if (cap & FLOW_CTRL_RX)
+ rxcr |= AVE_RXCR_FLOCTR;
+ else
+ rxcr &= ~AVE_RXCR_FLOCTR;
+ } else {
+ rxcr &= ~AVE_RXCR_FDUPEN;
+ rxcr &= ~AVE_RXCR_FLOCTR;
+ txcr &= ~AVE_TXCR_FLOCTR;
+ }
+
+ if (rxcr_org != rxcr) {
+ /* disable Rx mac */
+ ave_w32(ndev, AVE_RXCR, rxcr & ~AVE_RXCR_RXEN);
+ /* change and enable TX/Rx mac */
+ ave_w32(ndev, AVE_TXCR, txcr);
+ ave_w32(ndev, AVE_RXCR, rxcr);
+ }
+
+ phy_print_status(phydev);
+}
+
+static void ave_macaddr_init(struct net_device *ndev)
+{
+ unsigned char *mac_addr;
+ u32 val;
+
+ /* set macaddr */
+ mac_addr = ndev->dev_addr;
+ val = mac_addr[0] | (mac_addr[1] << 8) | (mac_addr[2] << 16)
+ | (mac_addr[3] << 24);
+ ave_w32(ndev, AVE_RXMAC1R, val);
+ val = mac_addr[4] | (mac_addr[5] << 8);
+ ave_w32(ndev, AVE_RXMAC2R, val);
+
+ /* pfsel unicast entry */
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+}
+
+static int ave_init(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ struct device *dev = ndev->dev.parent;
+ struct device_node *np = dev->of_node, *mdio_np;
+ struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+ struct phy_device *phydev;
+ int ret = 0;
+
+ /* enable clk because of hw access until ndo_open */
+ clk_prepare_enable(priv->clk);
+ reset_control_deassert(priv->rst);
+
+ ave_global_reset(ndev);
+
+ mdio_np = of_get_child_by_name(np, "mdio");
+ if (!mdio_np) {
+ dev_err(dev, "mdio node not found\n");
+ ret = -EINVAL;
+ goto out_clk_disable;
+ }
+ ret = of_mdiobus_register(priv->mdio, mdio_np);
+ of_node_put(mdio_np);
+ if (ret) {
+ dev_err(dev, "failed to register mdiobus\n");
+ goto out_clk_disable;
+ }
+
+ phydev = of_phy_get_and_connect(ndev, np, ave_adjust_link);
+ if (!phydev) {
+ dev_err(dev, "could not attach to PHY\n");
+ ret = -ENODEV;
+ goto out_mdio_unregister;
+ }
+
+ priv->phydev = phydev;
+
+ phy_ethtool_get_wol(phydev, &wol);
+ device_set_wakeup_capable(&ndev->dev, !!wol.supported);
+
+ if (!phy_interface_is_rgmii(phydev)) {
+ phydev->supported &= ~PHY_GBIT_FEATURES;
+ phydev->supported |= PHY_BASIC_FEATURES;
+ }
+ phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+ phy_attached_info(phydev);
+
+ return ret;
+
+out_mdio_unregister:
+ mdiobus_unregister(priv->mdio);
+out_clk_disable:
+ reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static void ave_uninit(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+
+ phy_disconnect(priv->phydev);
+
+ /* disable clk because of hw access after ndo_stop */
+ reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk);
+}
+
+static int ave_open(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ int entry;
+ int ret;
+ u32 val;
+
+ ret = request_irq(priv->irq, ave_interrupt, IRQF_SHARED, ndev->name,
+ ndev);
+ if (ret)
+ return ret;
+
+ priv->tx.desc = kcalloc(priv->tx.ndesc, sizeof(*priv->tx.desc),
+ GFP_KERNEL);
+ if (!priv->tx.desc) {
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+
+ priv->rx.desc = kcalloc(priv->rx.ndesc, sizeof(*priv->rx.desc),
+ GFP_KERNEL);
+ if (!priv->rx.desc) {
+ kfree(priv->tx.desc);
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+
+ /* initialize Tx work and descriptor */
+ priv->tx.proc_idx = 0;
+ priv->tx.done_idx = 0;
+ for (entry = 0; entry < priv->tx.ndesc; entry++) {
+ ave_wdesc_cmdsts(ndev, AVE_DESCID_TX, entry, 0);
+ ave_wdesc_addr(ndev, AVE_DESCID_TX, entry, 0);
+ }
+ ave_w32(ndev, AVE_TXDC, AVE_TXDC_ADDR_START
+ | (((priv->tx.ndesc * priv->desc_size) << 16) & AVE_TXDC_SIZE));
+
+ /* initialize Rx work and descriptor */
+ priv->rx.proc_idx = 0;
+ priv->rx.done_idx = 0;
+ for (entry = 0; entry < priv->rx.ndesc; entry++) {
+ if (ave_set_rxdesc(ndev, entry))
+ break;
+ }
+ ave_w32(ndev, AVE_RXDC0, AVE_RXDC0_ADDR_START
+ | (((priv->rx.ndesc * priv->desc_size) << 16) & AVE_RXDC0_SIZE));
+
+ ave_desc_switch(ndev, AVE_DESC_START);
+
+ ave_pfsel_init(ndev);
+ ave_macaddr_init(ndev);
+
+ /* set Rx configuration */
+ /* full duplex, enable pause drop, enalbe flow control */
+ val = AVE_RXCR_RXEN | AVE_RXCR_FDUPEN | AVE_RXCR_DRPEN |
+ AVE_RXCR_FLOCTR | (AVE_MAX_ETHFRAME & AVE_RXCR_MPSIZ_MASK);
+ ave_w32(ndev, AVE_RXCR, val);
+
+ /* set Tx configuration */
+ /* enable flow control, disable loopback */
+ ave_w32(ndev, AVE_TXCR, AVE_TXCR_FLOCTR);
+
+ /* enable timer, clear EN,INTM, and mask interval unit(BSCK) */
+ val = ave_r32(ndev, AVE_IIRQC) & AVE_IIRQC_BSCK;
+ val |= AVE_IIRQC_EN0 | (AVE_INTM_COUNT << 16);
+ ave_w32(ndev, AVE_IIRQC, val);
+
+ val = AVE_GI_RXIINT | AVE_GI_RXOVF | AVE_GI_TX;
+ ave_irq_restore(ndev, val);
+
+ napi_enable(&priv->napi_rx);
+ napi_enable(&priv->napi_tx);
+
+ phy_start(ndev->phydev);
+ phy_start_aneg(ndev->phydev);
+ netif_start_queue(ndev);
+
+ return 0;
+
+out_free_irq:
+ disable_irq(priv->irq);
+ free_irq(priv->irq, ndev);
+
+ return ret;
+}
+
+static int ave_stop(struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ int entry;
+
+ ave_irq_disable_all(ndev);
+ disable_irq(priv->irq);
+ free_irq(priv->irq, ndev);
+
+ netif_tx_disable(ndev);
+ phy_stop(ndev->phydev);
+ napi_disable(&priv->napi_tx);
+ napi_disable(&priv->napi_rx);
+
+ ave_desc_switch(ndev, AVE_DESC_STOP);
+
+ /* free Tx buffer */
+ for (entry = 0; entry < priv->tx.ndesc; entry++) {
+ if (!priv->tx.desc[entry].skbs)
+ continue;
+
+ ave_dma_unmap(ndev, &priv->tx.desc[entry], DMA_TO_DEVICE);
+ dev_kfree_skb_any(priv->tx.desc[entry].skbs);
+ priv->tx.desc[entry].skbs = NULL;
+ }
+ priv->tx.proc_idx = 0;
+ priv->tx.done_idx = 0;
+
+ /* free Rx buffer */
+ for (entry = 0; entry < priv->rx.ndesc; entry++) {
+ if (!priv->rx.desc[entry].skbs)
+ continue;
+
+ ave_dma_unmap(ndev, &priv->rx.desc[entry], DMA_FROM_DEVICE);
+ dev_kfree_skb_any(priv->rx.desc[entry].skbs);
+ priv->rx.desc[entry].skbs = NULL;
+ }
+ priv->rx.proc_idx = 0;
+ priv->rx.done_idx = 0;
+
+ kfree(priv->tx.desc);
+ kfree(priv->rx.desc);
+
+ return 0;
+}
+
+static int ave_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ u32 proc_idx, done_idx, ndesc, cmdsts;
+ int freepkt;
+ dma_addr_t paddr;
+
+ proc_idx = priv->tx.proc_idx;
+ done_idx = priv->tx.done_idx;
+ ndesc = priv->tx.ndesc;
+ freepkt = ((done_idx + ndesc - 1) - proc_idx) % ndesc;
+
+ /* stop queue when not enough entry */
+ if (unlikely(freepkt < 1)) {
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* add padding for short packet */
+ if (skb_put_padto(skb, ETH_ZLEN)) {
+ priv->stats_tx.dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ /* map Tx buffer
+ * Tx buffer set to the Tx descriptor doesn't have any restriction.
+ */
+ paddr = ave_dma_map(ndev, &priv->tx.desc[proc_idx],
+ skb->data,
+ skb->len, DMA_TO_DEVICE);
+ if (paddr == -ENOMEM) {
+ dev_kfree_skb_any(skb);
+ priv->stats_tx.dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ priv->tx.desc[proc_idx].skbs = skb;
+
+ ave_wdesc_addr(ndev, AVE_DESCID_TX, proc_idx, paddr);
+
+ cmdsts = AVE_STS_OWN | AVE_STS_1ST | AVE_STS_LAST
+ | (skb->len & AVE_STS_PKTLEN_TX_MASK);
+
+ /* set interrupt per AVE_FORCE_TXINTCNT or when queue is stopped */
+ if (!(proc_idx % AVE_FORCE_TXINTCNT) || netif_queue_stopped(ndev))
+ cmdsts |= AVE_STS_INTR;
+
+ /* disable checksum calculation when skb doesn't calurate checksum */
+ if (skb->ip_summed == CHECKSUM_NONE ||
+ skb->ip_summed == CHECKSUM_UNNECESSARY)
+ cmdsts |= AVE_STS_NOCSUM;
+
+ ave_wdesc_cmdsts(ndev, AVE_DESCID_TX, proc_idx, cmdsts);
+
+ priv->tx.proc_idx = (proc_idx + 1) % ndesc;
+
+ return NETDEV_TX_OK;
+}
+
+static int ave_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ return phy_mii_ioctl(ndev->phydev, ifr, cmd);
+}
+
+static void ave_set_rx_mode(struct net_device *ndev)
+{
+ int count, mc_cnt = netdev_mc_count(ndev);
+ struct netdev_hw_addr *hw_adr;
+ u32 val;
+ u8 v4multi_macadr[6] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 v6multi_macadr[6] = { 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ /* MAC addr filter enable for promiscious mode */
+ val = ave_r32(ndev, AVE_RXCR);
+ if (ndev->flags & IFF_PROMISC || !mc_cnt)
+ val &= ~AVE_RXCR_AFEN;
+ else
+ val |= AVE_RXCR_AFEN;
+ ave_w32(ndev, AVE_RXCR, val);
+
+ /* set all multicast address */
+ if ((ndev->flags & IFF_ALLMULTI) || mc_cnt > AVE_PF_MULTICAST_SIZE) {
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST,
+ v4multi_macadr, 1);
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST + 1,
+ v6multi_macadr, 1);
+ } else {
+ /* stop all multicast filter */
+ for (count = 0; count < AVE_PF_MULTICAST_SIZE; count++)
+ ave_pfsel_stop(ndev, AVE_PFNUM_MULTICAST + count);
+
+ /* set multicast addresses */
+ count = 0;
+ netdev_for_each_mc_addr(hw_adr, ndev) {
+ if (count == mc_cnt)
+ break;
+ ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST + count,
+ hw_adr->addr, 6);
+ count++;
+ }
+ }
+}
+
+static void ave_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ave_private *priv = netdev_priv(ndev);
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin_irq(&priv->stats_rx.syncp);
+ stats->rx_packets = priv->stats_rx.packets;
+ stats->rx_bytes = priv->stats_rx.bytes;
+ } while (u64_stats_fetch_retry_irq(&priv->stats_rx.syncp, start));
+
+ do {
+ start = u64_stats_fetch_begin_irq(&priv->stats_tx.syncp);
+ stats->tx_packets = priv->stats_tx.packets;
+ stats->tx_bytes = priv->stats_tx.bytes;
+ } while (u64_stats_fetch_retry_irq(&priv->stats_tx.syncp, start));
+
+ stats->rx_errors = priv->stats_rx.errors;
+ stats->tx_errors = priv->stats_tx.errors;
+ stats->rx_dropped = priv->stats_rx.dropped;
+ stats->tx_dropped = priv->stats_tx.dropped;
+ stats->rx_fifo_errors = priv->stats_rx.fifo_errors;
+ stats->collisions = priv->stats_tx.collisions;
+}
+
+static int ave_set_mac_address(struct net_device *ndev, void *p)
+{
+ int ret = eth_mac_addr(ndev, p);
+
+ if (ret)
+ return ret;
+
+ ave_macaddr_init(ndev);
+
+ return 0;
+}
+
+static const struct net_device_ops ave_netdev_ops = {
+ .ndo_init = ave_init,
+ .ndo_uninit = ave_uninit,
+ .ndo_open = ave_open,
+ .ndo_stop = ave_stop,
+ .ndo_start_xmit = ave_start_xmit,
+ .ndo_do_ioctl = ave_ioctl,
+ .ndo_set_rx_mode = ave_set_rx_mode,
+ .ndo_get_stats64 = ave_get_stats64,
+ .ndo_set_mac_address = ave_set_mac_address,
+};
+
+static int ave_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ u32 ave_id;
+ struct ave_private *priv;
+ const struct ave_soc_data *data;
+ phy_interface_t phy_mode;
+ struct net_device *ndev;
+ struct resource *res;
+ void __iomem *base;
+ int irq, ret = 0;
+ char buf[ETHTOOL_FWVERS_LEN];
+ const void *mac_addr;
+
+ data = of_device_get_match_data(dev);
+ if (WARN_ON(!data))
+ return -EINVAL;
+
+ phy_mode = of_get_phy_mode(np);
+ if (phy_mode < 0) {
+ dev_err(dev, "phy-mode not found\n");
+ return -EINVAL;
+ }
+ if ((!phy_interface_mode_is_rgmii(phy_mode)) &&
+ phy_mode != PHY_INTERFACE_MODE_RMII &&
+ phy_mode != PHY_INTERFACE_MODE_MII) {
+ dev_err(dev, "phy-mode is invalid\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "IRQ not found\n");
+ return irq;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ndev = alloc_etherdev(sizeof(struct ave_private));
+ if (!ndev) {
+ dev_err(dev, "can't allocate ethernet device\n");
+ return -ENOMEM;
+ }
+
+ ndev->netdev_ops = &ave_netdev_ops;
+ ndev->ethtool_ops = &ave_ethtool_ops;
+ SET_NETDEV_DEV(ndev, dev);
+
+ ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+ ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+
+ ndev->max_mtu = AVE_MAX_ETHFRAME - (ETH_HLEN + ETH_FCS_LEN);
+
+ mac_addr = of_get_mac_address(np);
+ if (mac_addr)
+ ether_addr_copy(ndev->dev_addr, mac_addr);
+
+ /* if the mac address is invalid, use random mac address */
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ eth_hw_addr_random(ndev);
+ dev_warn(dev, "Using random MAC address: %pM\n",
+ ndev->dev_addr);
+ }
+
+ priv = netdev_priv(ndev);
+ priv->base = base;
+ priv->irq = irq;
+ priv->ndev = ndev;
+ priv->msg_enable = netif_msg_init(-1, AVE_DEFAULT_MSG_ENABLE);
+ priv->phy_mode = phy_mode;
+ priv->data = data;
+
+ if (IS_DESC_64BIT(priv)) {
+ priv->desc_size = AVE_DESC_SIZE_64;
+ priv->tx.daddr = AVE_TXDM_64;
+ priv->rx.daddr = AVE_RXDM_64;
+ } else {
+ priv->desc_size = AVE_DESC_SIZE_32;
+ priv->tx.daddr = AVE_TXDM_32;
+ priv->rx.daddr = AVE_RXDM_32;
+ }
+ priv->tx.ndesc = AVE_NR_TXDESC;
+ priv->rx.ndesc = AVE_NR_RXDESC;
+
+ u64_stats_init(&priv->stats_rx.syncp);
+ u64_stats_init(&priv->stats_tx.syncp);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto out_free_netdev;
+ }
+
+ priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ ret = PTR_ERR(priv->rst);
+ goto out_free_netdev;
+ }
+
+ priv->mdio = devm_mdiobus_alloc(dev);
+ if (!priv->mdio) {
+ ret = -ENOMEM;
+ goto out_free_netdev;
+ }
+ priv->mdio->priv = ndev;
+ priv->mdio->parent = dev;
+ priv->mdio->read = ave_mdiobus_read;
+ priv->mdio->write = ave_mdiobus_write;
+ priv->mdio->name = "uniphier-mdio";
+ snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%x",
+ pdev->name, pdev->id);
+
+ /* Register as a NAPI supported driver */
+ netif_napi_add(ndev, &priv->napi_rx, ave_poll_rx, priv->rx.ndesc);
+ netif_tx_napi_add(ndev, &priv->napi_tx, ave_poll_tx, priv->tx.ndesc);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(dev, "failed to register netdevice\n");
+ goto out_del_napi;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ /* get ID and version */
+ ave_id = ave_r32(ndev, AVE_IDR);
+ ave_get_fwversion(ndev, buf, sizeof(buf));
+
+ dev_info(dev, "Socionext %c%c%c%c Ethernet IP %s (irq=%d, phy=%s)\n",
+ (ave_id >> 24) & 0xff, (ave_id >> 16) & 0xff,
+ (ave_id >> 8) & 0xff, (ave_id >> 0) & 0xff,
+ buf, priv->irq, phy_modes(phy_mode));
+
+ return 0;
+
+out_del_napi:
+ netif_napi_del(&priv->napi_rx);
+ netif_napi_del(&priv->napi_tx);
+out_free_netdev:
+ free_netdev(ndev);
+
+ return ret;
+}
+
+static int ave_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ave_private *priv = netdev_priv(ndev);
+
+ unregister_netdev(ndev);
+ netif_napi_del(&priv->napi_rx);
+ netif_napi_del(&priv->napi_tx);
+ mdiobus_unregister(priv->mdio);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct ave_soc_data ave_pro4_data = {
+ .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_pxs2_data = {
+ .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld11_data = {
+ .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld20_data = {
+ .is_desc_64bit = true,
+};
+
+static const struct of_device_id of_ave_match[] = {
+ {
+ .compatible = "socionext,uniphier-pro4-ave4",
+ .data = &ave_pro4_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-ave4",
+ .data = &ave_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-ave4",
+ .data = &ave_ld11_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-ave4",
+ .data = &ave_ld20_data,
+ },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_ave_match);
+
+static struct platform_driver ave_driver = {
+ .probe = ave_probe,
+ .remove = ave_remove,
+ .driver = {
+ .name = "ave",
+ .of_match_table = of_ave_match,
+ },
+};
+module_platform_driver(ave_driver);
+
+MODULE_DESCRIPTION("Socionext UniPhier AVE ethernet driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* Re: [PATCH net-next] ip6_tunnel: Allow rcv/xmit even if remote address is a local address
From: David Miller @ 2017-10-25 1:34 UTC (permalink / raw)
To: shmulik; +Cc: netdev, shmulik.ladkani
In-Reply-To: <20171020212515.4678-1-shmulik@nsof.io>
From: Shmulik Ladkani <shmulik@nsof.io>
Date: Sat, 21 Oct 2017 00:25:15 +0300
> From: Shmulik Ladkani <shmulik.ladkani@gmail.com>
>
> Currently, ip6_tnl_xmit_ctl drops tunneled packets if the remote
> address (outer v6 destination) is one of host's locally configured
> addresses.
> Same applies to ip6_tnl_rcv_ctl: it drops packets if the remote address
> (outer v6 source) is a local address.
>
> This prevents using ipxip6 (and ip6_gre) tunnels whose local/remote
> endpoints are on same host; OTOH v4 tunnels (ipip or gre) allow such
> configurations.
>
> An example where this proves useful is a system where entities are
> identified by their unique v6 addresses, and use tunnels to encapsulate
> traffic between them. The limitation prevents placing several entities
> on same host.
>
> Introduce IP6_TNL_F_ALLOW_LOCAL_REMOTE which allows to bypass this
> restriction.
>
> Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
Given this wasn't allowed for so long, making it configurable makes
sense.
Applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next] strparser: Use delayed work instead of timer for msg timeout
From: David Miller @ 2017-10-25 1:37 UTC (permalink / raw)
To: tom; +Cc: netdev, eric.dumazet, john.fastabend
In-Reply-To: <20171020234043.2629-1-tom@quantonium.net>
From: Tom Herbert <tom@quantonium.net>
Date: Fri, 20 Oct 2017 16:40:43 -0700
> Sock lock may be taken in the message timer function which is a
> problem since timers run in BH. Instead of timers use delayed_work.
>
> Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
> Fixes: bbb03029a899 ("strparser: Generalize strparser")
> Signed-off-by: Tom Herbert <tom@quantonium.net>
Since this bug exists in 'net' I've applied this patch there.
Thanks.
^ permalink raw reply
* Re: [PATCH] rxrpc: recvmsg: use BUG_ON instead of if condition followed by BUG
From: Tom Herbert @ 2017-10-25 1:41 UTC (permalink / raw)
To: Gustavo A. R. Silva
Cc: David S. Miller, Linux Kernel Network Developers, LKML
In-Reply-To: <20171024162004.GA29376@embeddedor.com>
Please combine these related patches fixing BUG in rxrpc into a patch
set with proper annotation,
Also, can any of these BUG_ONs be replaced by WARN_ONs? Warnings are
generally preferable to crashing the system.
Tom
On Tue, Oct 24, 2017 at 9:20 AM, Gustavo A. R. Silva
<garsilva@embeddedor.com> wrote:
> Use BUG_ON instead of if condition followed by BUG.
>
> This issue was detected with the help of Coccinelle.
>
> Signed-off-by: Gustavo A. R. Silva <garsilva@embeddedor.com>
> ---
> net/rxrpc/recvmsg.c | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
> index bdece21..9598b92 100644
> --- a/net/rxrpc/recvmsg.c
> +++ b/net/rxrpc/recvmsg.c
> @@ -243,8 +243,7 @@ static int rxrpc_verify_packet(struct rxrpc_call *call, struct sk_buff *skb,
> */
> if ((annotation & RXRPC_RX_ANNO_JUMBO) > 1) {
> __be16 tmp;
> - if (skb_copy_bits(skb, offset - 2, &tmp, 2) < 0)
> - BUG();
> + BUG_ON(skb_copy_bits(skb, offset - 2, &tmp, 2) < 0);
> cksum = ntohs(tmp);
> seq += (annotation & RXRPC_RX_ANNO_JUMBO) - 1;
> }
> @@ -503,8 +502,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
>
> release_sock(&rx->sk);
>
> - if (test_bit(RXRPC_CALL_RELEASED, &call->flags))
> - BUG();
> + BUG_ON(test_bit(RXRPC_CALL_RELEASED, &call->flags));
>
> if (test_bit(RXRPC_CALL_HAS_USERID, &call->flags)) {
> if (flags & MSG_CMSG_COMPAT) {
> --
> 2.7.4
>
^ permalink raw reply
* Re: [Patch net 00/15] net_sched: remove RCU callbacks from TC
From: David Miller @ 2017-10-25 1:43 UTC (permalink / raw)
To: xiyou.wangcong; +Cc: netdev, paulmck, jhs, john.fastabend, chrism
In-Reply-To: <20171023220304.2268-1-xiyou.wangcong@gmail.com>
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Mon, 23 Oct 2017 15:02:49 -0700
> Recently, the RCU callbacks used in TC filters and TC actions keep
> drawing my attention, they introduce at least 4 race condition bugs:
Like Eric, I think doing a full RCU sync on every delete is too big
a pill to swallow. This is a major control plane performance
regression.
Please find another reasonable way to fix this.
Thank you.
^ permalink raw reply
* Re: [PATCH net-next v3 0/3] bpf: permit multiple bpf attachments for a single perf tracepoint event
From: David Miller @ 2017-10-25 1:48 UTC (permalink / raw)
To: yhs; +Cc: peterz, rostedt, ast, daniel, kafai, netdev, kernel-team
In-Reply-To: <20171024065309.2401893-1-yhs@fb.com>
From: Yonghong Song <yhs@fb.com>
Date: Mon, 23 Oct 2017 23:53:06 -0700
> This patch set adds support to permit multiple bpf prog attachments
> for a single perf tracepoint event. Patch 1 does some cleanup such
> that perf_event_{set|free}_bpf_handler is called under the
> same condition. Patch 2 has the core implementation, and
> Patch 3 adds a test case.
>
> Changelogs:
> v2 -> v3:
> . fix compilation error.
> v1 -> v2:
> . fix a potential deadlock issue discovered by Daniel.
> . fix some coding style issues.
Series applied.
^ 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