Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v2 net-next 4/6] udp: flow dissector offload
From: Paolo Abeni @ 2017-08-30 10:36 UTC (permalink / raw)
  To: Tom Herbert, davem; +Cc: netdev
In-Reply-To: <20170829232711.1465-5-tom@quantonium.net>

On Tue, 2017-08-29 at 16:27 -0700, Tom Herbert wrote:
> Add support to perform UDP specific flow dissection. This is
> primarily intended for dissecting encapsulated packets in UDP
> encapsulation.
> 
> This patch adds a flow_dissect offload for UDP4 and UDP6. The backend
> function performs a socket lookup and calls the flow_dissect function
> if a socket is found.
> 
> Signed-off-by: Tom Herbert <tom@quantonium.net>
> ---
>  include/linux/udp.h      |  8 ++++++++
>  include/net/udp.h        |  8 ++++++++
>  include/net/udp_tunnel.h |  8 ++++++++
>  net/ipv4/udp_offload.c   | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  net/ipv4/udp_tunnel.c    |  1 +
>  net/ipv6/udp_offload.c   | 13 +++++++++++++
>  6 files changed, 83 insertions(+)
> 
> diff --git a/include/linux/udp.h b/include/linux/udp.h
> index eaea63bc79bb..2e90b189ef6a 100644
> --- a/include/linux/udp.h
> +++ b/include/linux/udp.h
> @@ -79,6 +79,14 @@ struct udp_sock {
>  	int			(*gro_complete)(struct sock *sk,
>  						struct sk_buff *skb,
>  						int nhoff);
> +	/* Flow dissector function for a UDP socket */
> +	enum flow_dissect_ret (*flow_dissect)(struct sock *sk,
> +			const struct sk_buff *skb,
> +			struct flow_dissector_key_control *key_control,
> +			struct flow_dissector *flow_dissector,
> +			void *target_container, void *data,
> +			__be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
> +			int *p_hlen, unsigned int flags);
>  
>  	/* udp_recvmsg try to use this before splicing sk_receive_queue */
>  	struct sk_buff_head	reader_queue ____cacheline_aligned_in_smp;
> diff --git a/include/net/udp.h b/include/net/udp.h
> index f3d1de6f0983..499e4faf8b14 100644
> --- a/include/net/udp.h
> +++ b/include/net/udp.h
> @@ -174,6 +174,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
>  				 struct udphdr *uh, udp_lookup_t lookup);
>  int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
>  
> +enum flow_dissect_ret udp_flow_dissect(const struct sk_buff *skb,
> +			udp_lookup_t lookup,
> +			struct flow_dissector_key_control *key_control,
> +			struct flow_dissector *flow_dissector,
> +			void *target_container, void *data,
> +			__be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
> +			int *p_hlen, unsigned int flags);
> +
>  static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
>  {
>  	struct udphdr *uh;
> diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
> index 10cce0dd4450..b7102e0f41a9 100644
> --- a/include/net/udp_tunnel.h
> +++ b/include/net/udp_tunnel.h
> @@ -69,6 +69,13 @@ typedef struct sk_buff **(*udp_tunnel_gro_receive_t)(struct sock *sk,
>  						     struct sk_buff *skb);
>  typedef int (*udp_tunnel_gro_complete_t)(struct sock *sk, struct sk_buff *skb,
>  					 int nhoff);
> +typedef enum flow_dissect_ret (*udp_tunnel_flow_dissect_t)(struct sock *sk,
> +			const struct sk_buff *skb,
> +			struct flow_dissector_key_control *key_control,
> +			struct flow_dissector *flow_dissector,
> +			void *target_container, void *data,
> +			__be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
> +			int *p_hlen, unsigned int flags);
>  
>  struct udp_tunnel_sock_cfg {
>  	void *sk_user_data;     /* user data used by encap_rcv call back */
> @@ -78,6 +85,7 @@ struct udp_tunnel_sock_cfg {
>  	udp_tunnel_encap_destroy_t encap_destroy;
>  	udp_tunnel_gro_receive_t gro_receive;
>  	udp_tunnel_gro_complete_t gro_complete;
> +	udp_tunnel_flow_dissect_t flow_dissect;
>  };
>  
>  /* Setup the given (UDP) sock to receive UDP encapsulated packets */
> diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
> index 97658bfc1b58..7f0a7ed4a6f7 100644
> --- a/net/ipv4/udp_offload.c
> +++ b/net/ipv4/udp_offload.c
> @@ -328,11 +328,56 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
>  	return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
>  }
>  
> +enum flow_dissect_ret udp_flow_dissect(const struct sk_buff *skb,
> +			udp_lookup_t lookup,
> +			struct flow_dissector_key_control *key_control,
> +			struct flow_dissector *flow_dissector,
> +			void *target_container, void *data,
> +			__be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
> +			int *p_hlen, unsigned int flags)
> +{
> +	enum flow_dissect_ret ret = FLOW_DISSECT_RET_CONTINUE;
> +	struct udphdr *uh, _uh;
> +	struct sock *sk;
> +
> +	uh = __skb_header_pointer(skb, *p_nhoff, sizeof(_uh), data,
> +				  *p_hlen, &_uh);
> +	if (!uh)
> +		return FLOW_DISSECT_RET_OUT_BAD;
> +
> +	rcu_read_lock();
> +
> +	sk = (*lookup)(skb, uh->source, uh->dest);
> +
> +	if (sk && udp_sk(sk)->flow_dissect)
> +		ret = udp_sk(sk)->flow_dissect(sk, skb, key_control,
> +					       flow_dissector, target_container,
> +					       data, p_proto, p_ip_proto,
> +					       p_nhoff, p_hlen, flags);
> +	rcu_read_unlock();
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(udp_flow_dissect);

If I read the above correctly, this is going to add another full UDP
lookup per UDP packet, can we avoid it with some static key enabled by
vxlan/fou/etc. ?

Thanks,

Paolo

^ permalink raw reply

* Re: [patch net-next 2/3] net/sched: Change cls_flower to use IDR
From: Simon Horman @ 2017-08-30 10:30 UTC (permalink / raw)
  To: Chris Mi
  Cc: netdev@vger.kernel.org, jhs@mojatatu.com,
	xiyou.wangcong@gmail.com, jiri@resnulli.us, davem@davemloft.net,
	mawilcox@microsoft.com
In-Reply-To: <VI1PR0501MB2143DEF749AEB68635BEEC0CAB9F0@VI1PR0501MB2143.eurprd05.prod.outlook.com>

On Tue, Aug 29, 2017 at 03:25:35AM +0000, Chris Mi wrote:
> 
> 
> > -----Original Message-----
> > From: Simon Horman [mailto:simon.horman@netronome.com]
> > Sent: Monday, August 28, 2017 7:37 PM
> > To: Chris Mi <chrism@mellanox.com>
> > Cc: netdev@vger.kernel.org; jhs@mojatatu.com;
> > xiyou.wangcong@gmail.com; jiri@resnulli.us; davem@davemloft.net;
> > mawilcox@microsoft.com
> > Subject: Re: [patch net-next 2/3] net/sched: Change cls_flower to use IDR
> > 
> > On Mon, Aug 28, 2017 at 02:41:16AM -0400, Chris Mi wrote:
> > > Currently, all filters with the same priority are linked in a doubly
> > > linked list. Every filter should have a unique handle. To make the
> > > handle unique, we need to iterate the list every time to see if the
> > > handle exists or not when inserting a new filter. It is time-consuming.
> > > For example, it takes about 5m3.169s to insert 64K rules.
> > >
> > > This patch changes cls_flower to use IDR. With this patch, it takes
> > > about 0m1.127s to insert 64K rules. The improvement is huge.
> > 
> > Very nice :)
> > 
> > > But please note that in this testing, all filters share the same action.
> > > If every filter has a unique action, that is another bottleneck.
> > > Follow-up patch in this patchset addresses that.
> > >
> > > Signed-off-by: Chris Mi <chrism@mellanox.com>
> > > Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> > > ---
> > >  net/sched/cls_flower.c | 55
> > > +++++++++++++++++++++-----------------------------
> > >  1 file changed, 23 insertions(+), 32 deletions(-)
> > >
> > > diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index
> > > bd9dab4..3d041d2 100644
> > > --- a/net/sched/cls_flower.c
> > > +++ b/net/sched/cls_flower.c
> > 
> > ...
> > 
> > > @@ -890,6 +870,7 @@ static int fl_change(struct net *net, struct sk_buff
> > *in_skb,
> > >  	struct cls_fl_filter *fnew;
> > >  	struct nlattr **tb;
> > >  	struct fl_flow_mask mask = {};
> > > +	unsigned long idr_index;
> > >  	int err;
> > >
> > >  	if (!tca[TCA_OPTIONS])
> > > @@ -920,13 +901,21 @@ static int fl_change(struct net *net, struct sk_buff
> > *in_skb,
> > >  		goto errout;
> > >
> > >  	if (!handle) {
> > > -		handle = fl_grab_new_handle(tp, head);
> > > -		if (!handle) {
> > > -			err = -EINVAL;
> > > +		err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
> > > +				    1, 0x80000000, GFP_KERNEL);
> > > +		if (err)
> > >  			goto errout;
> > > -		}
> > > +		fnew->handle = idr_index;
> > > +	}
> > > +
> > > +	/* user specifies a handle and it doesn't exist */
> > > +	if (handle && !fold) {
> > > +		err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
> > > +				    handle, handle + 1, GFP_KERNEL);
> > > +		if (err)
> > > +			goto errout;
> > > +		fnew->handle = idr_index;
> > >  	}
> > > -	fnew->handle = handle;
> > >
> > >  	if (tb[TCA_FLOWER_FLAGS]) {
> > >  		fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
> > > @@ -980,6 +969,8 @@ static int fl_change(struct net *net, struct sk_buff
> > *in_skb,
> > >  	*arg = fnew;
> > >
> > >  	if (fold) {
> > > +		fnew->handle = handle;
> > 
> > Can it be the case that fold is non-NULL and handle is zero?
> > The handling of that case seem to have changed in this patch.
> I don't think that could happen.  In function tc_ctl_tfilter(),
> 
> fl_get() will be called.  If handle is zero, fl_get() will return NULL.
> That means fold is NULL.

Thanks for the explanation, I see that now.

> > > +		idr_replace_ext(&head->handle_idr, fnew, fnew->handle);
> > >  		list_replace_rcu(&fold->list, &fnew->list);
> > >  		tcf_unbind_filter(tp, &fold->res);
> > >  		call_rcu(&fold->rcu, fl_destroy_filter);

^ permalink raw reply

* [net-next PATCHv6 2/2] net: socionext: Add NetSec driver
From: Jassi Brar @ 2017-08-30 10:26 UTC (permalink / raw)
  To: netdev, devicetree, linux-arm-kernel, davem
  Cc: patches, arnd, mark.rutland, robh+dt, andy, Jassi Brar
In-Reply-To: <1504088657-6102-1-git-send-email-jaswinder.singh@linaro.org>

This driver adds support for Socionext "netsec" IP Gigabit
Ethernet + PHY IP used in a variety of their ARM-based ASICs.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
 drivers/net/ethernet/Kconfig                       |   1 +
 drivers/net/ethernet/Makefile                      |   1 +
 drivers/net/ethernet/socionext/Kconfig             |  29 +
 drivers/net/ethernet/socionext/Makefile            |   1 +
 drivers/net/ethernet/socionext/netsec/Makefile     |   6 +
 drivers/net/ethernet/socionext/netsec/netsec.h     | 386 +++++++++++++
 .../socionext/netsec/netsec_desc_ring_access.c     | 618 +++++++++++++++++++++
 .../net/ethernet/socionext/netsec/netsec_ethtool.c |  76 +++
 .../ethernet/socionext/netsec/netsec_gmac_access.c | 329 +++++++++++
 .../net/ethernet/socionext/netsec/netsec_netdev.c  | 558 +++++++++++++++++++
 .../ethernet/socionext/netsec/netsec_platform.c    | 330 +++++++++++
 11 files changed, 2335 insertions(+)
 create mode 100644 drivers/net/ethernet/socionext/Kconfig
 create mode 100644 drivers/net/ethernet/socionext/Makefile
 create mode 100644 drivers/net/ethernet/socionext/netsec/Makefile
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec.h
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_ethtool.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_netdev.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_platform.c

diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index edae15ac..ef23120 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -169,6 +169,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 bf7f450..b2746b1 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -80,6 +80,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_SNI) += 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..e2bcf90
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Kconfig
@@ -0,0 +1,29 @@
+#
+# Socionext Network device configuration
+#
+
+config NET_VENDOR_SNI
+	bool "Socionext devices"
+	default y
+	---help---
+	  If you have a network (Ethernet) card belonging to this class, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  the questions about Socionext cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if NET_VENDOR_SNI
+
+config SNI_NETSEC
+	tristate "NETSEC Driver Support"
+	depends on OF
+	select PHYLIB
+	select MII
+help
+	  Enable for NETSEC support of Socionext FGAMC4 IP
+	  Provides Gigabit ethernet support
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called netsec.  If unsure, say N.
+
+endif # NET_VENDOR_SNI
diff --git a/drivers/net/ethernet/socionext/Makefile b/drivers/net/ethernet/socionext/Makefile
new file mode 100644
index 0000000..9555899
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SNI_NETSEC) += netsec/
diff --git a/drivers/net/ethernet/socionext/netsec/Makefile b/drivers/net/ethernet/socionext/netsec/Makefile
new file mode 100644
index 0000000..42f6bab
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/Makefile
@@ -0,0 +1,6 @@
+obj-m := netsec.o
+netsec-objs := netsec_desc_ring_access.o \
+		netsec_netdev.o \
+		netsec_ethtool.o \
+		netsec_platform.o \
+		netsec_gmac_access.o
diff --git a/drivers/net/ethernet/socionext/netsec/netsec.h b/drivers/net/ethernet/socionext/netsec/netsec.h
new file mode 100644
index 0000000..dadf5d9
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec.h
@@ -0,0 +1,386 @@
+/**
+ * netsec.h
+ *
+ *  Copyright (C) 2011 - 2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#ifndef NETSEC_INTERNAL_H
+#define NETSEC_INTERNAL_H
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#define NETSEC_FLOW_CONTROL_START_THRESHOLD	36
+#define NETSEC_FLOW_CONTROL_STOP_THRESHOLD	48
+
+#define NETSEC_CLK_MHZ				1000000
+
+#define NETSEC_RX_PKT_BUF_LEN			1522
+#define NETSEC_RX_JUMBO_PKT_BUF_LEN		9022
+
+#define NETSEC_NETDEV_TX_PKT_SCAT_NUM_MAX		19
+
+#define DESC_NUM 128
+
+#define NETSEC_TX_SHIFT_OWN_FIELD			31
+#define NETSEC_TX_SHIFT_LD_FIELD			30
+#define NETSEC_TX_SHIFT_DRID_FIELD		24
+#define NETSEC_TX_SHIFT_PT_FIELD			21
+#define NETSEC_TX_SHIFT_TDRID_FIELD		16
+#define NETSEC_TX_SHIFT_CC_FIELD			15
+#define NETSEC_TX_SHIFT_FS_FIELD			9
+#define NETSEC_TX_LAST				8
+#define NETSEC_TX_SHIFT_CO			7
+#define NETSEC_TX_SHIFT_SO			6
+#define NETSEC_TX_SHIFT_TRS_FIELD		4
+
+#define NETSEC_RX_PKT_OWN_FIELD			31
+#define NETSEC_RX_PKT_LD_FIELD			30
+#define NETSEC_RX_PKT_SDRID_FIELD			24
+#define NETSEC_RX_PKT_FR_FIELD			23
+#define NETSEC_RX_PKT_ER_FIELD			21
+#define NETSEC_RX_PKT_ERR_FIELD			16
+#define NETSEC_RX_PKT_TDRID_FIELD			12
+#define NETSEC_RX_PKT_FS_FIELD			9
+#define NETSEC_RX_PKT_LS_FIELD			8
+#define NETSEC_RX_PKT_CO_FIELD			6
+
+#define NETSEC_RX_PKT_ERR_MASK			3
+
+#define NETSEC_MAX_TX_PKT_LEN			1518
+#define NETSEC_MAX_TX_JUMBO_PKT_LEN		9018
+
+enum netsec_rings {
+	NETSEC_RING_TX,
+	NETSEC_RING_RX
+};
+
+#define NETSEC_RING_GMAC				15
+#define NETSEC_RING_MAX				1
+
+#define NETSEC_TCP_SEG_LEN_MAX			1460
+#define NETSEC_TCP_JUMBO_SEG_LEN_MAX		8960
+
+#define NETSEC_RX_CKSUM_NOTAVAIL			0
+#define NETSEC_RX_CKSUM_OK			1
+#define NETSEC_RX_CKSUM_NG			2
+
+#define NETSEC_TOP_IRQ_REG_CODE_LOAD_END		BIT(20)
+#define NETSEC_IRQ_TRANSITION_COMPLETE		BIT(4)
+#define NETSEC_IRQ_RX				BIT(1)
+#define NETSEC_IRQ_TX				BIT(0)
+
+#define NETSEC_IRQ_EMPTY				BIT(17)
+#define NETSEC_IRQ_ERR				BIT(16)
+#define NETSEC_IRQ_PKT_CNT			BIT(15)
+#define NETSEC_IRQ_TIMEUP				BIT(14)
+#define NETSEC_IRQ_RCV			(NETSEC_IRQ_PKT_CNT | NETSEC_IRQ_TIMEUP)
+
+#define NETSEC_IRQ_TX_DONE			BIT(15)
+#define NETSEC_IRQ_SND			(NETSEC_IRQ_TX_DONE | NETSEC_IRQ_TIMEUP)
+
+#define NETSEC_MODE_TRANS_COMP_IRQ_N2T		BIT(20)
+#define NETSEC_MODE_TRANS_COMP_IRQ_T2N		BIT(19)
+
+#define NETSEC_DESC_MIN				2
+#define NETSEC_DESC_MAX				2047
+#define NETSEC_INT_PKTCNT_MAX			2047
+
+#define NETSEC_FLOW_START_TH_MAX			95
+#define NETSEC_FLOW_STOP_TH_MAX			95
+#define NETSEC_FLOW_PAUSE_TIME_MIN		5
+
+#define NETSEC_CLK_EN_REG_DOM_ALL			0x3f
+
+#define NETSEC_REG_TOP_STATUS			0x80
+#define NETSEC_REG_TOP_INTEN			0x81
+#define NETSEC_REG_INTEN_SET			0x8d
+#define NETSEC_REG_INTEN_CLR			0x8e
+#define NETSEC_REG_NRM_TX_STATUS			0x100
+#define NETSEC_REG_NRM_TX_INTEN			0x101
+#define NETSEC_REG_NRM_TX_INTEN_SET		0x10a
+#define NETSEC_REG_NRM_TX_INTEN_CLR		0x10b
+#define NETSEC_REG_NRM_RX_STATUS			0x110
+#define NETSEC_REG_NRM_RX_INTEN			0x111
+#define NETSEC_REG_NRM_RX_INTEN_SET		0x11a
+#define NETSEC_REG_NRM_RX_INTEN_CLR		0x11b
+#define NETSEC_REG_RESERVED_RX_DESC_START		0x122
+#define NETSEC_REG_RESERVED_TX_DESC_START		0x132
+#define NETSEC_REG_CLK_EN				0x40
+#define NETSEC_REG_SOFT_RST			0x41
+#define NETSEC_REG_PKT_CTRL			0x50
+#define NETSEC_REG_COM_INIT			0x48
+#define NETSEC_REG_DMA_TMR_CTRL			0x83
+#define NETSEC_REG_F_TAIKI_MC_VER			0x8b
+#define NETSEC_REG_F_TAIKI_VER			0x8c
+#define NETSEC_REG_DMA_HM_CTRL			0x85
+#define NETSEC_REG_DMA_MH_CTRL			0x88
+#define NETSEC_REG_NRM_TX_PKTCNT			0x104
+#define NETSEC_REG_NRM_TX_DONE_TXINT_PKTCNT	0x106
+#define NETSEC_REG_NRM_RX_RXINT_PKTCNT		0x116
+#define NETSEC_REG_NRM_TX_TXINT_TMR		0x108
+#define NETSEC_REG_NRM_RX_RXINT_TMR		0x118
+#define NETSEC_REG_NRM_TX_DONE_PKTCNT		0x105
+#define NETSEC_REG_NRM_RX_PKTCNT			0x115
+#define NETSEC_REG_NRM_TX_TMR			0x107
+#define NETSEC_REG_NRM_RX_TMR			0x117
+#define NETSEC_REG_NRM_TX_DESC_START_UP		0x10d
+#define NETSEC_REG_NRM_TX_DESC_START_LW		0x102
+#define NETSEC_REG_NRM_RX_DESC_START_UP		0x11d
+#define NETSEC_REG_NRM_RX_DESC_START_LW		0x112
+#define NETSEC_REG_NRM_TX_CONFIG			0x10c
+#define NETSEC_REG_NRM_RX_CONFIG			0x11c
+#define MAC_REG_DATA				0x470
+#define MAC_REG_CMD				0x471
+#define MAC_REG_FLOW_TH				0x473
+#define MAC_REG_INTF_SEL			0x475
+#define MAC_REG_DESC_INIT			0x47f
+#define MAC_REG_DESC_SOFT_RST			0x481
+#define NETSEC_REG_MODE_TRANS_COMP_STATUS		0x140
+#define GMAC_REG_MCR				0x0000
+#define GMAC_REG_MFFR				0x0004
+#define GMAC_REG_GAR				0x0010
+#define GMAC_REG_GDR				0x0014
+#define GMAC_REG_FCR				0x0018
+#define GMAC_REG_BMR				0x1000
+#define GMAC_REG_RDLAR				0x100c
+#define GMAC_REG_TDLAR				0x1010
+#define GMAC_REG_OMR				0x1018
+
+#define NETSEC_PKT_CTRL_REG_MODE_NRM		BIT(28)
+#define NETSEC_PKT_CTRL_REG_EN_JUMBO		BIT(27)
+#define NETSEC_PKT_CTRL_REG_LOG_CHKSUM_ER		BIT(3)
+#define NETSEC_PKT_CTRL_REG_LOG_HD_INCOMPLETE	BIT(2)
+#define NETSEC_PKT_CTRL_REG_LOG_HD_ER		BIT(1)
+#define NETSEC_PKT_CTRL_REG_DRP_NO_MATCH		BIT(0)
+
+#define NETSEC_CLK_EN_REG_DOM_G			BIT(5)
+#define NETSEC_CLK_EN_REG_DOM_C			BIT(1)
+#define NETSEC_CLK_EN_REG_DOM_D			BIT(0)
+
+#define NETSEC_COM_INIT_REG_PKT			BIT(1)
+#define NETSEC_COM_INIT_REG_CORE			BIT(0)
+
+#define NETSEC_SOFT_RST_REG_RESET			0
+#define NETSEC_SOFT_RST_REG_RUN			BIT(31)
+
+#define NETSEC_DMA_CTRL_REG_STOP			1
+#define MH_CTRL__MODE_TRANS			BIT(20)
+
+#define NETSEC_GMAC_CMD_ST_READ			0
+#define NETSEC_GMAC_CMD_ST_WRITE			BIT(28)
+#define NETSEC_GMAC_CMD_ST_BUSY			BIT(31)
+
+#define NETSEC_GMAC_BMR_REG_COMMON		0x00412080
+#define NETSEC_GMAC_BMR_REG_RESET			0x00020181
+#define NETSEC_GMAC_BMR_REG_SWR			0x00000001
+
+#define NETSEC_GMAC_OMR_REG_ST			BIT(13)
+#define NETSEC_GMAC_OMR_REG_SR			BIT(1)
+
+#define NETSEC_GMAC_MCR_REG_IBN			BIT(30)
+#define NETSEC_GMAC_MCR_REG_CST			BIT(25)
+#define NETSEC_GMAC_MCR_REG_JE			BIT(20)
+#define NETSEC_MCR_PS				BIT(15)
+#define NETSEC_GMAC_MCR_REG_FES			BIT(14)
+#define NETSEC_GMAC_MCR_REG_FULL_DUPLEX_COMMON	0x0000280c
+#define NETSEC_GMAC_MCR_REG_HALF_DUPLEX_COMMON	0x0001a00c
+
+#define NETSEC_FCR_RFE				BIT(2)
+#define NETSEC_FCR_TFE				BIT(1)
+
+#define NETSEC_GMAC_GAR_REG_GW			BIT(1)
+#define NETSEC_GMAC_GAR_REG_GB			BIT(0)
+
+#define NETSEC_GMAC_GAR_REG_SHIFT_PA		11
+#define NETSEC_GMAC_GAR_REG_SHIFT_GR		6
+#define GMAC_REG_SHIFT_CR_GAR			2
+
+#define NETSEC_GMAC_GAR_REG_CR_25_35_MHZ		2
+#define NETSEC_GMAC_GAR_REG_CR_35_60_MHZ		3
+#define NETSEC_GMAC_GAR_REG_CR_60_100_MHZ		0
+#define NETSEC_GMAC_GAR_REG_CR_100_150_MHZ	1
+#define NETSEC_GMAC_GAR_REG_CR_150_250_MHZ	4
+#define NETSEC_GMAC_GAR_REG_CR_250_300_MHZ	5
+
+#define NETSEC_REG_NETSEC_VER_F_TAIKI		0x50000
+
+#define NETSEC_REG_DESC_RING_CONFIG_CFG_UP	BIT(31)
+#define NETSEC_REG_DESC_RING_CONFIG_CH_RST	BIT(30)
+#define NETSEC_REG_DESC_TMR_MODE		4
+#define NETSEC_REG_DESC_ENDIAN			0
+
+#define NETSEC_MAC_DESC_SOFT_RST_SOFT_RST		1
+#define NETSEC_MAC_DESC_INIT_REG_INIT		1
+
+/* this is used to interpret a register layout */
+struct netsec_pkt_ctrlaram {
+	u8 log_chksum_er_flag:1;
+	u8 log_hd_imcomplete_flag:1;
+	u8 log_hd_er_flag:1;
+};
+
+struct netsec_param {
+	struct netsec_pkt_ctrlaram pkt_ctrlaram;
+	bool use_jumbo_pkt_flag;
+};
+
+struct netsec_mac_mode {
+	u16 flow_start_th;
+	u16 flow_stop_th;
+	u16 pause_time;
+	bool flow_ctrl_enable_flag;
+};
+
+struct netsec_desc_ring {
+	spinlock_t spinlock_desc; /* protect descriptor access */
+	phys_addr_t desc_phys;
+	struct netsec_frag_info *frag;
+	struct sk_buff **priv;
+	void *ring_vaddr;
+	enum netsec_rings id;
+	int len;
+	u16 tx_done_num;
+	u16 rx_num;
+	u16 head;
+	u16 tail;
+	bool running;
+	bool full;
+};
+
+struct netsec_frag_info {
+	dma_addr_t dma_addr;
+	void *addr;
+	u16 len;
+};
+
+struct netsec_priv {
+	struct netsec_desc_ring desc_ring[NETSEC_RING_MAX + 1];
+	struct ethtool_coalesce et_coalesce;
+	struct netsec_mac_mode mac_mode;
+	struct netsec_param param;
+	struct napi_struct napi;
+	phys_addr_t rdlar_pa, tdlar_pa;
+	phy_interface_t phy_interface;
+	spinlock_t tx_queue_lock; /* protect transmit queue */
+	struct netsec_frag_info tx_info[MAX_SKB_FRAGS];
+	struct net_device *ndev;
+	struct device_node *phy_np;
+	struct mii_bus *mii_bus;
+	void __iomem *ioaddr;
+	struct device *dev;
+	struct clk *clk[3];
+	phys_addr_t scb_set_normal_tx_paddr;
+	u32 scb_pkt_ctrl_reg;
+	u32 rx_pkt_buf_len;
+	u32 msg_enable;
+	u32 freq;
+	int actual_link_speed;
+	int clock_count;
+	bool rx_cksum_offload_flag;
+	bool actual_duplex;
+	bool irq_registered;
+};
+
+struct netsec_tx_de {
+	u32 attr;
+	u32 data_buf_addr_up;
+	u32 data_buf_addr_lw;
+	u32 buf_len_info;
+};
+
+struct netsec_rx_de {
+	u32 attr;
+	u32 data_buf_addr_up;
+	u32 data_buf_addr_lw;
+	u32 buf_len_info;
+};
+
+struct netsec_tx_pkt_ctrl {
+	u16 tcp_seg_len;
+	bool tcp_seg_offload_flag;
+	bool cksum_offload_flag;
+};
+
+struct netsec_rx_pkt_info {
+	int rx_cksum_result;
+	int err_code;
+	bool is_fragmented;
+	bool err_flag;
+};
+
+struct netsec_skb_cb {
+	bool is_rx;
+};
+
+static inline void netsec_writel(struct netsec_priv *priv,
+				 u32 reg_addr, u32 val)
+{
+	writel_relaxed(val, priv->ioaddr + (reg_addr << 2));
+}
+
+static inline u32 netsec_readl(struct netsec_priv *priv, u32 reg_addr)
+{
+	return readl_relaxed(priv->ioaddr + (reg_addr << 2));
+}
+
+static inline void netsec_mark_skb_type(struct sk_buff *skb, bool is_rx)
+{
+	struct netsec_skb_cb *cb = (struct netsec_skb_cb *)skb->cb;
+
+	cb->is_rx = is_rx;
+}
+
+static inline bool skb_is_rx(struct sk_buff *skb)
+{
+	struct netsec_skb_cb *cb = (struct netsec_skb_cb *)skb->cb;
+
+	return cb->is_rx;
+}
+
+extern const struct net_device_ops netsec_netdev_ops;
+extern const struct ethtool_ops netsec_ethtool_ops;
+
+int netsec_start_gmac(struct netsec_priv *priv);
+int netsec_stop_gmac(struct netsec_priv *priv);
+int netsec_mii_register(struct netsec_priv *priv);
+void netsec_mii_unregister(struct netsec_priv *priv);
+int netsec_start_desc_ring(struct netsec_priv *priv, enum netsec_rings id);
+void netsec_stop_desc_ring(struct netsec_priv *priv, enum netsec_rings id);
+u16 netsec_get_rx_num(struct netsec_priv *priv);
+u16 netsec_get_tx_avail_num(struct netsec_priv *priv);
+int netsec_clean_tx_desc_ring(struct netsec_priv *priv);
+int netsec_clean_rx_desc_ring(struct netsec_priv *priv);
+int netsec_set_tx_pkt_data(struct netsec_priv *priv,
+			   const struct netsec_tx_pkt_ctrl *tx_ctrl,
+			   u8 count_frags, const struct netsec_frag_info *info,
+			   struct sk_buff *skb);
+int netsec_get_rx_pkt_data(struct netsec_priv *priv,
+			   struct netsec_rx_pkt_info *rxpi,
+			   struct netsec_frag_info *frag, u16 *len,
+			   struct sk_buff **skb);
+void netsec_ring_irq_enable(struct netsec_priv *priv,
+			    enum netsec_rings id, u32 i);
+void netsec_ring_irq_disable(struct netsec_priv *priv,
+			     enum netsec_rings id, u32 i);
+int netsec_alloc_desc_ring(struct netsec_priv *priv, enum netsec_rings id);
+void netsec_free_desc_ring(struct netsec_priv *priv,
+			   struct netsec_desc_ring *desc);
+int netsec_setup_rx_desc(struct netsec_priv *priv,
+			 struct netsec_desc_ring *desc);
+int netsec_netdev_napi_poll(struct napi_struct *napi_p, int budget);
+
+#endif /* NETSEC_INTERNAL_H */
diff --git a/drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c b/drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c
new file mode 100644
index 0000000..d8e23ca
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c
@@ -0,0 +1,618 @@
+/**
+ * drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+
+#include "netsec.h"
+
+static const u32 ads_irq_set[] = {
+	NETSEC_REG_NRM_TX_INTEN_SET,
+	NETSEC_REG_NRM_RX_INTEN_SET,
+};
+
+static const u32 desc_ring_irq_inten_clr_reg_addr[] = {
+	NETSEC_REG_NRM_TX_INTEN_CLR,
+	NETSEC_REG_NRM_RX_INTEN_CLR,
+};
+
+static const u32 int_tmr_reg_addr[] = {
+	NETSEC_REG_NRM_TX_TXINT_TMR,
+	NETSEC_REG_NRM_RX_RXINT_TMR,
+};
+
+static const u32 rx_pkt_cnt_reg_addr[] = {
+	0,
+	NETSEC_REG_NRM_RX_PKTCNT,
+};
+
+static const u32 tx_pkt_cnt_reg_addr[] = {
+	NETSEC_REG_NRM_TX_PKTCNT,
+	0,
+};
+
+static const u32 int_pkt_cnt_reg_addr[] = {
+	NETSEC_REG_NRM_TX_DONE_TXINT_PKTCNT,
+	NETSEC_REG_NRM_RX_RXINT_PKTCNT,
+};
+
+static const u32 tx_done_pkt_addr[] = {
+	NETSEC_REG_NRM_TX_DONE_PKTCNT,
+	0,
+};
+
+static const u32 netsec_desc_mask[] = {
+	[NETSEC_RING_TX] = NETSEC_GMAC_OMR_REG_ST,
+	[NETSEC_RING_RX] = NETSEC_GMAC_OMR_REG_SR
+};
+
+void netsec_ring_irq_enable(struct netsec_priv *priv,
+			    enum netsec_rings id, u32 irqf)
+{
+	netsec_writel(priv, ads_irq_set[id], irqf);
+}
+
+void netsec_ring_irq_disable(struct netsec_priv *priv,
+			     enum netsec_rings id, u32 irqf)
+{
+	netsec_writel(priv, desc_ring_irq_inten_clr_reg_addr[id], irqf);
+}
+
+static struct sk_buff *alloc_rx_pkt_buf(struct netsec_priv *priv,
+					struct netsec_frag_info *info)
+{
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb_ip_align(priv->ndev, info->len);
+	if (!skb)
+		return NULL;
+
+	netsec_mark_skb_type(skb, NETSEC_RING_RX);
+	info->addr = skb->data;
+	info->dma_addr = dma_map_single(priv->dev, info->addr, info->len,
+					DMA_FROM_DEVICE);
+	if (dma_mapping_error(priv->dev, info->dma_addr)) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+
+	return skb;
+}
+
+int netsec_alloc_desc_ring(struct netsec_priv *priv, enum netsec_rings id)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	desc->id = id;
+	desc->len = sizeof(struct netsec_tx_de); /* rx and tx desc same size */
+
+	spin_lock_init(&desc->spinlock_desc);
+
+	desc->ring_vaddr = dma_zalloc_coherent(priv->dev, desc->len * DESC_NUM,
+					       &desc->desc_phys, GFP_KERNEL);
+	if (!desc->ring_vaddr) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->frag = kcalloc(DESC_NUM, sizeof(*desc->frag), GFP_KERNEL);
+	if (!desc->frag) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->priv = kcalloc(DESC_NUM, sizeof(struct sk_buff *), GFP_KERNEL);
+	if (!desc->priv) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	return 0;
+
+err:
+	netsec_free_desc_ring(priv, desc);
+
+	return ret;
+}
+
+static void netsec_uninit_pkt_desc_ring(struct netsec_priv *priv,
+					struct netsec_desc_ring *desc)
+{
+	struct netsec_frag_info *frag;
+	u32 status;
+	u16 idx;
+
+	for (idx = 0; idx < DESC_NUM; idx++) {
+		frag = &desc->frag[idx];
+		if (!frag->addr)
+			continue;
+
+		status = *(u32 *)(desc->ring_vaddr + desc->len * idx);
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 skb_is_rx(desc->priv[idx]) ? DMA_FROM_DEVICE :
+							      DMA_TO_DEVICE);
+		if ((status >> NETSEC_TX_LAST) & 1)
+			dev_kfree_skb(desc->priv[idx]);
+	}
+
+	memset(desc->frag, 0, sizeof(struct netsec_frag_info) * DESC_NUM);
+	memset(desc->priv, 0, sizeof(struct sk_buff *) * DESC_NUM);
+	memset(desc->ring_vaddr, 0, desc->len * DESC_NUM);
+}
+
+void netsec_free_desc_ring(struct netsec_priv *priv,
+			   struct netsec_desc_ring *desc)
+{
+	if (desc->ring_vaddr && desc->frag && desc->priv)
+		netsec_uninit_pkt_desc_ring(priv, desc);
+
+	if (desc->ring_vaddr) {
+		dma_free_coherent(priv->dev, desc->len * DESC_NUM,
+				  desc->ring_vaddr, desc->desc_phys);
+		desc->ring_vaddr = NULL;
+	}
+	kfree(desc->frag);
+	desc->frag = NULL;
+	kfree(desc->priv);
+	desc->priv = NULL;
+}
+
+static void netsec_set_rx_de(struct netsec_priv *priv,
+			     struct netsec_desc_ring *desc, u16 idx,
+			     const struct netsec_frag_info *info,
+			     struct sk_buff *skb)
+{
+	struct netsec_rx_de *de = desc->ring_vaddr + desc->len * idx;
+	u32 attr = 1 << NETSEC_RX_PKT_OWN_FIELD | 1 << NETSEC_RX_PKT_FS_FIELD |
+			       1 << NETSEC_RX_PKT_LS_FIELD;
+
+	if (idx == DESC_NUM - 1)
+		attr |= 1 << NETSEC_RX_PKT_LD_FIELD;
+
+	de->data_buf_addr_up = info->dma_addr >> 32;
+	de->data_buf_addr_lw = info->dma_addr & 0xffffffff;
+	de->buf_len_info = info->len;
+	/* desc->attr makes the descriptor live, so it must be physically
+	 * written last after the rest of the descriptor body is already there
+	 */
+	wmb();
+	de->attr = attr;
+
+	desc->frag[idx].dma_addr = info->dma_addr;
+	desc->frag[idx].addr = info->addr;
+	desc->frag[idx].len = info->len;
+
+	desc->priv[idx] = skb;
+}
+
+int netsec_setup_rx_desc(struct netsec_priv *priv,
+			 struct netsec_desc_ring *desc)
+{
+	struct netsec_frag_info info;
+	struct sk_buff *skb;
+	int n;
+
+	info.len = priv->rx_pkt_buf_len;
+
+	for (n = 0; n < DESC_NUM; n++) {
+		skb = alloc_rx_pkt_buf(priv, &info);
+		if (!skb) {
+			netsec_uninit_pkt_desc_ring(priv, desc);
+			return -ENOMEM;
+		}
+		netsec_set_rx_de(priv, desc, n, &info, skb);
+	}
+
+	return 0;
+}
+
+static void netsec_set_tx_desc_entry(struct netsec_priv *priv,
+				     struct netsec_desc_ring *desc,
+				     const struct netsec_tx_pkt_ctrl *tx_ctrl,
+				     bool first_flag, bool last_flag,
+				     const struct netsec_frag_info *frag,
+				     struct sk_buff *skb)
+{
+	struct netsec_tx_de tx_desc_entry;
+	int idx = desc->head;
+
+	memset(&tx_desc_entry, 0, sizeof(struct netsec_tx_de));
+
+	tx_desc_entry.attr = (1 << NETSEC_TX_SHIFT_OWN_FIELD) |
+			     (desc->id << NETSEC_TX_SHIFT_DRID_FIELD) |
+			     (1 << NETSEC_TX_SHIFT_PT_FIELD) |
+			     (NETSEC_RING_GMAC << NETSEC_TX_SHIFT_TDRID_FIELD) |
+			     (first_flag << NETSEC_TX_SHIFT_FS_FIELD) |
+			     (last_flag << NETSEC_TX_LAST) |
+			     (tx_ctrl->cksum_offload_flag <<
+			      NETSEC_TX_SHIFT_CO) |
+			     (tx_ctrl->tcp_seg_offload_flag <<
+			      NETSEC_TX_SHIFT_SO) |
+			     (1 << NETSEC_TX_SHIFT_TRS_FIELD);
+	if (idx == DESC_NUM - 1)
+		tx_desc_entry.attr |= (1 << NETSEC_TX_SHIFT_LD_FIELD);
+
+	tx_desc_entry.data_buf_addr_up = frag->dma_addr >> 32;
+	tx_desc_entry.data_buf_addr_lw = frag->dma_addr & 0xffffffff;
+	tx_desc_entry.buf_len_info = (tx_ctrl->tcp_seg_len << 16) | frag->len;
+
+	memcpy(desc->ring_vaddr + (desc->len * idx), &tx_desc_entry, desc->len);
+
+	desc->frag[idx].dma_addr = frag->dma_addr;
+	desc->frag[idx].addr = frag->addr;
+	desc->frag[idx].len = frag->len;
+
+	desc->priv[idx] = skb;
+}
+
+static void netsec_get_rx_de(struct netsec_priv *priv,
+			     struct netsec_desc_ring *desc, u16 idx,
+			     struct netsec_rx_pkt_info *rxpi,
+			     struct netsec_frag_info *frag, u16 *len,
+			     struct sk_buff **skb)
+{
+	struct netsec_rx_de de;
+
+	memset(&de, 0, sizeof(struct netsec_rx_de));
+	memset(rxpi, 0, sizeof(struct netsec_rx_pkt_info));
+	memcpy(&de, ((void *)desc->ring_vaddr + desc->len * idx), desc->len);
+
+	dev_dbg(priv->dev, "%08x\n", *(u32 *)&de);
+	*len = de.buf_len_info >> 16;
+
+	rxpi->is_fragmented = (de.attr >> NETSEC_RX_PKT_FR_FIELD) & 1;
+	rxpi->err_flag = (de.attr >> NETSEC_RX_PKT_ER_FIELD) & 1;
+	rxpi->rx_cksum_result = (de.attr >> NETSEC_RX_PKT_CO_FIELD) & 3;
+	rxpi->err_code = (de.attr >> NETSEC_RX_PKT_ERR_FIELD) &
+							NETSEC_RX_PKT_ERR_MASK;
+	memcpy(frag, &desc->frag[idx], sizeof(*frag));
+	*skb = desc->priv[idx];
+}
+
+static void netsec_inc_desc_head_idx(struct netsec_priv *priv,
+				     struct netsec_desc_ring *desc, u16 inc)
+{
+	u32 sum;
+
+	sum = desc->head + inc;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->head = sum;
+	desc->full = desc->head == desc->tail;
+}
+
+static void netsec_inc_desc_tail_idx(struct netsec_priv *priv,
+				     struct netsec_desc_ring *desc)
+{
+	u32 sum;
+
+	sum = desc->tail + 1;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->tail = sum;
+	desc->full = false;
+}
+
+static u16 netsec_get_tx_avail_num_sub(struct netsec_priv *priv,
+				       const struct netsec_desc_ring *desc)
+{
+	if (desc->full)
+		return 0;
+
+	if (desc->tail > desc->head)
+		return desc->tail - desc->head;
+
+	return DESC_NUM + desc->tail - desc->head;
+}
+
+static u16 netsec_get_tx_done_num_sub(struct netsec_priv *priv,
+				      struct netsec_desc_ring *desc)
+{
+	desc->tx_done_num += netsec_readl(priv, tx_done_pkt_addr[desc->id]);
+
+	return desc->tx_done_num;
+}
+
+static int netsec_set_irq_coalesce_param(struct netsec_priv *priv,
+					 enum netsec_rings id)
+{
+	int max_frames, tmr;
+
+	switch (id) {
+	case NETSEC_RING_TX:
+		max_frames = priv->et_coalesce.tx_max_coalesced_frames;
+		tmr = priv->et_coalesce.tx_coalesce_usecs;
+		break;
+	case NETSEC_RING_RX:
+		max_frames = priv->et_coalesce.rx_max_coalesced_frames;
+		tmr = priv->et_coalesce.rx_coalesce_usecs;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	netsec_writel(priv, int_pkt_cnt_reg_addr[id], max_frames);
+	netsec_writel(priv, int_tmr_reg_addr[id], ((tmr != 0) << 31) | tmr);
+
+	return 0;
+}
+
+int netsec_start_desc_ring(struct netsec_priv *priv, enum netsec_rings id)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	spin_lock_bh(&desc->spinlock_desc);
+
+	if (desc->running) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	switch (desc->id) {
+	case NETSEC_RING_RX:
+		netsec_writel(priv, ads_irq_set[id], NETSEC_IRQ_RCV);
+		break;
+	case NETSEC_RING_TX:
+		netsec_writel(priv, ads_irq_set[id], NETSEC_IRQ_EMPTY);
+		break;
+	}
+
+	netsec_set_irq_coalesce_param(priv, desc->id);
+	desc->running = true;
+
+err:
+	spin_unlock_bh(&desc->spinlock_desc);
+
+	return ret;
+}
+
+void netsec_stop_desc_ring(struct netsec_priv *priv, enum netsec_rings id)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[id];
+
+	spin_lock_bh(&desc->spinlock_desc);
+	if (desc->running)
+		netsec_writel(priv, desc_ring_irq_inten_clr_reg_addr[id],
+			      NETSEC_IRQ_RCV | NETSEC_IRQ_EMPTY |
+			      NETSEC_IRQ_SND);
+
+	desc->running = false;
+	spin_unlock_bh(&desc->spinlock_desc);
+}
+
+u16 netsec_get_rx_num(struct netsec_priv *priv)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[NETSEC_RING_RX];
+	u32 result;
+
+	spin_lock(&desc->spinlock_desc);
+	if (desc->running) {
+		result = netsec_readl(priv,
+				      rx_pkt_cnt_reg_addr[NETSEC_RING_RX]);
+		desc->rx_num += result;
+		if (result)
+			netsec_inc_desc_head_idx(priv, desc, result);
+	}
+	spin_unlock(&desc->spinlock_desc);
+
+	return desc->rx_num;
+}
+
+u16 netsec_get_tx_avail_num(struct netsec_priv *priv)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[NETSEC_RING_TX];
+	u16 result;
+
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: not running tx desc\n", __func__);
+		result = 0;
+		goto err;
+	}
+
+	result = netsec_get_tx_avail_num_sub(priv, desc);
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return result;
+}
+
+int netsec_clean_tx_desc_ring(struct netsec_priv *priv)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[NETSEC_RING_TX];
+	unsigned int pkts = 0, bytes = 0;
+	struct netsec_frag_info *frag;
+	struct netsec_tx_de *entry;
+	bool is_last;
+
+	spin_lock(&desc->spinlock_desc);
+
+	netsec_get_tx_done_num_sub(priv, desc);
+
+	while ((desc->tail != desc->head || desc->full) && desc->tx_done_num) {
+		frag = &desc->frag[desc->tail];
+		entry = desc->ring_vaddr + desc->len * desc->tail;
+		is_last = (entry->attr >> NETSEC_TX_LAST) & 1;
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 DMA_TO_DEVICE);
+		if (is_last) {
+			pkts++;
+			bytes += desc->priv[desc->tail]->len;
+			dev_kfree_skb(desc->priv[desc->tail]);
+		}
+		memset(frag, 0, sizeof(*frag));
+		netsec_inc_desc_tail_idx(priv, desc);
+
+		if (is_last)
+			desc->tx_done_num--;
+	}
+
+	spin_unlock(&desc->spinlock_desc);
+
+	priv->ndev->stats.tx_packets += pkts;
+	priv->ndev->stats.tx_bytes += bytes;
+
+	netdev_completed_queue(priv->ndev, pkts, bytes);
+
+	return 0;
+}
+
+int netsec_clean_rx_desc_ring(struct netsec_priv *priv)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[NETSEC_RING_RX];
+
+	spin_lock(&desc->spinlock_desc);
+
+	while (desc->full || (desc->tail != desc->head)) {
+		netsec_set_rx_de(priv, desc, desc->tail,
+				 &desc->frag[desc->tail],
+				 desc->priv[desc->tail]);
+		desc->rx_num--;
+		netsec_inc_desc_tail_idx(priv, desc);
+	}
+
+	spin_unlock(&desc->spinlock_desc);
+
+	return 0;
+}
+
+int netsec_set_tx_pkt_data(struct netsec_priv *priv,
+			   const struct netsec_tx_pkt_ctrl *tx_ctrl,
+			   u8 count_frags, const struct netsec_frag_info *info,
+			   struct sk_buff *skb)
+{
+	struct netsec_desc_ring *desc;
+	u32 sum_len = 0;
+	unsigned int i;
+	int ret = 0;
+
+	if (tx_ctrl->tcp_seg_offload_flag && !tx_ctrl->cksum_offload_flag)
+		return -EINVAL;
+
+	if (tx_ctrl->tcp_seg_offload_flag) {
+		if (tx_ctrl->tcp_seg_len == 0)
+			return -EINVAL;
+
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (tx_ctrl->tcp_seg_len > NETSEC_TCP_JUMBO_SEG_LEN_MAX)
+				return -EINVAL;
+		} else {
+			if (tx_ctrl->tcp_seg_len > NETSEC_TCP_SEG_LEN_MAX)
+				return -EINVAL;
+		}
+	} else {
+		if (tx_ctrl->tcp_seg_len)
+			return -EINVAL;
+	}
+
+	if (!count_frags)
+		return -ERANGE;
+
+	for (i = 0; i < count_frags; i++) {
+		if ((info[i].len == 0) || (info[i].len > 0xffff)) {
+			netif_err(priv, drv, priv->ndev,
+				  "%s: bad info len\n", __func__);
+			return -EINVAL;
+		}
+		sum_len += info[i].len;
+	}
+
+	if (!tx_ctrl->tcp_seg_offload_flag) {
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (sum_len > NETSEC_MAX_TX_JUMBO_PKT_LEN)
+				return -EINVAL;
+		} else {
+			if (sum_len > NETSEC_MAX_TX_PKT_LEN)
+				return -EINVAL;
+		}
+	}
+
+	desc = &priv->desc_ring[NETSEC_RING_TX];
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		ret = -ENODEV;
+		goto end;
+	}
+
+	smp_rmb(); /* we need to see a consistent view of pending tx count */
+	if (count_frags > netsec_get_tx_avail_num_sub(priv, desc)) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	for (i = 0; i < count_frags; i++) {
+		netsec_set_tx_desc_entry(priv, desc, tx_ctrl, i == 0,
+					 i == count_frags - 1, &info[i], skb);
+		netsec_inc_desc_head_idx(priv, desc, 1);
+	}
+
+	wmb(); /* ensure the descriptor is flushed */
+	netsec_writel(priv, tx_pkt_cnt_reg_addr[NETSEC_RING_TX], 1);
+
+end:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
+
+int netsec_get_rx_pkt_data(struct netsec_priv *priv,
+			   struct netsec_rx_pkt_info *rxpi,
+			   struct netsec_frag_info *frag, u16 *len,
+			   struct sk_buff **skb)
+{
+	struct netsec_desc_ring *desc = &priv->desc_ring[NETSEC_RING_RX];
+	struct netsec_frag_info info;
+	struct sk_buff *tmp_skb;
+	int ret = 0;
+
+	spin_lock(&desc->spinlock_desc);
+
+	if (desc->rx_num == 0) {
+		dev_err(priv->dev, "%s 0 len rx\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	info.len = priv->rx_pkt_buf_len;
+	rmb(); /* we need to ensure we only see current data in descriptor */
+	tmp_skb = alloc_rx_pkt_buf(priv, &info);
+	if (!tmp_skb) {
+		netsec_set_rx_de(priv, desc, desc->tail,
+				 &desc->frag[desc->tail],
+				 desc->priv[desc->tail]);
+		ret = -ENOMEM;
+	} else {
+		netsec_get_rx_de(priv, desc, desc->tail, rxpi, frag, len, skb);
+		netsec_set_rx_de(priv, desc, desc->tail, &info, tmp_skb);
+	}
+
+	netsec_inc_desc_tail_idx(priv, desc);
+	desc->rx_num--;
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
diff --git a/drivers/net/ethernet/socionext/netsec/netsec_ethtool.c b/drivers/net/ethernet/socionext/netsec/netsec_ethtool.c
new file mode 100644
index 0000000..91f737d
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec_ethtool.c
@@ -0,0 +1,76 @@
+/**
+ * drivers/net/ethernet/socionext/netsec/netsec_ethtool.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include "netsec.h"
+
+static void netsec_et_get_drvinfo(struct net_device *net_device,
+				  struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, "netsec", sizeof(info->driver));
+	strlcpy(info->bus_info, dev_name(net_device->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static int netsec_et_get_coalesce(struct net_device *net_device,
+				  struct ethtool_coalesce *et_coalesce)
+{
+	struct netsec_priv *priv = netdev_priv(net_device);
+
+	*et_coalesce = priv->et_coalesce;
+
+	return 0;
+}
+
+static int netsec_et_set_coalesce(struct net_device *net_device,
+				  struct ethtool_coalesce *et_coalesce)
+{
+	struct netsec_priv *priv = netdev_priv(net_device);
+
+	if (et_coalesce->rx_max_coalesced_frames > NETSEC_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (et_coalesce->tx_max_coalesced_frames > NETSEC_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (!et_coalesce->rx_max_coalesced_frames)
+		return -EINVAL;
+	if (!et_coalesce->tx_max_coalesced_frames)
+		return -EINVAL;
+
+	priv->et_coalesce = *et_coalesce;
+
+	return 0;
+}
+
+static u32 netsec_et_get_msglevel(struct net_device *dev)
+{
+	struct netsec_priv *priv = netdev_priv(dev);
+
+	return priv->msg_enable;
+}
+
+static void netsec_et_set_msglevel(struct net_device *dev, u32 datum)
+{
+	struct netsec_priv *priv = netdev_priv(dev);
+
+	priv->msg_enable = datum;
+}
+
+const struct ethtool_ops netsec_ethtool_ops = {
+	.get_drvinfo		= netsec_et_get_drvinfo,
+	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
+	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
+	.get_link		= ethtool_op_get_link,
+	.get_coalesce		= netsec_et_get_coalesce,
+	.set_coalesce		= netsec_et_set_coalesce,
+	.get_msglevel		= netsec_et_get_msglevel,
+	.set_msglevel		= netsec_et_set_msglevel,
+};
diff --git a/drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c b/drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c
new file mode 100644
index 0000000..415c7b4
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c
@@ -0,0 +1,329 @@
+/**
+ * drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#include "netsec.h"
+
+#define TIMEOUT_SPINS_MAC 1000
+#define TIMEOUT_SECONDARY_MS_MAC 100
+
+static u32 netsec_clk_type(u32 freq)
+{
+	if (freq < 35 * NETSEC_CLK_MHZ)
+		return NETSEC_GMAC_GAR_REG_CR_25_35_MHZ;
+	if (freq < 60 * NETSEC_CLK_MHZ)
+		return NETSEC_GMAC_GAR_REG_CR_35_60_MHZ;
+	if (freq < 100 * NETSEC_CLK_MHZ)
+		return NETSEC_GMAC_GAR_REG_CR_60_100_MHZ;
+	if (freq < 150 * NETSEC_CLK_MHZ)
+		return NETSEC_GMAC_GAR_REG_CR_100_150_MHZ;
+	if (freq < 250 * NETSEC_CLK_MHZ)
+		return NETSEC_GMAC_GAR_REG_CR_150_250_MHZ;
+
+	return NETSEC_GMAC_GAR_REG_CR_250_300_MHZ;
+}
+
+static int netsec_wait_while_busy(struct netsec_priv *priv, u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+
+	while (--timeout && netsec_readl(priv, addr) & mask)
+		;
+	if (timeout)
+		return 0;
+
+	timeout = TIMEOUT_SECONDARY_MS_MAC;
+	while (--timeout && netsec_readl(priv, addr) & mask)
+		usleep_range(1000, 2000);
+
+	if (timeout)
+		return 0;
+
+	netdev_WARN(priv->ndev, "%s: timeout\n", __func__);
+
+	return -ETIMEDOUT;
+}
+
+static int netsec_mac_write(struct netsec_priv *priv, u32 addr, u32 value)
+{
+	netsec_writel(priv, MAC_REG_DATA, value);
+	netsec_writel(priv, MAC_REG_CMD, addr | NETSEC_GMAC_CMD_ST_WRITE);
+	return netsec_wait_while_busy(priv,
+				      MAC_REG_CMD, NETSEC_GMAC_CMD_ST_BUSY);
+}
+
+static int netsec_mac_read(struct netsec_priv *priv, u32 addr, u32 *read)
+{
+	int ret;
+
+	netsec_writel(priv, MAC_REG_CMD, addr | NETSEC_GMAC_CMD_ST_READ);
+	ret = netsec_wait_while_busy(priv,
+				     MAC_REG_CMD, NETSEC_GMAC_CMD_ST_BUSY);
+	if (ret)
+		return ret;
+
+	*read = netsec_readl(priv, MAC_REG_DATA);
+
+	return 0;
+}
+
+static int netsec_mac_wait_while_busy(struct netsec_priv *priv,
+				      u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+	int ret, data;
+
+	do {
+		ret = netsec_mac_read(priv, addr, &data);
+		if (ret)
+			break;
+	} while (--timeout && (data & mask));
+
+	if (timeout)
+		return 0;
+
+	timeout = TIMEOUT_SECONDARY_MS_MAC;
+	do {
+		usleep_range(1000, 2000);
+
+		ret = netsec_mac_read(priv, addr, &data);
+		if (ret)
+			break;
+	} while (--timeout && (data & mask));
+
+	if (timeout && !ret)
+		return 0;
+
+	netdev_WARN(priv->ndev, "%s: timeout\n", __func__);
+
+	return -ETIMEDOUT;
+}
+
+static int netsec_mac_update_to_phy_state(struct netsec_priv *priv)
+{
+	struct phy_device *phydev = priv->ndev->phydev;
+	u32 value = 0;
+
+	value = phydev->duplex ? NETSEC_GMAC_MCR_REG_FULL_DUPLEX_COMMON :
+				       NETSEC_GMAC_MCR_REG_HALF_DUPLEX_COMMON;
+
+	if (phydev->speed != SPEED_1000)
+		value |= NETSEC_MCR_PS;
+
+	if ((priv->phy_interface != PHY_INTERFACE_MODE_GMII) &&
+	    (phydev->speed == SPEED_100))
+		value |= NETSEC_GMAC_MCR_REG_FES;
+
+	value |= NETSEC_GMAC_MCR_REG_CST | NETSEC_GMAC_MCR_REG_JE;
+
+	if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII)
+		value |= NETSEC_GMAC_MCR_REG_IBN;
+
+	if (netsec_mac_write(priv, GMAC_REG_MCR, value))
+		return -ETIMEDOUT;
+
+	priv->actual_link_speed = phydev->speed;
+	priv->actual_duplex = phydev->duplex;
+	netif_info(priv, drv, priv->ndev, "%s: %uMbps, duplex:%d\n",
+		   __func__, phydev->speed, phydev->duplex);
+
+	return 0;
+}
+
+/* NB netsec_start_gmac() only called from adjust_link */
+
+int netsec_start_gmac(struct netsec_priv *priv)
+{
+	struct phy_device *phydev = priv->ndev->phydev;
+	u32 value = 0;
+	int ret;
+
+	if (priv->desc_ring[NETSEC_RING_TX].running &&
+	    priv->desc_ring[NETSEC_RING_RX].running)
+		return 0;
+
+	if (!priv->desc_ring[NETSEC_RING_RX].running &&
+	    !priv->desc_ring[NETSEC_RING_TX].running) {
+		if (phydev->speed != SPEED_1000)
+			value = (NETSEC_GMAC_MCR_REG_CST |
+				 NETSEC_GMAC_MCR_REG_HALF_DUPLEX_COMMON);
+
+		if (netsec_mac_write(priv, GMAC_REG_MCR, value))
+			return -ETIMEDOUT;
+		if (netsec_mac_write(priv, GMAC_REG_BMR,
+				     NETSEC_GMAC_BMR_REG_RESET))
+			return -ETIMEDOUT;
+
+		/* Wait soft reset */
+		usleep_range(1000, 5000);
+
+		ret = netsec_mac_read(priv, GMAC_REG_BMR, &value);
+		if (ret)
+			return ret;
+		if (value & NETSEC_GMAC_BMR_REG_SWR)
+			return -EAGAIN;
+
+		netsec_writel(priv, MAC_REG_DESC_SOFT_RST, 1);
+		if (netsec_wait_while_busy(priv, MAC_REG_DESC_SOFT_RST, 1))
+			return -ETIMEDOUT;
+
+		netsec_writel(priv, MAC_REG_DESC_INIT, 1);
+		if (netsec_wait_while_busy(priv, MAC_REG_DESC_INIT, 1))
+			return -ETIMEDOUT;
+
+		if (netsec_mac_write(priv, GMAC_REG_BMR,
+				     NETSEC_GMAC_BMR_REG_COMMON))
+			return -ETIMEDOUT;
+		if (netsec_mac_write(priv, GMAC_REG_RDLAR, priv->rdlar_pa))
+			return -ETIMEDOUT;
+		if (netsec_mac_write(priv, GMAC_REG_TDLAR, priv->tdlar_pa))
+			return -ETIMEDOUT;
+		if (netsec_mac_write(priv, GMAC_REG_MFFR, 0x80000001))
+			return -ETIMEDOUT;
+
+		ret = netsec_mac_update_to_phy_state(priv);
+		if (ret)
+			return ret;
+
+		if (priv->mac_mode.flow_ctrl_enable_flag) {
+			netsec_writel(priv, MAC_REG_FLOW_TH,
+				      (priv->mac_mode.flow_stop_th << 16) |
+				      priv->mac_mode.flow_start_th);
+			if (netsec_mac_write(priv, GMAC_REG_FCR,
+					     (priv->mac_mode.pause_time << 16) |
+					     NETSEC_FCR_RFE | NETSEC_FCR_TFE))
+				return -ETIMEDOUT;
+		}
+	}
+
+	ret = netsec_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (!priv->desc_ring[NETSEC_RING_RX].running) {
+		value |= NETSEC_GMAC_OMR_REG_SR;
+		netsec_start_desc_ring(priv, NETSEC_RING_RX);
+	}
+	if (!priv->desc_ring[NETSEC_RING_TX].running) {
+		value |= NETSEC_GMAC_OMR_REG_ST;
+		netsec_start_desc_ring(priv, NETSEC_RING_TX);
+	}
+
+	if (netsec_mac_write(priv, GMAC_REG_OMR, value))
+		return -ETIMEDOUT;
+
+	netsec_writel(priv, NETSEC_REG_INTEN_SET,
+		      NETSEC_IRQ_TX | NETSEC_IRQ_RX);
+
+	return 0;
+}
+
+int netsec_stop_gmac(struct netsec_priv *priv)
+{
+	u32 value;
+	int ret;
+
+	ret = netsec_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (priv->desc_ring[NETSEC_RING_RX].running) {
+		value &= ~NETSEC_GMAC_OMR_REG_SR;
+		netsec_stop_desc_ring(priv, NETSEC_RING_RX);
+	}
+	if (priv->desc_ring[NETSEC_RING_TX].running) {
+		value &= ~NETSEC_GMAC_OMR_REG_ST;
+		netsec_stop_desc_ring(priv, NETSEC_RING_TX);
+	}
+
+	priv->actual_link_speed = 0;
+	priv->actual_duplex = false;
+
+	return netsec_mac_write(priv, GMAC_REG_OMR, value);
+}
+
+static int netsec_phy_write(struct mii_bus *bus,
+			    int phy_addr, int reg, u16 val)
+{
+	struct netsec_priv *priv = bus->priv;
+
+	if (netsec_mac_write(priv, GMAC_REG_GDR, val))
+		return -ETIMEDOUT;
+	if (netsec_mac_write(priv, GMAC_REG_GAR,
+			     phy_addr << NETSEC_GMAC_GAR_REG_SHIFT_PA |
+			     reg << NETSEC_GMAC_GAR_REG_SHIFT_GR |
+			     NETSEC_GMAC_GAR_REG_GW | NETSEC_GMAC_GAR_REG_GB) |
+			     (netsec_clk_type(priv->freq) <<
+			      GMAC_REG_SHIFT_CR_GAR))
+		return -ETIMEDOUT;
+
+	return netsec_mac_wait_while_busy(priv, GMAC_REG_GAR,
+					  NETSEC_GMAC_GAR_REG_GB);
+}
+
+static int netsec_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr)
+{
+	struct netsec_priv *priv = bus->priv;
+	u32 data;
+	int ret;
+
+	if (netsec_mac_write(priv, GMAC_REG_GAR, NETSEC_GMAC_GAR_REG_GB |
+			     phy_addr << NETSEC_GMAC_GAR_REG_SHIFT_PA |
+			     reg_addr << NETSEC_GMAC_GAR_REG_SHIFT_GR |
+			     (netsec_clk_type(priv->freq) <<
+			      GMAC_REG_SHIFT_CR_GAR)))
+		return -ETIMEDOUT;
+
+	ret = netsec_mac_wait_while_busy(priv, GMAC_REG_GAR,
+					 NETSEC_GMAC_GAR_REG_GB);
+	if (ret)
+		return 0;
+
+	ret = netsec_mac_read(priv, GMAC_REG_GDR, &data);
+	if (ret)
+		return ret;
+
+	return data;
+}
+
+int netsec_mii_register(struct netsec_priv *priv)
+{
+	struct mii_bus *bus = mdiobus_alloc();
+	struct resource res;
+	int ret;
+
+	if (!bus)
+		return -ENOMEM;
+
+	of_address_to_resource(priv->dev->of_node, 0, &res);
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", priv->dev->of_node->full_name);
+	bus->priv = priv;
+	bus->name = "SNI NETSEC MDIO";
+	bus->read = netsec_phy_read;
+	bus->write = netsec_phy_write;
+	bus->parent = priv->dev;
+	priv->mii_bus = bus;
+
+	ret = of_mdiobus_register(bus, priv->dev->of_node);
+	if (ret) {
+		mdiobus_free(bus);
+		return ret;
+	}
+
+	return 0;
+}
+
+void netsec_mii_unregister(struct netsec_priv *priv)
+{
+	mdiobus_unregister(priv->mii_bus);
+	mdiobus_free(priv->mii_bus);
+	priv->mii_bus = NULL;
+}
diff --git a/drivers/net/ethernet/socionext/netsec/netsec_netdev.c b/drivers/net/ethernet/socionext/netsec/netsec_netdev.c
new file mode 100644
index 0000000..ff418c1
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec_netdev.c
@@ -0,0 +1,558 @@
+/**
+ * drivers/net/ethernet/socionext/netsec/netsec_netdev.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include <linux/pm_runtime.h>
+
+#include "netsec.h"
+
+#define WAIT_FW_RDY_TIMEOUT 50
+
+static const u32 desc_ring_irq_status_reg_addr[] = {
+	NETSEC_REG_NRM_TX_STATUS,
+	NETSEC_REG_NRM_RX_STATUS,
+};
+
+static const u32 desc_ads[] = {
+	NETSEC_REG_NRM_TX_CONFIG,
+	NETSEC_REG_NRM_RX_CONFIG,
+};
+
+static const u32 netsec_desc_start_reg_addr_up[] = {
+	NETSEC_REG_NRM_TX_DESC_START_UP,
+	NETSEC_REG_NRM_RX_DESC_START_UP,
+};
+
+static const u32 netsec_desc_start_reg_addr_lw[] = {
+	NETSEC_REG_NRM_TX_DESC_START_LW,
+	NETSEC_REG_NRM_RX_DESC_START_LW,
+};
+
+static int netsec_wait_for_ring_config_ready(struct netsec_priv *priv, int ring)
+{
+	int timeout = WAIT_FW_RDY_TIMEOUT;
+
+	while (--timeout && (netsec_readl(priv, desc_ads[ring]) &
+			    NETSEC_REG_DESC_RING_CONFIG_CFG_UP))
+		usleep_range(1000, 2000);
+
+	if (!timeout) {
+		netif_err(priv, hw, priv->ndev,
+			  "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static u32 netsec_calc_pkt_ctrl_reg_param(const struct netsec_pkt_ctrlaram
+					*pkt_ctrlaram_p)
+{
+	u32 param = NETSEC_PKT_CTRL_REG_MODE_NRM;
+
+	if (pkt_ctrlaram_p->log_chksum_er_flag)
+		param |= NETSEC_PKT_CTRL_REG_LOG_CHKSUM_ER;
+
+	if (pkt_ctrlaram_p->log_hd_imcomplete_flag)
+		param |= NETSEC_PKT_CTRL_REG_LOG_HD_INCOMPLETE;
+
+	if (pkt_ctrlaram_p->log_hd_er_flag)
+		param |= NETSEC_PKT_CTRL_REG_LOG_HD_ER;
+
+	return param;
+}
+
+static int netsec_configure_normal_mode(struct netsec_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	/* save scb set value  */
+	priv->scb_set_normal_tx_paddr = (phys_addr_t)netsec_readl(priv,
+			netsec_desc_start_reg_addr_up[NETSEC_RING_TX]) << 32;
+	priv->scb_set_normal_tx_paddr |= (phys_addr_t)netsec_readl(priv,
+			netsec_desc_start_reg_addr_lw[NETSEC_RING_TX]);
+
+	/* set desc_start addr */
+	netsec_writel(priv, netsec_desc_start_reg_addr_up[NETSEC_RING_RX],
+		      priv->desc_ring[NETSEC_RING_RX].desc_phys >> 32);
+	netsec_writel(priv, netsec_desc_start_reg_addr_lw[NETSEC_RING_RX],
+		      priv->desc_ring[NETSEC_RING_RX].desc_phys & 0xffffffff);
+
+	netsec_writel(priv, netsec_desc_start_reg_addr_up[NETSEC_RING_TX],
+		      priv->desc_ring[NETSEC_RING_TX].desc_phys >> 32);
+	netsec_writel(priv, netsec_desc_start_reg_addr_lw[NETSEC_RING_TX],
+		      priv->desc_ring[NETSEC_RING_TX].desc_phys & 0xffffffff);
+
+	/* set normal tx desc ring config */
+	value = (cpu_to_le32(1) == 1) << NETSEC_REG_DESC_ENDIAN |
+		NETSEC_REG_DESC_RING_CONFIG_CFG_UP |
+		NETSEC_REG_DESC_RING_CONFIG_CH_RST;
+	netsec_writel(priv, desc_ads[NETSEC_RING_TX], value);
+
+	value = (cpu_to_le32(1) == 1) << NETSEC_REG_DESC_ENDIAN |
+		NETSEC_REG_DESC_RING_CONFIG_CFG_UP |
+		NETSEC_REG_DESC_RING_CONFIG_CH_RST;
+	netsec_writel(priv, desc_ads[NETSEC_RING_RX], value);
+
+	if (netsec_wait_for_ring_config_ready(priv, NETSEC_RING_TX) ||
+	    netsec_wait_for_ring_config_ready(priv, NETSEC_RING_RX))
+		return -ETIMEDOUT;
+
+	return ret;
+}
+
+static int netsec_change_mode_to_normal(struct netsec_priv *priv)
+{
+	u32 value;
+
+	priv->scb_pkt_ctrl_reg = netsec_readl(priv, NETSEC_REG_PKT_CTRL);
+
+	value = netsec_calc_pkt_ctrl_reg_param(&priv->param.pkt_ctrlaram);
+
+	if (priv->param.use_jumbo_pkt_flag)
+		value |= NETSEC_PKT_CTRL_REG_EN_JUMBO;
+
+	value |= NETSEC_PKT_CTRL_REG_MODE_NRM;
+
+	/* change to normal mode */
+	netsec_writel(priv, NETSEC_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	netsec_writel(priv, NETSEC_REG_PKT_CTRL, value);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return 0;
+}
+
+static int netsec_change_mode_to_taiki(struct netsec_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	netsec_writel(priv, netsec_desc_start_reg_addr_up[NETSEC_RING_TX],
+		      priv->scb_set_normal_tx_paddr >> 32);
+	netsec_writel(priv, netsec_desc_start_reg_addr_lw[NETSEC_RING_TX],
+		      priv->scb_set_normal_tx_paddr & 0xffffffff);
+
+	value = NETSEC_REG_DESC_RING_CONFIG_CFG_UP |
+		NETSEC_REG_DESC_RING_CONFIG_CH_RST;
+
+	netsec_writel(priv, desc_ads[NETSEC_RING_TX], value);
+
+	if (netsec_wait_for_ring_config_ready(priv, NETSEC_RING_TX))
+		return -ETIMEDOUT;
+
+	netsec_writel(priv, NETSEC_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	netsec_writel(priv, NETSEC_REG_PKT_CTRL, priv->scb_pkt_ctrl_reg);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return ret;
+}
+
+static int netsec_clear_modechange_irq(struct netsec_priv *priv, u32 value)
+{
+	netsec_writel(priv, NETSEC_REG_MODE_TRANS_COMP_STATUS,
+		      (value & (NETSEC_MODE_TRANS_COMP_IRQ_N2T |
+		      NETSEC_MODE_TRANS_COMP_IRQ_T2N)));
+	return 0;
+}
+
+static int netsec_hw_configure_to_normal(struct netsec_priv *priv)
+{
+	int err;
+
+	err = netsec_configure_normal_mode(priv);
+	if (err) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: normal conf fail\n", __func__);
+		return err;
+	}
+	err = netsec_change_mode_to_normal(priv);
+	if (err) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: normal set fail\n", __func__);
+		return err;
+	}
+
+	return err;
+}
+
+static int netsec_hw_configure_to_taiki(struct netsec_priv *priv)
+{
+	int ret;
+
+	ret = netsec_change_mode_to_taiki(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: taiki set fail\n", __func__);
+		return ret;
+	}
+
+	/* Clear mode change complete IRQ */
+	ret = netsec_clear_modechange_irq(priv, NETSEC_MODE_TRANS_COMP_IRQ_T2N
+					  | NETSEC_MODE_TRANS_COMP_IRQ_N2T);
+
+	if (ret)
+		netif_err(priv, drv, priv->ndev,
+			  "%s: clear mode fail\n", __func__);
+
+	return ret;
+}
+
+static void netsec_ring_irq_clr(struct netsec_priv *priv,
+				unsigned int id, u32 value)
+{
+	netsec_writel(priv, desc_ring_irq_status_reg_addr[id],
+		      value & (NETSEC_IRQ_EMPTY | NETSEC_IRQ_ERR));
+}
+
+static void netsec_napi_tx_processing(struct napi_struct *napi_p)
+{
+	struct netsec_priv *priv = container_of(napi_p,
+						struct netsec_priv, napi);
+
+	netsec_ring_irq_clr(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+	netsec_clean_tx_desc_ring(priv);
+
+	if (netif_queue_stopped(priv->ndev) &&
+	    netsec_get_tx_avail_num(priv) >= NETSEC_NETDEV_TX_PKT_SCAT_NUM_MAX)
+		netif_wake_queue(priv->ndev);
+}
+
+int netsec_netdev_napi_poll(struct napi_struct *napi_p, int budget)
+{
+	struct netsec_priv *priv = container_of(napi_p,
+						struct netsec_priv, napi);
+	struct net_device *ndev = priv->ndev;
+	struct netsec_rx_pkt_info rx_info;
+	int ret, done = 0, rx_num = 0;
+	struct netsec_frag_info frag;
+	struct sk_buff *skb;
+	u16 len;
+
+	netsec_napi_tx_processing(napi_p);
+
+	while (done < budget) {
+		if (!rx_num) {
+			rx_num = netsec_get_rx_num(priv);
+			if (!rx_num)
+				break;
+		}
+		done++;
+		rx_num--;
+		ret = netsec_get_rx_pkt_data(priv, &rx_info, &frag, &len, &skb);
+		if (unlikely(ret == -ENOMEM)) {
+			netif_err(priv, drv, priv->ndev,
+				  "%s: rx fail %d\n", __func__, ret);
+			ndev->stats.rx_dropped++;
+			continue;
+		}
+		dma_unmap_single(priv->dev, frag.dma_addr, frag.len,
+				 DMA_FROM_DEVICE);
+		skb_put(skb, len);
+		skb->protocol = eth_type_trans(skb, priv->ndev);
+
+		if (priv->rx_cksum_offload_flag &&
+		    rx_info.rx_cksum_result == NETSEC_RX_CKSUM_OK)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+		napi_gro_receive(napi_p, skb);
+
+		ndev->stats.rx_packets++;
+		ndev->stats.rx_bytes += len;
+	}
+
+	if (done == budget)
+		return budget;
+
+	napi_complete(napi_p);
+	netsec_writel(priv, NETSEC_REG_INTEN_SET,
+		      NETSEC_IRQ_TX | NETSEC_IRQ_RX);
+
+	return done;
+}
+
+static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb,
+					    struct net_device *ndev)
+{
+	struct netsec_priv *priv = netdev_priv(ndev);
+	struct netsec_tx_pkt_ctrl tx_ctrl;
+	u16 pend_tx, tso_seg_len = 0;
+	skb_frag_t *frag;
+	int count_frags;
+	int ret, i;
+
+	memset(&tx_ctrl, 0, sizeof(struct netsec_tx_pkt_ctrl));
+
+	netsec_ring_irq_clr(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+
+	count_frags = skb_shinfo(skb)->nr_frags + 1;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL)
+		tx_ctrl.cksum_offload_flag = true;
+
+	if (skb_is_gso(skb))
+		tso_seg_len = skb_shinfo(skb)->gso_size;
+
+	if (tso_seg_len > 0) {
+		if (skb->protocol == htons(ETH_P_IP)) {
+			ip_hdr(skb)->tot_len = 0;
+			tcp_hdr(skb)->check =
+				~tcp_v4_check(0, ip_hdr(skb)->saddr,
+					      ip_hdr(skb)->daddr, 0);
+		} else {
+			ipv6_hdr(skb)->payload_len = 0;
+			tcp_hdr(skb)->check =
+				~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						 &ipv6_hdr(skb)->daddr,
+						 0, IPPROTO_TCP, 0);
+		}
+
+		tx_ctrl.tcp_seg_offload_flag = true;
+		tx_ctrl.tcp_seg_len = tso_seg_len;
+	}
+
+	priv->tx_info[0].dma_addr = dma_map_single(priv->dev, skb->data,
+						   skb_headlen(skb),
+						   DMA_TO_DEVICE);
+	if (dma_mapping_error(priv->dev, priv->tx_info[0].dma_addr)) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: DMA mapping failed\n", __func__);
+		return NETDEV_TX_OK;
+	}
+	priv->tx_info[0].addr = skb->data;
+	priv->tx_info[0].len = skb_headlen(skb);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		priv->tx_info[i + 1].dma_addr =
+			skb_frag_dma_map(priv->dev, frag, 0,
+					 skb_frag_size(frag), DMA_TO_DEVICE);
+		priv->tx_info[i + 1].addr = skb_frag_address(frag);
+		priv->tx_info[i + 1].len = frag->size;
+	}
+
+	netsec_mark_skb_type(skb, NETSEC_RING_TX);
+
+	ret = netsec_set_tx_pkt_data(priv, &tx_ctrl, count_frags,
+				     priv->tx_info, skb);
+	if (ret) {
+		netif_info(priv, drv, priv->ndev,
+			   "set tx pkt failed %d\n", ret);
+		for (i = 0; i < count_frags; i++)
+			dma_unmap_single(priv->dev, priv->tx_info[i].dma_addr,
+					 priv->tx_info[i].len, DMA_TO_DEVICE);
+		ndev->stats.tx_dropped++;
+
+		return NETDEV_TX_OK;
+	}
+
+	netdev_sent_queue(priv->ndev, skb->len);
+
+	spin_lock(&priv->tx_queue_lock);
+	pend_tx = netsec_get_tx_avail_num(priv);
+
+	if (pend_tx < NETSEC_NETDEV_TX_PKT_SCAT_NUM_MAX) {
+		netsec_ring_irq_enable(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+		netif_stop_queue(ndev);
+		goto err;
+	}
+	if (pend_tx <= DESC_NUM - 2) {
+		netsec_ring_irq_enable(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+		goto err;
+	}
+	netsec_ring_irq_disable(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+
+err:
+	spin_unlock(&priv->tx_queue_lock);
+
+	return NETDEV_TX_OK;
+}
+
+static int netsec_netdev_set_features(struct net_device *ndev,
+				      netdev_features_t features)
+{
+	struct netsec_priv *priv = netdev_priv(ndev);
+
+	priv->rx_cksum_offload_flag = !!(features & NETIF_F_RXCSUM);
+
+	return 0;
+}
+
+static void netsec_phy_adjust_link(struct net_device *ndev)
+{
+	struct netsec_priv *priv = netdev_priv(ndev);
+
+	if (priv->actual_link_speed == ndev->phydev->speed &&
+	    priv->actual_duplex == ndev->phydev->duplex)
+		return;
+
+	netsec_stop_gmac(priv);
+	netsec_start_gmac(priv);
+}
+
+static irqreturn_t netsec_irq_handler(int irq, void *dev_id)
+{
+	struct netsec_priv *priv = dev_id;
+	u32 status = netsec_readl(priv, NETSEC_REG_TOP_STATUS) &
+		     netsec_readl(priv, NETSEC_REG_TOP_INTEN);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & (NETSEC_IRQ_TX | NETSEC_IRQ_RX)) {
+		netsec_writel(priv, NETSEC_REG_INTEN_CLR,
+			      status & (NETSEC_IRQ_TX | NETSEC_IRQ_RX));
+		napi_schedule(&priv->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int netsec_netdev_open(struct net_device *ndev)
+{
+	struct netsec_priv *priv = netdev_priv(ndev);
+	struct phy_device *phydev = NULL;
+	u32 scb_irq_temp;
+	int ret, n;
+
+	scb_irq_temp = netsec_readl(priv, NETSEC_REG_TOP_INTEN);
+
+	for (n = 0; n <= NETSEC_RING_MAX; n++) {
+		ret = netsec_alloc_desc_ring(priv, n);
+		if (ret) {
+			netif_err(priv, probe, priv->ndev,
+				  "%s: alloc ring failed\n", __func__);
+			goto err;
+		}
+	}
+
+	ret = netsec_setup_rx_desc(priv, &priv->desc_ring[NETSEC_RING_RX]);
+	if (ret) {
+		netif_err(priv, probe, priv->ndev,
+			  "%s: fail setup ring\n", __func__);
+		goto err1;
+	}
+
+	pm_runtime_get_sync(priv->dev);
+
+	netsec_writel(priv, NETSEC_REG_INTEN_CLR, scb_irq_temp);
+
+	ret = netsec_hw_configure_to_normal(priv);
+	if (ret) {
+		netif_err(priv, probe, priv->ndev,
+			  "%s: normal fail %d\n", __func__, ret);
+		goto err1;
+	}
+
+	ret = request_irq(priv->ndev->irq, netsec_irq_handler,
+			  IRQF_SHARED, "netsec", priv);
+	if (ret) {
+		netif_err(priv, drv, priv->ndev, "request_irq failed\n");
+		goto err1;
+	}
+	priv->irq_registered = true;
+
+	ret = netsec_clean_rx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: clean rx desc fail\n", __func__);
+		goto err2;
+	}
+
+	ret = netsec_clean_tx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->ndev,
+			  "%s: clean tx desc fail\n", __func__);
+		goto err2;
+	}
+
+	netsec_ring_irq_clr(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+
+	phydev = of_phy_connect(priv->ndev, priv->phy_np,
+				&netsec_phy_adjust_link, 0,
+				priv->phy_interface);
+	if (!phydev) {
+		netif_err(priv, link, priv->ndev, "missing PHY\n");
+		goto err2;
+	}
+
+	phy_start_aneg(phydev);
+
+	netsec_ring_irq_disable(priv, NETSEC_RING_TX, NETSEC_IRQ_EMPTY);
+
+	netsec_start_gmac(priv);
+	napi_enable(&priv->napi);
+	netif_start_queue(ndev);
+
+	netsec_writel(priv, NETSEC_REG_INTEN_SET,
+		      NETSEC_IRQ_TX | NETSEC_IRQ_RX);
+
+	return 0;
+
+err2:
+	pm_runtime_put_sync(priv->dev);
+	free_irq(priv->ndev->irq, priv);
+	priv->irq_registered = false;
+err1:
+	for (n = 0; n <= NETSEC_RING_MAX; n++)
+		netsec_free_desc_ring(priv, &priv->desc_ring[n]);
+err:
+	netsec_writel(priv, NETSEC_REG_INTEN_SET, scb_irq_temp);
+
+	pm_runtime_put_sync(priv->dev);
+
+	return ret;
+}
+
+static int netsec_netdev_stop(struct net_device *ndev)
+{
+	struct netsec_priv *priv = netdev_priv(ndev);
+	int n;
+
+	phy_stop(ndev->phydev);
+	phy_disconnect(ndev->phydev);
+
+	netif_stop_queue(priv->ndev);
+	napi_disable(&priv->napi);
+
+	netsec_writel(priv, NETSEC_REG_INTEN_CLR, ~0);
+	netsec_stop_gmac(priv);
+	WARN_ON(netsec_hw_configure_to_taiki(priv));
+
+	pm_runtime_put_sync(priv->dev);
+
+	for (n = 0; n <= NETSEC_RING_MAX; n++)
+		netsec_free_desc_ring(priv, &priv->desc_ring[n]);
+
+	free_irq(priv->ndev->irq, priv);
+	priv->irq_registered = false;
+
+	return 0;
+}
+
+const struct net_device_ops netsec_netdev_ops = {
+	.ndo_open		= netsec_netdev_open,
+	.ndo_stop		= netsec_netdev_stop,
+	.ndo_start_xmit		= netsec_netdev_start_xmit,
+	.ndo_set_features	= netsec_netdev_set_features,
+	.ndo_set_mac_address    = eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
diff --git a/drivers/net/ethernet/socionext/netsec/netsec_platform.c b/drivers/net/ethernet/socionext/netsec/netsec_platform.c
new file mode 100644
index 0000000..d5c19f7
--- /dev/null
+++ b/drivers/net/ethernet/socionext/netsec/netsec_platform.c
@@ -0,0 +1,330 @@
+/**
+ * drivers/net/ethernet/socionext/netsec/netsec_platform.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "netsec.h"
+
+#define NETSEC_F_NETSEC_VER_MAJOR_NUM(x) (x & 0xffff0000)
+
+static int napi_weight = 64;
+unsigned short pause_time = 256;
+
+static int netsec_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev;
+	struct netsec_priv *priv;
+	struct resource *res;
+	const void *mac;
+	bool use_jumbo;
+	u32 hw_ver;
+	int err;
+	int ret;
+
+	ndev = alloc_etherdev(sizeof(*priv));
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+	priv->ndev = ndev;
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	platform_set_drvdata(pdev, priv);
+	priv->dev = &pdev->dev;
+
+	priv->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV |
+			   NETIF_MSG_LINK | NETIF_MSG_PROBE;
+
+	mac = of_get_mac_address(pdev->dev.of_node);
+	if (mac)
+		ether_addr_copy(ndev->dev_addr, mac);
+
+	if (!is_valid_ether_addr(ndev->dev_addr)) {
+		eth_hw_addr_random(ndev);
+		dev_warn(&pdev->dev, "No MAC address found, using random\n");
+	}
+
+	priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+	if (!priv->phy_np) {
+		netif_err(priv, probe, ndev, "missing phy in DT\n");
+		goto err1;
+	}
+
+	priv->phy_interface = of_get_phy_mode(pdev->dev.of_node);
+	if (priv->phy_interface < 0) {
+		netif_err(priv, probe, ndev,
+			  "%s: bad phy-if\n", __func__);
+		goto err1;
+	}
+
+	priv->ioaddr = of_iomap(priv->dev->of_node, 0);
+	if (!priv->ioaddr) {
+		netif_err(priv, probe, ndev, "of_iomap() failed\n");
+		err = -EINVAL;
+		goto err1;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		netif_err(priv, probe, ndev,
+			  "Missing rdlar resource\n");
+		goto err1;
+	}
+	priv->rdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!res) {
+		netif_err(priv, probe, ndev,
+			  "Missing tdlar resource\n");
+		goto err1;
+	}
+	priv->tdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		netif_err(priv, probe, ndev,
+			  "Missing IRQ resource\n");
+		goto err2;
+	}
+	ndev->irq = res->start;
+
+	while (priv->clock_count < ARRAY_SIZE(priv->clk)) {
+		priv->clk[priv->clock_count] =
+			of_clk_get(pdev->dev.of_node, priv->clock_count);
+		if (IS_ERR(priv->clk[priv->clock_count])) {
+			if (!priv->clock_count) {
+				netif_err(priv, probe, ndev,
+					  "Failed to get clock\n");
+				goto err3;
+			}
+			break;
+		}
+		priv->clock_count++;
+	}
+
+	/* disable by default */
+	priv->et_coalesce.rx_coalesce_usecs = 0;
+	priv->et_coalesce.rx_max_coalesced_frames = 1;
+	priv->et_coalesce.tx_coalesce_usecs = 0;
+	priv->et_coalesce.tx_max_coalesced_frames = 1;
+
+	use_jumbo = of_property_read_bool(pdev->dev.of_node, "use-jumbo");
+	priv->param.use_jumbo_pkt_flag = use_jumbo;
+
+	if (priv->param.use_jumbo_pkt_flag)
+		priv->rx_pkt_buf_len = NETSEC_RX_JUMBO_PKT_BUF_LEN;
+	else
+		priv->rx_pkt_buf_len = NETSEC_RX_PKT_BUF_LEN;
+
+	pm_runtime_enable(&pdev->dev);
+	/* runtime_pm coverage just for probe, open/close also cover it */
+	pm_runtime_get_sync(&pdev->dev);
+
+	hw_ver = netsec_readl(priv, NETSEC_REG_F_TAIKI_VER);
+	/* this driver only supports F_TAIKI style NETSEC */
+	if (NETSEC_F_NETSEC_VER_MAJOR_NUM(hw_ver) !=
+	    NETSEC_F_NETSEC_VER_MAJOR_NUM(NETSEC_REG_NETSEC_VER_F_TAIKI)) {
+		ret = -ENODEV;
+		goto err3;
+	}
+
+	dev_info(&pdev->dev, "IP rev %d.%d\n", hw_ver >> 16, hw_ver & 0xffff);
+
+	priv->mac_mode.flow_start_th = NETSEC_FLOW_CONTROL_START_THRESHOLD;
+	priv->mac_mode.flow_stop_th = NETSEC_FLOW_CONTROL_STOP_THRESHOLD;
+	priv->mac_mode.pause_time = pause_time;
+	priv->mac_mode.flow_ctrl_enable_flag = false;
+	priv->freq = clk_get_rate(priv->clk[0]);
+
+	netif_napi_add(ndev, &priv->napi, netsec_netdev_napi_poll,
+		       napi_weight);
+
+	/* MTU range */
+	ndev->min_mtu = ETH_MIN_MTU;
+	ndev->max_mtu = NETSEC_RX_JUMBO_PKT_BUF_LEN;
+
+	ndev->netdev_ops = &netsec_netdev_ops;
+	ndev->ethtool_ops = &netsec_ethtool_ops;
+	ndev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
+			       NETIF_F_IPV6_CSUM | NETIF_F_TSO |
+			       NETIF_F_TSO6 | NETIF_F_GSO |
+			       NETIF_F_HIGHDMA | NETIF_F_RXCSUM;
+	ndev->hw_features = ndev->features;
+
+	priv->rx_cksum_offload_flag = true;
+	spin_lock_init(&priv->tx_queue_lock);
+
+	err = netsec_mii_register(priv);
+	if (err) {
+		netif_err(priv, probe, ndev,
+			  "mii bus registration failed %d\n", err);
+		goto err3;
+	}
+
+	/* disable all other interrupt sources */
+	netsec_writel(priv, NETSEC_REG_INTEN_CLR, ~0);
+	netsec_writel(priv, NETSEC_REG_INTEN_SET,
+		      NETSEC_IRQ_TX | NETSEC_IRQ_RX);
+
+	err = register_netdev(ndev);
+	if (err) {
+		netif_err(priv, probe, ndev, "register_netdev() failed\n");
+		goto err4;
+	}
+
+	pm_runtime_put_sync_suspend(&pdev->dev);
+
+	netif_info(priv, probe, ndev, "initialized\n");
+
+	return 0;
+
+err4:
+	netsec_mii_unregister(priv);
+
+err3:
+	pm_runtime_put_sync_suspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	while (priv->clock_count > 0) {
+		priv->clock_count--;
+		clk_put(priv->clk[priv->clock_count]);
+	}
+err2:
+	iounmap(priv->ioaddr);
+err1:
+	free_netdev(ndev);
+
+	dev_err(&pdev->dev, "init failed\n");
+
+	return ret;
+}
+
+static int netsec_remove(struct platform_device *pdev)
+{
+	struct netsec_priv *priv = platform_get_drvdata(pdev);
+
+	unregister_netdev(priv->ndev);
+	netsec_mii_unregister(priv);
+	pm_runtime_disable(&pdev->dev);
+	iounmap(priv->ioaddr);
+	free_netdev(priv->ndev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int netsec_runtime_suspend(struct device *dev)
+{
+	struct netsec_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->ndev, "%s\n", __func__);
+
+	if (priv->irq_registered)
+		disable_irq(priv->ndev->irq);
+
+	netsec_writel(priv, NETSEC_REG_CLK_EN, 0);
+
+	for (n = priv->clock_count - 1; n >= 0; n--)
+		clk_disable_unprepare(priv->clk[n]);
+
+	return 0;
+}
+
+static int netsec_runtime_resume(struct device *dev)
+{
+	struct netsec_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->ndev, "%s\n", __func__);
+
+	/* first let the clocks back on */
+
+	for (n = 0; n < priv->clock_count; n++)
+		clk_prepare_enable(priv->clk[n]);
+
+	netsec_writel(priv, NETSEC_REG_CLK_EN, NETSEC_CLK_EN_REG_DOM_D |
+			NETSEC_CLK_EN_REG_DOM_C | NETSEC_CLK_EN_REG_DOM_G);
+
+	if (priv->irq_registered)
+		enable_irq(priv->ndev->irq);
+
+	return 0;
+}
+
+static int netsec_pm_suspend(struct device *dev)
+{
+	struct netsec_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->ndev, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return netsec_runtime_suspend(dev);
+}
+
+static int netsec_pm_resume(struct device *dev)
+{
+	struct netsec_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->ndev, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return netsec_runtime_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops netsec_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(netsec_pm_suspend, netsec_pm_resume)
+	SET_RUNTIME_PM_OPS(netsec_runtime_suspend, netsec_runtime_resume, NULL)
+};
+
+static const struct of_device_id netsec_dt_ids[] = {
+	{.compatible = "socionext,netsecv5"},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, netsec_dt_ids);
+
+static struct platform_driver netsec_driver = {
+	.probe = netsec_probe,
+	.remove = netsec_remove,
+	.driver = {
+		.name = "netsec",
+		.of_match_table = netsec_dt_ids,
+		.pm = &netsec_pm_ops,
+	},
+};
+
+module_platform_driver(netsec_driver);
+
+MODULE_AUTHOR("Socionext Inc");
+MODULE_DESCRIPTION("NETSEC Ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("platform:netsec");
-- 
2.7.4

^ permalink raw reply related

* [net-next PATCHv6 1/2] dt-bindings: net: Add DT bindings for Socionext Netsec
From: Jassi Brar @ 2017-08-30 10:25 UTC (permalink / raw)
  To: netdev, devicetree, linux-arm-kernel, davem
  Cc: patches, arnd, mark.rutland, robh+dt, andy, Jassi Brar
In-Reply-To: <1504088657-6102-1-git-send-email-jaswinder.singh@linaro.org>

This patch adds documentation for Device-Tree bindings for the
Socionext NetSec Controller driver.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
 .../devicetree/bindings/net/socionext-netsec.txt   | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/socionext-netsec.txt

diff --git a/Documentation/devicetree/bindings/net/socionext-netsec.txt b/Documentation/devicetree/bindings/net/socionext-netsec.txt
new file mode 100644
index 0000000..12d596c
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/socionext-netsec.txt
@@ -0,0 +1,46 @@
+* Socionext NetSec Ethernet Controller IP
+
+Required properties:
+- compatible: Should be "socionext,netsecv5"
+- reg: Address and length of the register sets, the first is the main
+	registers, then the rdlar and tdlar regions for the SoC
+- interrupts: Should contain ethernet controller interrupt
+- clocks: phandle to any clocks to be switched by runtime_pm
+- phy-mode: See ethernet.txt file in the same directory
+- max-speed: See ethernet.txt file in the same directory
+- max-frame-size: See ethernet.txt file in the same directory, if 9000 or
+	above jumbo frames are enabled
+- local-mac-address: See ethernet.txt file in the same directory
+- phy-handle: phandle to select child phy
+
+Optional properties:
+- use-jumbo: Boolean property to suggest if jumbo packets should be used or not
+
+For the child phy
+
+- compatible "ethernet-phy-ieee802.3-c22" is needed
+- device_type "ethernet-phy"
+- reg: phy address
+
+
+Example:
+	eth0: ethernet {
+		compatible = "socionext,netsecv5";
+		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy@1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
-- 
2.7.4

^ permalink raw reply related

* [net-next PATCHv6 0/2] net: ethernet: Socionext Netsec
From: Jassi Brar @ 2017-08-30 10:24 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: patches-QSEj5FYQhm4dnm+yROfE0A, arnd-r2nGTMty4D4,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	andy-/Zus8d0mwwtBDgjK7y7TUQ, Jassi Brar

Hello,

  The OGMA/Netsec controller is used in latest SoC from Socionext/Fujitsu.

I am refreshing the patchset by basically using official name of the IP
from 'OGMA' to 'Netsec'. And the company is renamed too, from Fujitsu
to Socionext to better reflect the reality.

 I have addressed comments (that could be) on the last revision -->
https://patchwork.kernel.org/patch/4540651/

 Of course, I have scanned changes to the drivers/net/ethernet since
last submission and integrated whichever applicable and rebased the
driver on top of last rc.

Thanks.

Jassi Brar (2):
  dt-bindings: net: Add DT bindings for Socionext Netsec
  net: socionext: Add NetSec driver

 .../devicetree/bindings/net/socionext-netsec.txt   |  46 ++
 drivers/net/ethernet/Kconfig                       |   1 +
 drivers/net/ethernet/Makefile                      |   1 +
 drivers/net/ethernet/socionext/Kconfig             |  29 +
 drivers/net/ethernet/socionext/Makefile            |   1 +
 drivers/net/ethernet/socionext/netsec/Makefile     |   6 +
 drivers/net/ethernet/socionext/netsec/netsec.h     | 386 +++++++++++++
 .../socionext/netsec/netsec_desc_ring_access.c     | 618 +++++++++++++++++++++
 .../net/ethernet/socionext/netsec/netsec_ethtool.c |  76 +++
 .../ethernet/socionext/netsec/netsec_gmac_access.c | 329 +++++++++++
 .../net/ethernet/socionext/netsec/netsec_netdev.c  | 558 +++++++++++++++++++
 .../ethernet/socionext/netsec/netsec_platform.c    | 330 +++++++++++
 12 files changed, 2381 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/socionext-netsec.txt
 create mode 100644 drivers/net/ethernet/socionext/Kconfig
 create mode 100644 drivers/net/ethernet/socionext/Makefile
 create mode 100644 drivers/net/ethernet/socionext/netsec/Makefile
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec.h
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_desc_ring_access.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_ethtool.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_gmac_access.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_netdev.c
 create mode 100644 drivers/net/ethernet/socionext/netsec/netsec_platform.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

* Re: [PATCH v2] ipv6: sr: fix get_srh() to comply with IPv6 standard "RFC 8200"
From: David Lebrun @ 2017-08-30 10:24 UTC (permalink / raw)
  To: Ahmed Abdelsalam, davem; +Cc: netdev
In-Reply-To: <1504083037-1605-1-git-send-email-amsalam20@gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 439 bytes --]

On 08/30/2017 10:50 AM, Ahmed Abdelsalam wrote:
> This patch fixes the get_srh(), so it gets the segment routing header
> regardless of its position in the chain of the extension headers in IPv6
> packet, and makes sure that the IPv6 routing extension header is of Type 4.
> 
> Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>

Note that this patch applies to net-next.

Acked-by: David Lebrun <david.lebrun@uclouvain.be>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply

* Re: [PATCH] net: stmmac: dwmac-sun8i: Use reset exclusive
From: Corentin Labbe @ 2017-08-30 10:21 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: peppe.cavallaro, alexandre.torgue, wens, netdev, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20170825171233.4xoihzakca3mqscd@flea.lan>

On Fri, Aug 25, 2017 at 07:12:33PM +0200, Maxime Ripard wrote:
> On Fri, Aug 25, 2017 at 05:17:33PM +0200, Corentin Labbe wrote:
> > On Fri, Aug 25, 2017 at 04:48:32PM +0200, Maxime Ripard wrote:
> > > On Fri, Aug 25, 2017 at 04:38:05PM +0200, Corentin Labbe wrote:
> > > > The current dwmac_sun8i module cannot be rmmod/modprobe due to that
> > > > the reset controller was not released when removed.
> > > > 
> > > > This patch remove ambiguity, by using of_reset_control_get_exclusive and
> > > > add the missing reset_control_put().
> > > > 
> > > > Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
> > > > ---
> > > >  drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 3 ++-
> > > >  1 file changed, 2 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> > > > index fffd6d5fc907..675a09629d85 100644
> > > > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> > > > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> > > > @@ -782,6 +782,7 @@ static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
> > > >  
> > > >  	clk_disable_unprepare(gmac->ephy_clk);
> > > >  	reset_control_assert(gmac->rst_ephy);
> > > > +	reset_control_put(gmac->rst_ephy);
> > > >  	return 0;
> > > >  }
> > > >  
> > > > @@ -942,7 +943,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
> > > >  			return -EINVAL;
> > > >  		}
> > > >  
> > > > -		gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL);
> > > > +		gmac->rst_ephy = of_reset_control_get_exclusive(plat_dat->phy_node, NULL);
> > > 
> > > Why not just use devm_reset_control_get?
> > > 
> > 
> > Because there no devm_ functions with of_
> 
> devm_reset_control_get uses of_reset_control_get internally.

Hello

You are right.
But since the ephy reset is in a child node, devm_ does not find it.

Regards

^ permalink raw reply

* Re: [PATCH] DSA support for Micrel KSZ8895
From: Maxim Uvarov @ 2017-08-30 10:06 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Pavel Machek, Andrew Lunn, Woojung.Huh, Nathan Conrad,
	Vivien Didelot, netdev, linux-kernel, Tristram.Ha
In-Reply-To: <237eee82-2edb-56c0-d644-2e6253a178dc@gmail.com>

2017-08-30 0:23 GMT+03:00 Florian Fainelli <f.fainelli@gmail.com>:
> On 08/29/2017 02:15 PM, Pavel Machek wrote:
>> On Tue 2017-08-29 14:26:04, Andrew Lunn wrote:
>>>> But the MDIO emaulation code is from their driver, after lots of
>>>> deletions.
>>>
>>> Is this driver supposed to run on lots of different OSs? That would
>>> explain why they ignored the Linux MDIO and PHY layers.
>>
>> It did not look particulary portable.
>
> Part of the problem is that they need to duplicate the standard MII
> definitions, whereas we could re-use those from include/linux/mii.h
> and/or mdio.h.
>
>>
>>> If possible, please make use of the Linux infrastructure.
>>
>> I did not find any infrastructure I could use instead
>> ksz_mdio_emulation.
>
> fixed PHY/swphy.c is as close as it could get, but it is a highly
> simplified version of this.
>
>>
>> Now, drivers/net/phy/spi_ks8995.c can access the PHY registers, and I
>> can not see any translation there, so there may be something I'm
>> missing.
>
> I don't see anything in that driver that seems to access PHY registers
> what makes you think it does?
>
> There's got to be a way to perform indirect accesses through SPI,
> Woojung, do you know?
>

As I understand they just attach phy on spi bus with generic driver:

        phy_addr = 0;
        phy_mode = PHY_INTERFACE_MODE_MII;
        snprintf(bus_id, MII_BUS_ID_SIZE, "sw.%d", sw_device_present);
        snprintf(phy_id, MII_BUS_ID_SIZE, PHY_ID_FMT, bus_id, phy_addr);
        phydev = phy_attach(netdev, phy_id, 0, phy_mode);
        if (!IS_ERR(phydev)) {
                phydev->adjust_link = sw_adjust_link;
                return phydev;
        }

Where is bus is:
bus = mdiobus_alloc();
bus->read = ksz_mii_read; (spi read function)
bus->write = ksz_mii_write;

Then just generic reads:
    .config_aneg    = genphy_config_aneg,
    .read_status    = genphy_read_status,


Maxim.

>>
>> Pointers would be welcome at this point.
>>
>> Thanks,
>>                                                                       Pavel
>>
>
>
> --
> Florian



-- 
Best regards,
Maxim Uvarov

^ permalink raw reply

* [PATCH net 5/9] sch_cbq: fix null pointer dereferences on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

CBQ can fail on ->init by wrong nl attributes or simply for missing any,
f.e. if it's set as a default qdisc then TCA_OPTIONS (opt) will be NULL
when it is activated. The first thing init does is parse opt but it will
dereference a null pointer if used as a default qdisc, also since init
failure at default qdisc invokes ->reset() which cancels all timers then
we'll also dereference two more null pointers (timer->base) as they were
never initialized.

To reproduce:
$ sysctl net.core.default_qdisc=cbq
$ ip l set ethX up

Crash log of the first null ptr deref:
[44727.907454] BUG: unable to handle kernel NULL pointer dereference at (null)
[44727.907600] IP: cbq_init+0x27/0x205
[44727.907676] PGD 59ff4067
[44727.907677] P4D 59ff4067
[44727.907742] PUD 59c70067
[44727.907807] PMD 0
[44727.907873]
[44727.907982] Oops: 0000 [#1] SMP
[44727.908054] Modules linked in:
[44727.908126] CPU: 1 PID: 21312 Comm: ip Not tainted 4.13.0-rc6+ #60
[44727.908235] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[44727.908477] task: ffff88005ad42700 task.stack: ffff880037214000
[44727.908672] RIP: 0010:cbq_init+0x27/0x205
[44727.908838] RSP: 0018:ffff8800372175f0 EFLAGS: 00010286
[44727.909018] RAX: ffffffff816c3852 RBX: ffff880058c53800 RCX: 0000000000000000
[44727.909222] RDX: 0000000000000004 RSI: 0000000000000000 RDI: ffff8800372175f8
[44727.909427] RBP: ffff880037217650 R08: ffffffff81b0f380 R09: 0000000000000000
[44727.909631] R10: ffff880037217660 R11: 0000000000000020 R12: ffffffff822a44c0
[44727.909835] R13: ffff880058b92000 R14: 00000000ffffffff R15: 0000000000000001
[44727.910040] FS:  00007ff8bc583740(0000) GS:ffff88005d880000(0000) knlGS:0000000000000000
[44727.910339] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[44727.910525] CR2: 0000000000000000 CR3: 00000000371e5000 CR4: 00000000000406e0
[44727.910731] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[44727.910936] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[44727.911141] Call Trace:
[44727.911291]  ? lockdep_init_map+0xb6/0x1ba
[44727.911461]  ? qdisc_alloc+0x14e/0x187
[44727.911626]  qdisc_create_dflt+0x7a/0x94
[44727.911794]  ? dev_activate+0x129/0x129
[44727.911959]  attach_one_default_qdisc+0x36/0x63
[44727.912132]  netdev_for_each_tx_queue+0x3d/0x48
[44727.912305]  dev_activate+0x4b/0x129
[44727.912468]  __dev_open+0xe7/0x104
[44727.912631]  __dev_change_flags+0xc6/0x15c
[44727.912799]  dev_change_flags+0x25/0x59
[44727.912966]  do_setlink+0x30c/0xb3f
[44727.913129]  ? check_chain_key+0xb0/0xfd
[44727.913294]  ? check_chain_key+0xb0/0xfd
[44727.913463]  rtnl_newlink+0x3a4/0x729
[44727.913626]  ? rtnl_newlink+0x117/0x729
[44727.913801]  ? ns_capable_common+0xd/0xb1
[44727.913968]  ? ns_capable+0x13/0x15
[44727.914131]  rtnetlink_rcv_msg+0x188/0x197
[44727.914300]  ? rcu_read_unlock+0x3e/0x5f
[44727.914465]  ? rtnl_newlink+0x729/0x729
[44727.914630]  netlink_rcv_skb+0x6c/0xce
[44727.914796]  rtnetlink_rcv+0x23/0x2a
[44727.914956]  netlink_unicast+0x103/0x181
[44727.915122]  netlink_sendmsg+0x326/0x337
[44727.915291]  sock_sendmsg_nosec+0x14/0x3f
[44727.915459]  sock_sendmsg+0x29/0x2e
[44727.915619]  ___sys_sendmsg+0x209/0x28b
[44727.915784]  ? do_raw_spin_unlock+0xcd/0xf8
[44727.915954]  ? _raw_spin_unlock+0x27/0x31
[44727.916121]  ? __handle_mm_fault+0x651/0xdb1
[44727.916290]  ? check_chain_key+0xb0/0xfd
[44727.916461]  __sys_sendmsg+0x45/0x63
[44727.916626]  ? __sys_sendmsg+0x45/0x63
[44727.916792]  SyS_sendmsg+0x19/0x1b
[44727.916950]  entry_SYSCALL_64_fastpath+0x23/0xc2
[44727.917125] RIP: 0033:0x7ff8bbc96690
[44727.917286] RSP: 002b:00007ffc360991e8 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[44727.917579] RAX: ffffffffffffffda RBX: ffffffff810d278c RCX: 00007ff8bbc96690
[44727.917783] RDX: 0000000000000000 RSI: 00007ffc36099230 RDI: 0000000000000003
[44727.917987] RBP: ffff880037217f98 R08: 0000000000000001 R09: 0000000000000003
[44727.918190] R10: 00007ffc36098fb0 R11: 0000000000000246 R12: 0000000000000006
[44727.918393] R13: 000000000066f1a0 R14: 00007ffc360a12e0 R15: 0000000000000000
[44727.918597]  ? trace_hardirqs_off_caller+0xa7/0xcf
[44727.918774] Code: 41 5f 5d c3 66 66 66 66 90 55 48 8d 56 04 45 31 c9
49 c7 c0 80 f3 b0 81 48 89 e5 41 55 41 54 53 48 89 fb 48 8d 7d a8 48 83
ec 48 <0f> b7 0e be 07 00 00 00 83 e9 04 e8 e6 f7 d8 ff 85 c0 0f 88 bb
[44727.919332] RIP: cbq_init+0x27/0x205 RSP: ffff8800372175f0
[44727.919516] CR2: 0000000000000000

Fixes: 0fbbeb1ba43b ("[PKT_SCHED]: Fix missing qdisc_destroy() in qdisc_create_dflt()")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_cbq.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 780db43300b1..156c8a33c677 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1139,6 +1139,13 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
 	struct tc_ratespec *r;
 	int err;
 
+	qdisc_watchdog_init(&q->watchdog, sch);
+	hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
+	q->delay_timer.function = cbq_undelay;
+
+	if (!opt)
+		return -EINVAL;
+
 	err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL);
 	if (err < 0)
 		return err;
@@ -1177,9 +1184,6 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
 	q->link.avpkt = q->link.allot/2;
 	q->link.minidle = -0x7FFFFFFF;
 
-	qdisc_watchdog_init(&q->watchdog, sch);
-	hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
-	q->delay_timer.function = cbq_undelay;
 	q->toplevel = TC_CBQ_MAXLEVEL;
 	q->now = psched_get_time();
 
-- 
2.1.4

^ permalink raw reply related

* Re: [PATCH net-next v6 3/3] openvswitch: enable NSH support
From: Hannes Frederic Sowa @ 2017-08-30  9:53 UTC (permalink / raw)
  To: Yi Yang; +Cc: netdev, dev, jbenc, e, blp, jan.scheurich
In-Reply-To: <1503670805-31051-4-git-send-email-yi.y.yang@intel.com>

Hello,

Yi Yang <yi.y.yang@intel.com> writes:

[...]

> +struct ovs_key_nsh {
> +	u8 flags;
> +	u8 ttl;
> +	u8 mdtype;
> +	u8 np;
> +	__be32 path_hdr;
> +	__be32 context[NSH_MD1_CONTEXT_SIZE];
> +};
> +
>  struct sw_flow_key {
>  	u8 tun_opts[IP_TUNNEL_OPTS_MAX];
>  	u8 tun_opts_len;
> @@ -144,6 +154,7 @@ struct sw_flow_key {
>  			};
>  		} ipv6;
>  	};
> +	struct ovs_key_nsh nsh;         /* network service header */
>  	struct {
>  		/* Connection tracking fields not packed above. */
>  		struct {

Does it makes sense to keep the context headers as part of the flow?
What is the reasoning behind it? With mdtype 2 headers this might either
not work very well or will increase sw_flow_key size causing slowdowns
for all protocols.

[...]

^ permalink raw reply

* [PATCH v2 1/3] dt-bindings: add SFF vendor prefix
From: Baruch Siach @ 2017-08-30  9:51 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Andrew Lunn, Florian Fainelli,
	David S. Miller, Russell King
  Cc: netdev, devicetree, Baruch Siach

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
v2: New patch in this series
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index daf465bef758..20fdb79a92d4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -287,6 +287,7 @@ schindler	Schindler
 seagate	Seagate Technology PLC
 semtech	Semtech Corporation
 sensirion	Sensirion AG
+sff	Small Form Factor Committee
 sgx	SGX Sensortech
 sharp	Sharp Corporation
 si-en	Si-En Technology Ltd.
-- 
2.14.1

^ permalink raw reply related

* [PATCH v2 3/3] net: phy: sfp: rename the rate-select property
From: Baruch Siach @ 2017-08-30  9:51 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Andrew Lunn, Florian Fainelli,
	David S. Miller, Russell King
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Baruch Siach
In-Reply-To: <d1e64c5752ef0dd5c5b543c2d4c3ef1783318173.1504086672.git.baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>

Make the Rx rate select control gpio property name match the documented
binding. This would make the addition of 'rate-select1-gpios' for SFP+
support more natural.

Signed-off-by: Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>
---
v2: New patch in this series
---
 drivers/net/phy/sfp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index fb2cf4342f48..b44c0e296662 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -62,7 +62,7 @@ static const char *gpio_of_names[] = {
 	"los",
 	"tx-fault",
 	"tx-disable",
-	"rate-select",
+	"rate-select0",
 };
 
 static const enum gpiod_flags gpio_flags[] = {
-- 
2.14.1

--
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 related

* [PATCH v2 2/3] dt-binding: net: sfp binding documentation
From: Baruch Siach @ 2017-08-30  9:51 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Andrew Lunn, Florian Fainelli,
	David S. Miller, Russell King
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Baruch Siach
In-Reply-To: <d1e64c5752ef0dd5c5b543c2d4c3ef1783318173.1504086672.git.baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>

Add device-tree binding documentation SFP transceivers. Support for SFP
transceivers has been recently introduced (drivers/net/phy/sfp.c).

Signed-off-by: Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>
---
v2:
  Rename -gpio properties to -gpios
  Rename the rate-select-gpio property to rate-select0-gpios
  Add the rate-select1-gpios property
  Add examples
---
 Documentation/devicetree/bindings/net/sff,sfp.txt | 74 +++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sff,sfp.txt

diff --git a/Documentation/devicetree/bindings/net/sff,sfp.txt b/Documentation/devicetree/bindings/net/sff,sfp.txt
new file mode 100644
index 000000000000..1d9c786d6287
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/sff,sfp.txt
@@ -0,0 +1,74 @@
+Small Form Factor (SFF) Committee Small Form-factor Pluggable (SFP)
+Transceiver
+
+Required properties:
+
+- compatible : must be "sff,sfp"
+
+Optional Properties:
+
+- i2c-bus : phandle of an I2C bus controller for the SFP two wire serial
+  interface
+
+- moddef0-gpios : phandle of the MOD-DEF0 (AKA Mod_ABS) module presence input
+  gpio signal
+
+- los-gpios : phandle of the Receiver Loss of Signal Indication input gpio
+  signal
+
+- tx-fault-gpios : phandle of the Module Transmitter Fault input gpio signal
+
+- tx-disable-gpios : phandle of the Transmitter Disable output gpio signal
+
+- rate-select0-gpios : phandle of the Rx Signaling Rate Select (AKA RS0) output
+  gpio
+
+- rate-select1-gpios : phandle of the Tx Signaling Rate Select (AKA RS1) output
+  gpio (SFP+ only)
+
+Example #1: Direct serdes to SFP connection
+
+sfp_eth3: sfp-eth3 {
+	compatible = "sff,sfp";
+	i2c-bus = <&sfp_1g_i2c>;
+	los-gpios = <&cpm_gpio2 22 GPIO_ACTIVE_HIGH>;
+	moddef0-gpios = <&cpm_gpio2 21 GPIO_ACTIVE_LOW>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&cpm_sfp_1g_pins &cps_sfp_1g_pins>;
+	tx-disable-gpios = <&cps_gpio1 24 GPIO_ACTIVE_HIGH>;
+	tx-fault-gpios = <&cpm_gpio2 19 GPIO_ACTIVE_HIGH>;
+};
+
+&cps_emac3 {
+	phy-mode = "sgmii";
+	phy-names = "comphy";
+	phys = <&cps_comphy 5 COMPHY_SGMII2>;
+	sfp = <&sfp_eth3>;
+};
+
+Example #2: Serdes to PHY to SFP connection
+
+sfp_eth0: sfp-eth0 {
+	compatible = "sff,sfp+";
+	i2c-bus = <&sfpp0_i2c>;
+	los-gpio = <&cps_gpio1 28 GPIO_ACTIVE_HIGH>;
+	moddef0-gpio = <&cps_gpio1 27 GPIO_ACTIVE_LOW>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&cps_sfpp0_pins>;
+	tx-disable-gpio = <&cps_gpio1 29 GPIO_ACTIVE_HIGH>;
+	tx-fault-gpio  = <&cps_gpio1 26 GPIO_ACTIVE_HIGH>;
+};
+
+p0_phy: ethernet-phy@0 {
+	compatible = "ethernet-phy-ieee802.3-c45";
+	pinctrl-names = "default";
+	pinctrl-0 = <&cpm_phy0_pins &cps_phy0_pins>;
+	reg = <0>;
+	interrupt = <&cpm_gpio2 18 IRQ_TYPE_EDGE_FALLING>;
+	sfp = <&sfp_eth0>;
+};
+
+&cpm_eth0 {
+	phy = <&p0_phy>;
+	phy-mode = "10gbase-kr";
+};
-- 
2.14.1

--
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 related

* [PATCH net 9/9] sch_tbf: fix two null pointer dereferences on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

sch_tbf calls qdisc_watchdog_cancel() in both its ->reset and ->destroy
callbacks but it may fail before the timer is initialized due to missing
options (either not supplied by user-space or set as a default qdisc),
also q->qdisc is used by ->reset and ->destroy so we need it initialized.

Reproduce:
$ sysctl net.core.default_qdisc=tbf
$ ip l set ethX up

Crash log:
[  959.160172] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
[  959.160323] IP: qdisc_reset+0xa/0x5c
[  959.160400] PGD 59cdb067
[  959.160401] P4D 59cdb067
[  959.160466] PUD 59ccb067
[  959.160532] PMD 0
[  959.160597]
[  959.160706] Oops: 0000 [#1] SMP
[  959.160778] Modules linked in: sch_tbf sch_sfb sch_prio sch_netem
[  959.160891] CPU: 2 PID: 1562 Comm: ip Not tainted 4.13.0-rc6+ #62
[  959.160998] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[  959.161157] task: ffff880059c9a700 task.stack: ffff8800376d0000
[  959.161263] RIP: 0010:qdisc_reset+0xa/0x5c
[  959.161347] RSP: 0018:ffff8800376d3610 EFLAGS: 00010286
[  959.161531] RAX: ffffffffa001b1dd RBX: ffff8800373a2800 RCX: 0000000000000000
[  959.161733] RDX: ffffffff8215f160 RSI: ffffffff8215f160 RDI: 0000000000000000
[  959.161939] RBP: ffff8800376d3618 R08: 00000000014080c0 R09: 00000000ffffffff
[  959.162141] R10: ffff8800376d3578 R11: 0000000000000020 R12: ffffffffa001d2c0
[  959.162343] R13: ffff880037538000 R14: 00000000ffffffff R15: 0000000000000001
[  959.162546] FS:  00007fcc5126b740(0000) GS:ffff88005d900000(0000) knlGS:0000000000000000
[  959.162844] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  959.163030] CR2: 0000000000000018 CR3: 000000005abc4000 CR4: 00000000000406e0
[  959.163233] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  959.163436] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  959.163638] Call Trace:
[  959.163788]  tbf_reset+0x19/0x64 [sch_tbf]
[  959.163957]  qdisc_destroy+0x8b/0xe5
[  959.164119]  qdisc_create_dflt+0x86/0x94
[  959.164284]  ? dev_activate+0x129/0x129
[  959.164449]  attach_one_default_qdisc+0x36/0x63
[  959.164623]  netdev_for_each_tx_queue+0x3d/0x48
[  959.164795]  dev_activate+0x4b/0x129
[  959.164957]  __dev_open+0xe7/0x104
[  959.165118]  __dev_change_flags+0xc6/0x15c
[  959.165287]  dev_change_flags+0x25/0x59
[  959.165451]  do_setlink+0x30c/0xb3f
[  959.165613]  ? check_chain_key+0xb0/0xfd
[  959.165782]  rtnl_newlink+0x3a4/0x729
[  959.165947]  ? rtnl_newlink+0x117/0x729
[  959.166121]  ? ns_capable_common+0xd/0xb1
[  959.166288]  ? ns_capable+0x13/0x15
[  959.166450]  rtnetlink_rcv_msg+0x188/0x197
[  959.166617]  ? rcu_read_unlock+0x3e/0x5f
[  959.166783]  ? rtnl_newlink+0x729/0x729
[  959.166948]  netlink_rcv_skb+0x6c/0xce
[  959.167113]  rtnetlink_rcv+0x23/0x2a
[  959.167273]  netlink_unicast+0x103/0x181
[  959.167439]  netlink_sendmsg+0x326/0x337
[  959.167607]  sock_sendmsg_nosec+0x14/0x3f
[  959.167772]  sock_sendmsg+0x29/0x2e
[  959.167932]  ___sys_sendmsg+0x209/0x28b
[  959.168098]  ? do_raw_spin_unlock+0xcd/0xf8
[  959.168267]  ? _raw_spin_unlock+0x27/0x31
[  959.168432]  ? __handle_mm_fault+0x651/0xdb1
[  959.168602]  ? check_chain_key+0xb0/0xfd
[  959.168773]  __sys_sendmsg+0x45/0x63
[  959.168934]  ? __sys_sendmsg+0x45/0x63
[  959.169100]  SyS_sendmsg+0x19/0x1b
[  959.169260]  entry_SYSCALL_64_fastpath+0x23/0xc2
[  959.169432] RIP: 0033:0x7fcc5097e690
[  959.169592] RSP: 002b:00007ffd0d5c7b48 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[  959.169887] RAX: ffffffffffffffda RBX: ffffffff810d278c RCX: 00007fcc5097e690
[  959.170089] RDX: 0000000000000000 RSI: 00007ffd0d5c7b90 RDI: 0000000000000003
[  959.170292] RBP: ffff8800376d3f98 R08: 0000000000000001 R09: 0000000000000003
[  959.170494] R10: 00007ffd0d5c7910 R11: 0000000000000246 R12: 0000000000000006
[  959.170697] R13: 000000000066f1a0 R14: 00007ffd0d5cfc40 R15: 0000000000000000
[  959.170900]  ? trace_hardirqs_off_caller+0xa7/0xcf
[  959.171076] Code: 00 41 c7 84 24 14 01 00 00 00 00 00 00 41 c7 84 24
98 00 00 00 00 00 00 00 41 5c 41 5d 41 5e 5d c3 66 66 66 66 90 55 48 89
e5 53 <48> 8b 47 18 48 89 fb 48 8b 40 48 48 85 c0 74 02 ff d0 48 8b bb
[  959.171637] RIP: qdisc_reset+0xa/0x5c RSP: ffff8800376d3610
[  959.171821] CR2: 0000000000000018

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Fixes: 0fbbeb1ba43b ("[PKT_SCHED]: Fix missing qdisc_destroy() in qdisc_create_dflt()")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_tbf.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index b2e4b6ad241a..493270f0d5b0 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -425,12 +425,13 @@ static int tbf_init(struct Qdisc *sch, struct nlattr *opt)
 {
 	struct tbf_sched_data *q = qdisc_priv(sch);
 
+	qdisc_watchdog_init(&q->watchdog, sch);
+	q->qdisc = &noop_qdisc;
+
 	if (opt == NULL)
 		return -EINVAL;
 
 	q->t_c = ktime_get_ns();
-	qdisc_watchdog_init(&q->watchdog, sch);
-	q->qdisc = &noop_qdisc;
 
 	return tbf_change(sch, opt);
 }
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 8/9] sch_sfq: fix null pointer dereference on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

Currently only a memory allocation failure can lead to this, so let's
initialize the timer first.

Fixes: 6529eaba33f0 ("net: sched: introduce tcf block infractructure")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_sfq.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 82469ef9655e..fc69fc5956e9 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -716,13 +716,13 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
 	int i;
 	int err;
 
+	setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
+			       (unsigned long)sch);
+
 	err = tcf_block_get(&q->block, &q->filter_list);
 	if (err)
 		return err;
 
-	setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
-			       (unsigned long)sch);
-
 	for (i = 0; i < SFQ_MAX_DEPTH + 1; i++) {
 		q->dep[i].next = i + SFQ_MAX_FLOWS;
 		q->dep[i].prev = i + SFQ_MAX_FLOWS;
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 7/9] sch_netem: avoid null pointer deref on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

netem can fail in ->init due to missing options (either not supplied by
user-space or used as a default qdisc) causing a timer->base null
pointer deref in its ->destroy() and ->reset() callbacks.

Reproduce:
$ sysctl net.core.default_qdisc=netem
$ ip l set ethX up

Crash log:
[ 1814.846943] BUG: unable to handle kernel NULL pointer dereference at (null)
[ 1814.847181] IP: hrtimer_active+0x17/0x8a
[ 1814.847270] PGD 59c34067
[ 1814.847271] P4D 59c34067
[ 1814.847337] PUD 37374067
[ 1814.847403] PMD 0
[ 1814.847468]
[ 1814.847582] Oops: 0000 [#1] SMP
[ 1814.847655] Modules linked in: sch_netem(O) sch_fq_codel(O)
[ 1814.847761] CPU: 3 PID: 1573 Comm: ip Tainted: G           O 4.13.0-rc6+ #62
[ 1814.847884] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[ 1814.848043] task: ffff88003723a700 task.stack: ffff88005adc8000
[ 1814.848235] RIP: 0010:hrtimer_active+0x17/0x8a
[ 1814.848407] RSP: 0018:ffff88005adcb590 EFLAGS: 00010246
[ 1814.848590] RAX: 0000000000000000 RBX: ffff880058e359d8 RCX: 0000000000000000
[ 1814.848793] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff880058e359d8
[ 1814.848998] RBP: ffff88005adcb5b0 R08: 00000000014080c0 R09: 00000000ffffffff
[ 1814.849204] R10: ffff88005adcb660 R11: 0000000000000020 R12: 0000000000000000
[ 1814.849410] R13: ffff880058e359d8 R14: 00000000ffffffff R15: 0000000000000001
[ 1814.849616] FS:  00007f733bbca740(0000) GS:ffff88005d980000(0000) knlGS:0000000000000000
[ 1814.849919] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1814.850107] CR2: 0000000000000000 CR3: 0000000059f0d000 CR4: 00000000000406e0
[ 1814.850313] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 1814.850518] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 1814.850723] Call Trace:
[ 1814.850875]  hrtimer_try_to_cancel+0x1a/0x93
[ 1814.851047]  hrtimer_cancel+0x15/0x20
[ 1814.851211]  qdisc_watchdog_cancel+0x12/0x14
[ 1814.851383]  netem_reset+0xe6/0xed [sch_netem]
[ 1814.851561]  qdisc_destroy+0x8b/0xe5
[ 1814.851723]  qdisc_create_dflt+0x86/0x94
[ 1814.851890]  ? dev_activate+0x129/0x129
[ 1814.852057]  attach_one_default_qdisc+0x36/0x63
[ 1814.852232]  netdev_for_each_tx_queue+0x3d/0x48
[ 1814.852406]  dev_activate+0x4b/0x129
[ 1814.852569]  __dev_open+0xe7/0x104
[ 1814.852730]  __dev_change_flags+0xc6/0x15c
[ 1814.852899]  dev_change_flags+0x25/0x59
[ 1814.853064]  do_setlink+0x30c/0xb3f
[ 1814.853228]  ? check_chain_key+0xb0/0xfd
[ 1814.853396]  ? check_chain_key+0xb0/0xfd
[ 1814.853565]  rtnl_newlink+0x3a4/0x729
[ 1814.853728]  ? rtnl_newlink+0x117/0x729
[ 1814.853905]  ? ns_capable_common+0xd/0xb1
[ 1814.854072]  ? ns_capable+0x13/0x15
[ 1814.854234]  rtnetlink_rcv_msg+0x188/0x197
[ 1814.854404]  ? rcu_read_unlock+0x3e/0x5f
[ 1814.854572]  ? rtnl_newlink+0x729/0x729
[ 1814.854737]  netlink_rcv_skb+0x6c/0xce
[ 1814.854902]  rtnetlink_rcv+0x23/0x2a
[ 1814.855064]  netlink_unicast+0x103/0x181
[ 1814.855230]  netlink_sendmsg+0x326/0x337
[ 1814.855398]  sock_sendmsg_nosec+0x14/0x3f
[ 1814.855584]  sock_sendmsg+0x29/0x2e
[ 1814.855747]  ___sys_sendmsg+0x209/0x28b
[ 1814.855912]  ? do_raw_spin_unlock+0xcd/0xf8
[ 1814.856082]  ? _raw_spin_unlock+0x27/0x31
[ 1814.856251]  ? __handle_mm_fault+0x651/0xdb1
[ 1814.856421]  ? check_chain_key+0xb0/0xfd
[ 1814.856592]  __sys_sendmsg+0x45/0x63
[ 1814.856755]  ? __sys_sendmsg+0x45/0x63
[ 1814.856923]  SyS_sendmsg+0x19/0x1b
[ 1814.857083]  entry_SYSCALL_64_fastpath+0x23/0xc2
[ 1814.857256] RIP: 0033:0x7f733b2dd690
[ 1814.857419] RSP: 002b:00007ffe1d3387d8 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[ 1814.858238] RAX: ffffffffffffffda RBX: ffffffff810d278c RCX: 00007f733b2dd690
[ 1814.858445] RDX: 0000000000000000 RSI: 00007ffe1d338820 RDI: 0000000000000003
[ 1814.858651] RBP: ffff88005adcbf98 R08: 0000000000000001 R09: 0000000000000003
[ 1814.858856] R10: 00007ffe1d3385a0 R11: 0000000000000246 R12: 0000000000000002
[ 1814.859060] R13: 000000000066f1a0 R14: 00007ffe1d3408d0 R15: 0000000000000000
[ 1814.859267]  ? trace_hardirqs_off_caller+0xa7/0xcf
[ 1814.859446] Code: 10 55 48 89 c7 48 89 e5 e8 45 a1 fb ff 31 c0 5d c3
31 c0 c3 66 66 66 66 90 55 48 89 e5 41 56 41 55 41 54 53 49 89 fd 49 8b
45 30 <4c> 8b 20 41 8b 5c 24 38 31 c9 31 d2 48 c7 c7 50 8e 1d 82 41 89
[ 1814.860022] RIP: hrtimer_active+0x17/0x8a RSP: ffff88005adcb590
[ 1814.860214] CR2: 0000000000000000

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Fixes: 0fbbeb1ba43b ("[PKT_SCHED]: Fix missing qdisc_destroy() in qdisc_create_dflt()")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_netem.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 1b3dd6190e93..14d1724e0dc4 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -933,11 +933,11 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt)
 	struct netem_sched_data *q = qdisc_priv(sch);
 	int ret;
 
+	qdisc_watchdog_init(&q->watchdog, sch);
+
 	if (!opt)
 		return -EINVAL;
 
-	qdisc_watchdog_init(&q->watchdog, sch);
-
 	q->loss_model = CLG_RANDOM;
 	ret = netem_change(sch, opt);
 	if (ret)
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 6/9] sch_fq_codel: avoid double free on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

It is very unlikely to happen but the backlogs memory allocation
could fail and will free q->flows, but then ->destroy() will free
q->flows too. For correctness remove the first free and let ->destroy
clean up.

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_fq_codel.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 337f2d6d81e4..2c0c05f2cc34 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -491,10 +491,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
 		if (!q->flows)
 			return -ENOMEM;
 		q->backlogs = kvzalloc(q->flows_cnt * sizeof(u32), GFP_KERNEL);
-		if (!q->backlogs) {
-			kvfree(q->flows);
+		if (!q->backlogs)
 			return -ENOMEM;
-		}
 		for (i = 0; i < q->flows_cnt; i++) {
 			struct fq_codel_flow *flow = q->flows + i;
 
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 4/9] sch_hfsc: fix null pointer deref and double free on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:49 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

Depending on where ->init fails we can get a null pointer deref due to
uninitialized hires timer (watchdog) or a double free of the qdisc hash
because it is already freed by ->destroy().

Fixes: 8d5537387505 ("net/sched/hfsc: allocate tcf block for hfsc root class")
Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_hfsc.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index fd15200f8627..11ab8dace901 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -1418,6 +1418,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
 	struct tc_hfsc_qopt *qopt;
 	int err;
 
+	qdisc_watchdog_init(&q->watchdog, sch);
+
 	if (opt == NULL || nla_len(opt) < sizeof(*qopt))
 		return -EINVAL;
 	qopt = nla_data(opt);
@@ -1430,7 +1432,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
 
 	err = tcf_block_get(&q->root.block, &q->root.filter_list);
 	if (err)
-		goto err_tcf;
+		return err;
 
 	q->root.cl_common.classid = sch->handle;
 	q->root.refcnt  = 1;
@@ -1448,13 +1450,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
 	qdisc_class_hash_insert(&q->clhash, &q->root.cl_common);
 	qdisc_class_hash_grow(sch, &q->clhash);
 
-	qdisc_watchdog_init(&q->watchdog, sch);
-
 	return 0;
-
-err_tcf:
-	qdisc_class_hash_destroy(&q->clhash);
-	return err;
 }
 
 static int
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 3/9] sch_hhf: fix null pointer dereference on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:48 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

If sch_hhf fails in its ->init() function (either due to wrong
user-space arguments as below or memory alloc failure of hh_flows) it
will do a null pointer deref of q->hh_flows in its ->destroy() function.

To reproduce the crash:
$ tc qdisc add dev eth0 root hhf quantum 2000000 non_hh_weight 10000000

Crash log:
[  690.654882] BUG: unable to handle kernel NULL pointer dereference at (null)
[  690.655565] IP: hhf_destroy+0x48/0xbc
[  690.655944] PGD 37345067
[  690.655948] P4D 37345067
[  690.656252] PUD 58402067
[  690.656554] PMD 0
[  690.656857]
[  690.657362] Oops: 0000 [#1] SMP
[  690.657696] Modules linked in:
[  690.658032] CPU: 3 PID: 920 Comm: tc Not tainted 4.13.0-rc6+ #57
[  690.658525] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[  690.659255] task: ffff880058578000 task.stack: ffff88005acbc000
[  690.659747] RIP: 0010:hhf_destroy+0x48/0xbc
[  690.660146] RSP: 0018:ffff88005acbf9e0 EFLAGS: 00010246
[  690.660601] RAX: 0000000000000000 RBX: 0000000000000020 RCX: 0000000000000000
[  690.661155] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffffffff821f63f0
[  690.661710] RBP: ffff88005acbfa08 R08: ffffffff81b10a90 R09: 0000000000000000
[  690.662267] R10: 00000000f42b7019 R11: ffff880058578000 R12: 00000000ffffffea
[  690.662820] R13: ffff8800372f6400 R14: 0000000000000000 R15: 0000000000000000
[  690.663769] FS:  00007f8ae5e8b740(0000) GS:ffff88005d980000(0000) knlGS:0000000000000000
[  690.667069] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  690.667965] CR2: 0000000000000000 CR3: 0000000058523000 CR4: 00000000000406e0
[  690.668918] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  690.669945] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  690.671003] Call Trace:
[  690.671743]  qdisc_create+0x377/0x3fd
[  690.672534]  tc_modify_qdisc+0x4d2/0x4fd
[  690.673324]  rtnetlink_rcv_msg+0x188/0x197
[  690.674204]  ? rcu_read_unlock+0x3e/0x5f
[  690.675091]  ? rtnl_newlink+0x729/0x729
[  690.675877]  netlink_rcv_skb+0x6c/0xce
[  690.676648]  rtnetlink_rcv+0x23/0x2a
[  690.677405]  netlink_unicast+0x103/0x181
[  690.678179]  netlink_sendmsg+0x326/0x337
[  690.678958]  sock_sendmsg_nosec+0x14/0x3f
[  690.679743]  sock_sendmsg+0x29/0x2e
[  690.680506]  ___sys_sendmsg+0x209/0x28b
[  690.681283]  ? __handle_mm_fault+0xc7d/0xdb1
[  690.681915]  ? check_chain_key+0xb0/0xfd
[  690.682449]  __sys_sendmsg+0x45/0x63
[  690.682954]  ? __sys_sendmsg+0x45/0x63
[  690.683471]  SyS_sendmsg+0x19/0x1b
[  690.683974]  entry_SYSCALL_64_fastpath+0x23/0xc2
[  690.684516] RIP: 0033:0x7f8ae529d690
[  690.685016] RSP: 002b:00007fff26d2d6b8 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[  690.685931] RAX: ffffffffffffffda RBX: ffffffff810d278c RCX: 00007f8ae529d690
[  690.686573] RDX: 0000000000000000 RSI: 00007fff26d2d700 RDI: 0000000000000003
[  690.687047] RBP: ffff88005acbff98 R08: 0000000000000001 R09: 0000000000000000
[  690.687519] R10: 00007fff26d2d480 R11: 0000000000000246 R12: 0000000000000002
[  690.687996] R13: 0000000001258070 R14: 0000000000000001 R15: 0000000000000000
[  690.688475]  ? trace_hardirqs_off_caller+0xa7/0xcf
[  690.688887] Code: 00 00 e8 2a 02 ae ff 49 8b bc 1d 60 02 00 00 48 83
c3 08 e8 19 02 ae ff 48 83 fb 20 75 dc 45 31 f6 4d 89 f7 4d 03 bd 20 02
00 00 <49> 8b 07 49 39 c7 75 24 49 83 c6 10 49 81 fe 00 40 00 00 75 e1
[  690.690200] RIP: hhf_destroy+0x48/0xbc RSP: ffff88005acbf9e0
[  690.690636] CR2: 0000000000000000

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Fixes: 10239edf86f1 ("net-qdisc-hhf: Heavy-Hitter Filter (HHF) qdisc")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_hhf.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index 51d3ba682af9..73a53c08091b 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -477,6 +477,9 @@ static void hhf_destroy(struct Qdisc *sch)
 		kvfree(q->hhf_valid_bits[i]);
 	}
 
+	if (!q->hh_flows)
+		return;
+
 	for (i = 0; i < HH_FLOWS_CNT; i++) {
 		struct hh_flow_state *flow, *next;
 		struct list_head *head = &q->hh_flows[i];
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 2/9] sch_multiq: fix double free on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:48 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

The below commit added a call to ->destroy() on init failure, but multiq
still frees ->queues on error in init, but ->queues is also freed by
->destroy() thus we get double free and corrupted memory.

Very easy to reproduce (eth0 not multiqueue):
$ tc qdisc add dev eth0 root multiq
RTNETLINK answers: Operation not supported
$ ip l add dumdum type dummy
(crash)

Trace log:
[ 3929.467747] general protection fault: 0000 [#1] SMP
[ 3929.468083] Modules linked in:
[ 3929.468302] CPU: 3 PID: 967 Comm: ip Not tainted 4.13.0-rc6+ #56
[ 3929.468625] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[ 3929.469124] task: ffff88003716a700 task.stack: ffff88005872c000
[ 3929.469449] RIP: 0010:__kmalloc_track_caller+0x117/0x1be
[ 3929.469746] RSP: 0018:ffff88005872f6a0 EFLAGS: 00010246
[ 3929.470042] RAX: 00000000000002de RBX: 0000000058a59000 RCX: 00000000000002df
[ 3929.470406] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff821f7020
[ 3929.470770] RBP: ffff88005872f6e8 R08: 000000000001f010 R09: 0000000000000000
[ 3929.471133] R10: ffff88005872f730 R11: 0000000000008cdd R12: ff006d75646d7564
[ 3929.471496] R13: 00000000014000c0 R14: ffff88005b403c00 R15: ffff88005b403c00
[ 3929.471869] FS:  00007f0b70480740(0000) GS:ffff88005d980000(0000) knlGS:0000000000000000
[ 3929.472286] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 3929.472677] CR2: 00007ffcee4f3000 CR3: 0000000059d45000 CR4: 00000000000406e0
[ 3929.473209] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 3929.474109] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 3929.474873] Call Trace:
[ 3929.475337]  ? kstrdup_const+0x23/0x25
[ 3929.475863]  kstrdup+0x2e/0x4b
[ 3929.476338]  kstrdup_const+0x23/0x25
[ 3929.478084]  __kernfs_new_node+0x28/0xbc
[ 3929.478478]  kernfs_new_node+0x35/0x55
[ 3929.478929]  kernfs_create_link+0x23/0x76
[ 3929.479478]  sysfs_do_create_link_sd.isra.2+0x85/0xd7
[ 3929.480096]  sysfs_create_link+0x33/0x35
[ 3929.480649]  device_add+0x200/0x589
[ 3929.481184]  netdev_register_kobject+0x7c/0x12f
[ 3929.481711]  register_netdevice+0x373/0x471
[ 3929.482174]  rtnl_newlink+0x614/0x729
[ 3929.482610]  ? rtnl_newlink+0x17f/0x729
[ 3929.483080]  rtnetlink_rcv_msg+0x188/0x197
[ 3929.483533]  ? rcu_read_unlock+0x3e/0x5f
[ 3929.483984]  ? rtnl_newlink+0x729/0x729
[ 3929.484420]  netlink_rcv_skb+0x6c/0xce
[ 3929.484858]  rtnetlink_rcv+0x23/0x2a
[ 3929.485291]  netlink_unicast+0x103/0x181
[ 3929.485735]  netlink_sendmsg+0x326/0x337
[ 3929.486181]  sock_sendmsg_nosec+0x14/0x3f
[ 3929.486614]  sock_sendmsg+0x29/0x2e
[ 3929.486973]  ___sys_sendmsg+0x209/0x28b
[ 3929.487340]  ? do_raw_spin_unlock+0xcd/0xf8
[ 3929.487719]  ? _raw_spin_unlock+0x27/0x31
[ 3929.488092]  ? __handle_mm_fault+0x651/0xdb1
[ 3929.488471]  ? check_chain_key+0xb0/0xfd
[ 3929.488847]  __sys_sendmsg+0x45/0x63
[ 3929.489206]  ? __sys_sendmsg+0x45/0x63
[ 3929.489576]  SyS_sendmsg+0x19/0x1b
[ 3929.489901]  entry_SYSCALL_64_fastpath+0x23/0xc2
[ 3929.490172] RIP: 0033:0x7f0b6fb93690
[ 3929.490423] RSP: 002b:00007ffcee4ed588 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[ 3929.490881] RAX: ffffffffffffffda RBX: ffffffff810d278c RCX: 00007f0b6fb93690
[ 3929.491198] RDX: 0000000000000000 RSI: 00007ffcee4ed5d0 RDI: 0000000000000003
[ 3929.491521] RBP: ffff88005872ff98 R08: 0000000000000001 R09: 0000000000000000
[ 3929.491801] R10: 00007ffcee4ed350 R11: 0000000000000246 R12: 0000000000000002
[ 3929.492075] R13: 000000000066f1a0 R14: 00007ffcee4f5680 R15: 0000000000000000
[ 3929.492352]  ? trace_hardirqs_off_caller+0xa7/0xcf
[ 3929.492590] Code: 8b 45 c0 48 8b 45 b8 74 17 48 8b 4d c8 83 ca ff 44
89 ee 4c 89 f7 e8 83 ca ff ff 49 89 c4 eb 49 49 63 56 20 48 8d 48 01 4d
8b 06 <49> 8b 1c 14 48 89 c2 4c 89 e0 65 49 0f c7 08 0f 94 c0 83 f0 01
[ 3929.493335] RIP: __kmalloc_track_caller+0x117/0x1be RSP: ffff88005872f6a0

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Fixes: f07d1501292b ("multiq: Further multiqueue cleanup")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_multiq.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index f143b7bbaa0d..9c454f5d6c38 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -257,12 +257,7 @@ static int multiq_init(struct Qdisc *sch, struct nlattr *opt)
 	for (i = 0; i < q->max_bands; i++)
 		q->queues[i] = &noop_qdisc;
 
-	err = multiq_tune(sch, opt);
-
-	if (err)
-		kfree(q->queues);
-
-	return err;
+	return multiq_tune(sch, opt);
 }
 
 static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb)
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 1/9] sch_htb: fix crash on init failure
From: Nikolay Aleksandrov @ 2017-08-30  9:48 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov
In-Reply-To: <1504086545-7777-1-git-send-email-nikolay@cumulusnetworks.com>

The commit below added a call to the ->destroy() callback for all qdiscs
which failed in their ->init(), but some were not prepared for such
change and can't handle partially initialized qdisc. HTB is one of them
and if any error occurs before the qdisc watchdog timer and qdisc work are
initialized then we can hit either a null ptr deref (timer->base) when
canceling in ->destroy or lockdep error info about trying to register
a non-static key and a stack dump. So to fix these two move the watchdog
timer and workqueue init before anything that can err out.
To reproduce userspace needs to send broken htb qdisc create request,
tested with a modified tc (q_htb.c).

Trace log:
[ 2710.897602] BUG: unable to handle kernel NULL pointer dereference at (null)
[ 2710.897977] IP: hrtimer_active+0x17/0x8a
[ 2710.898174] PGD 58fab067
[ 2710.898175] P4D 58fab067
[ 2710.898353] PUD 586c0067
[ 2710.898531] PMD 0
[ 2710.898710]
[ 2710.899045] Oops: 0000 [#1] SMP
[ 2710.899232] Modules linked in:
[ 2710.899419] CPU: 1 PID: 950 Comm: tc Not tainted 4.13.0-rc6+ #54
[ 2710.899646] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014
[ 2710.900035] task: ffff880059ed2700 task.stack: ffff88005ad4c000
[ 2710.900262] RIP: 0010:hrtimer_active+0x17/0x8a
[ 2710.900467] RSP: 0018:ffff88005ad4f960 EFLAGS: 00010246
[ 2710.900684] RAX: 0000000000000000 RBX: ffff88003701e298 RCX: 0000000000000000
[ 2710.900933] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88003701e298
[ 2710.901177] RBP: ffff88005ad4f980 R08: 0000000000000001 R09: 0000000000000001
[ 2710.901419] R10: ffff88005ad4f800 R11: 0000000000000400 R12: 0000000000000000
[ 2710.901663] R13: ffff88003701e298 R14: ffffffff822a4540 R15: ffff88005ad4fac0
[ 2710.901907] FS:  00007f2f5e90f740(0000) GS:ffff88005d880000(0000) knlGS:0000000000000000
[ 2710.902277] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2710.902500] CR2: 0000000000000000 CR3: 0000000058ca3000 CR4: 00000000000406e0
[ 2710.902744] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 2710.902977] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 2710.903180] Call Trace:
[ 2710.903332]  hrtimer_try_to_cancel+0x1a/0x93
[ 2710.903504]  hrtimer_cancel+0x15/0x20
[ 2710.903667]  qdisc_watchdog_cancel+0x12/0x14
[ 2710.903866]  htb_destroy+0x2e/0xf7
[ 2710.904097]  qdisc_create+0x377/0x3fd
[ 2710.904330]  tc_modify_qdisc+0x4d2/0x4fd
[ 2710.904511]  rtnetlink_rcv_msg+0x188/0x197
[ 2710.904682]  ? rcu_read_unlock+0x3e/0x5f
[ 2710.904849]  ? rtnl_newlink+0x729/0x729
[ 2710.905017]  netlink_rcv_skb+0x6c/0xce
[ 2710.905183]  rtnetlink_rcv+0x23/0x2a
[ 2710.905345]  netlink_unicast+0x103/0x181
[ 2710.905511]  netlink_sendmsg+0x326/0x337
[ 2710.905679]  sock_sendmsg_nosec+0x14/0x3f
[ 2710.905847]  sock_sendmsg+0x29/0x2e
[ 2710.906010]  ___sys_sendmsg+0x209/0x28b
[ 2710.906176]  ? do_raw_spin_unlock+0xcd/0xf8
[ 2710.906346]  ? _raw_spin_unlock+0x27/0x31
[ 2710.906514]  ? __handle_mm_fault+0x651/0xdb1
[ 2710.906685]  ? check_chain_key+0xb0/0xfd
[ 2710.906855]  __sys_sendmsg+0x45/0x63
[ 2710.907018]  ? __sys_sendmsg+0x45/0x63
[ 2710.907185]  SyS_sendmsg+0x19/0x1b
[ 2710.907344]  entry_SYSCALL_64_fastpath+0x23/0xc2

Note that probably this bug goes further back because the default qdisc
handling always calls ->destroy on init failure too.

Fixes: 87b60cfacf9f ("net_sched: fix error recovery at qdisc creation")
Fixes: 0fbbeb1ba43b ("[PKT_SCHED]: Fix missing qdisc_destroy() in qdisc_create_dflt()")
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
 net/sched/sch_htb.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 5d65ec5207e9..5bf5177b2bd3 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -1017,6 +1017,9 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
 	int err;
 	int i;
 
+	qdisc_watchdog_init(&q->watchdog, sch);
+	INIT_WORK(&q->work, htb_work_func);
+
 	if (!opt)
 		return -EINVAL;
 
@@ -1041,8 +1044,6 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
 	for (i = 0; i < TC_HTB_NUMPRIO; i++)
 		INIT_LIST_HEAD(q->drops + i);
 
-	qdisc_watchdog_init(&q->watchdog, sch);
-	INIT_WORK(&q->work, htb_work_func);
 	qdisc_skb_head_init(&q->direct_queue);
 
 	if (tb[TCA_HTB_DIRECT_QLEN])
-- 
2.1.4

^ permalink raw reply related

* [PATCH net 0/9] net/sched: init failure fixes
From: Nikolay Aleksandrov @ 2017-08-30  9:48 UTC (permalink / raw)
  To: netdev; +Cc: edumazet, jhs, xiyou.wangcong, jiri, roopa, Nikolay Aleksandrov

Hi all,
I went over all qdiscs' init, destroy and reset callbacks and found the
issues fixed in each patch. Mostly they are null pointer dereferences due
to uninitialized timer (qdisc watchdog) or double frees due to ->destroy
cleaning up a second time. There's more information in each patch.
I've tested these by either sending wrong attributes from user-spaces, no
attributes or by simulating memory alloc failure where applicable. Also
tried all of the qdiscs as a default qdisc.

Most of these bugs were present before commit 87b60cfacf9f, I've tried to
include proper fixes tags in each patch.

I haven't included individual patch acks in the set, I'd appreciate it if
you take another look and resend them.

Thanks,
 Nik

Nikolay Aleksandrov (9):
  sch_htb: fix crash on init failure
  sch_multiq: fix double free on init failure
  sch_hhf: fix null pointer dereference on init failure
  sch_hfsc: fix null pointer deref and double free on init failure
  sch_cbq: fix null pointer dereferences on init failure
  sch_fq_codel: avoid double free on init failure
  sch_netem: avoid null pointer deref on init failure
  sch_sfq: fix null pointer dereference on init failure
  sch_tbf: fix two null pointer dereferences on init failure

 net/sched/sch_cbq.c      | 10 +++++++---
 net/sched/sch_fq_codel.c |  4 +---
 net/sched/sch_hfsc.c     | 10 +++-------
 net/sched/sch_hhf.c      |  3 +++
 net/sched/sch_htb.c      |  5 +++--
 net/sched/sch_multiq.c   |  7 +------
 net/sched/sch_netem.c    |  4 ++--
 net/sched/sch_sfq.c      |  6 +++---
 net/sched/sch_tbf.c      |  5 +++--
 9 files changed, 26 insertions(+), 28 deletions(-)

-- 
2.1.4

^ permalink raw reply

* Re: [GIT] Networking
From: Pavel Machek @ 2017-08-30  9:48 UTC (permalink / raw)
  To: David Miller, kvalo, xiyou.wangcong; +Cc: torvalds, akpm, netdev, linux-kernel
In-Reply-To: <20170815.175219.753462032002961032.davem@davemloft.net>

[-- Attachment #1: Type: text/plain, Size: 400 bytes --]

Hi!

Could we get this one in?

wl1251 misses a spin_lock_init().

https://www.mail-archive.com/netdev@vger.kernel.org/msg177031.html

It seems pretty trivial, yet getting the backtraces is not nice.

Thanks,
   	 		     	     	 	       	   Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply

* [PATCH] net: bcm63xx_enet: make bcm_enetsw_ethtool_ops const
From: Bhumika Goyal @ 2017-08-30  9:25 UTC (permalink / raw)
  To: julia.lawall, f.fainelli, bcm-kernel-feedback-list, netdev,
	linux-arm-kernel, linux-kernel
  Cc: Bhumika Goyal

Make this const as it is never modified.

Signed-off-by: Bhumika Goyal <bhumirks@gmail.com>
---
 drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index 61a88b6..4f3845a 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -2674,7 +2674,7 @@ static int bcm_enetsw_set_ringparam(struct net_device *dev,
 	return 0;
 }
 
-static struct ethtool_ops bcm_enetsw_ethtool_ops = {
+static const struct ethtool_ops bcm_enetsw_ethtool_ops = {
 	.get_strings		= bcm_enetsw_get_strings,
 	.get_sset_count		= bcm_enetsw_get_sset_count,
 	.get_ethtool_stats      = bcm_enetsw_get_ethtool_stats,
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v3 3/6] dt: booting-without-of: DT fix s/#interrupt-cell/#interrupt-cells/
From: Michael Ellerman @ 2017-08-30  9:20 UTC (permalink / raw)
  To: Geert Uytterhoeven, David Airlie, Rob Herring, Mark Rutland,
	Carlo Caione, Kevin Hilman, Chanho Min, Catalin Marinas,
	Will Deacon
  Cc: Neil Armstrong, Benjamin Herrenschmidt, Paul Mackerras, dri-devel,
	linux-amlogic, devicetree, linux-arm-kernel, linux-kernel, netdev,
	linuxppc-dev, Geert Uytterhoeven
In-Reply-To: <1496407129-13527-4-git-send-email-geert+renesas@glider.be>

Geert Uytterhoeven <geert+renesas@glider.be> writes:

> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Acked-by: Rob Herring <robh@kernel.org>
> ---

Rob this has your ack, but I'd expect it to go via your tree? Or should
I grab it?

cheers

> diff --git a/Documentation/devicetree/booting-without-of.txt b/Documentation/devicetree/booting-without-of.txt
> index 280d283304bb82d8..f35d3adacb987f7d 100644
> --- a/Documentation/devicetree/booting-without-of.txt
> +++ b/Documentation/devicetree/booting-without-of.txt
> @@ -1309,7 +1309,7 @@ number and level/sense information. All interrupt children in an
>  OpenPIC interrupt domain use 2 cells per interrupt in their interrupts
>  property.
>  
> -The PCI bus binding specifies a #interrupt-cell value of 1 to encode
> +The PCI bus binding specifies a #interrupt-cells value of 1 to encode
>  which interrupt pin (INTA,INTB,INTC,INTD) is used.
>  
>  2) interrupt-parent property
> -- 
> 2.7.4

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox