Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Jarod Wilson @ 2016-10-19 14:17 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: linux-kernel, netdev, virtualization, K. Y. Srinivasan,
	Haiyang Zhang, Shrikrishna Khare, VMware, Inc.
In-Reply-To: <20161019170330-mutt-send-email-mst@kernel.org>

On Wed, Oct 19, 2016 at 05:03:43PM +0300, Michael S. Tsirkin wrote:
> On Wed, Oct 19, 2016 at 04:59:46PM +0300, Michael S. Tsirkin wrote:
> > On Tue, Oct 18, 2016 at 10:33:32PM -0400, Jarod Wilson wrote:
> > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > > index fad84f3..4885a42 100644
> > > --- a/drivers/net/virtio_net.c
> > > +++ b/drivers/net/virtio_net.c
> > > @@ -1419,17 +1419,6 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
> > >  	.set_settings = virtnet_set_settings,
> > >  };
> > >  
> > > -#define MIN_MTU 68
> > > -#define MAX_MTU 65535
> > > -
> > > -static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
> > > -{
> > > -	if (new_mtu < MIN_MTU || new_mtu > MAX_MTU)
> > > -		return -EINVAL;
> > > -	dev->mtu = new_mtu;
> > > -	return 0;
> > > -}
> > > -
> > >  static const struct net_device_ops virtnet_netdev = {
> > >  	.ndo_open            = virtnet_open,
> > >  	.ndo_stop   	     = virtnet_close,
> > > @@ -1437,7 +1426,6 @@ static const struct net_device_ops virtnet_netdev = {
> > >  	.ndo_validate_addr   = eth_validate_addr,
> > >  	.ndo_set_mac_address = virtnet_set_mac_address,
> > >  	.ndo_set_rx_mode     = virtnet_set_rx_mode,
> > > -	.ndo_change_mtu	     = virtnet_change_mtu,
> > >  	.ndo_get_stats64     = virtnet_stats,
> > >  	.ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
> > >  	.ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
> > > @@ -1748,6 +1736,9 @@ static bool virtnet_validate_features(struct virtio_device *vdev)
> > >  	return true;
> > >  }
> > >  
> > > +#define MIN_MTU ETH_MIN_MTU
> > > +#define MAX_MTU 65535
> > > +
> > 
> > Do we need a common macro for this?
> 
> I think it's actually IP_MAX_MTU.

Ah, yes, it is. I'm not sure why I didn't use that...

-- 
Jarod Wilson
jarod@redhat.com

^ permalink raw reply

* [PATCH net] net: core: Correctly iterate over lower adjacency list
From: idosch @ 2016-10-19 13:57 UTC (permalink / raw)
  To: davem; +Cc: netdev, jiri, eladr, yotamg, nogahf, ogerlitz, dsa, Ido Schimmel

From: Ido Schimmel <idosch@mellanox.com>

Tamir reported the following trace when processing ARP requests received
via a vlan device on top of a VLAN-aware bridge:

 NMI watchdog: BUG: soft lockup - CPU#1 stuck for 22s! [swapper/1:0]
[...]
 CPU: 1 PID: 0 Comm: swapper/1 Tainted: G        W       4.8.0-rc7 #1
 Hardware name: Mellanox Technologies Ltd. "MSN2100-CB2F"/"SA001017", BIOS 5.6.5 06/07/2016
 task: ffff88017edfea40 task.stack: ffff88017ee10000
 RIP: 0010:[<ffffffff815dcc73>]  [<ffffffff815dcc73>] netdev_all_lower_get_next_rcu+0x33/0x60
[...]
 Call Trace:
  <IRQ>
  [<ffffffffa015de0a>] mlxsw_sp_port_lower_dev_hold+0x5a/0xa0 [mlxsw_spectrum]
  [<ffffffffa016f1b0>] mlxsw_sp_router_netevent_event+0x80/0x150 [mlxsw_spectrum]
  [<ffffffff810ad07a>] notifier_call_chain+0x4a/0x70
  [<ffffffff810ad13a>] atomic_notifier_call_chain+0x1a/0x20
  [<ffffffff815ee77b>] call_netevent_notifiers+0x1b/0x20
  [<ffffffff815f2eb6>] neigh_update+0x306/0x740
  [<ffffffff815f38ce>] neigh_event_ns+0x4e/0xb0
  [<ffffffff8165ea3f>] arp_process+0x66f/0x700
  [<ffffffff8170214c>] ? common_interrupt+0x8c/0x8c
  [<ffffffff8165ec29>] arp_rcv+0x139/0x1d0
  [<ffffffff816e505a>] ? vlan_do_receive+0xda/0x320
  [<ffffffff815e3794>] __netif_receive_skb_core+0x524/0xab0
  [<ffffffff815e6830>] ? dev_queue_xmit+0x10/0x20
  [<ffffffffa06d612d>] ? br_forward_finish+0x3d/0xc0 [bridge]
  [<ffffffffa06e5796>] ? br_handle_vlan+0xf6/0x1b0 [bridge]
  [<ffffffff815e3d38>] __netif_receive_skb+0x18/0x60
  [<ffffffff815e3dc0>] netif_receive_skb_internal+0x40/0xb0
  [<ffffffff815e3e4c>] netif_receive_skb+0x1c/0x70
  [<ffffffffa06d7856>] br_pass_frame_up+0xc6/0x160 [bridge]
  [<ffffffffa06d63d7>] ? deliver_clone+0x37/0x50 [bridge]
  [<ffffffffa06d656c>] ? br_flood+0xcc/0x160 [bridge]
  [<ffffffffa06d7b14>] br_handle_frame_finish+0x224/0x4f0 [bridge]
  [<ffffffffa06d7f94>] br_handle_frame+0x174/0x300 [bridge]
  [<ffffffff815e3599>] __netif_receive_skb_core+0x329/0xab0
  [<ffffffff81374815>] ? find_next_bit+0x15/0x20
  [<ffffffff8135e802>] ? cpumask_next_and+0x32/0x50
  [<ffffffff810c9968>] ? load_balance+0x178/0x9b0
  [<ffffffff815e3d38>] __netif_receive_skb+0x18/0x60
  [<ffffffff815e3dc0>] netif_receive_skb_internal+0x40/0xb0
  [<ffffffff815e3e4c>] netif_receive_skb+0x1c/0x70
  [<ffffffffa01544a1>] mlxsw_sp_rx_listener_func+0x61/0xb0 [mlxsw_spectrum]
  [<ffffffffa005c9f7>] mlxsw_core_skb_receive+0x187/0x200 [mlxsw_core]
  [<ffffffffa007332a>] mlxsw_pci_cq_tasklet+0x63a/0x9b0 [mlxsw_pci]
  [<ffffffff81091986>] tasklet_action+0xf6/0x110
  [<ffffffff81704556>] __do_softirq+0xf6/0x280
  [<ffffffff8109213f>] irq_exit+0xdf/0xf0
  [<ffffffff817042b4>] do_IRQ+0x54/0xd0
  [<ffffffff8170214c>] common_interrupt+0x8c/0x8c

The problem is that netdev_all_lower_get_next_rcu() never advances the
iterator, thereby causing the loop over the lower adjacency list to run
forever.

Fix this by advancing the iterator and avoid the infinite loop.

Fixes: 7ce856aaaf13 ("mlxsw: spectrum: Add couple of lower device helper functions")
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reported-by: Tamir Winetroub <tamirw@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
---
Note that DavidA's series removes this code in net-next, but it wasn't
picked up for net, so I'm sending this patch.
Please consider queueing this for 4.8.y
---
 include/linux/netdevice.h |  2 +-
 net/core/dev.c            | 10 +++++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 136ae6bb..465e128 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3877,7 +3877,7 @@ struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev,
 	     ldev = netdev_all_lower_get_next(dev, &(iter)))
 
 #define netdev_for_each_all_lower_dev_rcu(dev, ldev, iter) \
-	for (iter = (dev)->all_adj_list.lower.next, \
+	for (iter = &(dev)->all_adj_list.lower, \
 	     ldev = netdev_all_lower_get_next_rcu(dev, &(iter)); \
 	     ldev; \
 	     ldev = netdev_all_lower_get_next_rcu(dev, &(iter)))
diff --git a/net/core/dev.c b/net/core/dev.c
index f1fe26f..b09ac57 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5511,10 +5511,14 @@ struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev,
 {
 	struct netdev_adjacent *lower;
 
-	lower = list_first_or_null_rcu(&dev->all_adj_list.lower,
-				       struct netdev_adjacent, list);
+	lower = list_entry_rcu((*iter)->next, struct netdev_adjacent, list);
+
+	if (&lower->list == &dev->all_adj_list.lower)
+		return NULL;
+
+	*iter = &lower->list;
 
-	return lower ? lower->dev : NULL;
+	return lower->dev;
 }
 EXPORT_SYMBOL(netdev_all_lower_get_next_rcu);
 
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Jarod Wilson @ 2016-10-19 14:15 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: linux-kernel, netdev, virtualization, K. Y. Srinivasan,
	Haiyang Zhang, Shrikrishna Khare, VMware, Inc.
In-Reply-To: <20161019165629-mutt-send-email-mst@kernel.org>

On Wed, Oct 19, 2016 at 04:59:46PM +0300, Michael S. Tsirkin wrote:
> On Tue, Oct 18, 2016 at 10:33:32PM -0400, Jarod Wilson wrote:
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index fad84f3..4885a42 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -1419,17 +1419,6 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
> >  	.set_settings = virtnet_set_settings,
> >  };
> >  
> > -#define MIN_MTU 68
> > -#define MAX_MTU 65535
> > -
> > -static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
> > -{
> > -	if (new_mtu < MIN_MTU || new_mtu > MAX_MTU)
> > -		return -EINVAL;
> > -	dev->mtu = new_mtu;
> > -	return 0;
> > -}
> > -
> >  static const struct net_device_ops virtnet_netdev = {
> >  	.ndo_open            = virtnet_open,
> >  	.ndo_stop   	     = virtnet_close,
> > @@ -1437,7 +1426,6 @@ static const struct net_device_ops virtnet_netdev = {
> >  	.ndo_validate_addr   = eth_validate_addr,
> >  	.ndo_set_mac_address = virtnet_set_mac_address,
> >  	.ndo_set_rx_mode     = virtnet_set_rx_mode,
> > -	.ndo_change_mtu	     = virtnet_change_mtu,
> >  	.ndo_get_stats64     = virtnet_stats,
> >  	.ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
> >  	.ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
> > @@ -1748,6 +1736,9 @@ static bool virtnet_validate_features(struct virtio_device *vdev)
> >  	return true;
> >  }
> >  
> > +#define MIN_MTU ETH_MIN_MTU
> > +#define MAX_MTU 65535
> > +
> 
> Do we need a common macro for this?

Probably. That value crops up in multiple drivers.

> >  static int virtnet_probe(struct virtio_device *vdev)
> >  {
> >  	int i, err;
> > @@ -1821,6 +1812,10 @@ static int virtnet_probe(struct virtio_device *vdev)
> >  
> >  	dev->vlan_features = dev->features;
> >  
> > +	/* MTU range: 68 - 65535 */
> > +	dev->min_mtu = MIN_MTU;
> > +	dev->max_mtu = MAX_MTU;
> > +
> >  	/* Configuration may specify what MAC to use.  Otherwise random. */
> >  	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC))
> >  		virtio_cread_bytes(vdev,
> > @@ -1875,8 +1870,10 @@ static int virtnet_probe(struct virtio_device *vdev)
> >  		mtu = virtio_cread16(vdev,
> >  				     offsetof(struct virtio_net_config,
> >  					      mtu));
> > -		if (virtnet_change_mtu(dev, mtu))
> > +		if (mtu >= dev->min_mtu && mtu <= dev->max_mtu) {
> > +			dev->mtu = mtu;
> >  			__virtio_clear_bit(vdev, VIRTIO_NET_F_MTU);
> 
> I think the logic is wrong here:
> 
> If mtu is legal, we set it but do not tell host.
> If it's out of range we tell host we use it
> but don't actually.
> 
> Should be the reverse.

Ah, yes, looks like it should be:

                if (mtu < dev->min_mtu || mtu > dev->max_mtu)
                        __virtio_clear_bit(vdev, VIRTIO_NET_F_MTU);
                else
                        dev->mtu = mtu;

-- 
Jarod Wilson
jarod@redhat.com

^ permalink raw reply

* [PATCH iproute2 net-next] tc: m_mirred: Add support for ingress redirect/mirror
From: Shmulik Ladkani @ 2016-10-19 14:14 UTC (permalink / raw)
  To: Stephen Hemminger, Jamal Hadi Salim; +Cc: netdev, Shmulik Ladkani

So far, only the 'egress' direction was implemented.

Allow specifying 'ingress' as the direction packet appears on the target
interface.

For example, this takes incoming 802.1q frames on veth0 and redirects
them for input on dummy0:

 # tc filter add dev veth0 parent ffff: pref 1 protocol 802.1q basic \
     action mirred ingress redirect dev dummy0

Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
---
 man/man8/tc-mirred.8 |  4 +---
 tc/m_mirred.c        | 40 ++++++++++++++++++++++++++++------------
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/man/man8/tc-mirred.8 b/man/man8/tc-mirred.8
index bba96e0..38833b4 100644
--- a/man/man8/tc-mirred.8
+++ b/man/man8/tc-mirred.8
@@ -30,9 +30,7 @@ receives. Mirroring is what is sometimes referred to as Switch Port Analyzer
 .TQ
 .B egress
 Specify the direction in which the packet shall appear on the destination
-interface. Currently only
-.B egress
-is implemented.
+interface.
 .TP
 .B mirror
 .TQ
diff --git a/tc/m_mirred.c b/tc/m_mirred.c
index 11f4c9b..01f916d 100644
--- a/tc/m_mirred.c
+++ b/tc/m_mirred.c
@@ -62,13 +62,13 @@ static const char *mirred_n2a(int action)
 }
 
 static int
-parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
-	     int tca_id, struct nlmsghdr *n)
+parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
+		int tca_id, struct nlmsghdr *n)
 {
 
 	int argc = *argc_p;
 	char **argv = *argv_p;
-	int ok = 0, iok = 0, mirror = 0, redir = 0;
+	int ok = 0, iok = 0, mirror = 0, redir = 0, ingress = 0, egress = 0;
 	struct tc_mirred p = {};
 	struct rtattr *tail;
 	char d[16] = {};
@@ -77,7 +77,21 @@ parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
 
 		if (matches(*argv, "action") == 0) {
 			break;
-		} else if (matches(*argv, "egress") == 0) {
+		} else if (!egress && matches(*argv, "egress") == 0) {
+			egress = 1;
+			if (ingress) {
+				fprintf(stderr, "Can't have both egress and ingress\n");
+				return -1;
+			}
+			NEXT_ARG();
+			ok++;
+			continue;
+		} else if (!ingress && matches(*argv, "ingress") == 0) {
+			ingress = 1;
+			if (egress) {
+				fprintf(stderr, "Can't have both ingress and egress\n");
+				return -1;
+			}
 			NEXT_ARG();
 			ok++;
 			continue;
@@ -96,7 +110,7 @@ parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
 					break;
 				}
 			} else if (!ok) {
-				fprintf(stderr, "was expecting egress (%s)\n", *argv);
+				fprintf(stderr, "was expecting egress or ingress (%s)\n", *argv);
 				break;
 
 			} else if (!mirror && matches(*argv, "mirror") == 0) {
@@ -105,7 +119,8 @@ parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
 					fprintf(stderr, "Can't have both mirror and redir\n");
 					return -1;
 				}
-				p.eaction = TCA_EGRESS_MIRROR;
+				p.eaction = egress ? TCA_EGRESS_MIRROR :
+					    TCA_INGRESS_MIRROR;
 				p.action = TC_ACT_PIPE;
 				ok++;
 			} else if (!redir && matches(*argv, "redirect") == 0) {
@@ -114,7 +129,8 @@ parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
 					fprintf(stderr, "Can't have both mirror and redir\n");
 					return -1;
 				}
-				p.eaction = TCA_EGRESS_REDIR;
+				p.eaction = egress ? TCA_EGRESS_REDIR :
+					    TCA_INGRESS_REDIR;
 				p.action = TC_ACT_STOLEN;
 				ok++;
 			} else if ((redir || mirror) && matches(*argv, "dev") == 0) {
@@ -154,7 +170,8 @@ parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
 	}
 
 
-	if (argc && p.eaction == TCA_EGRESS_MIRROR
+	if (argc &&
+	    (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
 	    && !action_a2n(*argv, &p.action, false))
 		NEXT_ARG();
 
@@ -207,8 +224,9 @@ parse_mirred(struct action_util *a, int *argc_p, char ***argv_p,
 	}
 
 
-	if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) {
-		int ret = parse_egress(a, &argc, &argv, tca_id, n);
+	if (matches(*argv, "egress") == 0 || matches(*argv, "ingress") == 0 ||
+	    matches(*argv, "index") == 0) {
+		int ret = parse_direction(a, &argc, &argv, tca_id, n);
 
 		if (ret == 0) {
 			*argc_p = argc;
@@ -216,8 +234,6 @@ parse_mirred(struct action_util *a, int *argc_p, char ***argv_p,
 			return 0;
 		}
 
-	} else if (matches(*argv, "ingress") == 0) {
-		fprintf(stderr, "mirred ingress not supported at the moment\n");
 	} else if (matches(*argv, "help") == 0) {
 		usage();
 	} else {
-- 
2.10.1

^ permalink raw reply related

* Re: [RFC 2/6] qed: Add iSCSI out of order packet handling.
From: Hannes Reinecke @ 2016-10-19  7:36 UTC (permalink / raw)
  To: manish.rangankar, lduncan, cleech
  Cc: martin.petersen, jejb, linux-scsi, netdev, Yuval.Mintz,
	QLogic-Storage-Upstream, Yuval Mintz, Arun Easi
In-Reply-To: <1476853273-22960-3-git-send-email-manish.rangankar@cavium.com>

On 10/19/2016 07:01 AM, manish.rangankar@cavium.com wrote:
> From: Yuval Mintz <Yuval.Mintz@qlogic.com>
> 
> This patch adds out of order packet handling for hardware offloaded
> iSCSI. Out of order packet handling requires driver buffer allocation
> and assistance.
> 
> Signed-off-by: Arun Easi <arun.easi@cavium.com>
> Signed-off-by: Yuval Mintz <yuval.mintz@cavium.com>
> ---
>  drivers/net/ethernet/qlogic/qed/Makefile   |   2 +-
>  drivers/net/ethernet/qlogic/qed/qed.h      |   1 +
>  drivers/net/ethernet/qlogic/qed/qed_dev.c  |  14 +-
>  drivers/net/ethernet/qlogic/qed/qed_ll2.c  | 559 +++++++++++++++++++++++++++--
>  drivers/net/ethernet/qlogic/qed/qed_ll2.h  |   9 +
>  drivers/net/ethernet/qlogic/qed/qed_ooo.c  | 510 ++++++++++++++++++++++++++
>  drivers/net/ethernet/qlogic/qed/qed_ooo.h  | 116 ++++++
>  drivers/net/ethernet/qlogic/qed/qed_roce.c |   1 +
>  drivers/net/ethernet/qlogic/qed/qed_spq.c  |   9 +
>  9 files changed, 1195 insertions(+), 26 deletions(-)
>  create mode 100644 drivers/net/ethernet/qlogic/qed/qed_ooo.c
>  create mode 100644 drivers/net/ethernet/qlogic/qed/qed_ooo.h
> 
> diff --git a/drivers/net/ethernet/qlogic/qed/Makefile b/drivers/net/ethernet/qlogic/qed/Makefile
> index b76669c..9121bf0 100644
> --- a/drivers/net/ethernet/qlogic/qed/Makefile
> +++ b/drivers/net/ethernet/qlogic/qed/Makefile
> @@ -6,4 +6,4 @@ qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \
>  qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o
>  qed-$(CONFIG_QED_LL2) += qed_ll2.o
>  qed-$(CONFIG_INFINIBAND_QEDR) += qed_roce.o
> -qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o
> +qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o qed_ooo.o
> diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
> index a61b1c0..e5626ae 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed.h
> +++ b/drivers/net/ethernet/qlogic/qed/qed.h
> @@ -380,6 +380,7 @@ struct qed_hwfn {
>  	/* Protocol related */
>  	bool				using_ll2;
>  	struct qed_ll2_info		*p_ll2_info;
> +	struct qed_ooo_info		*p_ooo_info;
>  	struct qed_rdma_info		*p_rdma_info;
>  	struct qed_iscsi_info		*p_iscsi_info;
>  	struct qed_pf_params		pf_params;
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
> index a4234c0..060e9a4 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
> +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
> @@ -32,6 +32,7 @@
>  #include "qed_iscsi.h"
>  #include "qed_ll2.h"
>  #include "qed_mcp.h"
> +#include "qed_ooo.h"
>  #include "qed_reg_addr.h"
>  #include "qed_sp.h"
>  #include "qed_sriov.h"
> @@ -157,8 +158,10 @@ void qed_resc_free(struct qed_dev *cdev)
>  		qed_ll2_free(p_hwfn, p_hwfn->p_ll2_info);
>  #endif
>  		if (IS_ENABLED(CONFIG_QEDI) &&
> -				p_hwfn->hw_info.personality == QED_PCI_ISCSI)
> +				p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
>  			qed_iscsi_free(p_hwfn, p_hwfn->p_iscsi_info);
> +			qed_ooo_free(p_hwfn, p_hwfn->p_ooo_info);
> +		}
>  		qed_iov_free(p_hwfn);
>  		qed_dmae_info_free(p_hwfn);
>  		qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info);
> @@ -416,6 +419,7 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
>  int qed_resc_alloc(struct qed_dev *cdev)
>  {
>  	struct qed_iscsi_info *p_iscsi_info;
> +	struct qed_ooo_info *p_ooo_info;
>  #ifdef CONFIG_QED_LL2
>  	struct qed_ll2_info *p_ll2_info;
>  #endif
> @@ -543,6 +547,10 @@ int qed_resc_alloc(struct qed_dev *cdev)
>  			if (!p_iscsi_info)
>  				goto alloc_no_mem;
>  			p_hwfn->p_iscsi_info = p_iscsi_info;
> +			p_ooo_info = qed_ooo_alloc(p_hwfn);
> +			if (!p_ooo_info)
> +				goto alloc_no_mem;
> +			p_hwfn->p_ooo_info = p_ooo_info;
>  		}
>  
>  		/* DMA info initialization */
> @@ -598,8 +606,10 @@ void qed_resc_setup(struct qed_dev *cdev)
>  			qed_ll2_setup(p_hwfn, p_hwfn->p_ll2_info);
>  #endif
>  		if (IS_ENABLED(CONFIG_QEDI) &&
> -				p_hwfn->hw_info.personality == QED_PCI_ISCSI)
> +				p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
>  			qed_iscsi_setup(p_hwfn, p_hwfn->p_iscsi_info);
> +			qed_ooo_setup(p_hwfn, p_hwfn->p_ooo_info);
> +		}
>  	}
>  }
>  
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
> index e67f3c9..4ce12e9 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c
> +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
> @@ -36,6 +36,7 @@
>  #include "qed_int.h"
>  #include "qed_ll2.h"
>  #include "qed_mcp.h"
> +#include "qed_ooo.h"
>  #include "qed_reg_addr.h"
>  #include "qed_sp.h"
>  
> @@ -295,27 +296,36 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
>  		list_del(&p_pkt->list_entry);
>  		b_last_packet = list_empty(&p_tx->active_descq);
>  		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
> -		p_tx->cur_completing_packet = *p_pkt;
> -		p_tx->cur_completing_bd_idx = 1;
> -		b_last_frag = p_tx->cur_completing_bd_idx == p_pkt->bd_used;
> -		tx_frag = p_pkt->bds_set[0].tx_frag;
> +		if (IS_ENABLED(CONFIG_QEDI) &&
> +			p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) {
> +			struct qed_ooo_buffer *p_buffer;
> +
> +			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
> +			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						p_buffer);
> +		} else {
> +			p_tx->cur_completing_packet = *p_pkt;
> +			p_tx->cur_completing_bd_idx = 1;
> +			b_last_frag = p_tx->cur_completing_bd_idx ==
> +				p_pkt->bd_used;
> +			tx_frag = p_pkt->bds_set[0].tx_frag;
>  #if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
> -		if (p_ll2_conn->gsi_enable)
> -			qed_ll2b_release_tx_gsi_packet(p_hwfn,
> -						       p_ll2_conn->my_id,
> -						       p_pkt->cookie,
> -						       tx_frag,
> -						       b_last_frag,
> -						       b_last_packet);
> -		else
> +			if (p_ll2_conn->gsi_enable)
> +				qed_ll2b_release_tx_gsi_packet(p_hwfn,
> +					       p_ll2_conn->my_id,
> +					       p_pkt->cookie,
> +					       tx_frag,
> +					       b_last_frag,
> +					       b_last_packet);
> +			else
>  #endif
> -			qed_ll2b_complete_tx_packet(p_hwfn,
> +				qed_ll2b_complete_tx_packet(p_hwfn,
>  						    p_ll2_conn->my_id,
>  						    p_pkt->cookie,
>  						    tx_frag,
>  						    b_last_frag,
>  						    b_last_packet);
> -
> +		}
>  	}
>  }
>  
> @@ -546,13 +556,466 @@ void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
>  		list_del(&p_pkt->list_entry);
>  		list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
>  
> -		rx_buf_addr = p_pkt->rx_buf_addr;
> -		cookie = p_pkt->cookie;
> +		if (IS_ENABLED(CONFIG_QEDI) &&
> +			p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) {
> +			struct qed_ooo_buffer *p_buffer;
> +
> +			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
> +			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						p_buffer);
> +		} else {
> +			rx_buf_addr = p_pkt->rx_buf_addr;
> +			cookie = p_pkt->cookie;
> +
> +			b_last = list_empty(&p_rx->active_descq);
> +		}
> +	}
> +}
> +
> +#if IS_ENABLED(CONFIG_QEDI)
> +static u8 qed_ll2_convert_rx_parse_to_tx_flags(u16 parse_flags)
> +{
> +	u8 bd_flags = 0;
> +
> +	if (GET_FIELD(parse_flags, PARSING_AND_ERR_FLAGS_TAG8021QEXIST))
> +		SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_VLAN_INSERTION, 1);
> +
> +	return bd_flags;
> +}
> +
> +static int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
> +{
> +	struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie;
> +	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
> +	u16 packet_length = 0, parse_flags = 0, vlan = 0;
> +	struct qed_ll2_rx_packet *p_pkt = NULL;
> +	u32 num_ooo_add_to_peninsula = 0, cid;
> +	union core_rx_cqe_union *cqe = NULL;
> +	u16 cq_new_idx = 0, cq_old_idx = 0;
> +	struct qed_ooo_buffer *p_buffer;
> +	struct ooo_opaque *iscsi_ooo;
> +	u8 placement_offset = 0;
> +	u8 cqe_type;
> +	int rc;
> +
> +	cq_new_idx = le16_to_cpu(*p_rx->p_fw_cons);
> +	cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
> +	if (cq_new_idx == cq_old_idx)
> +		return 0;
> +
> +	while (cq_new_idx != cq_old_idx) {
> +		struct core_rx_fast_path_cqe *p_cqe_fp;
> +
> +		cqe = qed_chain_consume(&p_rx->rcq_chain);
> +		cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
> +		cqe_type = cqe->rx_cqe_sp.type;
> +
> +		if (cqe_type != CORE_RX_CQE_TYPE_REGULAR) {
> +			DP_NOTICE(p_hwfn,
> +				  "Got a non-regular LB LL2 completion [type 0x%02x]\n",
> +				  cqe_type);
> +			return -EINVAL;
> +		}
> +		p_cqe_fp = &cqe->rx_cqe_fp;
> +
> +		placement_offset = p_cqe_fp->placement_offset;
> +		parse_flags = le16_to_cpu(p_cqe_fp->parse_flags.flags);
> +		packet_length = le16_to_cpu(p_cqe_fp->packet_length);
> +		vlan = le16_to_cpu(p_cqe_fp->vlan);
> +		iscsi_ooo = (struct ooo_opaque *)&p_cqe_fp->opaque_data;
> +		qed_ooo_save_history_entry(p_hwfn, p_hwfn->p_ooo_info,
> +					   iscsi_ooo);
> +		cid = le32_to_cpu(iscsi_ooo->cid);
> +
> +		/* Process delete isle first */
> +		if (iscsi_ooo->drop_size)
> +			qed_ooo_delete_isles(p_hwfn, p_hwfn->p_ooo_info, cid,
> +					     iscsi_ooo->drop_isle,
> +					     iscsi_ooo->drop_size);
> +
> +		if (iscsi_ooo->ooo_opcode == TCP_EVENT_NOP)
> +			continue;
> +
> +		/* Now process create/add/join isles */
> +		if (list_empty(&p_rx->active_descq)) {
> +			DP_NOTICE(p_hwfn,
> +				  "LL2 OOO RX chain has no submitted buffers\n");
> +			return -EIO;
> +		}
> +
> +		p_pkt = list_first_entry(&p_rx->active_descq,
> +					 struct qed_ll2_rx_packet, list_entry);
> +
> +		if ((iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_NEW_ISLE) ||
> +		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_RIGHT) ||
> +		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_LEFT) ||
> +		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_PEN) ||
> +		    (iscsi_ooo->ooo_opcode == TCP_EVENT_JOIN)) {
> +			if (!p_pkt) {
> +				DP_NOTICE(p_hwfn,
> +					  "LL2 OOO RX packet is not valid\n");
> +				return -EIO;
> +			}
> +			list_del(&p_pkt->list_entry);
> +			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
> +			p_buffer->packet_length = packet_length;
> +			p_buffer->parse_flags = parse_flags;
> +			p_buffer->vlan = vlan;
> +			p_buffer->placement_offset = placement_offset;
> +			qed_chain_consume(&p_rx->rxq_chain);
> +			list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
> +
> +			switch (iscsi_ooo->ooo_opcode) {
> +			case TCP_EVENT_ADD_NEW_ISLE:
> +				qed_ooo_add_new_isle(p_hwfn,
> +						     p_hwfn->p_ooo_info,
> +						     cid,
> +						     iscsi_ooo->ooo_isle,
> +						     p_buffer);
> +				break;
> +			case TCP_EVENT_ADD_ISLE_RIGHT:
> +				qed_ooo_add_new_buffer(p_hwfn,
> +						       p_hwfn->p_ooo_info,
> +						       cid,
> +						       iscsi_ooo->ooo_isle,
> +						       p_buffer,
> +						       QED_OOO_RIGHT_BUF);
> +				break;
> +			case TCP_EVENT_ADD_ISLE_LEFT:
> +				qed_ooo_add_new_buffer(p_hwfn,
> +						       p_hwfn->p_ooo_info,
> +						       cid,
> +						       iscsi_ooo->ooo_isle,
> +						       p_buffer,
> +						       QED_OOO_LEFT_BUF);
> +				break;
> +			case TCP_EVENT_JOIN:
> +				qed_ooo_add_new_buffer(p_hwfn,
> +						       p_hwfn->p_ooo_info,
> +						       cid,
> +						       iscsi_ooo->ooo_isle +
> +						       1,
> +						       p_buffer,
> +						       QED_OOO_LEFT_BUF);
> +				qed_ooo_join_isles(p_hwfn,
> +						   p_hwfn->p_ooo_info,
> +						   cid, iscsi_ooo->ooo_isle);
> +				break;
> +			case TCP_EVENT_ADD_PEN:
> +				num_ooo_add_to_peninsula++;
> +				qed_ooo_put_ready_buffer(p_hwfn,
> +							 p_hwfn->p_ooo_info,
> +							 p_buffer, true);
> +				break;
> +			}
> +		} else {
> +			DP_NOTICE(p_hwfn,
> +				  "Unexpected event (%d) TX OOO completion\n",
> +				  iscsi_ooo->ooo_opcode);
> +		}
> +	}
>  
> -		b_last = list_empty(&p_rx->active_descq);
> +	/* Submit RX buffer here */
> +	while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn,
> +						   p_hwfn->p_ooo_info))) {
> +		rc = qed_ll2_post_rx_buffer(p_hwfn, p_ll2_conn->my_id,
> +					    p_buffer->rx_buffer_phys_addr,
> +					    0, p_buffer, true);
> +		if (rc) {
> +			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						p_buffer);
> +			break;
> +		}
>  	}
> +
> +	/* Submit Tx buffers here */
> +	while ((p_buffer = qed_ooo_get_ready_buffer(p_hwfn,
> +						    p_hwfn->p_ooo_info))) {
> +		u16 l4_hdr_offset_w = 0;
> +		dma_addr_t first_frag;
> +		u8 bd_flags = 0;
> +
> +		first_frag = p_buffer->rx_buffer_phys_addr +
> +			     p_buffer->placement_offset;
> +		parse_flags = p_buffer->parse_flags;
> +		bd_flags = qed_ll2_convert_rx_parse_to_tx_flags(parse_flags);
> +		SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_FORCE_VLAN_MODE, 1);
> +		SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_L4_PROTOCOL, 1);
> +
> +		rc = qed_ll2_prepare_tx_packet(p_hwfn, p_ll2_conn->my_id, 1,
> +					       p_buffer->vlan, bd_flags,
> +					       l4_hdr_offset_w,
> +					       p_ll2_conn->tx_dest, 0,
> +					       first_frag,
> +					       p_buffer->packet_length,
> +					       p_buffer, true);
> +		if (rc) {
> +			qed_ooo_put_ready_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						 p_buffer, false);
> +			break;
> +		}
> +	}
> +
> +	return 0;
>  }
>  
> +static int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
> +{
> +	struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie;
> +	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
> +	struct qed_ll2_tx_packet *p_pkt = NULL;
> +	struct qed_ooo_buffer *p_buffer;
> +	bool b_dont_submit_rx = false;
> +	u16 new_idx = 0, num_bds = 0;
> +	int rc;
> +
> +	new_idx = le16_to_cpu(*p_tx->p_fw_cons);
> +	num_bds = ((s16)new_idx - (s16)p_tx->bds_idx);
> +
> +	if (!num_bds)
> +		return 0;
> +
> +	while (num_bds) {
> +		if (list_empty(&p_tx->active_descq))
> +			return -EINVAL;
> +
> +		p_pkt = list_first_entry(&p_tx->active_descq,
> +					 struct qed_ll2_tx_packet, list_entry);
> +		if (!p_pkt)
> +			return -EINVAL;
> +
> +		if (p_pkt->bd_used != 1) {
> +			DP_NOTICE(p_hwfn,
> +				  "Unexpectedly many BDs(%d) in TX OOO completion\n",
> +				  p_pkt->bd_used);
> +			return -EINVAL;
> +		}
> +
> +		list_del(&p_pkt->list_entry);
> +
> +		num_bds--;
> +		p_tx->bds_idx++;
> +		qed_chain_consume(&p_tx->txq_chain);
> +
> +		p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
> +		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
> +
> +		if (b_dont_submit_rx) {
> +			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						p_buffer);
> +			continue;
> +		}
> +
> +		rc = qed_ll2_post_rx_buffer(p_hwfn, p_ll2_conn->my_id,
> +					    p_buffer->rx_buffer_phys_addr, 0,
> +					    p_buffer, true);
> +		if (rc != 0) {
> +			qed_ooo_put_free_buffer(p_hwfn,
> +						p_hwfn->p_ooo_info, p_buffer);
> +			b_dont_submit_rx = true;
> +		}
> +	}
> +
> +	/* Submit Tx buffers here */
> +	while ((p_buffer = qed_ooo_get_ready_buffer(p_hwfn,
> +						    p_hwfn->p_ooo_info))) {
> +		u16 l4_hdr_offset_w = 0, parse_flags = p_buffer->parse_flags;
> +		dma_addr_t first_frag;
> +		u8 bd_flags = 0;
> +
> +		first_frag = p_buffer->rx_buffer_phys_addr +
> +		    p_buffer->placement_offset;
> +		bd_flags = qed_ll2_convert_rx_parse_to_tx_flags(parse_flags);
> +		SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_FORCE_VLAN_MODE, 1);
> +		SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_L4_PROTOCOL, 1);
> +		rc = qed_ll2_prepare_tx_packet(p_hwfn, p_ll2_conn->my_id, 1,
> +					       p_buffer->vlan, bd_flags,
> +					       l4_hdr_offset_w,
> +					       p_ll2_conn->tx_dest, 0,
> +					       first_frag,
> +					       p_buffer->packet_length,
> +					       p_buffer, true);
> +		if (rc != 0) {
> +			qed_ooo_put_ready_buffer(p_hwfn, p_hwfn->p_ooo_info,
> +						 p_buffer, false);
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +qed_ll2_acquire_connection_ooo(struct qed_hwfn *p_hwfn,
> +			       struct qed_ll2_info *p_ll2_info,
> +			       u16 rx_num_ooo_buffers, u16 mtu)
> +{
> +	struct qed_ooo_buffer *p_buf = NULL;
> +	void *p_virt;
> +	u16 buf_idx;
> +	int rc = 0;
> +
> +	if (p_ll2_info->conn_type != QED_LL2_TYPE_ISCSI_OOO)
> +		return rc;
> +
> +	if (!rx_num_ooo_buffers)
> +		return -EINVAL;
> +
> +	for (buf_idx = 0; buf_idx < rx_num_ooo_buffers; buf_idx++) {
> +		p_buf = kzalloc(sizeof(*p_buf), GFP_KERNEL);
> +		if (!p_buf) {
> +			DP_NOTICE(p_hwfn,
> +				  "Failed to allocate ooo descriptor\n");
> +			rc = -ENOMEM;
> +			goto out;
> +		}
> +
> +		p_buf->rx_buffer_size = mtu + 26 + ETH_CACHE_LINE_SIZE;
> +		p_buf->rx_buffer_size = (p_buf->rx_buffer_size +
> +					 ETH_CACHE_LINE_SIZE - 1) &
> +					~(ETH_CACHE_LINE_SIZE - 1);
> +		p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
> +					    p_buf->rx_buffer_size,
> +					    &p_buf->rx_buffer_phys_addr,
> +					    GFP_KERNEL);
> +		if (!p_virt) {
> +			DP_NOTICE(p_hwfn, "Failed to allocate ooo buffer\n");
> +			kfree(p_buf);
> +			rc = -ENOMEM;
> +			goto out;
> +		}
> +
> +		p_buf->rx_buffer_virt_addr = p_virt;
> +		qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, p_buf);
> +	}
> +
> +	DP_VERBOSE(p_hwfn, QED_MSG_LL2,
> +		   "Allocated [%04x] LL2 OOO buffers [each of size 0x%08x]\n",
> +		   rx_num_ooo_buffers, p_buf->rx_buffer_size);
> +
> +out:
> +	return rc;
> +}
> +
> +static void
> +qed_ll2_establish_connection_ooo(struct qed_hwfn *p_hwfn,
> +				 struct qed_ll2_info *p_ll2_conn)
> +{
> +	struct qed_ooo_buffer *p_buffer;
> +	int rc;
> +
> +	if (p_ll2_conn->conn_type != QED_LL2_TYPE_ISCSI_OOO)
> +		return;
> +
> +	qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
> +	while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn,
> +						   p_hwfn->p_ooo_info))) {
> +		rc = qed_ll2_post_rx_buffer(p_hwfn,
> +					    p_ll2_conn->my_id,
> +					    p_buffer->rx_buffer_phys_addr,
> +					    0, p_buffer, true);
> +		if (rc) {
> +			qed_ooo_put_free_buffer(p_hwfn,
> +						p_hwfn->p_ooo_info, p_buffer);
> +			break;
> +		}
> +	}
> +}
> +
> +static void qed_ll2_release_connection_ooo(struct qed_hwfn *p_hwfn,
> +					   struct qed_ll2_info *p_ll2_conn)
> +{
> +	struct qed_ooo_buffer *p_buffer;
> +
> +	if (p_ll2_conn->conn_type != QED_LL2_TYPE_ISCSI_OOO)
> +		return;
> +
> +	qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
> +	while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn,
> +						   p_hwfn->p_ooo_info))) {
> +		dma_free_coherent(&p_hwfn->cdev->pdev->dev,
> +				  p_buffer->rx_buffer_size,
> +				  p_buffer->rx_buffer_virt_addr,
> +				  p_buffer->rx_buffer_phys_addr);
> +		kfree(p_buffer);
> +	}
> +}
> +
> +static void qed_ll2_stop_ooo(struct qed_dev *cdev)
> +{
> +	struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
> +	u8 *handle = &hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id;
> +
> +	DP_VERBOSE(cdev, QED_MSG_STORAGE, "Stopping LL2 OOO queue [%02x]\n",
> +		   *handle);
> +
> +	qed_ll2_terminate_connection(hwfn, *handle);
> +	qed_ll2_release_connection(hwfn, *handle);
> +	*handle = QED_LL2_UNUSED_HANDLE;
> +}
> +
> +static int qed_ll2_start_ooo(struct qed_dev *cdev,
> +			     struct qed_ll2_params *params)
> +{
> +	struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
> +	u8 *handle = &hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id;
> +	struct qed_ll2_info *ll2_info;
> +	int rc;
> +
> +	ll2_info = kzalloc(sizeof(*ll2_info), GFP_KERNEL);
> +	if (!ll2_info) {
> +		DP_INFO(cdev, "Failed to allocate LL2 info buffer\n");
> +		return -ENOMEM;
> +	}
> +	ll2_info->conn_type = QED_LL2_TYPE_ISCSI_OOO;
> +	ll2_info->mtu = params->mtu;
> +	ll2_info->rx_drop_ttl0_flg = params->drop_ttl0_packets;
> +	ll2_info->rx_vlan_removal_en = params->rx_vlan_stripping;
> +	ll2_info->tx_tc = OOO_LB_TC;
> +	ll2_info->tx_dest = CORE_TX_DEST_LB;
> +
> +	rc = qed_ll2_acquire_connection(hwfn, ll2_info,
> +					QED_LL2_RX_SIZE, QED_LL2_TX_SIZE,
> +					handle);
> +	kfree(ll2_info);
> +	if (rc) {
> +		DP_INFO(cdev, "Failed to acquire LL2 OOO connection\n");
> +		goto out;
> +	}
> +
> +	rc = qed_ll2_establish_connection(hwfn, *handle);
> +	if (rc) {
> +		DP_INFO(cdev, "Failed to establist LL2 OOO connection\n");
> +		goto fail;
> +	}
> +
> +	return 0;
> +
> +fail:
> +	qed_ll2_release_connection(hwfn, *handle);
> +out:
> +	*handle = QED_LL2_UNUSED_HANDLE;
> +	return rc;
> +}
> +#else /* IS_ENABLED(CONFIG_QEDI) */
> +static inline int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn,
> +		void *p_cookie) { return -EINVAL; }
> +static inline int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn,
> +		void *p_cookie) { return -EINVAL; }
> +static inline int
> +qed_ll2_acquire_connection_ooo(struct qed_hwfn *p_hwfn,
> +			struct qed_ll2_info *p_ll2_info,
> +			u16 rx_num_ooo_buffers, u16 mtu) { return -EINVAL; }
> +static inline void
> +qed_ll2_establish_connection_ooo(struct qed_hwfn *p_hwfn,
> +			struct qed_ll2_info *p_ll2_conn) { return; }
> +static inline void qed_ll2_release_connection_ooo(struct qed_hwfn *p_hwfn,
> +			struct qed_ll2_info *p_ll2_conn) { return; }
> +static inline void qed_ll2_stop_ooo(struct qed_dev *cdev) { return; }
> +static inline int qed_ll2_start_ooo(struct qed_dev *cdev,
> +			struct qed_ll2_params *params) { return -EINVAL; }
> +#endif /* IS_ENABLED(CONFIG_QEDI) */
> +
>  static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,
>  				     struct qed_ll2_info *p_ll2_conn,
>  				     u8 action_on_error)
> @@ -594,7 +1057,8 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,
>  	p_ramrod->drop_ttl0_flg = p_ll2_conn->rx_drop_ttl0_flg;
>  	p_ramrod->inner_vlan_removal_en = p_ll2_conn->rx_vlan_removal_en;
>  	p_ramrod->queue_id = p_ll2_conn->queue_id;
> -	p_ramrod->main_func_queue = 1;
> +	p_ramrod->main_func_queue = (conn_type == QED_LL2_TYPE_ISCSI_OOO) ? 0
> +									  : 1;
>  
>  	if ((IS_MF_DEFAULT(p_hwfn) || IS_MF_SI(p_hwfn)) &&
>  	    p_ramrod->main_func_queue && (conn_type != QED_LL2_TYPE_ROCE)) {
> @@ -625,6 +1089,11 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
>  	if (!QED_LL2_TX_REGISTERED(p_ll2_conn))
>  		return 0;
>  
> +	if (p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO)
> +		p_ll2_conn->tx_stats_en = 0;
> +	else
> +		p_ll2_conn->tx_stats_en = 1;
> +
>  	/* Get SPQ entry */
>  	memset(&init_data, 0, sizeof(init_data));
>  	init_data.cid = p_ll2_conn->cid;
> @@ -642,7 +1111,6 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
>  	p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn));
>  	p_ramrod->sb_index = p_tx->tx_sb_index;
>  	p_ramrod->mtu = cpu_to_le16(p_ll2_conn->mtu);
> -	p_ll2_conn->tx_stats_en = 1;
>  	p_ramrod->stats_en = p_ll2_conn->tx_stats_en;
>  	p_ramrod->stats_id = p_ll2_conn->tx_stats_id;
>  
> @@ -866,9 +1334,22 @@ int qed_ll2_acquire_connection(struct qed_hwfn *p_hwfn,
>  	if (rc)
>  		goto q_allocate_fail;
>  
> +	if (IS_ENABLED(CONFIG_QEDI)) {
> +		rc = qed_ll2_acquire_connection_ooo(p_hwfn, p_ll2_info,
> +					    rx_num_desc * 2, p_params->mtu);
> +		if (rc)
> +			goto q_allocate_fail;
> +	}
> +
>  	/* Register callbacks for the Rx/Tx queues */
> -	comp_rx_cb = qed_ll2_rxq_completion;
> -	comp_tx_cb = qed_ll2_txq_completion;
> +	if (IS_ENABLED(CONFIG_QEDI) &&
> +			p_params->conn_type == QED_LL2_TYPE_ISCSI_OOO) {
> +		comp_rx_cb = qed_ll2_lb_rxq_completion;
> +		comp_tx_cb = qed_ll2_lb_txq_completion;
> +	} else {
> +		comp_rx_cb = qed_ll2_rxq_completion;
> +		comp_tx_cb = qed_ll2_txq_completion;
> +	}
>  
>  	if (rx_num_desc) {
>  		qed_int_register_cb(p_hwfn, comp_rx_cb,
> @@ -981,6 +1462,9 @@ int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
>  	if (p_hwfn->hw_info.personality != QED_PCI_ETH_ROCE)
>  		qed_wr(p_hwfn, p_hwfn->p_main_ptt, PRS_REG_USE_LIGHT_L2, 1);
>  
> +	if (IS_ENABLED(CONFIG_QEDI))
> +		qed_ll2_establish_connection_ooo(p_hwfn, p_ll2_conn);
> +
>  	return rc;
>  }
>  
> @@ -1223,6 +1707,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
>  			      u16 vlan,
>  			      u8 bd_flags,
>  			      u16 l4_hdr_offset_w,
> +			      enum qed_ll2_tx_dest e_tx_dest,
>  			      enum qed_ll2_roce_flavor_type qed_roce_flavor,
>  			      dma_addr_t first_frag,
>  			      u16 first_frag_len, void *cookie, u8 notify_fw)
> @@ -1232,6 +1717,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
>  	enum core_roce_flavor_type roce_flavor;
>  	struct qed_ll2_tx_queue *p_tx;
>  	struct qed_chain *p_tx_chain;
> +	enum core_tx_dest tx_dest;
>  	unsigned long flags;
>  	int rc = 0;
>  
> @@ -1262,6 +1748,8 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
>  		goto out;
>  	}
>  
> +	tx_dest = e_tx_dest == QED_LL2_TX_DEST_NW ? CORE_TX_DEST_NW :
> +						    CORE_TX_DEST_LB;
>  	if (qed_roce_flavor == QED_LL2_ROCE) {
>  		roce_flavor = CORE_ROCE;
>  	} else if (qed_roce_flavor == QED_LL2_RROCE) {
> @@ -1276,7 +1764,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
>  				      num_of_bds, first_frag,
>  				      first_frag_len, cookie, notify_fw);
>  	qed_ll2_prepare_tx_packet_set_bd(p_hwfn, p_ll2_conn, p_curp,
> -					 num_of_bds, CORE_TX_DEST_NW,
> +					 num_of_bds, tx_dest,
>  					 vlan, bd_flags, l4_hdr_offset_w,
>  					 roce_flavor,
>  					 first_frag, first_frag_len);
> @@ -1351,6 +1839,10 @@ int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
>  		qed_ll2_rxq_flush(p_hwfn, connection_handle);
>  	}
>  
> +	if (IS_ENABLED(CONFIG_QEDI) &&
> +			p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO)
> +		qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
> +
>  	return rc;
>  }
>  
> @@ -1381,6 +1873,9 @@ void qed_ll2_release_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
>  
>  	qed_cxt_release_cid(p_hwfn, p_ll2_conn->cid);
>  
> +	if (IS_ENABLED(CONFIG_QEDI))
> +		qed_ll2_release_connection_ooo(p_hwfn, p_ll2_conn);
> +
>  	mutex_lock(&p_ll2_conn->mutex);
>  	p_ll2_conn->b_active = false;
>  	mutex_unlock(&p_ll2_conn->mutex);
> @@ -1628,6 +2123,18 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
>  		goto release_terminate;
>  	}
>  
> +	if (IS_ENABLED(CONFIG_QEDI) &&
> +		(cdev->hwfns[0].hw_info.personality == QED_PCI_ISCSI) &&
> +		cdev->hwfns[0].pf_params.iscsi_pf_params.ooo_enable) {
> +		DP_VERBOSE(cdev, QED_MSG_STORAGE, "Starting OOO LL2 queue\n");
> +		rc = qed_ll2_start_ooo(cdev, params);
> +		if (rc) {
> +			DP_INFO(cdev,
> +				"Failed to initialize the OOO LL2 queue\n");
> +			goto release_terminate;
> +		}
> +	}
> +
>  	p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
>  	if (!p_ptt) {
>  		DP_INFO(cdev, "Failed to acquire PTT\n");
> @@ -1677,6 +2184,11 @@ static int qed_ll2_stop(struct qed_dev *cdev)
>  	qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt);
>  	eth_zero_addr(cdev->ll2_mac_address);
>  
> +	if (IS_ENABLED(CONFIG_QEDI) &&
> +		(cdev->hwfns[0].hw_info.personality == QED_PCI_ISCSI) &&
> +		cdev->hwfns[0].pf_params.iscsi_pf_params.ooo_enable)
> +		qed_ll2_stop_ooo(cdev);
> +
>  	rc = qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev),
>  					  cdev->ll2->handle);
>  	if (rc)
> @@ -1731,7 +2243,8 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb)
>  	rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev),
>  				       cdev->ll2->handle,
>  				       1 + skb_shinfo(skb)->nr_frags,
> -				       vlan, flags, 0, 0 /* RoCE FLAVOR */,
> +				       vlan, flags, 0, QED_LL2_TX_DEST_NW,
> +				       0 /* RoCE FLAVOR */,
>  				       mapping, skb->len, skb, 1);
>  	if (rc)
>  		goto err;
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.h b/drivers/net/ethernet/qlogic/qed/qed_ll2.h
> index 80a5dc2..2b31d30 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.h
> +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.h
> @@ -41,6 +41,12 @@ enum qed_ll2_conn_type {
>  	MAX_QED_LL2_RX_CONN_TYPE
>  };
>  
> +enum qed_ll2_tx_dest {
> +	QED_LL2_TX_DEST_NW, /* Light L2 TX Destination to the Network */
> +	QED_LL2_TX_DEST_LB, /* Light L2 TX Destination to the Loopback */
> +	QED_LL2_TX_DEST_MAX
> +};
> +
>  struct qed_ll2_rx_packet {
>  	struct list_head list_entry;
>  	struct core_rx_bd_with_buff_len *rxq_bd;
> @@ -192,6 +198,8 @@ int qed_ll2_post_rx_buffer(struct qed_hwfn *p_hwfn,
>   * @param l4_hdr_offset_w	L4 Header Offset from start of packet
>   *				(in words). This is needed if both l4_csum
>   *				and ipv6_ext are set
> + * @param e_tx_dest             indicates if the packet is to be transmitted via
> + *                              loopback or to the network
>   * @param first_frag
>   * @param first_frag_len
>   * @param cookie
> @@ -206,6 +214,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
>  			      u16 vlan,
>  			      u8 bd_flags,
>  			      u16 l4_hdr_offset_w,
> +			      enum qed_ll2_tx_dest e_tx_dest,
>  			      enum qed_ll2_roce_flavor_type qed_roce_flavor,
>  			      dma_addr_t first_frag,
>  			      u16 first_frag_len, void *cookie, u8 notify_fw);
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.c b/drivers/net/ethernet/qlogic/qed/qed_ooo.c
> new file mode 100644
> index 0000000..a037a6f
> --- /dev/null
> +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.c
> @@ -0,0 +1,510 @@
> +/* QLogic qed NIC Driver
> + * Copyright (c) 2015 QLogic Corporation
> + *
> + * This software is available under the terms of the GNU General Public License
> + * (GPL) Version 2, available from the file COPYING in the main directory of
> + * this source tree.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include "qed.h"
> +#include "qed_iscsi.h"
> +#include "qed_ll2.h"
> +#include "qed_ooo.h"
> +
> +static struct qed_ooo_archipelago
> +*qed_ooo_seek_archipelago(struct qed_hwfn *p_hwfn,
> +			  struct qed_ooo_info
> +			  *p_ooo_info,
> +			  u32 cid)
> +{
> +	struct qed_ooo_archipelago *p_archipelago = NULL;
> +
> +	list_for_each_entry(p_archipelago,
> +			    &p_ooo_info->archipelagos_list, list_entry) {
> +		if (p_archipelago->cid == cid)
> +			return p_archipelago;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct qed_ooo_isle *qed_ooo_seek_isle(struct qed_hwfn *p_hwfn,
> +					      struct qed_ooo_info *p_ooo_info,
> +					      u32 cid, u8 isle)
> +{
> +	struct qed_ooo_archipelago *p_archipelago = NULL;
> +	struct qed_ooo_isle *p_isle = NULL;
> +	u8 the_num_of_isle = 1;
> +
> +	p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
> +	if (!p_archipelago) {
> +		DP_NOTICE(p_hwfn,
> +			  "Connection %d is not found in OOO list\n", cid);
> +		return NULL;
> +	}
> +
> +	list_for_each_entry(p_isle, &p_archipelago->isles_list, list_entry) {
> +		if (the_num_of_isle == isle)
> +			return p_isle;
> +		the_num_of_isle++;
> +	}
> +
> +	return NULL;
> +}
> +
> +void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn,
> +				struct qed_ooo_info *p_ooo_info,
> +				struct ooo_opaque *p_cqe)
> +{
> +	struct qed_ooo_history *p_history = &p_ooo_info->ooo_history;
> +
> +	if (p_history->head_idx == p_history->num_of_cqes)
> +		p_history->head_idx = 0;
> +	p_history->p_cqes[p_history->head_idx] = *p_cqe;
> +	p_history->head_idx++;
> +}
> +
> +struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn)
> +{
> +	struct qed_ooo_info *p_ooo_info;
> +	u16 max_num_archipelagos = 0;
> +	u16 max_num_isles = 0;
> +	u32 i;
> +
> +	if (p_hwfn->hw_info.personality != QED_PCI_ISCSI) {
> +		DP_NOTICE(p_hwfn,
> +			  "Failed to allocate qed_ooo_info: unknown personality\n");
> +		return NULL;
> +	}
> +
> +	max_num_archipelagos = p_hwfn->pf_params.iscsi_pf_params.num_cons;
> +	max_num_isles = QED_MAX_NUM_ISLES + max_num_archipelagos;
> +
> +	if (!max_num_archipelagos) {
> +		DP_NOTICE(p_hwfn,
> +			  "Failed to allocate qed_ooo_info: unknown amount of connections\n");
> +		return NULL;
> +	}
> +
> +	p_ooo_info = kzalloc(sizeof(*p_ooo_info), GFP_KERNEL);
> +	if (!p_ooo_info) {
> +		DP_NOTICE(p_hwfn, "Failed to allocate qed_ooo_info\n");
> +		return NULL;
> +	}
> +
> +	INIT_LIST_HEAD(&p_ooo_info->free_buffers_list);
> +	INIT_LIST_HEAD(&p_ooo_info->ready_buffers_list);
> +	INIT_LIST_HEAD(&p_ooo_info->free_isles_list);
> +	INIT_LIST_HEAD(&p_ooo_info->free_archipelagos_list);
> +	INIT_LIST_HEAD(&p_ooo_info->archipelagos_list);
> +
> +	p_ooo_info->p_isles_mem = kcalloc(max_num_isles,
> +					  sizeof(struct qed_ooo_isle),
> +					  GFP_KERNEL);
> +	if (!p_ooo_info->p_isles_mem) {
> +		DP_NOTICE(p_hwfn, "Failed to allocate qed_ooo_info(isles)\n");
> +		goto no_isles_mem;
> +	}
> +
> +	for (i = 0; i < max_num_isles; i++) {
> +		INIT_LIST_HEAD(&p_ooo_info->p_isles_mem[i].buffers_list);
> +		list_add_tail(&p_ooo_info->p_isles_mem[i].list_entry,
> +			      &p_ooo_info->free_isles_list);
> +	}
> +
> +	p_ooo_info->p_archipelagos_mem =
> +				kcalloc(max_num_archipelagos,
> +					sizeof(struct qed_ooo_archipelago),
> +					GFP_KERNEL);
> +	if (!p_ooo_info->p_archipelagos_mem) {
> +		DP_NOTICE(p_hwfn,
> +			  "Failed to allocate qed_ooo_info(archpelagos)\n");
> +		goto no_archipelagos_mem;
> +	}
> +
> +	for (i = 0; i < max_num_archipelagos; i++) {
> +		INIT_LIST_HEAD(&p_ooo_info->p_archipelagos_mem[i].isles_list);
> +		list_add_tail(&p_ooo_info->p_archipelagos_mem[i].list_entry,
> +			      &p_ooo_info->free_archipelagos_list);
> +	}
> +
> +	p_ooo_info->ooo_history.p_cqes =
> +				kcalloc(QED_MAX_NUM_OOO_HISTORY_ENTRIES,
> +					sizeof(struct ooo_opaque),
> +					GFP_KERNEL);
> +	if (!p_ooo_info->ooo_history.p_cqes) {
> +		DP_NOTICE(p_hwfn, "Failed to allocate qed_ooo_info(history)\n");
> +		goto no_history_mem;
> +	}
> +
> +	return p_ooo_info;
> +
> +no_history_mem:
> +	kfree(p_ooo_info->p_archipelagos_mem);
> +no_archipelagos_mem:
> +	kfree(p_ooo_info->p_isles_mem);
> +no_isles_mem:
> +	kfree(p_ooo_info);
> +	return NULL;
> +}
> +
> +void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn,
> +				      struct qed_ooo_info *p_ooo_info, u32 cid)
> +{
> +	struct qed_ooo_archipelago *p_archipelago;
> +	struct qed_ooo_buffer *p_buffer;
> +	struct qed_ooo_isle *p_isle;
> +	bool b_found = false;
> +
> +	if (list_empty(&p_ooo_info->archipelagos_list))
> +		return;
> +
> +	list_for_each_entry(p_archipelago,
> +			    &p_ooo_info->archipelagos_list, list_entry) {
> +		if (p_archipelago->cid == cid) {
> +			list_del(&p_archipelago->list_entry);
> +			b_found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!b_found)
> +		return;
> +
> +	while (!list_empty(&p_archipelago->isles_list)) {
> +		p_isle = list_first_entry(&p_archipelago->isles_list,
> +					  struct qed_ooo_isle, list_entry);
> +
> +		list_del(&p_isle->list_entry);
> +
> +		while (!list_empty(&p_isle->buffers_list)) {
> +			p_buffer = list_first_entry(&p_isle->buffers_list,
> +						    struct qed_ooo_buffer,
> +						    list_entry);
> +
> +			if (!p_buffer)
> +				break;
> +
> +			list_del(&p_buffer->list_entry);
> +			list_add_tail(&p_buffer->list_entry,
> +				      &p_ooo_info->free_buffers_list);
> +		}
> +		list_add_tail(&p_isle->list_entry,
> +			      &p_ooo_info->free_isles_list);
> +	}
> +
> +	list_add_tail(&p_archipelago->list_entry,
> +		      &p_ooo_info->free_archipelagos_list);
> +}
> +
> +void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn,
> +			       struct qed_ooo_info *p_ooo_info)
> +{
> +	struct qed_ooo_archipelago *p_arch;
> +	struct qed_ooo_buffer *p_buffer;
> +	struct qed_ooo_isle *p_isle;
> +
> +	while (!list_empty(&p_ooo_info->archipelagos_list)) {
> +		p_arch = list_first_entry(&p_ooo_info->archipelagos_list,
> +					  struct qed_ooo_archipelago,
> +					  list_entry);
> +
> +		list_del(&p_arch->list_entry);
> +
> +		while (!list_empty(&p_arch->isles_list)) {
> +			p_isle = list_first_entry(&p_arch->isles_list,
> +						  struct qed_ooo_isle,
> +						  list_entry);
> +
> +			list_del(&p_isle->list_entry);
> +
> +			while (!list_empty(&p_isle->buffers_list)) {
> +				p_buffer =
> +				    list_first_entry(&p_isle->buffers_list,
> +						     struct qed_ooo_buffer,
> +						     list_entry);
> +
> +				if (!p_buffer)
> +					break;
> +
> +			list_del(&p_buffer->list_entry);
> +				list_add_tail(&p_buffer->list_entry,
> +					      &p_ooo_info->free_buffers_list);
> +			}
> +			list_add_tail(&p_isle->list_entry,
> +				      &p_ooo_info->free_isles_list);
> +		}
> +		list_add_tail(&p_arch->list_entry,
> +			      &p_ooo_info->free_archipelagos_list);
> +	}
> +	if (!list_empty(&p_ooo_info->ready_buffers_list))
> +		list_splice_tail_init(&p_ooo_info->ready_buffers_list,
> +				      &p_ooo_info->free_buffers_list);
> +}
> +
> +void qed_ooo_setup(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info)
> +{
> +	qed_ooo_release_all_isles(p_hwfn, p_ooo_info);
> +	memset(p_ooo_info->ooo_history.p_cqes, 0,
> +	       p_ooo_info->ooo_history.num_of_cqes *
> +	       sizeof(struct ooo_opaque));
> +	p_ooo_info->ooo_history.head_idx = 0;
> +}
> +
> +void qed_ooo_free(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info)
> +{
> +	struct qed_ooo_buffer *p_buffer;
> +
> +	qed_ooo_release_all_isles(p_hwfn, p_ooo_info);
> +	while (!list_empty(&p_ooo_info->free_buffers_list)) {
> +		p_buffer = list_first_entry(&p_ooo_info->free_buffers_list,
> +					    struct qed_ooo_buffer, list_entry);
> +
> +		if (!p_buffer)
> +			break;
> +
> +		list_del(&p_buffer->list_entry);
> +		dma_free_coherent(&p_hwfn->cdev->pdev->dev,
> +				  p_buffer->rx_buffer_size,
> +				  p_buffer->rx_buffer_virt_addr,
> +				  p_buffer->rx_buffer_phys_addr);
> +		kfree(p_buffer);
> +	}
> +
> +	kfree(p_ooo_info->p_isles_mem);
> +	kfree(p_ooo_info->p_archipelagos_mem);
> +	kfree(p_ooo_info->ooo_history.p_cqes);
> +	kfree(p_ooo_info);
> +}
> +
> +void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn,
> +			     struct qed_ooo_info *p_ooo_info,
> +			     struct qed_ooo_buffer *p_buffer)
> +{
> +	list_add_tail(&p_buffer->list_entry, &p_ooo_info->free_buffers_list);
> +}
> +
> +struct qed_ooo_buffer *qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn,
> +					       struct qed_ooo_info *p_ooo_info)
> +{
> +	struct qed_ooo_buffer *p_buffer = NULL;
> +
> +	if (!list_empty(&p_ooo_info->free_buffers_list)) {
> +		p_buffer = list_first_entry(&p_ooo_info->free_buffers_list,
> +					    struct qed_ooo_buffer, list_entry);
> +
> +		list_del(&p_buffer->list_entry);
> +	}
> +
> +	return p_buffer;
> +}
> +
> +void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn,
> +			      struct qed_ooo_info *p_ooo_info,
> +			      struct qed_ooo_buffer *p_buffer, u8 on_tail)
> +{
> +	if (on_tail)
> +		list_add_tail(&p_buffer->list_entry,
> +			      &p_ooo_info->ready_buffers_list);
> +	else
> +		list_add(&p_buffer->list_entry,
> +			 &p_ooo_info->ready_buffers_list);
> +}
> +
> +struct qed_ooo_buffer *qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn,
> +						struct qed_ooo_info *p_ooo_info)
> +{
> +	struct qed_ooo_buffer *p_buffer = NULL;
> +
> +	if (!list_empty(&p_ooo_info->ready_buffers_list)) {
> +		p_buffer = list_first_entry(&p_ooo_info->ready_buffers_list,
> +					    struct qed_ooo_buffer, list_entry);
> +
> +		list_del(&p_buffer->list_entry);
> +	}
> +
> +	return p_buffer;
> +}
> +
> +void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn,
> +			  struct qed_ooo_info *p_ooo_info,
> +			  u32 cid, u8 drop_isle, u8 drop_size)
> +{
> +	struct qed_ooo_archipelago *p_archipelago = NULL;
> +	struct qed_ooo_isle *p_isle = NULL;
> +	u8 isle_idx;
> +
> +	p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
> +	for (isle_idx = 0; isle_idx < drop_size; isle_idx++) {
> +		p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, drop_isle);
> +		if (!p_isle) {
> +			DP_NOTICE(p_hwfn,
> +				  "Isle %d is not found(cid %d)\n",
> +				  drop_isle, cid);
> +			return;
> +		}
> +		if (list_empty(&p_isle->buffers_list))
> +			DP_NOTICE(p_hwfn,
> +				  "Isle %d is empty(cid %d)\n", drop_isle, cid);
> +		else
> +			list_splice_tail_init(&p_isle->buffers_list,
> +					      &p_ooo_info->free_buffers_list);
> +
> +		list_del(&p_isle->list_entry);
> +		p_ooo_info->cur_isles_number--;
> +		list_add(&p_isle->list_entry, &p_ooo_info->free_isles_list);
> +	}
> +
> +	if (list_empty(&p_archipelago->isles_list)) {
> +		list_del(&p_archipelago->list_entry);
> +		list_add(&p_archipelago->list_entry,
> +			 &p_ooo_info->free_archipelagos_list);
> +	}
> +}
> +
> +void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn,
> +			  struct qed_ooo_info *p_ooo_info,
> +			  u32 cid, u8 ooo_isle,
> +			  struct qed_ooo_buffer *p_buffer)
> +{
> +	struct qed_ooo_archipelago *p_archipelago = NULL;
> +	struct qed_ooo_isle *p_prev_isle = NULL;
> +	struct qed_ooo_isle *p_isle = NULL;
> +
> +	if (ooo_isle > 1) {
> +		p_prev_isle = qed_ooo_seek_isle(p_hwfn,
> +						p_ooo_info, cid, ooo_isle - 1);
> +		if (!p_prev_isle) {
> +			DP_NOTICE(p_hwfn,
> +				  "Isle %d is not found(cid %d)\n",
> +				  ooo_isle - 1, cid);
> +			return;
> +		}
> +	}
> +	p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
> +	if (!p_archipelago && (ooo_isle != 1)) {
> +		DP_NOTICE(p_hwfn,
> +			  "Connection %d is not found in OOO list\n", cid);
> +		return;
> +	}
> +
> +	if (!list_empty(&p_ooo_info->free_isles_list)) {
> +		p_isle = list_first_entry(&p_ooo_info->free_isles_list,
> +					  struct qed_ooo_isle, list_entry);
> +
> +		list_del(&p_isle->list_entry);
> +		if (!list_empty(&p_isle->buffers_list)) {
> +			DP_NOTICE(p_hwfn, "Free isle is not empty\n");
> +			INIT_LIST_HEAD(&p_isle->buffers_list);
> +		}
> +	} else {
> +		DP_NOTICE(p_hwfn, "No more free isles\n");
> +		return;
> +	}
> +
> +	if (!p_archipelago &&
> +	    !list_empty(&p_ooo_info->free_archipelagos_list)) {
> +		p_archipelago =
> +		    list_first_entry(&p_ooo_info->free_archipelagos_list,
> +				     struct qed_ooo_archipelago, list_entry);
> +
> +		list_del(&p_archipelago->list_entry);
> +		if (!list_empty(&p_archipelago->isles_list)) {
> +			DP_NOTICE(p_hwfn,
> +				  "Free OOO connection is not empty\n");
> +			INIT_LIST_HEAD(&p_archipelago->isles_list);
> +		}
> +		p_archipelago->cid = cid;
> +		list_add(&p_archipelago->list_entry,
> +			 &p_ooo_info->archipelagos_list);
> +	} else if (!p_archipelago) {
> +		DP_NOTICE(p_hwfn, "No more free OOO connections\n");
> +		list_add(&p_isle->list_entry,
> +			 &p_ooo_info->free_isles_list);
> +		list_add(&p_buffer->list_entry,
> +			 &p_ooo_info->free_buffers_list);
> +		return;
> +	}
> +
> +	list_add(&p_buffer->list_entry, &p_isle->buffers_list);
> +	p_ooo_info->cur_isles_number++;
> +	p_ooo_info->gen_isles_number++;
> +
> +	if (p_ooo_info->cur_isles_number > p_ooo_info->max_isles_number)
> +		p_ooo_info->max_isles_number = p_ooo_info->cur_isles_number;
> +
> +	if (!p_prev_isle)
> +		list_add(&p_isle->list_entry, &p_archipelago->isles_list);
> +	else
> +		list_add(&p_isle->list_entry, &p_prev_isle->list_entry);
> +}
> +
> +void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn,
> +			    struct qed_ooo_info *p_ooo_info,
> +			    u32 cid,
> +			    u8 ooo_isle,
> +			    struct qed_ooo_buffer *p_buffer, u8 buffer_side)
> +{
> +	struct qed_ooo_isle *p_isle = NULL;
> +
> +	p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, ooo_isle);
> +	if (!p_isle) {
> +		DP_NOTICE(p_hwfn,
> +			  "Isle %d is not found(cid %d)\n", ooo_isle, cid);
> +		return;
> +	}
> +
> +	if (buffer_side == QED_OOO_LEFT_BUF)
> +		list_add(&p_buffer->list_entry, &p_isle->buffers_list);
> +	else
> +		list_add_tail(&p_buffer->list_entry, &p_isle->buffers_list);
> +}
> +
> +void qed_ooo_join_isles(struct qed_hwfn *p_hwfn,
> +			struct qed_ooo_info *p_ooo_info, u32 cid, u8 left_isle)
> +{
> +	struct qed_ooo_archipelago *p_archipelago = NULL;
> +	struct qed_ooo_isle *p_right_isle = NULL;
> +	struct qed_ooo_isle *p_left_isle = NULL;
> +
> +	p_right_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid,
> +					 left_isle + 1);
> +	if (!p_right_isle) {
> +		DP_NOTICE(p_hwfn,
> +			  "Right isle %d is not found(cid %d)\n",
> +			  left_isle + 1, cid);
> +		return;
> +	}
> +
> +	p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
> +	list_del(&p_right_isle->list_entry);
> +	p_ooo_info->cur_isles_number--;
> +	if (left_isle) {
> +		p_left_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid,
> +						left_isle);
> +		if (!p_left_isle) {
> +			DP_NOTICE(p_hwfn,
> +				  "Left isle %d is not found(cid %d)\n",
> +				  left_isle, cid);
> +			return;
> +		}
> +		list_splice_tail_init(&p_right_isle->buffers_list,
> +				      &p_left_isle->buffers_list);
> +	} else {
> +		list_splice_tail_init(&p_right_isle->buffers_list,
> +				      &p_ooo_info->ready_buffers_list);
> +		if (list_empty(&p_archipelago->isles_list)) {
> +			list_del(&p_archipelago->list_entry);
> +			list_add(&p_archipelago->list_entry,
> +				 &p_ooo_info->free_archipelagos_list);
> +		}
> +	}
> +	list_add_tail(&p_right_isle->list_entry, &p_ooo_info->free_isles_list);
> +}
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.h b/drivers/net/ethernet/qlogic/qed/qed_ooo.h
> new file mode 100644
> index 0000000..75c6e48
> --- /dev/null
> +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.h
> @@ -0,0 +1,116 @@
> +/* QLogic qed NIC Driver
> + * Copyright (c) 2015 QLogic Corporation
> + *
> + * This software is available under the terms of the GNU General Public License
> + * (GPL) Version 2, available from the file COPYING in the main directory of
> + * this source tree.
> + */
> +
> +#ifndef _QED_OOO_H
> +#define _QED_OOO_H
> +#include <linux/types.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include "qed.h"
> +
> +#define QED_MAX_NUM_ISLES	256
> +#define QED_MAX_NUM_OOO_HISTORY_ENTRIES	512
> +
> +#define QED_OOO_LEFT_BUF	0
> +#define QED_OOO_RIGHT_BUF	1
> +
> +struct qed_ooo_buffer {
> +	struct list_head list_entry;
> +	void *rx_buffer_virt_addr;
> +	dma_addr_t rx_buffer_phys_addr;
> +	u32 rx_buffer_size;
> +	u16 packet_length;
> +	u16 parse_flags;
> +	u16 vlan;
> +	u8 placement_offset;
> +};
> +
> +struct qed_ooo_isle {
> +	struct list_head list_entry;
> +	struct list_head buffers_list;
> +};
> +
> +struct qed_ooo_archipelago {
> +	struct list_head list_entry;
> +	struct list_head isles_list;
> +	u32 cid;
> +};
> +
> +struct qed_ooo_history {
> +	struct ooo_opaque *p_cqes;
> +	u32 head_idx;
> +	u32 num_of_cqes;
> +};
> +
> +struct qed_ooo_info {
> +	struct list_head free_buffers_list;
> +	struct list_head ready_buffers_list;
> +	struct list_head free_isles_list;
> +	struct list_head free_archipelagos_list;
> +	struct list_head archipelagos_list;
> +	struct qed_ooo_archipelago *p_archipelagos_mem;
> +	struct qed_ooo_isle *p_isles_mem;
> +	struct qed_ooo_history ooo_history;
> +	u32 cur_isles_number;
> +	u32 max_isles_number;
> +	u32 gen_isles_number;
> +};
> +
> +void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn,
> +				struct qed_ooo_info *p_ooo_info,
> +				struct ooo_opaque *p_cqe);
> +
> +struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn);
> +
> +void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn,
> +				      struct qed_ooo_info *p_ooo_info,
> +				      u32 cid);
> +
> +void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn,
> +			       struct qed_ooo_info *p_ooo_info);
> +
> +void qed_ooo_setup(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info);
> +
> +void qed_ooo_free(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info);
> +
> +void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn,
> +			     struct qed_ooo_info *p_ooo_info,
> +			     struct qed_ooo_buffer *p_buffer);
> +
> +struct qed_ooo_buffer *
> +qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn,
> +			struct qed_ooo_info *p_ooo_info);
> +
> +void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn,
> +			      struct qed_ooo_info *p_ooo_info,
> +			      struct qed_ooo_buffer *p_buffer, u8 on_tail);
> +
> +struct qed_ooo_buffer *
> +qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn,
> +			 struct qed_ooo_info *p_ooo_info);
> +
> +void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn,
> +			  struct qed_ooo_info *p_ooo_info,
> +			  u32 cid, u8 drop_isle, u8 drop_size);
> +
> +void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn,
> +			  struct qed_ooo_info *p_ooo_info,
> +			  u32 cid,
> +			  u8 ooo_isle, struct qed_ooo_buffer *p_buffer);
> +
> +void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn,
> +			    struct qed_ooo_info *p_ooo_info,
> +			    u32 cid,
> +			    u8 ooo_isle,
> +			    struct qed_ooo_buffer *p_buffer, u8 buffer_side);
> +
> +void qed_ooo_join_isles(struct qed_hwfn *p_hwfn,
> +			struct qed_ooo_info *p_ooo_info, u32 cid,
> +			u8 left_isle);
> +
> +#endif
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c
> index 2343005..1768cdb 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c
> +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c
> @@ -2866,6 +2866,7 @@ static int qed_roce_ll2_tx(struct qed_dev *cdev,
>  	/* Tx header */
>  	rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), roce_ll2->handle,
>  				       1 + pkt->n_seg, 0, flags, 0,
> +				       QED_LL2_TX_DEST_NW,
>  				       qed_roce_flavor, pkt->header.baddr,
>  				       pkt->header.len, pkt, 1);
>  	if (rc) {
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c
> index d3fa578..b44fd4c 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c
> +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c
> @@ -26,6 +26,7 @@
>  #include "qed_int.h"
>  #include "qed_iscsi.h"
>  #include "qed_mcp.h"
> +#include "qed_ooo.h"
>  #include "qed_reg_addr.h"
>  #include "qed_sp.h"
>  #include "qed_sriov.h"
> @@ -253,6 +254,14 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
>  	case PROTOCOLID_ISCSI:
>  		if (!IS_ENABLED(CONFIG_QEDI))
>  			return -EINVAL;
> +		if (p_eqe->opcode == ISCSI_EVENT_TYPE_ASYN_DELETE_OOO_ISLES) {
> +			u32 cid = le32_to_cpu(p_eqe->data.iscsi_info.cid);
> +
> +			qed_ooo_release_connection_isles(p_hwfn,
> +							 p_hwfn->p_ooo_info,
> +							 cid);
> +			return 0;
> +		}
>  
>  		if (p_hwfn->p_iscsi_info->event_cb) {
>  			struct qed_iscsi_info *p_iscsi = p_hwfn->p_iscsi_info;
> 
Hmm. The entire out-of-order handling is pretty generic. I really wonder
if this doesn't apply to iSCSI in general; surely iscsi_tcp suffers from
the same problem, no?
If so, wouldn't it be better to move it into generic (iSCSI) code so
that all implementations would benefit from it?

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		   Teamlead Storage & Networking
hare@suse.de			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)

^ permalink raw reply

* Re: [PATCH] ipv6: don't check for tmp_prefered_lft underflow
From: Jiri Bohac @ 2016-10-19 13:25 UTC (permalink / raw)
  To: David Miller
  Cc: julia.lawall, kuznet, jmorris, yoshfuji, kaber, netdev,
	kbuild-all
In-Reply-To: <20161019131636.zhusbqh63qlbq5vy@dwarf.suse.cz>

The check for an underflow of tmp_prefered_lft is always false
because tmp_prefered_lft is unsigned.

The intention of the check was to guard against racing with an
update of the temp_prefered_lft sysctl, potentially resulting in
an underflow and a very large preferred lifetime. However, the
result of the check in such a situation would be not creating the
temporary address at all, which might be an even worse outcome
than the bogus lifetime.

Drop the faulty check.

Signed-off-by: Jiri Bohac <jbohac@suse.cz>
Reported-by: Julia Lawall <julia.lawall@lip6.fr>
Fixes: 76506a986dc3 ("IPv6: fix DESYNC_FACTOR")

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index cc7c26d..f7c7c2b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1247,9 +1247,6 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 			      idev->cnf.temp_valid_lft + age);
 	tmp_prefered_lft = idev->cnf.temp_prefered_lft + age -
 			    idev->desync_factor;
-	/* guard against underflow in case of concurrent updates to cnf */
-	if (unlikely(tmp_prefered_lft < 0))
-		tmp_prefered_lft = 0;
 	tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft);
 	tmp_plen = ifp->prefix_len;
 	tmp_tstamp = ifp->tstamp;
-- 
Jiri Bohac <jbohac@suse.cz>
SUSE Labs, SUSE CZ

^ permalink raw reply related

* net/netlink: null-ptr-deref in netlink_dump/lock_acquire
From: Andrey Konovalov @ 2016-10-19 14:13 UTC (permalink / raw)
  To: David S. Miller, Johannes Berg, Florian Westphal, Michal Hocko,
	Vlastimil Babka, Herbert Xu, David Decotigny, Mel Gorman,
	David Herrmann, Tom Herbert, Eric Dumazet, netdev, LKML
  Cc: syzkaller, Kostya Serebryany, Alexander Potapenko, Dmitry Vyukov

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

Hi,

I've got the following error report while running the syzkaller fuzzer:

kasan: CONFIG_KASAN_INLINE enabled
kasan: GPF could be caused by NULL-ptr deref or user memory access
general protection fault: 0000 [#1] SMP KASAN
Modules linked in:
CPU: 1 PID: 3933 Comm: syz-executor Not tainted 4.9.0-rc1+ #230
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
task: ffff88006b79d800 task.stack: ffff88006bbc0000
RIP: 0010:[<ffffffff8120872d>]  [<ffffffff8120872d>]
__lock_acquire+0x12d/0x3450 kernel/locking/lockdep.c:3221
RSP: 0018:ffff88006bbc7420  EFLAGS: 00010006
RAX: 0000000000000046 RBX: dffffc0000000000 RCX: 0000000000000000
RDX: 000000000000000c RSI: 0000000000000000 RDI: 0000000000000003
RBP: ffff88006bbc75c0 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000000 R11: ffffffff85f42240 R12: ffff88006b79d800
R13: ffffffff84bfe4e0 R14: 0000000000000001 R15: 0000000000000060
FS:  00007fd9c41cc700(0000) GS:ffff88006cd00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000451f80 CR3: 00000000638f0000 CR4: 00000000000006e0
Stack:
 0000000000000000 ffff88006bbc0000 ffff88006bbc8000 0000000000000000
 0000000000000002 ffff88006b79d800 0000000000000000 ffff88006bbc7f48
 ffffffff852adc60 0000000000000000 ffffffff852adc64 1ffffffff0b40135
Call Trace:
 [<ffffffff8120c5ae>] lock_acquire+0x17e/0x340 kernel/locking/lockdep.c:3746
 [<     inline     >] __mutex_lock_common kernel/locking/mutex.c:521
 [<ffffffff83fb6fe1>] mutex_lock_nested+0xb1/0x890 kernel/locking/mutex.c:621
 [<ffffffff82db6fd0>] netlink_dump+0x50/0xac0 net/netlink/af_netlink.c:2067
 [<ffffffff82dba381>] __netlink_dump_start+0x501/0x770
net/netlink/af_netlink.c:2200
 [<ffffffff82dc35b2>] genl_family_rcv_msg+0xa02/0xc80
net/netlink/genetlink.c:595
 [<ffffffff82dc39e6>] genl_rcv_msg+0x1b6/0x270 net/netlink/genetlink.c:658
 [<ffffffff82dc1a70>] netlink_rcv_skb+0x2c0/0x3b0 net/netlink/af_netlink.c:2281
 [<ffffffff82dc2b98>] genl_rcv+0x28/0x40 net/netlink/genetlink.c:669
 [<     inline     >] netlink_unicast_kernel net/netlink/af_netlink.c:1214
 [<ffffffff82dc0329>] netlink_unicast+0x5a9/0x880 net/netlink/af_netlink.c:1240
 [<ffffffff82dc0fb7>] netlink_sendmsg+0x9b7/0xce0 net/netlink/af_netlink.c:1786
 [<     inline     >] sock_sendmsg_nosec net/socket.c:606
 [<ffffffff82b7075c>] sock_sendmsg+0xcc/0x110 net/socket.c:616
 [<ffffffff82b709c1>] sock_write_iter+0x221/0x3b0 net/socket.c:814
 [<     inline     >] new_sync_write fs/read_write.c:499
 [<ffffffff8151c944>] __vfs_write+0x334/0x570 fs/read_write.c:512
 [<ffffffff8152045b>] vfs_write+0x17b/0x500 fs/read_write.c:560
 [<     inline     >] SYSC_write fs/read_write.c:607
 [<ffffffff81523d84>] SyS_write+0xd4/0x1a0 fs/read_write.c:599
 [<ffffffff83fc0141>] entry_SYSCALL_64_fastpath+0x1f/0xc2
arch/x86/entry/entry_64.S:209
Code: 0f 1f 44 00 00 f6 c4 02 0f 85 24 0a 00 00 44 8b 35 c9 61 8b 03
45 85 f6 74 2c 4c 89 fa 48 bb 00 00 00 00 00 fc ff df 48 c1 ea 03 <80>
3c 1a 00 0f 85 04 2f 00 00 49 81 3f a0 dc 2a 85 41 be 00 00
RIP  [<ffffffff8120872d>] __lock_acquire+0x12d/0x3450
kernel/locking/lockdep.c:3221
 RSP <ffff88006bbc7420>
---[ end trace 685b3c182bf7f25c ]---

The reproducer is attached.

On commit 1a1891d762d6e64daf07b5be4817e3fbb29e3c59 (Oct 18).

[-- Attachment #2: netlink.c --]
[-- Type: text/x-csrc, Size: 5880 bytes --]

// autogenerated by syzkaller (http://github.com/google/syzkaller)

#ifndef __NR_syz_test
#define __NR_syz_test 1000001
#endif
#ifndef __NR_socket
#define __NR_socket 41
#endif
#ifndef __NR_write
#define __NR_write 1
#endif
#ifndef __NR_syz_fuse_mount
#define __NR_syz_fuse_mount 1000004
#endif
#ifndef __NR_syz_open_dev
#define __NR_syz_open_dev 1000002
#endif
#ifndef __NR_mmap
#define __NR_mmap 9
#endif
#ifndef __NR_syz_fuseblk_mount
#define __NR_syz_fuseblk_mount 1000005
#endif
#ifndef __NR_syz_open_pts
#define __NR_syz_open_pts 1000003
#endif

#include <fcntl.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

__thread int skip_segv;
__thread jmp_buf segv_env;

static void segv_handler(int sig, siginfo_t* info, void* uctx)
{
  if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED))
    _longjmp(segv_env, 1);
  exit(sig);
}

static void install_segv_handler()
{
  struct sigaction sa;
  memset(&sa, 0, sizeof(sa));
  sa.sa_sigaction = segv_handler;
  sa.sa_flags = SA_NODEFER | SA_SIGINFO;
  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGBUS, &sa, NULL);
}

#define NONFAILING(...)                                                \
  {                                                                    \
    __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST);               \
    if (_setjmp(segv_env) == 0) {                                      \
      __VA_ARGS__;                                                     \
    }                                                                  \
    __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST);               \
  }

static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
  if (a0 == 0xc || a0 == 0xb) {
    char buf[128];
    sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block",
            (uint8_t)a1, (uint8_t)a2);
    return open(buf, O_RDWR, 0);
  } else {
    char buf[1024];
    char* hash;
    strncpy(buf, (char*)a0, sizeof(buf));
    buf[sizeof(buf) - 1] = 0;
    while ((hash = strchr(buf, '#'))) {
      *hash = '0' + (char)(a1 % 10);
      a1 /= 10;
    }
    return open(buf, a2, 0);
  }
}

static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1)
{
  int ptyno = 0;
  if (ioctl(a0, TIOCGPTN, &ptyno))
    return -1;
  char buf[128];
  sprintf(buf, "/dev/pts/%d", ptyno);
  return open(buf, a1, 0);
}

static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1,
                                uintptr_t a2, uintptr_t a3,
                                uintptr_t a4, uintptr_t a5)
{
  uint64_t target = a0;
  uint64_t mode = a1;
  uint64_t uid = a2;
  uint64_t gid = a3;
  uint64_t maxread = a4;
  uint64_t flags = a5;

  int fd = open("/dev/fuse", O_RDWR);
  if (fd == -1)
    return fd;
  char buf[1024];
  sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd,
          (long)uid, (long)gid, (unsigned)mode & ~3u);
  if (maxread != 0)
    sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
  if (mode & 1)
    strcat(buf, ",default_permissions");
  if (mode & 2)
    strcat(buf, ",allow_other");
  syscall(SYS_mount, "", target, "fuse", flags, buf);
  return fd;
}

static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1,
                                   uintptr_t a2, uintptr_t a3,
                                   uintptr_t a4, uintptr_t a5,
                                   uintptr_t a6, uintptr_t a7)
{
  uint64_t target = a0;
  uint64_t blkdev = a1;
  uint64_t mode = a2;
  uint64_t uid = a3;
  uint64_t gid = a4;
  uint64_t maxread = a5;
  uint64_t blksize = a6;
  uint64_t flags = a7;

  int fd = open("/dev/fuse", O_RDWR);
  if (fd == -1)
    return fd;
  if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199)))
    return fd;
  char buf[256];
  sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd,
          (long)uid, (long)gid, (unsigned)mode & ~3u);
  if (maxread != 0)
    sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
  if (blksize != 0)
    sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize);
  if (mode & 1)
    strcat(buf, ",default_permissions");
  if (mode & 2)
    strcat(buf, ",allow_other");
  syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf);
  return fd;
}

static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1,
                                 uintptr_t a2, uintptr_t a3,
                                 uintptr_t a4, uintptr_t a5,
                                 uintptr_t a6, uintptr_t a7,
                                 uintptr_t a8)
{
  switch (nr) {
  default:
    return syscall(nr, a0, a1, a2, a3, a4, a5);
  case __NR_syz_test:
    return 0;
  case __NR_syz_open_dev:
    return syz_open_dev(a0, a1, a2);
  case __NR_syz_open_pts:
    return syz_open_pts(a0, a1);
  case __NR_syz_fuse_mount:
    return syz_fuse_mount(a0, a1, a2, a3, a4, a5);
  case __NR_syz_fuseblk_mount:
    return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
  }
}

long r[4];

int main()
{
  install_segv_handler();
  memset(r, -1, sizeof(r));
  r[0] = execute_syscall(__NR_mmap, 0x20000000ul, 0x9bc000ul, 0x3ul,
                         0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0);
  r[1] = execute_syscall(__NR_socket, 0x10ul, 0x3ul, 0x10ul, 0, 0, 0, 0,
                         0, 0);
  NONFAILING(memcpy((void*)0x2061a000,
                    "\x1f\x00\x00\x00\x24\x00\x07\x83\xf9\xff\xff\xff"
                    "\xff\xff\xff\xff\x03\x00\x09\x0a\x45\x58\xe2\x93"
                    "\x3e\x00\x03\x92\x04\x00\x1c\x18\x08\x00\xf3\x01"
                    "\x97\x68\x1a",
                    39));
  r[3] = execute_syscall(__NR_write, r[1], 0x2061a000ul, 0x27ul, 0, 0,
                         0, 0, 0, 0);
  return 0;
}

^ permalink raw reply

* Re: [PATCH net v2] flow_dissector: Check skb for VLAN only if skb specified.
From: Eric Garver @ 2016-10-19 14:00 UTC (permalink / raw)
  To: amir; +Cc: Or Gerlitz, David Miller, Linux Netdev List, Hadar Hen Zion
In-Reply-To: <CAPcc5Pg=vy67dMcr4U6DFwZu5UgSWyV6c662cv9XR-QXKfUVXw@mail.gmail.com>

On Wed, Oct 19, 2016 at 11:44:10AM +0300, Amir Vadai wrote:
> On Tue, Oct 18, 2016 at 4:59 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
> > On Mon, Oct 17, 2016 at 11:30 PM, Eric Garver <e@erig.me> wrote:
> >> Fixes a panic when calling eth_get_headlen(). Noticed on i40e driver.
> >>
> >> Fixes: d5709f7ab776 ("flow_dissector: For stripped vlan, get vlan info from skb->vlan_tci")
> >> Signed-off-by: Eric Garver <e@erig.me>
> >
> > Dave,
> >
> > Hadar is OOO and I have asked Amir to look on the fix, will appreciate
> > if we can have 24 hours to respond
> >
> > Or.
> 
> Reviewed and tested.
> 
> Acked-by: Amir Vadai <amir@vadai.me>

Amir,

Your ACK may have been lost on its way to the list. I received it, but
it's not showing on any list archives or patchwork.

^ permalink raw reply

* RE: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Haiyang Zhang via Virtualization @ 2016-10-19 14:07 UTC (permalink / raw)
  To: Jarod Wilson, linux-kernel@vger.kernel.org
  Cc: Michael S. Tsirkin, VMware, Inc., netdev@vger.kernel.org,
	virtualization@lists.linux-foundation.org, Shrikrishna Khare
In-Reply-To: <20161019023333.15760-6-jarod@redhat.com>



> -----Original Message-----
> From: Jarod Wilson [mailto:jarod@redhat.com]
> Sent: Tuesday, October 18, 2016 10:34 PM
> To: linux-kernel@vger.kernel.org
> Cc: Jarod Wilson <jarod@redhat.com>; netdev@vger.kernel.org;
> virtualization@lists.linux-foundation.org; KY Srinivasan
> <kys@microsoft.com>; Haiyang Zhang <haiyangz@microsoft.com>; Michael S.
> Tsirkin <mst@redhat.com>; Shrikrishna Khare <skhare@vmware.com>; VMware,
> Inc. <pv-drivers@vmware.com>
> Subject: [PATCH net-next 5/6] net: use core MTU range checking in virt
> drivers
> 
> hyperv_net:
> - set min/max_mtu
> 
> virtio_net:
> - set min/max_mtu
> - remove virtnet_change_mtu
> 
> vmxnet3:
> - set min/max_mtu
> 
> CC: netdev@vger.kernel.org
> CC: virtualization@lists.linux-foundation.org
> CC: "K. Y. Srinivasan" <kys@microsoft.com>
> CC: Haiyang Zhang <haiyangz@microsoft.com>
> CC: "Michael S. Tsirkin" <mst@redhat.com>
> CC: Shrikrishna Khare <skhare@vmware.com>
> CC: "VMware, Inc." <pv-drivers@vmware.com>
> Signed-off-by: Jarod Wilson <jarod@redhat.com>
> ---
>  drivers/net/hyperv/hyperv_net.h   |  4 ++--
>  drivers/net/hyperv/netvsc_drv.c   | 14 +++++++-------
>  drivers/net/virtio_net.c          | 23 ++++++++++-------------
>  drivers/net/vmxnet3/vmxnet3_drv.c |  7 ++++---
>  4 files changed, 23 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/net/hyperv/hyperv_net.h
> b/drivers/net/hyperv/hyperv_net.h
> index f4fbcb5..3958ada 100644
> --- a/drivers/net/hyperv/hyperv_net.h
> +++ b/drivers/net/hyperv/hyperv_net.h
> @@ -606,8 +606,8 @@ struct nvsp_message {
>  } __packed;
> 
> 
> -#define NETVSC_MTU 65536
> -#define NETVSC_MTU_MIN 68
> +#define NETVSC_MTU 65535

Why change it to 65535? For Hyperv host, this should be 65536.


> @@ -1343,6 +1336,13 @@ static int netvsc_probe(struct hv_device *dev,
> 
>  	netif_carrier_off(net);
> 
> +	/* MTU range: 68 - 1500 or 65521 */
> +	net->min_mtu = NETVSC_MTU_MIN;
> +	if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
> +		net->max_mtu = NETVSC_MTU - ETH_HLEN;
> +	else
> +		net->max_mtu = ETH_DATA_LEN;
> +
>  	netvsc_init_settings(net);
> 
>  	net_device_ctx = netdev_priv(net);

nvdev->nvsp_version is not set until after rndis_filter_device_add()
is successfully completed.
You need to move this part to the place just before this line:
 ret = register_netdev(net);

Thanks,
- Haiyang

^ permalink raw reply

* Re: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Michael S. Tsirkin @ 2016-10-19 14:03 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: VMware, Inc., netdev, Haiyang Zhang, linux-kernel, virtualization,
	Shrikrishna Khare
In-Reply-To: <20161019165629-mutt-send-email-mst@kernel.org>

On Wed, Oct 19, 2016 at 04:59:46PM +0300, Michael S. Tsirkin wrote:
> On Tue, Oct 18, 2016 at 10:33:32PM -0400, Jarod Wilson wrote:
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index fad84f3..4885a42 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -1419,17 +1419,6 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
> >  	.set_settings = virtnet_set_settings,
> >  };
> >  
> > -#define MIN_MTU 68
> > -#define MAX_MTU 65535
> > -
> > -static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
> > -{
> > -	if (new_mtu < MIN_MTU || new_mtu > MAX_MTU)
> > -		return -EINVAL;
> > -	dev->mtu = new_mtu;
> > -	return 0;
> > -}
> > -
> >  static const struct net_device_ops virtnet_netdev = {
> >  	.ndo_open            = virtnet_open,
> >  	.ndo_stop   	     = virtnet_close,
> > @@ -1437,7 +1426,6 @@ static const struct net_device_ops virtnet_netdev = {
> >  	.ndo_validate_addr   = eth_validate_addr,
> >  	.ndo_set_mac_address = virtnet_set_mac_address,
> >  	.ndo_set_rx_mode     = virtnet_set_rx_mode,
> > -	.ndo_change_mtu	     = virtnet_change_mtu,
> >  	.ndo_get_stats64     = virtnet_stats,
> >  	.ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
> >  	.ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
> > @@ -1748,6 +1736,9 @@ static bool virtnet_validate_features(struct virtio_device *vdev)
> >  	return true;
> >  }
> >  
> > +#define MIN_MTU ETH_MIN_MTU
> > +#define MAX_MTU 65535
> > +
> 
> Do we need a common macro for this?

I think it's actually IP_MAX_MTU.

> >  static int virtnet_probe(struct virtio_device *vdev)
> >  {
> >  	int i, err;
> > @@ -1821,6 +1812,10 @@ static int virtnet_probe(struct virtio_device *vdev)
> >  
> >  	dev->vlan_features = dev->features;
> >  
> > +	/* MTU range: 68 - 65535 */
> > +	dev->min_mtu = MIN_MTU;
> > +	dev->max_mtu = MAX_MTU;
> > +
> >  	/* Configuration may specify what MAC to use.  Otherwise random. */
> >  	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC))
> >  		virtio_cread_bytes(vdev,
> > @@ -1875,8 +1870,10 @@ static int virtnet_probe(struct virtio_device *vdev)
> >  		mtu = virtio_cread16(vdev,
> >  				     offsetof(struct virtio_net_config,
> >  					      mtu));
> > -		if (virtnet_change_mtu(dev, mtu))
> > +		if (mtu >= dev->min_mtu && mtu <= dev->max_mtu) {
> > +			dev->mtu = mtu;
> >  			__virtio_clear_bit(vdev, VIRTIO_NET_F_MTU);
> 
> I think the logic is wrong here:
> 
> If mtu is legal, we set it but do not tell host.
> If it's out of range we tell host we use it
> but don't actually.
> 
> Should be the reverse.
> 
> > +		}
> >  	}
> >  
> >  	if (vi->any_header_sg)
> 
> -- 
> MST

^ permalink raw reply

* Re: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Michael S. Tsirkin @ 2016-10-19 13:59 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: VMware, Inc., netdev, Haiyang Zhang, linux-kernel, virtualization,
	Shrikrishna Khare
In-Reply-To: <20161019023333.15760-6-jarod@redhat.com>

On Tue, Oct 18, 2016 at 10:33:32PM -0400, Jarod Wilson wrote:
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index fad84f3..4885a42 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -1419,17 +1419,6 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
>  	.set_settings = virtnet_set_settings,
>  };
>  
> -#define MIN_MTU 68
> -#define MAX_MTU 65535
> -
> -static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
> -{
> -	if (new_mtu < MIN_MTU || new_mtu > MAX_MTU)
> -		return -EINVAL;
> -	dev->mtu = new_mtu;
> -	return 0;
> -}
> -
>  static const struct net_device_ops virtnet_netdev = {
>  	.ndo_open            = virtnet_open,
>  	.ndo_stop   	     = virtnet_close,
> @@ -1437,7 +1426,6 @@ static const struct net_device_ops virtnet_netdev = {
>  	.ndo_validate_addr   = eth_validate_addr,
>  	.ndo_set_mac_address = virtnet_set_mac_address,
>  	.ndo_set_rx_mode     = virtnet_set_rx_mode,
> -	.ndo_change_mtu	     = virtnet_change_mtu,
>  	.ndo_get_stats64     = virtnet_stats,
>  	.ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
>  	.ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
> @@ -1748,6 +1736,9 @@ static bool virtnet_validate_features(struct virtio_device *vdev)
>  	return true;
>  }
>  
> +#define MIN_MTU ETH_MIN_MTU
> +#define MAX_MTU 65535
> +

Do we need a common macro for this?

>  static int virtnet_probe(struct virtio_device *vdev)
>  {
>  	int i, err;
> @@ -1821,6 +1812,10 @@ static int virtnet_probe(struct virtio_device *vdev)
>  
>  	dev->vlan_features = dev->features;
>  
> +	/* MTU range: 68 - 65535 */
> +	dev->min_mtu = MIN_MTU;
> +	dev->max_mtu = MAX_MTU;
> +
>  	/* Configuration may specify what MAC to use.  Otherwise random. */
>  	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC))
>  		virtio_cread_bytes(vdev,
> @@ -1875,8 +1870,10 @@ static int virtnet_probe(struct virtio_device *vdev)
>  		mtu = virtio_cread16(vdev,
>  				     offsetof(struct virtio_net_config,
>  					      mtu));
> -		if (virtnet_change_mtu(dev, mtu))
> +		if (mtu >= dev->min_mtu && mtu <= dev->max_mtu) {
> +			dev->mtu = mtu;
>  			__virtio_clear_bit(vdev, VIRTIO_NET_F_MTU);

I think the logic is wrong here:

If mtu is legal, we set it but do not tell host.
If it's out of range we tell host we use it
but don't actually.

Should be the reverse.

> +		}
>  	}
>  
>  	if (vi->any_header_sg)

-- 
MST

^ permalink raw reply

* Re: [PATCH v2 34/37] docs: fix locations of several documents that got moved
From: Mauro Carvalho Chehab @ 2016-10-19 13:59 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Linux Doc Mailing List, Mauro Carvalho Chehab, Jonathan Corbet,
	Rafael J. Wysocki, Len Brown, Jens Axboe, Alessandro Zummo,
	Alexandre Belloni, Rob Herring, Mark Rutland, Jean Delvare,
	Guenter Roeck, Karsten Keil, Mauro Carvalho Chehab,
	Steffen Klassert, Greg Kroah-Hartman, Johannes Berg,
	Jaroslav Kysela, Takashi Iwai, Paolo Bonzini,
	Radim Krčmář, Wim Van Sebroeck
In-Reply-To: <20161019103442.GD1461@amd>

Em Wed, 19 Oct 2016 12:34:42 +0200
Pavel Machek <pavel-+ZI9xUNit7I@public.gmane.org> escreveu:

> Hi!
> 
> 
> > --- a/Documentation/ABI/testing/sysfs-kernel-slab
> > +++ b/Documentation/ABI/testing/sysfs-kernel-slab
> > @@ -347,7 +347,7 @@ Description:
> >  		because of fragmentation, SLUB will retry with the minimum order
> >  		possible depending on its characteristics.
> >  		When debug_guardpage_minorder=N (N > 0) parameter is specified
> > -		(see Documentation/kernel-parameters.txt), the minimum possible
> > +		(see Documentation/admin-guide/kernel-parameters.rst), the minimum possible
> >  		order is used and this sysfs entry can not be used to change
> >  		the order at run time.  
> 
> Dunno, but kernel-parameters.txt was already quite long... for a file
> that is referenced quite often. Adding admin-guide/ into the path does
> not really help.

The big string name starts with Documentation/ :) There are some discussions 
about changing it to doc/ (or docs/). Also, as you said, kernel-parameters
is already a big name. Perhaps we could use, instead, "kernel-parms".

If we rename kernel-parameters.rst to kernel-parms.rst, plus the doc/ rename,
then the string size will actually reduce:

-		(see Documentation/kernel-parameters.txt), the minimum possible
+		(see doc/admin-guide/kernel-parms.rst), the minimum possible

> Maybe admin-guide should go directly into Documentation/ , as that's
> what our users are interested in?

There are several problems if we keep them at Documentation/ dir:

- We'll end by mixing documents already converted to ReST with documents
  not converted yet;

- A rename is needed anyway, as Sphinx only accepts ReST files that end
  with the extension(s) defined at Documentation/conf.py (currently,
  .rst);

- A partial documentation build is made by sub-directory. If we put
  files under /Documentation, there's no way to build just one
  book.

> Plus, I'm not sure how many developers will figure out that process/
> is what describes kernel patch submission process. We have processes
> in the kernel, after all...

Do you have a better idea?

> Could we leave symlinks in place? 

My initial patch did that. It created symlinks on the
Documentation/user (with was the previous name for the admin's guide).

The big issue is how to identify what files at Documentation/ that
were not converted yet.

On this patch series, we opted to move the file and keep just a
reference to the most relevant ones, pointing to the new location:
	https://git.linuxtv.org/mchehab/experimental.git/commit/?h=lkml-books-v2&id=217462c76ee1c12b45750723059a3461018b6975
	https://git.linuxtv.org/mchehab/experimental.git/commit/?h=lkml-books-v2&id=d15595a318356804ed1bc42f509e72de9d031513

We could do something similar to Documentation/kernel-parameters.txt.
Yet, the best is, IMHO, to keep this on the absolute minimum of files,
as it also makes harder to identify what txt files still require conversion.

> People say "please follow
> CodingStyle" when reviewing patches. Saying "please follow
> process/conding-style.rst" ... somehow I don't think that's going to
> happen.

As we have a Documentation/CodingStyle (pointing to the new location),
this should still work. 

That's said, using my maintainer's hat, when I review patches from a newbie,
I actually prefer to point them to some location with the current
practices, as they usually don't know much about the Kernel's way to
receive patch. So, I reply with something like:

	"Please read this:
		https://mchehab.fedorapeople.org/kernel_docs/process/index.html
	 with instructions about how to submit your work"

For an old contributor, I just say: "please follow the coding style" 
or I reply with the output of checkpatch.pl.

Maybe it is just me, but I very much prefer to point to some URL with
everything packed altogether than to do several reviews until someone
RTFM (Read The Fine Manual), and starts to submit it right.

Thanks,
Mauro

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply

* Re: [PATCH net-next 4/6] net: use core MTU range checking in core net infra
From: Sabrina Dubroca @ 2016-10-19 13:55 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, netdev, Nicolas Dichtel, Hannes Frederic Sowa,
	Tom Herbert, Daniel Borkmann, Alexander Duyck, Paolo Abeni,
	Jiri Benc, WANG Cong, Roopa Prabhu, Pravin B Shelar,
	Patrick McHardy, Stephen Hemminger, Pravin Shelar
In-Reply-To: <20161019023333.15760-5-jarod@redhat.com>

2016-10-18, 22:33:31 -0400, Jarod Wilson wrote:
> geneve:
> - Merge __geneve_change_mtu back into geneve_change_mtu, set max_mtu
> - This one isn't quite as straight-forward as others, could use some
>   closer inspection and testing
> 
> macvlan:
> - set min/max_mtu
> 
> tun:
> - set min/max_mtu, remove tun_net_change_mtu
> 
> vxlan:
> - Merge __vxlan_change_mtu back into vxlan_change_mtu, set min/max_mtu
> - This one is also not as straight-forward and could use closer inspection
>   and testing from vxlan folks
> 
> bridge:
> - set max_mtu via br_min_mtu()
> 
> openvswitch:
> - set min/max_mtu, remove internal_dev_change_mtu
> - note: max_mtu wasn't checked previously, it's been set to 65535, which
>   is the largest possible size supported
> 
> sch_teql:
> - set min/max_mtu (note: max_mtu previously unchecked, used max of 65535)

Nothing for other virtual netdevices? (dummy, veth, bond, etc) Their
MTU is limited to 1500 now.  Also missing macsec and ip_gre, probably
others that are using ether_setup.


[...]
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index 89a687f..81fc79a 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -184,17 +184,15 @@ static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev,
>  
>  static int br_change_mtu(struct net_device *dev, int new_mtu)
>  {
> +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
>  	struct net_bridge *br = netdev_priv(dev);
> -	if (new_mtu < 68 || new_mtu > br_min_mtu(br))
> -		return -EINVAL;
> -
> -	dev->mtu = new_mtu;
>  
> -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
>  	/* remember the MTU in the rtable for PMTU */
>  	dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
>  #endif
>  
> +	dev->mtu = new_mtu;
> +
>  	return 0;
>  }
>  
> @@ -390,6 +388,7 @@ void br_dev_setup(struct net_device *dev)
>  	dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
>  			   NETIF_F_HW_VLAN_STAG_TX;
>  	dev->vlan_features = COMMON_FEATURES;
> +	dev->max_mtu = br_min_mtu(br);

br_min_mtu uses br->port_list, which is only initialized a few lines
later (right after the spin_lock_init() at the end of the context of
this diff).

Besides, I don't think this works: br_min_mtu(br) changes when you add
and remove ports, or when you change the MTU of an enslaved
device. But this makes the max MTU for the bridge fixed (to 1500).

>  
>  	br->dev = dev;
>  	spin_lock_init(&br->lock);

> diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
> index e7da290..d5d6cae 100644
> --- a/net/openvswitch/vport-internal_dev.c
> +++ b/net/openvswitch/vport-internal_dev.c
> @@ -89,15 +89,6 @@ static const struct ethtool_ops internal_dev_ethtool_ops = {
>  	.get_link	= ethtool_op_get_link,
>  };
>  
> -static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
> -{
> -	if (new_mtu < 68)
> -		return -EINVAL;
> -
> -	netdev->mtu = new_mtu;
> -	return 0;
> -}
> -
>  static void internal_dev_destructor(struct net_device *dev)
>  {
>  	struct vport *vport = ovs_internal_dev_get_vport(dev);
> @@ -148,7 +139,6 @@ static const struct net_device_ops internal_dev_netdev_ops = {
>  	.ndo_stop = internal_dev_stop,
>  	.ndo_start_xmit = internal_dev_xmit,
>  	.ndo_set_mac_address = eth_mac_addr,
> -	.ndo_change_mtu = internal_dev_change_mtu,
>  	.ndo_get_stats64 = internal_get_stats,
>  	.ndo_set_rx_headroom = internal_set_rx_headroom,
>  };

vport-internal uses ether_setup, so the MTU is currently limited to
1500, no?


-- 
Sabrina

^ permalink raw reply

* [PATCH net-next] hv_netvsc: fix a race between netvsc_send() and netvsc_init_buf()
From: Vitaly Kuznetsov @ 2016-10-19 13:53 UTC (permalink / raw)
  To: netdev; +Cc: devel, Haiyang Zhang, Stephen Hemminger, linux-kernel

Fix in commit 880988348270 ("hv_netvsc: set nvdev link after populating
chn_table") turns out to be incomplete. A crash in
netvsc_get_next_send_section() is observed on mtu change when the device
is under load. The race I identified is: if we get to netvsc_send() after
we set net_device_ctx->nvdev link in netvsc_device_add() but before we
finish netvsc_connect_vsp()->netvsc_init_buf() send_section_map is not
allocated and we crash. Unfortunately we can't set net_device_ctx->nvdev
link after the netvsc_init_buf() call as during the negotiation we need
to receive packets and on the receive path we check for it. It would
probably be possible to split nvdev into a pair of nvdev_in and nvdev_out
links and check them accordingly in get_outbound_net_device()/
get_inbound_net_device() but this looks like an overkill.

Check that send_section_map is allocated in netvsc_send().

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
 drivers/net/hyperv/netvsc.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 720b5fa..e2bfaac 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -888,6 +888,13 @@ int netvsc_send(struct hv_device *device,
 	if (!net_device)
 		return -ENODEV;
 
+	/* We may race with netvsc_connect_vsp()/netvsc_init_buf() and get
+	 * here before the negotiation with the host is finished and
+	 * send_section_map may not be allocated yet.
+	 */
+	if (!net_device->send_section_map)
+		return -EAGAIN;
+
 	out_channel = net_device->chn_table[q_idx];
 
 	packet->send_buf_index = NETVSC_INVALID_INDEX;
-- 
2.7.4

^ permalink raw reply related

* net/ipx: null-ptr-deref in ipxrtr_route_packet
From: Andrey Konovalov @ 2016-10-19 13:16 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, David S. Miller, netdev, LKML
  Cc: syzkaller, Kostya Serebryany, Alexander Potapenko, Sasha Levin,
	Eric Dumazet, Dmitry Vyukov

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

Hi,

I've got the following error report while running the syzkaller fuzzer:

kasan: CONFIG_KASAN_INLINE enabled
kasan: GPF could be caused by NULL-ptr deref or user memory access
general protection fault: 0000 [#1] SMP KASAN
Modules linked in:
CPU: 0 PID: 3953 Comm: syz-executor Not tainted 4.9.0-rc1+ #228
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
task: ffff88006aa2ac00 task.stack: ffff880068a90000
RIP: 0010:[<ffffffff834ed8f4>]  [<ffffffff834ed8f4>]
ipxrtr_route_packet+0x4e4/0xbe0 net/ipx/ipx_route.c
:213
RSP: 0018:ffff880068a97b08  EFLAGS: 00010246
RAX: ffff88006b648500 RBX: ffff880068a97e40 RCX: dffffc0000000000
RDX: 0000000000000003 RSI: 0000000000000000 RDI: ffff88006b648960
RBP: ffff880068a97bc8 R08: dffffc0000000000 R09: 1ffff1000d4ddf97
R10: dffffc0000000000 R11: 0000000000000000 R12: ffff88006b410300
R13: 0000000000000000 R14: ffff88006444b68e R15: ffff88006a6efc80
FS:  00007f28cf665700(0000) GS:ffff88006cc00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000451f80 CR3: 0000000068a9a000 CR4: 00000000000006f0
Stack:
 ffff88006a6efd58 ffff880068a97dc0 ffff880068a97e44 1ffff1000d152f68
 000000000000001a 0000000000000000 ffff88006b648500 0000000041b58ab3
 ffffffff847fb90b ffffffff834ed410 ffffffff82b7cfea ffff8800ffffff97
Call Trace:
 [<ffffffff834e802e>] ipx_sendmsg+0x30e/0x550 net/ipx/af_ipx.c:1749
 [<     inline     >] sock_sendmsg_nosec net/socket.c:606
 [<ffffffff82b7075c>] sock_sendmsg+0xcc/0x110 net/socket.c:616
 [<ffffffff82b718a1>] SYSC_sendto+0x211/0x340 net/socket.c:1641
 [<ffffffff82b73e80>] SyS_sendto+0x40/0x50 net/socket.c:1609
 [<ffffffff83fc0141>] entry_SYSCALL_64_fastpath+0x1f/0xc2
arch/x86/entry/entry_64.S:209
Code: 41 80 7c 0d 00 00 0f 85 82 06 00 00 48 8b 85 70 ff ff ff 49 b8
00 00 00 00 00 fc ff df 4c 8b a8 60 04 00 00 4c 89 ee 48 c1 ee 03 <46>
0f b6 0c 06 45 84 c9 74 0a 41 80 f9 03 0f 8e e5 05 00 00 49
RIP  [<ffffffff834ed8f4>] ipxrtr_route_packet+0x4e4/0xbe0
net/ipx/ipx_route.c:213
 RSP <ffff880068a97b08>
---[ end trace f5bc9a28de6b2776 ]---
==================================================================

For some reason ipxs->intrfc ends up being NULL.

The reproducer is attached, you need to run a few instances simultaneously.

In case it's relevant, this is what I have in /etc/network/interfaces:

auto eth1
iface eth1 inet static
address 192.168.1.5
netmask 255.255.255.0
post-up arp -s 192.168.1.6 aa:aa:aa:aa:aa:aa
iface eth1 ipx static
frame EtherII
netnum 0x42424242

On commit 1a1891d762d6e64daf07b5be4817e3fbb29e3c59 (Oct 18).

[-- Attachment #2: ipx.c --]
[-- Type: text/x-csrc, Size: 7417 bytes --]

// autogenerated by syzkaller (http://github.com/google/syzkaller)

#ifndef __NR_sendto
#define __NR_sendto 44
#endif
#ifndef __NR_syz_fuse_mount
#define __NR_syz_fuse_mount 1000004
#endif
#ifndef __NR_syz_open_dev
#define __NR_syz_open_dev 1000002
#endif
#ifndef __NR_syz_test
#define __NR_syz_test 1000001
#endif
#ifndef __NR_mmap
#define __NR_mmap 9
#endif
#ifndef __NR_socket
#define __NR_socket 41
#endif
#ifndef __NR_bind
#define __NR_bind 49
#endif
#ifndef __NR_syz_fuseblk_mount
#define __NR_syz_fuseblk_mount 1000005
#endif
#ifndef __NR_syz_open_pts
#define __NR_syz_open_pts 1000003
#endif

#include <fcntl.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

__thread int skip_segv;
__thread jmp_buf segv_env;

static void segv_handler(int sig, siginfo_t* info, void* uctx)
{
  if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED))
    _longjmp(segv_env, 1);
  exit(sig);
}

static void install_segv_handler()
{
  struct sigaction sa;
  memset(&sa, 0, sizeof(sa));
  sa.sa_sigaction = segv_handler;
  sa.sa_flags = SA_NODEFER | SA_SIGINFO;
  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGBUS, &sa, NULL);
}

#define NONFAILING(...)                                                \
  {                                                                    \
    __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST);               \
    if (_setjmp(segv_env) == 0) {                                      \
      __VA_ARGS__;                                                     \
    }                                                                  \
    __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST);               \
  }

static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
  if (a0 == 0xc || a0 == 0xb) {
    char buf[128];
    sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block",
            (uint8_t)a1, (uint8_t)a2);
    return open(buf, O_RDWR, 0);
  } else {
    char buf[1024];
    char* hash;
    strncpy(buf, (char*)a0, sizeof(buf));
    buf[sizeof(buf) - 1] = 0;
    while ((hash = strchr(buf, '#'))) {
      *hash = '0' + (char)(a1 % 10);
      a1 /= 10;
    }
    return open(buf, a2, 0);
  }
}

static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1)
{
  int ptyno = 0;
  if (ioctl(a0, TIOCGPTN, &ptyno))
    return -1;
  char buf[128];
  sprintf(buf, "/dev/pts/%d", ptyno);
  return open(buf, a1, 0);
}

static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1,
                                uintptr_t a2, uintptr_t a3,
                                uintptr_t a4, uintptr_t a5)
{
  uint64_t target = a0;
  uint64_t mode = a1;
  uint64_t uid = a2;
  uint64_t gid = a3;
  uint64_t maxread = a4;
  uint64_t flags = a5;

  int fd = open("/dev/fuse", O_RDWR);
  if (fd == -1)
    return fd;
  char buf[1024];
  sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd,
          (long)uid, (long)gid, (unsigned)mode & ~3u);
  if (maxread != 0)
    sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
  if (mode & 1)
    strcat(buf, ",default_permissions");
  if (mode & 2)
    strcat(buf, ",allow_other");
  syscall(SYS_mount, "", target, "fuse", flags, buf);
  return fd;
}

static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1,
                                   uintptr_t a2, uintptr_t a3,
                                   uintptr_t a4, uintptr_t a5,
                                   uintptr_t a6, uintptr_t a7)
{
  uint64_t target = a0;
  uint64_t blkdev = a1;
  uint64_t mode = a2;
  uint64_t uid = a3;
  uint64_t gid = a4;
  uint64_t maxread = a5;
  uint64_t blksize = a6;
  uint64_t flags = a7;

  int fd = open("/dev/fuse", O_RDWR);
  if (fd == -1)
    return fd;
  if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199)))
    return fd;
  char buf[256];
  sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd,
          (long)uid, (long)gid, (unsigned)mode & ~3u);
  if (maxread != 0)
    sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
  if (blksize != 0)
    sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize);
  if (mode & 1)
    strcat(buf, ",default_permissions");
  if (mode & 2)
    strcat(buf, ",allow_other");
  syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf);
  return fd;
}

static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1,
                                 uintptr_t a2, uintptr_t a3,
                                 uintptr_t a4, uintptr_t a5,
                                 uintptr_t a6, uintptr_t a7,
                                 uintptr_t a8)
{
  switch (nr) {
  default:
    return syscall(nr, a0, a1, a2, a3, a4, a5);
  case __NR_syz_test:
    return 0;
  case __NR_syz_open_dev:
    return syz_open_dev(a0, a1, a2);
  case __NR_syz_open_pts:
    return syz_open_pts(a0, a1);
  case __NR_syz_fuse_mount:
    return syz_fuse_mount(a0, a1, a2, a3, a4, a5);
  case __NR_syz_fuseblk_mount:
    return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
  }
}

long r[18];

int main()
{
  install_segv_handler();
  memset(r, -1, sizeof(r));
  r[0] = execute_syscall(__NR_mmap, 0x20000000ul, 0xe000ul, 0x3ul,
                         0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0);
  r[1] = execute_syscall(__NR_socket, 0x4ul, 0x2ul, 0x0ul, 0, 0, 0, 0,
                         0, 0);
  r[2] = execute_syscall(__NR_mmap, 0x2000e000ul, 0x1000ul, 0x3ul,
                         0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0);
  NONFAILING(*(uint16_t*)0x2000e000 = (uint16_t)0x2);
  NONFAILING(*(uint16_t*)0x2000e002 = (uint16_t)0x4242);
  NONFAILING(*(uint32_t*)0x2000e004 = (uint32_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e008 = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e009 = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00a = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00b = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00c = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00d = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00e = (uint8_t)0x0);
  NONFAILING(*(uint8_t*)0x2000e00f = (uint8_t)0x0);
  r[14] = execute_syscall(__NR_bind, r[1], 0x2000e000ul, 0x10ul, 0, 0,
                          0, 0, 0, 0);
  NONFAILING(memcpy((void*)0x20007fe6, "\xae\x89\xfa\x41\xd0\x29\x69"
                                       "\xc3\xac\x79\xf0\x64\xc6\xad"
                                       "\x6b\x27\x63\x6b\x49\x0d\x99"
                                       "\x7b\x01\x9e\x3e\xb3",
                    26));
  NONFAILING(memcpy(
      (void*)0x2000d000,
      "\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00",
      128));
  r[17] = execute_syscall(__NR_sendto, r[1], 0x20007fe6ul, 0x1aul,
                          0x0ul, 0x2000d000ul, 0x80ul, 0, 0, 0);
  return 0;
}

^ permalink raw reply

* Re: [PATCH net-next 5/6] net: use core MTU range checking in virt drivers
From: Aaron Conole @ 2016-10-19 13:06 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, netdev, virtualization, K. Y. Srinivasan,
	Haiyang Zhang, Michael S. Tsirkin, Shrikrishna Khare,
	VMware, Inc.
In-Reply-To: <20161019023333.15760-6-jarod@redhat.com>

Jarod Wilson <jarod@redhat.com> writes:

> hyperv_net:
> - set min/max_mtu
>
> virtio_net:
> - set min/max_mtu
> - remove virtnet_change_mtu
>
> vmxnet3:
> - set min/max_mtu
>
> CC: netdev@vger.kernel.org
> CC: virtualization@lists.linux-foundation.org
> CC: "K. Y. Srinivasan" <kys@microsoft.com>
> CC: Haiyang Zhang <haiyangz@microsoft.com>
> CC: "Michael S. Tsirkin" <mst@redhat.com>
> CC: Shrikrishna Khare <skhare@vmware.com>
> CC: "VMware, Inc." <pv-drivers@vmware.com>
> Signed-off-by: Jarod Wilson <jarod@redhat.com>
> ---


The virtnet change looks good to me.  It simplifies an enhancement that
I was about to propose, so I'll let yours land first.

-Aaron

^ permalink raw reply

* [PATCH net-next v4 3/3] udp: use it's own memory accounting schema
From: Paolo Abeni @ 2016-10-19 12:47 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: David S. Miller, James Morris, Trond Myklebust, Alexander Duyck,
	Daniel Borkmann, Eric Dumazet, Tom Herbert, Hannes Frederic Sowa,
	Edward Cree, linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1476877189.git.pabeni-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Completely avoid default sock memory accounting and replace it
with udp-specific accounting.

Since the new memory accounting model encapsulates completely
the required locking, remove the socket lock on both enqueue and
dequeue, and avoid using the backlog on enqueue.

Be sure to clean-up rx queue memory on socket destruction, using
udp its own sk_destruct.

Tested using pktgen with random src port, 64 bytes packet,
wire-speed on a 10G link as sender and udp_sink as the receiver,
using an l4 tuple rxhash to stress the contention, and one or more
udp_sink instances with reuseport.

nr readers      Kpps (vanilla)  Kpps (patched)
1               170             420
3               1250            1900
6               3000            3500
9               4200            4400
12              5700            6100

v3 -> v4:
  - remove useless sk_rcvqueues_full() call

v2 -> v3:
  - do not set the now unsed backlog_rcv callback

v1 -> v2:
  - add memory pressure support
  - fixed dropwatch accounting for ipv6

Acked-by: Hannes Frederic Sowa <hannes-tFNcAqjVMyqKXQKiL6tip0B+6BGkLq7r@public.gmane.org>
Signed-off-by: Paolo Abeni <pabeni-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 net/ipv4/udp.c        | 42 ++++++++----------------------------------
 net/ipv6/udp.c        | 34 ++++++++--------------------------
 net/sunrpc/svcsock.c  | 20 ++++++++++++++++----
 net/sunrpc/xprtsock.c |  2 +-
 4 files changed, 33 insertions(+), 65 deletions(-)

diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 7047bb3..1925ca7 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1312,13 +1312,8 @@ static int first_packet_length(struct sock *sk)
 	res = skb ? skb->len : -1;
 	spin_unlock_bh(&rcvq->lock);
 
-	if (!skb_queue_empty(&list_kill)) {
-		bool slow = lock_sock_fast(sk);
-
+	if (!skb_queue_empty(&list_kill))
 		__skb_queue_purge(&list_kill);
-		sk_mem_reclaim_partial(sk);
-		unlock_sock_fast(sk, slow);
-	}
 	return res;
 }
 
@@ -1367,7 +1362,6 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
 	int err;
 	int is_udplite = IS_UDPLITE(sk);
 	bool checksum_valid = false;
-	bool slow;
 
 	if (flags & MSG_ERRQUEUE)
 		return ip_recv_error(sk, msg, len, addr_len);
@@ -1408,13 +1402,12 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
 	}
 
 	if (unlikely(err)) {
-		trace_kfree_skb(skb, udp_recvmsg);
 		if (!peeked) {
 			atomic_inc(&sk->sk_drops);
 			UDP_INC_STATS(sock_net(sk),
 				      UDP_MIB_INERRORS, is_udplite);
 		}
-		skb_free_datagram_locked(sk, skb);
+		kfree_skb(skb);
 		return err;
 	}
 
@@ -1439,16 +1432,15 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
 	if (flags & MSG_TRUNC)
 		err = ulen;
 
-	__skb_free_datagram_locked(sk, skb, peeking ? -err : err);
+	skb_consume_udp(sk, skb, peeking ? -err : err);
 	return err;
 
 csum_copy_err:
-	slow = lock_sock_fast(sk);
-	if (!skb_kill_datagram(sk, skb, flags)) {
+	if (!__sk_queue_drop_skb(sk, skb, flags)) {
 		UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
 		UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
 	}
-	unlock_sock_fast(sk, slow);
+	kfree_skb(skb);
 
 	/* starting over for a new packet, but check if we need to yield */
 	cond_resched();
@@ -1567,7 +1559,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 		sk_incoming_cpu_update(sk);
 	}
 
-	rc = __sock_queue_rcv_skb(sk, skb);
+	rc = __udp_enqueue_schedule_skb(sk, skb);
 	if (rc < 0) {
 		int is_udplite = IS_UDPLITE(sk);
 
@@ -1582,7 +1574,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	}
 
 	return 0;
-
 }
 
 static struct static_key udp_encap_needed __read_mostly;
@@ -1604,7 +1595,6 @@ void udp_encap_enable(void)
 int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	struct udp_sock *up = udp_sk(sk);
-	int rc;
 	int is_udplite = IS_UDPLITE(sk);
 
 	/*
@@ -1691,25 +1681,9 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 		goto drop;
 
 	udp_csum_pull_header(skb);
-	if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
-		__UDP_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS,
-				is_udplite);
-		goto drop;
-	}
-
-	rc = 0;
 
 	ipv4_pktinfo_prepare(sk, skb);
-	bh_lock_sock(sk);
-	if (!sock_owned_by_user(sk))
-		rc = __udp_queue_rcv_skb(sk, skb);
-	else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) {
-		bh_unlock_sock(sk);
-		goto drop;
-	}
-	bh_unlock_sock(sk);
-
-	return rc;
+	return __udp_queue_rcv_skb(sk, skb);
 
 csum_error:
 	__UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
@@ -2319,13 +2293,13 @@ struct proto udp_prot = {
 	.connect	   = ip4_datagram_connect,
 	.disconnect	   = udp_disconnect,
 	.ioctl		   = udp_ioctl,
+	.init		   = udp_init_sock,
 	.destroy	   = udp_destroy_sock,
 	.setsockopt	   = udp_setsockopt,
 	.getsockopt	   = udp_getsockopt,
 	.sendmsg	   = udp_sendmsg,
 	.recvmsg	   = udp_recvmsg,
 	.sendpage	   = udp_sendpage,
-	.backlog_rcv	   = __udp_queue_rcv_skb,
 	.release_cb	   = ip4_datagram_release_cb,
 	.hash		   = udp_lib_hash,
 	.unhash		   = udp_lib_unhash,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 9aa7c1c..71963b2 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -334,7 +334,6 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 	int is_udplite = IS_UDPLITE(sk);
 	bool checksum_valid = false;
 	int is_udp4;
-	bool slow;
 
 	if (flags & MSG_ERRQUEUE)
 		return ipv6_recv_error(sk, msg, len, addr_len);
@@ -378,7 +377,6 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 			goto csum_copy_err;
 	}
 	if (unlikely(err)) {
-		trace_kfree_skb(skb, udpv6_recvmsg);
 		if (!peeked) {
 			atomic_inc(&sk->sk_drops);
 			if (is_udp4)
@@ -388,7 +386,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 				UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS,
 					       is_udplite);
 		}
-		skb_free_datagram_locked(sk, skb);
+		kfree_skb(skb);
 		return err;
 	}
 	if (!peeked) {
@@ -437,12 +435,11 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 	if (flags & MSG_TRUNC)
 		err = ulen;
 
-	__skb_free_datagram_locked(sk, skb, peeking ? -err : err);
+	skb_consume_udp(sk, skb, peeking ? -err : err);
 	return err;
 
 csum_copy_err:
-	slow = lock_sock_fast(sk);
-	if (!skb_kill_datagram(sk, skb, flags)) {
+	if (!__sk_queue_drop_skb(sk, skb, flags)) {
 		if (is_udp4) {
 			UDP_INC_STATS(sock_net(sk),
 				      UDP_MIB_CSUMERRORS, is_udplite);
@@ -455,7 +452,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 				       UDP_MIB_INERRORS, is_udplite);
 		}
 	}
-	unlock_sock_fast(sk, slow);
+	kfree_skb(skb);
 
 	/* starting over for a new packet, but check if we need to yield */
 	cond_resched();
@@ -523,7 +520,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 		sk_incoming_cpu_update(sk);
 	}
 
-	rc = __sock_queue_rcv_skb(sk, skb);
+	rc = __udp_enqueue_schedule_skb(sk, skb);
 	if (rc < 0) {
 		int is_udplite = IS_UDPLITE(sk);
 
@@ -535,6 +532,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 		kfree_skb(skb);
 		return -1;
 	}
+
 	return 0;
 }
 
@@ -556,7 +554,6 @@ void udpv6_encap_enable(void)
 int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	struct udp_sock *up = udp_sk(sk);
-	int rc;
 	int is_udplite = IS_UDPLITE(sk);
 
 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
@@ -622,25 +619,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 		goto drop;
 
 	udp_csum_pull_header(skb);
-	if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
-		__UDP6_INC_STATS(sock_net(sk),
-				 UDP_MIB_RCVBUFERRORS, is_udplite);
-		goto drop;
-	}
 
 	skb_dst_drop(skb);
 
-	bh_lock_sock(sk);
-	rc = 0;
-	if (!sock_owned_by_user(sk))
-		rc = __udpv6_queue_rcv_skb(sk, skb);
-	else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) {
-		bh_unlock_sock(sk);
-		goto drop;
-	}
-	bh_unlock_sock(sk);
-
-	return rc;
+	return __udpv6_queue_rcv_skb(sk, skb);
 
 csum_error:
 	__UDP6_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
@@ -1433,12 +1415,12 @@ struct proto udpv6_prot = {
 	.connect	   = ip6_datagram_connect,
 	.disconnect	   = udp_disconnect,
 	.ioctl		   = udp_ioctl,
+	.init		   = udp_init_sock,
 	.destroy	   = udpv6_destroy_sock,
 	.setsockopt	   = udpv6_setsockopt,
 	.getsockopt	   = udpv6_getsockopt,
 	.sendmsg	   = udpv6_sendmsg,
 	.recvmsg	   = udpv6_recvmsg,
-	.backlog_rcv	   = __udpv6_queue_rcv_skb,
 	.release_cb	   = ip6_datagram_release_cb,
 	.hash		   = udp_lib_hash,
 	.unhash		   = udp_lib_unhash,
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 57625f6..e2a55dc 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -39,6 +39,7 @@
 #include <net/checksum.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
+#include <net/udp.h>
 #include <net/tcp.h>
 #include <net/tcp_states.h>
 #include <asm/uaccess.h>
@@ -129,6 +130,18 @@ static void svc_release_skb(struct svc_rqst *rqstp)
 	}
 }
 
+static void svc_release_udp_skb(struct svc_rqst *rqstp)
+{
+	struct sk_buff *skb = rqstp->rq_xprt_ctxt;
+
+	if (skb) {
+		rqstp->rq_xprt_ctxt = NULL;
+
+		dprintk("svc: service %p, releasing skb %p\n", rqstp, skb);
+		consume_skb(skb);
+	}
+}
+
 union svc_pktinfo_u {
 	struct in_pktinfo pkti;
 	struct in6_pktinfo pkti6;
@@ -575,7 +588,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
 			goto out_free;
 		}
 		local_bh_enable();
-		skb_free_datagram_locked(svsk->sk_sk, skb);
+		consume_skb(skb);
 	} else {
 		/* we can use it in-place */
 		rqstp->rq_arg.head[0].iov_base = skb->data;
@@ -602,8 +615,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
 
 	return len;
 out_free:
-	trace_kfree_skb(skb, svc_udp_recvfrom);
-	skb_free_datagram_locked(svsk->sk_sk, skb);
+	kfree_skb(skb);
 	return 0;
 }
 
@@ -660,7 +672,7 @@ static struct svc_xprt *svc_udp_create(struct svc_serv *serv,
 	.xpo_create = svc_udp_create,
 	.xpo_recvfrom = svc_udp_recvfrom,
 	.xpo_sendto = svc_udp_sendto,
-	.xpo_release_rqst = svc_release_skb,
+	.xpo_release_rqst = svc_release_udp_skb,
 	.xpo_detach = svc_sock_detach,
 	.xpo_free = svc_sock_free,
 	.xpo_prep_reply_hdr = svc_udp_prep_reply_hdr,
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index bf16883..8e04a43 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1074,7 +1074,7 @@ static void xs_udp_data_receive(struct sock_xprt *transport)
 		skb = skb_recv_datagram(sk, 0, 1, &err);
 		if (skb != NULL) {
 			xs_udp_data_read_skb(&transport->xprt, sk, skb);
-			skb_free_datagram_locked(sk, skb);
+			consume_skb(skb);
 			continue;
 		}
 		if (!test_and_clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state))
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" 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-next v4 1/3] net/socket: factor out helpers for memory and queue manipulation
From: Paolo Abeni @ 2016-10-19 12:47 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: David S. Miller, James Morris, Trond Myklebust, Alexander Duyck,
	Daniel Borkmann, Eric Dumazet, Tom Herbert, Hannes Frederic Sowa,
	Edward Cree, linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1476877189.git.pabeni-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Basic sock operations that udp code can use with its own
memory accounting schema. No functional change is introduced
in the existing APIs.

v2 -> v4:
  - avoid exporting __sock_enqueue_skb

v1 -> v2:
  - avoid export sock_rmem_free

Acked-by: Hannes Frederic Sowa <hannes-tFNcAqjVMyqKXQKiL6tip0B+6BGkLq7r@public.gmane.org>
Signed-off-by: Paolo Abeni <pabeni-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 include/net/sock.h  |  4 ++++
 net/core/datagram.c | 36 +++++++++++++++++------------
 net/core/sock.c     | 66 +++++++++++++++++++++++++++++++++++++----------------
 3 files changed, 72 insertions(+), 34 deletions(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index ebf75db..2764895 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1274,7 +1274,9 @@ static inline struct inode *SOCK_INODE(struct socket *socket)
 /*
  * Functions for memory accounting
  */
+int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind);
 int __sk_mem_schedule(struct sock *sk, int size, int kind);
+void __sk_mem_reduce_allocated(struct sock *sk, int amount);
 void __sk_mem_reclaim(struct sock *sk, int amount);
 
 #define SK_MEM_QUANTUM ((int)PAGE_SIZE)
@@ -1950,6 +1952,8 @@ void sk_reset_timer(struct sock *sk, struct timer_list *timer,
 
 void sk_stop_timer(struct sock *sk, struct timer_list *timer);
 
+int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
+			unsigned int flags);
 int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
 
diff --git a/net/core/datagram.c b/net/core/datagram.c
index b7de71f..bfb973a 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -323,6 +323,27 @@ void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len)
 }
 EXPORT_SYMBOL(__skb_free_datagram_locked);
 
+int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
+			unsigned int flags)
+{
+	int err = 0;
+
+	if (flags & MSG_PEEK) {
+		err = -ENOENT;
+		spin_lock_bh(&sk->sk_receive_queue.lock);
+		if (skb == skb_peek(&sk->sk_receive_queue)) {
+			__skb_unlink(skb, &sk->sk_receive_queue);
+			atomic_dec(&skb->users);
+			err = 0;
+		}
+		spin_unlock_bh(&sk->sk_receive_queue.lock);
+	}
+
+	atomic_inc(&sk->sk_drops);
+	return err;
+}
+EXPORT_SYMBOL(__sk_queue_drop_skb);
+
 /**
  *	skb_kill_datagram - Free a datagram skbuff forcibly
  *	@sk: socket
@@ -346,23 +367,10 @@ void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len)
 
 int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
 {
-	int err = 0;
-
-	if (flags & MSG_PEEK) {
-		err = -ENOENT;
-		spin_lock_bh(&sk->sk_receive_queue.lock);
-		if (skb == skb_peek(&sk->sk_receive_queue)) {
-			__skb_unlink(skb, &sk->sk_receive_queue);
-			atomic_dec(&skb->users);
-			err = 0;
-		}
-		spin_unlock_bh(&sk->sk_receive_queue.lock);
-	}
+	int err = __sk_queue_drop_skb(sk, skb, flags);
 
 	kfree_skb(skb);
-	atomic_inc(&sk->sk_drops);
 	sk_mem_reclaim_partial(sk);
-
 	return err;
 }
 EXPORT_SYMBOL(skb_kill_datagram);
diff --git a/net/core/sock.c b/net/core/sock.c
index c73e28f..7c85e6d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -404,8 +404,8 @@ static void sock_disable_timestamp(struct sock *sk, unsigned long flags)
 	}
 }
 
-
 int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+
 {
 	unsigned long flags;
 	struct sk_buff_head *list = &sk->sk_receive_queue;
@@ -2091,24 +2091,18 @@ int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb)
 EXPORT_SYMBOL(sk_wait_data);
 
 /**
- *	__sk_mem_schedule - increase sk_forward_alloc and memory_allocated
+ *	__sk_mem_raise_allocated - increase memory_allocated
  *	@sk: socket
  *	@size: memory size to allocate
+ *	@amt: pages to allocate
  *	@kind: allocation type
  *
- *	If kind is SK_MEM_SEND, it means wmem allocation. Otherwise it means
- *	rmem allocation. This function assumes that protocols which have
- *	memory_pressure use sk_wmem_queued as write buffer accounting.
+ *	Similar to __sk_mem_schedule(), but does not update sk_forward_alloc
  */
-int __sk_mem_schedule(struct sock *sk, int size, int kind)
+int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind)
 {
 	struct proto *prot = sk->sk_prot;
-	int amt = sk_mem_pages(size);
-	long allocated;
-
-	sk->sk_forward_alloc += amt * SK_MEM_QUANTUM;
-
-	allocated = sk_memory_allocated_add(sk, amt);
+	long allocated = sk_memory_allocated_add(sk, amt);
 
 	if (mem_cgroup_sockets_enabled && sk->sk_memcg &&
 	    !mem_cgroup_charge_skmem(sk->sk_memcg, amt))
@@ -2169,9 +2163,6 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind)
 
 	trace_sock_exceed_buf_limit(sk, prot, allocated);
 
-	/* Alas. Undo changes. */
-	sk->sk_forward_alloc -= amt * SK_MEM_QUANTUM;
-
 	sk_memory_allocated_sub(sk, amt);
 
 	if (mem_cgroup_sockets_enabled && sk->sk_memcg)
@@ -2179,18 +2170,40 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind)
 
 	return 0;
 }
+EXPORT_SYMBOL(__sk_mem_raise_allocated);
+
+/**
+ *	__sk_mem_schedule - increase sk_forward_alloc and memory_allocated
+ *	@sk: socket
+ *	@size: memory size to allocate
+ *	@kind: allocation type
+ *
+ *	If kind is SK_MEM_SEND, it means wmem allocation. Otherwise it means
+ *	rmem allocation. This function assumes that protocols which have
+ *	memory_pressure use sk_wmem_queued as write buffer accounting.
+ */
+int __sk_mem_schedule(struct sock *sk, int size, int kind)
+{
+	int ret, amt = sk_mem_pages(size);
+
+	sk->sk_forward_alloc += amt << SK_MEM_QUANTUM_SHIFT;
+	ret = __sk_mem_raise_allocated(sk, size, amt, kind);
+	if (!ret)
+		sk->sk_forward_alloc -= amt << SK_MEM_QUANTUM_SHIFT;
+	return ret;
+}
 EXPORT_SYMBOL(__sk_mem_schedule);
 
 /**
- *	__sk_mem_reclaim - reclaim memory_allocated
+ *	__sk_mem_reduce_allocated - reclaim memory_allocated
  *	@sk: socket
- *	@amount: number of bytes (rounded down to a SK_MEM_QUANTUM multiple)
+ *	@amount: number of quanta
+ *
+ *	Similar to __sk_mem_reclaim(), but does not update sk_forward_alloc
  */
-void __sk_mem_reclaim(struct sock *sk, int amount)
+void __sk_mem_reduce_allocated(struct sock *sk, int amount)
 {
-	amount >>= SK_MEM_QUANTUM_SHIFT;
 	sk_memory_allocated_sub(sk, amount);
-	sk->sk_forward_alloc -= amount << SK_MEM_QUANTUM_SHIFT;
 
 	if (mem_cgroup_sockets_enabled && sk->sk_memcg)
 		mem_cgroup_uncharge_skmem(sk->sk_memcg, amount);
@@ -2199,6 +2212,19 @@ void __sk_mem_reclaim(struct sock *sk, int amount)
 	    (sk_memory_allocated(sk) < sk_prot_mem_limits(sk, 0)))
 		sk_leave_memory_pressure(sk);
 }
+EXPORT_SYMBOL(__sk_mem_reduce_allocated);
+
+/**
+ *	__sk_mem_reclaim - reclaim sk_forward_alloc and memory_allocated
+ *	@sk: socket
+ *	@amount: number of bytes (rounded down to a SK_MEM_QUANTUM multiple)
+ */
+void __sk_mem_reclaim(struct sock *sk, int amount)
+{
+	amount >>= SK_MEM_QUANTUM_SHIFT;
+	sk->sk_forward_alloc -= amount << SK_MEM_QUANTUM_SHIFT;
+	__sk_mem_reduce_allocated(sk, amount);
+}
 EXPORT_SYMBOL(__sk_mem_reclaim);
 
 int sk_set_peek_off(struct sock *sk, int val)
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" 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

* net/sctp: use-after-free in __sctp_connect
From: Andrey Konovalov @ 2016-10-19 12:25 UTC (permalink / raw)
  To: Vlad Yasevich, Neil Horman, David S. Miller, linux-sctp, netdev,
	LKML
  Cc: syzkaller, Kostya Serebryany, Alexander Potapenko, Sasha Levin,
	Eric Dumazet, Dmitry Vyukov

Hi,

I've got the following error report while running the syzkaller fuzzer:

==================================================================
BUG: KASAN: use-after-free in __sctp_connect+0xabe/0xbf0 at addr
ffff88006b1dc610
Read of size 4 by task syz-executor/21837
CPU: 2 PID: 21837 Comm: syz-executor Not tainted 4.9.0-rc1+ #228
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
 ffff8800393ef930 ffffffff81b474f4 ffff88003e80ed40 ffff88006b1dc568
 ffff88006b1dd6a0 ffff88006b1dc560 ffff8800393ef958 ffffffff8150b33c
 ffff8800393ef9e8 ffff88003e80ed40 ffff8800eb1dc610 ffff8800393ef9d8
Call Trace:
 [<     inline     >] __dump_stack lib/dump_stack.c:15
 [<ffffffff81b474f4>] dump_stack+0xb3/0x10f lib/dump_stack.c:51
 [<ffffffff8150b33c>] kasan_object_err+0x1c/0x70 mm/kasan/report.c:156
 [<     inline     >] print_address_description mm/kasan/report.c:194
 [<ffffffff8150b5d7>] kasan_report_error+0x1f7/0x4d0 mm/kasan/report.c:283
 [<     inline     >] kasan_report mm/kasan/report.c:303
 [<ffffffff8150b96e>] __asan_report_load4_noabort+0x3e/0x40
mm/kasan/report.c:323
 [<ffffffff8393457e>] __sctp_connect+0xabe/0xbf0 net/sctp/socket.c:1219
 [<ffffffff83934832>] __sctp_setsockopt_connectx+0x182/0x1b0
net/sctp/socket.c:1329
 [<     inline     >] sctp_setsockopt_connectx net/sctp/socket.c:1361
 [<ffffffff8393c1d9>] sctp_setsockopt+0x1009/0x3d70 net/sctp/socket.c:3813
 [<ffffffff82b770d6>] sock_common_setsockopt+0x96/0xd0 net/core/sock.c:2688
 [<     inline     >] SYSC_setsockopt net/socket.c:1742
 [<ffffffff82b740b4>] SyS_setsockopt+0x154/0x240 net/socket.c:1721
 [<ffffffff83fc0141>] entry_SYSCALL_64_fastpath+0x1f/0xc2
Object at ffff88006b1dc568, in cache kmalloc-4096 size: 4096
Allocated:
PID = 21837
 [  270.449111] [<ffffffff8107e2d6>] save_stack_trace+0x16/0x20
arch/x86/kernel/stacktrace.c:57
 [  270.449111] [<ffffffff8150a6a6>] save_stack+0x46/0xd0 mm/kasan/kasan.c:495
 [  270.449111] [<     inline     >] set_track mm/kasan/kasan.c:507
 [  270.449111] [<ffffffff8150a91b>] kasan_kmalloc+0xab/0xe0
mm/kasan/kasan.c:598
 [  270.449111] [<ffffffff8150604c>] kmem_cache_alloc_trace+0xec/0x270
mm/slub.c:2735
 [  270.449111] [<     inline     >] kmalloc include/linux/slab.h:490
 [  270.449111] [<     inline     >] kzalloc include/linux/slab.h:636
 [  270.449111] [<ffffffff838f2b2f>] sctp_association_new+0x6f/0x1f50
net/sctp/associola.c:303
 [  270.449111] [<ffffffff8393402a>] __sctp_connect+0x56a/0xbf0
net/sctp/socket.c:1163
 [  270.449111] [<ffffffff83934832>]
__sctp_setsockopt_connectx+0x182/0x1b0 net/sctp/socket.c:1329
 [  270.449111] [<     inline     >] sctp_setsockopt_connectx
net/sctp/socket.c:1361
 [  270.449111] [<ffffffff8393c1d9>] sctp_setsockopt+0x1009/0x3d70
net/sctp/socket.c:3813
 [  270.449111] [<ffffffff82b770d6>] sock_common_setsockopt+0x96/0xd0
net/core/sock.c:2688
 [  270.449111] [<     inline     >] SYSC_setsockopt net/socket.c:1742
 [  270.449111] [<ffffffff82b740b4>] SyS_setsockopt+0x154/0x240
net/socket.c:1721
 [  270.449111] [<ffffffff83fc0141>] entry_SYSCALL_64_fastpath+0x1f/0xc2
Freed:
PID = 21837
 [  270.449111] [<ffffffff8107e2d6>] save_stack_trace+0x16/0x20
arch/x86/kernel/stacktrace.c:57
 [  270.449111] [<ffffffff8150a6a6>] save_stack+0x46/0xd0 mm/kasan/kasan.c:495
 [  270.449111] [<     inline     >] set_track mm/kasan/kasan.c:507
 [  270.449111] [<ffffffff8150af03>] kasan_slab_free+0x73/0xc0
mm/kasan/kasan.c:571
 [  270.449111] [<     inline     >] slab_free_hook mm/slub.c:1352
 [  270.449111] [<     inline     >] slab_free_freelist_hook mm/slub.c:1374
 [  270.449111] [<     inline     >] slab_free mm/slub.c:2951
 [  270.449111] [<ffffffff815073e8>] kfree+0xe8/0x2b0 mm/slub.c:3871
 [  270.449111] [<     inline     >] sctp_association_destroy
net/sctp/associola.c:426
 [  270.449111] [<ffffffff838f56e5>] sctp_association_put+0x155/0x250
net/sctp/associola.c:866
 [  270.449111] [<ffffffff8392e213>] sctp_wait_for_connect+0x313/0x460
net/sctp/socket.c:7544
 [  270.449111] [<ffffffff8393443b>] __sctp_connect+0x97b/0xbf0
net/sctp/socket.c:1217
 [  270.449111] [<ffffffff83934832>]
__sctp_setsockopt_connectx+0x182/0x1b0 net/sctp/socket.c:1329
 [  270.449111] [<     inline     >] sctp_setsockopt_connectx
net/sctp/socket.c:1361
 [  270.449111] [<ffffffff8393c1d9>] sctp_setsockopt+0x1009/0x3d70
net/sctp/socket.c:3813
 [  270.449111] [<ffffffff82b770d6>] sock_common_setsockopt+0x96/0xd0
net/core/sock.c:2688
 [  270.449111] [<     inline     >] SYSC_setsockopt net/socket.c:1742
 [  270.449111] [<ffffffff82b740b4>] SyS_setsockopt+0x154/0x240
net/socket.c:1721
 [  270.449111] [<ffffffff83fc0141>] entry_SYSCALL_64_fastpath+0x1f/0xc2
Memory state around the buggy address:
 ffff88006b1dc500: fc fc fc fc fc fc fc fc fc fc fc fc fc fb fb fb
 ffff88006b1dc580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88006b1dc600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                         ^
 ffff88006b1dc680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff88006b1dc700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

sctp_wait_for_connect ends up freeing asoc, which is later accessed to
read asoc->assoc_id.

On commit 1a1891d762d6e64daf07b5be4817e3fbb29e3c59 (Oct 18).

^ permalink raw reply

* Re: [PATCH 10/10] mm: replace access_process_vm() write parameter with gup_flags
From: Michael Ellerman @ 2016-10-19 11:10 UTC (permalink / raw)
  To: Lorenzo Stoakes, linux-mm
  Cc: linux-mips, linux-fbdev, Jan Kara, kvm, linux-sh, Dave Hansen,
	dri-devel, netdev, sparclinux, linux-ia64, linux-s390,
	linux-samsung-soc, linux-scsi, linux-rdma, x86, Hugh Dickins,
	linux-media, Rik van Riel, intel-gfx, adi-buildroot-devel,
	ceph-devel, linux-arm-kernel, Lorenzo Stoakes, linux-cris-kernel,
	Linus Torvalds, linuxppc-dev
In-Reply-To: <20161013002020.3062-11-lstoakes@gmail.com>

Lorenzo Stoakes <lstoakes@gmail.com> writes:

> diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
> index f52b7db3..010b7b3 100644
> --- a/arch/powerpc/kernel/ptrace32.c
> +++ b/arch/powerpc/kernel/ptrace32.c
> @@ -74,7 +74,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
>  			break;
>  
>  		copied = access_process_vm(child, (u64)addrOthers, &tmp,
> -				sizeof(tmp), 0);
> +				sizeof(tmp), FOLL_FORCE);
>  		if (copied != sizeof(tmp))
>  			break;
>  		ret = put_user(tmp, (u32 __user *)data);

LGTM.

> @@ -179,7 +179,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
>  			break;
>  		ret = 0;
>  		if (access_process_vm(child, (u64)addrOthers, &tmp,
> -					sizeof(tmp), 1) == sizeof(tmp))
> +					sizeof(tmp),
> +					FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
>  			break;
>  		ret = -EIO;
>  		break;

If you're respinning this anyway, can you format that as:

		if (access_process_vm(child, (u64)addrOthers, &tmp, sizeof(tmp),
				      FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
  			break;

I realise you probably deliberately didn't do that to make the diff clearer.

Either way:

Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)


cheers

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: tg3 BUG: spinlock lockup suspected
From: Meelis Roos @ 2016-10-19 11:06 UTC (permalink / raw)
  To: Deepak Khungar
  Cc: Siva Reddy Kallam, Linux Netdev List, sparclinux,
	Linux Kernel list
In-Reply-To: <CAF2ZziZiy+ZeoH1pPiBOWAdXrV1qzqhjBabxMW0jUjtYPfjQ8A@mail.gmail.com>

> Can you please share the Linux Distribution information along with sparc machine specification.

Sun Fire V210 (dual UltraSPARC IIIi), onboard NICs with tg3 
driver. Debian unstable snapshot (as last seen in Debian):

deb http://snapshot.debian.org/archive/debian/20150725T121538Z/ unstable main contrib non-free

except for udev and libudev1 that are 218-8 (older) because the latest 
udev at that moment was broken.

This implies gcc version 4.9.3 (Debian 4.9.3-2).


> Regards,
> Deepak Khungar
> 
> On Wed, Oct 19, 2016 at 10:18 AM, Siva Reddy Kallam <siva.kallam@broadcom.com> wrote:
>       On Mon, Oct 17, 2016 at 6:35 PM, Meelis Roos <mroos@linux.ee> wrote:
>       >> > Now I reproduced the bug even with 4.7-rc1 so it is older than 4.7. Will
>       >> > test further.
>       >>
>       >> It gets stranger and stranger - my old 4.7 image worked fine, freshly
>       >> compiled 4.7 exhibits the same problem.
>       >>
>       >> Toolchain has not changed, that I know for sure.
>       >>
>       >> What may have changed is kernel .config. My old conf was with whatever I
>       >> had during 4.7. Then I upgraded to 4.8-rc3 and then 4.8 and selected
>       >> values for "make oldconfig" new entries. Then went back to 4.7-rc1 and
>       >> then to 4.7 with this config, answering quiestion about new options when
>       >> any appeared. Diff is not available since I do not have the old configs
>       >> archived.
>       >
>       > I did some more digging. Found an older configuration that is working
>       > and recreated a newer one that is bad, for the same 4.7 kernel. This is
>       > reproducible now, from "make clean" state.
>       >
>       > Working config from 4.7-rc4 attached as config-4.7, broken config from
>       > 4.7 attached as config-4.7-bad.
>       >
>       > Will try to bisect the configs as time permits. But looking at the
>       > stack traces, the issue is probably timing related, when ip and dhclient
>       > do something with the same lock. seq_read that outputs stats could be
>       > reading /proc/net/dev that reads counters from each interface.
>       >
>       > ifupdown seems to use the following for dhcp interfaces:
>       >   up
>       >     [[/bin/ip link set dev %iface% address %hwaddress%]]
>       >     /sbin/dhclient -v -pf /run/dhclient.%iface%.pid -lf /var/lib/dhcp/dhclient.%iface%.leases -I -df /var/lib/dhcp/dhclient6.%iface%.leases %iface% \
>       > ...
>       >
>       > so ip link is setting link up, this creates some work for the
>       > background, and the dhclient goes adn reads /proc/net/dev, and lockup is
>       > suspected but not proven?
>       >
>       > I started a loop for test, doing cat /proc/net/dev in a loop and at the
>       > same link link up and down from console, but up and down is slow process
>       > and the loop did not seem to trigger the warning over night, so it was
>       > not so simple.
>       >
>       I am busy with other priority tasks. One of my colleague Deepak will
>       work this with you.
>       I added him to CC list.
>       Thanks.
>       >
>       >> > > [   83.716570] BUG: spinlock lockup suspected on CPU#0, dhclient/1014
>       >> > > [   83.797819]  lock: 0xfff000123c8e4a08, .magic: dead4ead, .owner: ip/1001, .owner_cpu: 1
>       >> > > [   83.903130] CPU: 0 PID: 1014 Comm: dhclient Not tainted 4.8.0 #4
>       >> > > [   83.982129] Call Trace:
>       >> > > [   84.014160]  [00000000004b7220] spin_dump+0x60/0xa0
>       >> > > [   84.078203]  [00000000004b73a0] do_raw_spin_lock+0xa0/0x120
>       >> > > [   84.106344] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
>       >> > > [   84.107193] ip (1001) used greatest stack depth: 2168 bytes left
>       >> > > [   84.306955]  [000000000092c0d0] _raw_spin_lock_bh+0x30/0x40
>       >> > > [   84.380188]  [00000000100822cc] tg3_get_stats64+0xc/0x80 [tg3]
>       >> > > [   84.456885]  [00000000007fac8c] dev_get_stats+0x2c/0xc0
>       >> > > [   84.525506]  [000000000081a4e8] dev_seq_printf_stats+0x8/0xe0
>       >> > > [   84.600986]  [000000000081a5e4] dev_seq_show+0x24/0x40
>       >> > > [   84.668467]  [00000000005cb6c4] seq_read+0x2c4/0x440
>       >> > > [   84.733656]  [000000000060b97c] proc_reg_read+0x3c/0x80
>       >> > > [   84.802282]  [00000000005a219c] __vfs_read+0x1c/0x140
>       >> > > [   84.868613]  [00000000005a2310] vfs_read+0x50/0x100
>       >> > > [   84.932662]  [00000000005a265c] SyS_read+0x3c/0xa0
>       >> > > [   84.995573]  [00000000004061d4] linux_sparc_syscall32+0x34/0x60
>       >> > > [   85.073748] * CPU[  0]: TSTATE[00000044f0001a22] TPC[00000000f79a16b0] TNPC[00000000f79a16b4] TASK[dhclient:1014]
>       >> > > [   85.208732]              TPC[f79a16b0] O7[f79405c8] I7[0] RPC[0]
>       >> > > [   85.287633]   CPU[  1]: TSTATE[0000004480001605] TPC[00000000004b26f0] TNPC[00000000004d0b0c] TASK[swapper/1:0]
>       >> > > [   85.420338]              TPC[trace_hardirqs_off+0x10/0x20] O7[rcu_idle_enter+0x64/0xa0] I7[cpu_startup_entry+0x1b0/0x240] RPC[rest_init+0x178/0x1a0]
>       >> > > [   85.664600] tg3 0000:00:02.0 eth0: Link is up at 100 Mbps, full duplex
>       >> > > [   85.750515] tg3 0000:00:02.0 eth0: Flow control is off for TX and off for RX
>       >> > > [   85.843994] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
>       >
>       > --
>       > Meelis Roos (mroos@linux.ee)
> 
> 
> 
> 

-- 
Meelis Roos (mroos@ut.ee)      http://www.cs.ut.ee/~mroos/

^ permalink raw reply

* Re: [PATCH v2 34/37] docs: fix locations of several documents that got moved
From: Pavel Machek @ 2016-10-19 10:34 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Linux Doc Mailing List, Mauro Carvalho Chehab, Jonathan Corbet,
	Rafael J. Wysocki, Len Brown, Jens Axboe, Alessandro Zummo,
	Alexandre Belloni, Rob Herring, Mark Rutland, Jean Delvare,
	Guenter Roeck, Karsten Keil, Mauro Carvalho Chehab,
	Steffen Klassert, Greg Kroah-Hartman, Johannes Berg,
	Jaroslav Kysela, Takashi Iwai, Paolo Bonzini,
	Radim Krčmář
In-Reply-To: <28c1f5d3867174a7017e3f3d9e9e4ebb096ceed8.1476801354.git.mchehab-JsYNTwtnfakRB7SZvlqPiA@public.gmane.org>

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

Hi!


> --- a/Documentation/ABI/testing/sysfs-kernel-slab
> +++ b/Documentation/ABI/testing/sysfs-kernel-slab
> @@ -347,7 +347,7 @@ Description:
>  		because of fragmentation, SLUB will retry with the minimum order
>  		possible depending on its characteristics.
>  		When debug_guardpage_minorder=N (N > 0) parameter is specified
> -		(see Documentation/kernel-parameters.txt), the minimum possible
> +		(see Documentation/admin-guide/kernel-parameters.rst), the minimum possible
>  		order is used and this sysfs entry can not be used to change
>  		the order at run time.

Dunno, but kernel-parameters.txt was already quite long... for a file
that is referenced quite often. Adding admin-guide/ into the path does
not really help.

Maybe admin-guide should go directly into Documentation/ , as that's
what our users are interested in?

Plus, I'm not sure how many developers will figure out that process/
is what describes kernel patch submission process. We have processes
in the kernel, after all...

Could we leave symlinks in place? People say "please follow
CodingStyle" when reviewing patches. Saying "please follow
process/conding-style.rst" ... somehow I don't think that's going to
happen.

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

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

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

^ permalink raw reply

* Re: [RFC 6/6] qedi: Add support for data path.
From: Hannes Reinecke @ 2016-10-19 10:24 UTC (permalink / raw)
  To: manish.rangankar, lduncan, cleech
  Cc: martin.petersen, jejb, linux-scsi, netdev, Yuval.Mintz,
	QLogic-Storage-Upstream, Nilesh Javali, Adheer Chandravanshi,
	Chad Dupuis, Saurav Kashyap, Arun Easi
In-Reply-To: <1476853273-22960-7-git-send-email-manish.rangankar@cavium.com>

On 10/19/2016 07:01 AM, manish.rangankar@cavium.com wrote:
> From: Manish Rangankar <manish.rangankar@cavium.com>
> 
> This patch adds support for data path and TMF handling.
> 
> Signed-off-by: Nilesh Javali <nilesh.javali@cavium.com>
> Signed-off-by: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
> Signed-off-by: Chad Dupuis <chad.dupuis@cavium.com>
> Signed-off-by: Saurav Kashyap <saurav.kashyap@cavium.com>
> Signed-off-by: Arun Easi <arun.easi@cavium.com>
> Signed-off-by: Manish Rangankar <manish.rangankar@cavium.com>
> ---
>  drivers/scsi/qedi/qedi_fw.c    | 1282 ++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/qedi/qedi_gbl.h   |    6 +
>  drivers/scsi/qedi/qedi_iscsi.c |    6 +
>  drivers/scsi/qedi/qedi_main.c  |    4 +
>  4 files changed, 1298 insertions(+)
> 
> diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c
> index a820785..af1e14d 100644
> --- a/drivers/scsi/qedi/qedi_fw.c
> +++ b/drivers/scsi/qedi/qedi_fw.c
> @@ -147,6 +147,114 @@ static void qedi_process_text_resp(struct qedi_ctx *qedi,
>  	spin_unlock(&session->back_lock);
>  }
>  
> +static void qedi_tmf_resp_work(struct work_struct *work)
> +{
> +	struct qedi_cmd *qedi_cmd =
> +				container_of(work, struct qedi_cmd, tmf_work);
> +	struct qedi_conn *qedi_conn = qedi_cmd->conn;
> +	struct qedi_ctx *qedi = qedi_conn->qedi;
> +	struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
> +	struct iscsi_session *session = conn->session;
> +	struct iscsi_tm_rsp *resp_hdr_ptr;
> +	struct iscsi_cls_session *cls_sess;
> +	int rval = 0;
> +
> +	set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +	resp_hdr_ptr =  (struct iscsi_tm_rsp *)qedi_cmd->tmf_resp_buf;
> +	cls_sess = iscsi_conn_to_session(qedi_conn->cls_conn);
> +
> +	iscsi_block_session(session->cls_session);
> +	rval = qedi_cleanup_all_io(qedi, qedi_conn, qedi_cmd->task, true);
> +	if (rval) {
> +		clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +		qedi_clear_task_idx(qedi, qedi_cmd->task_id);
> +		iscsi_unblock_session(session->cls_session);
> +		return;
> +	}
> +
> +	iscsi_unblock_session(session->cls_session);
> +	qedi_clear_task_idx(qedi, qedi_cmd->task_id);
> +
> +	spin_lock(&session->back_lock);
> +	__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0);
> +	spin_unlock(&session->back_lock);
> +	kfree(resp_hdr_ptr);
> +	clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +}
> +
> +static void qedi_process_tmf_resp(struct qedi_ctx *qedi,
> +				  union iscsi_cqe *cqe,
> +				  struct iscsi_task *task,
> +				  struct qedi_conn *qedi_conn)
> +
> +{
> +	struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
> +	struct iscsi_session *session = conn->session;
> +	struct iscsi_tmf_response_hdr *cqe_tmp_response;
> +	struct iscsi_tm_rsp *resp_hdr_ptr;
> +	struct iscsi_tm *tmf_hdr;
> +	struct qedi_cmd *qedi_cmd = NULL;
> +	u32 *tmp;
> +
> +	cqe_tmp_response = &cqe->cqe_common.iscsi_hdr.tmf_response;
> +
> +	qedi_cmd = task->dd_data;
> +	qedi_cmd->tmf_resp_buf = kzalloc(sizeof(*resp_hdr_ptr), GFP_KERNEL);
> +	if (!qedi_cmd->tmf_resp_buf) {
> +		QEDI_ERR(&qedi->dbg_ctx,
> +			 "Failed to allocate resp buf, cid=0x%x\n",
> +			  qedi_conn->iscsi_conn_id);
> +		return;
> +	}
> +
> +	spin_lock(&session->back_lock);
> +	resp_hdr_ptr =  (struct iscsi_tm_rsp *)qedi_cmd->tmf_resp_buf;
> +	memset(resp_hdr_ptr, 0, sizeof(struct iscsi_tm_rsp));
> +
> +	/* Fill up the header */
> +	resp_hdr_ptr->opcode = cqe_tmp_response->opcode;
> +	resp_hdr_ptr->flags = cqe_tmp_response->hdr_flags;
> +	resp_hdr_ptr->response = cqe_tmp_response->hdr_response;
> +	resp_hdr_ptr->hlength = 0;
> +
> +	hton24(resp_hdr_ptr->dlength,
> +	       (cqe_tmp_response->hdr_second_dword &
> +		ISCSI_TMF_RESPONSE_HDR_DATA_SEG_LEN_MASK));
> +	tmp = (u32 *)resp_hdr_ptr->dlength;
> +	resp_hdr_ptr->itt = build_itt(cqe->cqe_solicited.itid,
> +				      conn->session->age);
> +	resp_hdr_ptr->statsn = cpu_to_be32(cqe_tmp_response->stat_sn);
> +	resp_hdr_ptr->exp_cmdsn  = cpu_to_be32(cqe_tmp_response->exp_cmd_sn);
> +	resp_hdr_ptr->max_cmdsn = cpu_to_be32(cqe_tmp_response->max_cmd_sn);
> +
> +	tmf_hdr = (struct iscsi_tm *)qedi_cmd->task->hdr;
> +
> +	if (likely(qedi_cmd->io_cmd_in_list)) {
> +		qedi_cmd->io_cmd_in_list = false;
> +		list_del_init(&qedi_cmd->io_cmd);
> +		qedi_conn->active_cmd_count--;
> +	}
> +
> +	if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +	      ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) ||
> +	    ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +	      ISCSI_TM_FUNC_TARGET_WARM_RESET) ||
> +	    ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +	      ISCSI_TM_FUNC_TARGET_COLD_RESET)) {
> +		INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_resp_work);
> +		queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work);
> +		goto unblock_sess;
> +	}
> +
> +	qedi_clear_task_idx(qedi, qedi_cmd->task_id);
> +
> +	__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0);
> +	kfree(resp_hdr_ptr);
> +
> +unblock_sess:
> +	spin_unlock(&session->back_lock);
> +}
> +
>  static void qedi_process_login_resp(struct qedi_ctx *qedi,
>  				    union iscsi_cqe *cqe,
>  				    struct iscsi_task *task,
> @@ -470,6 +578,121 @@ static void qedi_process_reject_mesg(struct qedi_ctx *qedi,
>  	spin_unlock_bh(&session->back_lock);
>  }
>  
> +static void qedi_scsi_completion(struct qedi_ctx *qedi,
> +				 union iscsi_cqe *cqe,
> +				 struct iscsi_task *task,
> +				 struct iscsi_conn *conn)
> +{
> +	struct scsi_cmnd *sc_cmd;
> +	struct qedi_cmd *cmd = task->dd_data;
> +	struct iscsi_session *session = conn->session;
> +	struct iscsi_scsi_rsp *hdr;
> +	struct iscsi_data_in_hdr *cqe_data_in;
> +	int datalen = 0;
> +	struct qedi_conn *qedi_conn;
> +	u32 iscsi_cid;
> +	bool mark_cmd_node_deleted = false;
> +	u8 cqe_err_bits = 0;
> +
> +	iscsi_cid  = cqe->cqe_common.conn_id;
> +	qedi_conn = qedi->cid_que.conn_cid_tbl[iscsi_cid];
> +
> +	cqe_data_in = &cqe->cqe_common.iscsi_hdr.data_in;
> +	cqe_err_bits =
> +		cqe->cqe_common.error_bitmap.error_bits.cqe_error_status_bits;
> +
> +	spin_lock_bh(&session->back_lock);
> +	/* get the scsi command */
> +	sc_cmd = cmd->scsi_cmd;
> +
> +	if (!sc_cmd) {
> +		QEDI_WARN(&qedi->dbg_ctx, "sc_cmd is NULL!\n");
> +		goto error;
> +	}
> +
> +	if (!sc_cmd->SCp.ptr) {
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "SCp.ptr is NULL, returned in another context.\n");
> +		goto error;
> +	}
> +
> +	if (!sc_cmd->request) {
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "sc_cmd->request is NULL, sc_cmd=%p.\n",
> +			  sc_cmd);
> +		goto error;
> +	}
> +
> +	if (!sc_cmd->request->special) {
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "request->special is NULL so request not valid, sc_cmd=%p.\n",
> +			  sc_cmd);
> +		goto error;
> +	}
> +
> +	if (!sc_cmd->request->q) {
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "request->q is NULL so request is not valid, sc_cmd=%p.\n",
> +			  sc_cmd);
> +		goto error;
> +	}
> +
> +	qedi_iscsi_unmap_sg_list(cmd);
> +
> +	hdr = (struct iscsi_scsi_rsp *)task->hdr;
> +	hdr->opcode = cqe_data_in->opcode;
> +	hdr->max_cmdsn = cpu_to_be32(cqe_data_in->max_cmd_sn);
> +	hdr->exp_cmdsn = cpu_to_be32(cqe_data_in->exp_cmd_sn);
> +	hdr->itt = build_itt(cqe->cqe_solicited.itid, conn->session->age);
> +	hdr->response = cqe_data_in->reserved1;
> +	hdr->cmd_status = cqe_data_in->status_rsvd;
> +	hdr->flags = cqe_data_in->flags;
> +	hdr->residual_count = cpu_to_be32(cqe_data_in->residual_count);
> +
> +	if (hdr->cmd_status == SAM_STAT_CHECK_CONDITION) {
> +		datalen = cqe_data_in->reserved2 &
> +			  ISCSI_COMMON_HDR_DATA_SEG_LEN_MASK;
> +		memcpy((char *)conn->data, (char *)cmd->sense_buffer, datalen);
> +	}
> +
> +	/* If f/w reports data underrun err then set residual to IO transfer
> +	 * length, set Underrun flag and clear Overrun flag explicitly
> +	 */
> +	if (unlikely(cqe_err_bits &&
> +		     GET_FIELD(cqe_err_bits, CQE_ERROR_BITMAP_UNDER_RUN_ERR))) {
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
> +			  "Under flow itt=0x%x proto flags=0x%x tid=0x%x cid 0x%x fw resid 0x%x sc dlen 0x%x\n",
> +			  hdr->itt, cqe_data_in->flags, cmd->task_id,
> +			  qedi_conn->iscsi_conn_id, hdr->residual_count,
> +			  scsi_bufflen(sc_cmd));
> +		hdr->residual_count = cpu_to_be32(scsi_bufflen(sc_cmd));
> +		hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
> +		hdr->flags &= (~ISCSI_FLAG_CMD_OVERFLOW);
> +	}
> +
> +	spin_lock(&qedi_conn->list_lock);
> +	if (likely(cmd->io_cmd_in_list)) {
> +		cmd->io_cmd_in_list = false;
> +		list_del_init(&cmd->io_cmd);
> +		qedi_conn->active_cmd_count--;
> +		mark_cmd_node_deleted = true;
> +	}
> +	spin_unlock(&qedi_conn->list_lock);
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_TID,
> +		  "Freeing tid=0x%x for cid=0x%x\n",
> +		  cmd->task_id, qedi_conn->iscsi_conn_id);
> +	cmd->state = RESPONSE_RECEIVED;
> +	if (io_tracing)
> +		qedi_trace_io(qedi, task, cmd->task_id, QEDI_IO_TRACE_RSP);
> +
> +	qedi_clear_task_idx(qedi, cmd->task_id);
> +	__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr,
> +			     conn->data, datalen);
> +error:
> +	spin_unlock_bh(&session->back_lock);
> +}
> +
>  static void qedi_mtask_completion(struct qedi_ctx *qedi,
>  				  union iscsi_cqe *cqe,
>  				  struct iscsi_task *task,
> @@ -482,9 +705,16 @@ static void qedi_mtask_completion(struct qedi_ctx *qedi,
>  	iscsi_conn = conn->cls_conn->dd_data;
>  
>  	switch (hdr_opcode) {
> +	case ISCSI_OPCODE_SCSI_RESPONSE:
> +	case ISCSI_OPCODE_DATA_IN:
> +		qedi_scsi_completion(qedi, cqe, task, iscsi_conn);
> +		break;
>  	case ISCSI_OPCODE_LOGIN_RESPONSE:
>  		qedi_process_login_resp(qedi, cqe, task, conn);
>  		break;
> +	case ISCSI_OPCODE_TMF_RESPONSE:
> +		qedi_process_tmf_resp(qedi, cqe, task, conn);
> +		break;
>  	case ISCSI_OPCODE_TEXT_RESPONSE:
>  		qedi_process_text_resp(qedi, cqe, task, conn);
>  		break;
> @@ -520,6 +750,131 @@ static void qedi_process_nopin_local_cmpl(struct qedi_ctx *qedi,
>  	spin_unlock_bh(&session->back_lock);
>  }
>  
> +static void qedi_process_cmd_cleanup_resp(struct qedi_ctx *qedi,
> +					  struct iscsi_cqe_solicited *cqe,
> +					  struct iscsi_task *task,
> +					  struct iscsi_conn *conn)
> +{
> +	struct qedi_work_map *work, *work_tmp;
> +	u32 proto_itt = cqe->itid;
> +	u32 ptmp_itt = 0;
> +	itt_t protoitt = 0;
> +	int found = 0;
> +	struct qedi_cmd *qedi_cmd = NULL;
> +	u32 rtid = 0;
> +	u32 iscsi_cid;
> +	struct qedi_conn *qedi_conn;
> +	struct qedi_cmd *cmd_new, *dbg_cmd;
> +	struct iscsi_task *mtask;
> +	struct iscsi_tm *tmf_hdr = NULL;
> +
> +	iscsi_cid = cqe->conn_id;
> +	qedi_conn = qedi->cid_que.conn_cid_tbl[iscsi_cid];
> +
> +	/* Based on this itt get the corresponding qedi_cmd */
> +	spin_lock_bh(&qedi_conn->tmf_work_lock);
> +	list_for_each_entry_safe(work, work_tmp, &qedi_conn->tmf_work_list,
> +				 list) {
> +		if (work->rtid == proto_itt) {
> +			/* We found the command */
> +			qedi_cmd = work->qedi_cmd;
> +			if (!qedi_cmd->list_tmf_work) {
> +				QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +					  "TMF work not found, cqe->tid=0x%x, cid=0x%x\n",
> +					  proto_itt, qedi_conn->iscsi_conn_id);
> +				WARN_ON(1);
> +			}
> +			found = 1;
> +			mtask = qedi_cmd->task;
> +			tmf_hdr = (struct iscsi_tm *)mtask->hdr;
> +			rtid = work->rtid;
> +
> +			list_del_init(&work->list);
> +			kfree(work);
> +			qedi_cmd->list_tmf_work = NULL;
> +		}
> +	}
> +	spin_unlock_bh(&qedi_conn->tmf_work_lock);
> +
> +	if (found) {
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +			  "TMF work, cqe->tid=0x%x, tmf flags=0x%x, cid=0x%x\n",
> +			  proto_itt, tmf_hdr->flags, qedi_conn->iscsi_conn_id);
> +
> +		if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +		    ISCSI_TM_FUNC_ABORT_TASK) {
> +			spin_lock_bh(&conn->session->back_lock);
> +
> +			protoitt = build_itt(get_itt(tmf_hdr->rtt),
> +					     conn->session->age);
> +			task = iscsi_itt_to_task(conn, protoitt);
> +
> +			spin_unlock_bh(&conn->session->back_lock);
> +
> +			if (!task) {
> +				QEDI_NOTICE(&qedi->dbg_ctx,
> +					    "IO task completed, tmf rtt=0x%x, cid=0x%x\n",
> +					    get_itt(tmf_hdr->rtt),
> +					    qedi_conn->iscsi_conn_id);
> +				return;
> +			}
> +
> +			dbg_cmd = task->dd_data;
> +
> +			QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +				  "Abort tmf rtt=0x%x, i/o itt=0x%x, i/o tid=0x%x, cid=0x%x\n",
> +				  get_itt(tmf_hdr->rtt), get_itt(task->itt),
> +				  dbg_cmd->task_id, qedi_conn->iscsi_conn_id);
> +
> +			if (qedi_cmd->state == CLEANUP_WAIT_FAILED)
> +				qedi_cmd->state = CLEANUP_RECV;
> +
> +			qedi_clear_task_idx(qedi_conn->qedi, rtid);
> +
> +			spin_lock(&qedi_conn->list_lock);
> +			list_del_init(&dbg_cmd->io_cmd);
> +			qedi_conn->active_cmd_count--;
> +			spin_unlock(&qedi_conn->list_lock);
> +			qedi_cmd->state = CLEANUP_RECV;
> +			wake_up_interruptible(&qedi_conn->wait_queue);
> +		}
> +	} else if (qedi_conn->cmd_cleanup_req > 0) {
> +		spin_lock_bh(&conn->session->back_lock);
> +		qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt);
> +		protoitt = build_itt(ptmp_itt, conn->session->age);
> +		task = iscsi_itt_to_task(conn, protoitt);
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +			  "cleanup io itid=0x%x, protoitt=0x%x, cmd_cleanup_cmpl=%d, cid=0x%x\n",
> +			  cqe->itid, protoitt, qedi_conn->cmd_cleanup_cmpl,
> +			  qedi_conn->iscsi_conn_id);
> +
> +		spin_unlock_bh(&conn->session->back_lock);
> +		if (!task) {
> +			QEDI_NOTICE(&qedi->dbg_ctx,
> +				    "task is null, itid=0x%x, cid=0x%x\n",
> +				    cqe->itid, qedi_conn->iscsi_conn_id);
> +			return;
> +		}
> +		qedi_conn->cmd_cleanup_cmpl++;
> +		wake_up(&qedi_conn->wait_queue);
> +		cmd_new = task->dd_data;
> +
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_TID,
> +			  "Freeing tid=0x%x for cid=0x%x\n",
> +			  cqe->itid, qedi_conn->iscsi_conn_id);
> +		qedi_clear_task_idx(qedi_conn->qedi, cqe->itid);
> +
> +	} else {
> +		qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt);
> +		protoitt = build_itt(ptmp_itt, conn->session->age);
> +		task = iscsi_itt_to_task(conn, protoitt);
> +		QEDI_ERR(&qedi->dbg_ctx,
> +			 "Delayed or untracked cleanup response, itt=0x%x, tid=0x%x, cid=0x%x, task=%p\n",
> +			 protoitt, cqe->itid, qedi_conn->iscsi_conn_id, task);
> +		WARN_ON(1);
> +	}
> +}
> +
>  void qedi_fp_process_cqes(struct qedi_ctx *qedi, union iscsi_cqe *cqe,
>  			  uint16_t que_idx)
>  {
> @@ -619,6 +974,14 @@ void qedi_fp_process_cqes(struct qedi_ctx *qedi, union iscsi_cqe *cqe,
>  			break;
>  		}
>  		goto exit_fp_process;
> +	case ISCSI_CQE_TYPE_DUMMY:
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, "Dummy CqE\n");
> +		goto exit_fp_process;
> +	case ISCSI_CQE_TYPE_TASK_CLEANUP:
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, "CleanUp CqE\n");
> +		qedi_process_cmd_cleanup_resp(qedi, &cqe->cqe_solicited, task,
> +					      conn);
> +		goto exit_fp_process;
>  	default:
>  		QEDI_ERR(&qedi->dbg_ctx, "Error cqe.\n");
>  		break;
> @@ -904,6 +1267,440 @@ int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn,
>  	return 0;
>  }
>  
> +int qedi_cleanup_all_io(struct qedi_ctx *qedi, struct qedi_conn *qedi_conn,
> +			struct iscsi_task *task, bool in_recovery)
> +{
> +	int rval;
> +	struct iscsi_task *ctask;
> +	struct qedi_cmd *cmd, *cmd_tmp;
> +	struct iscsi_tm *tmf_hdr;
> +	unsigned int lun = 0;
> +	bool lun_reset = false;
> +	struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
> +	struct iscsi_session *session = conn->session;
> +
> +	/* From recovery, task is NULL or from tmf resp valid task */
> +	if (task) {
> +		tmf_hdr = (struct iscsi_tm *)task->hdr;
> +
> +		if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +			ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) {
> +			lun_reset = true;
> +			lun = scsilun_to_int(&tmf_hdr->lun);
> +		}
> +	}
> +
> +	qedi_conn->cmd_cleanup_req = 0;
> +	qedi_conn->cmd_cleanup_cmpl = 0;
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "active_cmd_count=%d, cid=0x%x, in_recovery=%d, lun_reset=%d\n",
> +		  qedi_conn->active_cmd_count, qedi_conn->iscsi_conn_id,
> +		  in_recovery, lun_reset);
> +
> +	if (lun_reset)
> +		spin_lock_bh(&session->back_lock);
> +
> +	spin_lock(&qedi_conn->list_lock);
> +
> +	list_for_each_entry_safe(cmd, cmd_tmp, &qedi_conn->active_cmd_list,
> +				 io_cmd) {
> +		ctask = cmd->task;
> +		if (ctask == task)
> +			continue;
> +
> +		if (lun_reset) {
> +			if (cmd->scsi_cmd && cmd->scsi_cmd->device) {
> +				QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +					  "tid=0x%x itt=0x%x scsi_cmd_ptr=%p device=%p task_state=%d cmd_state=0%x cid=0x%x\n",
> +					  cmd->task_id, get_itt(ctask->itt),
> +					  cmd->scsi_cmd, cmd->scsi_cmd->device,
> +					  ctask->state, cmd->state,
> +					  qedi_conn->iscsi_conn_id);
> +				if (cmd->scsi_cmd->device->lun != lun)
> +					continue;
> +			}
> +		}
> +		qedi_conn->cmd_cleanup_req++;
> +		qedi_iscsi_cleanup_task(ctask, true);
> +
> +		list_del_init(&cmd->io_cmd);
> +		qedi_conn->active_cmd_count--;
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "Deleted active cmd list node io_cmd=%p, cid=0x%x\n",
> +			  &cmd->io_cmd, qedi_conn->iscsi_conn_id);
> +	}
> +
> +	spin_unlock(&qedi_conn->list_lock);
> +
> +	if (lun_reset)
> +		spin_unlock_bh(&session->back_lock);
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "cmd_cleanup_req=%d, cid=0x%x\n",
> +		  qedi_conn->cmd_cleanup_req,
> +		  qedi_conn->iscsi_conn_id);
> +
> +	rval  = wait_event_interruptible_timeout(qedi_conn->wait_queue,
> +						 ((qedi_conn->cmd_cleanup_req ==
> +						 qedi_conn->cmd_cleanup_cmpl) ||
> +						 qedi_conn->ep),
> +						 5 * HZ);
> +	if (rval) {
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +			  "i/o cmd_cleanup_req=%d, equal to cmd_cleanup_cmpl=%d, cid=0x%x\n",
> +			  qedi_conn->cmd_cleanup_req,
> +			  qedi_conn->cmd_cleanup_cmpl,
> +			  qedi_conn->iscsi_conn_id);
> +
> +		return 0;
> +	}
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "i/o cmd_cleanup_req=%d, not equal to cmd_cleanup_cmpl=%d, cid=0x%x\n",
> +		  qedi_conn->cmd_cleanup_req,
> +		  qedi_conn->cmd_cleanup_cmpl,
> +		  qedi_conn->iscsi_conn_id);
> +
> +	iscsi_host_for_each_session(qedi->shost,
> +				    qedi_mark_device_missing);
> +	qedi_ops->common->drain(qedi->cdev);
> +
> +	/* Enable IOs for all other sessions except current.*/
> +	if (!wait_event_interruptible_timeout(qedi_conn->wait_queue,
> +					      (qedi_conn->cmd_cleanup_req ==
> +					       qedi_conn->cmd_cleanup_cmpl),
> +					      5 * HZ)) {
> +		iscsi_host_for_each_session(qedi->shost,
> +					    qedi_mark_device_available);
> +		return -1;
> +	}
> +
> +	iscsi_host_for_each_session(qedi->shost,
> +				    qedi_mark_device_available);
> +
> +	return 0;
> +}
> +
> +void qedi_clearsq(struct qedi_ctx *qedi, struct qedi_conn *qedi_conn,
> +		  struct iscsi_task *task)
> +{
> +	struct qedi_endpoint *qedi_ep;
> +	int rval;
> +
> +	qedi_ep = qedi_conn->ep;
> +	qedi_conn->cmd_cleanup_req = 0;
> +	qedi_conn->cmd_cleanup_cmpl = 0;
> +
> +	if (!qedi_ep) {
> +		QEDI_WARN(&qedi->dbg_ctx,
> +			  "Cannot proceed, ep already disconnected, cid=0x%x\n",
> +			  qedi_conn->iscsi_conn_id);
> +		return;
> +	}
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
> +		  "Clearing SQ for cid=0x%x, conn=%p, ep=%p\n",
> +		  qedi_conn->iscsi_conn_id, qedi_conn, qedi_ep);
> +
> +	qedi_ops->clear_sq(qedi->cdev, qedi_ep->handle);
> +
> +	rval = qedi_cleanup_all_io(qedi, qedi_conn, task, true);
> +	if (rval) {
> +		QEDI_ERR(&qedi->dbg_ctx,
> +			 "fatal error, need hard reset, cid=0x%x\n",
> +			 qedi_conn->iscsi_conn_id);
> +		WARN_ON(1);
> +	}
> +}
> +
> +static int qedi_wait_for_cleanup_request(struct qedi_ctx *qedi,
> +					 struct qedi_conn *qedi_conn,
> +					 struct iscsi_task *task,
> +					 struct qedi_cmd *qedi_cmd,
> +					 struct qedi_work_map *list_work)
> +{
> +	struct qedi_cmd *cmd = (struct qedi_cmd *)task->dd_data;
> +	int wait;
> +
> +	wait  = wait_event_interruptible_timeout(qedi_conn->wait_queue,
> +						 ((qedi_cmd->state ==
> +						   CLEANUP_RECV) ||
> +						 ((qedi_cmd->type == TYPEIO) &&
> +						  (cmd->state ==
> +						   RESPONSE_RECEIVED))),
> +						 5 * HZ);
> +	if (!wait) {
> +		qedi_cmd->state = CLEANUP_WAIT_FAILED;
> +
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +			  "Cleanup timedout tid=0x%x, issue connection recovery, cid=0x%x\n",
> +			  cmd->task_id, qedi_conn->iscsi_conn_id);
> +
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void qedi_tmf_work(struct work_struct *work)
> +{
> +	struct qedi_cmd *qedi_cmd =
> +		container_of(work, struct qedi_cmd, tmf_work);
> +	struct qedi_conn *qedi_conn = qedi_cmd->conn;
> +	struct qedi_ctx *qedi = qedi_conn->qedi;
> +	struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
> +	struct iscsi_cls_session *cls_sess;
> +	struct qedi_work_map *list_work = NULL;
> +	struct iscsi_task *mtask;
> +	struct qedi_cmd *cmd;
> +	struct iscsi_task *ctask;
> +	struct iscsi_tm *tmf_hdr;
> +	s16 rval = 0;
> +	s16 tid = 0;
> +
> +	mtask = qedi_cmd->task;
> +	tmf_hdr = (struct iscsi_tm *)mtask->hdr;
> +	cls_sess = iscsi_conn_to_session(qedi_conn->cls_conn);
> +	set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +
> +	ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt);
> +	if (!ctask || !ctask->sc) {
> +		QEDI_ERR(&qedi->dbg_ctx, "Task already completed\n");
> +		goto abort_ret;
> +	}
> +
> +	cmd = (struct qedi_cmd *)ctask->dd_data;
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
> +		  "Abort tmf rtt=0x%x, cmd itt=0x%x, cmd tid=0x%x, cid=0x%x\n",
> +		  get_itt(tmf_hdr->rtt), get_itt(ctask->itt), cmd->task_id,
> +		  qedi_conn->iscsi_conn_id);
> +
> +	if (do_not_recover) {
> +		QEDI_ERR(&qedi->dbg_ctx, "DONT SEND CLEANUP/ABORT %d\n",
> +			 do_not_recover);
> +		goto abort_ret;
> +	}
> +
> +	list_work = kzalloc(sizeof(*list_work), GFP_ATOMIC);
> +	if (!list_work) {
> +		QEDI_ERR(&qedi->dbg_ctx, "Memory alloction failed\n");
> +		goto abort_ret;
> +	}
> +
> +	qedi_cmd->type = TYPEIO;
> +	list_work->qedi_cmd = qedi_cmd;
> +	list_work->rtid = cmd->task_id;
> +	list_work->state = QEDI_WORK_SCHEDULED;
> +	qedi_cmd->list_tmf_work = list_work;
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "Queue tmf work=%p, list node=%p, cid=0x%x, tmf flags=0x%x\n",
> +		  list_work->ptr_tmf_work, list_work, qedi_conn->iscsi_conn_id,
> +		  tmf_hdr->flags);
> +
> +	spin_lock_bh(&qedi_conn->tmf_work_lock);
> +	list_add_tail(&list_work->list, &qedi_conn->tmf_work_list);
> +	spin_unlock_bh(&qedi_conn->tmf_work_lock);
> +
> +	qedi_iscsi_cleanup_task(ctask, false);
> +
> +	rval = qedi_wait_for_cleanup_request(qedi, qedi_conn, ctask, qedi_cmd,
> +					     list_work);
> +	if (rval == -1) {
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
> +			  "FW cleanup got escalated, cid=0x%x\n",
> +			  qedi_conn->iscsi_conn_id);
> +		goto ldel_exit;
> +	}
> +
> +	tid = qedi_get_task_idx(qedi);
> +	if (tid == -1) {
> +		QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n",
> +			 qedi_conn->iscsi_conn_id);
> +		goto ldel_exit;
> +	}
> +
> +	qedi_cmd->task_id = tid;
> +	qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task);
> +
> +abort_ret:
> +	clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +	return;
> +
> +ldel_exit:
> +	spin_lock_bh(&qedi_conn->tmf_work_lock);
> +	if (!qedi_cmd->list_tmf_work) {
> +		list_del_init(&list_work->list);
> +		qedi_cmd->list_tmf_work = NULL;
> +		kfree(list_work);
> +	}
> +	spin_unlock_bh(&qedi_conn->tmf_work_lock);
> +
> +	spin_lock(&qedi_conn->list_lock);
> +	list_del_init(&cmd->io_cmd);
> +	qedi_conn->active_cmd_count--;
> +	spin_unlock(&qedi_conn->list_lock);
> +
> +	clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
> +}
> +
> +static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
> +			       struct iscsi_task *mtask)
> +{
> +	struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
> +	struct qedi_ctx *qedi = qedi_conn->qedi;
> +	struct iscsi_task_context *fw_task_ctx;
> +	struct iscsi_tmf_request_hdr *fw_tmf_request;
> +	struct iscsi_sge *single_sge;
> +	struct qedi_cmd *qedi_cmd;
> +	struct qedi_cmd *cmd;
> +	struct iscsi_task *ctask;
> +	struct iscsi_tm *tmf_hdr;
> +	struct iscsi_sge *req_sge;
> +	struct iscsi_sge *resp_sge;
> +	u32 scsi_lun[2];
> +	s16 tid = 0, ptu_invalidate = 0;
> +
> +	req_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl;
> +	resp_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl;
> +	qedi_cmd = (struct qedi_cmd *)mtask->dd_data;
> +	tmf_hdr = (struct iscsi_tm *)mtask->hdr;
> +
> +	tid = qedi_cmd->task_id;
> +	qedi_update_itt_map(qedi, tid, mtask->itt);
> +
> +	fw_task_ctx =
> +	      (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid);
> +	memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context));
> +
> +	fw_tmf_request = &fw_task_ctx->ystorm_st_context.pdu_hdr.tmf_request;
> +	fw_tmf_request->itt = qedi_set_itt(tid, get_itt(mtask->itt));
> +	fw_tmf_request->cmd_sn = be32_to_cpu(tmf_hdr->cmdsn);
> +
> +	memcpy(scsi_lun, &tmf_hdr->lun, sizeof(struct scsi_lun));
> +	fw_tmf_request->lun.lo = be32_to_cpu(scsi_lun[0]);
> +	fw_tmf_request->lun.hi = be32_to_cpu(scsi_lun[1]);
> +
> +	if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) {
> +		ptu_invalidate = 1;
> +		qedi->tid_reuse_count[tid] = 0;
> +	}
> +	fw_task_ctx->ystorm_st_context.state.reuse_count =
> +						qedi->tid_reuse_count[tid];
> +	fw_task_ctx->mstorm_st_context.reuse_count =
> +						qedi->tid_reuse_count[tid]++;
> +
> +	if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +	     ISCSI_TM_FUNC_ABORT_TASK) {
> +		ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt);
> +		if (!ctask || !ctask->sc) {
> +			QEDI_ERR(&qedi->dbg_ctx,
> +				 "Could not get reference task\n");
> +			return 0;
> +		}
> +		cmd = (struct qedi_cmd *)ctask->dd_data;
> +		fw_tmf_request->rtt =
> +				qedi_set_itt(cmd->task_id,
> +					     get_itt(tmf_hdr->rtt));
> +	} else {
> +		fw_tmf_request->rtt = ISCSI_RESERVED_TAG;
> +	}
> +
> +	fw_tmf_request->opcode = tmf_hdr->opcode;
> +	fw_tmf_request->function = tmf_hdr->flags;
> +	fw_tmf_request->hdr_second_dword = ntoh24(tmf_hdr->dlength);
> +	fw_tmf_request->ref_cmd_sn = be32_to_cpu(tmf_hdr->refcmdsn);
> +
> +	single_sge = &fw_task_ctx->mstorm_st_context.sgl_union.single_sge;
> +	fw_task_ctx->mstorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH;
> +	fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id;
> +	single_sge->sge_addr.lo = resp_sge->sge_addr.lo;
> +	single_sge->sge_addr.hi = resp_sge->sge_addr.hi;
> +	single_sge->sge_len = resp_sge->sge_len;
> +
> +	SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +		  ISCSI_MFLAGS_SINGLE_SGE, 1);
> +	SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +		  ISCSI_MFLAGS_SLOW_IO, 0);
> +	fw_task_ctx->mstorm_st_context.sgl_size = 1;
> +	fw_task_ctx->mstorm_st_context.rem_task_size = resp_sge->sge_len;
> +
> +	/* Ustorm context */
> +	fw_task_ctx->ustorm_st_context.rem_rcv_len = 0;
> +	fw_task_ctx->ustorm_st_context.exp_data_transfer_len = 0;
> +	fw_task_ctx->ustorm_st_context.exp_data_sn = 0;
> +	fw_task_ctx->ustorm_st_context.task_type =  ISCSI_TASK_TYPE_MIDPATH;
> +	fw_task_ctx->ustorm_st_context.cq_rss_number = 0;
> +
> +	SET_FIELD(fw_task_ctx->ustorm_st_context.flags,
> +		  USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0);
> +	SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +		  ISCSI_REG1_NUM_FAST_SGES, 0);
> +
> +	fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id;
> +	SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1,
> +		  USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1);
> +	fw_task_ctx->ustorm_st_context.lun.lo = be32_to_cpu(scsi_lun[0]);
> +	fw_task_ctx->ustorm_st_context.lun.hi = be32_to_cpu(scsi_lun[1]);
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "Add TMF to SQ, tmf tid=0x%x, itt=0x%x, cid=0x%x\n",
> +		  tid,  mtask->itt, qedi_conn->iscsi_conn_id);
> +
> +	spin_lock(&qedi_conn->list_lock);
> +	list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list);
> +	qedi_cmd->io_cmd_in_list = true;
> +	qedi_conn->active_cmd_count++;
> +	spin_unlock(&qedi_conn->list_lock);
> +
> +	qedi_add_to_sq(qedi_conn, mtask, tid, ptu_invalidate, false);
> +	qedi_ring_doorbell(qedi_conn);
> +	return 0;
> +}
> +
> +int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn,
> +			  struct iscsi_task *mtask)
> +{
> +	struct qedi_ctx *qedi = qedi_conn->qedi;
> +	struct iscsi_tm *tmf_hdr;
> +	struct qedi_cmd *qedi_cmd = (struct qedi_cmd *)mtask->dd_data;
> +	s16 tid = 0;
> +
> +	tmf_hdr = (struct iscsi_tm *)mtask->hdr;
> +	qedi_cmd->task = mtask;
> +
> +	/* If abort task then schedule the work and return */
> +	if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +	    ISCSI_TM_FUNC_ABORT_TASK) {
> +		qedi_cmd->state = CLEANUP_WAIT;
> +		INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_work);
> +		queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work);
> +
> +	} else if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +		    ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) ||
> +		   ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +		    ISCSI_TM_FUNC_TARGET_WARM_RESET) ||
> +		   ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
> +		    ISCSI_TM_FUNC_TARGET_COLD_RESET)) {
> +		tid = qedi_get_task_idx(qedi);
> +		if (tid == -1) {
> +			QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n",
> +				 qedi_conn->iscsi_conn_id);
> +			return -1;
> +		}
> +		qedi_cmd->task_id = tid;
> +
> +		qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task);
> +
> +	} else {
> +		QEDI_ERR(&qedi->dbg_ctx, "Invalid tmf, cid=0x%x\n",
> +			 qedi_conn->iscsi_conn_id);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
>  int qedi_send_iscsi_text(struct qedi_conn *qedi_conn,
>  			 struct iscsi_task *task)
>  {
> @@ -1121,3 +1918,488 @@ int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn,
>  	qedi_ring_doorbell(qedi_conn);
>  	return 0;
>  }
> +
> +static int qedi_split_bd(struct qedi_cmd *cmd, u64 addr, int sg_len,
> +			 int bd_index)
> +{
> +	struct iscsi_sge *bd = cmd->io_tbl.sge_tbl;
> +	int frag_size, sg_frags;
> +
> +	sg_frags = 0;
> +
> +	while (sg_len) {
> +		if (addr % QEDI_PAGE_SIZE)
> +			frag_size =
> +				   (QEDI_PAGE_SIZE - (addr % QEDI_PAGE_SIZE));
> +		else
> +			frag_size = (sg_len > QEDI_BD_SPLIT_SZ) ? 0 :
> +				    (sg_len % QEDI_BD_SPLIT_SZ);
> +
> +		if (frag_size == 0)
> +			frag_size = QEDI_BD_SPLIT_SZ;
> +
> +		bd[bd_index + sg_frags].sge_addr.lo = (addr & 0xffffffff);
> +		bd[bd_index + sg_frags].sge_addr.hi = (addr >> 32);
> +		bd[bd_index + sg_frags].sge_len = (u16)frag_size;
> +		QEDI_INFO(&cmd->conn->qedi->dbg_ctx, QEDI_LOG_IO,
> +			  "split sge %d: addr=%llx, len=%x",
> +			  (bd_index + sg_frags), addr, frag_size);
> +
> +		addr += (u64)frag_size;
> +		sg_frags++;
> +		sg_len -= frag_size;
> +	}
> +	return sg_frags;
> +}
> +
> +static int qedi_map_scsi_sg(struct qedi_ctx *qedi, struct qedi_cmd *cmd)
> +{
> +	struct scsi_cmnd *sc = cmd->scsi_cmd;
> +	struct iscsi_sge *bd = cmd->io_tbl.sge_tbl;
> +	struct scatterlist *sg;
> +	int byte_count = 0;
> +	int bd_count = 0;
> +	int sg_count;
> +	int sg_len;
> +	int sg_frags;
> +	u64 addr, end_addr;
> +	int i;
> +
> +	WARN_ON(scsi_sg_count(sc) > QEDI_ISCSI_MAX_BDS_PER_CMD);
> +
> +	sg_count = dma_map_sg(&qedi->pdev->dev, scsi_sglist(sc),
> +			      scsi_sg_count(sc), sc->sc_data_direction);
> +
> +	/*
> +	 * New condition to send single SGE as cached-SGL.
> +	 * Single SGE with length less than 64K.
> +	 */
> +	sg = scsi_sglist(sc);
> +	if ((sg_count == 1) && (sg_dma_len(sg) <= MAX_SGLEN_FOR_CACHESGL)) {
> +		sg_len = sg_dma_len(sg);
> +		addr = (u64)sg_dma_address(sg);
> +
> +		bd[bd_count].sge_addr.lo = (addr & 0xffffffff);
> +		bd[bd_count].sge_addr.hi = (addr >> 32);
> +		bd[bd_count].sge_len = (u16)sg_len;
> +
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO,
> +			  "single-cashed-sgl: bd_count:%d addr=%llx, len=%x",
> +			  sg_count, addr, sg_len);
> +
> +		return ++bd_count;
> +	}
> +
> +	scsi_for_each_sg(sc, sg, sg_count, i) {
> +		sg_len = sg_dma_len(sg);
> +		addr = (u64)sg_dma_address(sg);
> +		end_addr = (addr + sg_len);
> +
> +		/*
> +		 * first sg elem in the 'list',
> +		 * check if end addr is page-aligned.
> +		 */
> +		if ((i == 0) && (sg_count > 1) && (end_addr % QEDI_PAGE_SIZE))
> +			cmd->use_slowpath = true;
> +
> +		/*
> +		 * last sg elem in the 'list',
> +		 * check if start addr is page-aligned.
> +		 */
> +		else if ((i == (sg_count - 1)) &&
> +			 (sg_count > 1) && (addr % QEDI_PAGE_SIZE))
> +			cmd->use_slowpath = true;
> +
> +		/*
> +		 * middle sg elements in list,
> +		 * check if start and end addr is page-aligned
> +		 */
> +		else if ((i != 0) && (i != (sg_count - 1)) &&
> +			 ((addr % QEDI_PAGE_SIZE) ||
> +			 (end_addr % QEDI_PAGE_SIZE)))
> +			cmd->use_slowpath = true;
> +
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO, "sg[%d] size=0x%x",
> +			  i, sg_len);
> +
> +		if (sg_len > QEDI_BD_SPLIT_SZ) {
> +			sg_frags = qedi_split_bd(cmd, addr, sg_len, bd_count);
> +		} else {
> +			sg_frags = 1;
> +			bd[bd_count].sge_addr.lo = addr & 0xffffffff;
> +			bd[bd_count].sge_addr.hi = addr >> 32;
> +			bd[bd_count].sge_len = sg_len;
> +		}
> +		byte_count += sg_len;
> +		bd_count += sg_frags;
> +	}
> +
> +	if (byte_count != scsi_bufflen(sc))
> +		QEDI_ERR(&qedi->dbg_ctx,
> +			 "byte_count = %d != scsi_bufflen = %d\n", byte_count,
> +			 scsi_bufflen(sc));
> +	else
> +		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO, "byte_count = %d\n",
> +			  byte_count);
> +
> +	WARN_ON(byte_count != scsi_bufflen(sc));
> +
> +	return bd_count;
> +}
> +
> +static void qedi_iscsi_map_sg_list(struct qedi_cmd *cmd)
> +{
> +	int bd_count;
> +	struct scsi_cmnd *sc = cmd->scsi_cmd;
> +
> +	if (scsi_sg_count(sc)) {
> +		bd_count  = qedi_map_scsi_sg(cmd->conn->qedi, cmd);
> +		if (bd_count == 0)
> +			return;
> +	} else {
> +		struct iscsi_sge *bd = cmd->io_tbl.sge_tbl;
> +
> +		bd[0].sge_addr.lo = 0;
> +		bd[0].sge_addr.hi = 0;
> +		bd[0].sge_len = 0;
> +		bd_count = 0;
> +	}
> +	cmd->io_tbl.sge_valid = bd_count;
> +}
> +
> +static void qedi_cpy_scsi_cdb(struct scsi_cmnd *sc, u32 *dstp)
> +{
> +	u32 dword;
> +	int lpcnt;
> +	u8 *srcp;
> +
> +	lpcnt = sc->cmd_len / sizeof(dword);
> +	srcp = (u8 *)sc->cmnd;
> +	while (lpcnt--) {
> +		memcpy(&dword, (const void *)srcp, 4);
> +		*dstp = cpu_to_be32(dword);
> +		srcp += 4;
> +		dstp++;
> +	}
> +	if (sc->cmd_len & 0x3) {
> +		dword = (u32)srcp[0] | ((u32)srcp[1] << 8);
> +		*dstp = cpu_to_be32(dword);
> +	}
> +}
> +
> +void qedi_trace_io(struct qedi_ctx *qedi, struct iscsi_task *task,
> +		   u16 tid, int8_t direction)
> +{
> +	struct qedi_io_log *io_log;
> +	struct iscsi_conn *conn = task->conn;
> +	struct qedi_conn *qedi_conn = conn->dd_data;
> +	struct scsi_cmnd *sc_cmd = task->sc;
> +	unsigned long flags;
> +	u8 op;
> +
> +	spin_lock_irqsave(&qedi->io_trace_lock, flags);
> +
> +	io_log = &qedi->io_trace_buf[qedi->io_trace_idx];
> +	io_log->direction = direction;
> +	io_log->task_id = tid;
> +	io_log->cid = qedi_conn->iscsi_conn_id;
> +	io_log->lun = sc_cmd->device->lun;
> +	io_log->op = sc_cmd->cmnd[0];
> +	op = sc_cmd->cmnd[0];
> +
> +	if (op == READ_10 || op == WRITE_10) {
> +		io_log->lba[0] = sc_cmd->cmnd[2];
> +		io_log->lba[1] = sc_cmd->cmnd[3];
> +		io_log->lba[2] = sc_cmd->cmnd[4];
> +		io_log->lba[3] = sc_cmd->cmnd[5];
> +	} else {
> +		io_log->lba[0] = 0;
> +		io_log->lba[1] = 0;
> +		io_log->lba[2] = 0;
> +		io_log->lba[3] = 0;
> +	}
Only for READ_10 and WRITE_10? What about the other read or write commands?

> +	io_log->bufflen = scsi_bufflen(sc_cmd);
> +	io_log->sg_count = scsi_sg_count(sc_cmd);
> +	io_log->fast_sgs = qedi->fast_sgls;
> +	io_log->cached_sgs = qedi->cached_sgls;
> +	io_log->slow_sgs = qedi->slow_sgls;
> +	io_log->cached_sge = qedi->use_cached_sge;
> +	io_log->slow_sge = qedi->use_slow_sge;
> +	io_log->fast_sge = qedi->use_fast_sge;
> +	io_log->result = sc_cmd->result;
> +	io_log->jiffies = jiffies;
> +	io_log->blk_req_cpu = smp_processor_id();
> +
> +	if (direction == QEDI_IO_TRACE_REQ) {
> +		/* For requests we only care about the submission CPU */
> +		io_log->req_cpu = smp_processor_id() % qedi->num_queues;
> +		io_log->intr_cpu = 0;
> +		io_log->blk_rsp_cpu = 0;
> +	} else if (direction == QEDI_IO_TRACE_RSP) {
> +		io_log->req_cpu = smp_processor_id() % qedi->num_queues;
> +		io_log->intr_cpu = qedi->intr_cpu;
> +		io_log->blk_rsp_cpu = smp_processor_id();
> +	}
> +
> +	qedi->io_trace_idx++;
> +	if (qedi->io_trace_idx == QEDI_IO_TRACE_SIZE)
> +		qedi->io_trace_idx = 0;
> +
> +	qedi->use_cached_sge = false;
> +	qedi->use_slow_sge = false;
> +	qedi->use_fast_sge = false;
> +
> +	spin_unlock_irqrestore(&qedi->io_trace_lock, flags);
> +}
> +
> +int qedi_iscsi_send_ioreq(struct iscsi_task *task)
> +{
> +	struct iscsi_conn *conn = task->conn;
> +	struct iscsi_session *session = conn->session;
> +	struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
> +	struct qedi_ctx *qedi = iscsi_host_priv(shost);
> +	struct qedi_conn *qedi_conn = conn->dd_data;
> +	struct qedi_cmd *cmd = task->dd_data;
> +	struct scsi_cmnd *sc = task->sc;
> +	struct iscsi_task_context *fw_task_ctx;
> +	struct iscsi_cached_sge_ctx *cached_sge;
> +	struct iscsi_phys_sgl_ctx *phys_sgl;
> +	struct iscsi_virt_sgl_ctx *virt_sgl;
> +	struct ystorm_iscsi_task_st_ctx *yst_cxt;
> +	struct mstorm_iscsi_task_st_ctx *mst_cxt;
> +	struct iscsi_sgl *sgl_struct;
> +	struct iscsi_sge *single_sge;
> +	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
> +	struct iscsi_sge *bd = cmd->io_tbl.sge_tbl;
> +	enum iscsi_task_type task_type;
> +	struct iscsi_cmd_hdr *fw_cmd;
> +	u32 scsi_lun[2];
> +	u16 cq_idx = smp_processor_id() % qedi->num_queues;
> +	s16 ptu_invalidate = 0;
> +	s16 tid = 0;
> +	u8 num_fast_sgs;
> +
> +	tid = qedi_get_task_idx(qedi);
> +	if (tid == -1)
> +		return -ENOMEM;
> +
> +	qedi_iscsi_map_sg_list(cmd);
> +
> +	int_to_scsilun(sc->device->lun, (struct scsi_lun *)scsi_lun);
> +	fw_task_ctx =
> +	      (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid);
> +
> +	memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context));
> +	cmd->task_id = tid;
> +
> +	/* Ystrom context */
Ystrom or Ystorm?

> +	fw_cmd = &fw_task_ctx->ystorm_st_context.pdu_hdr.cmd;
> +	SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_ATTR, ISCSI_ATTR_SIMPLE);
> +
> +	if (sc->sc_data_direction == DMA_TO_DEVICE) {
> +		if (conn->session->initial_r2t_en) {
> +			fw_task_ctx->ustorm_ag_context.exp_data_acked =
> +				min((conn->session->imm_data_en *
> +				    conn->max_xmit_dlength),
> +				    conn->session->first_burst);
> +			fw_task_ctx->ustorm_ag_context.exp_data_acked =
> +			      min(fw_task_ctx->ustorm_ag_context.exp_data_acked,
> +				  scsi_bufflen(sc));
> +		} else {
> +			fw_task_ctx->ustorm_ag_context.exp_data_acked =
> +			      min(conn->session->first_burst, scsi_bufflen(sc));
> +		}
> +
> +		SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_WRITE, 1);
> +		task_type = ISCSI_TASK_TYPE_INITIATOR_WRITE;
> +	} else {
> +		if (scsi_bufflen(sc))
> +			SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_READ, 1);
> +		task_type = ISCSI_TASK_TYPE_INITIATOR_READ;
> +	}
> +
> +	fw_cmd->lun.lo = be32_to_cpu(scsi_lun[0]);
> +	fw_cmd->lun.hi = be32_to_cpu(scsi_lun[1]);
> +
> +	qedi_update_itt_map(qedi, tid, task->itt);
> +	fw_cmd->itt = qedi_set_itt(tid, get_itt(task->itt));
> +	fw_cmd->expected_transfer_length = scsi_bufflen(sc);
> +	fw_cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
> +	fw_cmd->opcode = hdr->opcode;
> +	qedi_cpy_scsi_cdb(sc, (u32 *)fw_cmd->cdb);
> +
> +	/* Mstorm context */
> +	fw_task_ctx->mstorm_st_context.sense_db.lo = (u32)cmd->sense_buffer_dma;
> +	fw_task_ctx->mstorm_st_context.sense_db.hi =
> +					(u32)((u64)cmd->sense_buffer_dma >> 32);
> +	fw_task_ctx->mstorm_ag_context.task_cid = qedi_conn->iscsi_conn_id;
> +	fw_task_ctx->mstorm_st_context.task_type = task_type;
> +
> +	if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) {
> +		ptu_invalidate = 1;
> +		qedi->tid_reuse_count[tid] = 0;
> +	}
> +	fw_task_ctx->ystorm_st_context.state.reuse_count =
> +						     qedi->tid_reuse_count[tid];
> +	fw_task_ctx->mstorm_st_context.reuse_count =
> +						   qedi->tid_reuse_count[tid]++;
> +
> +	/* Ustrorm context */
Ustrorm?

> +	fw_task_ctx->ustorm_st_context.rem_rcv_len = scsi_bufflen(sc);
> +	fw_task_ctx->ustorm_st_context.exp_data_transfer_len = scsi_bufflen(sc);
> +	fw_task_ctx->ustorm_st_context.exp_data_sn =
> +						   be32_to_cpu(hdr->exp_statsn);
> +	fw_task_ctx->ustorm_st_context.task_type = task_type;
> +	fw_task_ctx->ustorm_st_context.cq_rss_number = cq_idx;
> +	fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id;
> +
> +	SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1,
> +		  USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1);
> +	SET_FIELD(fw_task_ctx->ustorm_st_context.flags,
> +		  USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0);
> +
> +	num_fast_sgs = (cmd->io_tbl.sge_valid ?
> +			min((u16)QEDI_FAST_SGE_COUNT,
> +			    (u16)cmd->io_tbl.sge_valid) : 0);
> +	SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +		  ISCSI_REG1_NUM_FAST_SGES, num_fast_sgs);
> +
> +	fw_task_ctx->ustorm_st_context.lun.lo = be32_to_cpu(scsi_lun[0]);
> +	fw_task_ctx->ustorm_st_context.lun.hi = be32_to_cpu(scsi_lun[1]);
> +
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO, "Total sge count [%d]\n",
> +		  cmd->io_tbl.sge_valid);
> +
> +	yst_cxt = &fw_task_ctx->ystorm_st_context;
> +	mst_cxt = &fw_task_ctx->mstorm_st_context;
> +	/* Tx path */
> +	if (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) {
> +		/* not considering  superIO or FastIO */
> +		if (cmd->io_tbl.sge_valid == 1) {
> +			cached_sge = &yst_cxt->state.sgl_ctx_union.cached_sge;
> +			cached_sge->sge.sge_addr.lo = bd[0].sge_addr.lo;
> +			cached_sge->sge.sge_addr.hi = bd[0].sge_addr.hi;
> +			cached_sge->sge.sge_len = bd[0].sge_len;
> +			qedi->cached_sgls++;
> +		} else if ((cmd->io_tbl.sge_valid != 1) && cmd->use_slowpath) {
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SLOW_IO, 1);
> +			SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +				  ISCSI_REG1_NUM_FAST_SGES, 0);
> +			phys_sgl = &yst_cxt->state.sgl_ctx_union.phys_sgl;
> +			phys_sgl->sgl_base.lo = (u32)(cmd->io_tbl.sge_tbl_dma);
> +			phys_sgl->sgl_base.hi =
> +				     (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32);
> +			phys_sgl->sgl_size = cmd->io_tbl.sge_valid;
> +			qedi->slow_sgls++;
> +		} else if ((cmd->io_tbl.sge_valid != 1) && !cmd->use_slowpath) {
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SLOW_IO, 0);
> +			SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +				  ISCSI_REG1_NUM_FAST_SGES,
> +				  min((u16)QEDI_FAST_SGE_COUNT,
> +				      (u16)cmd->io_tbl.sge_valid));
> +			virt_sgl = &yst_cxt->state.sgl_ctx_union.virt_sgl;
> +			virt_sgl->sgl_base.lo = (u32)(cmd->io_tbl.sge_tbl_dma);
> +			virt_sgl->sgl_base.hi =
> +				      (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32);
> +			virt_sgl->sgl_initial_offset =
> +				 (u32)bd[0].sge_addr.lo & (QEDI_PAGE_SIZE - 1);
> +			qedi->fast_sgls++;
> +		}
> +		fw_task_ctx->mstorm_st_context.sgl_size = cmd->io_tbl.sge_valid;
> +		fw_task_ctx->mstorm_st_context.rem_task_size = scsi_bufflen(sc);
> +	} else {
> +	/* Rx path */
> +		if (cmd->io_tbl.sge_valid == 1) {
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SLOW_IO, 0);
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SINGLE_SGE, 1);
> +			single_sge = &mst_cxt->sgl_union.single_sge;
> +			single_sge->sge_addr.lo = bd[0].sge_addr.lo;
> +			single_sge->sge_addr.hi = bd[0].sge_addr.hi;
> +			single_sge->sge_len = bd[0].sge_len;
> +			qedi->cached_sgls++;
> +		} else if ((cmd->io_tbl.sge_valid != 1) && cmd->use_slowpath) {
> +			sgl_struct = &mst_cxt->sgl_union.sgl_struct;
> +			sgl_struct->sgl_addr.lo =
> +						(u32)(cmd->io_tbl.sge_tbl_dma);
> +			sgl_struct->sgl_addr.hi =
> +				     (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32);
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SLOW_IO, 1);
> +			SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +				  ISCSI_REG1_NUM_FAST_SGES, 0);
> +			sgl_struct->updated_sge_size = 0;
> +			sgl_struct->updated_sge_offset = 0;
> +			qedi->slow_sgls++;
> +		} else if ((cmd->io_tbl.sge_valid != 1) && !cmd->use_slowpath) {
> +			sgl_struct = &mst_cxt->sgl_union.sgl_struct;
> +			sgl_struct->sgl_addr.lo =
> +						(u32)(cmd->io_tbl.sge_tbl_dma);
> +			sgl_struct->sgl_addr.hi =
> +				     (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32);
> +			sgl_struct->byte_offset =
> +				(u32)bd[0].sge_addr.lo & (QEDI_PAGE_SIZE - 1);
> +			SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags,
> +				  ISCSI_MFLAGS_SLOW_IO, 0);
> +			SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map,
> +				  ISCSI_REG1_NUM_FAST_SGES, 0);
> +			sgl_struct->updated_sge_size = 0;
> +			sgl_struct->updated_sge_offset = 0;
> +			qedi->fast_sgls++;
> +		}
> +		fw_task_ctx->mstorm_st_context.sgl_size = cmd->io_tbl.sge_valid;
> +		fw_task_ctx->mstorm_st_context.rem_task_size = scsi_bufflen(sc);
> +	}
> +
> +	if (cmd->io_tbl.sge_valid == 1)
> +		/* Singel-SGL */
> +		qedi->use_cached_sge = true;
> +	else {
> +		if (cmd->use_slowpath)
> +			qedi->use_slow_sge = true;
> +		else
> +			qedi->use_fast_sge = true;
> +	}
> +	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO,
> +		  "%s: %s-SGL: num_sges=0x%x first-sge-lo=0x%x first-sge-hi=0x%x",
> +		  (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) ?
> +		  "Write " : "Read ", (cmd->io_tbl.sge_valid == 1) ?
> +		  "Single" : (cmd->use_slowpath ? "SLOW" : "FAST"),
> +		  (u16)cmd->io_tbl.sge_valid, (u32)(cmd->io_tbl.sge_tbl_dma),
> +		  (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32));
> +
> +	/*  Add command in active command list */
> +	spin_lock(&qedi_conn->list_lock);
> +	list_add_tail(&cmd->io_cmd, &qedi_conn->active_cmd_list);
> +	cmd->io_cmd_in_list = true;
> +	qedi_conn->active_cmd_count++;
> +	spin_unlock(&qedi_conn->list_lock);
> +
> +	qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false);
> +	qedi_ring_doorbell(qedi_conn);
> +	if (io_tracing)
> +		qedi_trace_io(qedi, task, tid, QEDI_IO_TRACE_REQ);
> +
> +	return 0;
> +}
> +
> +int qedi_iscsi_cleanup_task(struct iscsi_task *task, bool mark_cmd_node_deleted)
> +{
> +	struct iscsi_conn *conn = task->conn;
> +	struct qedi_conn *qedi_conn = conn->dd_data;
> +	struct qedi_cmd *cmd = task->dd_data;
> +	s16 ptu_invalidate = 0;
> +
> +	QEDI_INFO(&qedi_conn->qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
> +		  "issue cleanup tid=0x%x itt=0x%x task_state=%d cmd_state=0%x cid=0x%x\n",
> +		  cmd->task_id, get_itt(task->itt), task->state,
> +		  cmd->state, qedi_conn->iscsi_conn_id);
> +
> +	qedi_add_to_sq(qedi_conn, task, cmd->task_id, ptu_invalidate, true);
> +	qedi_ring_doorbell(qedi_conn);
> +
> +	return 0;
> +}
> diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h
> index 85ea3d7..c50c2b1 100644
> --- a/drivers/scsi/qedi/qedi_gbl.h
> +++ b/drivers/scsi/qedi/qedi_gbl.h
> @@ -28,11 +28,14 @@ int qedi_send_iscsi_login(struct qedi_conn *qedi_conn,
>  			  struct iscsi_task *task);
>  int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn,
>  			   struct iscsi_task *task);
> +int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn,
> +			  struct iscsi_task *mtask);
>  int qedi_send_iscsi_text(struct qedi_conn *qedi_conn,
>  			 struct iscsi_task *task);
>  int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn,
>  			   struct iscsi_task *task,
>  			   char *datap, int data_len, int unsol);
> +int qedi_iscsi_send_ioreq(struct iscsi_task *task);
>  int qedi_get_task_idx(struct qedi_ctx *qedi);
>  void qedi_clear_task_idx(struct qedi_ctx *qedi, int idx);
>  int qedi_iscsi_cleanup_task(struct iscsi_task *task,
> @@ -53,6 +56,9 @@ void qedi_start_conn_recovery(struct qedi_ctx *qedi,
>  int qedi_recover_all_conns(struct qedi_ctx *qedi);
>  void qedi_fp_process_cqes(struct qedi_ctx *qedi, union iscsi_cqe *cqe,
>  			  uint16_t que_idx);
> +int qedi_cleanup_all_io(struct qedi_ctx *qedi,
> +			struct qedi_conn *qedi_conn,
> +			struct iscsi_task *task, bool in_recovery);
>  void qedi_trace_io(struct qedi_ctx *qedi, struct iscsi_task *task,
>  		   u16 tid, int8_t direction);
>  int qedi_alloc_id(struct qedi_portid_tbl *id_tbl, u16 id);
> diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c
> index caecdb8..7a07211 100644
> --- a/drivers/scsi/qedi/qedi_iscsi.c
> +++ b/drivers/scsi/qedi/qedi_iscsi.c
> @@ -755,6 +755,9 @@ static int qedi_iscsi_send_generic_request(struct iscsi_task *task)
>  	case ISCSI_OP_LOGOUT:
>  		rc = qedi_send_iscsi_logout(qedi_conn, task);
>  		break;
> +	case ISCSI_OP_SCSI_TMFUNC:
> +		rc = qedi_iscsi_abort_work(qedi_conn, task);
> +		break;
>  	case ISCSI_OP_TEXT:
>  		rc = qedi_send_iscsi_text(qedi_conn, task);
>  		break;
> @@ -804,6 +807,9 @@ static int qedi_task_xmit(struct iscsi_task *task)
>  
>  	if (!sc)
>  		return qedi_mtask_xmit(conn, task);
> +
> +	cmd->scsi_cmd = sc;
> +	return qedi_iscsi_send_ioreq(task);
>  }
>  
>  static struct iscsi_endpoint *
> diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
> index 22d19a3..fd0d335 100644
> --- a/drivers/scsi/qedi/qedi_main.c
> +++ b/drivers/scsi/qedi/qedi_main.c
> @@ -43,6 +43,10 @@
>  module_param(debug, uint, S_IRUGO | S_IWUSR);
>  MODULE_PARM_DESC(debug, " Default debug level");
>  
> +uint io_tracing;
> +module_param(io_tracing, uint, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(io_tracing,
> +		 " Enable logging of SCSI requests/completions into trace buffer. (default off).");
>  const struct qed_iscsi_ops *qedi_ops;
>  static struct scsi_transport_template *qedi_scsi_transport;
>  static struct pci_driver qedi_pci_driver;
> 
Cheers,

Hannes
-- 
Dr. Hannes Reinecke		   Teamlead Storage & Networking
hare@suse.de			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)

^ permalink raw reply

* [PATCH net-next v2 2/2] openvswitch: remove unnecessary EXPORT_SYMBOLs
From: Jiri Benc @ 2016-10-19  9:26 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA; +Cc: dev-yBygre7rU0TnMu66kgdUjQ
In-Reply-To: <cover.1476869133.git.jbenc-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Some symbols exported to other modules are really used only by
openvswitch.ko. Remove the exports.

Tested by loading all 4 openvswitch modules, nothing breaks.

Signed-off-by: Jiri Benc <jbenc@redhat.com>
---
v2: ovs_vport_deferred_free is completely removed by the first patch
---
 net/openvswitch/datapath.c     | 2 --
 net/openvswitch/vport-netdev.c | 1 -
 net/openvswitch/vport.c        | 1 -
 3 files changed, 4 deletions(-)

diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 4d67ea856067..194435aa1165 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -59,7 +59,6 @@
 #include "vport-netdev.h"
 
 int ovs_net_id __read_mostly;
-EXPORT_SYMBOL_GPL(ovs_net_id);
 
 static struct genl_family dp_packet_genl_family;
 static struct genl_family dp_flow_genl_family;
@@ -131,7 +130,6 @@ int lockdep_ovsl_is_held(void)
 	else
 		return 1;
 }
-EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held);
 #endif
 
 static struct vport *new_vport(const struct vport_parms *);
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 4e3972344aa6..e825753de1e0 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -162,7 +162,6 @@ void ovs_netdev_detach_dev(struct vport *vport)
 				netdev_master_upper_dev_get(vport->dev));
 	dev_set_promiscuity(vport->dev, -1);
 }
-EXPORT_SYMBOL_GPL(ovs_netdev_detach_dev);
 
 static void netdev_destroy(struct vport *vport)
 {
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 9d0a87845843..9bb85b35a1fb 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -463,7 +463,6 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
 	ovs_dp_process_packet(skb, &key);
 	return 0;
 }
-EXPORT_SYMBOL_GPL(ovs_vport_receive);
 
 static unsigned int packet_length(const struct sk_buff *skb)
 {
-- 
1.8.3.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

^ permalink raw reply related

* [PATCH net-next v2 1/2] openvswitch: remove unused functions
From: Jiri Benc @ 2016-10-19  9:26 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA; +Cc: dev-yBygre7rU0TnMu66kgdUjQ
In-Reply-To: <cover.1476869133.git.jbenc-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

ovs_vport_deferred_free is not used anywhere. It's the only caller of
free_vport_rcu thus this one can be removed, too.

Signed-off-by: Jiri Benc <jbenc@redhat.com>
---
new in v2
---
 net/openvswitch/vport.c | 16 ----------------
 net/openvswitch/vport.h |  1 -
 2 files changed, 17 deletions(-)

diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 7387418ac514..9d0a87845843 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -465,22 +465,6 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
 }
 EXPORT_SYMBOL_GPL(ovs_vport_receive);
 
-static void free_vport_rcu(struct rcu_head *rcu)
-{
-	struct vport *vport = container_of(rcu, struct vport, rcu);
-
-	ovs_vport_free(vport);
-}
-
-void ovs_vport_deferred_free(struct vport *vport)
-{
-	if (!vport)
-		return;
-
-	call_rcu(&vport->rcu, free_vport_rcu);
-}
-EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
-
 static unsigned int packet_length(const struct sk_buff *skb)
 {
 	unsigned int length = skb->len - ETH_HLEN;
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index f01f28a567ad..46e5b69927c7 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -149,7 +149,6 @@ struct vport_ops {
 struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *,
 			      const struct vport_parms *);
 void ovs_vport_free(struct vport *);
-void ovs_vport_deferred_free(struct vport *vport);
 
 #define VPORT_ALIGN 8
 
-- 
1.8.3.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

^ permalink raw reply related


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