public inbox for linux-rdma@vger.kernel.org
 help / color / mirror / Atom feed
From: Steve Wise <swise-7bPotxP6k4+P2YhJcF5u+vpXobYPEAuW@public.gmane.org>
To: miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
Cc: rdreier-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org,
	linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [PATCH 2/2] RDMA/nes: add support of iWARP multicast acceleration over IB_QPT_RAW_ETY QP type
Date: Tue, 04 May 2010 12:19:15 -0500	[thread overview]
Message-ID: <4BE05713.6030101@opengridcomputing.com> (raw)
In-Reply-To: <20100430165434.1386.80375.stgit-dAdtdUp2yJRU7keBU/FxOFDQ4js95KgL@public.gmane.org>

Hey Mirek,

It looks like this patch adds a new file interface for a UD service.  
Why didn't you extend the existing UD interface as needed? 

What IO is supported with these changes?  IMA via the raw QP, but what 
ud_post_send() and friends used for?


Steve.



miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org wrote:
> This patch implements iWarp multicast acceleration (IMA)
> over IB_QPT_RAW_ETY QP type in nes driver.
>
> Application creates a raw eth QP (IBV_QPT_RAW_ETH in user-space) and
> manages the multicast via ibv_attach_mcast and ibv_detach_mcast calls.
>
> Calling ibv_attach_mcast/ibv_datach_mcast has an effect of
> enabling/disabling L2 MAC address filters in HW.
>
> Signed-off-by: Mirek Walukiewicz <miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>
>
>
> ---
>
>  drivers/infiniband/hw/nes/Makefile    |    2 
>  drivers/infiniband/hw/nes/nes.c       |    4 
>  drivers/infiniband/hw/nes/nes.h       |    2 
>  drivers/infiniband/hw/nes/nes_nic.c   |   11 
>  drivers/infiniband/hw/nes/nes_ud.c    | 2070 +++++++++++++++++++++++++++++++++
>  drivers/infiniband/hw/nes/nes_ud.h    |   86 +
>  drivers/infiniband/hw/nes/nes_verbs.c |  221 +++-
>  drivers/infiniband/hw/nes/nes_verbs.h |    7 
>  8 files changed, 2388 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/infiniband/hw/nes/nes_ud.c
>  create mode 100644 drivers/infiniband/hw/nes/nes_ud.h
>
>
> diff --git a/drivers/infiniband/hw/nes/Makefile b/drivers/infiniband/hw/nes/Makefile
> index 3514851..850df8e 100644
> --- a/drivers/infiniband/hw/nes/Makefile
> +++ b/drivers/infiniband/hw/nes/Makefile
> @@ -1,3 +1,3 @@
>  obj-$(CONFIG_INFINIBAND_NES) += iw_nes.o
>  
> -iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o
> +iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o nes_ud.o
> diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
> index de7b9d7..e430804 100644
> --- a/drivers/infiniband/hw/nes/nes.c
> +++ b/drivers/infiniband/hw/nes/nes.c
> @@ -60,6 +60,8 @@
>  #include <linux/route.h>
>  #include <net/ip_fib.h>
>  
> +#include "nes_ud.h"
> +
>  MODULE_AUTHOR("NetEffect");
>  MODULE_DESCRIPTION("NetEffect RNIC Low-level iWARP Driver");
>  MODULE_LICENSE("Dual BSD/GPL");
> @@ -1205,6 +1207,7 @@ static int __init nes_init_module(void)
>  		if (retval1 < 0)
>  			printk(KERN_ERR PFX "Unable to create NetEffect sys files.\n");
>  	}
> +	nes_ud_init();
>  	return retval;
>  }
>  
> @@ -1214,6 +1217,7 @@ static int __init nes_init_module(void)
>   */
>  static void __exit nes_exit_module(void)
>  {
> +	nes_ud_exit();
>  	nes_cm_stop();
>  	nes_remove_driver_sysfs(&nes_pci_driver);
>  
> diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
> index cc78fee..faf420f 100644
> --- a/drivers/infiniband/hw/nes/nes.h
> +++ b/drivers/infiniband/hw/nes/nes.h
> @@ -102,6 +102,7 @@
>  #define NES_DRV_OPT_NO_INLINE_DATA       0x00000080
>  #define NES_DRV_OPT_DISABLE_INT_MOD      0x00000100
>  #define NES_DRV_OPT_DISABLE_VIRT_WQ      0x00000200
> +#define NES_DRV_OPT_MCAST_LOGPORT_MAP    0x00000800
>  
>  #define NES_AEQ_EVENT_TIMEOUT         2500
>  #define NES_DISCONNECT_EVENT_TIMEOUT  2000
> @@ -128,6 +129,7 @@
>  #define NES_DBG_IW_RX       0x00020000
>  #define NES_DBG_IW_TX       0x00040000
>  #define NES_DBG_SHUTDOWN    0x00080000
> +#define NES_DBG_UD          0x00100000
>  #define NES_DBG_RSVD1       0x10000000
>  #define NES_DBG_RSVD2       0x20000000
>  #define NES_DBG_RSVD3       0x40000000
> diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
> index b7c813f..c7bbb83 100644
> --- a/drivers/infiniband/hw/nes/nes_nic.c
> +++ b/drivers/infiniband/hw/nes/nes_nic.c
> @@ -897,7 +897,7 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev)
>  			((mc_nic_index = nesvnic->mcrq_mcast_filter(nesvnic,
>  					get_addr(addrs, i++))) == 0));
>  			if (mc_nic_index < 0)
> -				mc_nic_index = nesvnic->nic_index;
> +				mc_nic_index = (1 << nesvnic->nic_index);
>  			while (nesadapter->pft_mcast_map[mc_index] < 16 &&
>  				nesadapter->pft_mcast_map[mc_index] !=
>  					nesvnic->nic_index &&
> @@ -930,7 +930,7 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev)
>  				nes_write_indexed(nesdev,
>  						perfect_filter_register_address+4+(mc_index * 8),
>  						(u32)macaddr_high | NES_MAC_ADDR_VALID |
> -						((((u32)(1<<mc_nic_index)) << 16)));
> +					((((u32)(mc_nic_index)) << 16)));
>  				nesadapter->pft_mcast_map[mc_index] =
>  							nesvnic->nic_index;
>  			} else {
> @@ -1676,8 +1676,11 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
>  			(nesvnic->nesdev->nesadapter->port_count == 1 &&
>  			nesvnic->nesdev->nesadapter->adapter_fcn_count == 2)) {
>  				nesvnic->qp_nic_index[0] = nesvnic->nic_index;
> -				nesvnic->qp_nic_index[1] = nesvnic->nic_index
> -									+ 2;
> +
> +			if (nes_drv_opt & NES_DRV_OPT_MCAST_LOGPORT_MAP)
> +				nesvnic->qp_nic_index[1] = 0xf;
> +			else
> +				nesvnic->qp_nic_index[1] = nesvnic->nic_index+2;
>  				nesvnic->qp_nic_index[2] = 0xf;
>  				nesvnic->qp_nic_index[3] = 0xf;
>  		} else {
> diff --git a/drivers/infiniband/hw/nes/nes_ud.c b/drivers/infiniband/hw/nes/nes_ud.c
> new file mode 100644
> index 0000000..f004855
> --- /dev/null
> +++ b/drivers/infiniband/hw/nes/nes_ud.c
> @@ -0,0 +1,2070 @@
> +/*
> + * Copyright (c) 2008 - 2010 Intel Corporation.  All rights reserved.
> + * Copyright (c) 2006 - 2008 Neteffect, All rights reserved.
> + * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses.  You may choose to be licensed 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, or the
> + * OpenIB.org BSD license below:
> + *
> + *     Redistribution and use in source and binary forms, with or
> + *     without modification, are permitted provided that the following
> + *     conditions are met:
> + *
> + *      - Redistributions of source code must retain the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer.
> + *
> + *      - Redistributions in binary form must reproduce the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer in the documentation and/or other materials
> + *        provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/completion.h>
> +#include <linux/mutex.h>
> +#include <linux/poll.h>
> +#include <linux/idr.h>
> +#include <linux/in.h>
> +#include <linux/in6.h>
> +#include <linux/device.h>
> +#include <linux/netdevice.h>
> +#include <linux/list.h>
> +#include <linux/miscdevice.h>
> +#include <linux/device.h>
> +
> +#include <rdma/ib_umem.h>
> +#include <rdma/ib_user_verbs.h>
> +
> +#include "nes.h"
> +#include "nes_ud.h"
> +
> +#define NES_UD_BASE_XMIT_NIC_QPID  28
> +#define NES_UD_BASE_RECV_NIC_IDX   12
> +#define NES_UD_BASE_XMIT_NIC_IDX   8
> +#define NES_UD_MAX_NIC_CNT 8
> +#define NES_UD_CLEANUP_TIMEOUT (HZ)
> +#define NES_UD_MCAST_TBL_SZ 128
> +#define NES_UD_SINGLE_HDR_SZ 64
> +#define NES_UD_CQE_NUM NES_NIC_WQ_SIZE
> +#define NES_UD_SKSQ_WAIT_TIMEOUT	100000
> +#define NES_UD_MAX_REG_CNT 128
> +
> +#define NES_UD_MAX_ADAPTERS 4 /* number of supported interfaces for  RAW ETH */
> +
> +#define NES_UD_MAX_REG_HASH_CNT 256 /* last byte of the STAG is hash key */
> +
> +/*
> + * the same multicast could be allocated up to 2 owners so there could be
> + *  two differentmcast entries allocated for the same mcas address
> + */
> +struct nes_ud_file;
> +struct nes_ud_mcast {
> +	u8 addr[3];
> +	u8 in_use;
> +	struct nes_ud_file *owner;
> +	u8 nic_mask;
> +};
> +
> +struct nes_ud_mem_region {
> +	struct list_head list;
> +	dma_addr_t *addrs;
> +	u64 va;
> +	u64 length;
> +	u32 pg_cnt;
> +	u32 in_use;
> +	u32 stag; /* stag related this structure */
> +};
> +
> +struct nic_queue_info {
> +	u32 qpn;
> +	u32 nic_index;
> +	u32 logical_port;
> +	enum nes_ud_dev_priority prio;
> +	enum nes_ud_queue_type queue_type;
> +	struct nes_ud_file *file;
> +	struct nes_ud_file file_body;
> +};
> +
> +struct nes_ud_resources {
> +	int num_logport_confed;
> +	int num_allocated_nics;
> +	u8 logport_2_map;
> +	u8 logport_3_map;
> +	u32 original_6000;
> +	u32 original_60b8;
> +	struct nic_queue_info nics[NES_UD_MAX_NIC_CNT];
> +	struct mutex          mutex;
> +	struct nes_ud_mcast mcast[NES_UD_MCAST_TBL_SZ];
> +	u32 adapter_no;  /* the allocated adapter no */
> +
> +	/* the unique ID of the NE020 adapter */
> +	/*- it is allocated once per HW */
> +	struct nes_adapter *pAdap;
> +};
> +
> +/* memory hash list entry */
> +struct nes_ud_hash_mem {
> +    struct list_head   list;
> +    int                read_stats;
> +};
> +
> +
> +
> +struct nes_ud_mem {
> +	/* hash list of registered STAGs */
> +	struct nes_ud_hash_mem mrs[NES_UD_MAX_REG_HASH_CNT];
> +	struct mutex          mutex;
> +};
> +
> +/* the QP in format x.y.z where x is adapter no, */
> +/* y is ud file idx in adapter, z is a qp no */
> +static struct nes_ud_mem ud_mem;
> +
> +struct nes_ud_send_wr {
> +    u32               wr_cnt;
> +    u32               qpn;
> +    u32               flags;
> +    u32               resv[1];
> +    struct ib_sge     sg_list[64];
> +};
> +
> +struct nes_ud_recv_wr {
> +    u32               wr_cnt;
> +    u32               qpn;
> +    u32	              resv[2];
> +    struct ib_sge     sg_list[64];
> +};
> +
> +static struct nes_ud_resources nes_ud_rsc[NES_UD_MAX_ADAPTERS];
> +static struct workqueue_struct *nes_ud_workqueue;
> +
> +/*
> + * locate_ud_adapter
> + *
> + * the function locates the UD adapter
> +*  on base of the adapter unique ID (structure nes_adapter)
> + */
> +static inline
> +struct nes_ud_resources *locate_ud_adapter(struct nes_adapter *pAdapt)
> +{
> +	int i;
> +	struct nes_ud_resources *pRsc;
> +
> +	for (i = 0; i < NES_UD_MAX_ADAPTERS; i++) {
> +		pRsc = &nes_ud_rsc[i];
> +
> +		if (pRsc->pAdap == pAdapt)
> +			return pRsc;
> +
> +	}
> +	return NULL;
> +}
> +
> +/*
> + * allocate_ud_adapter()
> + *
> + * function allocates a new adapter
> + */
> +static inline
> +struct nes_ud_resources *allocate_ud_adapter(struct nes_adapter *pAdapt)
> +{
> +    int i;
> +    struct nes_ud_resources *pRsc;
> +
> +    for (i = 0; i < NES_UD_MAX_ADAPTERS; i++) {
> +	pRsc = &nes_ud_rsc[i];
> +	if (pRsc->pAdap == NULL) {
> +		pRsc->pAdap = pAdapt;
> +		nes_debug(NES_DBG_UD, "new UD Adapter allocated %d"
> +		" for adapter %p no =%d\n", i, pAdapt, pRsc->adapter_no);
> +		return pRsc;
> +		}
> +	}
> +	nes_debug(NES_DBG_UD, "Unable to allocate adapter\n");
> +	return NULL;
> +}
> +
> +static inline
> +struct nes_ud_file *allocate_nic_queue(struct nes_vnic *nesvnic,
> +					enum nes_ud_queue_type queue_type)
> +{
> +    struct nes_ud_file *file = NULL;
> +	int i = 0;
> +	u8 select_log_port = 0xf;
> +	struct nes_device *nesdev = nesvnic->nesdev;
> +	int log_port_2_alloced = 0;
> +	int log_port_3_alloced = 0;
> +	int ret = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	/* the first thing that must be done is determine the adapter */
> +	/* number max the adapter could have up to 2 interfaces */
> +	if (nesvnic->nic_index != 0 && nesvnic->nic_index != 1) {
> +		nes_debug(NES_DBG_UD, "nic queue allocation failed"
> +		" nesvnic->nic_index = %d\n", nesvnic->nic_index);
> +		return NULL;
> +	}
> +
> +	/* locate device on base of nesvnic */
> +	/* - when it is an unknown card a new one is allocated */
> +	pRsc = locate_ud_adapter(nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return NULL;
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active == 0)
> +			continue;
> +		if (pRsc->nics[i].logical_port == 2 &&
> +		    queue_type == pRsc->nics[i].queue_type)
> +				log_port_2_alloced++;
> +		if (pRsc->nics[i].logical_port == 3 &&
> +		    queue_type == pRsc->nics[i].queue_type)
> +				log_port_3_alloced++;
> +	}
> +
> +	/* check dual/single card */
> +	if (pRsc->logport_2_map != pRsc->logport_3_map) {
> +		/* a dual port card */
> +		/* allocation is NIC2, NIC2, NIC3, NIC3 */
> +		/*- no RX packat replication supported */
> +		if (log_port_2_alloced < 2 &&
> +		    pRsc->logport_2_map == nesvnic->nic_index)
> +				select_log_port = 2;
> +		else if (log_port_3_alloced < 2 &&
> +			 pRsc->logport_3_map == nesvnic->nic_index)
> +				select_log_port = 3;
> +	} else {
> +		/* single port card */
> +		/* change allocation scheme to NIC2,NIC3,NIC2,NIC3 */
> +		switch (log_port_2_alloced + log_port_3_alloced) {
> +		case 0: /* no QPs allocated - use NIC2 */
> +			if (pRsc->logport_2_map == nesvnic->nic_index)
> +				select_log_port = 2;
> +
> +			break;
> +		case 1: /* NIC2 or NIC3 allocated */
> +			if (log_port_2_alloced > 0) {
> +				/* if NIC2 allocated use NIC3 */
> +				if (pRsc->logport_3_map == nesvnic->nic_index)
> +					select_log_port = 3;
> +
> +			} else {
> +				/* when NIC3 allocated use NIC2 */
> +				if (pRsc->logport_2_map == nesvnic->nic_index)
> +					select_log_port = 2;
> +
> +			}
> +			break;
> +
> +		case 2:
> +		/* NIC2 and NIC3 allocated or both ports on NIC3 - use NIC2 */
> +			if ((log_port_2_alloced == 1) ||
> +			    (log_port_3_alloced == 2)) {
> +				if (pRsc->logport_2_map == nesvnic->nic_index)
> +					select_log_port = 2;
> +
> +			} else {
> +				/* both ports allocated on NIC2 - use NIC3 */
> +				if (pRsc->logport_3_map == nesvnic->nic_index)
> +					select_log_port = 3;
> +
> +			}
> +				break;
> +		case 3:
> +			/* when both NIC2 allocated use NIC3 */
> +			if (log_port_2_alloced == 2) {
> +				if (pRsc->logport_3_map == nesvnic->nic_index)
> +					select_log_port = 3;
> +
> +			} else {
> +				/* when both NIC3 alloced use NIC2 */
> +				if (pRsc->logport_2_map == nesvnic->nic_index)
> +					select_log_port = 2;
> +			}
> +			break;
> +
> +		default:
> +			break;
> +		}
> +	}
> +	if (select_log_port == 0xf) {
> +		ret = -1;
> +		nes_debug(NES_DBG_UD, "%s(%d) logport allocation failed "
> +		"log_port_2_alloced=%d log_port_3_alloced=%d\n",
> +			__func__, __LINE__, log_port_2_alloced,
> +			log_port_3_alloced);
> +		goto out;
> +	}
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) log_port_2_alloced=%d "
> +		"log_port_3_alloced=%d select_log_port=%d\n",
> +		__func__, __LINE__, log_port_2_alloced,
> +		log_port_3_alloced, select_log_port);
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active == 1)
> +			continue;
> +		if (pRsc->nics[i].logical_port == select_log_port &&
> +		    queue_type == pRsc->nics[i].queue_type) {
> +
> +			/* file is preallocated during initialization */
> +			file = pRsc->nics[i].file;
> +			memset(file, 0, sizeof(*file));
> +
> +			file->nesvnic = nesvnic;
> +			file->queue_type = queue_type;
> +
> +			file->prio = pRsc->nics[i].prio;
> +			file->qpn = pRsc->nics[i].qpn;
> +			file->nes_ud_nic_index = pRsc->nics[i].nic_index;
> +			file->rsc_idx = i;
> +			file->adapter_no = pRsc->adapter_no;
> +			goto out;
> +		}
> +	}
> +
> +out:
> +	return file;
> +}
> +
> +static inline int  del_rsc_list(struct nes_ud_file *file)
> +{
> +	int logport_2_cnt = 0;
> +	int logport_3_cnt = 0;
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	int i = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	if (file == NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) file is NULL\n",
> +			__func__, __LINE__);
> +		return -EFAULT;
> +	}
> +	if (file->nesvnic == NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) file->nesvnic is NULL\n",
> +			__func__, __LINE__);
> +		return -EFAULT;
> +	}
> +	if (nesdev == NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) nesdev is NULL\n",
> +			__func__, __LINE__);
> +		return -EFAULT;
> +	}
> +
> +	/* locate device on base of nesvnic */
> +	/*- when it is an unknown card a new one is allocated */
> +	pRsc = locate_ud_adapter(nesdev->nesadapter);
> +	if (pRsc == NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) cannot locate an allocated "
> +			"adapter  is NULL\n", __func__, __LINE__);
> +		return -EFAULT;
> +	}
> +	if (--pRsc->num_allocated_nics == 0) {
> +		nes_write_indexed(nesdev, 0x60b8, pRsc->original_60b8);
> +		nes_write_indexed(nesdev, 0x6000, pRsc->original_6000);
> +		pRsc->num_logport_confed = 0;
> +	}
> +	BUG_ON(pRsc->num_allocated_nics < 0);
> +	BUG_ON(file->rsc_idx >= NES_UD_MAX_NIC_CNT);
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active &&
> +		    pRsc->nics[i].logical_port == 2)
> +			logport_2_cnt++;
> +		if (pRsc->nics[i].file->active &&
> +		    pRsc->nics[i].logical_port == 3)
> +			logport_3_cnt++;
> +	}
> +
> +	if (pRsc->num_logport_confed != 0x3 && logport_2_cnt == 0)
> +		pRsc->logport_2_map = 0xf;
> +
> +	if (pRsc->num_logport_confed != 0x3 && logport_3_cnt == 0)
> +		pRsc->logport_3_map = 0xf;
> +	return 0;
> +}
> +
> +/*
> +* the QPN contains now the number of the RAW ETH
> +* adapter and QPN number on the adapter
> +* the adapter number is located in the highier
> +* 8 bits so QPN is stored as [adapter:qpn]
> +*/
> +static inline
> +struct nes_ud_file *get_file_by_qpn(struct nes_ud_resources *pRsc, int qpn)
> +{
> +	int i = 0;
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active &&
> +		    pRsc->nics[i].qpn == (qpn & 0xff))
> +			return pRsc->nics[i].file;
> +
> +	}
> +	return NULL;
> +}
> +
> +/* function counts all ETH RAW entities that have  */
> +/* a specific type and relation to specific vnic */
> +static inline
> +int count_files_by_nic(struct nes_vnic *nesvnic,
> +			enum nes_ud_queue_type queue_type)
> +{
> +	int count = 0;
> +	int i = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return 0;
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active &&
> +		    pRsc->nics[i].file->nesvnic == nesvnic &&
> +		    pRsc->nics[i].queue_type == queue_type)
> +				count++;
> +	}
> +	return count;
> +}
> +
> +/* function counts all RAW ETH  entities the have a specific type */
> +static inline
> +int count_files(struct nes_vnic *nesvnic, enum nes_ud_queue_type queue_type)
> +{
> +	int count = 0;
> +	int i = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return 0;
> +
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active &&
> +		    pRsc->nics[i].queue_type == queue_type)
> +			count++;
> +	}
> +	return count;
> +}
> +
> +/*
> + * the function locates the entry allocated by IGMP and modifies the
> + * PFT entry with the list of the NICs allowed to receive that multicast
> + * the NIC0/NIC1 are removed due to performance issue so tcpdum
> + * like tools cannot receive the accelerated multicasts
> + */
> +static void mcast_fix_filter_table_single(struct nes_ud_file *file, u8 *addr)
> +{
> +  struct nes_device *nesdev = file->nesvnic->nesdev;
> +  int i = 0;
> +  u32 macaddr_low;
> +  u32 orig_low;
> +  u32 macaddr_high;
> +  u32 prev_high;
> +
> +  for (i = 0; i < 48; i++) {
> +	macaddr_low = nes_read_indexed(nesdev,
> +					NES_IDX_PERFECT_FILTER_LOW + i*8);
> +	orig_low = macaddr_low;
> +	macaddr_high = nes_read_indexed(nesdev,
> +					NES_IDX_PERFECT_FILTER_LOW + 4 + i*8);
> +	if (!(macaddr_high & NES_MAC_ADDR_VALID))
> +		continue;
> +	if ((macaddr_high & 0xffff) != 0x0100)
> +		continue;
> +	if ((macaddr_low & 0xff) != addr[2])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != addr[1])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != addr[0])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != 0x5e)
> +		continue;
> +	/* hit - that means Linux or other UD set this bit earlier  */
> +	prev_high = macaddr_high;
> +	nes_write_indexed(nesdev, NES_IDX_PERFECT_FILTER_LOW + 4 + i*8, 0);
> +	macaddr_high = (macaddr_high & 0xfffcffff) |
> +					((1<<file->nes_ud_nic_index) << 16);
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) found addr to fix, "
> +		 "i=%d, macaddr_high=0x%x  macaddr_low=0x%x "
> +		 "nic_idx=%d prev_high=0x%x\n",
> +		 __func__, __LINE__, i, macaddr_high, orig_low,
> +		file->nes_ud_nic_index, prev_high);
> +	nes_write_indexed(nesdev,
> +			NES_IDX_PERFECT_FILTER_LOW + 4 + i*8, macaddr_high);
> +	break;
> +  }
> +}
> +
> +/* this function is implemented that way because the Linux multicast API
> +   use the multicast list approach. When a new multicast address is added
> +   all PFT table is reinitialized by linux and all entries must be fixed
> +   by this procedure
> +*/
> +static void mcast_fix_filter_table(struct nes_ud_file *file)
> +{
> +	int i;
> +	struct nes_ud_resources *pRsc;
> +
> +	pRsc = locate_ud_adapter(file->nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return;
> +
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (pRsc->mcast[i].in_use != 0)
> +			mcast_fix_filter_table_single(pRsc->mcast[i].owner,
> +							pRsc->mcast[i].addr);
> +	}
> +}
> +
> +/* function invalidates the PFT entry */
> +static void remove_mcast_from_pft(struct nes_ud_file *file, u8 *addr)
> +{
> +  struct nes_device *nesdev = file->nesvnic->nesdev;
> +  int i = 0;
> +   u32 macaddr_low;
> +  u32 orig_low;
> +  u32 macaddr_high;
> +  u32 prev_high;
> +
> +  for (i = 0; i < 48; i++) {
> +	macaddr_low = nes_read_indexed(nesdev,
> +					NES_IDX_PERFECT_FILTER_LOW + i*8);
> +	orig_low = macaddr_low;
> +	macaddr_high = nes_read_indexed(nesdev,
> +					NES_IDX_PERFECT_FILTER_LOW + 4 + i*8);
> +	if (!(macaddr_high & NES_MAC_ADDR_VALID))
> +		continue;
> +
> +	if ((macaddr_high & 0xffff) != 0x0100)
> +		continue;
> +	if ((macaddr_low & 0xff) != addr[2])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != addr[1])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != addr[0])
> +		continue;
> +	macaddr_low >>= 8;
> +	if ((macaddr_low & 0xff) != 0x5e)
> +		continue;
> +	/* hit - that means Linux or other UD set this bit earlier */
> +	/* so remove the NIC from MAC address reception */
> +	prev_high = macaddr_high;
> +	macaddr_high = (macaddr_high & 0xfffcffff) &
> +					~((1<<file->nes_ud_nic_index) << 16);
> +	nes_debug(NES_DBG_UD, "%s(%d) found addr to mcast remove,"
> +		"i=%d, macaddr_high=0x%x  macaddr_low=0x%x "
> +		"nic_idx=%d prev_high=0x%x\n", __func__, __LINE__, i,
> +		macaddr_high, orig_low, file->nes_ud_nic_index, prev_high);
> +	nes_write_indexed(nesdev, NES_IDX_PERFECT_FILTER_LOW + 4 + i*8,
> +							macaddr_high);
> +	break;
> +	}
> +
> +}
> +
> +/*
> +* the function returns a mask of the NICs
> +* assotiated with given multicast address
> +*/
> +static int nes_ud_mcast_filter(struct nes_vnic *nesvnic, __u8 *dmi_addr)
> +{
> +	int i = 0;
> +	int ret = 0;
> +	int mask = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return 0;
> +
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (pRsc->mcast[i].in_use &&
> +		    pRsc->mcast[i].addr[0] == dmi_addr[3] &&
> +		    pRsc->mcast[i].addr[1] == dmi_addr[4] &&
> +		    pRsc->mcast[i].addr[2] == dmi_addr[5]) {
> +			mask = (pRsc->mcast[i].owner->mcast_mode ==
> +				NES_UD_MCAST_PFT_MODE) ?
> +				pRsc->mcast[i].owner->nes_ud_nic_index : 0;
> +
> +			ret = ret | (1 << mask);
> +			nes_debug(NES_DBG_UD, "mcast filter, "
> +				"fpr=%02X%02X%02X ret=%d\n",
> +				dmi_addr[3], dmi_addr[4], dmi_addr[5], ret);
> +		}
> +	}
> +	if (ret == 0)
> +		return -1;
> +	else
> +		return ret;
> +
> +}
> +
> +static __u32 mqueue_key[4] = { 0x0, 0x80, 0x0, 0x0 };
> +
> +static inline __u8 nes_ud_calculate_hash(__u8 dest_addr_lsb)
> +{
> +  __u8 in[8];
> +  __u32 key_arr[4];
> +  int i;
> +  __u32 result = 0;
> +  int j, k;
> +  __u8 shift_in, next_shift_in;
> +
> +  in[0] = 0;
> +  in[1] = 0;
> +  in[2] = 0;
> +  in[3] = 0;
> +
> +  in[4] = 0;
> +
> +  in[5] = 0;
> +  in[6] = 0;
> +  in[7] = dest_addr_lsb;
> +
> +
> +
> +	for (i = 0; i < 4; i++)
> +		key_arr[3-i] = mqueue_key[i];
> +
> +
> +
> +	for (i = 0; i < 8; i++) {
> +		for (j = 7; j >= 0; j--) {
> +			if (in[i] & (1 << j))
> +				result = result ^ key_arr[0];
> +
> +			shift_in = 0;
> +			for (k = 3; k >= 0; k--) {
> +				next_shift_in = key_arr[k] >> 31;
> +				key_arr[k] = (key_arr[k] << 1) + shift_in;
> +				shift_in = next_shift_in;
> +			}
> +		}
> +	}
> +	return result & 0x7f;
> +}
> +
> +static inline void nes_ud_enable_mqueue(struct nes_ud_file *file)
> +{
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	int mqueue_config0;
> +	int mqueue_config2;
> +	int instance = file->nes_ud_nic_index & 0x1;
> +
> +	mqueue_config0 = nes_read_indexed(nesdev, 0x6400);
> +	mqueue_config0 |= (4 | (instance & 0x3)) << (file->nes_ud_nic_index*3);
> +	nes_write_indexed(nesdev, 0x6400, mqueue_config0);
> +	mqueue_config0 = nes_read_indexed(nesdev, 0x6400);
> +
> +	mqueue_config2 = nes_read_indexed(nesdev, 0x6408);
> +	mqueue_config2 |= (2 << (instance*2)) | (6 << (instance*3+8));
> +	nes_write_indexed(nesdev, 0x6408, mqueue_config2);
> +	mqueue_config2 = nes_read_indexed(nesdev, 0x6408);
> +
> +	nes_write_indexed(nesdev, 0x64a0+instance*0x100, mqueue_key[0]);
> +	nes_write_indexed(nesdev, 0x64a4+instance*0x100, mqueue_key[1]);
> +	nes_write_indexed(nesdev, 0x64a8+instance*0x100, mqueue_key[2]);
> +	nes_write_indexed(nesdev, 0x64ac+instance*0x100, mqueue_key[3]);
> +
> +	nes_debug(NES_DBG_UD, "mq_config0=0x%x mq_config2=0x%x nic_idx= %d\n",
> +		  mqueue_config0, mqueue_config2, file->nes_ud_nic_index);
> +
> +}
> +
> +
> +
> +static inline
> +void nes_ud_redirect_from_mqueue(struct nes_ud_file *file, int num_queues)
> +{
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	int instance = file->nes_ud_nic_index & 0x1;
> +	unsigned addr = 0x6420+instance*0x100;
> +	unsigned value;
> +	int i;
> +
> +	value  = (file->prio == NES_UD_DEV_PRIO_LOW || num_queues == 1) ?
> +							0x0 : 0x11111111;
> +	for (i = 0; i < 16; i++)
> +		nes_write_indexed(nesdev, addr+i*4, value);
> +}
> +
> +
> +static int nes_ud_create_nic(struct nes_ud_file *file)
> +{
> +	struct nes_vnic *nesvnic = file->nesvnic;
> +	struct nes_device *nesdev = nesvnic->nesdev;
> +	struct nes_hw_nic_qp_context *nic_context;
> +	struct nes_hw_cqp_wqe *cqp_wqe;
> +	struct nes_cqp_request *cqp_request;
> +	unsigned long flags;
> +	void *vmem;
> +	dma_addr_t pmem;
> +	u64 u64temp;
> +	int ret = 0;
> +
> +	BUG_ON(file->nic_vbase != NULL);
> +
> +	file->nic_mem_size = 256 +
> +			(NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe)) +
> +			sizeof(struct nes_hw_nic_qp_context);
> +
> +	file->nic_vbase = pci_alloc_consistent(nesdev->pcidev,
> +						file->nic_mem_size,
> +						&file->nic_pbase);
> +	if (!file->nic_vbase) {
> +		nes_debug(NES_DBG_UD, "Unable to allocate memory for NIC host "
> +			"descriptor rings\n");
> +		return -ENOMEM;
> +	}
> +
> +	memset(file->nic_vbase, 0, file->nic_mem_size);
> +
> +	vmem = (void *)(((unsigned long long)file->nic_vbase + (256 - 1)) &
> +			~(unsigned long long)(256 - 1));
> +	pmem = (dma_addr_t)(((unsigned long long)file->nic_pbase + (256 - 1)) &
> +			~(unsigned long long)(256 - 1));
> +
> +	file->wq_vbase = vmem;
> +	file->wq_pbase = pmem;
> +	file->head = 0;
> +	file->tail = 0;
> +
> +	vmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe));
> +	pmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe));
> +
> +	cqp_request = nesvnic->get_cqp_request(nesdev);
> +	if (cqp_request == NULL) {
> +		nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n");
> +		goto fail_cqp_req_alloc;
> +	}
> +	cqp_request->waiting = 1;
> +	cqp_wqe = &cqp_request->cqp_wqe;
> +
> +	cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
> +			cpu_to_le32(NES_CQP_CREATE_QP | NES_CQP_QP_TYPE_NIC);
> +	cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(file->qpn);
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX] =
> +			cpu_to_le32((u32)((u64)(&nesdev->cqp)));
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] =
> +			cpu_to_le32((u32)(((u64)(&nesdev->cqp))>>32));
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0;
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
> +
> +
> +	nic_context = vmem;
> +
> +	nic_context->context_words[NES_NIC_CTX_MISC_IDX] =
> +			cpu_to_le32((u32)NES_NIC_CTX_SIZE |
> +			((u32)PCI_FUNC(nesdev->pcidev->devfn) << 12) |
> +			(1 << 18));
> +
> +	nic_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] = 0;
> +	nic_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] = 0;
> +	nic_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] = 0;
> +	nic_context->context_words[NES_NIC_CTX_RQ_HIGH_IDX] = 0;
> +
> +	u64temp = (u64)file->wq_pbase;
> +	if (file->queue_type == NES_UD_SEND_QUEUE) {
> +		nic_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] =
> +						cpu_to_le32((u32)u64temp);
> +		nic_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] =
> +					cpu_to_le32((u32)(u64temp >> 32));
> +	} else {
> +		nic_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] =
> +						cpu_to_le32((u32)u64temp);
> +		nic_context->context_words[NES_NIC_CTX_RQ_HIGH_IDX] =
> +					cpu_to_le32((u32)(u64temp >> 32));
> +	}
> +
> +	u64temp = (u64)pmem;
> +
> +	cqp_wqe->wqe_words[NES_CQP_QP_WQE_CONTEXT_LOW_IDX] =
> +						cpu_to_le32((u32)u64temp);
> +	cqp_wqe->wqe_words[NES_CQP_QP_WQE_CONTEXT_HIGH_IDX] =
> +					cpu_to_le32((u32)(u64temp >> 32));
> +
> +	atomic_set(&cqp_request->refcount, 2);
> +	nesvnic->post_cqp_request(nesdev, cqp_request);
> +
> +	/* Wait for CQP */
> +	ret = wait_event_timeout(cqp_request->waitq,
> +				(cqp_request->request_done != 0),
> +				NES_EVENT_TIMEOUT);
> +	if (!ret)
> +		nes_debug(NES_DBG_UD, "NES_UD NIC QP%u "
> +			"create timeout expired\n", file->qpn);
> +
> +
> +	if (atomic_dec_and_test(&cqp_request->refcount)) {
> +		if (cqp_request->dynamic) {
> +			kfree(cqp_request);
> +		} else {
> +			spin_lock_irqsave(&nesdev->cqp.lock, flags);
> +			list_add_tail(&cqp_request->list,
> +						&nesdev->cqp_avail_reqs);
> +			spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
> +		}
> +	}
> +	nes_debug(NES_DBG_UD, "Created NIC, qpn=%d, SQ/RQ pa=0x%p va=%p "
> +		"virt_to_phys=%p\n", file->qpn,
> +		(void *)file->wq_pbase, (void *)file->nic_vbase,
> +		(void *)virt_to_phys(file->nic_vbase));
> +	return ret;
> +
> + fail_cqp_req_alloc:
> +	pci_free_consistent(nesdev->pcidev, file->nic_mem_size, file->nic_vbase,
> +			file->nic_pbase);
> +	file->nic_vbase = NULL;
> +	return -EFAULT;
> +}
> +
> +
> +static void nes_ud_destroy_nic(struct nes_ud_file *file)
> +{
> +	struct nes_vnic *nesvnic = file->nesvnic;
> +	struct nes_device *nesdev = nesvnic->nesdev;
> +	struct nes_hw_cqp_wqe *cqp_wqe;
> +	struct nes_cqp_request *cqp_request;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	cqp_request = nesvnic->get_cqp_request(nesdev);
> +	if (cqp_request == NULL) {
> +		nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n");
> +		return;
> +	}
> +	cqp_request->waiting = 1;
> +	cqp_wqe = &cqp_request->cqp_wqe;
> +
> +	cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
> +			cpu_to_le32(NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_NIC);
> +	cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(file->qpn);
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX] =
> +			cpu_to_le32((u32)((u64)(&nesdev->cqp)));
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] =
> +			cpu_to_le32((u32)(((u64)(&nesdev->cqp)) >> 32));
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0;
> +	cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
> +
> +	atomic_set(&cqp_request->refcount, 2);
> +	nesvnic->post_cqp_request(nesdev, cqp_request);
> +
> +	/* Wait for CQP */
> +	ret = wait_event_timeout(cqp_request->waitq,
> +			(cqp_request->request_done != 0),
> +			NES_EVENT_TIMEOUT);
> +	if (!ret)
> +		nes_debug(NES_DBG_UD, "NES_UD NIC QP%u "
> +			"destroy timeout expired\n", file->qpn);
> +
> +	if (atomic_dec_and_test(&cqp_request->refcount)) {
> +		if (cqp_request->dynamic) {
> +			kfree(cqp_request);
> +		} else {
> +			spin_lock_irqsave(&nesdev->cqp.lock, flags);
> +			list_add_tail(&cqp_request->list,
> +					&nesdev->cqp_avail_reqs);
> +			spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
> +		}
> +	}
> +
> +	pci_free_consistent(nesdev->pcidev, file->nic_mem_size, file->nic_vbase,
> +			file->nic_pbase);
> +	file->nic_vbase = NULL;
> +	file->qp_ptr = NULL;
> +
> +	return;
> +}
> +
> +static void nes_ud_free_resources(struct nes_ud_file *file)
> +{
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	int nic_active = 0;
> +	int mcast_all = 0;
> +	int mcast_en = 0;
> +	int wqm_config0 = 0;
> +	wait_queue_head_t     waitq;
> +	int num_queues  = 0;
> +	nes_debug(NES_DBG_UD, " %s(%d) NAME=%s nes_ud_qpid=%d\n",
> +		__func__, __LINE__, file->ifrn_name, file->qpn);
> +
> +	if (!file->nesvnic || !file->active)
> +		return;
> +
> +	if (file->queue_type == NES_UD_SEND_QUEUE) {
> +		nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +		nic_active &= ~(1 << file->nes_ud_nic_index);
> +		nes_write_indexed(nesdev, NES_IDX_NIC_ACTIVE, nic_active);
> +		nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +	} else {
> +		num_queues = count_files_by_nic(file->nesvnic,
> +						file->queue_type);
> +
> +	if (num_queues == 1) {
> +
> +		nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +		nic_active &= ~(1 << file->nes_ud_nic_index);
> +		nes_write_indexed(nesdev, NES_IDX_NIC_ACTIVE, nic_active);
> +		nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +
> +		mcast_all = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
> +		mcast_all &= ~(1 << file->nes_ud_nic_index);
> +		nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, mcast_all);
> +		mcast_all = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
> +
> +		mcast_en = nes_read_indexed(nesdev,
> +						NES_IDX_NIC_MULTICAST_ENABLE);
> +		mcast_en &= ~(1 << file->nes_ud_nic_index);
> +		nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE,
> +								mcast_en);
> +		mcast_en = nes_read_indexed(nesdev,
> +						NES_IDX_NIC_MULTICAST_ENABLE);
> +
> +		nes_debug(NES_DBG_UD, "nic_active=0x%x, mcast_en=0x%x, "
> +			"mcast_all=0x%x nic_index=%d num_queues=%d\n",
> +			nic_active, mcast_en, mcast_all,
> +			file->nes_ud_nic_index, num_queues);
> +	  }
> +
> +	 nes_ud_redirect_from_mqueue(file, num_queues);
> +	 num_queues = count_files(file->nesvnic, file->queue_type);
> +	if (num_queues == 1) {
> +		nes_debug(NES_DBG_UD, "Last receive queue, "
> +			"restoring MPP debug register\n");
> +		nes_write_indexed(nesdev, 0xA00, 0x200);
> +		nes_write_indexed(nesdev, 0xA40, 0x200);
> +	  }
> +	}
> +
> +
> +
> +	nes_ud_destroy_nic(file);
> +
> +	if (file->queue_type == NES_UD_RECV_QUEUE) {
> +		wqm_config0 = nes_read_indexed(nesdev, 0x5000);
> +		wqm_config0 &= ~0x8000;
> +		nes_write_indexed(nesdev, 0x5000, wqm_config0);
> +
> +		init_waitqueue_head(&waitq);
> +
> +		wait_event_timeout(waitq, 0, NES_UD_CLEANUP_TIMEOUT);
> +
> +		nes_debug(NES_DBG_UD, "%s(%d) enabling stall_no_wqes\n",
> +			__func__, __LINE__);
> +		wqm_config0 = nes_read_indexed(nesdev, 0x5000);
> +		wqm_config0 |= 0x8000;
> +		nes_write_indexed(nesdev, 0x5000, wqm_config0);
> +	}
> +
> +	dev_put(file->nesvnic->netdev);
> +
> +	file->active = 0;
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) done\n", __func__, __LINE__);
> +}
> +
> +
> +static int nes_ud_init_channel(struct nes_ud_file *file)
> +{
> +	struct nes_device *nesdev = NULL;
> +	int ret = 0;
> +	int nic_active = 0;
> +	int mcast_all = 0;
> +	int mcast_en = 0;
> +	int link_ag = 0;
> +	int mpp4_dbg = 0;
> +
> +	nesdev = file->nesvnic->nesdev;
> +
> +	ret = nes_ud_create_nic(file);
> +	if (ret != 0)
> +		return ret;
> +
> +	if (file->queue_type == NES_UD_RECV_QUEUE) {
> +
> +		file->nesvnic->mcrq_mcast_filter = nes_ud_mcast_filter;
> +
> +		mcast_en = nes_read_indexed(nesdev,
> +					NES_IDX_NIC_MULTICAST_ENABLE);
> +		mcast_en |= 1 << file->nes_ud_nic_index;
> +		nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE,
> +								mcast_en);
> +		mcast_en = nes_read_indexed(nesdev,
> +					NES_IDX_NIC_MULTICAST_ENABLE);
> +
> +		/* the only case when we use PFT is for single port
> +		two functions, which probably would be the
> +		most common usage model :), but anyway */
> +	if (file->mcast_mode == NES_UD_MCAST_ALL_MODE) {
> +		mcast_all = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
> +		mcast_all |= 1 << file->nes_ud_nic_index;
> +		nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, mcast_all);
> +		mcast_all = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
> +	}
> +	if (nesdev->nesadapter->port_count <= 2) {
> +		link_ag = 0x00;
> +		nes_write_indexed(nesdev, 0x6038, link_ag);
> +		link_ag = nes_read_indexed(nesdev, 0x6038);
> +	}
> +	if (nesdev->nesadapter->netdev_count <= 2)
> +		nes_ud_enable_mqueue(file);
> +
> +	nes_write_indexed(nesdev, 0xA00, 0x245);
> +	nes_write_indexed(nesdev, 0xA40, 0x245);
> +
> +	}
> +	/* NES_UD_SEND_QUEUE */
> +	else {
> +		mpp4_dbg = nes_read_indexed(nesdev, 0xb00);
> +		mpp4_dbg |= 1 << 12;
> +		nes_write_indexed(nesdev, 0xb00, mpp4_dbg);
> +		mpp4_dbg = nes_read_indexed(nesdev, 0xb00);
> +	}
> +
> +	nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +	nic_active |= 1 << file->nes_ud_nic_index;
> +	nes_write_indexed(nesdev, NES_IDX_NIC_ACTIVE, nic_active);
> +	nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
> +
> +	nes_debug(NES_DBG_UD, "nic_active=0x%x, mcast_en=0x%x, "
> +		"mcast_all=0x%x nic_index=%d link_ag=0x%x mpp4_dbg=0x%x\n",
> +		nic_active, mcast_en, mcast_all, file->nes_ud_nic_index,
> +		link_ag, mpp4_dbg);
> +
> +	return ret;
> +}
> +
> +static struct nes_ud_file *nes_ud_get_nxt_channel(struct nes_vnic *nesvnic,
> +					enum nes_ud_queue_type queue_type)
> +{
> +	struct nes_ud_file *file = NULL;
> +	struct net_device *netdev = NULL;
> +	struct nes_device *nesdev = NULL;
> +	struct nes_ud_resources *pRsc;
> +
> +	netdev = nesvnic->netdev;
> +	nesdev = nesvnic->nesdev;
> +
> +	pRsc = locate_ud_adapter(nesdev->nesadapter);
> +	if (pRsc == NULL) {
> +		pRsc = allocate_ud_adapter(nesdev->nesadapter);
> +		if (pRsc == NULL)
> +			return NULL;
> +
> +	}
> +	if (pRsc->num_logport_confed == 0) {
> +		pRsc->original_60b8 = nes_read_indexed(nesdev, 0x60b8);
> +		pRsc->original_6000 = nes_read_indexed(nesdev, 0x6000);
> +		/* everything goes to port 0x0 */
> +		if ((nesvnic->nesdev->nesadapter->port_count == 1) ||
> +		    (nes_drv_opt & NES_DRV_OPT_MCAST_LOGPORT_MAP)) {
> +			/* single port card or dual port using single if */
> +			pRsc->num_logport_confed = 0x3;
> +			pRsc->logport_2_map = 0x0;
> +			pRsc->logport_3_map = 0x0;
> +			nes_write_indexed(nesdev, 0x60b8, 0x3);
> +			nes_write_indexed(nesdev, 0x6000, 0x0);
> +		} else {
> +			pRsc->num_logport_confed = 0x3;
> +			pRsc->logport_2_map = 0x0;
> +			pRsc->logport_3_map = 0x1;
> +		}
> +		nes_debug(NES_DBG_UD, "%s(%d) num_logport_confed=%d "
> +			"original_6000=%d logport_3_map = %d nes_drv_opt=%x\n",
> +			__func__, __LINE__, pRsc->num_logport_confed,
> +			pRsc->original_6000, pRsc->logport_3_map, nes_drv_opt);
> +	}
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) logport_2_map=%d logport_3_map=%d\n",
> +		 __func__, __LINE__, pRsc->logport_2_map, pRsc->logport_3_map);
> +
> +	file = allocate_nic_queue(nesvnic, queue_type);
> +	if (file == NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) failed to allocate NIC\n",
> +			__func__, __LINE__);
> +		return NULL;
> +	}
> +
> +	file->active = 1;
> +	memcpy(file->ifrn_name, netdev->name, IFNAMSIZ);
> +
> +	/* for now use pft always */
> +	file->mcast_mode = NES_UD_MCAST_PFT_MODE;
> +
> +	nes_debug(NES_DBG_UD, " %s(%d) NAME=%s qpn=%d nes_ud_nic_index=%d "
> +		"nes_ud_nic.qp_id=%d mcast_mode=%d port_count=%d "
> +		"netdev_count=%d\n", __func__, __LINE__, file->ifrn_name,
> +		 file->qpn, file->nes_ud_nic_index, file->nesvnic->mcrq_qp_id,
> +		 file->mcast_mode, nesdev->nesadapter->port_count,
> +		nesdev->nesadapter->netdev_count);
> +
> +	file->mss = netdev->mtu-28;
> +	pRsc->num_allocated_nics++;
> +	BUG_ON(pRsc->num_allocated_nics > 8);
> +
> +	return file;
> +
> +}
> +
> +static struct nes_ud_mem_region *nes_ud_allocate_mr(u32 npages)
> +{
> +	struct nes_ud_mem_region *mr = NULL;
> +
> +	mr = vmalloc(sizeof(*mr));
> +	if (mr == NULL)
> +		return NULL;
> +
> +
> +	mr->addrs = vmalloc(npages * sizeof(dma_addr_t));
> +	if (!mr->addrs) {
> +		nes_debug(NES_DBG_UD, "%s(%d) Cannot allocate mr struct "
> +		"for %d pages\n", __func__, __LINE__, npages);
> +		vfree(mr);
> +		return NULL;
> +	}
> +	mr->pg_cnt = npages;
> +	mr->in_use = 1;
> +
> +	INIT_LIST_HEAD(&mr->list);
> +
> +	return mr;
> +}
> +
> +static void nes_ud_free_mr(struct nes_ud_mem_region *mr)
> +{
> +	if (mr->addrs != NULL)
> +		vfree(mr->addrs);
> +
> +	vfree(mr);
> +}
> +
> +/* nes_ud_get_hash_entry()
> + *
> + * function returns a key for hash table
> + */
> +static inline
> +int nes_ud_get_hash_entry(u32 stag)
> +{
> +	return stag & 0xff;
> +}
> +
> +
> +/* nes_ud_lookup_mr()
> + *
> + * function returns a pointer to mr realized by specific STAG
> + */
> +static inline
> +struct nes_ud_mem_region *nes_ud_lookup_mr(u32 stag)
> +{
> +	int key;
> +	struct nes_ud_mem_region *mr;
> +
> +	key = nes_ud_get_hash_entry(stag);
> +
> +	list_for_each_entry(mr, &ud_mem.mrs[key].list, list) {
> +		ud_mem.mrs[key].read_stats++;
> +		if (mr->stag == stag)
> +			return mr;
> +
> +	}
> +	return NULL;
> +}
> +
> +/* nes_ud_add_mr_hash()
> + *
> + * the function inserts the mr entry into the hash list
> + * the stag is a key
> + */
> +static inline
> +int nes_ud_add_mr_hash(struct nes_ud_mem_region *mr)
> +{
> +	int key;
> +
> +	/* first check if the stag is unique */
> +	if (nes_ud_lookup_mr(mr->stag) != NULL) {
> +		nes_debug(NES_DBG_UD, "%s(%d) double STAG error stag=%x\n",
> +			__func__, __LINE__, mr->stag);
> +		return -1;
> +	}
> +	key = nes_ud_get_hash_entry(mr->stag);
> +
> +	/* structure is global so mutexes are necessary */
> +	mutex_lock(&ud_mem.mutex);
> +
> +	/* add mr to the list at start  */
> +	list_add(&mr->list, &ud_mem.mrs[key].list);
> +
> +	mutex_unlock(&ud_mem.mutex);
> +
> +	return 0;
> +
> +}
> +
> +/* nes_ud_del_mr()
> + *
> + * the function removes the entry from the hash list
> + * the stag is the key
> + */
> +static inline
> +void nes_ud_del_mr(struct nes_ud_mem_region *mr)
> +{
> +	/* structure is global so mutexes are necessary */
> +	mutex_lock(&ud_mem.mutex);
> +
> +	list_del(&mr->list);
> +
> +	/* init entry */
> +	INIT_LIST_HEAD(&mr->list);
> +
> +	mutex_unlock(&ud_mem.mutex);
> +}
> +
> +/* nes_ud_cleanup_mr()
> + *
> + * function deletes and and frees all hash entries
> + */
> +static inline
> +void nes_ud_cleanup_mr(void)
> +{
> +	struct nes_ud_mem_region *mr;
> +	struct nes_ud_mem_region *next;
> +	int i;
> +
> +	/* structure is global so mutexes are necessary */
> +	mutex_lock(&ud_mem.mutex);
> +
> +	for (i = 0; i < NES_UD_MAX_REG_HASH_CNT; i++) {
> +		if (list_empty(&ud_mem.mrs[i].list))
> +			continue;
> +
> +		list_for_each_entry_safe(mr, next, &ud_mem.mrs[i].list, list) {
> +			nes_debug(NES_DBG_UD, "%s(%d) non free stag=%x\n",
> +				__func__, __LINE__, mr->stag);
> +			list_del_init(&mr->list);
> +
> +			nes_ud_free_mr(mr);
> +		}
> +	}
> +
> +	mutex_unlock(&ud_mem.mutex);
> +}
> +
> +u32 nes_ud_reg_mr(struct ib_umem *region, u64 length, u64 virt, u32 stag)
> +{
> +	unsigned long npages =
> +		PAGE_ALIGN(region->length + region->offset) >> PAGE_SHIFT;
> +	struct nes_ud_mem_region *mr = nes_ud_allocate_mr(npages);
> +	struct ib_umem_chunk *chunk;
> +	dma_addr_t page;
> +	u32 chunk_pages = 0;
> +	int nmap_index;
> +	int i = 0;
> +	int mr_id = 0;
> +	nes_debug(NES_DBG_UD, "%s(%d) mr=%p length=%d virt=%p\n",
> +		__func__, __LINE__, mr, (int)length, (void *)virt);
> +	if (!mr)
> +		return 0;
> +
> +
> +	mr->stag = stag;
> +
> +	mr->va = virt;
> +	mr->length = length;
> +	list_for_each_entry(chunk, &region->chunk_list, list) {
> +	for (nmap_index = 0; nmap_index < chunk->nmap; ++nmap_index) {
> +		page = sg_dma_address(&chunk->page_list[nmap_index]);
> +		chunk_pages = sg_dma_len(&chunk->page_list[nmap_index]) >> 12;
> +		if (page & ~PAGE_MASK)
> +			goto reg_user_mr_err;
> +		if (!chunk_pages)
> +			goto reg_user_mr_err;
> +
> +		for (i = 0; i < chunk_pages; i++) {
> +			mr->addrs[mr_id] = page;
> +			page += PAGE_SIZE;
> +			if (++mr_id > npages)
> +				goto reg_user_mr_err;
> +			}
> +		}
> +	}
> +	nes_debug(NES_DBG_UD, "%s(%d) stag=0x%x mr_id=%d npages=%d\n",
> +		__func__, __LINE__, stag, mr_id, (int)npages);
> +	nes_ud_add_mr_hash(mr);
> +	return stag;
> +
> +reg_user_mr_err:
> +	if (mr)
> +		nes_ud_free_mr(mr);
> +
> +	return 0;
> +}
> +
> +
> +int nes_ud_dereg_mr(u32 stag)
> +{
> +	struct nes_ud_mem_region *mr = NULL;
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) stag=0x%x\n", __func__, __LINE__, stag);
> +
> +	mr = nes_ud_lookup_mr(stag);
> +	if (mr != NULL) {
> +		nes_ud_del_mr(mr);
> +		nes_ud_free_mr(mr);
> +	} else {
> +		nes_debug(NES_DBG_UD, "%s(%d) unknown stag=0x%x\n",
> +		__func__, __LINE__, stag);
> +	}
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) done\n", __func__, __LINE__);
> +	return 0;
> +}
> +
> +
> +int nes_ud_unsubscribe_mcast(struct nes_ud_file *file, union ib_gid *gid)
> +{
> +	int ret = 0;
> +	int i;
> +	struct nes_ud_resources *pRsc;
> +
> +	if (file->queue_type == NES_UD_SEND_QUEUE)
> +		return -EFAULT;
> +
> +	pRsc = locate_ud_adapter(file->nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return -EFAULT;
> +
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (pRsc->mcast[i].in_use &&
> +			pRsc->mcast[i].owner == file &&
> +			pRsc->mcast[i].addr[0] == gid->raw[13] &&
> +			pRsc->mcast[i].addr[1] == gid->raw[14] &&
> +			pRsc->mcast[i].addr[2] == gid->raw[15]) {
> +				pRsc->mcast[i].in_use = 0;
> +				goto out;
> +		}
> +	}
> +
> +	ret = -EFAULT;
> +out:
> +	nes_debug(NES_DBG_UD, "%s(%d) %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X \
> +		ret=%d mcast=%d\n", __func__, __LINE__, gid->raw[10],
> +		gid->raw[11], gid->raw[12], gid->raw[13], gid->raw[14],
> +		gid->raw[15], ret , i);
> +	return ret;
> +
> +}
> +
> +/* function returns a number of allocated multicast entries in given adapter */
> +static int get_mcast_number_alloced(struct nes_ud_resources *pRsc)
> +{
> +	int i;
> +	int no = 0;
> +
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (pRsc->mcast[i].in_use != 0)
> +			no++;
> +
> +	}
> +	return no;
> +}
> +
> +/* function subscribe a multicast group in the system - PFT modification */
> +int nes_ud_subscribe_mcast(struct nes_ud_file *file, union ib_gid *gid)
> +{
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	int ret = 0;
> +	int i;
> +	__u8 hash_idx = 0;
> +	__u8 instance = file->nes_ud_nic_index & 0x1;
> +	unsigned addr = 0;
> +	unsigned mqueue_ind_tbl;
> +	struct nes_ud_resources *pRsc;
> +
> +	struct net_device *netdev = file->nesvnic->netdev;
> +	struct dev_mc_list *mc_list;
> +	int	multicast_address_exist = 0;
> +
> +
> +	if (file->queue_type == NES_UD_SEND_QUEUE)
> +		return -EFAULT;
> +
> +	pRsc = locate_ud_adapter(nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return -EFAULT;
> +
> +	for (mc_list = netdev->mc_list;
> +		mc_list != NULL;
> +		mc_list = mc_list->next) {
> +		if (mc_list != NULL) {
> +			if ((mc_list->dmi_addr[3] == gid->raw[13]) &&
> +			    (mc_list->dmi_addr[4] == gid->raw[14]) &&
> +			    (mc_list->dmi_addr[5] == gid->raw[15]) &&
> +			    (mc_list->dmi_addr[0] == 0x01) &&
> +			    (mc_list->dmi_addr[1] == 0) &&
> +			    (mc_list->dmi_addr[2] == 0x5e)) {
> +				multicast_address_exist = 1;
> +				break;
> +			}
> +		} else {
> +			break;
> +		}
> +	}
> +
> +	if (multicast_address_exist == 0) {
> +		nes_debug(NES_DBG_UD, "WARNING: multicast address not exist "
> +			"on multicast list\n");
> +		return -EFAULT;
> +	}
> +
> +	/* first check that we have not subecribed to this mcast address, yet */
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if ((pRsc->mcast[i].in_use > 0) &&
> +		    (pRsc->mcast[i].addr[0] == gid->raw[13]) &&
> +		    (pRsc->mcast[i].addr[1] == gid->raw[14]) &&
> +		    (pRsc->mcast[i].addr[2] == gid->raw[15])) {
> +			if (pRsc->mcast[i].owner == file) {
> +				nes_debug(NES_DBG_UD, "WARNING - subscribing  "
> +				"mcast to the same nes_ud more than once\n");
> +				break;
> +			} else {
> +		/* receiving the same multicast on different NICs is allowed:
> +			1. when two different NICS are used
> +			2.  exactly one QP exists on this adapter
> +			3. The existing QP was allocated as first
> +				or the second in the system
> +		  */
> +			if (pRsc->mcast[i].owner->nes_ud_nic_index !=
> +						file->nes_ud_nic_index) {
> +				if (get_mcast_number_alloced(pRsc) == 1) {
> +					if ((i == 0) || (i == 1)) {
> +						/* add the mask of other nics
> +						that subscribe this address  */
> +						break;
> +					}
> +				}
> +			}
> +			nes_debug(NES_DBG_UD, "ERROR - subscribing same mcast "
> +				"to the diff nes_ud's and NIC  owner_idx = %d "
> +				"file_idx = %d\n",
> +				pRsc->mcast[i].owner->nes_ud_nic_index,
> +				file->nes_ud_nic_index);
> +			ret = -EFAULT;
> +		    }
> +		    goto out;
> +		}
> +	}
> +
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (!pRsc->mcast[i].in_use) {
> +			pRsc->mcast[i].addr[0] = gid->raw[13];
> +			pRsc->mcast[i].addr[1] = gid->raw[14];
> +			pRsc->mcast[i].addr[2] = gid->raw[15];
> +			pRsc->mcast[i].owner = file;
> +			pRsc->mcast[i].in_use = 1;
> +
> +			hash_idx =
> +				nes_ud_calculate_hash(pRsc->mcast[i].addr[2]);
> +
> +			addr = 0x6420 + ((hash_idx >> 3) << 2) + instance*0x100;
> +			mqueue_ind_tbl = nes_read_indexed(nesdev, addr);
> +			if (file->prio == NES_UD_DEV_PRIO_HIGH)
> +				mqueue_ind_tbl &= ~(1 << ((hash_idx & 0x7)*4));
> +			else
> +				mqueue_ind_tbl |= 1 << ((hash_idx & 0x7)*4);
> +
> +			nes_write_indexed(nesdev, addr, mqueue_ind_tbl);
> +			mqueue_ind_tbl = nes_read_indexed(nesdev, addr);
> +
> +			nes_debug(NES_DBG_UD, "%s(%d) addr=0x%x "
> +				 "mqueue_ind_tbl=0x%x hash=0x%x, mac=0x%x\n",
> +				__func__, __LINE__, addr, mqueue_ind_tbl,
> +				hash_idx, pRsc->mcast[i].addr[2]);
> +			/* take care of the case when linux join_mcast
> +			is called before mcast_attach in that case our pft
> +			will already be programmed with that mcast address,
> +			just with wrong NIC we need just to find an address,
> +			and fix the NIC additionally the mask with other NICs
> +			that subscribed the address are added*/
> +
> +			mcast_fix_filter_table(file);
> +			goto out;
> +		}
> +	}
> +	ret = -EFAULT;
> +
> +out:
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X \
> +		ret=%d\n", __func__, __LINE__, gid->raw[10], gid->raw[11],
> +		gid->raw[12], gid->raw[13], gid->raw[14], gid->raw[15], ret);
> +
> +	return ret;
> +}
> +
> +
> +static inline
> +int nes_ud_post_recv(struct nes_ud_file *file,
> +			u32 adap_no,
> +			struct nes_ud_recv_wr *nes_ud_wr)
> +{
> +	struct nes_hw_nic_rq_wqe *nic_rqe;
> +	struct nes_hw_nic_rq_wqe *rq_vbase =
> +				(struct nes_hw_nic_rq_wqe *)file->wq_vbase;
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	u16 *wqe_fragment_length = NULL;
> +	u32 mr_offset;
> +	u32 page_offset;
> +	u32 page_id;
> +	struct nes_ud_mem_region *mr = NULL;
> +	int remaining_length = 0;
> +	int wqe_fragment_index = 0;
> +	int err = 0;
> +	int i = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	/* check if qp is activated */
> +	if (file->active == 0)
> +		return -EFAULT;
> +
> +	pRsc = &nes_ud_rsc[adap_no];
> +
> +	/* let's assume for now that max sge count is 1 */
> +	for (i = 0; i < nes_ud_wr->wr_cnt; i++) {
> +		nic_rqe = &rq_vbase[file->head];
> +
> +		mr = nes_ud_lookup_mr(nes_ud_wr->sg_list[i].lkey);
> +		if (mr == NULL)
> +			return -EFAULT;
> +
> +
> +		if (mr->va > nes_ud_wr->sg_list[i].addr ||
> +		    (nes_ud_wr->sg_list[i].addr + nes_ud_wr->sg_list[i].length >
> +							mr->va + mr->length)) {
> +			err = -EFAULT;
> +			goto out;
> +		}
> +
> +		mr_offset = nes_ud_wr->sg_list[i].addr - mr->va;
> +		page_offset = nes_ud_wr->sg_list[i].addr & ~PAGE_MASK;
> +		page_id = ((mr->va & ~PAGE_MASK) + mr_offset) >> PAGE_SHIFT;
> +
> +		wqe_fragment_length =
> +		(u16 *)&nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX];
> +
> +		remaining_length = nes_ud_wr->sg_list[i].length;
> +		wqe_fragment_index = 0;
> +
> +		while (remaining_length > 0) {
> +			if (wqe_fragment_index >= 4) {
> +				err = -EFAULT;
> +				goto out;
> +			}
> +
> +			set_wqe_64bit_value(nic_rqe->wqe_words,
> +			NES_NIC_RQ_WQE_FRAG0_LOW_IDX + 2*wqe_fragment_index,
> +				  mr->addrs[page_id]+page_offset);
> +
> +		      wqe_fragment_length[wqe_fragment_index] =
> +				cpu_to_le16(PAGE_SIZE - page_offset);
> +
> +		      remaining_length -= PAGE_SIZE - page_offset;
> +		      page_offset = 0;
> +		      page_id++;
> +		      wqe_fragment_index++;
> +		}
> +
> +		nes_write32(nesdev->regs+NES_WQE_ALLOC, (1 << 24) |  file->qpn);
> +
> +		file->head = (file->head+1) & ~NES_NIC_WQ_SIZE;
> +	}
> +out:
> +	return err;
> +}
> +
> +static inline
> +int nes_ud_post_send(struct nes_ud_file *file,
> +			u32 adap_no,
> +			struct nes_ud_send_wr *nes_ud_wr)
> +{
> +	struct nes_hw_nic_sq_wqe *nic_sqe;
> +	struct nes_hw_nic_sq_wqe *sq_vbase =
> +			(struct nes_hw_nic_sq_wqe *)file->wq_vbase;
> +	struct nes_device *nesdev = file->nesvnic->nesdev;
> +	u16 *wqe_fragment_length = NULL;
> +	u32 mr_offset;
> +	u32 page_offset;
> +	u32 page_id;
> +	struct nes_ud_mem_region *mr = NULL;
> +	int remaining_length = 0;
> +	int wqe_fragment_index = 0;
> +	int err = 0;
> +	int misc_flags = NES_NIC_SQ_WQE_COMPLETION;
> +	int i = 0;
> +	struct nes_ud_resources *pRsc;
> +
> +	/* check if qp is activated */
> +	if (file->active == 0)
> +		return -EFAULT;
> +
> +	pRsc = &nes_ud_rsc[adap_no];
> +
> +	/* check if is not set checksum */
> +	if (!(nes_ud_wr->flags & IB_SEND_IP_CSUM))
> +		misc_flags |= NES_NIC_SQ_WQE_DISABLE_CHKSUM;
> +
> +	/* let's assume for now that max sge count is 1 */
> +	for (i = 0; i < nes_ud_wr->wr_cnt; i++) {
> +		nic_sqe = &sq_vbase[file->head];
> +
> +		mr = nes_ud_lookup_mr(nes_ud_wr->sg_list[i].lkey);
> +		if (mr == NULL)
> +			return -EFAULT;
> +
> +
> +		if ((mr->va > nes_ud_wr->sg_list[i].addr) ||
> +		     (nes_ud_wr->sg_list[i].addr+nes_ud_wr->sg_list[i].length >
> +							mr->va + mr->length)) {
> +
> +			err = -EFAULT;
> +			goto out;
> +		}
> +
> +		mr_offset = nes_ud_wr->sg_list[i].addr - mr->va;
> +		page_offset = nes_ud_wr->sg_list[i].addr & ~PAGE_MASK;
> +		page_id = ((mr->va & ~PAGE_MASK) + mr_offset) >> PAGE_SHIFT;
> +
> +		wqe_fragment_length =
> +		(u16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX];
> +
> +		wqe_fragment_length++; /* skip vlan tag */
> +		remaining_length = nes_ud_wr->sg_list[i].length;
> +		wqe_fragment_index = 0;
> +
> +		while (remaining_length > 0) {
> +			if (wqe_fragment_index >= 4) {
> +				err = -EFAULT;
> +				goto out;
> +			}
> +			set_wqe_64bit_value(nic_sqe->wqe_words,
> +				NES_NIC_SQ_WQE_FRAG0_LOW_IDX +
> +						2*wqe_fragment_index,
> +				mr->addrs[page_id]+page_offset);
> +		      wqe_fragment_length[wqe_fragment_index] =
> +				cpu_to_le16(PAGE_SIZE - page_offset);
> +		      remaining_length -= PAGE_SIZE - page_offset;
> +		      page_offset = 0;
> +		      page_id++;
> +		      wqe_fragment_index++;
> +		}
> +		nic_sqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
> +				cpu_to_le32(nes_ud_wr->sg_list[i].length);
> +		nic_sqe->wqe_words[NES_NIC_SQ_WQE_MISC_IDX] =
> +						cpu_to_le32(misc_flags);
> +
> +		nes_write32(nesdev->regs+NES_WQE_ALLOC,
> +					(1 << 24) | (1 << 23) | file->qpn);
> +
> +		file->head = (file->head+1) & ~NES_NIC_WQ_SIZE;
> +	}
> +out:
> +	return err;
> +}
> +
> +
> +
> +static void nes_ud_mcast_cleanup_work(struct nes_ud_file *file)
> +{
> +	int i = 0;
> +	int num_queues = count_files_by_nic(file->nesvnic, file->queue_type);
> +	struct nes_ud_resources *pRsc;
> +
> +	pRsc = locate_ud_adapter(file->nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return;
> +
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) file->rsc_idx=%d\n",
> +			__func__, __LINE__, file->rsc_idx);
> +
> +	mutex_lock(&pRsc->mutex);
> +	for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) {
> +		if (pRsc->mcast[i].owner == file) {
> +			nes_debug(NES_DBG_UD, "%s(%d) mcast cleared idx=%d "
> +				"%2.2X:%2.2X:%2.2X\n", __func__, __LINE__,
> +				i, pRsc->mcast[i].addr[0],
> +				pRsc->mcast[i].addr[1],
> +				pRsc->mcast[i].addr[2]);
> +
> +			pRsc->mcast[i].in_use = 0;
> +			remove_mcast_from_pft(file, pRsc->mcast[i].addr);
> +		}
> +	}
> +
> +	if (del_rsc_list(file) == 0) {
> +		if (num_queues == 1)
> +			file->nesvnic->mcrq_mcast_filter = NULL;
> +
> +	}
> +	mutex_unlock(&pRsc->mutex);
> +}
> +
> +struct nes_ud_file *nes_ud_create_wq(struct nes_vnic *nesvnic, int isrecv)
> +{
> +	struct nes_ud_file *file;
> +	int ret = 0;
> +	file = nes_ud_get_nxt_channel(nesvnic, (isrecv) ?
> +				NES_UD_RECV_QUEUE : NES_UD_SEND_QUEUE);
> +	if (!file)
> +		return NULL;
> +
> +
> +	ret =  nes_ud_init_channel(file);
> +	if (ret != 0) {
> +		del_rsc_list(file);
> +		return NULL;
> +	}
> +
> +	dev_hold(file->nesvnic->netdev);
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) file=%p\n", __func__, __LINE__, file);
> +	return file;
> +}
> +
> +
> +
> +int nes_ud_destroy_wq(struct nes_ud_file *file)
> +{
> +	struct nes_ud_resources *pRsc;
> +	int count = 0;
> +	int i;
> +	pRsc = locate_ud_adapter(file->nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return -EFAULT;
> +
> +	if (file->active) {
> +		nes_ud_mcast_cleanup_work(file);
> +		nes_ud_free_resources(file);
> +	}
> +
> +	/* check if the the adapter has any queues */
> +	for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) {
> +		if (pRsc->nics[i].file->active != 0)
> +			count++;
> +
> +	}
> +	if (count == 0) {
> +		nes_debug(NES_DBG_UD, "%s(%d) adapter %d "
> +				"is ready to next use\n",
> +				__func__, __LINE__, pRsc->adapter_no);
> +		pRsc->pAdap = NULL;
> +	}
> +	nes_debug(NES_DBG_UD, "%s(%d) done\n", __func__, __LINE__);
> +	return 0;
> +}
> +
> +
> +struct nes_ud_sksq_file {
> +	unsigned long shared_page;
> +	struct nes_ud_file *nes_ud_send_file;
> +	struct nes_ud_file *nes_ud_recv_file;
> +};
> +
> +static ssize_t nes_ud_sksq_write(struct file *filp, const char __user *buf,
> +		size_t len, loff_t *pos)
> +{
> +	struct nes_ud_sksq_file *file = filp->private_data;
> +	struct nes_ud_send_wr *nes_ud_wr =
> +			(struct nes_ud_send_wr *)file->shared_page;
> +	u32 adap_no;
> +	u32 nic_no;
> +
> +	nic_no = ((nes_ud_wr->qpn >> 16) & 0x0f00) >> 8;
> +	adap_no = ((nes_ud_wr->qpn >> 16) & 0xf000) >> 12;
> +	if (unlikely(!file->nes_ud_send_file)) {
> +		struct nes_ud_file *nes_ud_file = NULL;
> +
> +	nes_ud_file = nes_ud_rsc[adap_no].nics[nic_no].file;
> +	/* the nic must be active and previously activated */
> +	if ((nes_ud_file->active == 0) ||
> +		(nes_ud_file->qpn != ((nes_ud_wr->qpn >> 16) & 0xff)))
> +			return -EAGAIN;
> +
> +		file->nes_ud_send_file = nes_ud_file;
> +		nes_debug(NES_DBG_UD, "send shared page addr = %p "
> +				"adap_no = %d nic_no=%d qpn=%x\n",
> +				nes_ud_wr, adap_no, nic_no, nes_ud_wr->qpn);
> +  }
> +  return nes_ud_post_send(file->nes_ud_send_file, adap_no, nes_ud_wr);
> +
> +}
> +
> +static ssize_t nes_ud_sksq_read(struct file *filp, char __user *buf,
> +		size_t len, loff_t *pos)
> +{
> +	struct nes_ud_sksq_file *file = filp->private_data;
> +	struct nes_ud_recv_wr *nes_ud_recv_wr;
> +	u32 adap_no;
> +	u32 nic_no;
> +
> +	nes_ud_recv_wr = (struct nes_ud_recv_wr *)(file->shared_page+2048);
> +	adap_no = (nes_ud_recv_wr->qpn & 0xf000) >> 12;
> +	nic_no = (nes_ud_recv_wr->qpn & 0x0f00) >> 8;
> +
> +	if (unlikely(!file->nes_ud_recv_file)) {
> +		struct nes_ud_file *nes_ud_file = NULL;
> +
> +		nes_ud_file = nes_ud_rsc[adap_no].nics[nic_no].file;
> +		/* the nic must be active and previously activated */
> +		if ((nes_ud_file->active == 0) ||
> +			(nes_ud_file->qpn != (nes_ud_recv_wr->qpn & 0xff)))
> +				return -EAGAIN;
> +
> +		file->nes_ud_recv_file  = nes_ud_file;
> +		nes_debug(NES_DBG_UD, "recv shared page addr = %p "
> +				"adap_no = %d nic_no=%d qpn=%x\n",
> +			nes_ud_recv_wr, adap_no, nic_no, nes_ud_recv_wr->qpn);
> +	}
> +	return nes_ud_post_recv(file->nes_ud_recv_file,
> +				adap_no, nes_ud_recv_wr);
> +}
> +
> +static int nes_ud_sksq_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct nes_ud_sksq_file *file = filp->private_data;
> +
> +	nes_debug(NES_DBG_UD, "shared mem pgprot_val(prot)=0x%x pa=%p\n",
> +			(unsigned int)pgprot_val(vma->vm_page_prot),
> +			(void *)virt_to_phys((void *)file->shared_page));
> +	if (remap_pfn_range(vma, vma->vm_start,
> +			virt_to_phys((void *)file->shared_page) >> PAGE_SHIFT,
> +			 PAGE_SIZE, vma->vm_page_prot)) {
> +		printk(KERN_ERR "remap_pfn_range failed.\n");
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +
> +static int nes_ud_sksq_open(struct inode *inode, struct file *filp)
> +{
> +	struct nes_ud_sksq_file *file;
> +
> +	file = kmalloc(sizeof *file, GFP_KERNEL);
> +	if (!file)
> +		return -ENOMEM;
> +
> +	memset(file, 0, sizeof *file);
> +	nes_debug(NES_DBG_UD, "%s(%d) file=%p\n",
> +			__func__, __LINE__, file);
> +
> +	filp->private_data = file;
> +	file->nes_ud_send_file = NULL;
> +	file->nes_ud_recv_file = NULL;
> +
> +	file->shared_page = __get_free_page(GFP_USER);
> +	return 0;
> +}
> +
> +static int nes_ud_sksq_close(struct inode *inode, struct file *filp)
> +{
> +
> +	struct nes_ud_sksq_file *file = filp->private_data;
> +
> +	if (file->shared_page) {
> +		free_page(file->shared_page);
> +		file->shared_page = 0;
> +	}
> +	kfree(file);
> +	return 0;
> +}
> +
> +static const struct file_operations nes_ud_sksq_fops = {
> +	.owner = THIS_MODULE,
> +	.open = nes_ud_sksq_open,
> +	.release = nes_ud_sksq_close,
> +	.write = nes_ud_sksq_write,
> +	.read = nes_ud_sksq_read,
> +	.mmap = nes_ud_sksq_mmap,
> +};
> +
> +
> +static struct miscdevice nes_ud_sksq_misc = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = "nes_ud_sksq",
> +	.fops = &nes_ud_sksq_fops,
> +};
> +
> +/*
> + * function replaces the CQ pointer in QP stored in the file
> + * the QP must have a valid CQ pointers assotiated with it
> + */
> +int nes_ud_cq_replace(struct nes_vnic *nesvnic, struct nes_cq *cq)
> +{
> +	u32 cq_num;
> +	struct nes_ud_file *file;
> +	struct nes_ud_resources *pRsc;
> +
> +	BUG_ON(!cq);
> +
> +	pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter);
> +	if (pRsc == NULL)
> +		return -EFAULT;
> +
> +
> +	/* now create a QP number on base cq and adapter no */
> +	cq_num = cq->hw_cq.cq_number;
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) cq_number=%d\n",
> +			__func__, __LINE__, cq_num);
> +
> +	/* the QP number should have the same number like CQ number */
> +	file = get_file_by_qpn(pRsc, cq_num);
> +	if (!file) {
> +		nes_debug(NES_DBG_UD, "%s(%d) file not found\n",
> +				__func__, __LINE__);
> +		return -EFAULT;
> +	}
> +	if (file->qp_ptr) {
> +		if (file->queue_type == NES_UD_RECV_QUEUE) {
> +			nes_debug(NES_DBG_UD, "%s(%d) RECV file found "
> +			"old=%p new=%p\n", __func__, __LINE__,
> +			file->qp_ptr->ibqp.recv_cq, cq);
> +			file->qp_ptr->ibqp.recv_cq = &cq->ibcq;
> +		}
> +		if (file->queue_type == NES_UD_SEND_QUEUE) {
> +			nes_debug(NES_DBG_UD, "%s(%d) SEND file found "
> +			"old=%p new=%p\n", __func__, __LINE__,
> +			file->qp_ptr->ibqp.send_cq, cq);
> +
> +			file->qp_ptr->ibqp.send_cq = &cq->ibcq;
> +		}
> +	}
> +	return 0;
> +}
> +int nes_ud_init(void)
> +{
> +	int i = 0;
> +	int adap_no;
> +	struct nes_ud_resources *pRsc;
> +
> +	nes_debug(NES_DBG_UD, "%s(%d)\n", __func__, __LINE__);
> +
> +	/* the memory registration is global for all NICS */
> +	memset(&ud_mem, 0, sizeof(ud_mem));
> +
> +	/* init hash list of memory entries */
> +	for (i = 0; i < NES_UD_MAX_REG_HASH_CNT; i++) {
> +		INIT_LIST_HEAD(&ud_mem.mrs[i].list);
> +		ud_mem.mrs[i].read_stats = 0;
> +	}
> +	mutex_init(&ud_mem.mutex);
> +
> +	/*allocate resources fro each adapter */
> +	for (adap_no = 0; adap_no < NES_UD_MAX_ADAPTERS; adap_no++) {
> +		pRsc = &nes_ud_rsc[adap_no];
> +
> +		memset(pRsc, 0, sizeof(*pRsc));
> +
> +		mutex_init(&pRsc->mutex);
> +
> +		pRsc->adapter_no = adap_no;
> +		pRsc->pAdap = NULL;
> +
> +		pRsc->num_logport_confed = 0;
> +		pRsc->num_allocated_nics = 0;
> +		pRsc->logport_2_map = 0xf;
> +		pRsc->logport_3_map = 0xf;
> +		for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++)
> +			pRsc->mcast[i].in_use = 0;
> +
> +		pRsc->nics[0].qpn = 20;
> +		pRsc->nics[0].nic_index = 2;
> +		pRsc->nics[0].logical_port = 2;
> +		pRsc->nics[0].prio = NES_UD_DEV_PRIO_HIGH;
> +		pRsc->nics[0].queue_type = NES_UD_RECV_QUEUE;
> +		pRsc->nics[0].file = &pRsc->nics[0].file_body;
> +
> +		pRsc->nics[1].qpn = 22;
> +		pRsc->nics[1].nic_index = 3;
> +		pRsc->nics[1].logical_port = 3;
> +		pRsc->nics[1].prio = NES_UD_DEV_PRIO_HIGH;
> +		pRsc->nics[1].queue_type = NES_UD_RECV_QUEUE;
> +		pRsc->nics[1].file = &pRsc->nics[1].file_body;
> +
> +		pRsc->nics[2].qpn = 21;
> +		pRsc->nics[2].nic_index = 2;
> +		pRsc->nics[2].logical_port = 2;
> +		pRsc->nics[2].prio = NES_UD_DEV_PRIO_LOW;
> +		pRsc->nics[2].queue_type = NES_UD_RECV_QUEUE;
> +		pRsc->nics[2].file = &pRsc->nics[2].file_body;
> +
> +		pRsc->nics[3].qpn = 23;
> +		pRsc->nics[3].nic_index = 3;
> +		pRsc->nics[3].logical_port = 3;
> +		pRsc->nics[3].prio = NES_UD_DEV_PRIO_LOW;
> +		pRsc->nics[3].queue_type = NES_UD_RECV_QUEUE;
> +		pRsc->nics[3].file = &pRsc->nics[3].file_body;
> +
> +		pRsc->nics[4].qpn = 26;
> +		pRsc->nics[4].nic_index = 6;
> +		pRsc->nics[4].logical_port = 2;
> +		pRsc->nics[4].prio = NES_UD_DEV_PRIO_HIGH;
> +		pRsc->nics[4].queue_type = NES_UD_SEND_QUEUE;
> +		pRsc->nics[4].file = &pRsc->nics[4].file_body;
> +
> +		pRsc->nics[5].qpn = 27;
> +		pRsc->nics[5].nic_index = 7;
> +		pRsc->nics[5].logical_port = 3;
> +		pRsc->nics[5].prio = NES_UD_DEV_PRIO_HIGH;
> +		pRsc->nics[5].queue_type = NES_UD_SEND_QUEUE;
> +		pRsc->nics[5].file = &pRsc->nics[5].file_body;
> +
> +		pRsc->nics[6].qpn = 30;
> +		pRsc->nics[6].nic_index = 10;
> +		pRsc->nics[6].logical_port = 2;
> +		pRsc->nics[6].prio = NES_UD_DEV_PRIO_LOW;
> +		pRsc->nics[6].queue_type = NES_UD_SEND_QUEUE;
> +		pRsc->nics[6].file = &pRsc->nics[6].file_body;
> +
> +		pRsc->nics[7].qpn = 31;
> +		pRsc->nics[7].nic_index = 11;
> +		pRsc->nics[7].logical_port = 3;
> +		pRsc->nics[7].prio = NES_UD_DEV_PRIO_LOW;
> +		pRsc->nics[7].queue_type = NES_UD_SEND_QUEUE;
> +		pRsc->nics[7].file = &pRsc->nics[7].file_body;
> +
> +	}
> +	nes_ud_workqueue = create_singlethread_workqueue("nes_ud");
> +
> +	return misc_register(&nes_ud_sksq_misc);
> +}
> +
> +
> +int nes_ud_exit(void)
> +{
> +	/* clean memory hash list */
> +	nes_ud_cleanup_mr();
> +	misc_deregister(&nes_ud_sksq_misc);
> +	return 0;
> +}
> +
> diff --git a/drivers/infiniband/hw/nes/nes_ud.h b/drivers/infiniband/hw/nes/nes_ud.h
> new file mode 100644
> index 0000000..5a03b33
> --- /dev/null
> +++ b/drivers/infiniband/hw/nes/nes_ud.h
> @@ -0,0 +1,86 @@
> +/*
> + * Copyright (c) 2008 - 2010 Intel Corporation.  All rights reserved.
> + * Copyright (c) 2006 - 2008 Neteffect, All rights reserved.
> + * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses.  You may choose to be licensed 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, or the
> + * OpenIB.org BSD license below:
> + *
> + *     Redistribution and use in source and binary forms, with or
> + *     without modification, are permitted provided that the following
> + *     conditions are met:
> + *
> + *      - Redistributions of source code must retain the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer.
> + *
> + *      - Redistributions in binary form must reproduce the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer in the documentation and/or other materials
> + *        provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +#ifndef __NES_UD_H
> +#define __NES_UD_H
> +
> +enum nes_ud_dev_priority {
> +	NES_UD_DEV_PRIO_HIGH,
> +	NES_UD_DEV_PRIO_LOW,
> +};
> +
> +enum nes_ud_queue_type {
> +  NES_UD_RECV_QUEUE,
> +  NES_UD_SEND_QUEUE,
> +};
> +
> +enum nes_ud_mcast_mode {
> +  NES_UD_MCAST_ALL_MODE,
> +  NES_UD_MCAST_PFT_MODE,
> +};
> +
> +
> +struct nes_ud_file {
> +	struct nes_vnic *nesvnic;
> +	u8 active;
> +	char ifrn_name[IFNAMSIZ];
> +	int nes_ud_nic_index;
> +	int qpn;
> +	enum nes_ud_dev_priority prio;
> +	enum nes_ud_mcast_mode mcast_mode;
> +	enum nes_ud_queue_type queue_type;
> +	void      *nic_vbase;
> +	dma_addr_t nic_pbase;
> +	int        nic_mem_size;
> +	void      *wq_vbase;
> +	dma_addr_t wq_pbase;
> +	int mss;
> +	struct delayed_work mcast_cleanup_work;
> +	int head;
> +	int tail;
> +	u32 rsc_idx;
> +	struct nes_qp *qp_ptr; /* it is association used for CQ replacement */
> +	u32 adapter_no;    /* assotiation to allocated adapter */
> +};
> +
> +int nes_ud_init(void);
> +int nes_ud_exit(void);
> +struct nes_ud_file *nes_ud_create_wq(struct nes_vnic *nesvnic, int isrecv);
> +int nes_ud_destroy_wq(struct nes_ud_file *file);
> +u32 nes_ud_reg_mr(struct ib_umem *region, u64 length, u64 virt, u32 stag);
> +int nes_ud_dereg_mr(u32 stag);
> +int nes_ud_subscribe_mcast(struct nes_ud_file *file, union ib_gid *gid);
> +int nes_ud_unsubscribe_mcast(struct nes_ud_file *file, union ib_gid *gid);
> +int nes_ud_cq_replace(struct nes_vnic *nesvnic, struct nes_cq *cq);
> +
> +#endif
> diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
> index e54f312..ff39235 100644
> --- a/drivers/infiniband/hw/nes/nes_verbs.c
> +++ b/drivers/infiniband/hw/nes/nes_verbs.c
> @@ -46,6 +46,8 @@
>  
>  #include <rdma/ib_umem.h>
>  
> +#include "nes_ud.h"
> +
>  atomic_t mod_qp_timouts;
>  atomic_t qps_created;
>  atomic_t sw_qps_destroyed;
> @@ -1139,7 +1141,6 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
>  	if (init_attr->create_flags)
>  		return ERR_PTR(-EINVAL);
>  
> -	atomic_inc(&qps_created);
>  	switch (init_attr->qp_type) {
>  		case IB_QPT_RC:
>  			if (nes_drv_opt & NES_DRV_OPT_NO_INLINE_DATA) {
> @@ -1405,10 +1406,122 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
>  					nesqp->hwqp.qp_id, nesqp, (u32)sizeof(*nesqp));
>  			spin_lock_init(&nesqp->lock);
>  			nes_add_ref(&nesqp->ibqp);
> +			/* moved here to be sure that QP is really created */
> +			/*(now it counted a number of QP creation trials */
> +			atomic_inc(&qps_created);
>  			break;
> -		default:
> -			nes_debug(NES_DBG_QP, "Invalid QP type: %d\n", init_attr->qp_type);
> -			return ERR_PTR(-EINVAL);
> +
> +	case IB_QPT_RAW_ETY:
> +	if (!ibpd->uobject)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* we are about to destroy those cqs w/o destroying qp
> +	 now free memory for nespbl that is not used
> +	 first map nespbl with the qp created */
> +	if (ibpd->uobject->context) {
> +		nes_ucontext = to_nesucontext(ibpd->uobject->context);
> +		if (udata) {
> +			if (ib_copy_from_udata(&req,
> +				udata,
> +				sizeof(struct nes_create_qp_req))) {
> +				return ERR_PTR(-EFAULT);
> +			}
> +			if (req.user_wqe_buffers) {
> +				err = 1;
> +				list_for_each_entry(nespbl,
> +				&nes_ucontext->qp_reg_mem_list,
> +				list) {
> +					if (nespbl->user_base ==
> +						req.user_wqe_buffers) {
> +						list_del(&nespbl->list);
> +						err = 0;
> +						/* done with memory allocated
> +						during nes_reg_user_mr() */
> +						pci_free_consistent(
> +							nesdev->pcidev,
> +							nespbl->pbl_size,
> +							nespbl->pbl_vbase,
> +							nespbl->pbl_pbase);
> +						kfree(nespbl);
> +						break;
> +					}
> +				}
> +			}
> +		}
> +	}
> +	/* Need 512 (actually now 1024) byte alignment on this structure */
> +	mem = kzalloc(sizeof(*nesqp)+NES_SW_CONTEXT_ALIGN-1, GFP_KERNEL);
> +	if (!mem) {
> +		nes_debug(NES_DBG_UD, "Unable to allocate QP\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +	u64nesqp = (unsigned long)mem;
> +	u64nesqp += ((u64)NES_SW_CONTEXT_ALIGN) - 1;
> +	u64temp = ((u64)NES_SW_CONTEXT_ALIGN) - 1;
> +	u64nesqp &= ~u64temp;
> +	nesqp = (struct nes_qp *)(unsigned long)u64nesqp;
> +	nesqp->allocated_buffer = mem;
> +
> +	nesqp->rx_ud_wq = nes_ud_create_wq(nesvnic, 1);
> +	nesqp->tx_ud_wq = nes_ud_create_wq(nesvnic, 0);
> +	if ((!nesqp->rx_ud_wq) || (!nesqp->tx_ud_wq)) {
> +		kfree(nesqp->allocated_buffer);
> +		return ERR_PTR(-EFAULT);
> +	}
> +
> +	/* create association between qp and tx/rx files
> +	 it is used when CQ is replaced from user space */
> +	nesqp->rx_ud_wq->qp_ptr = nesqp;
> +	nesqp->tx_ud_wq->qp_ptr = nesqp;
> +
> +	sq_size = init_attr->cap.max_send_wr;
> +	rq_size = init_attr->cap.max_recv_wr;
> +	nes_debug(NES_DBG_UD, "%s(%d) sq_size=%d rq_size=%d\n",
> +				__func__,
> +				__LINE__, sq_size, rq_size);
> +	uresp.actual_sq_size = sq_size;
> +	uresp.actual_rq_size = rq_size;
> +
> +	/* Init qp size due to ibv_query_qp requirements */
> +	nesqp->hwqp.sq_size = sq_size;
> +	nesqp->hwqp.rq_size = rq_size;
> +
> +	/* enhance the response qp number with adapter number and QP number
> +	on this adapter
> +	 user space will use this identifier when packets will be posted */
> +	uresp.qp_id = nesqp->rx_ud_wq->qpn |
> +			(nesqp->rx_ud_wq->adapter_no << 12) |
> +			(nesqp->rx_ud_wq->rsc_idx << 8);
> +	uresp.qp_id = uresp.qp_id |
> +			((nesqp->tx_ud_wq->qpn |
> +			(nesqp->tx_ud_wq->adapter_no << 12) |
> +			(nesqp->tx_ud_wq->rsc_idx << 8)) << 16);
> +
> +	nesqp->hwqp.qp_id = uresp.qp_id;
> +	nesqp->ibqp.qp_num = uresp.qp_id;
> +
> +	nes_debug(NES_DBG_UD, "%s(%d) qpid=0x%x\n",
> +			__func__, __LINE__, uresp.qp_id);
> +	if (ib_copy_to_udata(udata, &uresp, sizeof uresp)) {
> +		kfree(nesqp->allocated_buffer);
> +		return ERR_PTR(-EFAULT);
> +	}
> +	/* the usecount is decreased because without it
> +	the cq re-creation in user-spce will fail */
> +	atomic_dec(&init_attr->send_cq->usecnt);
> +	atomic_dec(&init_attr->recv_cq->usecnt);
> +	nes_add_ref(&nesqp->ibqp);
> +	spin_lock_init(&nesqp->lock);
> +
> +	/* moved here to be sure that QP is really created
> +	(now it counted a number of QP creation trials */
> +	atomic_inc(&qps_created);
> +	return &nesqp->ibqp;
> +
> +	default:
> +		nes_debug(NES_DBG_QP, "Invalid QP type: %d\n",
> +					init_attr->qp_type);
> +		return ERR_PTR(-EINVAL);
>  	}
>  
>  	nesqp->sig_all = (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR);
> @@ -1462,6 +1575,8 @@ static void nes_clean_cq(struct nes_qp *nesqp, struct nes_cq *nescq)
>  static int nes_destroy_qp(struct ib_qp *ibqp)
>  {
>  	struct nes_qp *nesqp = to_nesqp(ibqp);
> +	struct nes_cq *scq;
> +	struct nes_cq *rcq;
>  	struct nes_ucontext *nes_ucontext;
>  	struct ib_qp_attr attr;
>  	struct iw_cm_id *cm_id;
> @@ -1471,6 +1586,39 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
>  	atomic_inc(&sw_qps_destroyed);
>  	nesqp->destroyed = 1;
>  
> +	if (nesqp->ibqp.qp_type == IB_QPT_RAW_ETY) {
> +		/* check the QP refernece count */
> +		if (atomic_read(&nesqp->refcount) == 0)
> +			BUG();
> +		if (atomic_dec_and_test(&nesqp->refcount)) {
> +			/* destroy send and rcv  QPs */
> +			if (nesqp->rx_ud_wq)
> +				nes_ud_destroy_wq(nesqp->rx_ud_wq);
> +			nesqp->rx_ud_wq = 0;
> +
> +			if (nesqp->tx_ud_wq)
> +				nes_ud_destroy_wq(nesqp->tx_ud_wq);
> +			nesqp->tx_ud_wq = 0;
> +			atomic_inc(&qps_destroyed);
> +
> +			/* to prevent the destroy of cq before QP
> +			destroy the usecount is used */
> +			if (ibqp->send_cq) {
> +				scq = to_nescq(ibqp->send_cq);
> +				atomic_inc(&ibqp->send_cq->usecnt);
> +				atomic_dec(&scq->usecnt);
> +			}
> +			if (ibqp->recv_cq) {
> +				rcq = to_nescq(ibqp->recv_cq);
> +				atomic_inc(&ibqp->recv_cq->usecnt);
> +				atomic_dec(&rcq->usecnt);
> +			}
> +			/* free memory for the qp */
> +			kfree(nesqp->allocated_buffer);
> +		}
> +		return 0;
> +	}
> +
>  	/* Blow away the connection if it exists. */
>  	if (nesqp->ibqp_state >= IB_QPS_INIT && nesqp->ibqp_state <= IB_QPS_RTS) {
>  		/* if (nesqp->ibqp_state == IB_QPS_RTS) { */
> @@ -1567,9 +1715,18 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> +	/* to make sure that RAW ETH cq will be not destoyed
> +	without qp destroy the internal usecount is used
> +	 the ibcq usecount cannot be used because the  RAW ETH makes
> +	recreation of the CQs after QP creation
> +	 when this situation occured (mcrqf != 0) the usecount is increase
> +	 the ibcq usecount is cleared after successfull CQ creation */
> +	atomic_set(&nescq->usecnt, 0);
> +
>  	nescq->hw_cq.cq_size = max(entries + 1, 5);
>  	nescq->hw_cq.cq_number = cq_num;
>  	nescq->ibcq.cqe = nescq->hw_cq.cq_size - 1;
> +	nescq->mcrqf = 0;
>  
>  
>  	if (context) {
> @@ -1586,8 +1743,23 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
>  				nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 28 + 2 * ((nes_ucontext->mcrqf & 0xf) - 1);
>  			else if (nes_ucontext->mcrqf & 0x40000000)
>  				nescq->hw_cq.cq_number = nes_ucontext->mcrqf & 0xffff;
> +			else if (nes_ucontext->mcrqf & 0x20000000) {
> +				/* the cq number is coded
> +						adapter:4/nic:4/cq_num:8 */
> +				nescq->hw_cq.cq_number =
> +						nes_ucontext->mcrqf & 0x00ff;
> +
> +				/* to prevent the cq destroy before qp destroy
> +				the internal usecount is increased
> +				in this place it is the  RAW ETH specific CQ
> +				(after re-creation)
> +				only  RAW ETH type QP destroy can decrease
> +				this usecounter */
> +				atomic_inc(&nescq->usecnt);
> +			}
>  			else
>  				nescq->hw_cq.cq_number = nesvnic->mcrq_qp_id + nes_ucontext->mcrqf-1;
> +
>  			nescq->mcrqf = nes_ucontext->mcrqf;
>  			nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
>  		}
> @@ -1776,6 +1948,10 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
>  			kfree(nescq);
>  			return ERR_PTR(-EFAULT);
>  		}
> +		if (nes_ucontext->mcrqf & 0x20000000) {
> +			/* change the cq address only for  RAW in QP pointer */
> +			nes_ud_cq_replace(nesvnic, nescq);
> +		}
>  	}
>  
>  	return &nescq->ibcq;
> @@ -1805,6 +1981,11 @@ static int nes_destroy_cq(struct ib_cq *ib_cq)
>  	nesdev = nesvnic->nesdev;
>  	nesadapter = nesdev->nesadapter;
>  
> +	if (atomic_read(&nescq->usecnt) != 0) {
> +		nes_debug(NES_DBG_CQ, "CQ is in use now. %d\n",
> +				(int) atomic_read(&nescq->usecnt));
> +		return -EBUSY;
> +	}
>  	nes_debug(NES_DBG_CQ, "Destroy CQ%u\n", nescq->hw_cq.cq_number);
>  
>  	/* Send DestroyCQ request to CQP */
> @@ -2540,6 +2721,13 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
>  				nesmr->ibmr.lkey = stag;
>  				nesmr->mode = IWNES_MEMREG_TYPE_MEM;
>  				ibmr = &nesmr->ibmr;
> +				/* register memory parallelly for RAW ETH */
> +				if (nes_ud_reg_mr(region, length,
> +						virt, stag) == 0) {
> +					ib_umem_release(region);
> +					kfree(nesmr);
> +					ibmr = ERR_PTR(-ENOMEM);
> +				}
>  			} else {
>  				ib_umem_release(region);
>  				kfree(nesmr);
> @@ -2733,6 +2921,9 @@ static int nes_dereg_mr(struct ib_mr *ib_mr)
>  	}
>  	nes_free_resource(nesadapter, nesadapter->allocated_mrs,
>  			(ib_mr->rkey & 0x0fffff00) >> 8);
> +	ret = nes_ud_dereg_mr(ib_mr->rkey);
> +	if (ret != 0)
> +		return ret;
>  
>  	kfree(nesmr);
>  
> @@ -2939,6 +3130,9 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
>  			nesqp->hwqp.qp_id, attr->qp_state, nesqp->ibqp_state,
>  			nesqp->iwarp_state, atomic_read(&nesqp->refcount));
>  
> +	if (ibqp->qp_type == IB_QPT_RAW_ETY)
> +		return 0;
> +
>  	spin_lock_irqsave(&nesqp->lock, qplockflags);
>  
>  	nes_debug(NES_DBG_MOD_QP, "QP%u: hw_iwarp_state=0x%X, hw_tcp_state=0x%X,"
> @@ -3208,8 +3402,10 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
>   */
>  static int nes_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
>  {
> -	nes_debug(NES_DBG_INIT, "\n");
> -	return -ENOSYS;
> +	int ret =  -ENOSYS;
> +	struct nes_qp *nesqp = to_nesqp(ibqp);
> +	ret =  nes_ud_subscribe_mcast(nesqp->rx_ud_wq, gid);
> +	return ret;
>  }
>  
>  
> @@ -3218,8 +3414,10 @@ static int nes_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
>   */
>  static int nes_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
>  {
> -	nes_debug(NES_DBG_INIT, "\n");
> -	return -ENOSYS;
> +	int ret =  -ENOSYS;
> +	struct nes_qp *nesqp = to_nesqp(ibqp);
> +	ret =  nes_ud_unsubscribe_mcast(nesqp->rx_ud_wq, gid);
> +	return ret;
>  }
>  
>  
> @@ -3846,6 +4044,7 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
>  		return NULL;
>  	}
>  	strlcpy(nesibdev->ibdev.name, "nes%d", IB_DEVICE_NAME_MAX);
> +	strcpy(nesibdev->ibdev.name, netdev->name);
>  	nesibdev->ibdev.owner = THIS_MODULE;
>  
>  	nesibdev->ibdev.node_type = RDMA_NODE_RNIC;
> @@ -3868,6 +4067,9 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
>  			(1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ) |
>  			(1ull << IB_USER_VERBS_CMD_CREATE_QP) |
>  			(1ull << IB_USER_VERBS_CMD_MODIFY_QP) |
> +			(1ull << IB_USER_VERBS_CMD_QUERY_QP) |
> +			(1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) |
> +			(1ull << IB_USER_VERBS_CMD_DETACH_MCAST) |
>  			(1ull << IB_USER_VERBS_CMD_POLL_CQ) |
>  			(1ull << IB_USER_VERBS_CMD_DESTROY_QP) |
>  			(1ull << IB_USER_VERBS_CMD_ALLOC_MW) |
> @@ -3911,8 +4113,9 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
>  	nesibdev->ibdev.alloc_fast_reg_page_list = nes_alloc_fast_reg_page_list;
>  	nesibdev->ibdev.free_fast_reg_page_list = nes_free_fast_reg_page_list;
>  
> -	nesibdev->ibdev.attach_mcast = nes_multicast_attach;
>  	nesibdev->ibdev.detach_mcast = nes_multicast_detach;
> +	nesibdev->ibdev.attach_mcast = nes_multicast_attach;
> +
>  	nesibdev->ibdev.process_mad = nes_process_mad;
>  
>  	nesibdev->ibdev.req_notify_cq = nes_req_notify_cq;
> diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
> index 2df9993..cbb6585 100644
> --- a/drivers/infiniband/hw/nes/nes_verbs.h
> +++ b/drivers/infiniband/hw/nes/nes_verbs.h
> @@ -79,6 +79,7 @@ struct nes_mr {
>  	u16               pbls_used;
>  	u8                mode;
>  	u8                pbl_4k;
> +	u32               mcrqf;
>  };
>  
>  struct nes_hw_pb {
> @@ -116,7 +117,8 @@ struct nes_cq {
>  	spinlock_t       lock;
>  	u8               virtual_cq;
>  	u8               pad[3];
> -	u32		 mcrqf;
> +	atomic_t         usecnt;
> +	u32              mcrqf;
>  };
>  
>  struct nes_wq {
> @@ -130,6 +132,7 @@ struct disconn_work {
>  
>  struct iw_cm_id;
>  struct ietf_mpa_frame;
> +struct nes_ud_file;
>  
>  struct nes_qp {
>  	struct ib_qp          ibqp;
> @@ -176,5 +179,7 @@ struct nes_qp {
>  	u8                    hw_tcp_state;
>  	u8                    term_flags;
>  	u8                    sq_kmapped;
> +	struct nes_ud_file    *rx_ud_wq;
> +	struct nes_ud_file    *tx_ud_wq;
>  };
>  #endif			/* NES_VERBS_H */
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>   

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  parent reply	other threads:[~2010-05-04 17:19 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-04-30 16:53 [PATCH 0/2] RDMA/core: add support for iWarp multicast acceleration over IB_QPT_RAW_ETY QP type Mirek Walukiewicz
     [not found] ` <20100430165249.1386.73960.stgit-dAdtdUp2yJRU7keBU/FxOFDQ4js95KgL@public.gmane.org>
2010-04-30 16:54   ` [PATCH 1/2] " miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w
     [not found]     ` <20100430165348.1386.77503.stgit-dAdtdUp2yJRU7keBU/FxOFDQ4js95KgL@public.gmane.org>
2010-05-10 15:51       ` Steve Wise
2010-04-30 16:55   ` [PATCH 2/2] RDMA/nes: add support of iWARP " miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w
     [not found]     ` <20100430165434.1386.80375.stgit-dAdtdUp2yJRU7keBU/FxOFDQ4js95KgL@public.gmane.org>
2010-05-04 17:19       ` Steve Wise [this message]
     [not found]         ` <4BE05713.6030101-7bPotxP6k4+P2YhJcF5u+vpXobYPEAuW@public.gmane.org>
2010-05-04 18:09           ` Walukiewicz, Miroslaw
     [not found]             ` <BE2BFE91933D1B4089447C64486040801B6784ED-IGOiFh9zz4wLt2AQoY/u9bfspsVTdybXVpNB7YpNyf8@public.gmane.org>
2010-05-04 18:15               ` Steve Wise
     [not found]                 ` <4BE06425.6000104-7bPotxP6k4+P2YhJcF5u+vpXobYPEAuW@public.gmane.org>
2010-05-05 13:42                   ` Walukiewicz, Miroslaw
     [not found]                     ` <BE2BFE91933D1B4089447C64486040801D00D549-IGOiFh9zz4wLt2AQoY/u9bfspsVTdybXVpNB7YpNyf8@public.gmane.org>
2010-05-05 14:56                       ` Steve Wise
     [not found]                         ` <4BE18710.9090304-7bPotxP6k4+P2YhJcF5u+vpXobYPEAuW@public.gmane.org>
2010-05-06 16:27                           ` Walukiewicz, Miroslaw
2010-05-03 14:35   ` [PATCH 0/2] RDMA/core: add support for iWarp " Steve Wise

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4BE05713.6030101@opengridcomputing.com \
    --to=swise-7bpotxp6k4+p2yhjcf5u+vpxobypeauw@public.gmane.org \
    --cc=linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=miroslaw.walukiewicz-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
    --cc=rdreier-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox