Netdev List
 help / color / mirror / Atom feed
* [patch 06/11] [PATCH] af_iucv: add HiperSockets transport
From: frank.blaschka @ 2011-07-27 16:12 UTC (permalink / raw)
  To: netdev, linux-s390; +Cc: Ursula Braun
In-Reply-To: <20110727161242.018577444@de.ibm.com>

[-- Attachment #1: net1117-af_iucv-add-HiperSockets-transport.patch --]
[-- Type: text/plain, Size: 29293 bytes --]

From: Ursula Braun <ursula.braun@de.ibm.com>

The current transport mechanism for af_iucv is the z/VM offered
communications facility IUCV. To provide equivalent support when
running Linux in an LPAR, HiperSockets transport is added to the
AF_IUCV address family. It requires explicit binding of an AF_IUCV
socket to a HiperSockets device. A new packet_type ETH_P_AF_IUCV
is announced. An af_iucv specific transport header is defined
preceding the skb data. A small protocol is implemented for
connecting and for flow control/congestion management.

Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
---
 include/net/iucv/af_iucv.h |   52 +++
 net/iucv/af_iucv.c         |  743 ++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 724 insertions(+), 71 deletions(-)

--- a/include/net/iucv/af_iucv.h
+++ b/include/net/iucv/af_iucv.h
@@ -14,6 +14,7 @@
 #include <linux/list.h>
 #include <linux/poll.h>
 #include <linux/socket.h>
+#include <net/iucv/iucv.h>
 
 #ifndef AF_IUCV
 #define AF_IUCV		32
@@ -33,6 +34,7 @@ enum {
 };
 
 #define IUCV_QUEUELEN_DEFAULT	65535
+#define IUCV_HIPER_MSGLIM_DEFAULT	128
 #define IUCV_CONN_TIMEOUT	(HZ * 40)
 #define IUCV_DISCONN_TIMEOUT	(HZ * 2)
 #define IUCV_CONN_IDLE_TIMEOUT	(HZ * 60)
@@ -57,8 +59,51 @@ struct sock_msg_q {
 	spinlock_t		lock;
 };
 
+#define AF_IUCV_FLAG_ACK 0x1
+#define AF_IUCV_FLAG_SYN 0x2
+#define AF_IUCV_FLAG_FIN 0x4
+#define AF_IUCV_FLAG_WIN 0x8
+
+struct af_iucv_trans_hdr {
+	u16 magic;
+	u8 version;
+	u8 flags;
+	u16 window;
+	char destNodeID[8];
+	char destUserID[8];
+	char destAppName[16];
+	char srcNodeID[8];
+	char srcUserID[8];
+	char srcAppName[16];             /* => 70 bytes */
+	struct iucv_message iucv_hdr;    /* => 33 bytes */
+	u8 pad;                          /* total 104 bytes */
+} __packed;
+
+enum iucv_tx_notify {
+	/* transmission of skb is completed and was successful */
+	TX_NOTIFY_OK = 0,
+	/* target is unreachable */
+	TX_NOTIFY_UNREACHABLE = 1,
+	/* transfer pending queue full */
+	TX_NOTIFY_TPQFULL = 2,
+	/* general error */
+	TX_NOTIFY_GENERALERROR = 3,
+	/* transmission of skb is pending - may interleave
+	 * with TX_NOTIFY_DELAYED_* */
+	TX_NOTIFY_PENDING = 4,
+	/* transmission of skb was done successfully (delayed) */
+	TX_NOTIFY_DELAYED_OK = 5,
+	/* target unreachable (detected delayed) */
+	TX_NOTIFY_DELAYED_UNREACHABLE = 6,
+	/* general error (detected delayed) */
+	TX_NOTIFY_DELAYED_GENERALERROR = 7,
+};
+
 #define iucv_sk(__sk) ((struct iucv_sock *) __sk)
 
+#define AF_IUCV_TRANS_IUCV 0
+#define AF_IUCV_TRANS_HIPER 1
+
 struct iucv_sock {
 	struct sock		sk;
 	char			src_user_id[8];
@@ -75,6 +120,13 @@ struct iucv_sock {
 	unsigned int		send_tag;
 	u8			flags;
 	u16			msglimit;
+	u16			msglimit_peer;
+	atomic_t		msg_sent;
+	atomic_t		msg_recv;
+	atomic_t		pendings;
+	int			transport;
+	void                    (*sk_txnotify)(struct sk_buff *skb,
+					       enum iucv_tx_notify n);
 };
 
 /* iucv socket options (SOL_IUCV) */
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -27,7 +27,6 @@
 #include <asm/cpcmd.h>
 #include <linux/kmod.h>
 
-#include <net/iucv/iucv.h>
 #include <net/iucv/af_iucv.h>
 
 #define VERSION "1.1"
@@ -92,6 +91,12 @@ do {									\
 static void iucv_sock_kill(struct sock *sk);
 static void iucv_sock_close(struct sock *sk);
 
+static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
+	struct packet_type *pt, struct net_device *orig_dev);
+static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
+		   struct sk_buff *skb, u8 flags);
+static void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify);
+
 /* Call Back functions */
 static void iucv_callback_rx(struct iucv_path *, struct iucv_message *);
 static void iucv_callback_txdone(struct iucv_path *, struct iucv_message *);
@@ -296,7 +301,11 @@ static inline int iucv_below_msglim(stru
 
 	if (sk->sk_state != IUCV_CONNECTED)
 		return 1;
-	return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim);
+	if (iucv->transport == AF_IUCV_TRANS_IUCV)
+		return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim);
+	else
+		return ((atomic_read(&iucv->msg_sent) < iucv->msglimit_peer) &&
+			(atomic_read(&iucv->pendings) <= 0));
 }
 
 /**
@@ -314,6 +323,79 @@ static void iucv_sock_wake_msglim(struct
 	rcu_read_unlock();
 }
 
+/**
+ * afiucv_hs_send() - send a message through HiperSockets transport
+ */
+static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
+		   struct sk_buff *skb, u8 flags)
+{
+	struct net *net = sock_net(sock);
+	struct iucv_sock *iucv = iucv_sk(sock);
+	struct af_iucv_trans_hdr *phs_hdr;
+	struct sk_buff *nskb;
+	int err, confirm_recv = 0;
+
+	memset(skb->head, 0, ETH_HLEN);
+	phs_hdr = (struct af_iucv_trans_hdr *)skb_push(skb,
+					sizeof(struct af_iucv_trans_hdr));
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_push(skb, ETH_HLEN);
+	skb_reset_mac_header(skb);
+	memset(phs_hdr, 0, sizeof(struct af_iucv_trans_hdr));
+
+	phs_hdr->magic = ETH_P_AF_IUCV;
+	phs_hdr->version = 1;
+	phs_hdr->flags = flags;
+	if (flags == AF_IUCV_FLAG_SYN)
+		phs_hdr->window = iucv->msglimit;
+	else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
+		confirm_recv = atomic_read(&iucv->msg_recv);
+		phs_hdr->window = confirm_recv;
+		if (confirm_recv)
+			phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
+	}
+	memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
+	memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
+	memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
+	memcpy(phs_hdr->srcAppName, iucv->src_name, 8);
+	ASCEBC(phs_hdr->destUserID, sizeof(phs_hdr->destUserID));
+	ASCEBC(phs_hdr->destAppName, sizeof(phs_hdr->destAppName));
+	ASCEBC(phs_hdr->srcUserID, sizeof(phs_hdr->srcUserID));
+	ASCEBC(phs_hdr->srcAppName, sizeof(phs_hdr->srcAppName));
+	if (imsg)
+		memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
+
+	rcu_read_lock();
+	skb->dev = dev_get_by_index_rcu(net, sock->sk_bound_dev_if);
+	rcu_read_unlock();
+	if (!skb->dev)
+		return -ENODEV;
+	if (!(skb->dev->flags & IFF_UP))
+		return -ENETDOWN;
+	if (skb->len > skb->dev->mtu) {
+		if (sock->sk_type == SOCK_SEQPACKET)
+			return -EMSGSIZE;
+		else
+			skb_trim(skb, skb->dev->mtu);
+	}
+	skb->protocol = ETH_P_AF_IUCV;
+	skb_shinfo(skb)->tx_flags |= SKBTX_DRV_NEEDS_SK_REF;
+	nskb = skb_clone(skb, GFP_ATOMIC);
+	if (!nskb)
+		return -ENOMEM;
+	skb_queue_tail(&iucv->send_skb_q, nskb);
+	err = dev_queue_xmit(skb);
+	if (err) {
+		skb_unlink(nskb, &iucv->send_skb_q);
+		kfree_skb(nskb);
+	} else {
+		atomic_sub(confirm_recv, &iucv->msg_recv);
+		WARN_ON(atomic_read(&iucv->msg_recv) < 0);
+	}
+	return err;
+}
+
 /* Timers */
 static void iucv_sock_timeout(unsigned long arg)
 {
@@ -382,6 +464,8 @@ static void iucv_sock_close(struct sock 
 	unsigned char user_data[16];
 	struct iucv_sock *iucv = iucv_sk(sk);
 	unsigned long timeo;
+	int err, blen;
+	struct sk_buff *skb;
 
 	iucv_sock_clear_timer(sk);
 	lock_sock(sk);
@@ -392,6 +476,20 @@ static void iucv_sock_close(struct sock 
 		break;
 
 	case IUCV_CONNECTED:
+		if (iucv->transport == AF_IUCV_TRANS_HIPER) {
+			/* send fin */
+			blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
+			skb = sock_alloc_send_skb(sk, blen, 1, &err);
+			if (skb) {
+				skb_reserve(skb,
+					sizeof(struct af_iucv_trans_hdr) +
+					ETH_HLEN);
+				err = afiucv_hs_send(NULL, sk, skb,
+						     AF_IUCV_FLAG_FIN);
+			}
+			sk->sk_state = IUCV_DISCONN;
+			sk->sk_state_change(sk);
+		}
 	case IUCV_DISCONN:
 		sk->sk_state = IUCV_CLOSING;
 		sk->sk_state_change(sk);
@@ -461,9 +559,13 @@ static struct sock *iucv_sock_alloc(stru
 	spin_lock_init(&iucv->message_q.lock);
 	skb_queue_head_init(&iucv->backlog_skb_q);
 	iucv->send_tag = 0;
+	atomic_set(&iucv->pendings, 0);
 	iucv->flags = 0;
-	iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
+	iucv->msglimit = 0;
+	atomic_set(&iucv->msg_sent, 0);
+	atomic_set(&iucv->msg_recv, 0);
 	iucv->path = NULL;
+	iucv->sk_txnotify = afiucv_hs_callback_txnotify;
 	memset(&iucv->src_user_id , 0, 32);
 
 	sk->sk_destruct = iucv_sock_destruct;
@@ -595,7 +697,9 @@ static int iucv_sock_bind(struct socket 
 	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
 	struct sock *sk = sock->sk;
 	struct iucv_sock *iucv;
-	int err;
+	int err = 0;
+	struct net_device *dev;
+	char uid[9];
 
 	/* Verify the input sockaddr */
 	if (!addr || addr->sa_family != AF_IUCV)
@@ -614,19 +718,46 @@ static int iucv_sock_bind(struct socket 
 		err = -EADDRINUSE;
 		goto done_unlock;
 	}
-	if (iucv->path) {
-		err = 0;
+	if (iucv->path)
 		goto done_unlock;
-	}
 
 	/* Bind the socket */
-	memcpy(iucv->src_name, sa->siucv_name, 8);
 
-	/* Copy the user id */
-	memcpy(iucv->src_user_id, iucv_userid, 8);
-	sk->sk_state = IUCV_BOUND;
-	err = 0;
+	if (pr_iucv)
+		if (!memcmp(sa->siucv_user_id, iucv_userid, 8))
+			goto vm_bind; /* VM IUCV transport */
 
+	/* try hiper transport */
+	memcpy(uid, sa->siucv_user_id, sizeof(uid));
+	ASCEBC(uid, 8);
+	rcu_read_lock();
+	for_each_netdev_rcu(&init_net, dev) {
+		if (!memcmp(dev->perm_addr, uid, 8)) {
+			memcpy(iucv->src_name, sa->siucv_name, 8);
+			memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
+			sock->sk->sk_bound_dev_if = dev->ifindex;
+			sk->sk_state = IUCV_BOUND;
+			iucv->transport = AF_IUCV_TRANS_HIPER;
+			if (!iucv->msglimit)
+				iucv->msglimit = IUCV_HIPER_MSGLIM_DEFAULT;
+			rcu_read_unlock();
+			goto done_unlock;
+		}
+	}
+	rcu_read_unlock();
+vm_bind:
+	if (pr_iucv) {
+		/* use local userid for backward compat */
+		memcpy(iucv->src_name, sa->siucv_name, 8);
+		memcpy(iucv->src_user_id, iucv_userid, 8);
+		sk->sk_state = IUCV_BOUND;
+		iucv->transport = AF_IUCV_TRANS_IUCV;
+		if (!iucv->msglimit)
+			iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
+		goto done_unlock;
+	}
+	/* found no dev to bind */
+	err = -ENODEV;
 done_unlock:
 	/* Release the socket list lock */
 	write_unlock_bh(&iucv_sk_list.lock);
@@ -662,12 +793,33 @@ static int iucv_sock_autobind(struct soc
 
 	memcpy(&iucv->src_name, name, 8);
 
+	if (!iucv->msglimit)
+		iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
+
 	return err;
 }
 
-/* Connect an unconnected socket */
-static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
-			     int alen, int flags)
+static int afiucv_hs_connect(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
+	int err = 0;
+
+	/* send syn */
+	skb = sock_alloc_send_skb(sk, blen, 1, &err);
+	if (!skb) {
+		err = -ENOMEM;
+		goto done;
+	}
+	skb->dev = NULL;
+	skb_reserve(skb, blen);
+	err = afiucv_hs_send(NULL, sk, skb, AF_IUCV_FLAG_SYN);
+done:
+	return err;
+}
+
+static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
 {
 	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
 	struct sock *sk = sock->sk;
@@ -675,27 +827,6 @@ static int iucv_sock_connect(struct sock
 	unsigned char user_data[16];
 	int err;
 
-	if (addr->sa_family != AF_IUCV || alen < sizeof(struct sockaddr_iucv))
-		return -EINVAL;
-
-	if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND)
-		return -EBADFD;
-
-	if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET)
-		return -EINVAL;
-
-	if (sk->sk_state == IUCV_OPEN) {
-		err = iucv_sock_autobind(sk);
-		if (unlikely(err))
-			return err;
-	}
-
-	lock_sock(sk);
-
-	/* Set the destination information */
-	memcpy(iucv->dst_user_id, sa->siucv_user_id, 8);
-	memcpy(iucv->dst_name, sa->siucv_name, 8);
-
 	high_nmcpy(user_data, sa->siucv_name);
 	low_nmcpy(user_data, iucv->src_name);
 	ASCEBC(user_data, sizeof(user_data));
@@ -728,20 +859,61 @@ static int iucv_sock_connect(struct sock
 			err = -ECONNREFUSED;
 			break;
 		}
-		goto done;
+	}
+done:
+	return err;
+}
+
+/* Connect an unconnected socket */
+static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
+			     int alen, int flags)
+{
+	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
+	struct sock *sk = sock->sk;
+	struct iucv_sock *iucv = iucv_sk(sk);
+	int err;
+
+	if (addr->sa_family != AF_IUCV || alen < sizeof(struct sockaddr_iucv))
+		return -EINVAL;
+
+	if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND)
+		return -EBADFD;
+
+	if (sk->sk_state == IUCV_OPEN &&
+	    iucv->transport == AF_IUCV_TRANS_HIPER)
+		return -EBADFD; /* explicit bind required */
+
+	if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET)
+		return -EINVAL;
+
+	if (sk->sk_state == IUCV_OPEN) {
+		err = iucv_sock_autobind(sk);
+		if (unlikely(err))
+			return err;
 	}
 
-	if (sk->sk_state != IUCV_CONNECTED) {
+	lock_sock(sk);
+
+	/* Set the destination information */
+	memcpy(iucv->dst_user_id, sa->siucv_user_id, 8);
+	memcpy(iucv->dst_name, sa->siucv_name, 8);
+
+	if (iucv->transport == AF_IUCV_TRANS_HIPER)
+		err = afiucv_hs_connect(sock);
+	else
+		err = afiucv_path_connect(sock, addr);
+	if (err)
+		goto done;
+
+	if (sk->sk_state != IUCV_CONNECTED)
 		err = iucv_sock_wait(sk, iucv_sock_in_state(sk, IUCV_CONNECTED,
 							    IUCV_DISCONN),
 				     sock_sndtimeo(sk, flags & O_NONBLOCK));
-	}
 
-	if (sk->sk_state == IUCV_DISCONN) {
+	if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED)
 		err = -ECONNREFUSED;
-	}
 
-	if (err) {
+	if (err && iucv->transport == AF_IUCV_TRANS_IUCV) {
 		pr_iucv->path_sever(iucv->path, NULL);
 		iucv_path_free(iucv->path);
 		iucv->path = NULL;
@@ -965,9 +1137,16 @@ static int iucv_sock_sendmsg(struct kioc
 	 * this is fine for SOCK_SEQPACKET (unless we want to support
 	 * segmented records using the MSG_EOR flag), but
 	 * for SOCK_STREAM we might want to improve it in future */
-	skb = sock_alloc_send_skb(sk, len, noblock, &err);
+	if (iucv->transport == AF_IUCV_TRANS_HIPER)
+		skb = sock_alloc_send_skb(sk,
+			len + sizeof(struct af_iucv_trans_hdr) + ETH_HLEN,
+			noblock, &err);
+	else
+		skb = sock_alloc_send_skb(sk, len, noblock, &err);
 	if (!skb)
 		goto out;
+	if (iucv->transport == AF_IUCV_TRANS_HIPER)
+		skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN);
 	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
 		err = -EFAULT;
 		goto fail;
@@ -988,6 +1167,15 @@ static int iucv_sock_sendmsg(struct kioc
 	/* increment and save iucv message tag for msg_completion cbk */
 	txmsg.tag = iucv->send_tag++;
 	memcpy(CB_TAG(skb), &txmsg.tag, CB_TAG_LEN);
+	if (iucv->transport == AF_IUCV_TRANS_HIPER) {
+		atomic_inc(&iucv->msg_sent);
+		err = afiucv_hs_send(&txmsg, sk, skb, 0);
+		if (err) {
+			atomic_dec(&iucv->msg_sent);
+			goto fail;
+		}
+		goto release;
+	}
 	skb_queue_tail(&iucv->send_skb_q, skb);
 
 	if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags)
@@ -1028,6 +1216,7 @@ static int iucv_sock_sendmsg(struct kioc
 		goto fail;
 	}
 
+release:
 	release_sock(sk);
 	return len;
 
@@ -1160,7 +1349,8 @@ static int iucv_sock_recvmsg(struct kioc
 	struct sock *sk = sock->sk;
 	struct iucv_sock *iucv = iucv_sk(sk);
 	unsigned int copied, rlen;
-	struct sk_buff *skb, *rskb, *cskb;
+	struct sk_buff *skb, *rskb, *cskb, *sskb;
+	int blen;
 	int err = 0;
 
 	if ((sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_SEVERED) &&
@@ -1185,7 +1375,7 @@ static int iucv_sock_recvmsg(struct kioc
 	copied = min_t(unsigned int, rlen, len);
 
 	cskb = skb;
-	if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) {
+	if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
 		if (!(flags & MSG_PEEK))
 			skb_queue_head(&sk->sk_receive_queue, skb);
 		return -EFAULT;
@@ -1223,6 +1413,7 @@ static int iucv_sock_recvmsg(struct kioc
 		}
 
 		kfree_skb(skb);
+		atomic_inc(&iucv->msg_recv);
 
 		/* Queue backlog skbs */
 		spin_lock_bh(&iucv->message_q.lock);
@@ -1239,6 +1430,24 @@ static int iucv_sock_recvmsg(struct kioc
 		if (skb_queue_empty(&iucv->backlog_skb_q)) {
 			if (!list_empty(&iucv->message_q.list))
 				iucv_process_message_q(sk);
+			if (atomic_read(&iucv->msg_recv) >=
+							iucv->msglimit / 2) {
+				/* send WIN to peer */
+				blen = sizeof(struct af_iucv_trans_hdr) +
+					ETH_HLEN;
+				sskb = sock_alloc_send_skb(sk, blen, 1, &err);
+				if (sskb) {
+					skb_reserve(sskb,
+						sizeof(struct af_iucv_trans_hdr)
+						+ ETH_HLEN);
+					err = afiucv_hs_send(NULL, sk, sskb,
+							     AF_IUCV_FLAG_WIN);
+				}
+				if (err) {
+					sk->sk_state = IUCV_DISCONN;
+					sk->sk_state_change(sk);
+				}
+			}
 		}
 		spin_unlock_bh(&iucv->message_q.lock);
 	}
@@ -1698,6 +1907,389 @@ static void iucv_callback_shutdown(struc
 	bh_unlock_sock(sk);
 }
 
+/***************** HiperSockets transport callbacks ********************/
+static void afiucv_swap_src_dest(struct sk_buff *skb)
+{
+	struct af_iucv_trans_hdr *trans_hdr =
+				(struct af_iucv_trans_hdr *)skb->data;
+	char tmpID[8];
+	char tmpName[8];
+
+	ASCEBC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
+	ASCEBC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
+	ASCEBC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
+	ASCEBC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
+	memcpy(tmpID, trans_hdr->srcUserID, 8);
+	memcpy(tmpName, trans_hdr->srcAppName, 8);
+	memcpy(trans_hdr->srcUserID, trans_hdr->destUserID, 8);
+	memcpy(trans_hdr->srcAppName, trans_hdr->destAppName, 8);
+	memcpy(trans_hdr->destUserID, tmpID, 8);
+	memcpy(trans_hdr->destAppName, tmpName, 8);
+	skb_push(skb, ETH_HLEN);
+	memset(skb->data, 0, ETH_HLEN);
+}
+
+/**
+ * afiucv_hs_callback_syn - react on received SYN
+ **/
+static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
+{
+	struct sock *nsk;
+	struct iucv_sock *iucv, *niucv;
+	struct af_iucv_trans_hdr *trans_hdr;
+	int err;
+
+	iucv = iucv_sk(sk);
+	trans_hdr = (struct af_iucv_trans_hdr *)skb->data;
+	if (!iucv) {
+		/* no sock - connection refused */
+		afiucv_swap_src_dest(skb);
+		trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
+		err = dev_queue_xmit(skb);
+		goto out;
+	}
+
+	nsk = iucv_sock_alloc(NULL, sk->sk_type, GFP_ATOMIC);
+	bh_lock_sock(sk);
+	if ((sk->sk_state != IUCV_LISTEN) ||
+	    sk_acceptq_is_full(sk) ||
+	    !nsk) {
+		/* error on server socket - connection refused */
+		if (nsk)
+			sk_free(nsk);
+		afiucv_swap_src_dest(skb);
+		trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
+		err = dev_queue_xmit(skb);
+		bh_unlock_sock(sk);
+		goto out;
+	}
+
+	niucv = iucv_sk(nsk);
+	iucv_sock_init(nsk, sk);
+	niucv->transport = AF_IUCV_TRANS_HIPER;
+	niucv->msglimit = iucv->msglimit;
+	if (!trans_hdr->window)
+		niucv->msglimit_peer = IUCV_HIPER_MSGLIM_DEFAULT;
+	else
+		niucv->msglimit_peer = trans_hdr->window;
+	memcpy(niucv->dst_name, trans_hdr->srcAppName, 8);
+	memcpy(niucv->dst_user_id, trans_hdr->srcUserID, 8);
+	memcpy(niucv->src_name, iucv->src_name, 8);
+	memcpy(niucv->src_user_id, iucv->src_user_id, 8);
+	nsk->sk_bound_dev_if = sk->sk_bound_dev_if;
+	afiucv_swap_src_dest(skb);
+	trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK;
+	trans_hdr->window = niucv->msglimit;
+	/* if receiver acks the xmit connection is established */
+	err = dev_queue_xmit(skb);
+	if (!err) {
+		iucv_accept_enqueue(sk, nsk);
+		nsk->sk_state = IUCV_CONNECTED;
+		sk->sk_data_ready(sk, 1);
+	} else
+		iucv_sock_kill(nsk);
+	bh_unlock_sock(sk);
+
+out:
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_callback_synack() - react on received SYN-ACK
+ **/
+static int afiucv_hs_callback_synack(struct sock *sk, struct sk_buff *skb)
+{
+	struct iucv_sock *iucv = iucv_sk(sk);
+	struct af_iucv_trans_hdr *trans_hdr =
+					(struct af_iucv_trans_hdr *)skb->data;
+
+	if (!iucv)
+		goto out;
+	if (sk->sk_state != IUCV_BOUND)
+		goto out;
+	bh_lock_sock(sk);
+	iucv->msglimit_peer = trans_hdr->window;
+	sk->sk_state = IUCV_CONNECTED;
+	sk->sk_state_change(sk);
+	bh_unlock_sock(sk);
+out:
+	kfree_skb(skb);
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_callback_synfin() - react on received SYN_FIN
+ **/
+static int afiucv_hs_callback_synfin(struct sock *sk, struct sk_buff *skb)
+{
+	struct iucv_sock *iucv = iucv_sk(sk);
+
+	if (!iucv)
+		goto out;
+	if (sk->sk_state != IUCV_BOUND)
+		goto out;
+	bh_lock_sock(sk);
+	sk->sk_state = IUCV_DISCONN;
+	sk->sk_state_change(sk);
+	bh_unlock_sock(sk);
+out:
+	kfree_skb(skb);
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_callback_fin() - react on received FIN
+ **/
+static int afiucv_hs_callback_fin(struct sock *sk, struct sk_buff *skb)
+{
+	struct iucv_sock *iucv = iucv_sk(sk);
+
+	/* other end of connection closed */
+	if (iucv) {
+		bh_lock_sock(sk);
+		if (!list_empty(&iucv->accept_q))
+			sk->sk_state = IUCV_SEVERED;
+		else
+			sk->sk_state = IUCV_DISCONN;
+		sk->sk_state_change(sk);
+		bh_unlock_sock(sk);
+	}
+	kfree_skb(skb);
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_callback_win() - react on received WIN
+ **/
+static int afiucv_hs_callback_win(struct sock *sk, struct sk_buff *skb)
+{
+	struct iucv_sock *iucv = iucv_sk(sk);
+	struct af_iucv_trans_hdr *trans_hdr =
+					(struct af_iucv_trans_hdr *)skb->data;
+
+	if (!iucv)
+		return NET_RX_SUCCESS;
+
+	if (sk->sk_state != IUCV_CONNECTED)
+		return NET_RX_SUCCESS;
+
+	atomic_sub(trans_hdr->window, &iucv->msg_sent);
+	iucv_sock_wake_msglim(sk);
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_callback_rx() - react on received data
+ **/
+static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
+{
+	struct iucv_sock *iucv = iucv_sk(sk);
+
+	if (!iucv) {
+		kfree_skb(skb);
+		return NET_RX_SUCCESS;
+	}
+
+	if (sk->sk_state != IUCV_CONNECTED) {
+		kfree_skb(skb);
+		return NET_RX_SUCCESS;
+	}
+
+		/* write stuff from iucv_msg to skb cb */
+	if (skb->len <= sizeof(struct af_iucv_trans_hdr)) {
+		kfree_skb(skb);
+		return NET_RX_SUCCESS;
+	}
+	skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
+	skb_reset_transport_header(skb);
+	skb_reset_network_header(skb);
+	spin_lock(&iucv->message_q.lock);
+	if (skb_queue_empty(&iucv->backlog_skb_q)) {
+		if (sock_queue_rcv_skb(sk, skb)) {
+			/* handle rcv queue full */
+			skb_queue_tail(&iucv->backlog_skb_q, skb);
+		}
+	} else
+		skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb);
+	spin_unlock(&iucv->message_q.lock);
+	return NET_RX_SUCCESS;
+}
+
+/**
+ * afiucv_hs_rcv() - base function for arriving data through HiperSockets
+ *                   transport
+ *                   called from netif RX softirq
+ **/
+static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
+	struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct hlist_node *node;
+	struct sock *sk;
+	struct iucv_sock *iucv;
+	struct af_iucv_trans_hdr *trans_hdr;
+	char nullstring[8];
+	int err = 0;
+
+	skb_pull(skb, ETH_HLEN);
+	trans_hdr = (struct af_iucv_trans_hdr *)skb->data;
+	EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
+	EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
+	EBCASC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
+	EBCASC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
+	memset(nullstring, 0, sizeof(nullstring));
+	iucv = NULL;
+	sk = NULL;
+	read_lock(&iucv_sk_list.lock);
+	sk_for_each(sk, node, &iucv_sk_list.head) {
+		if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
+			if ((!memcmp(&iucv_sk(sk)->src_name,
+				     trans_hdr->destAppName, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->src_user_id,
+				     trans_hdr->destUserID, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->dst_name, nullstring, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->dst_user_id,
+				     nullstring, 8))) {
+				iucv = iucv_sk(sk);
+				break;
+			}
+		} else {
+			if ((!memcmp(&iucv_sk(sk)->src_name,
+				     trans_hdr->destAppName, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->src_user_id,
+				     trans_hdr->destUserID, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->dst_name,
+				     trans_hdr->srcAppName, 8)) &&
+			    (!memcmp(&iucv_sk(sk)->dst_user_id,
+				     trans_hdr->srcUserID, 8))) {
+				iucv = iucv_sk(sk);
+				break;
+			}
+		}
+	}
+	read_unlock(&iucv_sk_list.lock);
+	if (!iucv)
+		sk = NULL;
+
+	/* no sock
+	how should we send with no sock
+	1) send without sock no send rc checking?
+	2) introduce default sock to handle this cases
+
+	 SYN -> send SYN|ACK in good case, send SYN|FIN in bad case
+	 data -> send FIN
+	 SYN|ACK, SYN|FIN, FIN -> no action? */
+
+	switch (trans_hdr->flags) {
+	case AF_IUCV_FLAG_SYN:
+		/* connect request */
+		err = afiucv_hs_callback_syn(sk, skb);
+		break;
+	case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK):
+		/* connect request confirmed */
+		err = afiucv_hs_callback_synack(sk, skb);
+		break;
+	case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN):
+		/* connect request refused */
+		err = afiucv_hs_callback_synfin(sk, skb);
+		break;
+	case (AF_IUCV_FLAG_FIN):
+		/* close request */
+		err = afiucv_hs_callback_fin(sk, skb);
+		break;
+	case (AF_IUCV_FLAG_WIN):
+		err = afiucv_hs_callback_win(sk, skb);
+		if (skb->len > sizeof(struct af_iucv_trans_hdr))
+			err = afiucv_hs_callback_rx(sk, skb);
+		else
+			kfree(skb);
+		break;
+	case 0:
+		/* plain data frame */
+		err = afiucv_hs_callback_rx(sk, skb);
+		break;
+	default:
+		;
+	}
+
+	return err;
+}
+
+/**
+ * afiucv_hs_callback_txnotify() - handle send notifcations from HiperSockets
+ *                                 transport
+ **/
+static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
+					enum iucv_tx_notify n)
+{
+	struct sock *isk = skb->sk;
+	struct sock *sk = NULL;
+	struct iucv_sock *iucv = NULL;
+	struct sk_buff_head *list;
+	struct sk_buff *list_skb;
+	struct sk_buff *this = NULL;
+	unsigned long flags;
+	struct hlist_node *node;
+
+	read_lock(&iucv_sk_list.lock);
+	sk_for_each(sk, node, &iucv_sk_list.head)
+		if (sk == isk) {
+			iucv = iucv_sk(sk);
+			break;
+		}
+	read_unlock(&iucv_sk_list.lock);
+
+	if (!iucv)
+		return;
+
+	bh_lock_sock(sk);
+	list = &iucv->send_skb_q;
+	list_skb = list->next;
+	if (skb_queue_empty(list))
+		goto out_unlock;
+
+	spin_lock_irqsave(&list->lock, flags);
+	while (list_skb != (struct sk_buff *)list) {
+		if (skb_shinfo(list_skb) == skb_shinfo(skb)) {
+			this = list_skb;
+			switch (n) {
+			case TX_NOTIFY_OK:
+				__skb_unlink(this, list);
+				iucv_sock_wake_msglim(sk);
+				kfree_skb(this);
+				break;
+			case TX_NOTIFY_PENDING:
+				atomic_inc(&iucv->pendings);
+				break;
+			case TX_NOTIFY_DELAYED_OK:
+				__skb_unlink(this, list);
+				atomic_dec(&iucv->pendings);
+				if (atomic_read(&iucv->pendings) <= 0)
+					iucv_sock_wake_msglim(sk);
+				kfree_skb(this);
+				break;
+			case TX_NOTIFY_UNREACHABLE:
+			case TX_NOTIFY_DELAYED_UNREACHABLE:
+			case TX_NOTIFY_TPQFULL: /* not yet used */
+			case TX_NOTIFY_GENERALERROR:
+			case TX_NOTIFY_DELAYED_GENERALERROR:
+				__skb_unlink(this, list);
+				kfree_skb(this);
+				if (!list_empty(&iucv->accept_q))
+					sk->sk_state = IUCV_SEVERED;
+				else
+					sk->sk_state = IUCV_DISCONN;
+				sk->sk_state_change(sk);
+				break;
+			}
+			break;
+		}
+		list_skb = list_skb->next;
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+
+out_unlock:
+	bh_unlock_sock(sk);
+}
 static const struct proto_ops iucv_sock_ops = {
 	.family		= PF_IUCV,
 	.owner		= THIS_MODULE,
@@ -1724,7 +2316,12 @@ static const struct net_proto_family iuc
 	.create	= iucv_sock_create,
 };
 
-static int __init afiucv_iucv_init(void)
+static struct packet_type iucv_packet_type = {
+	.type = cpu_to_be16(ETH_P_AF_IUCV),
+	.func = afiucv_hs_rcv,
+};
+
+static int afiucv_iucv_init(void)
 {
 	int err;
 
@@ -1763,24 +2360,22 @@ static int __init afiucv_init(void)
 {
 	int err;
 
-	if (!MACHINE_IS_VM) {
-		pr_err("The af_iucv module cannot be loaded"
-		       " without z/VM\n");
-		err = -EPROTONOSUPPORT;
-		goto out;
-	}
-	cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
-	if (unlikely(err)) {
-		WARN_ON(err);
-		err = -EPROTONOSUPPORT;
-		goto out;
-	}
+	if (MACHINE_IS_VM) {
+		cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
+		if (unlikely(err)) {
+			WARN_ON(err);
+			err = -EPROTONOSUPPORT;
+			goto out;
+		}
 
-	pr_iucv = try_then_request_module(symbol_get(iucv_if), "iucv");
-	if (!pr_iucv) {
-		printk(KERN_WARNING "iucv_if lookup failed\n");
-		err = -EPROTONOSUPPORT;
-		goto out;
+		pr_iucv = try_then_request_module(symbol_get(iucv_if), "iucv");
+		if (!pr_iucv) {
+			printk(KERN_WARNING "iucv_if lookup failed\n");
+			memset(&iucv_userid, 0, sizeof(iucv_userid));
+		}
+	} else {
+		memset(&iucv_userid, 0, sizeof(iucv_userid));
+		pr_iucv = NULL;
 	}
 
 	err = proto_register(&iucv_proto, 0);
@@ -1790,10 +2385,12 @@ static int __init afiucv_init(void)
 	if (err)
 		goto out_proto;
 
-	err = afiucv_iucv_init();
-	if (err)
-		goto out_sock;
-
+	if (pr_iucv) {
+		err = afiucv_iucv_init();
+		if (err)
+			goto out_sock;
+	}
+	dev_add_pack(&iucv_packet_type);
 	return 0;
 
 out_sock:
@@ -1808,10 +2405,13 @@ out:
 
 static void __exit afiucv_exit(void)
 {
-	device_unregister(af_iucv_dev);
-	driver_unregister(&af_iucv_driver);
-	pr_iucv->iucv_unregister(&af_iucv_handler, 0);
-	symbol_put(iucv_if);
+	if (pr_iucv) {
+		device_unregister(af_iucv_dev);
+		driver_unregister(&af_iucv_driver);
+		pr_iucv->iucv_unregister(&af_iucv_handler, 0);
+		symbol_put(iucv_if);
+	}
+	dev_remove_pack(&iucv_packet_type);
 	sock_unregister(PF_IUCV);
 	proto_unregister(&iucv_proto);
 }
@@ -1824,3 +2424,4 @@ MODULE_DESCRIPTION("IUCV Sockets ver " V
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_NETPROTO(PF_IUCV);
+


^ permalink raw reply

* [patch 11/11] [PATCH] qeth: add support for af_iucv HiperSockets transport
From: frank.blaschka @ 2011-07-27 16:12 UTC (permalink / raw)
  To: netdev, linux-s390; +Cc: Einar Lueck, Ursula Braun
In-Reply-To: <20110727161242.018577444@de.ibm.com>

[-- Attachment #1: netnext_qeth_iucv.patch --]
[-- Type: text/plain, Size: 29304 bytes --]

From: Frank Blaschka <frank.blaschka@de.ibm.com>

This patch extends the HiperSockets device driver to send and receive
af_iucv traffic over HiperSockets transport.
TX: Driver uses new asynchronous delivery of storage blocks to pass
    flow control/congestion information from the HiperSockets microcode
    to the af_iucv socket.
RX: Memory for incoming traffic is preallocated and passed to 
    HiperSockets layer. If receiver is not capable to clean its buffers
    shared with HiperSockets and pass new memory to the HiperSockets
    layer this will cause flow control/congestion events on the
    sender.

Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: Einar Lueck <elelueck@de.ibm.com>
Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
---
 drivers/s390/net/qeth_core.h      |   19 ++-
 drivers/s390/net/qeth_core_main.c |  230 +++++++++++++++++++++++++++++++-------
 drivers/s390/net/qeth_l2_main.c   |    2 
 drivers/s390/net/qeth_l3.h        |    4 
 drivers/s390/net/qeth_l3_main.c   |   87 +++++++++++---
 drivers/s390/net/qeth_l3_sys.c    |  110 ++++++++++++++++++
 6 files changed, 391 insertions(+), 61 deletions(-)

--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -217,6 +217,7 @@ static inline int qeth_is_ipa_enabled(st
  */
 #define QETH_TX_TIMEOUT		100 * HZ
 #define QETH_RCD_TIMEOUT	60 * HZ
+#define QETH_RECLAIM_WORK_TIME	HZ
 #define QETH_HEADER_SIZE	32
 #define QETH_MAX_PORTNO		15
 
@@ -265,6 +266,7 @@ static inline int qeth_is_ipa_enabled(st
 
 /* large receive scatter gather copy break */
 #define QETH_RX_SG_CB (PAGE_SIZE >> 1)
+#define QETH_RX_PULL_LEN 256
 
 struct qeth_hdr_layer3 {
 	__u8  id;
@@ -382,6 +384,16 @@ enum qeth_qdio_buffer_states {
 	QETH_QDIO_BUF_PRIMED,
 	/*
 	 * inbound: not applicable
+	 * outbound: identified to be pending in TPQ
+	 */
+	QETH_QDIO_BUF_PENDING,
+	/*
+	 * inbound: not applicable
+	 * outbound: found in completion queue
+	 */
+	QETH_QDIO_BUF_IN_CQ,
+	/*
+	 * inbound: not applicable
 	 * outbound: handled via transfer pending / completion queue
 	 */
 	QETH_QDIO_BUF_HANDLED_DELAYED,
@@ -409,6 +421,7 @@ struct qeth_qdio_buffer {
 	struct qdio_buffer *buffer;
 	/* the buffer pool entry currently associated to this buffer */
 	struct qeth_buffer_pool_entry *pool_entry;
+	struct sk_buff *rx_skb;
 };
 
 struct qeth_qdio_q {
@@ -674,6 +687,7 @@ struct qeth_card_options {
 	enum qeth_ipa_isolation_modes isolation;
 	int sniffer;
 	enum qeth_cq cq;
+	char hsuid[9];
 };
 
 /*
@@ -771,6 +785,8 @@ struct qeth_card {
 	struct mutex discipline_mutex;
 	struct napi_struct napi;
 	struct qeth_rx rx;
+	struct delayed_work buffer_reclaim_work;
+	int reclaim_index;
 };
 
 struct qeth_card_list_struct {
@@ -836,6 +852,7 @@ int qeth_core_create_device_attributes(s
 void qeth_core_remove_device_attributes(struct device *);
 int qeth_core_create_osn_attributes(struct device *);
 void qeth_core_remove_osn_attributes(struct device *);
+void qeth_buffer_reclaim_work(struct work_struct *);
 
 /* exports for qeth discipline device drivers */
 extern struct qeth_card_list_struct qeth_core_card_list;
@@ -864,7 +881,7 @@ int qeth_check_qdio_errors(struct qeth_c
 		unsigned int, const char *);
 void qeth_queue_input_buffer(struct qeth_card *, int);
 struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
-		struct qdio_buffer *, struct qdio_buffer_element **, int *,
+		struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *,
 		struct qeth_hdr **);
 void qeth_schedule_recovery(struct qeth_card *);
 void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long);
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -21,6 +21,7 @@
 #include <linux/mii.h>
 #include <linux/kthread.h>
 #include <linux/slab.h>
+#include <net/iucv/af_iucv.h>
 
 #include <asm/ebcdic.h>
 #include <asm/io.h>
@@ -58,6 +59,10 @@ static void qeth_setup_ccw(struct qeth_c
 static void qeth_free_buffer_pool(struct qeth_card *);
 static int qeth_qdio_establish(struct qeth_card *);
 static void qeth_free_qdio_buffers(struct qeth_card *);
+static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
+		struct qeth_qdio_out_buffer *buf,
+		enum iucv_tx_notify notification);
+static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf);
 static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
 		struct qeth_qdio_out_buffer *buf,
 		enum qeth_qdio_buffer_states newbufstate);
@@ -204,7 +209,7 @@ static int qeth_alloc_buffer_pool(struct
 
 	QETH_CARD_TEXT(card, 5, "alocpool");
 	for (i = 0; i < card->qdio.init_pool.buf_count; ++i) {
-		pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL);
+		pool_entry = kzalloc(sizeof(*pool_entry), GFP_KERNEL);
 		if (!pool_entry) {
 			qeth_free_buffer_pool(card);
 			return -ENOMEM;
@@ -331,6 +336,30 @@ static inline void qeth_free_cq(struct q
 	card->qdio.out_bufstates = NULL;
 }
 
+static inline enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15,
+	int delayed) {
+	enum iucv_tx_notify n;
+
+	switch (sbalf15) {
+	case 0:
+		n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK;
+		break;
+	case 4:
+	case 16:
+	case 17:
+	case 18:
+		n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE :
+			TX_NOTIFY_UNREACHABLE;
+		break;
+	default:
+		n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR :
+			TX_NOTIFY_GENERALERROR;
+		break;
+	}
+
+	return n;
+}
+
 static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q,
 	int bidx, int forced_cleanup)
 {
@@ -345,6 +374,11 @@ static inline void qeth_cleanup_handled_
 				struct qeth_qdio_out_buffer *f = c;
 				QETH_CARD_TEXT(f->q->card, 5, "fp");
 				QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f);
+				/* release here to avoid interleaving between
+				   outbound tasklet and inbound tasklet
+				   regarding notifications and lifecycle */
+				qeth_release_skbs(c);
+
 				c = f->next_pending;
 				BUG_ON(head->next_pending != f);
 				head->next_pending = c;
@@ -363,6 +397,7 @@ static inline void qeth_qdio_handle_aob(
 		unsigned long phys_aob_addr) {
 	struct qaob *aob;
 	struct qeth_qdio_out_buffer *buffer;
+	enum iucv_tx_notify notification;
 
 	aob = (struct qaob *) phys_to_virt(phys_aob_addr);
 	QETH_CARD_TEXT(card, 5, "haob");
@@ -372,6 +407,22 @@ static inline void qeth_qdio_handle_aob(
 
 	BUG_ON(buffer == NULL);
 
+	if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED,
+			   QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) {
+		notification = TX_NOTIFY_OK;
+	} else {
+		BUG_ON(atomic_read(&buffer->state) != QETH_QDIO_BUF_PENDING);
+
+		atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ);
+		notification = TX_NOTIFY_DELAYED_OK;
+	}
+
+	if (aob->aorc != 0)  {
+		QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc);
+		notification = qeth_compute_cq_notification(aob->aorc, 1);
+	}
+	qeth_notify_skbs(buffer->q, buffer, notification);
+
 	buffer->aob = NULL;
 	qeth_clear_output_buffer(buffer->q, buffer,
 				QETH_QDIO_BUF_HANDLED_DELAYED);
@@ -738,7 +789,7 @@ static int qeth_setup_channel(struct qet
 	QETH_DBF_TEXT(SETUP, 2, "setupch");
 	for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) {
 		channel->iob[cnt].data =
-			kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
+			kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
 		if (channel->iob[cnt].data == NULL)
 			break;
 		channel->iob[cnt].state = BUF_STATE_FREE;
@@ -1033,23 +1084,60 @@ out:
 	return;
 }
 
-static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
+static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
 		struct qeth_qdio_out_buffer *buf,
-		enum qeth_qdio_buffer_states newbufstate)
+		enum iucv_tx_notify notification)
 {
-	int i;
 	struct sk_buff *skb;
 
-	/* is PCI flag set on buffer? */
-	if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ)
-		atomic_dec(&queue->set_pci_flags_count);
+	if (skb_queue_empty(&buf->skb_list))
+		goto out;
+	skb = skb_peek(&buf->skb_list);
+	while (skb) {
+		QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
+		QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb);
+		if (skb->protocol == ETH_P_AF_IUCV) {
+			if (skb->sk) {
+				struct iucv_sock *iucv = iucv_sk(skb->sk);
+				iucv->sk_txnotify(skb, notification);
+			}
+		}
+		if (skb_queue_is_last(&buf->skb_list, skb))
+			skb = NULL;
+		else
+			skb = skb_queue_next(&buf->skb_list, skb);
+	}
+out:
+	return;
+}
+
+static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf)
+{
+	struct sk_buff *skb;
 
 	skb = skb_dequeue(&buf->skb_list);
 	while (skb) {
+		QETH_CARD_TEXT(buf->q->card, 5, "skbr");
+		QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb);
 		atomic_dec(&skb->users);
 		dev_kfree_skb_any(skb);
 		skb = skb_dequeue(&buf->skb_list);
 	}
+}
+
+static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
+		struct qeth_qdio_out_buffer *buf,
+		enum qeth_qdio_buffer_states newbufstate)
+{
+	int i;
+
+	/* is PCI flag set on buffer? */
+	if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ)
+		atomic_dec(&queue->set_pci_flags_count);
+
+	if (newbufstate == QETH_QDIO_BUF_EMPTY) {
+		qeth_release_skbs(buf);
+	}
 	for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {
 		if (buf->buffer->element[i].addr && buf->is_header[i])
 			kmem_cache_free(qeth_core_header_cache,
@@ -1111,14 +1199,16 @@ static void qeth_free_buffer_pool(struct
 
 static void qeth_free_qdio_buffers(struct qeth_card *card)
 {
-	int i;
+	int i, j;
 
 	if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==
 		QETH_QDIO_UNINITIALIZED)
 		return;
 
 	qeth_free_cq(card);
-
+	cancel_delayed_work_sync(&card->buffer_reclaim_work);
+	for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
+		kfree_skb(card->qdio.in_q->bufs[j].rx_skb);
 	kfree(card->qdio.in_q);
 	card->qdio.in_q = NULL;
 	/* inbound buffer pool */
@@ -1287,6 +1377,7 @@ static int qeth_setup_card(struct qeth_c
 	card->ipato.invert6 = 0;
 	/* init QDIO stuff */
 	qeth_init_qdio_info(card);
+	INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
 	return 0;
 }
 
@@ -1308,7 +1399,7 @@ static struct qeth_card *qeth_alloc_card
 	if (!card)
 		goto out;
 	QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
-	card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+	card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
 	if (!card->ip_tbd_list) {
 		QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
 		goto out_card;
@@ -2237,7 +2328,7 @@ static int qeth_alloc_qdio_buffers(struc
 		QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)
 		return 0;
 
-	card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q),
+	card->qdio.in_q = kzalloc(sizeof(struct qeth_qdio_q),
 				   GFP_KERNEL);
 	if (!card->qdio.in_q)
 		goto out_nomem;
@@ -2245,27 +2336,28 @@ static int qeth_alloc_qdio_buffers(struc
 	QETH_DBF_HEX(SETUP, 2, &card->qdio.in_q, sizeof(void *));
 	memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q));
 	/* give inbound qeth_qdio_buffers their qdio_buffers */
-	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i)
+	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) {
 		card->qdio.in_q->bufs[i].buffer =
 			&card->qdio.in_q->qdio_bufs[i];
+		card->qdio.in_q->bufs[i].rx_skb = NULL;
+	}
 	/* inbound buffer pool */
 	if (qeth_alloc_buffer_pool(card))
 		goto out_freeinq;
 
 	/* outbound */
 	card->qdio.out_qs =
-		kmalloc(card->qdio.no_out_queues *
+		kzalloc(card->qdio.no_out_queues *
 			sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
 	if (!card->qdio.out_qs)
 		goto out_freepool;
 	for (i = 0; i < card->qdio.no_out_queues; ++i) {
-		card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
+		card->qdio.out_qs[i] = kzalloc(sizeof(struct qeth_qdio_out_q),
 					       GFP_KERNEL);
 		if (!card->qdio.out_qs[i])
 			goto out_freeoutq;
 		QETH_DBF_TEXT_(SETUP, 2, "outq %i", i);
 		QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *));
-		memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
 		card->qdio.out_qs[i]->queue_no = i;
 		/* give outbound qeth_qdio_buffers their qdio_buffers */
 		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
@@ -2563,6 +2655,12 @@ static int qeth_init_input_buffer(struct
 	struct qeth_buffer_pool_entry *pool_entry;
 	int i;
 
+	if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) {
+		buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN);
+		if (!buf->rx_skb)
+			return 1;
+	}
+
 	pool_entry = qeth_find_free_buffer_pool_entry(card);
 	if (!pool_entry)
 		return 1;
@@ -2952,9 +3050,19 @@ int qeth_check_qdio_errors(struct qeth_c
 }
 EXPORT_SYMBOL_GPL(qeth_check_qdio_errors);
 
+void qeth_buffer_reclaim_work(struct work_struct *work)
+{
+	struct qeth_card *card = container_of(work, struct qeth_card,
+		buffer_reclaim_work.work);
+
+	QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index);
+	qeth_queue_input_buffer(card, card->reclaim_index);
+}
+
 void qeth_queue_input_buffer(struct qeth_card *card, int index)
 {
 	struct qeth_qdio_q *queue = card->qdio.in_q;
+	struct list_head *lh;
 	int count;
 	int i;
 	int rc;
@@ -2986,6 +3094,20 @@ void qeth_queue_input_buffer(struct qeth
 			atomic_add_unless(&card->force_alloc_skb, -1, 0);
 		}
 
+		if (!count) {
+			i = 0;
+			list_for_each(lh, &card->qdio.in_buf_pool.entry_list)
+				i++;
+			if (i == card->qdio.in_buf_pool.buf_count) {
+				QETH_CARD_TEXT(card, 2, "qsarbw");
+				card->reclaim_index = index;
+				schedule_delayed_work(
+					&card->buffer_reclaim_work,
+					QETH_RECLAIM_WORK_TIME);
+			}
+			return;
+		}
+
 		/*
 		 * according to old code it should be avoided to requeue all
 		 * 128 buffers in order to benefit from PCI avoidance.
@@ -3394,15 +3516,34 @@ void qeth_qdio_output_handler(struct ccw
 		if (queue->bufstates &&
 		    (queue->bufstates[bidx].flags &
 		     QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) {
+			BUG_ON(card->options.cq != QETH_CQ_ENABLED);
+
+			if (atomic_cmpxchg(&buffer->state,
+					   QETH_QDIO_BUF_PRIMED,
+					   QETH_QDIO_BUF_PENDING) ==
+				QETH_QDIO_BUF_PRIMED) {
+				qeth_notify_skbs(queue, buffer,
+						 TX_NOTIFY_PENDING);
+			}
 			buffer->aob = queue->bufstates[bidx].aob;
 			QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx);
-			QETH_CARD_TEXT_(queue->card, 5, "aob");
+			QETH_CARD_TEXT(queue->card, 5, "aob");
 			QETH_CARD_TEXT_(queue->card, 5, "%lx",
 					virt_to_phys(buffer->aob));
 			BUG_ON(bidx < 0 || bidx >= QDIO_MAX_BUFFERS_PER_Q);
-			if (qeth_init_qdio_out_buf(queue, bidx))
+			if (qeth_init_qdio_out_buf(queue, bidx)) {
+				QETH_CARD_TEXT(card, 2, "outofbuf");
 				qeth_schedule_recovery(card);
+			}
 		} else {
+			if (card->options.cq == QETH_CQ_ENABLED) {
+				enum iucv_tx_notify n;
+
+				n = qeth_compute_cq_notification(
+					buffer->buffer->element[15].sflags, 0);
+				qeth_notify_skbs(queue, buffer, n);
+			}
+
 			qeth_clear_output_buffer(queue, buffer,
 						QETH_QDIO_BUF_EMPTY);
 		}
@@ -4333,7 +4474,7 @@ static int qeth_qdio_establish(struct qe
 	qeth_create_qib_param_field(card, qib_param_field);
 	qeth_create_qib_param_field_blkt(card, qib_param_field);
 
-	in_sbal_ptrs = kmalloc(card->qdio.no_in_queues *
+	in_sbal_ptrs = kzalloc(card->qdio.no_in_queues *
 			       QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),
 			       GFP_KERNEL);
 	if (!in_sbal_ptrs) {
@@ -4357,7 +4498,7 @@ static int qeth_qdio_establish(struct qe
 	qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll);
 
 	out_sbal_ptrs =
-		kmalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q *
+		kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q *
 			sizeof(void *), GFP_KERNEL);
 	if (!out_sbal_ptrs) {
 		rc = -ENOMEM;
@@ -4555,29 +4696,36 @@ out:
 }
 EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
 
-static inline int qeth_create_skb_frag(struct qdio_buffer_element *element,
+static inline int qeth_create_skb_frag(struct qeth_qdio_buffer *qethbuffer,
+		struct qdio_buffer_element *element,
 		struct sk_buff **pskb, int offset, int *pfrag, int data_len)
 {
 	struct page *page = virt_to_page(element->addr);
 	if (*pskb == NULL) {
-		/* the upper protocol layers assume that there is data in the
-		 * skb itself. Copy a small amount (64 bytes) to make them
-		 * happy. */
-		*pskb = dev_alloc_skb(64 + ETH_HLEN);
-		if (!(*pskb))
-			return -ENOMEM;
+		if (qethbuffer->rx_skb) {
+			/* only if qeth_card.options.cq == QETH_CQ_ENABLED */
+			*pskb = qethbuffer->rx_skb;
+			qethbuffer->rx_skb = NULL;
+		} else {
+			*pskb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN);
+			if (!(*pskb))
+				return -ENOMEM;
+		}
+
 		skb_reserve(*pskb, ETH_HLEN);
-		if (data_len <= 64) {
+		if (data_len <= QETH_RX_PULL_LEN) {
 			memcpy(skb_put(*pskb, data_len), element->addr + offset,
 				data_len);
 		} else {
 			get_page(page);
-			memcpy(skb_put(*pskb, 64), element->addr + offset, 64);
-			skb_fill_page_desc(*pskb, *pfrag, page, offset + 64,
-				data_len - 64);
-			(*pskb)->data_len += data_len - 64;
-			(*pskb)->len      += data_len - 64;
-			(*pskb)->truesize += data_len - 64;
+			memcpy(skb_put(*pskb, QETH_RX_PULL_LEN),
+			       element->addr + offset, QETH_RX_PULL_LEN);
+			skb_fill_page_desc(*pskb, *pfrag, page,
+				offset + QETH_RX_PULL_LEN,
+				data_len - QETH_RX_PULL_LEN);
+			(*pskb)->data_len += data_len - QETH_RX_PULL_LEN;
+			(*pskb)->len      += data_len - QETH_RX_PULL_LEN;
+			(*pskb)->truesize += data_len - QETH_RX_PULL_LEN;
 			(*pfrag)++;
 		}
 	} else {
@@ -4594,11 +4742,12 @@ static inline int qeth_create_skb_frag(s
 }
 
 struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
-		struct qdio_buffer *buffer,
+		struct qeth_qdio_buffer *qethbuffer,
 		struct qdio_buffer_element **__element, int *__offset,
 		struct qeth_hdr **hdr)
 {
 	struct qdio_buffer_element *element = *__element;
+	struct qdio_buffer *buffer = qethbuffer->buffer;
 	int offset = *__offset;
 	struct sk_buff *skb = NULL;
 	int skb_len = 0;
@@ -4643,9 +4792,10 @@ struct sk_buff *qeth_core_get_next_skb(s
 	if (!skb_len)
 		return NULL;
 
-	if ((skb_len >= card->options.rx_sg_cb) &&
-	    (!(card->info.type == QETH_CARD_TYPE_OSN)) &&
-	    (!atomic_read(&card->force_alloc_skb))) {
+	if (((skb_len >= card->options.rx_sg_cb) &&
+	     (!(card->info.type == QETH_CARD_TYPE_OSN)) &&
+	     (!atomic_read(&card->force_alloc_skb))) ||
+	    (card->options.cq == QETH_CQ_ENABLED)) {
 		use_rx_sg = 1;
 	} else {
 		skb = dev_alloc_skb(skb_len + headroom);
@@ -4660,8 +4810,8 @@ struct sk_buff *qeth_core_get_next_skb(s
 		data_len = min(skb_len, (int)(element->length - offset));
 		if (data_len) {
 			if (use_rx_sg) {
-				if (qeth_create_skb_frag(element, &skb, offset,
-				    &frag, data_len))
+				if (qeth_create_skb_frag(qethbuffer, element,
+				    &skb, offset, &frag, data_len))
 					goto no_mem;
 			} else {
 				memcpy(skb_put(skb, data_len), data_ptr,
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -409,7 +409,7 @@ static int qeth_l2_process_inbound_buffe
 	BUG_ON(!budget);
 	while (budget) {
 		skb = qeth_core_get_next_skb(card,
-			card->qdio.in_q->bufs[card->rx.b_index].buffer,
+			&card->qdio.in_q->bufs[card->rx.b_index],
 			&card->rx.b_element, &card->rx.e_offset, &hdr);
 		if (!skb) {
 			*done = 1;
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -63,5 +63,9 @@ int qeth_l3_add_rxip(struct qeth_card *,
 void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
 			const u8 *);
 int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
+struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
+int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
+int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
+void qeth_l3_set_ip_addr_list(struct qeth_card *);
 
 #endif /* __QETH_L3_H__ */
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -29,6 +29,7 @@
 #include <net/ip.h>
 #include <net/arp.h>
 #include <net/ip6_checksum.h>
+#include <net/iucv/af_iucv.h>
 
 #include "qeth_l3.h"
 
@@ -267,7 +268,7 @@ static int __qeth_l3_insert_ip_todo(stru
 	}
 }
 
-static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
 {
 	unsigned long flags;
 	int rc = 0;
@@ -286,7 +287,7 @@ static int qeth_l3_delete_ip(struct qeth
 	return rc;
 }
 
-static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
 {
 	unsigned long flags;
 	int rc = 0;
@@ -305,7 +306,7 @@ static int qeth_l3_add_ip(struct qeth_ca
 }
 
 
-static struct qeth_ipaddr *qeth_l3_get_addr_buffer(
+struct qeth_ipaddr *qeth_l3_get_addr_buffer(
 				enum qeth_prot_versions prot)
 {
 	struct qeth_ipaddr *addr;
@@ -421,7 +422,7 @@ again:
 	list_splice(&fail_list, &card->ip_list);
 }
 
-static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
+void qeth_l3_set_ip_addr_list(struct qeth_card *card)
 {
 	struct list_head *tbd_list;
 	struct qeth_ipaddr *todo, *addr;
@@ -438,7 +439,7 @@ static void qeth_l3_set_ip_addr_list(str
 
 	spin_lock_irqsave(&card->ip_lock, flags);
 	tbd_list = card->ip_tbd_list;
-	card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+	card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
 	if (!card->ip_tbd_list) {
 		QETH_CARD_TEXT(card, 0, "silnomem");
 		card->ip_tbd_list = tbd_list;
@@ -1993,12 +1994,13 @@ static int qeth_l3_process_inbound_buffe
 	__u16 vlan_tag = 0;
 	int is_vlan;
 	unsigned int len;
+	__u16 magic;
 
 	*done = 0;
 	BUG_ON(!budget);
 	while (budget) {
 		skb = qeth_core_get_next_skb(card,
-			card->qdio.in_q->bufs[card->rx.b_index].buffer,
+			&card->qdio.in_q->bufs[card->rx.b_index],
 			&card->rx.b_element, &card->rx.e_offset, &hdr);
 		if (!skb) {
 			*done = 1;
@@ -2007,12 +2009,26 @@ static int qeth_l3_process_inbound_buffe
 		skb->dev = card->dev;
 		switch (hdr->hdr.l3.id) {
 		case QETH_HEADER_TYPE_LAYER3:
-			is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
+			magic = *(__u16 *)skb->data;
+			if ((card->info.type == QETH_CARD_TYPE_IQD) &&
+			    (magic == ETH_P_AF_IUCV)) {
+				skb->protocol = ETH_P_AF_IUCV;
+				skb->pkt_type = PACKET_HOST;
+				skb->mac_header = NET_SKB_PAD;
+				skb->dev = card->dev;
+				len = skb->len;
+				card->dev->header_ops->create(skb, card->dev, 0,
+					card->dev->dev_addr, "FAKELL",
+					card->dev->addr_len);
+				netif_receive_skb(skb);
+			} else {
+				is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
 						      &vlan_tag);
-			len = skb->len;
-			if (is_vlan && !card->options.sniffer)
-				__vlan_hwaccel_put_tag(skb, vlan_tag);
-			napi_gro_receive(&card->napi, skb);
+				len = skb->len;
+				if (is_vlan && !card->options.sniffer)
+					__vlan_hwaccel_put_tag(skb, vlan_tag);
+				napi_gro_receive(&card->napi, skb);
+			}
 			break;
 		case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
 			skb->pkt_type = PACKET_HOST;
@@ -2784,6 +2800,30 @@ int inline qeth_l3_get_cast_type(struct
 	return cast_type;
 }
 
+static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
+		struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+	char daddr[16];
+	struct af_iucv_trans_hdr *iucv_hdr;
+
+	skb_pull(skb, 14);
+	card->dev->header_ops->create(skb, card->dev, 0,
+				      card->dev->dev_addr, card->dev->dev_addr,
+				      card->dev->addr_len);
+	skb_pull(skb, 14);
+	iucv_hdr = (struct af_iucv_trans_hdr *)skb->data;
+	memset(hdr, 0, sizeof(struct qeth_hdr));
+	hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+	hdr->hdr.l3.ext_flags = 0;
+	hdr->hdr.l3.length = skb->len;
+	hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
+	memset(daddr, 0, sizeof(daddr));
+	daddr[0] = 0xfe;
+	daddr[1] = 0x80;
+	memcpy(&daddr[8], iucv_hdr->destUserID, 8);
+	memcpy(hdr->hdr.l3.dest_addr, daddr, 16);
+}
+
 static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
 		struct sk_buff *skb, int ipv, int cast_type)
 {
@@ -2936,8 +2976,11 @@ static int qeth_l3_hard_start_xmit(struc
 	int data_offset = -1;
 	int nr_frags;
 
-	if (((card->info.type == QETH_CARD_TYPE_IQD) && (!ipv)) ||
-	     card->options.sniffer)
+	if (((card->info.type == QETH_CARD_TYPE_IQD) &&
+	     (((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
+	      ((card->options.cq == QETH_CQ_ENABLED) &&
+	       (skb->protocol != ETH_P_AF_IUCV)))) ||
+	    card->options.sniffer)
 			goto tx_drop;
 
 	if ((card->state != CARD_STATE_UP) || !card->lan_online) {
@@ -2959,7 +3002,10 @@ static int qeth_l3_hard_start_xmit(struc
 	if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
 	    (skb_shinfo(skb)->nr_frags == 0)) {
 		new_skb = skb;
-		data_offset = ETH_HLEN;
+		if (new_skb->protocol == ETH_P_AF_IUCV)
+			data_offset = 0;
+		else
+			data_offset = ETH_HLEN;
 		hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
 		if (!hdr)
 			goto tx_drop;
@@ -3025,9 +3071,13 @@ static int qeth_l3_hard_start_xmit(struc
 			qeth_l3_fill_header(card, hdr, new_skb, ipv,
 						cast_type);
 		} else {
-			qeth_l3_fill_header(card, hdr, new_skb, ipv,
-						cast_type);
-			hdr->hdr.l3.length = new_skb->len - data_offset;
+			if (new_skb->protocol == ETH_P_AF_IUCV)
+				qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
+			else {
+				qeth_l3_fill_header(card, hdr, new_skb, ipv,
+							cast_type);
+				hdr->hdr.l3.length = new_skb->len - data_offset;
+			}
 		}
 
 		if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -3290,6 +3340,8 @@ static int qeth_l3_setup_netdev(struct q
 		card->dev->flags |= IFF_NOARP;
 		card->dev->netdev_ops = &qeth_l3_netdev_ops;
 		qeth_l3_iqd_read_initial_mac(card);
+		if (card->options.hsuid[0])
+			memcpy(card->dev->perm_addr, card->options.hsuid, 9);
 	} else
 		return -ENODEV;
 
@@ -3660,7 +3712,6 @@ static int qeth_l3_ip6_event(struct noti
 	struct qeth_ipaddr *addr;
 	struct qeth_card *card;
 
-
 	card = qeth_l3_get_card_from_dev(dev);
 	if (!card)
 		return NOTIFY_DONE;
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -9,7 +9,7 @@
  */
 
 #include <linux/slab.h>
-
+#include <asm/ebcdic.h>
 #include "qeth_l3.h"
 
 #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
@@ -308,6 +308,8 @@ static ssize_t qeth_l3_dev_sniffer_store
 
 	if (card->info.type != QETH_CARD_TYPE_IQD)
 		return -EPERM;
+	if (card->options.cq == QETH_CQ_ENABLED)
+		return -EPERM;
 
 	mutex_lock(&card->conf_mutex);
 	if ((card->state != CARD_STATE_DOWN) &&
@@ -347,6 +349,111 @@ out:
 static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
 		qeth_l3_dev_sniffer_store);
 
+
+static ssize_t qeth_l3_dev_hsuid_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qeth_card *card = dev_get_drvdata(dev);
+	char tmp_hsuid[9];
+
+	if (!card)
+		return -EINVAL;
+
+	if (card->info.type != QETH_CARD_TYPE_IQD)
+		return -EPERM;
+
+	if (card->state == CARD_STATE_DOWN)
+		return -EPERM;
+
+	memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid));
+	EBCASC(tmp_hsuid, 8);
+	return sprintf(buf, "%s\n", tmp_hsuid);
+}
+
+static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qeth_card *card = dev_get_drvdata(dev);
+	struct qeth_ipaddr *addr;
+	char *tmp;
+	int i;
+
+	if (!card)
+		return -EINVAL;
+
+	if (card->info.type != QETH_CARD_TYPE_IQD)
+		return -EPERM;
+	if (card->state != CARD_STATE_DOWN &&
+	    card->state != CARD_STATE_RECOVER)
+		return -EPERM;
+	if (card->options.sniffer)
+		return -EPERM;
+	if (card->options.cq == QETH_CQ_NOTAVAILABLE)
+		return -EPERM;
+
+	tmp = strsep((char **)&buf, "\n");
+	if (strlen(tmp) > 8)
+		return -EINVAL;
+
+	if (card->options.hsuid[0]) {
+		/* delete old ip address */
+		addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
+		if (addr != NULL) {
+			addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+			addr->u.a6.addr.s6_addr32[1] = 0x00000000;
+			for (i = 8; i < 16; i++)
+				addr->u.a6.addr.s6_addr[i] =
+					card->options.hsuid[i - 8];
+			addr->u.a6.pfxlen = 0;
+			addr->type = QETH_IP_TYPE_NORMAL;
+		} else
+			return -ENOMEM;
+		if (!qeth_l3_delete_ip(card, addr))
+			kfree(addr);
+		qeth_l3_set_ip_addr_list(card);
+	}
+
+	if (strlen(tmp) == 0) {
+		/* delete ip address only */
+		card->options.hsuid[0] = '\0';
+		if (card->dev)
+			memcpy(card->dev->perm_addr, card->options.hsuid, 9);
+		qeth_configure_cq(card, QETH_CQ_DISABLED);
+		return count;
+	}
+
+	if (qeth_configure_cq(card, QETH_CQ_ENABLED))
+		return -EPERM;
+
+	for (i = 0; i < 8; i++)
+		card->options.hsuid[i] = ' ';
+	card->options.hsuid[8] = '\0';
+	strncpy(card->options.hsuid, tmp, strlen(tmp));
+	ASCEBC(card->options.hsuid, 8);
+	if (card->dev)
+		memcpy(card->dev->perm_addr, card->options.hsuid, 9);
+
+	addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
+	if (addr != NULL) {
+		addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+		addr->u.a6.addr.s6_addr32[1] = 0x00000000;
+		for (i = 8; i < 16; i++)
+			addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8];
+		addr->u.a6.pfxlen = 0;
+		addr->type = QETH_IP_TYPE_NORMAL;
+	} else
+		return -ENOMEM;
+	if (!qeth_l3_add_ip(card, addr))
+		kfree(addr);
+	qeth_l3_set_ip_addr_list(card);
+
+	return count;
+}
+
+static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show,
+		   qeth_l3_dev_hsuid_store);
+
+
 static struct attribute *qeth_l3_device_attrs[] = {
 	&dev_attr_route4.attr,
 	&dev_attr_route6.attr,
@@ -354,6 +461,7 @@ static struct attribute *qeth_l3_device_
 	&dev_attr_broadcast_mode.attr,
 	&dev_attr_canonical_macaddr.attr,
 	&dev_attr_sniffer.attr,
+	&dev_attr_hsuid.attr,
 	NULL,
 };
 


^ permalink raw reply

* Re: [PATCH 01/14] add Documentation/namespaces/user_namespace.txt
From: Randy Dunlap @ 2011-07-27 16:02 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-kernel, dhowells, ebiederm, containers, netdev, akpm,
	Serge E. Hallyn
In-Reply-To: <20110727153848.GA17288@hallyn.com>

On Wed, 27 Jul 2011 15:38:48 +0000 Serge E. Hallyn wrote:

> > > +Working notes
> > > +=============
> > 
> > A lot of this file is working notes and will need to be updated...
> 
> Yup.  I can leave it out of this file and keep it on the wiki instead, if
> that is preferred.

Either place is OK with me, as long as you continue to update it
and don't let it go stale.

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

^ permalink raw reply

* Re: [PATCH 01/14] add Documentation/namespaces/user_namespace.txt
From: Serge E. Hallyn @ 2011-07-27 15:38 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: linux-kernel, dhowells, ebiederm, containers, netdev, akpm,
	Serge E. Hallyn
In-Reply-To: <20110726132249.69533206.rdunlap@xenotime.net>

Quoting Randy Dunlap (rdunlap@xenotime.net):
> On Tue, 26 Jul 2011 18:58:24 +0000 Serge Hallyn wrote:
> 
> > From: Serge E. Hallyn <serge.hallyn@canonical.com>
> > 
> > This will hold some info about the design.  Currently it contains
> > future todos, issues and questions.
> > 
> > Changelog:
> >    jul 26: incorporate feed back from David Howells.
> > 
> > Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com>
> > Cc: Eric W. Biederman <ebiederm@xmission.com>
> > Cc: David Howells <dhowells@redhat.com>
> > ---
> >  Documentation/namespaces/user_namespace.txt |  107 +++++++++++++++++++++++++++
> >  1 files changed, 107 insertions(+), 0 deletions(-)
> >  create mode 100644 Documentation/namespaces/user_namespace.txt
> > 
> > diff --git a/Documentation/namespaces/user_namespace.txt b/Documentation/namespaces/user_namespace.txt
> > new file mode 100644
> > index 0000000..7e50517
> > --- /dev/null
> > +++ b/Documentation/namespaces/user_namespace.txt
> > @@ -0,0 +1,107 @@
> > +Description
> > +===========
> > +
> > +Traditionally, each task is owned by a user ID (UID) and belongs to one or more
> > +groups (GID).  Both are simple numeric IDs, though userspace usually translates
> > +them to names.  The user namespace allows tasks to have different views of the
> > +UIDs and GIDs associated with tasks and other resources.  (See 'UID mapping'
> > +below for more)
> 
>          for more.)

Thanks for reviewing, Randy.

> > +
> > +The user namespace is a simple hierarchical one.  The system starts with all
> > +tasks belonging to the initial user namespace.  A task creates a new user
> > +namespace by passing the CLONE_NEWUSER flag to clone(2).  This requires the
> > +creating task to have the CAP_SETUID, CAP_SETGID, and CAP_CHOWN capabilities,
> > +but it does not need to be running as root.  The clone(2) call will result in a
> > +new task which to itself appears to be running as UID and GID 0, but to its
> > +creator seems to have the creator's credentials.
> > +
> > +Any task in or resource belonging to the initial user namespace will, to this
> > +new task, appear to belong to UID and GID -1 - which is usually known as
> 
> that extra hyphen is confusing.  how about:
> 
>                               to UID and GID -1, which is
> 
> > +'nobody'.  Permission to open such files will be granted according to world

As I'd been asked to switch from comma, I'll restructure, something like:

"To this new task, any resource belonging to the initial user namespace will
appear to belong to user 'nobody', which has UID and GID -1."

> > +access permissions.  UID comparisons and group membership checks will return
> > +false, and privilege will be denied.
> > +
> > +When a task belonging to (for example) userid 500 in the initial user namespace
> > +creates a new user namespace, even though the new task will see itself as
> > +belonging to UID 0, any task in the initial user namespace will see it as
> > +belonging to UID 500.  Therefore, UID 500 in the initial user namespace will be
> > +able to kill the new task.  Files created by the new user will (eventually) be
> > +seen by tasks in its own user namespace as belonging to UID 0, but to tasks in
> > +the initial user namespace as belonging to UID 500.
> > +
> > +Note that this userid mapping for the VFS is not yet implemented, though the
> > +lkml and containers mailing list archives will show several previous
> > +prototypes.  In the end, those got hung up waiting on the concept of targeted
> > +capabilities to be developed, which, thanks to the insight of Eric Biederman,
> > +they finally did.
> > +
> > +Relationship between the User namespace and other namespaces
> > +============================================================
> > +
> > +Other namespaces, such as UTS and network, are owned by a user namespace.  When
> > +such a namespace is created, it is assigned to the user namespace of the task
> > +by which it was created.  Therefore, attempts to exercise privilege to
> > +resources in, for instance, a particular network namespace, can be properly
> > +validated by checking whether the caller has the needed privilege (i.e.
> > +CAP_NET_ADMIN) targeted to the user namespace which owns the network namespace.
> > +This is done using the ns_capable() function.
> > +
> > +As an example, if a new task is cloned with a private user namespace but
> > +no private network namespace, then the task's network namespace is owned
> > +by the parent user namespace.  The new task has no privilege to the
> > +parent user namespace, so it will not be able to create or configure
> > +network devices.  If, instead, the task were cloned with both private
> > +user and network namespaces, then the private network namespace is owned
> > +by the private user namespace, and so root in the new user namespace
> > +will have privilege targeted to the network namespace.  It will be able
> > +to create and configure network devices.
> > +
> > +UID Mapping
> > +===========
> > +The current plan (see 'flexible UID mapping' at
> > +https://wiki.ubuntu.com/UserNamespace) is:
> > +
> > +The UID/GID stored on disk will be that in the init_user_ns.  Most likely
> > +UID/GID in other namespaces will be stored in xattrs.  But Eric was advocating
> > +(a few years ago) leaving the details up to filesystems while providing a lib/
> > +stock implementation.  See the thread around here
> 
>                                                 here:
> 
> > +http://www.mail-archive.com/devel@openvz.org/msg09331.html
> > +
> > +
> > +Working notes
> > +=============
> 
> A lot of this file is working notes and will need to be updated...

Yup.  I can leave it out of this file and keep it on the wiki instead, if
that is preferred.

> > +Capability checks for actions related to syslog must be against the
> > +init_user_ns until syslog is containerized.
> > +
> > +Same is true for reboot and power, control groups, devices, and time.
> > +
> > +Perf actions (kernel/event/core.c for instance) will always be constrained to
> > +init_user_ns.
> > +
> > +Q:
> > +Is accounting considered properly containerized wrt pidns?  (it appears to be).
> 
> s/wrt/with respect to/
> 
> > +If so, then we can change the capable() check in kernel/acct.c to
> > +'ns_capable(current_pid_ns()->user_ns, CAP_PACCT)'
> > +
> > +Q:
> > +For things like nice and schedaffinity, we could allow root in a container to
> > +control those, and leave only cgroups to constrain the container.  I'm not sure
> > +whether that is right, or whether it violates admin expectations.
> > +
> > +I deferred some of commoncap.c.  I'm punting on xattr stuff as they take
> > +dentries, not inodes.
> > +
> > +For drivers/tty/tty_io.c and drivers/tty/vt/vt.c, we'll want to (for some of
> > +them) target the capability checks at the user_ns owning the tty.  That will
> > +have to wait until we get userns owning files straightened out.
> > +
> > +We need to figure out how to label devices.  Should we just toss a user_ns
> > +right into struct device?
> > +
> > +capable(CAP_MAC_ADMIN) checks are always to be against init_user_ns, unless
> > +some day LSMs were to be containerized, near zero chance.
> > +
> > +inode_owner_or_capable() should probably take an optional ns and cap parameter.
> > +If cap is 0, then CAP_FOWNER is checked.  If ns is NULL, we derive the ns from
> > +inode.  But if ns is provided, then callers who need to derive
> > +inode_userns(inode) anyway can save a few cycles.
> > -- 
> 
> 
> ---
> ~Randy
> *** Remember to use Documentation/SubmitChecklist when testing your code ***

^ permalink raw reply

* [PATCH] vfs: avoid taking locks if inode not in lists
From: Eric Dumazet @ 2011-07-27 15:21 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Tim Chen, Al Viro, David Miller, Andi Kleen, Matthew Wilcox,
	Anton Blanchard, npiggin, linux-kernel, linux-fsdevel, netdev
In-Reply-To: <1311672994.2355.17.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

Le mardi 26 juillet 2011 à 11:36 +0200, Eric Dumazet a écrit :
> Le mardi 26 juillet 2011 à 05:03 -0400, Christoph Hellwig a écrit :
> > On Tue, Jul 26, 2011 at 10:21:06AM +0200, Eric Dumazet wrote:
> > > Well, not 'last' contention point, as we still hit remove_inode_hash(),
> > 
> > There should be no ned to put pipe or anon inodes on the inode hash.
> > Probably sockets don't need it either, but I'd need to look at it in
> > detail.
> > 
> > > inode_wb_list_del()
> > 
> > The should never be on the wb list either, doing an unlocked check for
> > actually beeing on the list before taking the lock should help you.
> 
> Yes, it might even help regular inodes ;)
> 
> > 
> > > inode_lru_list_del(),
> > 
> > No real need to keep inodes in the LRU if we only allocate them using
> > new_inode but never look them up either.  You might want to try setting
> > .drop_inode to generic_delete_inode for these.
> 
> Yes, I'll take a look, thanks.

If I am not mistaken, we can add unlocked checks on the three hot spots.

After following patch, a close(socket(PF_INET, SOCK_DGRAM, 0)) pair on
my dev machine takes ~3us instead of ~9us.

Maybe its better to split it in three patches, just let me know.

22us -> 3us, thats a nice patch series ;)

Thanks

[PATCH] vfs: avoid taking locks if inode not in lists

sockets and pipes inodes destruction hits three possibly contended
locks :

system-wide inode_hash_lock in remove_inode_hash()
superblock s_inode_lru_lock in inode_lru_list_del()
bdi wb.list_lock in inode_wb_list_del()

Before even taking locks, we can perform an unlocked test to check if
inode can possibly be in the lists.

On a 2x4x2 machine, a close(socket()) pair can be 200% faster with these
changes.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 fs/fs-writeback.c |   10 ++++++----
 fs/inode.c        |    6 ++++++
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 1599aa9..8b90bdb 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -182,11 +182,13 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi)
  */
 void inode_wb_list_del(struct inode *inode)
 {
-	struct backing_dev_info *bdi = inode_to_bdi(inode);
+	if (!list_empty(&inode->i_wb_list)) {
+		struct backing_dev_info *bdi = inode_to_bdi(inode);
 
-	spin_lock(&bdi->wb.list_lock);
-	list_del_init(&inode->i_wb_list);
-	spin_unlock(&bdi->wb.list_lock);
+		spin_lock(&bdi->wb.list_lock);
+		list_del_init(&inode->i_wb_list);
+		spin_unlock(&bdi->wb.list_lock);
+	}
 }
 
 /*
diff --git a/fs/inode.c b/fs/inode.c
index d0c72ff..796a420 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -338,6 +338,9 @@ static void inode_lru_list_add(struct inode *inode)
 
 static void inode_lru_list_del(struct inode *inode)
 {
+	if (list_empty(&inode->i_lru))
+		return;
+
 	spin_lock(&inode->i_sb->s_inode_lru_lock);
 	if (!list_empty(&inode->i_lru)) {
 		list_del_init(&inode->i_lru);
@@ -406,6 +409,9 @@ EXPORT_SYMBOL(__insert_inode_hash);
  */
 void remove_inode_hash(struct inode *inode)
 {
+	if (inode_unhashed(inode))
+		return;
+
 	spin_lock(&inode_hash_lock);
 	spin_lock(&inode->i_lock);
 	hlist_del_init(&inode->i_hash);


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

^ permalink raw reply related

* Re: Oops when insmod rtl8192ce
From: Larry Finger @ 2011-07-27 14:37 UTC (permalink / raw)
  To: hubert Liao
  Cc: Chaoming Li, John W. Linville,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAMzZzFGpDiFAhsLAb+SQxjbGMzBoOStF_Lr7Eb3PT6Drcf=VdA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 07/27/2011 04:26 AM, hubert Liao wrote:
> Hi,
> We got an oops when insmod rtl8192ce module (the board is an ARM soc),
> accroding the oops message, find it's because in rtl_pci_probe()
> called _rtl_pci_find_adapter(),
> in this funcation, the  pdev->bus->self is a NULL pointer .
> static boot _rtl_pci_find_adapter(strcut pci_dev *dev,
>                struct ieee80211_hw *hw)
> {
> struct pci_dev *bridge_pdev = pdev->bus->self;   //line 1601
> ...
> pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;<--
> [oops here] line 1700
> ...
> }
> here, I just want to know why the bus->self  is NULL?
> ----
> [  148.186632] Unable to handle kernel NULL pointer dereference at
> virtual address 00000020

As John Linville suggested, please open a bugzilla report.

I would also like some additional information. What kernel are you using? In 
addition, please post the 'lspci -nnk' information for your card.

I also think that pdev->bus should have been setup before the initialization 
code in rtl8192ce was called. I have not tested the driver on other than x86 and 
x86_64 architectures because of hardware availability, thus ARM may expose some 
problems. Is this soc little-endian?

Thanks,
Larry

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

^ permalink raw reply

* Re: Re: [PATCHv4 10/11] drop_monitor: Use *_dec_not_zero instead of *_add_unless
From: Neil Horman @ 2011-07-27 14:25 UTC (permalink / raw)
  To: Sven Eckelmann; +Cc: linux-arch, linux-kernel, netdev
In-Reply-To: <2127465.xrCKehbL3A@sven-laptop.home.narfation.org>

On Wed, Jul 27, 2011 at 01:52:50PM +0200, Sven Eckelmann wrote:
> On Wednesday 27 July 2011 06:59:07 Neil Horman wrote:
> > On Wed, Jul 27, 2011 at 11:47:49AM +0200, Sven Eckelmann wrote:
> > > atomic_dec_not_zero is defined for each architecture through
> > > <linux/atomic.h> to provide the functionality of
> > > atomic_add_unless(x, -1, 0).
> > > 
> > > Signed-off-by: Sven Eckelmann <sven@narfation.org>
> > > Cc: Neil Horman <nhorman@tuxdriver.com>
> > > Cc: netdev@vger.kernel.org
> > > ---
> > > 
> > >  net/core/drop_monitor.c |    2 +-
> > >  1 files changed, 1 insertions(+), 1 deletions(-)
> > > 
> > > diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> > > index 7f36b38..ef4a05d 100644
> > > --- a/net/core/drop_monitor.c
> > > +++ b/net/core/drop_monitor.c
> > > @@ -137,7 +137,7 @@ static void trace_drop_common(struct sk_buff *skb,
> > > void *location)
> > > 
> > >  	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
> > > 
> > > -	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
> > > +	if (!atomic_dec_not_zero(&data->dm_hit_count)) {
> > > 
> > >  		/*
> > >  		
> > >  		 * we're already at zero, discard this hit
> > >  		 */
> > 
> > Wheres the patch that creates the per arch definition of this function?  I
> > see the other posts in this series went to lkml, but the archives don't
> > have the first in the series anywhere, which ostensibly adds the
> > definition. Neil
> 
> Most architectures don't use a per architecture definition anymore, but a 
> cross-architecture the definition in include/linux/atomic.h.
> 
> The 01/11 can be found in different archives under the message id 
> 1311760070-21532-1-git-send-email-sven@narfation.org ... for example gmane: 
> http://article.gmane.org/gmane.linux.ports.arm.kernel/126704
> 
> Kind regards,
> 	Sven
Ok, thank you, I just didn't see the patch that implemented it in the lkml
archive at MARC, and wanted to be sure this wasn't proposed for a different tree
than the patch with the definition.

Acked-by: Neil Horman <nhorman@tuxdriver.com>

^ permalink raw reply

* strange behaviour of MC7700 with sierra_net
From: Phil Sutter @ 2011-07-27 14:12 UTC (permalink / raw)
  To: Elina Pasheva
  Cc: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

Hello everyone,

I am testing the above module on linux-2.6.39.2 which should contain the
latest changes to at least sierra_net.c. Although I am able to bring the
module up and then can get traffic through it, initialisation seems to
be a little picky.

After connecting the module via USB, I get the following final
initialisation message:
| Jul 27 14:01:15 (none) user.info kernel: [166371.982356] sierra_net 1-1:1.7: wwan0: register 'sierra_net' at usb-0000:00:08.2-1, Sierra Wireless USB-to-WWAN Modem, 4a:82:22:b9:05:07

Doing nothing (successfully), the driver starts printing errors after a
while:
| Jul 27 14:02:15 (none) user.err kernel: [166432.201461] sierra_net 1-1:1.7: wwan0: Submit SYNC failed -32
| Jul 27 14:02:15 (none) user.err kernel: [166432.201609] sierra_net 1-1:1.7: wwan0: Send SYNC failed, status -32
| Jul 27 14:02:15 (none) user.err kernel: [166432.205446] sierra_net 1-1:1.7: wwan0: Submit SYNC failed -32
| Jul 27 14:02:15 (none) user.err kernel: [166432.205590] sierra_net 1-1:1.7: wwan0: Send SYNC failed, status -32

The above messages are the first ones to appear (so there's a delay of
60 seconds in between), and they repeat each 2 seconds from then on.
Depending on how I continue at that point, results vary:

a) 'ip link set wwan0 up': setting the interface up stops the above error
   messages from being printed. This worked every time I tried.

b) Sending 'ATZ' on the control-tty: this makes the error-messages
   disappear for about 6 seconds, so two iterations of the sync-timer
   seem to succeed. When I then try to continue initialisation, I usually
   get to 'AT+C' (for AT+CPIN='1234'), then the control-tty dies (neither
   echoing of typed characters, nor feedback from the modem printed).

Trying a) from the state after b) indeed makes the error-messages go
away, but the tty stays dead until I power-cycle the module. Needless to
say, without the control-tty the module is completely useless.

My first thought was that the driver shouldn't try to SYNC while the
interface being down, but apparently sierra_net_send_sync() doesn't get
called anymore after setting the interface up.

So in order to prevent the module from dieing, I need to up the
interface before initialisation via AT-interface.

Another interesting aspect: the above error stays gone after the
interface has been upped once, even if it's brought down right
afterwards without doing anything else. OK, not completely - there needs
to be a little delay in which the interface stays up, but a tenth of a
second was enough.

What else can I do to track this problem down further? What additional
information do you need from me? Any advice is highly appreciated, of
course!

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

^ permalink raw reply

* Re: Oops when insmod rtl8192ce
From: John W. Linville @ 2011-07-27 13:53 UTC (permalink / raw)
  To: hubert Liao
  Cc: wlanfae-Rasf1IRRPZFBDgjK7y7TUQ, Larry Finger, Chaoming Li,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAMzZzFGqPv6Nbw8J8JGNKcLA76sAs3oD6+hH9NPCjwA33fYpOQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Wed, Jul 27, 2011 at 05:20:15PM +0800, hubert Liao wrote:
> Hi,
> 
> We got an oops when insmod rtl8192ce module (the board is an ARM soc),
> accroding the oops message, find it's because in rtl_pci_probe() called
> _rtl_pci_find_adapter(),
> in this funcation, the  pdev->bus->self is a NULL pointer .
> 
> static boot _rtl_pci_find_adapter(strcut pci_dev *dev,
>               struct ieee80211_hw *hw)
> {
> 
> struct pci_dev *bridge_pdev = pdev->bus->self;   //line 1601
> ...
> 
> pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor; <-- [oops
> here] line 1700
> 
> ...
> }
> 
> here, I just want to know why the bus->self  is NULL?

pdev is coming straight from what is passed to the PCI probe routine.
It seems like pdev->bus->self should already be set before that
happens.

Please open a bug at bugzilla.kernel.org to help us keep track of
this -- thanks!

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org			might be all we have.  Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: gro: Only reset frag0 when skb can be pulled
From: David Miller @ 2011-07-27 13:17 UTC (permalink / raw)
  To: herbert; +Cc: netdev
In-Reply-To: <20110727131008.GA28825@gondor.apana.org.au>

From: Herbert Xu <herbert@gondor.hengli.com.au>
Date: Wed, 27 Jul 2011 21:10:08 +0800

> gro: Only reset frag0 when skb can be pulled
> 
> Currently skb_gro_header_slow unconditionally resets frag0 and
> frag0_len.  However, when we can't pull on the skb this leaves
> the GRO fields in an inconsistent state.
> 
> This patch fixes this by only resetting those fields after the
> pskb_may_pull test.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Applied, thanks.

I'll queue this up for -stable too.

^ permalink raw reply

* gro: Only reset frag0 when skb can be pulled
From: Herbert Xu @ 2011-07-27 13:10 UTC (permalink / raw)
  To: David S. Miller, netdev

Hi:

gro: Only reset frag0 when skb can be pulled

Currently skb_gro_header_slow unconditionally resets frag0 and
frag0_len.  However, when we can't pull on the skb this leaves
the GRO fields in an inconsistent state.

This patch fixes this by only resetting those fields after the
pskb_may_pull test.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ea6f4aa..a692a7c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1679,9 +1679,12 @@ static inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen)
 static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
 					unsigned int offset)
 {
+	if (!pskb_may_pull(skb, hlen))
+		return NULL;
+
 	NAPI_GRO_CB(skb)->frag0 = NULL;
 	NAPI_GRO_CB(skb)->frag0_len = 0;
-	return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
+	return skb->data + offset;
 }
 
 static inline void *skb_gro_mac_header(struct sk_buff *skb)

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply related

* Re: Realtek 8139 Flow Control?
From: Sven Anders @ 2011-07-27 12:29 UTC (permalink / raw)
  To: netdev; +Cc: jgarzik
In-Reply-To: <4E2DC710.5000100@anduras.de>

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

Sven Anders wrote:
> I have just a short question.
> 
> We have appliances with Realtek 8139C/8139C+ (rev 10) chipsets (with the
> PCI IDs: 10ec:8139). We are using the 8139too driver (version: 0.9.28)
> According the datasheet the chipset supports Flow Control (IEEE 802.3x).
> 
> I want to know, if the support for enabling flow control is missing in
> the driver by purpose or is only not implemented due to lack of time?

Hi, again!

Can somebody of the old implementors please answer this question?

We need that feature and I'm willing to implement it, but I need the
confirmation, that it was not done due to lack of time and not because
will not work (correctly)...

Regards
 Sven Anders

-- 
 Sven Anders <anders@anduras.de>                 () UTF-8 Ribbon Campaign
                                                 /\ Support plain text e-mail
 ANDURAS intranet security AG
 Messestrasse 3 - 94036 Passau - Germany
 Web: www.anduras.de - Tel: +49 (0)851-4 90 50-0 - Fax: +49 (0)851-4 90 50-55

Those who would give up essential Liberty, to purchase a little
temporary Safety, deserve neither Liberty nor Safety.
  - Benjamin Franklin

[-- Attachment #2: anders.vcf --]
[-- Type: text/x-vcard, Size: 339 bytes --]

begin:vcard
fn:Sven Anders
n:Anders;Sven
org:ANDURAS AG;Research and Development
adr;quoted-printable:;;Messestra=C3=9Fe 3;Passau;Bavaria;94036;Germany
email;internet:anders@anduras.de
title:Dipl. Inf.
tel;work:++49 (0)851 / 490 50 -0
tel;fax:++49 (0)851 / 590 50 - 55
x-mozilla-html:FALSE
url:http://www.anduras.de
version:2.1
end:vcard


^ permalink raw reply

* Re: IP over 802.2 with LLC/SNAP
From: Alan Cox @ 2011-07-27 12:32 UTC (permalink / raw)
  To: Alan Ott; +Cc: linux-kernel, linux-net, netdev
In-Reply-To: <4E2F8B7B.80906@signal11.us>

> So the question is, does Linux support IP over 802.2 with LLC/SNAP? Is 
> there a sysfs/proc entry that I have to turn on to make this work (I 
> didn't find one)? I have the LLC2 module loaded, and I believe my packet 
> to be correct, since Windows recognizes it and since Wireshark doesn't 
> give any red flags on it. I've been unable to find anything about this 
> kind of thing in my searching.

Linux supports LLC/SNAP and various things over it (IPX/Appletalk DDP
etc) but not IP over it, as it's one of those standards bodies driven
bogosities which nobody ever actually deployed.

You are about the first person in the known universe to care 8). Now
there isn't any reason for not supporting it. The receive side is a case
of using register_snap_client() and piping the frames into the IP stack.

The send side could be tackled two ways I can see, one would be to allow
for snap protocols to be set somehow on devices and routes, the other
that might be simply would be to create a 'snapifier' device that added
snap headers then routed the frame via the bound physical device as
802.2 LLC. That way you'd be able to do

	route add gould-relic dev snap0

In both cases you'd need to tackle ARP but that seems to need no
configuration as SNAP requests get SNAP replies, and so on.

It should be a 'simple matter of hacking' and there are example devices
that fiddle with packets and add headers etc you can nick a lot of code
from. I figure if you can write an IP stack for an ancient Gould system
you can probably do that.

The other way is to use the Linux raw packet interfaces, open a raw
socket, push a BPF filter onto it to just get the frames for IP/SNAP and
ARP/SNAP then modify them and feed them to the kernel tun/tap interface.

Alan

^ permalink raw reply

* Re: Re: [PATCHv4 10/11] drop_monitor: Use *_dec_not_zero instead of *_add_unless
From: Sven Eckelmann @ 2011-07-27 11:52 UTC (permalink / raw)
  To: Neil Horman; +Cc: linux-arch, linux-kernel, netdev
In-Reply-To: <20110727105907.GA19001@hmsreliant.think-freely.org>

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

On Wednesday 27 July 2011 06:59:07 Neil Horman wrote:
> On Wed, Jul 27, 2011 at 11:47:49AM +0200, Sven Eckelmann wrote:
> > atomic_dec_not_zero is defined for each architecture through
> > <linux/atomic.h> to provide the functionality of
> > atomic_add_unless(x, -1, 0).
> > 
> > Signed-off-by: Sven Eckelmann <sven@narfation.org>
> > Cc: Neil Horman <nhorman@tuxdriver.com>
> > Cc: netdev@vger.kernel.org
> > ---
> > 
> >  net/core/drop_monitor.c |    2 +-
> >  1 files changed, 1 insertions(+), 1 deletions(-)
> > 
> > diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> > index 7f36b38..ef4a05d 100644
> > --- a/net/core/drop_monitor.c
> > +++ b/net/core/drop_monitor.c
> > @@ -137,7 +137,7 @@ static void trace_drop_common(struct sk_buff *skb,
> > void *location)
> > 
> >  	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
> > 
> > -	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
> > +	if (!atomic_dec_not_zero(&data->dm_hit_count)) {
> > 
> >  		/*
> >  		
> >  		 * we're already at zero, discard this hit
> >  		 */
> 
> Wheres the patch that creates the per arch definition of this function?  I
> see the other posts in this series went to lkml, but the archives don't
> have the first in the series anywhere, which ostensibly adds the
> definition. Neil

Most architectures don't use a per architecture definition anymore, but a 
cross-architecture the definition in include/linux/atomic.h.

The 01/11 can be found in different archives under the message id 
1311760070-21532-1-git-send-email-sven@narfation.org ... for example gmane: 
http://article.gmane.org/gmane.linux.ports.arm.kernel/126704

Kind regards,
	Sven

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH] bridge: mask forwarding of IEEE 802 local multicast groups
From: David Lamparter @ 2011-07-27 11:17 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Nick Carter, netdev, Michał Mirosław, davem,
	David Lamparter
In-Reply-To: <20110715163345.GD1407585@jupiter.n2.diac24.net>

On Fri, Jul 15, 2011 at 06:33:45PM +0200, David Lamparter wrote:
> On Fri, Jul 15, 2011 at 06:03:57PM +0200, David Lamparter wrote:
> > On Fri, Jul 15, 2011 at 04:44:50PM +0100, Nick Carter wrote:
> > > On 12 July 2011 12:36, David Lamparter <equinox@diac24.net> wrote:
> > > > On Mon, Jul 11, 2011 at 08:27:55AM -0700, Stephen Hemminger wrote:
> > > >> I am still undecided on this. Understand the need, but don't like idea
> > > >> of bridge behaving in non-conforming manner. Will see if IEEE 802 committee
> > > >> has any input.
> > > >
> > > > The patch doesn't make the bridge behave nonconformant. The default mask
> > > > is 0, which just keeps the old behaviour.
> 
> P.S.: I'd like to once more stress this. In my opinion the patch should
> be merged because it provides desireable functionality at a small cost
> (one test, one knob) and __does not change any default behaviour__.

Stephen, anything new on this?

-David

^ permalink raw reply

* Re: [PATCHv4 10/11] drop_monitor: Use *_dec_not_zero instead of *_add_unless
From: Neil Horman @ 2011-07-27 10:59 UTC (permalink / raw)
  To: Sven Eckelmann; +Cc: linux-arch, linux-kernel, netdev
In-Reply-To: <1311760070-21532-10-git-send-email-sven@narfation.org>

On Wed, Jul 27, 2011 at 11:47:49AM +0200, Sven Eckelmann wrote:
> atomic_dec_not_zero is defined for each architecture through
> <linux/atomic.h> to provide the functionality of
> atomic_add_unless(x, -1, 0).
> 
> Signed-off-by: Sven Eckelmann <sven@narfation.org>
> Cc: Neil Horman <nhorman@tuxdriver.com>
> Cc: netdev@vger.kernel.org
> ---
>  net/core/drop_monitor.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> index 7f36b38..ef4a05d 100644
> --- a/net/core/drop_monitor.c
> +++ b/net/core/drop_monitor.c
> @@ -137,7 +137,7 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
>  	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
>  
>  
> -	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
> +	if (!atomic_dec_not_zero(&data->dm_hit_count)) {
>  		/*
>  		 * we're already at zero, discard this hit
>  		 */
> -- 
> 1.7.5.4
> 
> 
Wheres the patch that creates the per arch definition of this function?  I see
the other posts in this series went to lkml, but the archives don't have the
first in the series anywhere, which ostensibly adds the definition.  
Neil


^ permalink raw reply

* special handling before ndo_start_xmit() been invoked
From: jiangtao.jit @ 2011-07-27 10:48 UTC (permalink / raw)
  To: netdev; +Cc: jiangtao.jit

Hi all:

in struct net_device
there is a function pointer    rx_handler_func_t __rcu	*rx_handler
it's very convenient to register a function in an externel module
and do some special handlings

so i think what about to add an tx_handler_func
before        ndo_start_xmit() been invoked
and do some special things before transmit

is it necessary or there is already some other better methods to achieve this goal ?
Thank you all

-------
2011-07-27 
jiangtao.jit 


^ permalink raw reply

* [PATCHv4 10/11] drop_monitor: Use *_dec_not_zero instead of *_add_unless
From: Sven Eckelmann @ 2011-07-27  9:47 UTC (permalink / raw)
  To: linux-arch; +Cc: linux-kernel, Sven Eckelmann, Neil Horman, netdev
In-Reply-To: <1311760070-21532-1-git-send-email-sven@narfation.org>

atomic_dec_not_zero is defined for each architecture through
<linux/atomic.h> to provide the functionality of
atomic_add_unless(x, -1, 0).

Signed-off-by: Sven Eckelmann <sven@narfation.org>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: netdev@vger.kernel.org
---
 net/core/drop_monitor.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 7f36b38..ef4a05d 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -137,7 +137,7 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
 	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
 
 
-	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
+	if (!atomic_dec_not_zero(&data->dm_hit_count)) {
 		/*
 		 * we're already at zero, discard this hit
 		 */
-- 
1.7.5.4


^ permalink raw reply related

* Oops when insmod rtl8192ce
From: hubert Liao @ 2011-07-27  9:26 UTC (permalink / raw)
  To: Larry Finger, Chaoming Li, John W. Linville, linux-wireless,
	netdev
In-Reply-To: <CAMzZzFGqPv6Nbw8J8JGNKcLA76sAs3oD6+hH9NPCjwA33fYpOQ@mail.gmail.com>

Hi,
We got an oops when insmod rtl8192ce module (the board is an ARM soc),
accroding the oops message, find it's because in rtl_pci_probe()
called _rtl_pci_find_adapter(),
in this funcation, the  pdev->bus->self is a NULL pointer .
static boot _rtl_pci_find_adapter(strcut pci_dev *dev,
              struct ieee80211_hw *hw)
{
struct pci_dev *bridge_pdev = pdev->bus->self;   //line 1601
...
pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor; <--
[oops here] line 1700
...
}
here, I just want to know why the bus->self  is NULL?
----
[  148.186632] Unable to handle kernel NULL pointer dereference at
virtual address 00000020
[  148.195255] pgd = cfb40000
[  148.198178] [00000020] *pgd=0e54d831, *pte=00000000, *ppte=00000000
[  148.204743] Internal error: Oops: 17 [#1] PREEMPT
[  148.209426] Modules linked in: rtl8192ce(+) rtl8192c_common rtlwifi
[  148.215702] CPU: 0    Not tainted  (3.0.0-05684-ge371d46 #34)
[  148.221453] PC is at rtl_pci_probe+0x398/0x1bd8 [rtlwifi]
[  148.226827] LR is at 0x4
[  148.229356] pc : [<bf00fdc0>]    lr : [<00000004>]    psr: 80000013
[  148.229364] sp : cfb37d10  ip : bf012606  fp : ce518dc0
[  148.240800] r10: ce51abbc  r9 : 00008178  r8 : cf807000
[  148.246001] r7 : 00004000  r6 : 00000000  r5 : 00000000  r4 : 1fff7fff
[  148.252498] r3 : 0000037c  r2 : 00000000  r1 : bf012584  r0 : 00000000
[  148.258997] Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[  148.266108] Control: 0005397f  Table: 0fb40000  DAC: 00000015
[  148.271826] Process insmod (pid: 650, stack limit = 0xcfb36270)
[  148.277718] Stack: (0xcfb37d10 to 0xcfb38000)
[  148.282061] 7d00:                                     00000000
c018928c 00000000 ce54f870
[  148.290212] 7d20: 00000001 cf804680 bf012584 ce518240 ce518dc0
ce51abbc cf807060 000010ec
[  148.298368] 7d40: 00000000 ce54f810 cfb37d90 c00f2194 00000000
000012c7 cfb37d90 ce54f810
[  148.306524] 7d60: cfb37d90 c00f22f8 00000109 01000000 cfb37d90
bf043c80 cf807000 00000000
[  148.314672] 7d80: bf043c50 cfa7df00 c054ba00 bf043c80 bf049000
c01a8d80 bf043c80 c01a901c
[  148.322828] 7da0: cf807068 bf043c50 cf807000 bf0439b0 bf043c80
cf807060 cf807060 bf043c80
[  148.330984] 7dc0: c01dce54 c01dcd04 cf807060 cf807094 bf043c80
c01dce54 cfa7df00 c04bb780
[  148.339140] 7de0: 00000000 c01dcee0 00000000 cfb37df8 bf043c80
c01dc4f8 cf805c38 cf87c810
[  148.347296] 7e00: bf043c50 cf81cb00 bf043c80 bf043c80 c04a4f0c
c01dbd00 bf0437ac 00000000
[  148.355452] 7e20: bf043c50 bf043c50 bf043c80 bf043c80 c04a4f0c
cfb36000 c004e388 c01dd284
[  148.363609] 7e40: bf043c50 cfb0c540 bf043c80 c04a4f0c cfb36000
c01a92dc 00000001 cfb0c540
[  148.371765] 7e60: 00000001 bf04642c cfb36000 bf049014 00000001
c0008578 c004e460 c037204c
[  148.379920] 7e80: c049d93c 00000000 bf04642c ffffffff 00000001
c003d1f8 00000000 ce51e240
[  148.388069] 7ea0: 00000001 00000001 cfb0c540 00000001 bf04642c
00000010 bf046474 c004e388
[  148.396225] 7ec0: c004e460 c004f62c bf046438 00000001 cfb36000
bf046438 d0c0d6da d0bcbcfc
[  148.404381] 7ee0: bf046550 c03786cc d0ad7000 00136709 d0bcb554
d0bcb368 d0c0c554 cfa6a800
[  148.412536] 7f00: 0001156c 0001225c 00000000 00000000 00000031
00000032 00000018 00000000
[  148.420683] 7f20: 00000010 00000000 776c7472 00696669 00000000
00000000 00000000 00000000
[  148.428831] 7f40: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000
[  148.436978] 7f60: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 c009b170
[  148.445127] 7f80: 00000003 40085000 bedf0db4 00000069 00000080
c0008ec4 cfb36000 00000000
[  148.453283] 7fa0: 00000000 c0008d40 40085000 bedf0db4 40389008
00136709 000b72df 00000000
[  148.461439] 7fc0: 40085000 bedf0db4 00000069 00000080 bedf0db8
000b72df bedf0db8 00000000
[  148.469587] 7fe0: 00000001 bedf0a5c 0001c7e8 40326ac4 60000010
40389008 00000000 00000000
[  148.477783] [<bf00fdc0>] (rtl_pci_probe+0x398/0x1bd8 [rtlwifi])
from [<c01a8d80>] (local_pci_probe+0x18/0x1c)
[  148.487677] [<c01a8d80>] (local_pci_probe+0x18/0x1c) from
[<c01a901c>] (pci_device_probe+0x6c/0x90)
[  148.496701] [<c01a901c>] (pci_device_probe+0x6c/0x90) from
[<c01dcd04>] (driver_probe_device+0x80/0x1d0)
[  148.506156] [<c01dcd04>] (driver_probe_device+0x80/0x1d0) from
[<c01dcee0>] (__driver_attach+0x8c/0x90)
[  148.515521] [<c01dcee0>] (__driver_attach+0x8c/0x90) from
[<c01dc4f8>] (bus_for_each_dev+0x60/0x94)
[  148.524541] [<c01dc4f8>] (bus_for_each_dev+0x60/0x94) from
[<c01dbd00>] (bus_add_driver+0xa0/0x25c)
[  148.533561] [<c01dbd00>] (bus_add_driver+0xa0/0x25c) from
[<c01dd284>] (driver_register+0x6c/0x154)
[  148.542581] [<c01dd284>] (driver_register+0x6c/0x154) from
[<c01a92dc>] (__pci_register_driver+0x38/0xa8)
[  148.552148] [<c01a92dc>] (__pci_register_driver+0x38/0xa8) from
[<bf049014>] (rtl92ce_module_init+0x14/0x58 [rtl8192ce])
[  148.563007] [<bf049014>] (rtl92ce_module_init+0x14/0x58
[rtl8192ce]) from [<c0008578>] (do_one_initcall+0x34/0x19c)
[  148.573413] [<c0008578>] (do_one_initcall+0x34/0x19c) from
[<c004f62c>] (sys_init_module+0xf4/0x13a4)
[  148.582609] [<c004f62c>] (sys_init_module+0xf4/0x13a4) from
[<c0008d40>] (ret_fast_syscall+0x0/0x2c)
[  148.591713] Code: e598201c e2022007 e5ca2377 e59d0030 (e1d002b0)
[  148.602704] ---[ end trace be518a2fa6d9a905 ]---

^ permalink raw reply

* RE: [PATCH net-next-2.6 2/2] be2net: use stats-sync to read/write 64-bit stats
From: Sathya.Perla @ 2011-07-27  5:34 UTC (permalink / raw)
  To: shemminger; +Cc: netdev
In-Reply-To: <20110726075826.26bca7a9@s6510.ftrdhcpuser.net>

>-----Original Message-----
>From: Stephen Hemminger [mailto:shemminger@vyatta.com]
>Sent: Tuesday, July 26, 2011 8:28 PM
>
>On Tue, 26 Jul 2011 10:40:15 +0530
>Sathya Perla <sathya.perla@emulex.com> wrote:
>
>> 64-bit stats in be2net are written/read as follows using the stats-sync
>> interface for safe access in 32-bit archs:
>>
>> 64-bit 		sync			writer			reader
>> stats
>> --------------------------------------------------------------------------
>----
>> tx_stats	tx_stats->sync		be_xmit			be_get_stats64,
>> 								ethtool
>> tx-compl	tx_stats->sync_compl	tx-compl-processing	ethtool
>> rx-stats	rx_stats->sync		rx-compl-processing	be_get_stats64,
>> 								ethtool,
>> 								eqd-update
>>
>> This patch is based on Stephen Hemminger's earlier patch on the same
>issue...
>>
>> Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
>
>Is the tx complete stat even worth the effort? does it provide a useful
>metric?
>Since rx/tx bytes are already in regular stats, keeping them in ethtool
>stats
>is redundant.
>
>These are just minor nits, you can ignore this advice if you want.

Stephen, in the 1/2 patch (in this set) I did remove the aggregate rx/tx bytes & pkts
from ethtool to avoid duplication. I've retained the *per-ring* stats in
ethtool as they are not provided by be_get_stats64().

The per-ring tx-compl stat, has helped on occasion in field debugging.

^ permalink raw reply

* [PATCH 2/2] ASIX: Use only 11 bits of header for data size
From: Marek Vasut @ 2011-07-27  2:44 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev, linux-usb, gregkh, Marek Vasut
In-Reply-To: <1311734687-23551-1-git-send-email-marek.vasut@gmail.com>

The AX88772B uses only 11 bits of the header for the actual size. The other bits
are used for something else. This causes dmesg full of messages:

	asix_rx_fixup() Bad Header Length

This patch trims the check to only 11 bits. I believe on older chips, the
remaining 5 top bits are unused.

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
---
 drivers/net/usb/asix.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

NOTE: If possible, can someone test/verify this patch with other ASIX chips ?
NOTE2: If Ack-ed, Greg, can you get this into -stable?

diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index d5b62a4..c5c4b4d 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -314,11 +314,11 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	skb_pull(skb, 4);
 
 	while (skb->len > 0) {
-		if ((header & 0xffff) != ((~header >> 16) & 0xffff))
+		if ((header & 0x07ff) != ((~header >> 16) & 0x07ff))
 			netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
 
 		/* get the packet length */
-		size = (u16) (header & 0x0000ffff);
+		size = (u16) (header & 0x000007ff);
 
 		if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
 			u8 alignment = (unsigned long)skb->data & 0x3;
-- 
1.7.5.3

^ permalink raw reply related

* [PATCH 1/2] ASIX: Simplify condition in rx_fixup()
From: Marek Vasut @ 2011-07-27  2:44 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev, linux-usb, gregkh, Marek Vasut

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
---
 drivers/net/usb/asix.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index 5250288..d5b62a4 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -314,10 +314,9 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	skb_pull(skb, 4);
 
 	while (skb->len > 0) {
-		if ((short)(header & 0x0000ffff) !=
-		    ~((short)((header & 0xffff0000) >> 16))) {
+		if ((header & 0xffff) != ((~header >> 16) & 0xffff))
 			netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
-		}
+
 		/* get the packet length */
 		size = (u16) (header & 0x0000ffff);
 
-- 
1.7.5.3

^ permalink raw reply related

* [PATCH 8/8] bna: Tx and Rx Redesign
From: Rasesh Mody @ 2011-07-27  2:10 UTC (permalink / raw)
  To: davem, netdev; +Cc: adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1311732648-29876-1-git-send-email-rmody@brocade.com>

Change details:
 - This patch contains the changes as a result of redesigning of Tx, Rx data
   path setup. In the old design, setting up Txqs, Rxqs were done in the driver.
   With the new design, most of the hardware setup steps for the Txq, Rxqs are
   moved to FW. Host driver issues commands to FW through the message queue to
   setup/teardown tx, rx data path. FW performs necessary steps and responds
   back to the driver with a status.
 - As a result of this redesign, the state machine implementation for Tx, Rx
   objects have changed significantly. Instead of doing the raw register access,
   these state machines mostly send a command to FW and wait for response and
   take the next action. In addition to tx, rx datapath setup, this patch also
   deals with rx filter configuration - such as unicast address, multicast
   address, vlan filter, promiscuous mode etc.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/bna/bna_tx_rx.c | 3966 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 3966 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/bna/bna_tx_rx.c

diff --git a/drivers/net/bna/bna_tx_rx.c b/drivers/net/bna/bna_tx_rx.c
new file mode 100644
index 0000000..03799eb
--- /dev/null
+++ b/drivers/net/bna/bna_tx_rx.c
@@ -0,0 +1,3966 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+  */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include "bna.h"
+#include "bfi.h"
+
+/**
+ * IB
+ */
+void
+bna_ib_coalescing_timeo_set(struct bna_ib *ib, u8 coalescing_timeo)
+{
+	ib->coalescing_timeo = coalescing_timeo;
+	ib->door_bell.doorbell_ack = BNA_DOORBELL_IB_INT_ACK(
+				(u32)ib->coalescing_timeo, 0);
+}
+
+/**
+ * RXF
+ */
+
+#define bna_rxf_vlan_cfg_soft_reset(rxf)				\
+do {									\
+	(rxf)->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL;		\
+	(rxf)->vlan_strip_pending = true;				\
+} while (0)
+
+#define bna_rxf_rss_cfg_soft_reset(rxf)					\
+do {									\
+	if ((rxf)->rss_status == BNA_STATUS_T_ENABLED)			\
+		(rxf)->rss_pending = (BNA_RSS_F_RIT_PENDING |		\
+				BNA_RSS_F_CFG_PENDING |			\
+				BNA_RSS_F_STATUS_PENDING);		\
+} while (0)
+
+static int bna_rxf_cfg_apply(struct bna_rxf *rxf);
+static void bna_rxf_cfg_reset(struct bna_rxf *rxf);
+static int bna_rxf_fltr_clear(struct bna_rxf *rxf);
+static int bna_rxf_ucast_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_promisc_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_allmulti_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_vlan_strip_cfg_apply(struct bna_rxf *rxf);
+static int bna_rxf_ucast_cfg_reset(struct bna_rxf *rxf,
+					enum bna_cleanup_type cleanup);
+static int bna_rxf_promisc_cfg_reset(struct bna_rxf *rxf,
+					enum bna_cleanup_type cleanup);
+static int bna_rxf_allmulti_cfg_reset(struct bna_rxf *rxf,
+					enum bna_cleanup_type cleanup);
+
+bfa_fsm_state_decl(bna_rxf, stopped, struct bna_rxf,
+			enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, paused, struct bna_rxf,
+			enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, cfg_wait, struct bna_rxf,
+			enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, started, struct bna_rxf,
+			enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, fltr_clr_wait, struct bna_rxf,
+			enum bna_rxf_event);
+bfa_fsm_state_decl(bna_rxf, last_resp_wait, struct bna_rxf,
+			enum bna_rxf_event);
+
+static void
+bna_rxf_sm_stopped_entry(struct bna_rxf *rxf)
+{
+	call_rxf_stop_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_stopped(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_START:
+		if (rxf->flags & BNA_RXF_F_PAUSED) {
+			bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+			call_rxf_start_cbfn(rxf);
+		} else
+			bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+		break;
+
+	case RXF_E_STOP:
+		call_rxf_stop_cbfn(rxf);
+		break;
+
+	case RXF_E_FAIL:
+		/* No-op */
+		break;
+
+	case RXF_E_CONFIG:
+		call_rxf_cam_fltr_cbfn(rxf);
+		break;
+
+	case RXF_E_PAUSE:
+		rxf->flags |= BNA_RXF_F_PAUSED;
+		call_rxf_pause_cbfn(rxf);
+		break;
+
+	case RXF_E_RESUME:
+		rxf->flags &= ~BNA_RXF_F_PAUSED;
+		call_rxf_resume_cbfn(rxf);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_rxf_sm_paused_entry(struct bna_rxf *rxf)
+{
+	call_rxf_pause_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_paused(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_STOP:
+	case RXF_E_FAIL:
+		bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+		break;
+
+	case RXF_E_CONFIG:
+		call_rxf_cam_fltr_cbfn(rxf);
+		break;
+
+	case RXF_E_RESUME:
+		rxf->flags &= ~BNA_RXF_F_PAUSED;
+		bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_rxf_sm_cfg_wait_entry(struct bna_rxf *rxf)
+{
+	if (!bna_rxf_cfg_apply(rxf)) {
+		/* No more pending config updates */
+		bfa_fsm_set_state(rxf, bna_rxf_sm_started);
+	}
+}
+
+static void
+bna_rxf_sm_cfg_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_STOP:
+		bfa_fsm_set_state(rxf, bna_rxf_sm_last_resp_wait);
+		break;
+
+	case RXF_E_FAIL:
+		bna_rxf_cfg_reset(rxf);
+		call_rxf_start_cbfn(rxf);
+		call_rxf_cam_fltr_cbfn(rxf);
+		call_rxf_resume_cbfn(rxf);
+		bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+		break;
+
+	case RXF_E_CONFIG:
+		/* No-op */
+		break;
+
+	case RXF_E_PAUSE:
+		rxf->flags |= BNA_RXF_F_PAUSED;
+		call_rxf_start_cbfn(rxf);
+		bfa_fsm_set_state(rxf, bna_rxf_sm_fltr_clr_wait);
+		break;
+
+	case RXF_E_FW_RESP:
+		if (!bna_rxf_cfg_apply(rxf)) {
+			/* No more pending config updates */
+			bfa_fsm_set_state(rxf, bna_rxf_sm_started);
+		}
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_rxf_sm_started_entry(struct bna_rxf *rxf)
+{
+	call_rxf_start_cbfn(rxf);
+	call_rxf_cam_fltr_cbfn(rxf);
+	call_rxf_resume_cbfn(rxf);
+}
+
+static void
+bna_rxf_sm_started(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_STOP:
+	case RXF_E_FAIL:
+		bna_rxf_cfg_reset(rxf);
+		bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+		break;
+
+	case RXF_E_CONFIG:
+		bfa_fsm_set_state(rxf, bna_rxf_sm_cfg_wait);
+		break;
+
+	case RXF_E_PAUSE:
+		rxf->flags |= BNA_RXF_F_PAUSED;
+		if (!bna_rxf_fltr_clear(rxf))
+			bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+		else
+			bfa_fsm_set_state(rxf, bna_rxf_sm_fltr_clr_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_rxf_sm_fltr_clr_wait_entry(struct bna_rxf *rxf)
+{
+}
+
+static void
+bna_rxf_sm_fltr_clr_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_FAIL:
+		bna_rxf_cfg_reset(rxf);
+		call_rxf_pause_cbfn(rxf);
+		bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+		break;
+
+	case RXF_E_FW_RESP:
+		if (!bna_rxf_fltr_clear(rxf)) {
+			/* No more pending CAM entries to clear */
+			bfa_fsm_set_state(rxf, bna_rxf_sm_paused);
+		}
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_rxf_sm_last_resp_wait_entry(struct bna_rxf *rxf)
+{
+}
+
+static void
+bna_rxf_sm_last_resp_wait(struct bna_rxf *rxf, enum bna_rxf_event event)
+{
+	switch (event) {
+	case RXF_E_FAIL:
+	case RXF_E_FW_RESP:
+		bna_rxf_cfg_reset(rxf);
+		bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_bfi_ucast_req(struct bna_rxf *rxf, struct bna_mac *mac,
+		enum bfi_enet_h2i_msgs req_type)
+{
+	struct bfi_enet_ucast_req *req = &rxf->bfi_enet_cmd.ucast_req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, req_type, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+	bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_ucast_req)));
+	memcpy(&req->mac_addr, &mac->addr, sizeof(mac_t));
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_ucast_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_add_req(struct bna_rxf *rxf, struct bna_mac *mac)
+{
+	struct bfi_enet_mcast_add_req *req =
+		&rxf->bfi_enet_cmd.mcast_add_req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, BFI_ENET_H2I_MAC_MCAST_ADD_REQ,
+		0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+	bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_mcast_add_req)));
+	memcpy(&req->mac_addr, &mac->addr, sizeof(mac_t));
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_mcast_add_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_del_req(struct bna_rxf *rxf, u16 handle)
+{
+	struct bfi_enet_mcast_del_req *req =
+		&rxf->bfi_enet_cmd.mcast_del_req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET, BFI_ENET_H2I_MAC_MCAST_DEL_REQ,
+		0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+	bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_mcast_del_req)));
+	req->handle = htons(handle);
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_mcast_del_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_mcast_filter_req(struct bna_rxf *rxf, enum bna_status status)
+{
+	struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_MAC_MCAST_FILTER_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	req->enable = status;
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_promisc_req(struct bna_rxf *rxf, enum bna_status status)
+{
+	struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RX_PROMISCUOUS_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	req->enable = status;
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_vlan_filter_set(struct bna_rxf *rxf, u8 block_idx)
+{
+	struct bfi_enet_rx_vlan_req *req = &rxf->bfi_enet_cmd.vlan_req;
+	int i;
+	int j;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RX_VLAN_SET_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rx_vlan_req)));
+	req->block_idx = block_idx;
+	for (i = 0; i < (BFI_ENET_VLAN_BLOCK_SIZE / 32); i++) {
+		j = (block_idx * (BFI_ENET_VLAN_BLOCK_SIZE / 32)) + i;
+		if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED)
+			req->bit_mask[i] =
+				htonl(rxf->vlan_filter_table[j]);
+		else
+			req->bit_mask[i] = 0xFFFFFFFF;
+	}
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_rx_vlan_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_vlan_strip_enable(struct bna_rxf *rxf)
+{
+	struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	req->enable = rxf->vlan_strip_status;
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rit_cfg(struct bna_rxf *rxf)
+{
+	struct bfi_enet_rit_req *req = &rxf->bfi_enet_cmd.rit_req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RIT_CFG_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rit_req)));
+	req->size = htons(rxf->rit_size);
+	memcpy(&req->table[0], rxf->rit, rxf->rit_size);
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_rit_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rss_cfg(struct bna_rxf *rxf)
+{
+	struct bfi_enet_rss_cfg_req *req = &rxf->bfi_enet_cmd.rss_req;
+	int i;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RSS_CFG_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rss_cfg_req)));
+	req->cfg.type = rxf->rss_cfg.hash_type;
+	req->cfg.mask = rxf->rss_cfg.hash_mask;
+	for (i = 0; i < BFI_ENET_RSS_KEY_LEN; i++)
+		req->cfg.key[i] =
+			htonl(rxf->rss_cfg.toeplitz_hash_key[i]);
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_rss_cfg_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+static void
+bna_bfi_rss_enable(struct bna_rxf *rxf)
+{
+	struct bfi_enet_enable_req *req = &rxf->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RSS_ENABLE_REQ, 0, rxf->rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	req->enable = rxf->rss_status;
+	bfa_msgq_cmd_set(&rxf->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &req->mh);
+	bfa_msgq_cmd_post(&rxf->rx->bna->msgq, &rxf->msgq_cmd);
+}
+
+/* This function gets the multicast MAC that has already been added to CAM */
+static struct bna_mac *
+bna_rxf_mcmac_get(struct bna_rxf *rxf, u8 *mac_addr)
+{
+	struct bna_mac *mac;
+	struct list_head *qe;
+
+	list_for_each(qe, &rxf->mcast_active_q) {
+		mac = (struct bna_mac *)qe;
+		if (BNA_MAC_IS_EQUAL(&mac->addr, mac_addr))
+			return mac;
+	}
+
+	list_for_each(qe, &rxf->mcast_pending_del_q) {
+		mac = (struct bna_mac *)qe;
+		if (BNA_MAC_IS_EQUAL(&mac->addr, mac_addr))
+			return mac;
+	}
+
+	return NULL;
+}
+
+static struct bna_mcam_handle *
+bna_rxf_mchandle_get(struct bna_rxf *rxf, int handle)
+{
+	struct bna_mcam_handle *mchandle;
+	struct list_head *qe;
+
+	list_for_each(qe, &rxf->mcast_handle_q) {
+		mchandle = (struct bna_mcam_handle *)qe;
+		if (mchandle->handle == handle)
+			return mchandle;
+	}
+
+	return NULL;
+}
+
+static void
+bna_rxf_mchandle_attach(struct bna_rxf *rxf, u8 *mac_addr, int handle)
+{
+	struct bna_mac *mcmac;
+	struct bna_mcam_handle *mchandle;
+
+	mcmac = bna_rxf_mcmac_get(rxf, mac_addr);
+	mchandle = bna_rxf_mchandle_get(rxf, handle);
+	if (mchandle == NULL) {
+		mchandle = bna_mcam_mod_handle_get(&rxf->rx->bna->mcam_mod);
+		mchandle->handle = handle;
+		mchandle->refcnt = 0;
+		list_add_tail(&mchandle->qe, &rxf->mcast_handle_q);
+	}
+	mchandle->refcnt++;
+	mcmac->handle = mchandle;
+}
+
+static int
+bna_rxf_mcast_del(struct bna_rxf *rxf, struct bna_mac *mac,
+		enum bna_cleanup_type cleanup)
+{
+	struct bna_mcam_handle *mchandle;
+	int ret = 0;
+
+	mchandle = mac->handle;
+	if (mchandle == NULL)
+		return ret;
+
+	mchandle->refcnt--;
+	if (mchandle->refcnt == 0) {
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_mcast_del_req(rxf, mchandle->handle);
+			ret = 1;
+		}
+		list_del(&mchandle->qe);
+		bfa_q_qe_init(&mchandle->qe);
+		bna_mcam_mod_handle_put(&rxf->rx->bna->mcam_mod, mchandle);
+	}
+	mac->handle = NULL;
+
+	return ret;
+}
+
+static int
+bna_rxf_mcast_cfg_apply(struct bna_rxf *rxf)
+{
+	struct bna_mac *mac = NULL;
+	struct list_head *qe;
+	int ret;
+
+	/* Delete multicast entries previousely added */
+	while (!list_empty(&rxf->mcast_pending_del_q)) {
+		bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		ret = bna_rxf_mcast_del(rxf, mac, BNA_HARD_CLEANUP);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+		if (ret)
+			return ret;
+	}
+
+	/* Add multicast entries */
+	if (!list_empty(&rxf->mcast_pending_add_q)) {
+		bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		list_add_tail(&mac->qe, &rxf->mcast_active_q);
+		bna_bfi_mcast_add_req(rxf, mac);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_vlan_cfg_apply(struct bna_rxf *rxf)
+{
+	u8 vlan_pending_bitmask;
+	int block_idx = 0;
+
+	if (rxf->vlan_pending_bitmask) {
+		vlan_pending_bitmask = rxf->vlan_pending_bitmask;
+		while (!(vlan_pending_bitmask & 0x1)) {
+			block_idx++;
+			vlan_pending_bitmask >>= 1;
+		}
+		rxf->vlan_pending_bitmask &= ~(1 << block_idx);
+		bna_bfi_rx_vlan_filter_set(rxf, block_idx);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_mcast_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+	struct list_head *qe;
+	struct bna_mac *mac;
+	int ret;
+
+	/* Throw away delete pending mcast entries */
+	while (!list_empty(&rxf->mcast_pending_del_q)) {
+		bfa_q_deq(&rxf->mcast_pending_del_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		ret = bna_rxf_mcast_del(rxf, mac, cleanup);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+		if (ret)
+			return ret;
+	}
+
+	/* Move active mcast entries to pending_add_q */
+	while (!list_empty(&rxf->mcast_active_q)) {
+		bfa_q_deq(&rxf->mcast_active_q, &qe);
+		bfa_q_qe_init(qe);
+		list_add_tail(qe, &rxf->mcast_pending_add_q);
+		mac = (struct bna_mac *)qe;
+		if (bna_rxf_mcast_del(rxf, mac, cleanup))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_rss_cfg_apply(struct bna_rxf *rxf)
+{
+	if (rxf->rss_pending) {
+		if (rxf->rss_pending & BNA_RSS_F_RIT_PENDING) {
+			rxf->rss_pending &= ~BNA_RSS_F_RIT_PENDING;
+			bna_bfi_rit_cfg(rxf);
+			return 1;
+		}
+
+		if (rxf->rss_pending & BNA_RSS_F_CFG_PENDING) {
+			rxf->rss_pending &= ~BNA_RSS_F_CFG_PENDING;
+			bna_bfi_rss_cfg(rxf);
+			return 1;
+		}
+
+		if (rxf->rss_pending & BNA_RSS_F_STATUS_PENDING) {
+			rxf->rss_pending &= ~BNA_RSS_F_STATUS_PENDING;
+			bna_bfi_rss_enable(rxf);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_cfg_apply(struct bna_rxf *rxf)
+{
+	if (bna_rxf_ucast_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_mcast_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_promisc_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_allmulti_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_vlan_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_vlan_strip_cfg_apply(rxf))
+		return 1;
+
+	if (bna_rxf_rss_cfg_apply(rxf))
+		return 1;
+
+	return 0;
+}
+
+/* Only software reset */
+static int
+bna_rxf_fltr_clear(struct bna_rxf *rxf)
+{
+	if (bna_rxf_ucast_cfg_reset(rxf, BNA_HARD_CLEANUP))
+		return 1;
+
+	if (bna_rxf_mcast_cfg_reset(rxf, BNA_HARD_CLEANUP))
+		return 1;
+
+	if (bna_rxf_promisc_cfg_reset(rxf, BNA_HARD_CLEANUP))
+		return 1;
+
+	if (bna_rxf_allmulti_cfg_reset(rxf, BNA_HARD_CLEANUP))
+		return 1;
+
+	return 0;
+}
+
+static void
+bna_rxf_cfg_reset(struct bna_rxf *rxf)
+{
+	bna_rxf_ucast_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+	bna_rxf_mcast_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+	bna_rxf_promisc_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+	bna_rxf_allmulti_cfg_reset(rxf, BNA_SOFT_CLEANUP);
+	bna_rxf_vlan_cfg_soft_reset(rxf);
+	bna_rxf_rss_cfg_soft_reset(rxf);
+}
+
+static void
+bna_rit_init(struct bna_rxf *rxf, int rit_size)
+{
+	struct bna_rx *rx = rxf->rx;
+	struct bna_rxp *rxp;
+	struct list_head *qe;
+	int offset = 0;
+
+	rxf->rit_size = rit_size;
+	list_for_each(qe, &rx->rxp_q) {
+		rxp = (struct bna_rxp *)qe;
+		rxf->rit[offset] = rxp->cq.ccb->id;
+		offset++;
+	}
+
+}
+
+void
+bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr)
+{
+	bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
+}
+
+void
+bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf,
+			struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_mcast_add_req *req =
+		&rxf->bfi_enet_cmd.mcast_add_req;
+	struct bfi_enet_mcast_add_rsp *rsp =
+		(struct bfi_enet_mcast_add_rsp *)msghdr;
+
+	bna_rxf_mchandle_attach(rxf, (u8 *)&req->mac_addr,
+		ntohs(rsp->handle));
+	bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
+}
+
+static void
+bna_rxf_init(struct bna_rxf *rxf,
+		struct bna_rx *rx,
+		struct bna_rx_config *q_config,
+		struct bna_res_info *res_info)
+{
+	rxf->rx = rx;
+
+	INIT_LIST_HEAD(&rxf->ucast_pending_add_q);
+	INIT_LIST_HEAD(&rxf->ucast_pending_del_q);
+	rxf->ucast_pending_set = 0;
+	rxf->ucast_active_set = 0;
+	INIT_LIST_HEAD(&rxf->ucast_active_q);
+	rxf->ucast_pending_mac = NULL;
+
+	INIT_LIST_HEAD(&rxf->mcast_pending_add_q);
+	INIT_LIST_HEAD(&rxf->mcast_pending_del_q);
+	INIT_LIST_HEAD(&rxf->mcast_active_q);
+	INIT_LIST_HEAD(&rxf->mcast_handle_q);
+
+	if (q_config->paused)
+		rxf->flags |= BNA_RXF_F_PAUSED;
+
+	rxf->rit = (u8 *)
+		res_info[BNA_RX_RES_MEM_T_RIT].res_u.mem_info.mdl[0].kva;
+	bna_rit_init(rxf, q_config->num_paths);
+
+	rxf->rss_status = q_config->rss_status;
+	if (rxf->rss_status == BNA_STATUS_T_ENABLED) {
+		rxf->rss_cfg = q_config->rss_config;
+		rxf->rss_pending |= BNA_RSS_F_CFG_PENDING;
+		rxf->rss_pending |= BNA_RSS_F_RIT_PENDING;
+		rxf->rss_pending |= BNA_RSS_F_STATUS_PENDING;
+	}
+
+	rxf->vlan_filter_status = BNA_STATUS_T_DISABLED;
+	memset(rxf->vlan_filter_table, 0,
+			(sizeof(u32) * ((BFI_ENET_VLAN_ID_MAX + 1) / 32)));
+	rxf->vlan_filter_table[0] |= 1; /* for pure priority tagged frames */
+	rxf->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL;
+
+	rxf->vlan_strip_status = q_config->vlan_strip_status;
+
+	bfa_fsm_set_state(rxf, bna_rxf_sm_stopped);
+}
+
+static void
+bna_rxf_uninit(struct bna_rxf *rxf)
+{
+	struct bna_mac *mac;
+
+	rxf->ucast_pending_set = 0;
+	rxf->ucast_active_set = 0;
+
+	while (!list_empty(&rxf->ucast_pending_add_q)) {
+		bfa_q_deq(&rxf->ucast_pending_add_q, &mac);
+		bfa_q_qe_init(&mac->qe);
+		bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+	}
+
+	if (rxf->ucast_pending_mac) {
+		bfa_q_qe_init(&rxf->ucast_pending_mac->qe);
+		bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod,
+			rxf->ucast_pending_mac);
+		rxf->ucast_pending_mac = NULL;
+	}
+
+	while (!list_empty(&rxf->mcast_pending_add_q)) {
+		bfa_q_deq(&rxf->mcast_pending_add_q, &mac);
+		bfa_q_qe_init(&mac->qe);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+	}
+
+	rxf->rxmode_pending = 0;
+	rxf->rxmode_pending_bitmask = 0;
+	if (rxf->rx->bna->promisc_rid == rxf->rx->rid)
+		rxf->rx->bna->promisc_rid = BFI_INVALID_RID;
+	if (rxf->rx->bna->default_mode_rid == rxf->rx->rid)
+		rxf->rx->bna->default_mode_rid = BFI_INVALID_RID;
+
+	rxf->rss_pending = 0;
+	rxf->vlan_strip_pending = false;
+
+	rxf->flags = 0;
+
+	rxf->rx = NULL;
+}
+
+static void
+bna_rx_cb_rxf_started(struct bna_rx *rx)
+{
+	bfa_fsm_send_event(rx, RX_E_RXF_STARTED);
+}
+
+static void
+bna_rxf_start(struct bna_rxf *rxf)
+{
+	rxf->start_cbfn = bna_rx_cb_rxf_started;
+	rxf->start_cbarg = rxf->rx;
+	bfa_fsm_send_event(rxf, RXF_E_START);
+}
+
+static void
+bna_rx_cb_rxf_stopped(struct bna_rx *rx)
+{
+	bfa_fsm_send_event(rx, RX_E_RXF_STOPPED);
+}
+
+static void
+bna_rxf_stop(struct bna_rxf *rxf)
+{
+	rxf->stop_cbfn = bna_rx_cb_rxf_stopped;
+	rxf->stop_cbarg = rxf->rx;
+	bfa_fsm_send_event(rxf, RXF_E_STOP);
+}
+
+static void
+bna_rxf_fail(struct bna_rxf *rxf)
+{
+	bfa_fsm_send_event(rxf, RXF_E_FAIL);
+}
+
+enum bna_cb_status
+bna_rx_ucast_set(struct bna_rx *rx, u8 *ucmac,
+		 void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+
+	if (rxf->ucast_pending_mac == NULL) {
+		rxf->ucast_pending_mac =
+				bna_ucam_mod_mac_get(&rxf->rx->bna->ucam_mod);
+		if (rxf->ucast_pending_mac == NULL)
+			return BNA_CB_UCAST_CAM_FULL;
+		bfa_q_qe_init(&rxf->ucast_pending_mac->qe);
+	}
+
+	memcpy(rxf->ucast_pending_mac->addr, ucmac, ETH_ALEN);
+	rxf->ucast_pending_set = 1;
+	rxf->cam_fltr_cbfn = cbfn;
+	rxf->cam_fltr_cbarg = rx->bna->bnad;
+
+	bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+	return BNA_CB_SUCCESS;
+}
+
+enum bna_cb_status
+bna_rx_mcast_add(struct bna_rx *rx, u8 *addr,
+		 void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	struct bna_mac *mac;
+
+	/* Check if already added or pending addition */
+	if (bna_mac_find(&rxf->mcast_active_q, addr) ||
+		bna_mac_find(&rxf->mcast_pending_add_q, addr)) {
+		if (cbfn)
+			cbfn(rx->bna->bnad, rx);
+		return BNA_CB_SUCCESS;
+	}
+
+	mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
+	if (mac == NULL)
+		return BNA_CB_MCAST_LIST_FULL;
+	bfa_q_qe_init(&mac->qe);
+	memcpy(mac->addr, addr, ETH_ALEN);
+	list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
+
+	rxf->cam_fltr_cbfn = cbfn;
+	rxf->cam_fltr_cbarg = rx->bna->bnad;
+
+	bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+	return BNA_CB_SUCCESS;
+}
+
+enum bna_cb_status
+bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mclist,
+		     void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	struct list_head list_head;
+	struct list_head *qe;
+	u8 *mcaddr;
+	struct bna_mac *mac;
+	int i;
+
+	/* Allocate nodes */
+	INIT_LIST_HEAD(&list_head);
+	for (i = 0, mcaddr = mclist; i < count; i++) {
+		mac = bna_mcam_mod_mac_get(&rxf->rx->bna->mcam_mod);
+		if (mac == NULL)
+			goto err_return;
+		bfa_q_qe_init(&mac->qe);
+		memcpy(mac->addr, mcaddr, ETH_ALEN);
+		list_add_tail(&mac->qe, &list_head);
+
+		mcaddr += ETH_ALEN;
+	}
+
+	/* Purge the pending_add_q */
+	while (!list_empty(&rxf->mcast_pending_add_q)) {
+		bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+	}
+
+	/* Schedule active_q entries for deletion */
+	while (!list_empty(&rxf->mcast_active_q)) {
+		bfa_q_deq(&rxf->mcast_active_q, &qe);
+		mac = (struct bna_mac *)qe;
+		bfa_q_qe_init(&mac->qe);
+		list_add_tail(&mac->qe, &rxf->mcast_pending_del_q);
+	}
+
+	/* Add the new entries */
+	while (!list_empty(&list_head)) {
+		bfa_q_deq(&list_head, &qe);
+		mac = (struct bna_mac *)qe;
+		bfa_q_qe_init(&mac->qe);
+		list_add_tail(&mac->qe, &rxf->mcast_pending_add_q);
+	}
+
+	rxf->cam_fltr_cbfn = cbfn;
+	rxf->cam_fltr_cbarg = rx->bna->bnad;
+	bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+	return BNA_CB_SUCCESS;
+
+err_return:
+	while (!list_empty(&list_head)) {
+		bfa_q_deq(&list_head, &qe);
+		mac = (struct bna_mac *)qe;
+		bfa_q_qe_init(&mac->qe);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+	}
+
+	return BNA_CB_MCAST_LIST_FULL;
+}
+
+void
+bna_rx_vlan_add(struct bna_rx *rx, int vlan_id)
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	int index = (vlan_id >> BFI_VLAN_WORD_SHIFT);
+	int bit = (1 << (vlan_id & BFI_VLAN_WORD_MASK));
+	int group_id = (vlan_id >> BFI_VLAN_BLOCK_SHIFT);
+
+	rxf->vlan_filter_table[index] |= bit;
+	if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
+		rxf->vlan_pending_bitmask |= (1 << group_id);
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+	}
+}
+
+void
+bna_rx_vlan_del(struct bna_rx *rx, int vlan_id)
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	int index = (vlan_id >> BFI_VLAN_WORD_SHIFT);
+	int bit = (1 << (vlan_id & BFI_VLAN_WORD_MASK));
+	int group_id = (vlan_id >> BFI_VLAN_BLOCK_SHIFT);
+
+	rxf->vlan_filter_table[index] &= ~bit;
+	if (rxf->vlan_filter_status == BNA_STATUS_T_ENABLED) {
+		rxf->vlan_pending_bitmask |= (1 << group_id);
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+	}
+}
+
+static int
+bna_rxf_ucast_cfg_apply(struct bna_rxf *rxf)
+{
+	struct bna_mac *mac = NULL;
+	struct list_head *qe;
+
+	/* Delete MAC addresses previousely added */
+	if (!list_empty(&rxf->ucast_pending_del_q)) {
+		bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		bna_bfi_ucast_req(rxf, mac, BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+		bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+		return 1;
+	}
+
+	/* Set default unicast MAC */
+	if (rxf->ucast_pending_set) {
+		rxf->ucast_pending_set = 0;
+		memcpy(rxf->ucast_active_mac.addr,
+			rxf->ucast_pending_mac->addr, ETH_ALEN);
+		rxf->ucast_active_set = 1;
+		bna_bfi_ucast_req(rxf, &rxf->ucast_active_mac,
+			BFI_ENET_H2I_MAC_UCAST_SET_REQ);
+		return 1;
+	}
+
+	/* Add additional MAC entries */
+	if (!list_empty(&rxf->ucast_pending_add_q)) {
+		bfa_q_deq(&rxf->ucast_pending_add_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		list_add_tail(&mac->qe, &rxf->ucast_active_q);
+		bna_bfi_ucast_req(rxf, mac, BFI_ENET_H2I_MAC_UCAST_ADD_REQ);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_ucast_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+	struct list_head *qe;
+	struct bna_mac *mac;
+
+	/* Throw away delete pending ucast entries */
+	while (!list_empty(&rxf->ucast_pending_del_q)) {
+		bfa_q_deq(&rxf->ucast_pending_del_q, &qe);
+		bfa_q_qe_init(qe);
+		mac = (struct bna_mac *)qe;
+		if (cleanup == BNA_SOFT_CLEANUP)
+			bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+		else {
+			bna_bfi_ucast_req(rxf, mac,
+				BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+			bna_ucam_mod_mac_put(&rxf->rx->bna->ucam_mod, mac);
+			return 1;
+		}
+	}
+
+	/* Move active ucast entries to pending_add_q */
+	while (!list_empty(&rxf->ucast_active_q)) {
+		bfa_q_deq(&rxf->ucast_active_q, &qe);
+		bfa_q_qe_init(qe);
+		list_add_tail(qe, &rxf->ucast_pending_add_q);
+		if (cleanup == BNA_HARD_CLEANUP) {
+			mac = (struct bna_mac *)qe;
+			bna_bfi_ucast_req(rxf, mac,
+				BFI_ENET_H2I_MAC_UCAST_DEL_REQ);
+			return 1;
+		}
+	}
+
+	if (rxf->ucast_active_set) {
+		rxf->ucast_pending_set = 1;
+		rxf->ucast_active_set = 0;
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_ucast_req(rxf, &rxf->ucast_active_mac,
+				BFI_ENET_H2I_MAC_UCAST_CLR_REQ);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_promisc_cfg_apply(struct bna_rxf *rxf)
+{
+	struct bna *bna = rxf->rx->bna;
+
+	/* Enable/disable promiscuous mode */
+	if (is_promisc_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask)) {
+		/* move promisc configuration from pending -> active */
+		promisc_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active |= BNA_RXMODE_PROMISC;
+		bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_ENABLED);
+		return 1;
+	} else if (is_promisc_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask)) {
+		/* move promisc configuration from pending -> active */
+		promisc_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+		bna->promisc_rid = BFI_INVALID_RID;
+		bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_promisc_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+	struct bna *bna = rxf->rx->bna;
+
+	/* Clear pending promisc mode disable */
+	if (is_promisc_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask)) {
+		promisc_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+		bna->promisc_rid = BFI_INVALID_RID;
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+			return 1;
+		}
+	}
+
+	/* Move promisc mode config from active -> pending */
+	if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
+		promisc_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_PROMISC;
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_rx_promisc_req(rxf, BNA_STATUS_T_DISABLED);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_allmulti_cfg_apply(struct bna_rxf *rxf)
+{
+	/* Enable/disable allmulti mode */
+	if (is_allmulti_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask)) {
+		/* move allmulti configuration from pending -> active */
+		allmulti_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active |= BNA_RXMODE_ALLMULTI;
+		bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_DISABLED);
+		return 1;
+	} else if (is_allmulti_disable(rxf->rxmode_pending,
+					rxf->rxmode_pending_bitmask)) {
+		/* move allmulti configuration from pending -> active */
+		allmulti_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+		bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_allmulti_cfg_reset(struct bna_rxf *rxf, enum bna_cleanup_type cleanup)
+{
+	/* Clear pending allmulti mode disable */
+	if (is_allmulti_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask)) {
+		allmulti_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+			return 1;
+		}
+	}
+
+	/* Move allmulti mode config from active -> pending */
+	if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
+		allmulti_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		rxf->rxmode_active &= ~BNA_RXMODE_ALLMULTI;
+		if (cleanup == BNA_HARD_CLEANUP) {
+			bna_bfi_mcast_filter_req(rxf, BNA_STATUS_T_ENABLED);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+bna_rxf_promisc_enable(struct bna_rxf *rxf)
+{
+	struct bna *bna = rxf->rx->bna;
+	int ret = 0;
+
+	if (is_promisc_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask) ||
+		(rxf->rxmode_active & BNA_RXMODE_PROMISC)) {
+		/* Do nothing if pending enable or already enabled */
+	} else if (is_promisc_disable(rxf->rxmode_pending,
+					rxf->rxmode_pending_bitmask)) {
+		/* Turn off pending disable command */
+		promisc_inactive(rxf->rxmode_pending,
+			rxf->rxmode_pending_bitmask);
+	} else {
+		/* Schedule enable */
+		promisc_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		bna->promisc_rid = rxf->rx->rid;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int
+bna_rxf_promisc_disable(struct bna_rxf *rxf)
+{
+	struct bna *bna = rxf->rx->bna;
+	int ret = 0;
+
+	if (is_promisc_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask) ||
+		(!(rxf->rxmode_active & BNA_RXMODE_PROMISC))) {
+		/* Do nothing if pending disable or already disabled */
+	} else if (is_promisc_enable(rxf->rxmode_pending,
+					rxf->rxmode_pending_bitmask)) {
+		/* Turn off pending enable command */
+		promisc_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		bna->promisc_rid = BFI_INVALID_RID;
+	} else if (rxf->rxmode_active & BNA_RXMODE_PROMISC) {
+		/* Schedule disable */
+		promisc_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int
+bna_rxf_allmulti_enable(struct bna_rxf *rxf)
+{
+	int ret = 0;
+
+	if (is_allmulti_enable(rxf->rxmode_pending,
+			rxf->rxmode_pending_bitmask) ||
+			(rxf->rxmode_active & BNA_RXMODE_ALLMULTI)) {
+		/* Do nothing if pending enable or already enabled */
+	} else if (is_allmulti_disable(rxf->rxmode_pending,
+					rxf->rxmode_pending_bitmask)) {
+		/* Turn off pending disable command */
+		allmulti_inactive(rxf->rxmode_pending,
+			rxf->rxmode_pending_bitmask);
+	} else {
+		/* Schedule enable */
+		allmulti_enable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int
+bna_rxf_allmulti_disable(struct bna_rxf *rxf)
+{
+	int ret = 0;
+
+	if (is_allmulti_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask) ||
+		(!(rxf->rxmode_active & BNA_RXMODE_ALLMULTI))) {
+		/* Do nothing if pending disable or already disabled */
+	} else if (is_allmulti_enable(rxf->rxmode_pending,
+					rxf->rxmode_pending_bitmask)) {
+		/* Turn off pending enable command */
+		allmulti_inactive(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+	} else if (rxf->rxmode_active & BNA_RXMODE_ALLMULTI) {
+		/* Schedule disable */
+		allmulti_disable(rxf->rxmode_pending,
+				rxf->rxmode_pending_bitmask);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int
+bna_rxf_vlan_strip_cfg_apply(struct bna_rxf *rxf)
+{
+	if (rxf->vlan_strip_pending) {
+			rxf->vlan_strip_pending = false;
+			bna_bfi_vlan_strip_enable(rxf);
+			return 1;
+	}
+
+	return 0;
+}
+
+enum bna_cb_status
+bna_rx_mcast_del(struct bna_rx *rx, u8 *addr,
+		 void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	struct bna_mac *mac;
+
+
+	mac = bna_mac_find(&rxf->mcast_pending_add_q, addr);
+	if (mac) {
+		list_del(&mac->qe);
+		bfa_q_qe_init(&mac->qe);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+		if (cbfn)
+			(*cbfn)(rx->bna->bnad, rx);
+		return BNA_CB_SUCCESS;
+	}
+
+	mac = bna_mac_find(&rxf->mcast_active_q, addr);
+	if (mac) {
+		list_del(&mac->qe);
+		bfa_q_qe_init(&mac->qe);
+		list_add_tail(&mac->qe, &rxf->mcast_pending_del_q);
+		rxf->cam_fltr_cbfn = cbfn;
+		rxf->cam_fltr_cbarg = rx->bna->bnad;
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+		return BNA_CB_SUCCESS;
+	}
+
+	return BNA_CB_INVALID_MAC;
+}
+
+void
+bna_rx_mcast_delall(struct bna_rx *rx,
+		    void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	struct list_head *qe;
+	struct bna_mac *mac;
+	int need_hw_config = 0;
+
+
+	/* Purge all entries from pending_add_q */
+	while (!list_empty(&rxf->mcast_pending_add_q)) {
+		bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+		mac = (struct bna_mac *)qe;
+		bfa_q_qe_init(&mac->qe);
+		bna_mcam_mod_mac_put(&rxf->rx->bna->mcam_mod, mac);
+	}
+
+	/* Schedule all entries in active_q for deletion */
+	while (!list_empty(&rxf->mcast_active_q)) {
+		bfa_q_deq(&rxf->mcast_active_q, &qe);
+		mac = (struct bna_mac *)qe;
+		bfa_q_qe_init(&mac->qe);
+		list_add_tail(&mac->qe, &rxf->mcast_pending_del_q);
+		need_hw_config = 1;
+	}
+
+	if (need_hw_config) {
+		rxf->cam_fltr_cbfn = cbfn;
+		rxf->cam_fltr_cbarg = rx->bna->bnad;
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+		return;
+	}
+
+	if (cbfn)
+		(*cbfn)(rx->bna->bnad, rx);
+}
+
+/**
+ * RX
+ */
+
+#define	BNA_GET_RXQS(qcfg)	(((qcfg)->rxp_type == BNA_RXP_SINGLE) ?	\
+	(qcfg)->num_paths : ((qcfg)->num_paths * 2))
+
+#define	SIZE_TO_PAGES(size)	(((size) >> PAGE_SHIFT) + ((((size) &\
+	(PAGE_SIZE - 1)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT))
+
+#define	call_rx_stop_cbfn(rx)						\
+do {								    \
+	if ((rx)->stop_cbfn) {						\
+		void (*cbfn)(void *, struct bna_rx *);	  \
+		void *cbarg;					    \
+		cbfn = (rx)->stop_cbfn;				 \
+		cbarg = (rx)->stop_cbarg;			       \
+		(rx)->stop_cbfn = NULL;					\
+		(rx)->stop_cbarg = NULL;				\
+		cbfn(cbarg, rx);					\
+	}							       \
+} while (0)
+
+#define bfi_enet_datapath_q_init(bfi_q, bna_qpt)			\
+do {									\
+	struct bna_dma_addr cur_q_addr =				\
+		*((struct bna_dma_addr *)((bna_qpt)->kv_qpt_ptr));	\
+	(bfi_q)->pg_tbl.a32.addr_lo = (bna_qpt)->hw_qpt_ptr.lsb;	\
+	(bfi_q)->pg_tbl.a32.addr_hi = (bna_qpt)->hw_qpt_ptr.msb;	\
+	(bfi_q)->first_entry.a32.addr_lo = cur_q_addr.lsb;		\
+	(bfi_q)->first_entry.a32.addr_hi = cur_q_addr.msb;		\
+	(bfi_q)->pages = htons((u16)(bna_qpt)->page_count);	\
+	(bfi_q)->page_sz = htons((u16)(bna_qpt)->page_size);\
+} while (0)
+
+static void bna_bfi_rx_enet_start(struct bna_rx *rx);
+static void bna_rx_enet_stop(struct bna_rx *rx);
+static void bna_rx_mod_cb_rx_stopped(void *arg, struct bna_rx *rx);
+
+bfa_fsm_state_decl(bna_rx, stopped,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, start_wait,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, rxf_start_wait,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, started,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, rxf_stop_wait,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, stop_wait,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, cleanup_wait,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, failed,
+	struct bna_rx, enum bna_rx_event);
+bfa_fsm_state_decl(bna_rx, quiesce_wait,
+	struct bna_rx, enum bna_rx_event);
+
+static void bna_rx_sm_stopped_entry(struct bna_rx *rx)
+{
+	call_rx_stop_cbfn(rx);
+}
+
+static void bna_rx_sm_stopped(struct bna_rx *rx,
+				enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_START:
+		bfa_fsm_set_state(rx, bna_rx_sm_start_wait);
+		break;
+
+	case RX_E_STOP:
+		call_rx_stop_cbfn(rx);
+		break;
+
+	case RX_E_FAIL:
+		/* no-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+static void bna_rx_sm_start_wait_entry(struct bna_rx *rx)
+{
+	bna_bfi_rx_enet_start(rx);
+}
+
+static void bna_rx_sm_start_wait(struct bna_rx *rx,
+				enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_STOP:
+		bfa_fsm_set_state(rx, bna_rx_sm_stop_wait);
+		break;
+
+	case RX_E_FAIL:
+		bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+		break;
+
+	case RX_E_STARTED:
+		bfa_fsm_set_state(rx, bna_rx_sm_rxf_start_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+static void bna_rx_sm_rxf_start_wait_entry(struct bna_rx *rx)
+{
+	rx->rx_post_cbfn(rx->bna->bnad, rx);
+	bna_rxf_start(&rx->rxf);
+}
+
+static void bna_rx_sm_rxf_start_wait(struct bna_rx *rx,
+				enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_STOP:
+		bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
+		break;
+
+	case RX_E_FAIL:
+		bfa_fsm_set_state(rx, bna_rx_sm_failed);
+		bna_rxf_fail(&rx->rxf);
+		rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+		break;
+
+	case RX_E_RXF_STARTED:
+		bfa_fsm_set_state(rx, bna_rx_sm_started);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+void
+bna_rx_sm_started_entry(struct bna_rx *rx)
+{
+	struct bna_rxp *rxp;
+	struct list_head *qe_rxp;
+	int is_regular = (rx->type == BNA_RX_T_REGULAR);
+
+	/* Start IB */
+	list_for_each(qe_rxp, &rx->rxp_q) {
+		rxp = (struct bna_rxp *)qe_rxp;
+		bna_ib_start(rx->bna, &rxp->cq.ib, is_regular);
+	}
+
+	bna_ethport_cb_rx_started(&rx->bna->ethport);
+}
+
+void
+bna_rx_sm_started(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_STOP:
+		bfa_fsm_set_state(rx, bna_rx_sm_rxf_stop_wait);
+		bna_ethport_cb_rx_stopped(&rx->bna->ethport);
+		bna_rxf_stop(&rx->rxf);
+		break;
+
+	case RX_E_FAIL:
+		bfa_fsm_set_state(rx, bna_rx_sm_failed);
+		bna_ethport_cb_rx_stopped(&rx->bna->ethport);
+		bna_rxf_fail(&rx->rxf);
+		rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+void
+bna_rx_sm_rxf_stop_wait_entry(struct bna_rx *rx)
+{
+}
+
+void
+bna_rx_sm_rxf_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_FAIL:
+		bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+		bna_rxf_fail(&rx->rxf);
+		rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+		break;
+
+	case RX_E_RXF_STARTED:
+		bna_rxf_stop(&rx->rxf);
+		break;
+
+	case RX_E_RXF_STOPPED:
+		bfa_fsm_set_state(rx, bna_rx_sm_stop_wait);
+		bna_rx_enet_stop(rx);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+
+}
+
+void
+bna_rx_sm_stop_wait_entry(struct bna_rx *rx)
+{
+}
+
+void
+bna_rx_sm_stop_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_FAIL:
+	case RX_E_STOPPED:
+		bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+		rx->rx_cleanup_cbfn(rx->bna->bnad, rx);
+		break;
+
+	case RX_E_STARTED:
+		bna_rx_enet_stop(rx);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+void
+bna_rx_sm_cleanup_wait_entry(struct bna_rx *rx)
+{
+}
+
+void
+bna_rx_sm_cleanup_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_FAIL:
+	case RX_E_RXF_STOPPED:
+		/* No-op */
+		break;
+
+	case RX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+static void
+bna_rx_sm_failed_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_failed(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_START:
+		bfa_fsm_set_state(rx, bna_rx_sm_quiesce_wait);
+		break;
+
+	case RX_E_STOP:
+		bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+		break;
+
+	case RX_E_FAIL:
+	case RX_E_RXF_STARTED:
+	case RX_E_RXF_STOPPED:
+		/* No-op */
+		break;
+
+	case RX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+}	}
+
+static void
+bna_rx_sm_quiesce_wait_entry(struct bna_rx *rx)
+{
+}
+
+static void
+bna_rx_sm_quiesce_wait(struct bna_rx *rx, enum bna_rx_event event)
+{
+	switch (event) {
+	case RX_E_STOP:
+		bfa_fsm_set_state(rx, bna_rx_sm_cleanup_wait);
+		break;
+
+	case RX_E_FAIL:
+		bfa_fsm_set_state(rx, bna_rx_sm_failed);
+		break;
+
+	case RX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(rx, bna_rx_sm_start_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+		break;
+	}
+}
+
+static void
+bna_bfi_rx_enet_start(struct bna_rx *rx)
+{
+	struct bfi_enet_rx_cfg_req *cfg_req = &rx->bfi_enet_cmd.cfg_req;
+	struct bna_rxp *rxp = NULL;
+	struct bna_rxq *q0 = NULL, *q1 = NULL;
+	struct list_head *rxp_qe;
+	int i;
+
+	bfi_msgq_mhdr_set(cfg_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RX_CFG_SET_REQ, 0, rx->rid);
+	cfg_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rx_cfg_req)));
+
+	cfg_req->num_queue_sets = rx->num_paths;
+	for (i = 0, rxp_qe = bfa_q_first(&rx->rxp_q);
+		i < rx->num_paths;
+		i++, rxp_qe = bfa_q_next(rxp_qe)) {
+		rxp = (struct bna_rxp *)rxp_qe;
+
+		GET_RXQS(rxp, q0, q1);
+		switch (rxp->type) {
+		case BNA_RXP_SLR:
+		case BNA_RXP_HDS:
+			/* Small RxQ */
+			bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].qs.q,
+						&q1->qpt);
+			cfg_req->q_cfg[i].qs.rx_buffer_size =
+				htons((u16)q1->buffer_size);
+			/* Fall through */
+
+		case BNA_RXP_SINGLE:
+			/* Large/Single RxQ */
+			bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].ql.q,
+						&q0->qpt);
+			q0->buffer_size =
+				bna_enet_mtu_get(&rx->bna->enet);
+			cfg_req->q_cfg[i].ql.rx_buffer_size =
+				htons((u16)q0->buffer_size);
+			break;
+
+		default:
+			BUG_ON(1);
+		}
+
+		bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].cq.q,
+					&rxp->cq.qpt);
+
+		cfg_req->q_cfg[i].ib.index_addr.a32.addr_lo =
+			rxp->cq.ib.ib_seg_host_addr.lsb;
+		cfg_req->q_cfg[i].ib.index_addr.a32.addr_hi =
+			rxp->cq.ib.ib_seg_host_addr.msb;
+		cfg_req->q_cfg[i].ib.intr.msix_index =
+			htons((u16)rxp->cq.ib.intr_vector);
+	}
+
+	cfg_req->ib_cfg.int_pkt_dma = BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.int_enabled = BNA_STATUS_T_ENABLED;
+	cfg_req->ib_cfg.int_pkt_enabled = BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.continuous_coalescing = BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.msix = (rxp->cq.ib.intr_type == BNA_INTR_T_MSIX)
+				? BNA_STATUS_T_ENABLED :
+				BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.coalescing_timeout =
+			htonl((u32)rxp->cq.ib.coalescing_timeo);
+	cfg_req->ib_cfg.inter_pkt_timeout =
+			htonl((u32)rxp->cq.ib.interpkt_timeo);
+	cfg_req->ib_cfg.inter_pkt_count = (u8)rxp->cq.ib.interpkt_count;
+
+	switch (rxp->type) {
+	case BNA_RXP_SLR:
+		cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_LARGE_SMALL;
+		break;
+
+	case BNA_RXP_HDS:
+		cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_HDS;
+		cfg_req->rx_cfg.hds.type = rx->hds_cfg.hdr_type;
+		cfg_req->rx_cfg.hds.force_offset = rx->hds_cfg.forced_offset;
+		cfg_req->rx_cfg.hds.max_header_size = rx->hds_cfg.forced_offset;
+		break;
+
+	case BNA_RXP_SINGLE:
+		cfg_req->rx_cfg.rxq_type = BFI_ENET_RXQ_SINGLE;
+		break;
+
+	default:
+		BUG_ON(1);
+	}
+	cfg_req->rx_cfg.strip_vlan = rx->rxf.vlan_strip_status;
+
+	bfa_msgq_cmd_set(&rx->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_rx_cfg_req), &cfg_req->mh);
+	bfa_msgq_cmd_post(&rx->bna->msgq, &rx->msgq_cmd);
+}
+
+static void
+bna_bfi_rx_enet_stop(struct bna_rx *rx)
+{
+	struct bfi_enet_req *req = &rx->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_RX_CFG_CLR_REQ, 0, rx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_req)));
+	bfa_msgq_cmd_set(&rx->msgq_cmd, NULL, NULL, sizeof(struct bfi_enet_req),
+		&req->mh);
+	bfa_msgq_cmd_post(&rx->bna->msgq, &rx->msgq_cmd);
+}
+
+static void
+bna_rx_enet_stop(struct bna_rx *rx)
+{
+	struct bna_rxp *rxp;
+	struct list_head		 *qe_rxp;
+
+	/* Stop IB */
+	list_for_each(qe_rxp, &rx->rxp_q) {
+		rxp = (struct bna_rxp *)qe_rxp;
+		bna_ib_stop(rx->bna, &rxp->cq.ib);
+	}
+
+	bna_bfi_rx_enet_stop(rx);
+}
+
+static int
+bna_rx_res_check(struct bna_rx_mod *rx_mod, struct bna_rx_config *rx_cfg)
+{
+	if ((rx_mod->rx_free_count == 0) ||
+		(rx_mod->rxp_free_count == 0) ||
+		(rx_mod->rxq_free_count == 0))
+		return 0;
+
+	if (rx_cfg->rxp_type == BNA_RXP_SINGLE) {
+		if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
+			(rx_mod->rxq_free_count < rx_cfg->num_paths))
+				return 0;
+	} else {
+		if ((rx_mod->rxp_free_count < rx_cfg->num_paths) ||
+			(rx_mod->rxq_free_count < (2 * rx_cfg->num_paths)))
+			return 0;
+	}
+
+	return 1;
+}
+
+static struct bna_rxq *
+bna_rxq_get(struct bna_rx_mod *rx_mod)
+{
+	struct bna_rxq *rxq = NULL;
+	struct list_head	*qe = NULL;
+
+	bfa_q_deq(&rx_mod->rxq_free_q, &qe);
+	rx_mod->rxq_free_count--;
+	rxq = (struct bna_rxq *)qe;
+	bfa_q_qe_init(&rxq->qe);
+
+	return rxq;
+}
+
+static void
+bna_rxq_put(struct bna_rx_mod *rx_mod, struct bna_rxq *rxq)
+{
+	bfa_q_qe_init(&rxq->qe);
+	list_add_tail(&rxq->qe, &rx_mod->rxq_free_q);
+	rx_mod->rxq_free_count++;
+}
+
+static struct bna_rxp *
+bna_rxp_get(struct bna_rx_mod *rx_mod)
+{
+	struct list_head	*qe = NULL;
+	struct bna_rxp *rxp = NULL;
+
+	bfa_q_deq(&rx_mod->rxp_free_q, &qe);
+	rx_mod->rxp_free_count--;
+	rxp = (struct bna_rxp *)qe;
+	bfa_q_qe_init(&rxp->qe);
+
+	return rxp;
+}
+
+static void
+bna_rxp_put(struct bna_rx_mod *rx_mod, struct bna_rxp *rxp)
+{
+	bfa_q_qe_init(&rxp->qe);
+	list_add_tail(&rxp->qe, &rx_mod->rxp_free_q);
+	rx_mod->rxp_free_count++;
+}
+
+static struct bna_rx *
+bna_rx_get(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+	struct list_head	*qe = NULL;
+	struct bna_rx *rx = NULL;
+
+	if (type == BNA_RX_T_REGULAR) {
+		bfa_q_deq(&rx_mod->rx_free_q, &qe);
+	} else
+		bfa_q_deq_tail(&rx_mod->rx_free_q, &qe);
+
+	rx_mod->rx_free_count--;
+	rx = (struct bna_rx *)qe;
+	bfa_q_qe_init(&rx->qe);
+	list_add_tail(&rx->qe, &rx_mod->rx_active_q);
+	rx->type = type;
+
+	return rx;
+}
+
+static void
+bna_rx_put(struct bna_rx_mod *rx_mod, struct bna_rx *rx)
+{
+	struct list_head *prev_qe = NULL;
+	struct list_head *qe;
+
+	bfa_q_qe_init(&rx->qe);
+
+	list_for_each(qe, &rx_mod->rx_free_q) {
+		if (((struct bna_rx *)qe)->rid < rx->rid)
+			prev_qe = qe;
+		else
+			break;
+	}
+
+	if (prev_qe == NULL) {
+		/* This is the first entry */
+		bfa_q_enq_head(&rx_mod->rx_free_q, &rx->qe);
+	} else if (bfa_q_next(prev_qe) == &rx_mod->rx_free_q) {
+		/* This is the last entry */
+		list_add_tail(&rx->qe, &rx_mod->rx_free_q);
+	} else {
+		/* Somewhere in the middle */
+		bfa_q_next(&rx->qe) = bfa_q_next(prev_qe);
+		bfa_q_prev(&rx->qe) = prev_qe;
+		bfa_q_next(prev_qe) = &rx->qe;
+		bfa_q_prev(bfa_q_next(&rx->qe)) = &rx->qe;
+	}
+
+	rx_mod->rx_free_count++;
+}
+
+static void
+bna_rxp_add_rxqs(struct bna_rxp *rxp, struct bna_rxq *q0,
+		struct bna_rxq *q1)
+{
+	switch (rxp->type) {
+	case BNA_RXP_SINGLE:
+		rxp->rxq.single.only = q0;
+		rxp->rxq.single.reserved = NULL;
+		break;
+	case BNA_RXP_SLR:
+		rxp->rxq.slr.large = q0;
+		rxp->rxq.slr.small = q1;
+		break;
+	case BNA_RXP_HDS:
+		rxp->rxq.hds.data = q0;
+		rxp->rxq.hds.hdr = q1;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+bna_rxq_qpt_setup(struct bna_rxq *rxq,
+		struct bna_rxp *rxp,
+		u32 page_count,
+		u32 page_size,
+		struct bna_mem_descr *qpt_mem,
+		struct bna_mem_descr *swqpt_mem,
+		struct bna_mem_descr *page_mem)
+{
+	int	i;
+
+	rxq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+	rxq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+	rxq->qpt.kv_qpt_ptr = qpt_mem->kva;
+	rxq->qpt.page_count = page_count;
+	rxq->qpt.page_size = page_size;
+
+	rxq->rcb->sw_qpt = (void **) swqpt_mem->kva;
+
+	for (i = 0; i < rxq->qpt.page_count; i++) {
+		rxq->rcb->sw_qpt[i] = page_mem[i].kva;
+		((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].lsb =
+			page_mem[i].dma.lsb;
+		((struct bna_dma_addr *)rxq->qpt.kv_qpt_ptr)[i].msb =
+			page_mem[i].dma.msb;
+	}
+}
+
+static void
+bna_rxp_cqpt_setup(struct bna_rxp *rxp,
+		u32 page_count,
+		u32 page_size,
+		struct bna_mem_descr *qpt_mem,
+		struct bna_mem_descr *swqpt_mem,
+		struct bna_mem_descr *page_mem)
+{
+	int	i;
+
+	rxp->cq.qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+	rxp->cq.qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+	rxp->cq.qpt.kv_qpt_ptr = qpt_mem->kva;
+	rxp->cq.qpt.page_count = page_count;
+	rxp->cq.qpt.page_size = page_size;
+
+	rxp->cq.ccb->sw_qpt = (void **) swqpt_mem->kva;
+
+	for (i = 0; i < rxp->cq.qpt.page_count; i++) {
+		rxp->cq.ccb->sw_qpt[i] = page_mem[i].kva;
+
+		((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].lsb =
+			page_mem[i].dma.lsb;
+		((struct bna_dma_addr *)rxp->cq.qpt.kv_qpt_ptr)[i].msb =
+			page_mem[i].dma.msb;
+	}
+}
+
+static void
+bna_rx_mod_cb_rx_stopped(void *arg, struct bna_rx *rx)
+{
+	struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
+
+	bfa_wc_down(&rx_mod->rx_stop_wc);
+}
+
+static void
+bna_rx_mod_cb_rx_stopped_all(void *arg)
+{
+	struct bna_rx_mod *rx_mod = (struct bna_rx_mod *)arg;
+
+	if (rx_mod->stop_cbfn)
+		rx_mod->stop_cbfn(&rx_mod->bna->enet);
+	rx_mod->stop_cbfn = NULL;
+}
+
+static void
+bna_rx_start(struct bna_rx *rx)
+{
+	rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+	if (rx->rx_flags & BNA_RX_F_ENABLED)
+		bfa_fsm_send_event(rx, RX_E_START);
+}
+
+static void
+bna_rx_stop(struct bna_rx *rx)
+{
+	rx->rx_flags &= ~BNA_RX_F_ENET_STARTED;
+	if (rx->fsm == (bfa_fsm_t) bna_rx_sm_stopped)
+		bna_rx_mod_cb_rx_stopped(&rx->bna->rx_mod, rx);
+	else {
+		rx->stop_cbfn = bna_rx_mod_cb_rx_stopped;
+		rx->stop_cbarg = &rx->bna->rx_mod;
+		bfa_fsm_send_event(rx, RX_E_STOP);
+	}
+}
+
+static void
+bna_rx_fail(struct bna_rx *rx)
+{
+	/* Indicate Enet is not enabled, and failed */
+	rx->rx_flags &= ~BNA_RX_F_ENET_STARTED;
+	bfa_fsm_send_event(rx, RX_E_FAIL);
+}
+
+void
+bna_rx_mod_start(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+	struct bna_rx *rx;
+	struct list_head *qe;
+
+	rx_mod->flags |= BNA_RX_MOD_F_ENET_STARTED;
+	if (type == BNA_RX_T_LOOPBACK)
+		rx_mod->flags |= BNA_RX_MOD_F_ENET_LOOPBACK;
+
+	list_for_each(qe, &rx_mod->rx_active_q) {
+		rx = (struct bna_rx *)qe;
+		if (rx->type == type)
+			bna_rx_start(rx);
+	}
+}
+
+void
+bna_rx_mod_stop(struct bna_rx_mod *rx_mod, enum bna_rx_type type)
+{
+	struct bna_rx *rx;
+	struct list_head *qe;
+
+	rx_mod->flags &= ~BNA_RX_MOD_F_ENET_STARTED;
+	rx_mod->flags &= ~BNA_RX_MOD_F_ENET_LOOPBACK;
+
+	rx_mod->stop_cbfn = bna_enet_cb_rx_stopped;
+
+	bfa_wc_init(&rx_mod->rx_stop_wc, bna_rx_mod_cb_rx_stopped_all, rx_mod);
+
+	list_for_each(qe, &rx_mod->rx_active_q) {
+		rx = (struct bna_rx *)qe;
+		if (rx->type == type) {
+			bfa_wc_up(&rx_mod->rx_stop_wc);
+			bna_rx_stop(rx);
+		}
+	}
+
+	bfa_wc_wait(&rx_mod->rx_stop_wc);
+}
+
+void
+bna_rx_mod_fail(struct bna_rx_mod *rx_mod)
+{
+	struct bna_rx *rx;
+	struct list_head *qe;
+
+	rx_mod->flags &= ~BNA_RX_MOD_F_ENET_STARTED;
+	rx_mod->flags &= ~BNA_RX_MOD_F_ENET_LOOPBACK;
+
+	list_for_each(qe, &rx_mod->rx_active_q) {
+		rx = (struct bna_rx *)qe;
+		bna_rx_fail(rx);
+	}
+}
+
+void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna,
+			struct bna_res_info *res_info)
+{
+	int	index;
+	struct bna_rx *rx_ptr;
+	struct bna_rxp *rxp_ptr;
+	struct bna_rxq *rxq_ptr;
+
+	rx_mod->bna = bna;
+	rx_mod->flags = 0;
+
+	rx_mod->rx = (struct bna_rx *)
+		res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.mdl[0].kva;
+	rx_mod->rxp = (struct bna_rxp *)
+		res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mdl[0].kva;
+	rx_mod->rxq = (struct bna_rxq *)
+		res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mdl[0].kva;
+
+	/* Initialize the queues */
+	INIT_LIST_HEAD(&rx_mod->rx_free_q);
+	rx_mod->rx_free_count = 0;
+	INIT_LIST_HEAD(&rx_mod->rxq_free_q);
+	rx_mod->rxq_free_count = 0;
+	INIT_LIST_HEAD(&rx_mod->rxp_free_q);
+	rx_mod->rxp_free_count = 0;
+	INIT_LIST_HEAD(&rx_mod->rx_active_q);
+
+	/* Build RX queues */
+	for (index = 0; index < bna->ioceth.attr.num_rxp; index++) {
+		rx_ptr = &rx_mod->rx[index];
+
+		bfa_q_qe_init(&rx_ptr->qe);
+		INIT_LIST_HEAD(&rx_ptr->rxp_q);
+		rx_ptr->bna = NULL;
+		rx_ptr->rid = index;
+		rx_ptr->stop_cbfn = NULL;
+		rx_ptr->stop_cbarg = NULL;
+
+		list_add_tail(&rx_ptr->qe, &rx_mod->rx_free_q);
+		rx_mod->rx_free_count++;
+	}
+
+	/* build RX-path queue */
+	for (index = 0; index < bna->ioceth.attr.num_rxp; index++) {
+		rxp_ptr = &rx_mod->rxp[index];
+		bfa_q_qe_init(&rxp_ptr->qe);
+		list_add_tail(&rxp_ptr->qe, &rx_mod->rxp_free_q);
+		rx_mod->rxp_free_count++;
+	}
+
+	/* build RXQ queue */
+	for (index = 0; index < (bna->ioceth.attr.num_rxp * 2); index++) {
+		rxq_ptr = &rx_mod->rxq[index];
+		bfa_q_qe_init(&rxq_ptr->qe);
+		list_add_tail(&rxq_ptr->qe, &rx_mod->rxq_free_q);
+		rx_mod->rxq_free_count++;
+	}
+}
+
+void
+bna_rx_mod_uninit(struct bna_rx_mod *rx_mod)
+{
+	struct list_head		*qe;
+	int i;
+
+	i = 0;
+	list_for_each(qe, &rx_mod->rx_free_q)
+		i++;
+
+	i = 0;
+	list_for_each(qe, &rx_mod->rxp_free_q)
+		i++;
+
+	i = 0;
+	list_for_each(qe, &rx_mod->rxq_free_q)
+		i++;
+
+	rx_mod->bna = NULL;
+}
+
+void
+bna_bfi_rx_enet_start_rsp(struct bna_rx *rx, struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_rx_cfg_rsp *cfg_rsp = &rx->bfi_enet_cmd.cfg_rsp;
+	struct bna_rxp *rxp = NULL;
+	struct bna_rxq *q0 = NULL, *q1 = NULL;
+	struct list_head *rxp_qe;
+	int i;
+
+	bfa_msgq_rsp_copy(&rx->bna->msgq, (u8 *)cfg_rsp,
+		sizeof(struct bfi_enet_rx_cfg_rsp));
+
+	rx->hw_id = cfg_rsp->hw_id;
+
+	for (i = 0, rxp_qe = bfa_q_first(&rx->rxp_q);
+		i < rx->num_paths;
+		i++, rxp_qe = bfa_q_next(rxp_qe)) {
+		rxp = (struct bna_rxp *)rxp_qe;
+		GET_RXQS(rxp, q0, q1);
+
+		/* Setup doorbells */
+		rxp->cq.ccb->i_dbell->doorbell_addr =
+			rx->bna->pcidev.pci_bar_kva
+			+ ntohl(cfg_rsp->q_handles[i].i_dbell);
+		rxp->hw_id = cfg_rsp->q_handles[i].hw_cqid;
+		q0->rcb->q_dbell =
+			rx->bna->pcidev.pci_bar_kva
+			+ ntohl(cfg_rsp->q_handles[i].ql_dbell);
+		q0->hw_id = cfg_rsp->q_handles[i].hw_lqid;
+		if (q1) {
+			q1->rcb->q_dbell =
+			rx->bna->pcidev.pci_bar_kva
+			+ ntohl(cfg_rsp->q_handles[i].qs_dbell);
+			q1->hw_id = cfg_rsp->q_handles[i].hw_sqid;
+		}
+
+		/* Initialize producer/consumer indexes */
+		(*rxp->cq.ccb->hw_producer_index) = 0;
+		rxp->cq.ccb->producer_index = 0;
+		q0->rcb->producer_index = q0->rcb->consumer_index = 0;
+		if (q1)
+			q1->rcb->producer_index = q1->rcb->consumer_index = 0;
+	}
+
+	bfa_fsm_send_event(rx, RX_E_STARTED);
+}
+
+void
+bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx, struct bfi_msgq_mhdr *msghdr)
+{
+	bfa_fsm_send_event(rx, RX_E_STOPPED);
+}
+
+void
+bna_rx_res_req(struct bna_rx_config *q_cfg, struct bna_res_info *res_info)
+{
+	u32 cq_size, hq_size, dq_size;
+	u32 cpage_count, hpage_count, dpage_count;
+	struct bna_mem_info *mem_info;
+	u32 cq_depth;
+	u32 hq_depth;
+	u32 dq_depth;
+
+	dq_depth = q_cfg->q_depth;
+	hq_depth = ((q_cfg->rxp_type == BNA_RXP_SINGLE) ? 0 : q_cfg->q_depth);
+	cq_depth = dq_depth + hq_depth;
+
+	BNA_TO_POWER_OF_2_HIGH(cq_depth);
+	cq_size = cq_depth * BFI_CQ_WI_SIZE;
+	cq_size = ALIGN(cq_size, PAGE_SIZE);
+	cpage_count = SIZE_TO_PAGES(cq_size);
+
+	BNA_TO_POWER_OF_2_HIGH(dq_depth);
+	dq_size = dq_depth * BFI_RXQ_WI_SIZE;
+	dq_size = ALIGN(dq_size, PAGE_SIZE);
+	dpage_count = SIZE_TO_PAGES(dq_size);
+
+	if (BNA_RXP_SINGLE != q_cfg->rxp_type) {
+		BNA_TO_POWER_OF_2_HIGH(hq_depth);
+		hq_size = hq_depth * BFI_RXQ_WI_SIZE;
+		hq_size = ALIGN(hq_size, PAGE_SIZE);
+		hpage_count = SIZE_TO_PAGES(hq_size);
+	} else
+		hpage_count = 0;
+
+	res_info[BNA_RX_RES_MEM_T_CCB].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = sizeof(struct bna_ccb);
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_RCB].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = sizeof(struct bna_rcb);
+	mem_info->num = BNA_GET_RXQS(q_cfg);
+
+	res_info[BNA_RX_RES_MEM_T_CQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = cpage_count * sizeof(struct bna_dma_addr);
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_CSWQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = cpage_count * sizeof(void *);
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = PAGE_SIZE;
+	mem_info->num = cpage_count * q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_DQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = dpage_count * sizeof(struct bna_dma_addr);
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_DSWQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = dpage_count * sizeof(void *);
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_DPAGE].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = PAGE_SIZE;
+	mem_info->num = dpage_count * q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_HQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = hpage_count * sizeof(struct bna_dma_addr);
+	mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
+
+	res_info[BNA_RX_RES_MEM_T_HSWQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = hpage_count * sizeof(void *);
+	mem_info->num = (hpage_count ? q_cfg->num_paths : 0);
+
+	res_info[BNA_RX_RES_MEM_T_HPAGE].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = (hpage_count ? PAGE_SIZE : 0);
+	mem_info->num = (hpage_count ? (hpage_count * q_cfg->num_paths) : 0);
+
+	res_info[BNA_RX_RES_MEM_T_IBIDX].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = BFI_IBIDX_SIZE;
+	mem_info->num = q_cfg->num_paths;
+
+	res_info[BNA_RX_RES_MEM_T_RIT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_RX_RES_MEM_T_RIT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = BFI_ENET_RSS_RIT_MAX;
+	mem_info->num = 1;
+
+	res_info[BNA_RX_RES_T_INTR].res_type = BNA_RES_T_INTR;
+	res_info[BNA_RX_RES_T_INTR].res_u.intr_info.intr_type = BNA_INTR_T_MSIX;
+	res_info[BNA_RX_RES_T_INTR].res_u.intr_info.num = q_cfg->num_paths;
+}
+
+struct bna_rx *
+bna_rx_create(struct bna *bna, struct bnad *bnad,
+		struct bna_rx_config *rx_cfg,
+		struct bna_rx_event_cbfn *rx_cbfn,
+		struct bna_res_info *res_info,
+		void *priv)
+{
+	struct bna_rx_mod *rx_mod = &bna->rx_mod;
+	struct bna_rx *rx;
+	struct bna_rxp *rxp;
+	struct bna_rxq *q0;
+	struct bna_rxq *q1;
+	struct bna_intr_info *intr_info;
+	u32 page_count;
+	struct bna_mem_descr *ccb_mem;
+	struct bna_mem_descr *rcb_mem;
+	struct bna_mem_descr *unmapq_mem;
+	struct bna_mem_descr *cqpt_mem;
+	struct bna_mem_descr *cswqpt_mem;
+	struct bna_mem_descr *cpage_mem;
+	struct bna_mem_descr *hqpt_mem;
+	struct bna_mem_descr *dqpt_mem;
+	struct bna_mem_descr *hsqpt_mem;
+	struct bna_mem_descr *dsqpt_mem;
+	struct bna_mem_descr *hpage_mem;
+	struct bna_mem_descr *dpage_mem;
+	int i, cpage_idx = 0, dpage_idx = 0, hpage_idx = 0;
+	int dpage_count, hpage_count, rcb_idx;
+
+	if (!bna_rx_res_check(rx_mod, rx_cfg))
+		return NULL;
+
+	intr_info = &res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
+	ccb_mem = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info.mdl[0];
+	rcb_mem = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info.mdl[0];
+	unmapq_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[0];
+	cqpt_mem = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info.mdl[0];
+	cswqpt_mem = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info.mdl[0];
+	cpage_mem = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.mdl[0];
+	hqpt_mem = &res_info[BNA_RX_RES_MEM_T_HQPT].res_u.mem_info.mdl[0];
+	dqpt_mem = &res_info[BNA_RX_RES_MEM_T_DQPT].res_u.mem_info.mdl[0];
+	hsqpt_mem = &res_info[BNA_RX_RES_MEM_T_HSWQPT].res_u.mem_info.mdl[0];
+	dsqpt_mem = &res_info[BNA_RX_RES_MEM_T_DSWQPT].res_u.mem_info.mdl[0];
+	hpage_mem = &res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.mdl[0];
+	dpage_mem = &res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.mdl[0];
+
+	page_count = res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.num /
+			rx_cfg->num_paths;
+
+	dpage_count = res_info[BNA_RX_RES_MEM_T_DPAGE].res_u.mem_info.num /
+			rx_cfg->num_paths;
+
+	hpage_count = res_info[BNA_RX_RES_MEM_T_HPAGE].res_u.mem_info.num /
+			rx_cfg->num_paths;
+
+	rx = bna_rx_get(rx_mod, rx_cfg->rx_type);
+	rx->bna = bna;
+	rx->rx_flags = 0;
+	INIT_LIST_HEAD(&rx->rxp_q);
+	rx->stop_cbfn = NULL;
+	rx->stop_cbarg = NULL;
+	rx->priv = priv;
+
+	rx->rcb_setup_cbfn = rx_cbfn->rcb_setup_cbfn;
+	rx->rcb_destroy_cbfn = rx_cbfn->rcb_destroy_cbfn;
+	rx->ccb_setup_cbfn = rx_cbfn->ccb_setup_cbfn;
+	rx->ccb_destroy_cbfn = rx_cbfn->ccb_destroy_cbfn;
+	/* Following callbacks are mandatory */
+	rx->rx_cleanup_cbfn = rx_cbfn->rx_cleanup_cbfn;
+	rx->rx_post_cbfn = rx_cbfn->rx_post_cbfn;
+
+	if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_ENET_STARTED) {
+		switch (rx->type) {
+		case BNA_RX_T_REGULAR:
+			if (!(rx->bna->rx_mod.flags &
+				BNA_RX_MOD_F_ENET_LOOPBACK))
+				rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+			break;
+		case BNA_RX_T_LOOPBACK:
+			if (rx->bna->rx_mod.flags & BNA_RX_MOD_F_ENET_LOOPBACK)
+				rx->rx_flags |= BNA_RX_F_ENET_STARTED;
+			break;
+		}
+	}
+
+	rx->num_paths = rx_cfg->num_paths;
+	for (i = 0, rcb_idx = 0; i < rx->num_paths; i++) {
+		rxp = bna_rxp_get(rx_mod);
+		list_add_tail(&rxp->qe, &rx->rxp_q);
+		rxp->type = rx_cfg->rxp_type;
+		rxp->rx = rx;
+		rxp->cq.rx = rx;
+
+		q0 = bna_rxq_get(rx_mod);
+		if (BNA_RXP_SINGLE == rx_cfg->rxp_type)
+			q1 = NULL;
+		else
+			q1 = bna_rxq_get(rx_mod);
+
+		if (1 == intr_info->num)
+			rxp->vector = intr_info->idl[0].vector;
+		else
+			rxp->vector = intr_info->idl[i].vector;
+
+		/* Setup IB */
+
+		rxp->cq.ib.ib_seg_host_addr.lsb =
+		res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.lsb;
+		rxp->cq.ib.ib_seg_host_addr.msb =
+		res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.msb;
+		rxp->cq.ib.ib_seg_host_addr_kva =
+		res_info[BNA_RX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].kva;
+		rxp->cq.ib.intr_type = intr_info->intr_type;
+		if (intr_info->intr_type == BNA_INTR_T_MSIX)
+			rxp->cq.ib.intr_vector = rxp->vector;
+		else
+			rxp->cq.ib.intr_vector = (1 << rxp->vector);
+		rxp->cq.ib.coalescing_timeo = rx_cfg->coalescing_timeo;
+		rxp->cq.ib.interpkt_count = BFI_RX_INTERPKT_COUNT;
+		rxp->cq.ib.interpkt_timeo = BFI_RX_INTERPKT_TIMEO;
+
+		bna_rxp_add_rxqs(rxp, q0, q1);
+
+		/* Setup large Q */
+
+		q0->rx = rx;
+		q0->rxp = rxp;
+
+		q0->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
+		q0->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
+		rcb_idx++;
+		q0->rcb->q_depth = rx_cfg->q_depth;
+		q0->rcb->rxq = q0;
+		q0->rcb->bnad = bna->bnad;
+		q0->rcb->id = 0;
+		q0->rx_packets = q0->rx_bytes = 0;
+		q0->rx_packets_with_error = q0->rxbuf_alloc_failed = 0;
+
+		bna_rxq_qpt_setup(q0, rxp, dpage_count, PAGE_SIZE,
+			&dqpt_mem[i], &dsqpt_mem[i], &dpage_mem[dpage_idx]);
+		q0->rcb->page_idx = dpage_idx;
+		q0->rcb->page_count = dpage_count;
+		dpage_idx += dpage_count;
+
+		if (rx->rcb_setup_cbfn)
+			rx->rcb_setup_cbfn(bnad, q0->rcb);
+
+		/* Setup small Q */
+
+		if (q1) {
+			q1->rx = rx;
+			q1->rxp = rxp;
+
+			q1->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
+			q1->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
+			rcb_idx++;
+			q1->rcb->q_depth = rx_cfg->q_depth;
+			q1->rcb->rxq = q1;
+			q1->rcb->bnad = bna->bnad;
+			q1->rcb->id = 1;
+			q1->buffer_size = (rx_cfg->rxp_type == BNA_RXP_HDS) ?
+					rx_cfg->hds_config.forced_offset
+					: rx_cfg->small_buff_size;
+			q1->rx_packets = q1->rx_bytes = 0;
+			q1->rx_packets_with_error = q1->rxbuf_alloc_failed = 0;
+
+			bna_rxq_qpt_setup(q1, rxp, hpage_count, PAGE_SIZE,
+				&hqpt_mem[i], &hsqpt_mem[i],
+				&hpage_mem[hpage_idx]);
+			q1->rcb->page_idx = hpage_idx;
+			q1->rcb->page_count = hpage_count;
+			hpage_idx += hpage_count;
+
+			if (rx->rcb_setup_cbfn)
+				rx->rcb_setup_cbfn(bnad, q1->rcb);
+		}
+
+		/* Setup CQ */
+
+		rxp->cq.ccb = (struct bna_ccb *) ccb_mem[i].kva;
+		rxp->cq.ccb->q_depth =	rx_cfg->q_depth +
+					((rx_cfg->rxp_type == BNA_RXP_SINGLE) ?
+					0 : rx_cfg->q_depth);
+		rxp->cq.ccb->cq = &rxp->cq;
+		rxp->cq.ccb->rcb[0] = q0->rcb;
+		q0->rcb->ccb = rxp->cq.ccb;
+		if (q1) {
+			rxp->cq.ccb->rcb[1] = q1->rcb;
+			q1->rcb->ccb = rxp->cq.ccb;
+		}
+		rxp->cq.ccb->hw_producer_index =
+			(u32 *)rxp->cq.ib.ib_seg_host_addr_kva;
+		rxp->cq.ccb->i_dbell = &rxp->cq.ib.door_bell;
+		rxp->cq.ccb->intr_type = rxp->cq.ib.intr_type;
+		rxp->cq.ccb->intr_vector = rxp->cq.ib.intr_vector;
+		rxp->cq.ccb->rx_coalescing_timeo =
+			rxp->cq.ib.coalescing_timeo;
+		rxp->cq.ccb->pkt_rate.small_pkt_cnt = 0;
+		rxp->cq.ccb->pkt_rate.large_pkt_cnt = 0;
+		rxp->cq.ccb->bnad = bna->bnad;
+		rxp->cq.ccb->id = i;
+
+		bna_rxp_cqpt_setup(rxp, page_count, PAGE_SIZE,
+			&cqpt_mem[i], &cswqpt_mem[i], &cpage_mem[cpage_idx]);
+		rxp->cq.ccb->page_idx = cpage_idx;
+		rxp->cq.ccb->page_count = page_count;
+		cpage_idx += page_count;
+
+		if (rx->ccb_setup_cbfn)
+			rx->ccb_setup_cbfn(bnad, rxp->cq.ccb);
+	}
+
+	rx->hds_cfg = rx_cfg->hds_config;
+
+	bna_rxf_init(&rx->rxf, rx, rx_cfg, res_info);
+
+	bfa_fsm_set_state(rx, bna_rx_sm_stopped);
+
+	rx_mod->rid_mask |= (1 << rx->rid);
+
+	return rx;
+}
+
+void
+bna_rx_destroy(struct bna_rx *rx)
+{
+	struct bna_rx_mod *rx_mod = &rx->bna->rx_mod;
+	struct bna_rxq *q0 = NULL;
+	struct bna_rxq *q1 = NULL;
+	struct bna_rxp *rxp;
+	struct list_head *qe;
+
+	bna_rxf_uninit(&rx->rxf);
+
+	while (!list_empty(&rx->rxp_q)) {
+		bfa_q_deq(&rx->rxp_q, &rxp);
+		GET_RXQS(rxp, q0, q1);
+		if (rx->rcb_destroy_cbfn)
+			rx->rcb_destroy_cbfn(rx->bna->bnad, q0->rcb);
+		q0->rcb = NULL;
+		q0->rxp = NULL;
+		q0->rx = NULL;
+		bna_rxq_put(rx_mod, q0);
+
+		if (q1) {
+			if (rx->rcb_destroy_cbfn)
+				rx->rcb_destroy_cbfn(rx->bna->bnad, q1->rcb);
+			q1->rcb = NULL;
+			q1->rxp = NULL;
+			q1->rx = NULL;
+			bna_rxq_put(rx_mod, q1);
+		}
+		rxp->rxq.slr.large = NULL;
+		rxp->rxq.slr.small = NULL;
+
+		if (rx->ccb_destroy_cbfn)
+			rx->ccb_destroy_cbfn(rx->bna->bnad, rxp->cq.ccb);
+		rxp->cq.ccb = NULL;
+		rxp->rx = NULL;
+		bna_rxp_put(rx_mod, rxp);
+	}
+
+	list_for_each(qe, &rx_mod->rx_active_q) {
+		if (qe == &rx->qe) {
+			list_del(&rx->qe);
+			bfa_q_qe_init(&rx->qe);
+			break;
+		}
+	}
+
+	rx_mod->rid_mask &= ~(1 << rx->rid);
+
+	rx->bna = NULL;
+	rx->priv = NULL;
+	bna_rx_put(rx_mod, rx);
+}
+
+void
+bna_rx_enable(struct bna_rx *rx)
+{
+	if (rx->fsm != (bfa_sm_t)bna_rx_sm_stopped)
+		return;
+
+	rx->rx_flags |= BNA_RX_F_ENABLED;
+	if (rx->rx_flags & BNA_RX_F_ENET_STARTED)
+		bfa_fsm_send_event(rx, RX_E_START);
+}
+
+void
+bna_rx_disable(struct bna_rx *rx, enum bna_cleanup_type type,
+		void (*cbfn)(void *, struct bna_rx *))
+{
+	if (type == BNA_SOFT_CLEANUP) {
+		/* h/w should not be accessed. Treat we're stopped */
+		(*cbfn)(rx->bna->bnad, rx);
+	} else {
+		rx->stop_cbfn = cbfn;
+		rx->stop_cbarg = rx->bna->bnad;
+
+		rx->rx_flags &= ~BNA_RX_F_ENABLED;
+
+		bfa_fsm_send_event(rx, RX_E_STOP);
+	}
+}
+
+void
+bna_rx_cleanup_complete(struct bna_rx *rx)
+{
+	bfa_fsm_send_event(rx, RX_E_CLEANUP_DONE);
+}
+
+enum bna_cb_status
+bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode new_mode,
+		enum bna_rxmode bitmask,
+		void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+	struct bna_rxf *rxf = &rx->rxf;
+	int need_hw_config = 0;
+
+	/* Error checks */
+
+	if (is_promisc_enable(new_mode, bitmask)) {
+		/* If promisc mode is already enabled elsewhere in the system */
+		if ((rx->bna->promisc_rid != BFI_INVALID_RID) &&
+			(rx->bna->promisc_rid != rxf->rx->rid))
+			goto err_return;
+
+		/* If default mode is already enabled in the system */
+		if (rx->bna->default_mode_rid != BFI_INVALID_RID)
+			goto err_return;
+
+		/* Trying to enable promiscuous and default mode together */
+		if (is_default_enable(new_mode, bitmask))
+			goto err_return;
+	}
+
+	if (is_default_enable(new_mode, bitmask)) {
+		/* If default mode is already enabled elsewhere in the system */
+		if ((rx->bna->default_mode_rid != BFI_INVALID_RID) &&
+			(rx->bna->default_mode_rid != rxf->rx->rid)) {
+				goto err_return;
+		}
+
+		/* If promiscuous mode is already enabled in the system */
+		if (rx->bna->promisc_rid != BFI_INVALID_RID)
+			goto err_return;
+	}
+
+	/* Process the commands */
+
+	if (is_promisc_enable(new_mode, bitmask)) {
+		if (bna_rxf_promisc_enable(rxf))
+			need_hw_config = 1;
+	} else if (is_promisc_disable(new_mode, bitmask)) {
+		if (bna_rxf_promisc_disable(rxf))
+			need_hw_config = 1;
+	}
+
+	if (is_allmulti_enable(new_mode, bitmask)) {
+		if (bna_rxf_allmulti_enable(rxf))
+			need_hw_config = 1;
+	} else if (is_allmulti_disable(new_mode, bitmask)) {
+		if (bna_rxf_allmulti_disable(rxf))
+			need_hw_config = 1;
+	}
+
+	/* Trigger h/w if needed */
+
+	if (need_hw_config) {
+		rxf->cam_fltr_cbfn = cbfn;
+		rxf->cam_fltr_cbarg = rx->bna->bnad;
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+	} else if (cbfn)
+		(*cbfn)(rx->bna->bnad, rx);
+
+	return BNA_CB_SUCCESS;
+
+err_return:
+	return BNA_CB_FAIL;
+}
+
+void
+bna_rx_vlanfilter_enable(struct bna_rx *rx)
+{
+	struct bna_rxf *rxf = &rx->rxf;
+
+	if (rxf->vlan_filter_status == BNA_STATUS_T_DISABLED) {
+		rxf->vlan_filter_status = BNA_STATUS_T_ENABLED;
+		rxf->vlan_pending_bitmask = (u8)BFI_VLAN_BMASK_ALL;
+		bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+	}
+}
+
+void
+bna_rx_coalescing_timeo_set(struct bna_rx *rx, int coalescing_timeo)
+{
+	struct bna_rxp *rxp;
+	struct list_head *qe;
+
+	list_for_each(qe, &rx->rxp_q) {
+		rxp = (struct bna_rxp *)qe;
+		rxp->cq.ccb->rx_coalescing_timeo = coalescing_timeo;
+		bna_ib_coalescing_timeo_set(&rxp->cq.ib, coalescing_timeo);
+	}
+}
+
+void
+bna_rx_dim_reconfig(struct bna *bna, u32 vector[][BNA_BIAS_T_MAX])
+{
+	int i, j;
+
+	for (i = 0; i < BNA_LOAD_T_MAX; i++)
+		for (j = 0; j < BNA_BIAS_T_MAX; j++)
+			bna->rx_mod.dim_vector[i][j] = vector[i][j];
+}
+
+void
+bna_rx_dim_update(struct bna_ccb *ccb)
+{
+	struct bna *bna = ccb->cq->rx->bna;
+	u32 load, bias;
+	u32 pkt_rt, small_rt, large_rt;
+	u8 coalescing_timeo;
+
+	if ((ccb->pkt_rate.small_pkt_cnt == 0) &&
+		(ccb->pkt_rate.large_pkt_cnt == 0))
+		return;
+
+	/* Arrive at preconfigured coalescing timeo value based on pkt rate */
+
+	small_rt = ccb->pkt_rate.small_pkt_cnt;
+	large_rt = ccb->pkt_rate.large_pkt_cnt;
+
+	pkt_rt = small_rt + large_rt;
+
+	if (pkt_rt < BNA_PKT_RATE_10K)
+		load = BNA_LOAD_T_LOW_4;
+	else if (pkt_rt < BNA_PKT_RATE_20K)
+		load = BNA_LOAD_T_LOW_3;
+	else if (pkt_rt < BNA_PKT_RATE_30K)
+		load = BNA_LOAD_T_LOW_2;
+	else if (pkt_rt < BNA_PKT_RATE_40K)
+		load = BNA_LOAD_T_LOW_1;
+	else if (pkt_rt < BNA_PKT_RATE_50K)
+		load = BNA_LOAD_T_HIGH_1;
+	else if (pkt_rt < BNA_PKT_RATE_60K)
+		load = BNA_LOAD_T_HIGH_2;
+	else if (pkt_rt < BNA_PKT_RATE_80K)
+		load = BNA_LOAD_T_HIGH_3;
+	else
+		load = BNA_LOAD_T_HIGH_4;
+
+	if (small_rt > (large_rt << 1))
+		bias = 0;
+	else
+		bias = 1;
+
+	ccb->pkt_rate.small_pkt_cnt = 0;
+	ccb->pkt_rate.large_pkt_cnt = 0;
+
+	coalescing_timeo = bna->rx_mod.dim_vector[load][bias];
+	ccb->rx_coalescing_timeo = coalescing_timeo;
+
+	/* Set it to IB */
+	bna_ib_coalescing_timeo_set(&ccb->cq->ib, coalescing_timeo);
+}
+
+u32 bna_napi_dim_vector[BNA_LOAD_T_MAX][BNA_BIAS_T_MAX] = {
+	{12, 12},
+	{6, 10},
+	{5, 10},
+	{4, 8},
+	{3, 6},
+	{3, 6},
+	{2, 4},
+	{1, 2},
+};
+
+/**
+ * TX
+ */
+#define call_tx_stop_cbfn(tx)						\
+do {									\
+	if ((tx)->stop_cbfn) {						\
+		void (*cbfn)(void *, struct bna_tx *);		\
+		void *cbarg;						\
+		cbfn = (tx)->stop_cbfn;					\
+		cbarg = (tx)->stop_cbarg;				\
+		(tx)->stop_cbfn = NULL;					\
+		(tx)->stop_cbarg = NULL;				\
+		cbfn(cbarg, (tx));					\
+	}								\
+} while (0)
+
+#define call_tx_prio_change_cbfn(tx)					\
+do {									\
+	if ((tx)->prio_change_cbfn) {					\
+		void (*cbfn)(struct bnad *, struct bna_tx *);	\
+		cbfn = (tx)->prio_change_cbfn;				\
+		(tx)->prio_change_cbfn = NULL;				\
+		cbfn((tx)->bna->bnad, (tx));				\
+	}								\
+} while (0)
+
+static void bna_tx_mod_cb_tx_stopped(void *tx_mod, struct bna_tx *tx);
+static void bna_bfi_tx_enet_start(struct bna_tx *tx);
+static void bna_tx_enet_stop(struct bna_tx *tx);
+
+enum bna_tx_event {
+	TX_E_START			= 1,
+	TX_E_STOP			= 2,
+	TX_E_FAIL			= 3,
+	TX_E_STARTED			= 4,
+	TX_E_STOPPED			= 5,
+	TX_E_PRIO_CHANGE		= 6,
+	TX_E_CLEANUP_DONE		= 7,
+	TX_E_BW_UPDATE			= 8,
+};
+
+bfa_fsm_state_decl(bna_tx, stopped, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, start_wait, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, started, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, stop_wait, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, cleanup_wait, struct bna_tx,
+			enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, prio_stop_wait, struct bna_tx,
+			enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, prio_cleanup_wait, struct bna_tx,
+			enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, failed, struct bna_tx, enum bna_tx_event);
+bfa_fsm_state_decl(bna_tx, quiesce_wait, struct bna_tx,
+			enum bna_tx_event);
+
+static void
+bna_tx_sm_stopped_entry(struct bna_tx *tx)
+{
+	call_tx_stop_cbfn(tx);
+}
+
+static void
+bna_tx_sm_stopped(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_START:
+		bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+		break;
+
+	case TX_E_STOP:
+		call_tx_stop_cbfn(tx);
+		break;
+
+	case TX_E_FAIL:
+		/* No-op */
+		break;
+
+	case TX_E_PRIO_CHANGE:
+		call_tx_prio_change_cbfn(tx);
+		break;
+
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_start_wait_entry(struct bna_tx *tx)
+{
+	bna_bfi_tx_enet_start(tx);
+}
+
+static void
+bna_tx_sm_start_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_STOP:
+		tx->flags &= ~(BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED);
+		bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+		break;
+
+	case TX_E_FAIL:
+		tx->flags &= ~(BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED);
+		bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+		break;
+
+	case TX_E_STARTED:
+		if (tx->flags & (BNA_TX_F_PRIO_CHANGED | BNA_TX_F_BW_UPDATED)) {
+			tx->flags &= ~(BNA_TX_F_PRIO_CHANGED |
+				BNA_TX_F_BW_UPDATED);
+			bfa_fsm_set_state(tx, bna_tx_sm_prio_stop_wait);
+		} else
+			bfa_fsm_set_state(tx, bna_tx_sm_started);
+		break;
+
+	case TX_E_PRIO_CHANGE:
+		tx->flags |=  BNA_TX_F_PRIO_CHANGED;
+		break;
+
+	case TX_E_BW_UPDATE:
+		tx->flags |= BNA_TX_F_BW_UPDATED;
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_started_entry(struct bna_tx *tx)
+{
+	struct bna_txq *txq;
+	struct list_head		 *qe;
+	int is_regular = (tx->type == BNA_TX_T_REGULAR);
+
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		txq->tcb->priority = txq->priority;
+		/* Start IB */
+		bna_ib_start(tx->bna, &txq->ib, is_regular);
+	}
+	tx->tx_resume_cbfn(tx->bna->bnad, tx);
+}
+
+static void
+bna_tx_sm_started(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_STOP:
+		bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+		tx->tx_stall_cbfn(tx->bna->bnad, tx);
+		bna_tx_enet_stop(tx);
+		break;
+
+	case TX_E_FAIL:
+		bfa_fsm_set_state(tx, bna_tx_sm_failed);
+		tx->tx_stall_cbfn(tx->bna->bnad, tx);
+		tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+		break;
+
+	case TX_E_PRIO_CHANGE:
+	case TX_E_BW_UPDATE:
+		bfa_fsm_set_state(tx, bna_tx_sm_prio_stop_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_stop_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_FAIL:
+	case TX_E_STOPPED:
+		bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+		tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+		break;
+
+	case TX_E_STARTED:
+		/**
+		 * We are here due to start_wait -> stop_wait transition on
+		 * TX_E_STOP event
+		 */
+		bna_tx_enet_stop(tx);
+		break;
+
+	case TX_E_PRIO_CHANGE:
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_cleanup_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_cleanup_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_FAIL:
+	case TX_E_PRIO_CHANGE:
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	case TX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_prio_stop_wait_entry(struct bna_tx *tx)
+{
+	tx->tx_stall_cbfn(tx->bna->bnad, tx);
+	bna_tx_enet_stop(tx);
+}
+
+static void
+bna_tx_sm_prio_stop_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_STOP:
+		bfa_fsm_set_state(tx, bna_tx_sm_stop_wait);
+		break;
+
+	case TX_E_FAIL:
+		bfa_fsm_set_state(tx, bna_tx_sm_failed);
+		call_tx_prio_change_cbfn(tx);
+		tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+		break;
+
+	case TX_E_STOPPED:
+		bfa_fsm_set_state(tx, bna_tx_sm_prio_cleanup_wait);
+		break;
+
+	case TX_E_PRIO_CHANGE:
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_prio_cleanup_wait_entry(struct bna_tx *tx)
+{
+	call_tx_prio_change_cbfn(tx);
+	tx->tx_cleanup_cbfn(tx->bna->bnad, tx);
+}
+
+static void
+bna_tx_sm_prio_cleanup_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_STOP:
+		bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+		break;
+
+	case TX_E_FAIL:
+		bfa_fsm_set_state(tx, bna_tx_sm_failed);
+		break;
+
+	case TX_E_PRIO_CHANGE:
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	case TX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_failed_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_failed(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_START:
+		bfa_fsm_set_state(tx, bna_tx_sm_quiesce_wait);
+		break;
+
+	case TX_E_STOP:
+		bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+		break;
+
+	case TX_E_FAIL:
+		/* No-op */
+		break;
+
+	case TX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_tx_sm_quiesce_wait_entry(struct bna_tx *tx)
+{
+}
+
+static void
+bna_tx_sm_quiesce_wait(struct bna_tx *tx, enum bna_tx_event event)
+{
+	switch (event) {
+	case TX_E_STOP:
+		bfa_fsm_set_state(tx, bna_tx_sm_cleanup_wait);
+		break;
+
+	case TX_E_FAIL:
+		bfa_fsm_set_state(tx, bna_tx_sm_failed);
+		break;
+
+	case TX_E_CLEANUP_DONE:
+		bfa_fsm_set_state(tx, bna_tx_sm_start_wait);
+		break;
+
+	case TX_E_BW_UPDATE:
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_bfi_tx_enet_start(struct bna_tx *tx)
+{
+	struct bfi_enet_tx_cfg_req *cfg_req = &tx->bfi_enet_cmd.cfg_req;
+	struct bna_txq *txq = NULL;
+	struct list_head *qe;
+	int i;
+
+	bfi_msgq_mhdr_set(cfg_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_TX_CFG_SET_REQ, 0, tx->rid);
+	cfg_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_tx_cfg_req)));
+
+	cfg_req->num_queues = tx->num_txq;
+	for (i = 0, qe = bfa_q_first(&tx->txq_q);
+		i < tx->num_txq;
+		i++, qe = bfa_q_next(qe)) {
+		txq = (struct bna_txq *)qe;
+
+		bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].q.q, &txq->qpt);
+		cfg_req->q_cfg[i].q.priority = txq->priority;
+
+		cfg_req->q_cfg[i].ib.index_addr.a32.addr_lo =
+			txq->ib.ib_seg_host_addr.lsb;
+		cfg_req->q_cfg[i].ib.index_addr.a32.addr_hi =
+			txq->ib.ib_seg_host_addr.msb;
+		cfg_req->q_cfg[i].ib.intr.msix_index =
+			htons((u16)txq->ib.intr_vector);
+	}
+
+	cfg_req->ib_cfg.int_pkt_dma = BNA_STATUS_T_ENABLED;
+	cfg_req->ib_cfg.int_enabled = BNA_STATUS_T_ENABLED;
+	cfg_req->ib_cfg.int_pkt_enabled = BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.continuous_coalescing = BNA_STATUS_T_ENABLED;
+	cfg_req->ib_cfg.msix = (txq->ib.intr_type == BNA_INTR_T_MSIX)
+				? BNA_STATUS_T_ENABLED : BNA_STATUS_T_DISABLED;
+	cfg_req->ib_cfg.coalescing_timeout =
+			htonl((u32)txq->ib.coalescing_timeo);
+	cfg_req->ib_cfg.inter_pkt_timeout =
+			htonl((u32)txq->ib.interpkt_timeo);
+	cfg_req->ib_cfg.inter_pkt_count = (u8)txq->ib.interpkt_count;
+
+	cfg_req->tx_cfg.vlan_mode = BFI_ENET_TX_VLAN_WI;
+	cfg_req->tx_cfg.vlan_id = htons((u16)tx->txf_vlan_id);
+	cfg_req->tx_cfg.admit_tagged_frame = BNA_STATUS_T_DISABLED;
+	cfg_req->tx_cfg.apply_vlan_filter = BNA_STATUS_T_DISABLED;
+
+	bfa_msgq_cmd_set(&tx->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_tx_cfg_req), &cfg_req->mh);
+	bfa_msgq_cmd_post(&tx->bna->msgq, &tx->msgq_cmd);
+}
+
+static void
+bna_bfi_tx_enet_stop(struct bna_tx *tx)
+{
+	struct bfi_enet_req *req = &tx->bfi_enet_cmd.req;
+
+	bfi_msgq_mhdr_set(req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_TX_CFG_CLR_REQ, 0, tx->rid);
+	req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_req)));
+	bfa_msgq_cmd_set(&tx->msgq_cmd, NULL, NULL, sizeof(struct bfi_enet_req),
+		&req->mh);
+	bfa_msgq_cmd_post(&tx->bna->msgq, &tx->msgq_cmd);
+}
+
+static void
+bna_tx_enet_stop(struct bna_tx *tx)
+{
+	struct bna_txq *txq;
+	struct list_head		 *qe;
+
+	/* Stop IB */
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		bna_ib_stop(tx->bna, &txq->ib);
+	}
+
+	bna_bfi_tx_enet_stop(tx);
+}
+
+static void
+bna_txq_qpt_setup(struct bna_txq *txq, int page_count, int page_size,
+		struct bna_mem_descr *qpt_mem,
+		struct bna_mem_descr *swqpt_mem,
+		struct bna_mem_descr *page_mem)
+{
+	int i;
+
+	txq->qpt.hw_qpt_ptr.lsb = qpt_mem->dma.lsb;
+	txq->qpt.hw_qpt_ptr.msb = qpt_mem->dma.msb;
+	txq->qpt.kv_qpt_ptr = qpt_mem->kva;
+	txq->qpt.page_count = page_count;
+	txq->qpt.page_size = page_size;
+
+	txq->tcb->sw_qpt = (void **) swqpt_mem->kva;
+
+	for (i = 0; i < page_count; i++) {
+		txq->tcb->sw_qpt[i] = page_mem[i].kva;
+
+		((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].lsb =
+			page_mem[i].dma.lsb;
+		((struct bna_dma_addr *)txq->qpt.kv_qpt_ptr)[i].msb =
+			page_mem[i].dma.msb;
+	}
+}
+
+static struct bna_tx *
+bna_tx_get(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+	struct list_head	*qe = NULL;
+	struct bna_tx *tx = NULL;
+
+	if (list_empty(&tx_mod->tx_free_q))
+		return NULL;
+	if (type == BNA_TX_T_REGULAR) {
+		bfa_q_deq(&tx_mod->tx_free_q, &qe);
+	} else {
+		bfa_q_deq_tail(&tx_mod->tx_free_q, &qe);
+	}
+	tx = (struct bna_tx *)qe;
+	bfa_q_qe_init(&tx->qe);
+	tx->type = type;
+
+	return tx;
+}
+
+static void
+bna_tx_free(struct bna_tx *tx)
+{
+	struct bna_tx_mod *tx_mod = &tx->bna->tx_mod;
+	struct bna_txq *txq;
+	struct list_head *prev_qe;
+	struct list_head *qe;
+
+	while (!list_empty(&tx->txq_q)) {
+		bfa_q_deq(&tx->txq_q, &txq);
+		bfa_q_qe_init(&txq->qe);
+		txq->tcb = NULL;
+		txq->tx = NULL;
+		list_add_tail(&txq->qe, &tx_mod->txq_free_q);
+	}
+
+	list_for_each(qe, &tx_mod->tx_active_q) {
+		if (qe == &tx->qe) {
+			list_del(&tx->qe);
+			bfa_q_qe_init(&tx->qe);
+			break;
+		}
+	}
+
+	tx->bna = NULL;
+	tx->priv = NULL;
+
+	prev_qe = NULL;
+	list_for_each(qe, &tx_mod->tx_free_q) {
+		if (((struct bna_tx *)qe)->rid < tx->rid)
+			prev_qe = qe;
+		else {
+			break;
+		}
+	}
+
+	if (prev_qe == NULL) {
+		/* This is the first entry */
+		bfa_q_enq_head(&tx_mod->tx_free_q, &tx->qe);
+	} else if (bfa_q_next(prev_qe) == &tx_mod->tx_free_q) {
+		/* This is the last entry */
+		list_add_tail(&tx->qe, &tx_mod->tx_free_q);
+	} else {
+		/* Somewhere in the middle */
+		bfa_q_next(&tx->qe) = bfa_q_next(prev_qe);
+		bfa_q_prev(&tx->qe) = prev_qe;
+		bfa_q_next(prev_qe) = &tx->qe;
+		bfa_q_prev(bfa_q_next(&tx->qe)) = &tx->qe;
+	}
+}
+
+static void
+bna_tx_start(struct bna_tx *tx)
+{
+	tx->flags |= BNA_TX_F_ENET_STARTED;
+	if (tx->flags & BNA_TX_F_ENABLED)
+		bfa_fsm_send_event(tx, TX_E_START);
+}
+
+static void
+bna_tx_stop(struct bna_tx *tx)
+{
+	tx->stop_cbfn = bna_tx_mod_cb_tx_stopped;
+	tx->stop_cbarg = &tx->bna->tx_mod;
+
+	tx->flags &= ~BNA_TX_F_ENET_STARTED;
+	bfa_fsm_send_event(tx, TX_E_STOP);
+}
+
+static void
+bna_tx_fail(struct bna_tx *tx)
+{
+	tx->flags &= ~BNA_TX_F_ENET_STARTED;
+	bfa_fsm_send_event(tx, TX_E_FAIL);
+}
+
+static void
+bna_tx_prio_changed(struct bna_tx *tx)
+{
+	struct bna_tx_mod *tx_mod = &tx->bna->tx_mod;
+	struct bna_txq *txq;
+	struct list_head		 *qe;
+
+	/* No need of priority reconfiguration for loopback Tx */
+	if (tx->type != BNA_TX_T_REGULAR)
+		return;
+
+	/**
+	 * If there are exactly 8 TxQs, each one occupies one priority.
+	 * In such case, there is nothing to reconfigure, since their
+	 * priorities remain the same.
+	 */
+	if (tx->num_txq != BFI_TX_MAX_PRIO) {
+		list_for_each(qe, &tx->txq_q) {
+			txq = (struct bna_txq *)qe;
+			txq->priority = (u8)tx_mod->default_prio;
+		}
+
+		bfa_fsm_send_event(tx, TX_E_PRIO_CHANGE);
+	}
+}
+
+void
+bna_bfi_tx_enet_start_rsp(struct bna_tx *tx, struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_tx_cfg_rsp *cfg_rsp = &tx->bfi_enet_cmd.cfg_rsp;
+	struct bna_txq *txq = NULL;
+	struct list_head *qe;
+	int i;
+
+	bfa_msgq_rsp_copy(&tx->bna->msgq, (u8 *)cfg_rsp,
+		sizeof(struct bfi_enet_tx_cfg_rsp));
+
+	tx->hw_id = cfg_rsp->hw_id;
+
+	for (i = 0, qe = bfa_q_first(&tx->txq_q);
+		i < tx->num_txq; i++, qe = bfa_q_next(qe)) {
+		txq = (struct bna_txq *)qe;
+
+		/* Setup doorbells */
+		txq->tcb->i_dbell->doorbell_addr =
+			tx->bna->pcidev.pci_bar_kva
+			+ ntohl(cfg_rsp->q_handles[i].i_dbell);
+		txq->tcb->q_dbell =
+			tx->bna->pcidev.pci_bar_kva
+			+ ntohl(cfg_rsp->q_handles[i].q_dbell);
+		txq->hw_id = cfg_rsp->q_handles[i].hw_qid;
+
+		/* Initialize producer/consumer indexes */
+		(*txq->tcb->hw_consumer_index) = 0;
+		txq->tcb->producer_index = txq->tcb->consumer_index = 0;
+	}
+
+	bfa_fsm_send_event(tx, TX_E_STARTED);
+}
+
+void
+bna_bfi_tx_enet_stop_rsp(struct bna_tx *tx, struct bfi_msgq_mhdr *msghdr)
+{
+	bfa_fsm_send_event(tx, TX_E_STOPPED);
+}
+
+void
+bna_bfi_bw_update_aen(struct bna_tx_mod *tx_mod)
+{
+	struct bna_tx *tx;
+	struct list_head		*qe;
+
+	list_for_each(qe, &tx_mod->tx_active_q) {
+		tx = (struct bna_tx *)qe;
+		bfa_fsm_send_event(tx, TX_E_BW_UPDATE);
+	}
+}
+
+void
+bna_tx_res_req(int num_txq, int txq_depth, struct bna_res_info *res_info)
+{
+	u32 q_size;
+	u32 page_count;
+	struct bna_mem_info *mem_info;
+
+	res_info[BNA_TX_RES_MEM_T_TCB].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = sizeof(struct bna_tcb);
+	mem_info->num = num_txq;
+
+	q_size = txq_depth * BFI_TXQ_WI_SIZE;
+	q_size = ALIGN(q_size, PAGE_SIZE);
+	page_count = q_size >> PAGE_SHIFT;
+
+	res_info[BNA_TX_RES_MEM_T_QPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = page_count * sizeof(struct bna_dma_addr);
+	mem_info->num = num_txq;
+
+	res_info[BNA_TX_RES_MEM_T_SWQPT].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_KVA;
+	mem_info->len = page_count * sizeof(void *);
+	mem_info->num = num_txq;
+
+	res_info[BNA_TX_RES_MEM_T_PAGE].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = PAGE_SIZE;
+	mem_info->num = num_txq * page_count;
+
+	res_info[BNA_TX_RES_MEM_T_IBIDX].res_type = BNA_RES_T_MEM;
+	mem_info = &res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info;
+	mem_info->mem_type = BNA_MEM_T_DMA;
+	mem_info->len = BFI_IBIDX_SIZE;
+	mem_info->num = num_txq;
+
+	res_info[BNA_TX_RES_INTR_T_TXCMPL].res_type = BNA_RES_T_INTR;
+	res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.intr_type =
+			BNA_INTR_T_MSIX;
+	res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info.num = num_txq;
+}
+
+struct bna_tx *
+bna_tx_create(struct bna *bna, struct bnad *bnad,
+		struct bna_tx_config *tx_cfg,
+		struct bna_tx_event_cbfn *tx_cbfn,
+		struct bna_res_info *res_info, void *priv)
+{
+	struct bna_intr_info *intr_info;
+	struct bna_tx_mod *tx_mod = &bna->tx_mod;
+	struct bna_tx *tx;
+	struct bna_txq *txq;
+	struct list_head *qe;
+	int page_count;
+	int page_size;
+	int page_idx;
+	int i;
+
+	intr_info = &res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
+	page_count = (res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.num) /
+			tx_cfg->num_txq;
+	page_size = res_info[BNA_TX_RES_MEM_T_PAGE].res_u.mem_info.len;
+
+	/**
+	 * Get resources
+	 */
+
+	if ((intr_info->num != 1) && (intr_info->num != tx_cfg->num_txq))
+		return NULL;
+
+	/* Tx */
+
+	tx = bna_tx_get(tx_mod, tx_cfg->tx_type);
+	if (!tx)
+		return NULL;
+	tx->bna = bna;
+	tx->priv = priv;
+
+	/* TxQs */
+
+	INIT_LIST_HEAD(&tx->txq_q);
+	for (i = 0; i < tx_cfg->num_txq; i++) {
+		if (list_empty(&tx_mod->txq_free_q))
+			goto err_return;
+
+		bfa_q_deq(&tx_mod->txq_free_q, &txq);
+		bfa_q_qe_init(&txq->qe);
+		list_add_tail(&txq->qe, &tx->txq_q);
+		txq->tx = tx;
+	}
+
+	/*
+	 * Initialize
+	 */
+
+	/* Tx */
+
+	tx->tcb_setup_cbfn = tx_cbfn->tcb_setup_cbfn;
+	tx->tcb_destroy_cbfn = tx_cbfn->tcb_destroy_cbfn;
+	/* Following callbacks are mandatory */
+	tx->tx_stall_cbfn = tx_cbfn->tx_stall_cbfn;
+	tx->tx_resume_cbfn = tx_cbfn->tx_resume_cbfn;
+	tx->tx_cleanup_cbfn = tx_cbfn->tx_cleanup_cbfn;
+
+	list_add_tail(&tx->qe, &tx_mod->tx_active_q);
+
+	tx->num_txq = tx_cfg->num_txq;
+
+	tx->flags = 0;
+	if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_ENET_STARTED) {
+		switch (tx->type) {
+		case BNA_TX_T_REGULAR:
+			if (!(tx->bna->tx_mod.flags &
+				BNA_TX_MOD_F_ENET_LOOPBACK))
+				tx->flags |= BNA_TX_F_ENET_STARTED;
+			break;
+		case BNA_TX_T_LOOPBACK:
+			if (tx->bna->tx_mod.flags & BNA_TX_MOD_F_ENET_LOOPBACK)
+				tx->flags |= BNA_TX_F_ENET_STARTED;
+			break;
+		}
+	}
+
+	/* TxQ */
+
+	i = 0;
+	page_idx = 0;
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		txq->tcb = (struct bna_tcb *)
+		res_info[BNA_TX_RES_MEM_T_TCB].res_u.mem_info.mdl[i].kva;
+		txq->tx_packets = 0;
+		txq->tx_bytes = 0;
+
+		/* IB */
+		txq->ib.ib_seg_host_addr.lsb =
+		res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.lsb;
+		txq->ib.ib_seg_host_addr.msb =
+		res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].dma.msb;
+		txq->ib.ib_seg_host_addr_kva =
+		res_info[BNA_TX_RES_MEM_T_IBIDX].res_u.mem_info.mdl[i].kva;
+		txq->ib.intr_type = intr_info->intr_type;
+		txq->ib.intr_vector = (intr_info->num == 1) ?
+					intr_info->idl[0].vector :
+					intr_info->idl[i].vector;
+		if (intr_info->intr_type == BNA_INTR_T_INTX)
+			txq->ib.intr_vector = (1 <<  txq->ib.intr_vector);
+		txq->ib.coalescing_timeo = tx_cfg->coalescing_timeo;
+		txq->ib.interpkt_timeo = 0; /* Not used */
+		txq->ib.interpkt_count = BFI_TX_INTERPKT_COUNT;
+
+		/* TCB */
+
+		txq->tcb->q_depth = tx_cfg->txq_depth;
+		txq->tcb->unmap_q = (void *)
+		res_info[BNA_TX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[i].kva;
+		txq->tcb->hw_consumer_index =
+			(u32 *)txq->ib.ib_seg_host_addr_kva;
+		txq->tcb->i_dbell = &txq->ib.door_bell;
+		txq->tcb->intr_type = txq->ib.intr_type;
+		txq->tcb->intr_vector = txq->ib.intr_vector;
+		txq->tcb->txq = txq;
+		txq->tcb->bnad = bnad;
+		txq->tcb->id = i;
+
+		/* QPT, SWQPT, Pages */
+		bna_txq_qpt_setup(txq, page_count, page_size,
+			&res_info[BNA_TX_RES_MEM_T_QPT].res_u.mem_info.mdl[i],
+			&res_info[BNA_TX_RES_MEM_T_SWQPT].res_u.mem_info.mdl[i],
+			&res_info[BNA_TX_RES_MEM_T_PAGE].
+				  res_u.mem_info.mdl[page_idx]);
+		txq->tcb->page_idx = page_idx;
+		txq->tcb->page_count = page_count;
+		page_idx += page_count;
+
+		/* Callback to bnad for setting up TCB */
+		if (tx->tcb_setup_cbfn)
+			(tx->tcb_setup_cbfn)(bna->bnad, txq->tcb);
+
+		if (tx_cfg->num_txq == BFI_TX_MAX_PRIO)
+			txq->priority = txq->tcb->id;
+		else
+			txq->priority = tx_mod->default_prio;
+
+		i++;
+	}
+
+	tx->txf_vlan_id = 0;
+
+	bfa_fsm_set_state(tx, bna_tx_sm_stopped);
+
+	tx_mod->rid_mask |= (1 << tx->rid);
+
+	return tx;
+
+err_return:
+	bna_tx_free(tx);
+	return NULL;
+}
+
+void
+bna_tx_destroy(struct bna_tx *tx)
+{
+	struct bna_txq *txq;
+	struct list_head *qe;
+
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		if (tx->tcb_destroy_cbfn)
+			(tx->tcb_destroy_cbfn)(tx->bna->bnad, txq->tcb);
+	}
+
+	tx->bna->tx_mod.rid_mask &= ~(1 << tx->rid);
+	bna_tx_free(tx);
+}
+
+void
+bna_tx_enable(struct bna_tx *tx)
+{
+	if (tx->fsm != (bfa_sm_t)bna_tx_sm_stopped)
+		return;
+
+	tx->flags |= BNA_TX_F_ENABLED;
+
+	if (tx->flags & BNA_TX_F_ENET_STARTED)
+		bfa_fsm_send_event(tx, TX_E_START);
+}
+
+void
+bna_tx_disable(struct bna_tx *tx, enum bna_cleanup_type type,
+		void (*cbfn)(void *, struct bna_tx *))
+{
+	if (type == BNA_SOFT_CLEANUP) {
+		(*cbfn)(tx->bna->bnad, tx);
+		return;
+	}
+
+	tx->stop_cbfn = cbfn;
+	tx->stop_cbarg = tx->bna->bnad;
+
+	tx->flags &= ~BNA_TX_F_ENABLED;
+
+	bfa_fsm_send_event(tx, TX_E_STOP);
+}
+
+void
+bna_tx_cleanup_complete(struct bna_tx *tx)
+{
+	bfa_fsm_send_event(tx, TX_E_CLEANUP_DONE);
+}
+
+void
+bna_tx_prio_set(struct bna_tx *tx, int prio,
+		void (*cbfn)(struct bnad *, struct bna_tx *))
+{
+	struct bna_txq *txq;
+	struct list_head		 *qe;
+
+	tx->prio_change_cbfn = cbfn;
+
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		txq->priority = (u8)prio;
+	}
+
+	bfa_fsm_send_event(tx, TX_E_PRIO_CHANGE);
+}
+
+static void
+bna_tx_mod_cb_tx_stopped(void *arg, struct bna_tx *tx)
+{
+	struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
+
+	bfa_wc_down(&tx_mod->tx_stop_wc);
+}
+
+static void
+bna_tx_mod_cb_tx_stopped_all(void *arg)
+{
+	struct bna_tx_mod *tx_mod = (struct bna_tx_mod *)arg;
+
+	if (tx_mod->stop_cbfn)
+		tx_mod->stop_cbfn(&tx_mod->bna->enet);
+	tx_mod->stop_cbfn = NULL;
+}
+
+void
+bna_tx_mod_init(struct bna_tx_mod *tx_mod, struct bna *bna,
+		struct bna_res_info *res_info)
+{
+	int i;
+
+	tx_mod->bna = bna;
+	tx_mod->flags = 0;
+
+	tx_mod->tx = (struct bna_tx *)
+		res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.mdl[0].kva;
+	tx_mod->txq = (struct bna_txq *)
+		res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mdl[0].kva;
+
+	INIT_LIST_HEAD(&tx_mod->tx_free_q);
+	INIT_LIST_HEAD(&tx_mod->tx_active_q);
+
+	INIT_LIST_HEAD(&tx_mod->txq_free_q);
+
+	for (i = 0; i < bna->ioceth.attr.num_txq; i++) {
+		tx_mod->tx[i].rid = i;
+		bfa_q_qe_init(&tx_mod->tx[i].qe);
+		list_add_tail(&tx_mod->tx[i].qe, &tx_mod->tx_free_q);
+		bfa_q_qe_init(&tx_mod->txq[i].qe);
+		list_add_tail(&tx_mod->txq[i].qe, &tx_mod->txq_free_q);
+	}
+
+	tx_mod->prio_map = BFI_TX_PRIO_MAP_ALL;
+	tx_mod->default_prio = 0;
+	tx_mod->iscsi_over_cee = BNA_STATUS_T_DISABLED;
+	tx_mod->iscsi_prio = -1;
+}
+
+void
+bna_tx_mod_uninit(struct bna_tx_mod *tx_mod)
+{
+	struct list_head		*qe;
+	int i;
+
+	i = 0;
+	list_for_each(qe, &tx_mod->tx_free_q)
+		i++;
+
+	i = 0;
+	list_for_each(qe, &tx_mod->txq_free_q)
+		i++;
+
+	tx_mod->bna = NULL;
+}
+
+void
+bna_tx_mod_start(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+	struct bna_tx *tx;
+	struct list_head		*qe;
+
+	tx_mod->flags |= BNA_TX_MOD_F_ENET_STARTED;
+	if (type == BNA_TX_T_LOOPBACK)
+		tx_mod->flags |= BNA_TX_MOD_F_ENET_LOOPBACK;
+
+	list_for_each(qe, &tx_mod->tx_active_q) {
+		tx = (struct bna_tx *)qe;
+		if (tx->type == type)
+			bna_tx_start(tx);
+	}
+}
+
+void
+bna_tx_mod_stop(struct bna_tx_mod *tx_mod, enum bna_tx_type type)
+{
+	struct bna_tx *tx;
+	struct list_head		*qe;
+
+	tx_mod->flags &= ~BNA_TX_MOD_F_ENET_STARTED;
+	tx_mod->flags &= ~BNA_TX_MOD_F_ENET_LOOPBACK;
+
+	tx_mod->stop_cbfn = bna_enet_cb_tx_stopped;
+
+	bfa_wc_init(&tx_mod->tx_stop_wc, bna_tx_mod_cb_tx_stopped_all, tx_mod);
+
+	list_for_each(qe, &tx_mod->tx_active_q) {
+		tx = (struct bna_tx *)qe;
+		if (tx->type == type) {
+			bfa_wc_up(&tx_mod->tx_stop_wc);
+			bna_tx_stop(tx);
+		}
+	}
+
+	bfa_wc_wait(&tx_mod->tx_stop_wc);
+}
+
+void
+bna_tx_mod_fail(struct bna_tx_mod *tx_mod)
+{
+	struct bna_tx *tx;
+	struct list_head		*qe;
+
+	tx_mod->flags &= ~BNA_TX_MOD_F_ENET_STARTED;
+	tx_mod->flags &= ~BNA_TX_MOD_F_ENET_LOOPBACK;
+
+	list_for_each(qe, &tx_mod->tx_active_q) {
+		tx = (struct bna_tx *)qe;
+		bna_tx_fail(tx);
+	}
+}
+
+void
+bna_tx_mod_prio_reconfig(struct bna_tx_mod *tx_mod, int cee_linkup,
+			u8 prio_map, u8 iscsi_prio_map)
+{
+	struct bna_tx *tx;
+	struct list_head		*qe;
+	int need_txq_reconfig = 0;
+	int iscsi_prio = -1;
+	int default_prio = -1;
+	int i;
+
+	/* Select the priority map */
+	if (!cee_linkup) {
+		/* No CEE. Use all priorities */
+		prio_map = BFI_TX_PRIO_MAP_ALL;
+		iscsi_prio_map = 0;
+		default_prio = 0;
+	} else {
+		/* Select default priority */
+		for (i = 0; i < BFI_TX_MAX_PRIO; i++) {
+			if ((prio_map >> i) & 0x1) {
+				default_prio = i;
+				break;
+			}
+		}
+
+		/* Derive iscsi priority */
+		if (iscsi_prio_map) {
+			for (i = 0; i < BFI_TX_MAX_PRIO; i++) {
+				if ((iscsi_prio_map >> i) & 0x1) {
+					iscsi_prio = i;
+					break;
+				}
+			}
+		}
+
+		/**
+		 * Network traffic priority map is a superset of iSCSI and
+		 * non iSCSI traffic. We are only giving a 'lift' to iSCSI
+		 * traffic by redirecting it to iSCSI priority. Other NW
+		 * traffic is still allowed to use iSCSI priority
+		 */
+		prio_map |= iscsi_prio_map;
+	}
+
+	if ((prio_map != tx_mod->prio_map) ||
+	    (default_prio != tx_mod->default_prio) ||
+	    (iscsi_prio_map && (iscsi_prio != tx_mod->iscsi_prio)))
+		need_txq_reconfig = 1;
+
+	tx_mod->prio_map = prio_map;
+	tx_mod->default_prio = default_prio;
+	tx_mod->iscsi_over_cee = (iscsi_prio_map ? BNA_STATUS_T_ENABLED :
+				BNA_STATUS_T_DISABLED);
+	tx_mod->iscsi_prio = iscsi_prio;
+	tx_mod->prio_reconfigured = need_txq_reconfig;
+
+	/* Reconfigure the TxQs */
+	if (need_txq_reconfig) {
+		list_for_each(qe, &tx_mod->tx_active_q) {
+			tx = (struct bna_tx *)qe;
+			bna_tx_prio_changed(tx);
+		}
+	}
+}
+
+void
+bna_tx_coalescing_timeo_set(struct bna_tx *tx, int coalescing_timeo)
+{
+	struct bna_txq *txq;
+	struct list_head *qe;
+
+	list_for_each(qe, &tx->txq_q) {
+		txq = (struct bna_txq *)qe;
+		bna_ib_coalescing_timeo_set(&txq->ib, coalescing_timeo);
+	}
+}
-- 
1.7.1


^ permalink raw reply related

* [PATCH 7/8] bna: Introduce ENET as New Driver and FW Interface
From: Rasesh Mody @ 2011-07-27  2:10 UTC (permalink / raw)
  To: davem, netdev; +Cc: adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1311732648-29876-1-git-send-email-rmody@brocade.com>

Change details:
 - This patch contains the messages, opcodes and structure format for the
   messages and responses exchanged between driver and the FW. In addition
   this patch contains the state machine implementation for Ethport, Enet,
   IOCEth.
 - Ethport object is responsible for receiving link state events, sending
   port enable/disable commands to FW.
 - Enet object is responsible for synchronizing initialization/teardown of
   tx & rx datapath configuration.
 - IOCEth object is responsible for init/un-init of IO Controller in the
   adapter which runs the FW.
 - This patch also contains code for initialization and resource assignment
   for Ethport, Enet, IOCEth, Tx, Rx objects.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/bna/bfi_enet.h |  902 ++++++++++++++++++
 drivers/net/bna/bna_enet.c | 2199 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 3101 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/bna/bfi_enet.h
 create mode 100644 drivers/net/bna/bna_enet.c

diff --git a/drivers/net/bna/bfi_enet.h b/drivers/net/bna/bfi_enet.h
new file mode 100644
index 0000000..17b34ab
--- /dev/null
+++ b/drivers/net/bna/bfi_enet.h
@@ -0,0 +1,902 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+/**
+ * @file bfi_enet.h BNA Hardware and Firmware Interface
+ */
+
+/**
+ * Skipping statistics collection to avoid clutter.
+ * Command is no longer needed:
+ *	MTU
+ *	TxQ Stop
+ *	RxQ Stop
+ *	RxF Enable/Disable
+ *
+ * HDS-off request is dynamic
+ * keep structures as multiple of 32-bit fields for alignment.
+ * All values must be written in big-endian.
+ */
+#ifndef __BFI_ENET_H__
+#define __BFI_ENET_H__
+
+#include "bfa_defs.h"
+#include "bfi.h"
+
+#pragma pack(1)
+
+#define BFI_ENET_CFG_MAX		32	/* Max resources per PF */
+
+#define BFI_ENET_TXQ_PRIO_MAX		8
+#define BFI_ENET_RX_QSET_MAX		16
+#define BFI_ENET_TXQ_WI_VECT_MAX	4
+
+#define BFI_ENET_VLAN_ID_MAX		4096
+#define BFI_ENET_VLAN_BLOCK_SIZE	512	/* in bits */
+#define BFI_ENET_VLAN_BLOCKS_MAX					\
+	(BFI_ENET_VLAN_ID_MAX / BFI_ENET_VLAN_BLOCK_SIZE)
+#define BFI_ENET_VLAN_WORD_SIZE		32	/* in bits */
+#define BFI_ENET_VLAN_WORDS_MAX						\
+	(BFI_ENET_VLAN_BLOCK_SIZE / BFI_ENET_VLAN_WORD_SIZE)
+
+#define BFI_ENET_RSS_RIT_MAX		64	/* entries */
+#define BFI_ENET_RSS_KEY_LEN		10	/* 32-bit words */
+
+union bfi_addr_be_u {
+	struct {
+		u32	addr_hi;	/* Most Significant 32-bits */
+		u32	addr_lo;	/* Least Significant 32-Bits */
+	} a32;
+};
+
+/**
+ *	T X   Q U E U E   D E F I N E S
+ */
+/* TxQ Vector (a.k.a. Tx-Buffer Descriptor) */
+/* TxQ Entry Opcodes */
+#define BFI_ENET_TXQ_WI_SEND		(0x402)	/* Single Frame Transmission */
+#define BFI_ENET_TXQ_WI_SEND_LSO	(0x403)	/* Multi-Frame Transmission */
+#define BFI_ENET_TXQ_WI_EXTENSION	(0x104)	/* Extension WI */
+
+/* TxQ Entry Control Flags */
+#define BFI_ENET_TXQ_WI_CF_FCOE_CRC	(1 << 8)
+#define BFI_ENET_TXQ_WI_CF_IPID_MODE	(1 << 5)
+#define BFI_ENET_TXQ_WI_CF_INS_PRIO	(1 << 4)
+#define BFI_ENET_TXQ_WI_CF_INS_VLAN	(1 << 3)
+#define BFI_ENET_TXQ_WI_CF_UDP_CKSUM	(1 << 2)
+#define BFI_ENET_TXQ_WI_CF_TCP_CKSUM	(1 << 1)
+#define BFI_ENET_TXQ_WI_CF_IP_CKSUM	(1 << 0)
+
+struct bfi_enet_txq_wi_base {
+	u8			reserved;
+	u8			num_vectors;	/* number of vectors present */
+	u16			opcode;
+			/* BFI_ENET_TXQ_WI_SEND or BFI_ENET_TXQ_WI_SEND_LSO */
+	u16			flags;		/* OR of all the flags */
+	u16			l4_hdr_size_n_offset;
+	u16			vlan_tag;
+	u16			lso_mss;	/* Only 14 LSB are valid */
+	u32			frame_length;	/* Only 24 LSB are valid */
+};
+
+struct bfi_enet_txq_wi_ext {
+	u16			reserved;
+	u16			opcode;		/* BFI_ENET_TXQ_WI_EXTENSION */
+	u32			reserved2[3];
+};
+
+struct bfi_enet_txq_wi_vector {			/* Tx Buffer Descriptor */
+	u16			reserved;
+	u16			length;		/* Only 14 LSB are valid */
+	union bfi_addr_be_u	addr;
+};
+
+/**
+ *  TxQ Entry Structure
+ *
+ */
+struct bfi_enet_txq_entry {
+	union {
+		struct bfi_enet_txq_wi_base	base;
+		struct bfi_enet_txq_wi_ext	ext;
+	} wi;
+	struct bfi_enet_txq_wi_vector vector[BFI_ENET_TXQ_WI_VECT_MAX];
+};
+
+#define wi_hdr		wi.base
+#define wi_ext_hdr	wi.ext
+
+#define BFI_ENET_TXQ_WI_L4_HDR_N_OFFSET(_hdr_size, _offset) \
+		(((_hdr_size) << 10) | ((_offset) & 0x3FF))
+
+/**
+ *   R X   Q U E U E   D E F I N E S
+ */
+struct bfi_enet_rxq_entry {
+	union bfi_addr_be_u  rx_buffer;
+};
+
+/**
+ *   R X   C O M P L E T I O N   Q U E U E   D E F I N E S
+ */
+/* CQ Entry Flags */
+#define	BFI_ENET_CQ_EF_MAC_ERROR	(1 <<  0)
+#define	BFI_ENET_CQ_EF_FCS_ERROR	(1 <<  1)
+#define	BFI_ENET_CQ_EF_TOO_LONG		(1 <<  2)
+#define	BFI_ENET_CQ_EF_FC_CRC_OK	(1 <<  3)
+
+#define	BFI_ENET_CQ_EF_RSVD1		(1 <<  4)
+#define	BFI_ENET_CQ_EF_L4_CKSUM_OK	(1 <<  5)
+#define	BFI_ENET_CQ_EF_L3_CKSUM_OK	(1 <<  6)
+#define	BFI_ENET_CQ_EF_HDS_HEADER	(1 <<  7)
+
+#define	BFI_ENET_CQ_EF_UDP		(1 <<  8)
+#define	BFI_ENET_CQ_EF_TCP		(1 <<  9)
+#define	BFI_ENET_CQ_EF_IP_OPTIONS	(1 << 10)
+#define	BFI_ENET_CQ_EF_IPV6		(1 << 11)
+
+#define	BFI_ENET_CQ_EF_IPV4		(1 << 12)
+#define	BFI_ENET_CQ_EF_VLAN		(1 << 13)
+#define	BFI_ENET_CQ_EF_RSS		(1 << 14)
+#define	BFI_ENET_CQ_EF_RSVD2		(1 << 15)
+
+#define	BFI_ENET_CQ_EF_MCAST_MATCH	(1 << 16)
+#define	BFI_ENET_CQ_EF_MCAST		(1 << 17)
+#define BFI_ENET_CQ_EF_BCAST		(1 << 18)
+#define	BFI_ENET_CQ_EF_REMOTE		(1 << 19)
+
+#define	BFI_ENET_CQ_EF_LOCAL		(1 << 20)
+
+/* CQ Entry Structure */
+struct bfi_enet_cq_entry {
+	u32 flags;
+	u16	vlan_tag;
+	u16	length;
+	u32	rss_hash;
+	u8	valid;
+	u8	reserved1;
+	u8	reserved2;
+	u8	rxq_id;
+};
+
+/**
+ *   E N E T   C O N T R O L   P A T H   C O M M A N D S
+ */
+struct bfi_enet_q {
+	union bfi_addr_u	pg_tbl;
+	union bfi_addr_u	first_entry;
+	u16		pages;	/* # of pages */
+	u16		page_sz;
+};
+
+struct bfi_enet_txq {
+	struct bfi_enet_q	q;
+	u8			priority;
+	u8			rsvd[3];
+};
+
+struct bfi_enet_rxq {
+	struct bfi_enet_q	q;
+	u16		rx_buffer_size;
+	u16		rsvd;
+};
+
+struct bfi_enet_cq {
+	struct bfi_enet_q	q;
+};
+
+struct bfi_enet_ib_cfg {
+	u8		int_pkt_dma;
+	u8		int_enabled;
+	u8		int_pkt_enabled;
+	u8		continuous_coalescing;
+	u8		msix;
+	u8		rsvd[3];
+	u32	coalescing_timeout;
+	u32	inter_pkt_timeout;
+	u8		inter_pkt_count;
+	u8		rsvd1[3];
+};
+
+struct bfi_enet_ib {
+	union bfi_addr_u	index_addr;
+	union {
+		u16	msix_index;
+		u16	intx_bitmask;
+	} intr;
+	u16		rsvd;
+};
+
+/**
+ * ENET command messages
+ */
+enum bfi_enet_h2i_msgs {
+	/* Rx Commands */
+	BFI_ENET_H2I_RX_CFG_SET_REQ = 1,
+	BFI_ENET_H2I_RX_CFG_CLR_REQ = 2,
+
+	BFI_ENET_H2I_RIT_CFG_REQ = 3,
+	BFI_ENET_H2I_RSS_CFG_REQ = 4,
+	BFI_ENET_H2I_RSS_ENABLE_REQ = 5,
+	BFI_ENET_H2I_RX_PROMISCUOUS_REQ = 6,
+	BFI_ENET_H2I_RX_DEFAULT_REQ = 7,
+
+	BFI_ENET_H2I_MAC_UCAST_SET_REQ = 8,
+	BFI_ENET_H2I_MAC_UCAST_CLR_REQ = 9,
+	BFI_ENET_H2I_MAC_UCAST_ADD_REQ = 10,
+	BFI_ENET_H2I_MAC_UCAST_DEL_REQ = 11,
+
+	BFI_ENET_H2I_MAC_MCAST_ADD_REQ = 12,
+	BFI_ENET_H2I_MAC_MCAST_DEL_REQ = 13,
+	BFI_ENET_H2I_MAC_MCAST_FILTER_REQ = 14,
+
+	BFI_ENET_H2I_RX_VLAN_SET_REQ = 15,
+	BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ = 16,
+
+	/* Tx Commands */
+	BFI_ENET_H2I_TX_CFG_SET_REQ = 17,
+	BFI_ENET_H2I_TX_CFG_CLR_REQ = 18,
+
+	/* Port Commands */
+	BFI_ENET_H2I_PORT_ADMIN_UP_REQ = 19,
+	BFI_ENET_H2I_SET_PAUSE_REQ = 20,
+	BFI_ENET_H2I_DIAG_LOOPBACK_REQ = 21,
+
+	/* Get Attributes Command */
+	BFI_ENET_H2I_GET_ATTR_REQ = 22,
+
+	/*  Statistics Commands */
+	BFI_ENET_H2I_STATS_GET_REQ = 23,
+	BFI_ENET_H2I_STATS_CLR_REQ = 24,
+
+	BFI_ENET_H2I_WOL_MAGIC_REQ = 25,
+	BFI_ENET_H2I_WOL_FRAME_REQ = 26,
+
+	BFI_ENET_H2I_MAX = 27,
+};
+
+enum bfi_enet_i2h_msgs {
+	/* Rx Responses */
+	BFI_ENET_I2H_RX_CFG_SET_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_CFG_SET_REQ),
+	BFI_ENET_I2H_RX_CFG_CLR_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_CFG_CLR_REQ),
+
+	BFI_ENET_I2H_RIT_CFG_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RIT_CFG_REQ),
+	BFI_ENET_I2H_RSS_CFG_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RSS_CFG_REQ),
+	BFI_ENET_I2H_RSS_ENABLE_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RSS_ENABLE_REQ),
+	BFI_ENET_I2H_RX_PROMISCUOUS_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_PROMISCUOUS_REQ),
+	BFI_ENET_I2H_RX_DEFAULT_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_DEFAULT_REQ),
+
+	BFI_ENET_I2H_MAC_UCAST_SET_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_SET_REQ),
+	BFI_ENET_I2H_MAC_UCAST_CLR_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_CLR_REQ),
+	BFI_ENET_I2H_MAC_UCAST_ADD_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_ADD_REQ),
+	BFI_ENET_I2H_MAC_UCAST_DEL_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_UCAST_DEL_REQ),
+
+	BFI_ENET_I2H_MAC_MCAST_ADD_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_ADD_REQ),
+	BFI_ENET_I2H_MAC_MCAST_DEL_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_DEL_REQ),
+	BFI_ENET_I2H_MAC_MCAST_FILTER_RSP =
+		BFA_I2HM(BFI_ENET_H2I_MAC_MCAST_FILTER_REQ),
+
+	BFI_ENET_I2H_RX_VLAN_SET_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_VLAN_SET_REQ),
+
+	BFI_ENET_I2H_RX_VLAN_STRIP_ENABLE_RSP =
+		BFA_I2HM(BFI_ENET_H2I_RX_VLAN_STRIP_ENABLE_REQ),
+
+	/* Tx Responses */
+	BFI_ENET_I2H_TX_CFG_SET_RSP =
+		BFA_I2HM(BFI_ENET_H2I_TX_CFG_SET_REQ),
+	BFI_ENET_I2H_TX_CFG_CLR_RSP =
+		BFA_I2HM(BFI_ENET_H2I_TX_CFG_CLR_REQ),
+
+	/* Port Responses */
+	BFI_ENET_I2H_PORT_ADMIN_RSP =
+		BFA_I2HM(BFI_ENET_H2I_PORT_ADMIN_UP_REQ),
+
+	BFI_ENET_I2H_SET_PAUSE_RSP =
+		BFA_I2HM(BFI_ENET_H2I_SET_PAUSE_REQ),
+	BFI_ENET_I2H_DIAG_LOOPBACK_RSP =
+		BFA_I2HM(BFI_ENET_H2I_DIAG_LOOPBACK_REQ),
+
+	/*  Attributes Response */
+	BFI_ENET_I2H_GET_ATTR_RSP =
+		BFA_I2HM(BFI_ENET_H2I_GET_ATTR_REQ),
+
+	/* Statistics Responses */
+	BFI_ENET_I2H_STATS_GET_RSP =
+		BFA_I2HM(BFI_ENET_H2I_STATS_GET_REQ),
+	BFI_ENET_I2H_STATS_CLR_RSP =
+		BFA_I2HM(BFI_ENET_H2I_STATS_CLR_REQ),
+
+	BFI_ENET_I2H_WOL_MAGIC_RSP =
+		BFA_I2HM(BFI_ENET_H2I_WOL_MAGIC_REQ),
+	BFI_ENET_I2H_WOL_FRAME_RSP =
+		BFA_I2HM(BFI_ENET_H2I_WOL_FRAME_REQ),
+
+	/* AENs */
+	BFI_ENET_I2H_LINK_DOWN_AEN = BFA_I2HM(BFI_ENET_H2I_MAX),
+	BFI_ENET_I2H_LINK_UP_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 1),
+
+	BFI_ENET_I2H_PORT_ENABLE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 2),
+	BFI_ENET_I2H_PORT_DISABLE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 3),
+
+	BFI_ENET_I2H_BW_UPDATE_AEN = BFA_I2HM(BFI_ENET_H2I_MAX + 4),
+};
+
+/**
+ *  The following error codes can be returned by the enet commands
+ */
+enum bfi_enet_err {
+	BFI_ENET_CMD_OK		= 0,
+	BFI_ENET_CMD_FAIL	= 1,
+	BFI_ENET_CMD_DUP_ENTRY	= 2,	/* !< Duplicate entry in CAM */
+	BFI_ENET_CMD_CAM_FULL	= 3,	/* !< CAM is full */
+	BFI_ENET_CMD_NOT_OWNER	= 4,	/* !< Not permitted, b'cos not owner */
+	BFI_ENET_CMD_NOT_EXEC	= 5,	/* !< Was not sent to f/w at all */
+	BFI_ENET_CMD_WAITING	= 6,	/* !< Waiting for completion */
+	BFI_ENET_CMD_PORT_DISABLED = 7,	/* !< port in disabled state */
+};
+
+/**
+ * Generic Request
+ *
+ * bfi_enet_req is used by:
+ *	BFI_ENET_H2I_RX_CFG_CLR_REQ
+ *	BFI_ENET_H2I_TX_CFG_CLR_REQ
+ */
+struct bfi_enet_req {
+	struct bfi_msgq_mhdr mh;
+};
+
+/**
+ * Enable/Disable Request
+ *
+ * bfi_enet_enable_req is used by:
+ *	BFI_ENET_H2I_RSS_ENABLE_REQ	(enet_id must be zero)
+ *	BFI_ENET_H2I_RX_PROMISCUOUS_REQ (enet_id must be zero)
+ *	BFI_ENET_H2I_RX_DEFAULT_REQ	(enet_id must be zero)
+ *	BFI_ENET_H2I_RX_MAC_MCAST_FILTER_REQ
+ *	BFI_ENET_H2I_PORT_ADMIN_UP_REQ	(enet_id must be zero)
+ */
+struct bfi_enet_enable_req {
+	struct		bfi_msgq_mhdr mh;
+	u8		enable;		/* 1 = enable;  0 = disable */
+	u8		rsvd[3];
+};
+
+/**
+ * Generic Response
+ */
+struct bfi_enet_rsp {
+	struct bfi_msgq_mhdr mh;
+	u8		error;		/*!< if error see cmd_offset */
+	u8		rsvd;
+	u16		cmd_offset;	/*!< offset to invalid parameter */
+};
+
+/**
+ * GLOBAL CONFIGURATION
+ */
+
+/**
+ * bfi_enet_attr_req is used by:
+ *	BFI_ENET_H2I_GET_ATTR_REQ
+ */
+struct bfi_enet_attr_req {
+	struct bfi_msgq_mhdr	mh;
+};
+
+/**
+ * bfi_enet_attr_rsp is used by:
+ *	BFI_ENET_I2H_GET_ATTR_RSP
+ */
+struct bfi_enet_attr_rsp {
+	struct bfi_msgq_mhdr mh;
+	u8		error;		/*!< if error see cmd_offset */
+	u8		rsvd;
+	u16		cmd_offset;	/*!< offset to invalid parameter */
+	u32		max_cfg;
+	u32		max_ucmac;
+	u32		rit_size;
+};
+
+/**
+ * Tx Configuration
+ *
+ * bfi_enet_tx_cfg is used by:
+ *	BFI_ENET_H2I_TX_CFG_SET_REQ
+ */
+enum bfi_enet_tx_vlan_mode {
+	BFI_ENET_TX_VLAN_NOP	= 0,
+	BFI_ENET_TX_VLAN_INS	= 1,
+	BFI_ENET_TX_VLAN_WI	= 2,
+};
+
+struct bfi_enet_tx_cfg {
+	u8		vlan_mode;	/*!< processing mode */
+	u8		rsvd;
+	u16		vlan_id;
+	u8		admit_tagged_frame;
+	u8		apply_vlan_filter;
+	u8		add_to_vswitch;
+	u8		rsvd1[1];
+};
+
+struct bfi_enet_tx_cfg_req {
+	struct bfi_msgq_mhdr mh;
+	u8			num_queues;	/* # of Tx Queues */
+	u8			rsvd[3];
+
+	struct {
+		struct bfi_enet_txq	q;
+		struct bfi_enet_ib	ib;
+	} q_cfg[BFI_ENET_TXQ_PRIO_MAX];
+
+	struct bfi_enet_ib_cfg	ib_cfg;
+
+	struct bfi_enet_tx_cfg	tx_cfg;
+};
+
+struct bfi_enet_tx_cfg_rsp {
+	struct		bfi_msgq_mhdr mh;
+	u8		error;
+	u8		hw_id;		/* For debugging */
+	u8		rsvd[2];
+	struct {
+		u32	q_dbell;	/* PCI base address offset */
+		u32	i_dbell;	/* PCI base address offset */
+		u8	hw_qid;		/* For debugging */
+		u8	rsvd[3];
+	} q_handles[BFI_ENET_TXQ_PRIO_MAX];
+};
+
+/**
+ * Rx Configuration
+ *
+ * bfi_enet_rx_cfg is used by:
+ *	BFI_ENET_H2I_RX_CFG_SET_REQ
+ */
+enum bfi_enet_rxq_type {
+	BFI_ENET_RXQ_SINGLE		= 1,
+	BFI_ENET_RXQ_LARGE_SMALL	= 2,
+	BFI_ENET_RXQ_HDS		= 3,
+	BFI_ENET_RXQ_HDS_OPT_BASED	= 4,
+};
+
+enum bfi_enet_hds_type {
+	BFI_ENET_HDS_FORCED	= 0x01,
+	BFI_ENET_HDS_IPV6_UDP	= 0x02,
+	BFI_ENET_HDS_IPV6_TCP	= 0x04,
+	BFI_ENET_HDS_IPV4_TCP	= 0x08,
+	BFI_ENET_HDS_IPV4_UDP	= 0x10,
+};
+
+struct bfi_enet_rx_cfg {
+	u8		rxq_type;
+	u8		rsvd[3];
+
+	struct {
+		u8			max_header_size;
+		u8			force_offset;
+		u8			type;
+		u8			rsvd1;
+	} hds;
+
+	u8		multi_buffer;
+	u8		strip_vlan;
+	u8		drop_untagged;
+	u8		rsvd2;
+};
+
+/*
+ * Multicast frames are received on the ql of q-set index zero.
+ * On the completion queue.  RxQ ID = even is for large/data buffer queues
+ * and RxQ ID = odd is for small/header buffer queues.
+ */
+struct bfi_enet_rx_cfg_req {
+	struct bfi_msgq_mhdr mh;
+	u8			num_queue_sets;	/* # of Rx Queue Sets */
+	u8			rsvd[3];
+
+	struct {
+		struct bfi_enet_rxq	ql;	/* large/data/single buffers */
+		struct bfi_enet_rxq	qs;	/* small/header buffers */
+		struct bfi_enet_cq	cq;
+		struct bfi_enet_ib	ib;
+	} q_cfg[BFI_ENET_RX_QSET_MAX];
+
+	struct bfi_enet_ib_cfg	ib_cfg;
+
+	struct bfi_enet_rx_cfg	rx_cfg;
+};
+
+struct bfi_enet_rx_cfg_rsp {
+	struct bfi_msgq_mhdr mh;
+	u8		error;
+	u8		hw_id;	 /* For debugging */
+	u8		rsvd[2];
+	struct {
+		u32	ql_dbell; /* PCI base address offset */
+		u32	qs_dbell; /* PCI base address offset */
+		u32	i_dbell;  /* PCI base address offset */
+		u8		hw_lqid;  /* For debugging */
+		u8		hw_sqid;  /* For debugging */
+		u8		hw_cqid;  /* For debugging */
+		u8		rsvd;
+	} q_handles[BFI_ENET_RX_QSET_MAX];
+};
+
+/**
+ * RIT
+ *
+ * bfi_enet_rit_req is used by:
+ *	BFI_ENET_H2I_RIT_CFG_REQ
+ */
+struct bfi_enet_rit_req {
+	struct	bfi_msgq_mhdr mh;
+	u16	size;			/* number of table-entries used */
+	u8	rsvd[2];
+	u8	table[BFI_ENET_RSS_RIT_MAX];
+};
+
+/**
+ * RSS
+ *
+ * bfi_enet_rss_cfg_req is used by:
+ *	BFI_ENET_H2I_RSS_CFG_REQ
+ */
+enum bfi_enet_rss_type {
+	BFI_ENET_RSS_IPV6	= 0x01,
+	BFI_ENET_RSS_IPV6_TCP	= 0x02,
+	BFI_ENET_RSS_IPV4	= 0x04,
+	BFI_ENET_RSS_IPV4_TCP	= 0x08
+};
+
+struct bfi_enet_rss_cfg {
+	u8	type;
+	u8	mask;
+	u8	rsvd[2];
+	u32	key[BFI_ENET_RSS_KEY_LEN];
+};
+
+struct bfi_enet_rss_cfg_req {
+	struct bfi_msgq_mhdr	mh;
+	struct bfi_enet_rss_cfg	cfg;
+};
+
+/**
+ * MAC Unicast
+ *
+ * bfi_enet_rx_vlan_req is used by:
+ *	BFI_ENET_H2I_MAC_UCAST_SET_REQ
+ *	BFI_ENET_H2I_MAC_UCAST_CLR_REQ
+ *	BFI_ENET_H2I_MAC_UCAST_ADD_REQ
+ *	BFI_ENET_H2I_MAC_UCAST_DEL_REQ
+ */
+struct bfi_enet_ucast_req {
+	struct bfi_msgq_mhdr	mh;
+	mac_t			mac_addr;
+	u8			rsvd[2];
+};
+
+/**
+ * MAC Unicast + VLAN
+ */
+struct bfi_enet_mac_n_vlan_req {
+	struct bfi_msgq_mhdr	mh;
+	u16			vlan_id;
+	mac_t			mac_addr;
+};
+
+/**
+ * MAC Multicast
+ *
+ * bfi_enet_mac_mfilter_add_req is used by:
+ *	BFI_ENET_H2I_MAC_MCAST_ADD_REQ
+ */
+struct bfi_enet_mcast_add_req {
+	struct bfi_msgq_mhdr	mh;
+	mac_t			mac_addr;
+	u8			rsvd[2];
+};
+
+/**
+ * bfi_enet_mac_mfilter_add_rsp is used by:
+ *	BFI_ENET_I2H_MAC_MCAST_ADD_RSP
+ */
+struct bfi_enet_mcast_add_rsp {
+	struct bfi_msgq_mhdr	mh;
+	u8			error;
+	u8			rsvd;
+	u16			cmd_offset;
+	u16			handle;
+	u8			rsvd1[2];
+};
+
+/**
+ * bfi_enet_mac_mfilter_del_req is used by:
+ *	BFI_ENET_H2I_MAC_MCAST_DEL_REQ
+ */
+struct bfi_enet_mcast_del_req {
+	struct bfi_msgq_mhdr	mh;
+	u16			handle;
+	u8			rsvd[2];
+};
+
+/**
+ * VLAN
+ *
+ * bfi_enet_rx_vlan_req is used by:
+ *	BFI_ENET_H2I_RX_VLAN_SET_REQ
+ */
+struct bfi_enet_rx_vlan_req {
+	struct bfi_msgq_mhdr	mh;
+	u8			block_idx;
+	u8			rsvd[3];
+	u32			bit_mask[BFI_ENET_VLAN_WORDS_MAX];
+};
+
+/**
+ * PAUSE
+ *
+ * bfi_enet_set_pause_req is used by:
+ *	BFI_ENET_H2I_SET_PAUSE_REQ
+ */
+struct bfi_enet_set_pause_req {
+	struct bfi_msgq_mhdr	mh;
+	u8			rsvd[2];
+	u8			tx_pause;	/* 1 = enable;  0 = disable */
+	u8			rx_pause;	/* 1 = enable;  0 = disable */
+};
+
+/**
+ * DIAGNOSTICS
+ *
+ * bfi_enet_diag_lb_req is used by:
+ *      BFI_ENET_H2I_DIAG_LOOPBACK
+ */
+struct bfi_enet_diag_lb_req {
+	struct bfi_msgq_mhdr	mh;
+	u8			rsvd[2];
+	u8			mode;		/* cable or Serdes */
+	u8			enable;		/* 1 = enable;  0 = disable */
+};
+
+/**
+ * enum for Loopback opmodes
+ */
+enum {
+	BFI_ENET_DIAG_LB_OPMODE_EXT = 0,
+	BFI_ENET_DIAG_LB_OPMODE_CBL = 1,
+};
+
+/**
+ * STATISTICS
+ *
+ * bfi_enet_stats_req is used by:
+ *    BFI_ENET_H2I_STATS_GET_REQ
+ *    BFI_ENET_I2H_STATS_CLR_REQ
+ */
+struct bfi_enet_stats_req {
+	struct bfi_msgq_mhdr	mh;
+	u16			stats_mask;
+	u8			rsvd[2];
+	u32			rx_enet_mask;
+	u32			tx_enet_mask;
+	union bfi_addr_u	host_buffer;
+};
+
+/**
+ * defines for "stats_mask" above.
+ */
+#define BFI_ENET_STATS_MAC    (1 << 0)    /* !< MAC Statistics */
+#define BFI_ENET_STATS_BPC    (1 << 1)    /* !< Pause Stats from BPC */
+#define BFI_ENET_STATS_RAD    (1 << 2)    /* !< Rx Admission Statistics */
+#define BFI_ENET_STATS_RX_FC  (1 << 3)    /* !< Rx FC Stats from RxA */
+#define BFI_ENET_STATS_TX_FC  (1 << 4)    /* !< Tx FC Stats from TxA */
+
+#define BFI_ENET_STATS_ALL    0x1f
+
+/* TxF Frame Statistics */
+struct bfi_enet_stats_txf {
+	u64 ucast_octets;
+	u64 ucast;
+	u64 ucast_vlan;
+
+	u64 mcast_octets;
+	u64 mcast;
+	u64 mcast_vlan;
+
+	u64 bcast_octets;
+	u64 bcast;
+	u64 bcast_vlan;
+
+	u64 errors;
+	u64 filter_vlan;      /* frames filtered due to VLAN */
+	u64 filter_mac_sa;    /* frames filtered due to SA check */
+};
+
+/* RxF Frame Statistics */
+struct bfi_enet_stats_rxf {
+	u64 ucast_octets;
+	u64 ucast;
+	u64 ucast_vlan;
+
+	u64 mcast_octets;
+	u64 mcast;
+	u64 mcast_vlan;
+
+	u64 bcast_octets;
+	u64 bcast;
+	u64 bcast_vlan;
+	u64 frame_drops;
+};
+
+/* Fixme combine fc_tx & fc_rx */
+/* FC Tx Frame Statistics */
+struct bfi_enet_stats_fc_tx {
+	u64 txf_ucast_octets;
+	u64 txf_ucast;
+	u64 txf_ucast_vlan;
+
+	u64 txf_mcast_octets;
+	u64 txf_mcast;
+	u64 txf_mcast_vlan;
+
+	u64 txf_bcast_octets;
+	u64 txf_bcast;
+	u64 txf_bcast_vlan;
+
+	u64 txf_parity_errors;
+	u64 txf_timeout;
+	u64 txf_fid_parity_errors;
+};
+
+/* FC Rx Frame Statistics */
+struct bfi_enet_stats_fc_rx {
+	u64 rxf_ucast_octets;
+	u64 rxf_ucast;
+	u64 rxf_ucast_vlan;
+
+	u64 rxf_mcast_octets;
+	u64 rxf_mcast;
+	u64 rxf_mcast_vlan;
+
+	u64 rxf_bcast_octets;
+	u64 rxf_bcast;
+	u64 rxf_bcast_vlan;
+};
+
+/* RAD Frame Statistics */
+struct bfi_enet_stats_rad {
+	u64 rx_frames;
+	u64 rx_octets;
+	u64 rx_vlan_frames;
+
+	u64 rx_ucast;
+	u64 rx_ucast_octets;
+	u64 rx_ucast_vlan;
+
+	u64 rx_mcast;
+	u64 rx_mcast_octets;
+	u64 rx_mcast_vlan;
+
+	u64 rx_bcast;
+	u64 rx_bcast_octets;
+	u64 rx_bcast_vlan;
+
+	u64 rx_drops;
+};
+
+/* BPC Tx Registers */
+struct bfi_enet_stats_bpc {
+	/* transmit stats */
+	u64 tx_pause[8];
+	u64 tx_zero_pause[8];	/*!< Pause cancellation */
+	/*!<Pause initiation rather than retention */
+	u64 tx_first_pause[8];
+
+	/* receive stats */
+	u64 rx_pause[8];
+	u64 rx_zero_pause[8];	/*!< Pause cancellation */
+	/*!<Pause initiation rather than retention */
+	u64 rx_first_pause[8];
+};
+
+/* MAC Rx Statistics */
+struct bfi_enet_stats_mac {
+	u64 frame_64;		/* both rx and tx counter */
+	u64 frame_65_127;		/* both rx and tx counter */
+	u64 frame_128_255;		/* both rx and tx counter */
+	u64 frame_256_511;		/* both rx and tx counter */
+	u64 frame_512_1023;	/* both rx and tx counter */
+	u64 frame_1024_1518;	/* both rx and tx counter */
+	u64 frame_1519_1522;	/* both rx and tx counter */
+
+	/* receive stats */
+	u64 rx_bytes;
+	u64 rx_packets;
+	u64 rx_fcs_error;
+	u64 rx_multicast;
+	u64 rx_broadcast;
+	u64 rx_control_frames;
+	u64 rx_pause;
+	u64 rx_unknown_opcode;
+	u64 rx_alignment_error;
+	u64 rx_frame_length_error;
+	u64 rx_code_error;
+	u64 rx_carrier_sense_error;
+	u64 rx_undersize;
+	u64 rx_oversize;
+	u64 rx_fragments;
+	u64 rx_jabber;
+	u64 rx_drop;
+
+	/* transmit stats */
+	u64 tx_bytes;
+	u64 tx_packets;
+	u64 tx_multicast;
+	u64 tx_broadcast;
+	u64 tx_pause;
+	u64 tx_deferral;
+	u64 tx_excessive_deferral;
+	u64 tx_single_collision;
+	u64 tx_muliple_collision;
+	u64 tx_late_collision;
+	u64 tx_excessive_collision;
+	u64 tx_total_collision;
+	u64 tx_pause_honored;
+	u64 tx_drop;
+	u64 tx_jabber;
+	u64 tx_fcs_error;
+	u64 tx_control_frame;
+	u64 tx_oversize;
+	u64 tx_undersize;
+	u64 tx_fragments;
+};
+
+/**
+ * Complete statistics, DMAed from fw to host followed by
+ * BFI_ENET_I2H_STATS_GET_RSP
+ */
+struct bfi_enet_stats {
+	struct bfi_enet_stats_mac	mac_stats;
+	struct bfi_enet_stats_bpc	bpc_stats;
+	struct bfi_enet_stats_rad	rad_stats;
+	struct bfi_enet_stats_rad	rlb_stats;
+	struct bfi_enet_stats_fc_rx	fc_rx_stats;
+	struct bfi_enet_stats_fc_tx	fc_tx_stats;
+	struct bfi_enet_stats_rxf	rxf_stats[BFI_ENET_CFG_MAX];
+	struct bfi_enet_stats_txf	txf_stats[BFI_ENET_CFG_MAX];
+};
+
+#pragma pack()
+
+#endif  /* __BFI_ENET_H__ */
diff --git a/drivers/net/bna/bna_enet.c b/drivers/net/bna/bna_enet.c
new file mode 100644
index 0000000..668c72e
--- /dev/null
+++ b/drivers/net/bna/bna_enet.c
@@ -0,0 +1,2199 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2011 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include "bna.h"
+
+static void
+bna_err_handler(struct bna *bna, u32 intr_status)
+{
+	if (BNA_IS_HALT_INTR(bna, intr_status))
+		bna_halt_clear(bna);
+
+	bfa_nw_ioc_error_isr(&bna->ioceth.ioc);
+}
+
+void
+bna_mbox_handler(struct bna *bna, u32 intr_status)
+{
+	if (BNA_IS_ERR_INTR(bna, intr_status)) {
+		bna_err_handler(bna, intr_status);
+		return;
+	}
+	if (BNA_IS_MBOX_INTR(bna, intr_status))
+		bfa_nw_ioc_mbox_isr(&bna->ioceth.ioc);
+}
+
+static void
+bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr)
+{
+	struct bna *bna = (struct bna *)arg;
+	struct bna_tx *tx;
+	struct bna_rx *rx;
+
+	switch (msghdr->msg_id) {
+	case BFI_ENET_I2H_RX_CFG_SET_RSP:
+		bna_rx_from_rid(bna, msghdr->enet_id, rx);
+		if (rx)
+			bna_bfi_rx_enet_start_rsp(rx, msghdr);
+		break;
+
+	case BFI_ENET_I2H_RX_CFG_CLR_RSP:
+		bna_rx_from_rid(bna, msghdr->enet_id, rx);
+		if (rx)
+			bna_bfi_rx_enet_stop_rsp(rx, msghdr);
+		break;
+
+	case BFI_ENET_I2H_RIT_CFG_RSP:
+	case BFI_ENET_I2H_RSS_CFG_RSP:
+	case BFI_ENET_I2H_RSS_ENABLE_RSP:
+	case BFI_ENET_I2H_RX_PROMISCUOUS_RSP:
+	case BFI_ENET_I2H_RX_DEFAULT_RSP:
+	case BFI_ENET_I2H_MAC_UCAST_SET_RSP:
+	case BFI_ENET_I2H_MAC_UCAST_CLR_RSP:
+	case BFI_ENET_I2H_MAC_UCAST_ADD_RSP:
+	case BFI_ENET_I2H_MAC_UCAST_DEL_RSP:
+	case BFI_ENET_I2H_MAC_MCAST_DEL_RSP:
+	case BFI_ENET_I2H_MAC_MCAST_FILTER_RSP:
+	case BFI_ENET_I2H_RX_VLAN_SET_RSP:
+	case BFI_ENET_I2H_RX_VLAN_STRIP_ENABLE_RSP:
+		bna_rx_from_rid(bna, msghdr->enet_id, rx);
+		if (rx)
+			bna_bfi_rxf_cfg_rsp(&rx->rxf, msghdr);
+		break;
+
+	case BFI_ENET_I2H_MAC_MCAST_ADD_RSP:
+		bna_rx_from_rid(bna, msghdr->enet_id, rx);
+		if (rx)
+			bna_bfi_rxf_mcast_add_rsp(&rx->rxf, msghdr);
+		break;
+
+	case BFI_ENET_I2H_TX_CFG_SET_RSP:
+		bna_tx_from_rid(bna, msghdr->enet_id, tx);
+		if (tx)
+			bna_bfi_tx_enet_start_rsp(tx, msghdr);
+		break;
+
+	case BFI_ENET_I2H_TX_CFG_CLR_RSP:
+		bna_tx_from_rid(bna, msghdr->enet_id, tx);
+		if (tx)
+			bna_bfi_tx_enet_stop_rsp(tx, msghdr);
+		break;
+
+	case BFI_ENET_I2H_PORT_ADMIN_RSP:
+		bna_bfi_ethport_admin_rsp(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_DIAG_LOOPBACK_RSP:
+		bna_bfi_ethport_lpbk_rsp(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_SET_PAUSE_RSP:
+		bna_bfi_pause_set_rsp(&bna->enet, msghdr);
+		break;
+
+	case BFI_ENET_I2H_GET_ATTR_RSP:
+		bna_bfi_attr_get_rsp(&bna->ioceth, msghdr);
+		break;
+
+	case BFI_ENET_I2H_STATS_GET_RSP:
+		bna_bfi_stats_get_rsp(bna, msghdr);
+		break;
+
+	case BFI_ENET_I2H_STATS_CLR_RSP:
+		/* No-op */
+		break;
+
+	case BFI_ENET_I2H_LINK_UP_AEN:
+		bna_bfi_ethport_linkup_aen(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_LINK_DOWN_AEN:
+		bna_bfi_ethport_linkdown_aen(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_PORT_ENABLE_AEN:
+		bna_bfi_ethport_enable_aen(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_PORT_DISABLE_AEN:
+		bna_bfi_ethport_disable_aen(&bna->ethport, msghdr);
+		break;
+
+	case BFI_ENET_I2H_BW_UPDATE_AEN:
+		bna_bfi_bw_update_aen(&bna->tx_mod);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/**
+ * ETHPORT
+ */
+#define call_ethport_stop_cbfn(_ethport)				\
+do {									\
+	if ((_ethport)->stop_cbfn) {					\
+		void (*cbfn)(struct bna_enet *);			\
+		cbfn = (_ethport)->stop_cbfn;				\
+		(_ethport)->stop_cbfn = NULL;				\
+		cbfn(&(_ethport)->bna->enet);				\
+	}								\
+} while (0)
+
+#define call_ethport_adminup_cbfn(ethport, status)			\
+do {									\
+	if ((ethport)->adminup_cbfn) {					\
+		void (*cbfn)(struct bnad *, enum bna_cb_status);	\
+		cbfn = (ethport)->adminup_cbfn;				\
+		(ethport)->adminup_cbfn = NULL;				\
+		cbfn((ethport)->bna->bnad, status);			\
+	}								\
+} while (0)
+
+static inline int
+ethport_can_be_up(struct bna_ethport *ethport)
+{
+	int ready = 0;
+	if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+		ready = ((ethport->flags & BNA_ETHPORT_F_ADMIN_UP) &&
+			 (ethport->flags & BNA_ETHPORT_F_RX_STARTED) &&
+			 (ethport->flags & BNA_ETHPORT_F_PORT_ENABLED));
+	else
+		ready = ((ethport->flags & BNA_ETHPORT_F_ADMIN_UP) &&
+			 (ethport->flags & BNA_ETHPORT_F_RX_STARTED) &&
+			 !(ethport->flags & BNA_ETHPORT_F_PORT_ENABLED));
+	return ready;
+}
+
+#define ethport_is_up ethport_can_be_up
+
+static void bna_bfi_ethport_up(struct bna_ethport *ethport);
+static void bna_bfi_ethport_down(struct bna_ethport *ethport);
+
+enum bna_ethport_event {
+	ETHPORT_E_START			= 1,
+	ETHPORT_E_STOP			= 2,
+	ETHPORT_E_FAIL			= 3,
+	ETHPORT_E_UP			= 4,
+	ETHPORT_E_DOWN			= 5,
+	ETHPORT_E_FWRESP_UP_OK		= 6,
+	ETHPORT_E_FWRESP_DOWN		= 7,
+	ETHPORT_E_FWRESP_UP_FAIL	= 8,
+};
+
+bfa_fsm_state_decl(bna_ethport, stopped, struct bna_ethport,
+			enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, down, struct bna_ethport,
+			enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, up_resp_wait, struct bna_ethport,
+			enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, down_resp_wait, struct bna_ethport,
+			enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, up, struct bna_ethport,
+			enum bna_ethport_event);
+bfa_fsm_state_decl(bna_ethport, last_resp_wait, struct bna_ethport,
+			enum bna_ethport_event);
+
+static void
+bna_ethport_sm_stopped_entry(struct bna_ethport *ethport)
+{
+	call_ethport_stop_cbfn(ethport);
+}
+
+static void
+bna_ethport_sm_stopped(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_START:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+		break;
+
+	case ETHPORT_E_STOP:
+		call_ethport_stop_cbfn(ethport);
+		break;
+
+	case ETHPORT_E_FAIL:
+		/* No-op */
+		break;
+
+	case ETHPORT_E_DOWN:
+		/* This event is received due to Rx objects failing */
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ethport_sm_down_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_down(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_STOP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_FAIL:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_UP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_up_resp_wait);
+		bna_bfi_ethport_up(ethport);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ethport_sm_up_resp_wait_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_up_resp_wait(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_STOP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+		break;
+
+	case ETHPORT_E_FAIL:
+		call_ethport_adminup_cbfn(ethport, BNA_CB_FAIL);
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_DOWN:
+		call_ethport_adminup_cbfn(ethport, BNA_CB_INTERRUPT);
+		bfa_fsm_set_state(ethport, bna_ethport_sm_down_resp_wait);
+		break;
+
+	case ETHPORT_E_FWRESP_UP_OK:
+		call_ethport_adminup_cbfn(ethport, BNA_CB_SUCCESS);
+		bfa_fsm_set_state(ethport, bna_ethport_sm_up);
+		break;
+
+	case ETHPORT_E_FWRESP_UP_FAIL:
+		call_ethport_adminup_cbfn(ethport, BNA_CB_FAIL);
+		bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+		break;
+
+	case ETHPORT_E_FWRESP_DOWN:
+		/* down_resp_wait -> up_resp_wait transition on ETHPORT_E_UP */
+		bna_bfi_ethport_up(ethport);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ethport_sm_down_resp_wait_entry(struct bna_ethport *ethport)
+{
+	/**
+	 * NOTE: Do not call bna_bfi_ethport_down() here. That will over step
+	 * mbox due to up_resp_wait -> down_resp_wait transition on event
+	 * ETHPORT_E_DOWN
+	 */
+}
+
+static void
+bna_ethport_sm_down_resp_wait(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_STOP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+		break;
+
+	case ETHPORT_E_FAIL:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_UP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_up_resp_wait);
+		break;
+
+	case ETHPORT_E_FWRESP_UP_OK:
+		/* up_resp_wait->down_resp_wait transition on ETHPORT_E_DOWN */
+		bna_bfi_ethport_down(ethport);
+		break;
+
+	case ETHPORT_E_FWRESP_UP_FAIL:
+	case ETHPORT_E_FWRESP_DOWN:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_down);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ethport_sm_up_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_up(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_STOP:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_last_resp_wait);
+		bna_bfi_ethport_down(ethport);
+		break;
+
+	case ETHPORT_E_FAIL:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_DOWN:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_down_resp_wait);
+		bna_bfi_ethport_down(ethport);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ethport_sm_last_resp_wait_entry(struct bna_ethport *ethport)
+{
+}
+
+static void
+bna_ethport_sm_last_resp_wait(struct bna_ethport *ethport,
+			enum bna_ethport_event event)
+{
+	switch (event) {
+	case ETHPORT_E_FAIL:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	case ETHPORT_E_DOWN:
+		/**
+		 * This event is received due to Rx objects stopping in
+		 * parallel to ethport
+		 */
+		/* No-op */
+		break;
+
+	case ETHPORT_E_FWRESP_UP_OK:
+		/* up_resp_wait->last_resp_wait transition on ETHPORT_T_STOP */
+		bna_bfi_ethport_down(ethport);
+		break;
+
+	case ETHPORT_E_FWRESP_UP_FAIL:
+	case ETHPORT_E_FWRESP_DOWN:
+		bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_bfi_ethport_admin_up(struct bna_ethport *ethport)
+{
+	struct bfi_enet_enable_req *admin_up_req =
+		&ethport->bfi_enet_cmd.admin_req;
+
+	bfi_msgq_mhdr_set(admin_up_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_PORT_ADMIN_UP_REQ, 0, 0);
+	admin_up_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	admin_up_req->enable = BNA_STATUS_T_ENABLED;
+
+	bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &admin_up_req->mh);
+	bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_admin_down(struct bna_ethport *ethport)
+{
+	struct bfi_enet_enable_req *admin_down_req =
+		&ethport->bfi_enet_cmd.admin_req;
+
+	bfi_msgq_mhdr_set(admin_down_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_PORT_ADMIN_UP_REQ, 0, 0);
+	admin_down_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_enable_req)));
+	admin_down_req->enable = BNA_STATUS_T_DISABLED;
+
+	bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_enable_req), &admin_down_req->mh);
+	bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_lpbk_up(struct bna_ethport *ethport)
+{
+	struct bfi_enet_diag_lb_req *lpbk_up_req =
+		&ethport->bfi_enet_cmd.lpbk_req;
+
+	bfi_msgq_mhdr_set(lpbk_up_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_DIAG_LOOPBACK_REQ, 0, 0);
+	lpbk_up_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_diag_lb_req)));
+	lpbk_up_req->mode = (ethport->bna->enet.type ==
+				BNA_ENET_T_LOOPBACK_INTERNAL) ?
+				BFI_ENET_DIAG_LB_OPMODE_EXT :
+				BFI_ENET_DIAG_LB_OPMODE_CBL;
+	lpbk_up_req->enable = BNA_STATUS_T_ENABLED;
+
+	bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_diag_lb_req), &lpbk_up_req->mh);
+	bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_lpbk_down(struct bna_ethport *ethport)
+{
+	struct bfi_enet_diag_lb_req *lpbk_down_req =
+		&ethport->bfi_enet_cmd.lpbk_req;
+
+	bfi_msgq_mhdr_set(lpbk_down_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_DIAG_LOOPBACK_REQ, 0, 0);
+	lpbk_down_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_diag_lb_req)));
+	lpbk_down_req->enable = BNA_STATUS_T_DISABLED;
+
+	bfa_msgq_cmd_set(&ethport->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_diag_lb_req), &lpbk_down_req->mh);
+	bfa_msgq_cmd_post(&ethport->bna->msgq, &ethport->msgq_cmd);
+}
+
+static void
+bna_bfi_ethport_up(struct bna_ethport *ethport)
+{
+	if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+		bna_bfi_ethport_admin_up(ethport);
+	else
+		bna_bfi_ethport_lpbk_up(ethport);
+}
+
+static void
+bna_bfi_ethport_down(struct bna_ethport *ethport)
+{
+	if (ethport->bna->enet.type == BNA_ENET_T_REGULAR)
+		bna_bfi_ethport_admin_down(ethport);
+	else
+		bna_bfi_ethport_lpbk_down(ethport);
+}
+
+void
+bna_ethport_init(struct bna_ethport *ethport, struct bna *bna)
+{
+	ethport->flags |= (BNA_ETHPORT_F_ADMIN_UP | BNA_ETHPORT_F_PORT_ENABLED);
+	ethport->bna = bna;
+
+	ethport->link_status = BNA_LINK_DOWN;
+	ethport->link_cbfn = bnad_cb_ethport_link_status;
+
+	ethport->rx_started_count = 0;
+
+	ethport->stop_cbfn = NULL;
+	ethport->adminup_cbfn = NULL;
+
+	bfa_fsm_set_state(ethport, bna_ethport_sm_stopped);
+}
+
+void
+bna_ethport_uninit(struct bna_ethport *ethport)
+{
+	ethport->flags &= ~BNA_ETHPORT_F_ADMIN_UP;
+	ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+
+	ethport->bna = NULL;
+}
+
+void
+bna_ethport_start(struct bna_ethport *ethport)
+{
+	bfa_fsm_send_event(ethport, ETHPORT_E_START);
+}
+
+void
+bna_ethport_stop(struct bna_ethport *ethport)
+{
+	ethport->stop_cbfn = bna_enet_cb_ethport_stopped;
+	bfa_fsm_send_event(ethport, ETHPORT_E_STOP);
+}
+
+void
+bna_ethport_fail(struct bna_ethport *ethport)
+{
+	/* Reset the physical port status to enabled */
+	ethport->flags |= BNA_ETHPORT_F_PORT_ENABLED;
+
+	if (ethport->link_status != BNA_LINK_DOWN) {
+		ethport->link_status = BNA_LINK_DOWN;
+		ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+	}
+	bfa_fsm_send_event(ethport, ETHPORT_E_FAIL);
+}
+
+void
+bna_ethport_admin_up(struct bna_ethport *ethport,
+		void (*cbfn)(struct bnad *, enum bna_cb_status))
+{
+	ethport->adminup_cbfn = cbfn;
+
+	if (ethport->flags & BNA_ETHPORT_F_ADMIN_UP) {
+		call_ethport_adminup_cbfn(ethport, BNA_CB_SUCCESS);
+		return;
+	}
+
+	if ((ethport->bna->enet.type != BNA_ENET_T_REGULAR) &&
+		(ethport->flags & BNA_ETHPORT_F_PORT_ENABLED)) {
+		call_ethport_adminup_cbfn(ethport, BNA_CB_FAIL);
+		return;
+	}
+
+	ethport->flags |= BNA_ETHPORT_F_ADMIN_UP;
+
+	if (ethport_can_be_up(ethport))
+		bfa_fsm_send_event(ethport, ETHPORT_E_UP);
+}
+
+void
+bna_ethport_admin_down(struct bna_ethport *ethport)
+{
+	int ethport_up = ethport_is_up(ethport);
+
+	if (!(ethport->flags & BNA_ETHPORT_F_ADMIN_UP))
+		return;
+
+	ethport->flags &= ~BNA_ETHPORT_F_ADMIN_UP;
+
+	if (ethport_up)
+		bfa_fsm_send_event(ethport, ETHPORT_E_DOWN);
+}
+
+/* Should be called only when ethport is disabled */
+void
+bna_ethport_linkcbfn_set(struct bna_ethport *ethport,
+		      void (*linkcbfn)(struct bnad *, enum bna_link_status))
+{
+	ethport->link_cbfn = linkcbfn;
+}
+
+void
+bna_bfi_ethport_enable_aen(struct bna_ethport *ethport,
+				struct bfi_msgq_mhdr *msghdr)
+{
+	ethport->flags |= BNA_ETHPORT_F_PORT_ENABLED;
+
+	if (ethport_can_be_up(ethport))
+		bfa_fsm_send_event(ethport, ETHPORT_E_UP);
+}
+
+void
+bna_bfi_ethport_disable_aen(struct bna_ethport *ethport,
+				struct bfi_msgq_mhdr *msghdr)
+{
+	int ethport_up = ethport_is_up(ethport);
+
+	ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+
+	if (ethport_up)
+		bfa_fsm_send_event(ethport, ETHPORT_E_DOWN);
+}
+
+void
+bna_ethport_cb_rx_started(struct bna_ethport *ethport)
+{
+	ethport->rx_started_count++;
+
+	if (ethport->rx_started_count == 1) {
+		ethport->flags |= BNA_ETHPORT_F_RX_STARTED;
+
+		if (ethport_can_be_up(ethport))
+			bfa_fsm_send_event(ethport, ETHPORT_E_UP);
+	}
+}
+
+void
+bna_ethport_cb_rx_stopped(struct bna_ethport *ethport)
+{
+	int ethport_up = ethport_is_up(ethport);
+
+	ethport->rx_started_count--;
+
+	if (ethport->rx_started_count == 0) {
+		ethport->flags &= ~BNA_ETHPORT_F_RX_STARTED;
+
+		if (ethport_up)
+			bfa_fsm_send_event(ethport, ETHPORT_E_DOWN);
+	}
+}
+
+void
+bna_bfi_ethport_admin_rsp(struct bna_ethport *ethport,
+				struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_enable_req *admin_req =
+		&ethport->bfi_enet_cmd.admin_req;
+	struct bfi_enet_rsp *rsp = (struct bfi_enet_rsp *)msghdr;
+
+	switch (admin_req->enable) {
+	case BNA_STATUS_T_ENABLED:
+		if (rsp->error == BFI_ENET_CMD_OK)
+			bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_OK);
+		else {
+			ethport->flags &= ~BNA_ETHPORT_F_PORT_ENABLED;
+			bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_FAIL);
+		}
+		break;
+
+	case BNA_STATUS_T_DISABLED:
+		bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_DOWN);
+		ethport->link_status = BNA_LINK_DOWN;
+		ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+		break;
+	}
+}
+
+void
+bna_bfi_ethport_lpbk_rsp(struct bna_ethport *ethport,
+				struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_diag_lb_req *diag_lb_req =
+		&ethport->bfi_enet_cmd.lpbk_req;
+	struct bfi_enet_rsp *rsp = (struct bfi_enet_rsp *)msghdr;
+
+	switch (diag_lb_req->enable) {
+	case BNA_STATUS_T_ENABLED:
+		if (rsp->error == BFI_ENET_CMD_OK)
+			bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_OK);
+		else {
+			ethport->flags &= ~BNA_ETHPORT_F_ADMIN_UP;
+			bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_UP_FAIL);
+		}
+		break;
+
+	case BNA_STATUS_T_DISABLED:
+		bfa_fsm_send_event(ethport, ETHPORT_E_FWRESP_DOWN);
+		break;
+	}
+}
+
+void
+bna_bfi_ethport_linkup_aen(struct bna_ethport *ethport,
+			struct bfi_msgq_mhdr *msghdr)
+{
+	ethport->link_status = BNA_LINK_UP;
+
+	/* Dispatch events */
+	ethport->link_cbfn(ethport->bna->bnad, ethport->link_status);
+}
+
+void
+bna_bfi_ethport_linkdown_aen(struct bna_ethport *ethport,
+				struct bfi_msgq_mhdr *msghdr)
+{
+	ethport->link_status = BNA_LINK_DOWN;
+
+	/* Dispatch events */
+	ethport->link_cbfn(ethport->bna->bnad, BNA_LINK_DOWN);
+}
+
+int
+bna_ethport_is_disabled(struct bna_ethport *ethport)
+{
+	int ret = (!(ethport->flags & BNA_ETHPORT_F_PORT_ENABLED));
+	return ret;
+}
+
+/**
+ * ENET
+ */
+#define bna_enet_chld_start(enet)					\
+do {									\
+	enum bna_tx_type tx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;			\
+	enum bna_rx_type rx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;			\
+	bna_ethport_start(&(enet)->bna->ethport);			\
+	bna_tx_mod_start(&(enet)->bna->tx_mod, tx_type);		\
+	bna_rx_mod_start(&(enet)->bna->rx_mod, rx_type);		\
+} while (0)
+
+#define bna_enet_chld_stop(enet)					\
+do {									\
+	enum bna_tx_type tx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;			\
+	enum bna_rx_type rx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;			\
+	bfa_wc_init(&(enet)->chld_stop_wc, bna_enet_cb_chld_stopped, (enet));\
+	bfa_wc_up(&(enet)->chld_stop_wc);				\
+	bna_ethport_stop(&(enet)->bna->ethport);			\
+	bfa_wc_up(&(enet)->chld_stop_wc);				\
+	bna_tx_mod_stop(&(enet)->bna->tx_mod, tx_type);			\
+	bfa_wc_up(&(enet)->chld_stop_wc);				\
+	bna_rx_mod_stop(&(enet)->bna->rx_mod, rx_type);			\
+	bfa_wc_wait(&(enet)->chld_stop_wc);				\
+} while (0)
+
+#define bna_enet_chld_fail(enet)					\
+do {									\
+	bna_ethport_fail(&(enet)->bna->ethport);			\
+	bna_tx_mod_fail(&(enet)->bna->tx_mod);				\
+	bna_rx_mod_fail(&(enet)->bna->rx_mod);				\
+} while (0)
+
+#define bna_enet_rx_start(enet)						\
+do {									\
+	enum bna_rx_type rx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;			\
+	bna_rx_mod_start(&(enet)->bna->rx_mod, rx_type);		\
+} while (0)
+
+#define bna_enet_rx_stop(enet)						\
+do {									\
+	enum bna_rx_type rx_type =					\
+		((enet)->type == BNA_ENET_T_REGULAR) ?			\
+		BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;			\
+	bfa_wc_init(&(enet)->chld_stop_wc, bna_enet_cb_chld_stopped, (enet));\
+	bfa_wc_up(&(enet)->chld_stop_wc);				\
+	bna_rx_mod_stop(&(enet)->bna->rx_mod, rx_type);			\
+	bfa_wc_wait(&(enet)->chld_stop_wc);				\
+} while (0)
+
+#define call_enet_stop_cbfn(enet)					\
+do {									\
+	if ((enet)->stop_cbfn) {					\
+		void (*cbfn)(void *);					\
+		void *cbarg;						\
+		cbfn = (enet)->stop_cbfn;				\
+		cbarg = (enet)->stop_cbarg;				\
+		(enet)->stop_cbfn = NULL;				\
+		(enet)->stop_cbarg = NULL;				\
+		cbfn(cbarg);						\
+	}								\
+} while (0)
+
+#define call_enet_pause_cbfn(enet)					\
+do {									\
+	if ((enet)->pause_cbfn) {					\
+		void (*cbfn)(struct bnad *);				\
+		cbfn = (enet)->pause_cbfn;				\
+		(enet)->pause_cbfn = NULL;				\
+		cbfn((enet)->bna->bnad);				\
+	}								\
+} while (0)
+
+#define call_enet_mtu_cbfn(enet)					\
+do {									\
+	if ((enet)->mtu_cbfn) {						\
+		void (*cbfn)(struct bnad *);				\
+		cbfn = (enet)->mtu_cbfn;				\
+		(enet)->mtu_cbfn = NULL;				\
+		cbfn((enet)->bna->bnad);				\
+	}								\
+} while (0)
+
+static void bna_enet_cb_chld_stopped(void *arg);
+static void bna_bfi_pause_set(struct bna_enet *enet);
+
+enum bna_enet_event {
+	ENET_E_START			= 1,
+	ENET_E_STOP			= 2,
+	ENET_E_FAIL			= 3,
+	ENET_E_PAUSE_CFG		= 4,
+	ENET_E_MTU_CFG			= 5,
+	ENET_E_FWRESP_PAUSE		= 6,
+	ENET_E_CHLD_STOPPED		= 7,
+};
+
+bfa_fsm_state_decl(bna_enet, stopped, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, pause_init_wait, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, last_resp_wait, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, started, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, cfg_wait, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, cfg_stop_wait, struct bna_enet,
+			enum bna_enet_event);
+bfa_fsm_state_decl(bna_enet, chld_stop_wait, struct bna_enet,
+			enum bna_enet_event);
+
+static void
+bna_enet_sm_stopped_entry(struct bna_enet *enet)
+{
+	call_enet_pause_cbfn(enet);
+	call_enet_mtu_cbfn(enet);
+	call_enet_stop_cbfn(enet);
+}
+
+static void
+bna_enet_sm_stopped(struct bna_enet *enet, enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_START:
+		bfa_fsm_set_state(enet, bna_enet_sm_pause_init_wait);
+		break;
+
+	case ENET_E_STOP:
+		call_enet_stop_cbfn(enet);
+		break;
+
+	case ENET_E_FAIL:
+		/* No-op */
+		break;
+
+	case ENET_E_PAUSE_CFG:
+		call_enet_pause_cbfn(enet);
+		break;
+
+	case ENET_E_MTU_CFG:
+		call_enet_mtu_cbfn(enet);
+		break;
+
+	case ENET_E_CHLD_STOPPED:
+		/**
+		 * This event is received due to Ethport, Tx and Rx objects
+		 * failing
+		 */
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_pause_init_wait_entry(struct bna_enet *enet)
+{
+	bna_bfi_pause_set(enet);
+}
+
+static void
+bna_enet_sm_pause_init_wait(struct bna_enet *enet,
+				enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_STOP:
+		enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+		bfa_fsm_set_state(enet, bna_enet_sm_last_resp_wait);
+		break;
+
+	case ENET_E_FAIL:
+		enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		break;
+
+	case ENET_E_PAUSE_CFG:
+		enet->flags |= BNA_ENET_F_PAUSE_CHANGED;
+		break;
+
+	case ENET_E_MTU_CFG:
+		/* No-op */
+		break;
+
+	case ENET_E_FWRESP_PAUSE:
+		if (enet->flags & BNA_ENET_F_PAUSE_CHANGED) {
+			enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+			bna_bfi_pause_set(enet);
+		} else {
+			bfa_fsm_set_state(enet, bna_enet_sm_started);
+			bna_enet_chld_start(enet);
+		}
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_last_resp_wait_entry(struct bna_enet *enet)
+{
+	enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+}
+
+static void
+bna_enet_sm_last_resp_wait(struct bna_enet *enet,
+				enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_FAIL:
+	case ENET_E_FWRESP_PAUSE:
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_started_entry(struct bna_enet *enet)
+{
+	/**
+	 * NOTE: Do not call bna_enet_chld_start() here, since it will be
+	 * inadvertently called during cfg_wait->started transition as well
+	 */
+	call_enet_pause_cbfn(enet);
+	call_enet_mtu_cbfn(enet);
+}
+
+static void
+bna_enet_sm_started(struct bna_enet *enet,
+			enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_STOP:
+		bfa_fsm_set_state(enet, bna_enet_sm_chld_stop_wait);
+		break;
+
+	case ENET_E_FAIL:
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		bna_enet_chld_fail(enet);
+		break;
+
+	case ENET_E_PAUSE_CFG:
+		bfa_fsm_set_state(enet, bna_enet_sm_cfg_wait);
+		bna_bfi_pause_set(enet);
+		break;
+
+	case ENET_E_MTU_CFG:
+		bfa_fsm_set_state(enet, bna_enet_sm_cfg_wait);
+		bna_enet_rx_stop(enet);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_cfg_wait_entry(struct bna_enet *enet)
+{
+}
+
+static void
+bna_enet_sm_cfg_wait(struct bna_enet *enet,
+			enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_STOP:
+		enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+		enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+		bfa_fsm_set_state(enet, bna_enet_sm_cfg_stop_wait);
+		break;
+
+	case ENET_E_FAIL:
+		enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+		enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		bna_enet_chld_fail(enet);
+		break;
+
+	case ENET_E_PAUSE_CFG:
+		enet->flags |= BNA_ENET_F_PAUSE_CHANGED;
+		break;
+
+	case ENET_E_MTU_CFG:
+		enet->flags |= BNA_ENET_F_MTU_CHANGED;
+		break;
+
+	case ENET_E_CHLD_STOPPED:
+		bna_enet_rx_start(enet);
+		/* Fall through */
+	case ENET_E_FWRESP_PAUSE:
+		if (enet->flags & BNA_ENET_F_PAUSE_CHANGED) {
+			enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+			bna_bfi_pause_set(enet);
+		} else if (enet->flags & BNA_ENET_F_MTU_CHANGED) {
+			enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+			bna_enet_rx_stop(enet);
+		} else {
+			bfa_fsm_set_state(enet, bna_enet_sm_started);
+		}
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_cfg_stop_wait_entry(struct bna_enet *enet)
+{
+	enet->flags &= ~BNA_ENET_F_PAUSE_CHANGED;
+	enet->flags &= ~BNA_ENET_F_MTU_CHANGED;
+}
+
+static void
+bna_enet_sm_cfg_stop_wait(struct bna_enet *enet,
+				enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_FAIL:
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		bna_enet_chld_fail(enet);
+		break;
+
+	case ENET_E_FWRESP_PAUSE:
+	case ENET_E_CHLD_STOPPED:
+		bfa_fsm_set_state(enet, bna_enet_sm_chld_stop_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_enet_sm_chld_stop_wait_entry(struct bna_enet *enet)
+{
+	bna_enet_chld_stop(enet);
+}
+
+static void
+bna_enet_sm_chld_stop_wait(struct bna_enet *enet,
+				enum bna_enet_event event)
+{
+	switch (event) {
+	case ENET_E_FAIL:
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		bna_enet_chld_fail(enet);
+		break;
+
+	case ENET_E_CHLD_STOPPED:
+		bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_bfi_pause_set(struct bna_enet *enet)
+{
+	struct bfi_enet_set_pause_req *pause_req = &enet->pause_req;
+
+	bfi_msgq_mhdr_set(pause_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_SET_PAUSE_REQ, 0, 0);
+	pause_req->mh.num_entries = htons(
+	bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_set_pause_req)));
+	pause_req->tx_pause = enet->pause_config.tx_pause;
+	pause_req->rx_pause = enet->pause_config.rx_pause;
+
+	bfa_msgq_cmd_set(&enet->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_set_pause_req), &pause_req->mh);
+	bfa_msgq_cmd_post(&enet->bna->msgq, &enet->msgq_cmd);
+}
+
+static void
+bna_enet_cb_chld_stopped(void *arg)
+{
+	struct bna_enet *enet = (struct bna_enet *)arg;
+
+	bfa_fsm_send_event(enet, ENET_E_CHLD_STOPPED);
+}
+
+void
+bna_enet_init(struct bna_enet *enet, struct bna *bna)
+{
+	enet->bna = bna;
+	enet->flags = 0;
+	enet->mtu = 0;
+	enet->type = BNA_ENET_T_REGULAR;
+
+	enet->stop_cbfn = NULL;
+	enet->stop_cbarg = NULL;
+
+	enet->pause_cbfn = NULL;
+
+	enet->mtu_cbfn = NULL;
+
+	bfa_fsm_set_state(enet, bna_enet_sm_stopped);
+}
+
+void
+bna_enet_uninit(struct bna_enet *enet)
+{
+	enet->flags = 0;
+
+	enet->bna = NULL;
+}
+
+void
+bna_enet_start(struct bna_enet *enet)
+{
+	enet->flags |= BNA_ENET_F_IOCETH_READY;
+	if (enet->flags & BNA_ENET_F_ENABLED)
+		bfa_fsm_send_event(enet, ENET_E_START);
+}
+
+void
+bna_enet_stop(struct bna_enet *enet)
+{
+	enet->stop_cbfn = bna_ioceth_cb_enet_stopped;
+	enet->stop_cbarg = &enet->bna->ioceth;
+
+	enet->flags &= ~BNA_ENET_F_IOCETH_READY;
+	bfa_fsm_send_event(enet, ENET_E_STOP);
+}
+
+void
+bna_enet_fail(struct bna_enet *enet)
+{
+	enet->flags &= ~BNA_ENET_F_IOCETH_READY;
+	bfa_fsm_send_event(enet, ENET_E_FAIL);
+}
+
+void
+bna_enet_cb_ethport_stopped(struct bna_enet *enet)
+{
+	bfa_wc_down(&enet->chld_stop_wc);
+}
+
+void
+bna_enet_cb_tx_stopped(struct bna_enet *enet)
+{
+	bfa_wc_down(&enet->chld_stop_wc);
+}
+
+void
+bna_enet_cb_rx_stopped(struct bna_enet *enet)
+{
+	bfa_wc_down(&enet->chld_stop_wc);
+}
+
+void
+bna_bfi_pause_set_rsp(struct bna_enet *enet, struct bfi_msgq_mhdr *msghdr)
+{
+	bfa_fsm_send_event(enet, ENET_E_FWRESP_PAUSE);
+}
+
+int
+bna_enet_mtu_get(struct bna_enet *enet)
+{
+	return enet->mtu;
+}
+
+void
+bna_enet_enable(struct bna_enet *enet)
+{
+	if (enet->fsm != (bfa_sm_t)bna_enet_sm_stopped)
+		return;
+
+	enet->flags |= BNA_ENET_F_ENABLED;
+
+	if (enet->flags & BNA_ENET_F_IOCETH_READY)
+		bfa_fsm_send_event(enet, ENET_E_START);
+}
+
+void
+bna_enet_disable(struct bna_enet *enet, enum bna_cleanup_type type,
+		 void (*cbfn)(void *))
+{
+	if (type == BNA_SOFT_CLEANUP) {
+		(*cbfn)(enet->bna->bnad);
+		return;
+	}
+
+	enet->stop_cbfn = cbfn;
+	enet->stop_cbarg = enet->bna->bnad;
+
+	enet->flags &= ~BNA_ENET_F_ENABLED;
+
+	bfa_fsm_send_event(enet, ENET_E_STOP);
+}
+
+void
+bna_enet_pause_config(struct bna_enet *enet,
+		      struct bna_pause_config *pause_config,
+		      void (*cbfn)(struct bnad *))
+{
+	enet->pause_config = *pause_config;
+
+	enet->pause_cbfn = cbfn;
+
+	bfa_fsm_send_event(enet, ENET_E_PAUSE_CFG);
+}
+
+void
+bna_enet_mtu_set(struct bna_enet *enet, int mtu,
+		 void (*cbfn)(struct bnad *))
+{
+	enet->mtu = mtu;
+
+	enet->mtu_cbfn = cbfn;
+
+	bfa_fsm_send_event(enet, ENET_E_MTU_CFG);
+}
+
+void
+bna_enet_perm_mac_get(struct bna_enet *enet, mac_t *mac)
+{
+	*mac = bfa_nw_ioc_get_mac(&enet->bna->ioceth.ioc);
+}
+
+/* Should be called only when enet is disabled */
+void
+bna_enet_type_set(struct bna_enet *enet, enum bna_enet_type type)
+{
+	enet->type = type;
+}
+
+enum bna_enet_type
+bna_enet_type_get(struct bna_enet *enet)
+{
+	return enet->type;
+}
+
+/**
+ * IOCETH
+ */
+#define enable_mbox_intr(_ioceth)					\
+do {									\
+	u32 intr_status;						\
+	bna_intr_status_get((_ioceth)->bna, intr_status);		\
+	bnad_cb_mbox_intr_enable((_ioceth)->bna->bnad);			\
+	bna_mbox_intr_enable((_ioceth)->bna);				\
+} while (0)
+
+#define disable_mbox_intr(_ioceth)					\
+do {									\
+	bna_mbox_intr_disable((_ioceth)->bna);				\
+	bnad_cb_mbox_intr_disable((_ioceth)->bna->bnad);		\
+} while (0)
+
+#define call_ioceth_stop_cbfn(_ioceth)					\
+do {									\
+	if ((_ioceth)->stop_cbfn) {					\
+		void (*cbfn)(struct bnad *);				\
+		struct bnad *cbarg;					\
+		cbfn = (_ioceth)->stop_cbfn;				\
+		cbarg = (_ioceth)->stop_cbarg;				\
+		(_ioceth)->stop_cbfn = NULL;				\
+		(_ioceth)->stop_cbarg = NULL;				\
+		cbfn(cbarg);						\
+	}								\
+} while (0)
+
+#define bna_stats_mod_uninit(_stats_mod)				\
+do {									\
+} while (0)
+
+#define bna_stats_mod_start(_stats_mod)					\
+do {									\
+	(_stats_mod)->ioc_ready = true;					\
+} while (0)
+
+#define bna_stats_mod_stop(_stats_mod)					\
+do {									\
+	(_stats_mod)->ioc_ready = false;				\
+} while (0)
+
+#define bna_stats_mod_fail(_stats_mod)					\
+do {									\
+	(_stats_mod)->ioc_ready = false;				\
+	(_stats_mod)->stats_get_busy = false;				\
+	(_stats_mod)->stats_clr_busy = false;				\
+} while (0)
+
+static void bna_bfi_attr_get(struct bna_ioceth *ioceth);
+
+enum bna_ioceth_event {
+	IOCETH_E_ENABLE			= 1,
+	IOCETH_E_DISABLE		= 2,
+	IOCETH_E_IOC_RESET		= 3,
+	IOCETH_E_IOC_FAILED		= 4,
+	IOCETH_E_IOC_READY		= 5,
+	IOCETH_E_ENET_ATTR_RESP		= 6,
+	IOCETH_E_ENET_STOPPED		= 7,
+	IOCETH_E_IOC_DISABLED		= 8,
+};
+
+bfa_fsm_state_decl(bna_ioceth, stopped, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ioc_ready_wait, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, enet_attr_wait, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ready, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, last_resp_wait, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, enet_stop_wait, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, ioc_disable_wait, struct bna_ioceth,
+			enum bna_ioceth_event);
+bfa_fsm_state_decl(bna_ioceth, failed, struct bna_ioceth,
+			enum bna_ioceth_event);
+
+static void
+bna_ioceth_sm_stopped_entry(struct bna_ioceth *ioceth)
+{
+	call_ioceth_stop_cbfn(ioceth);
+}
+
+static void
+bna_ioceth_sm_stopped(struct bna_ioceth *ioceth,
+			enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_ENABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_ready_wait);
+		bfa_nw_ioc_enable(&ioceth->ioc);
+		break;
+
+	case IOCETH_E_DISABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+		break;
+
+	case IOCETH_E_IOC_RESET:
+		enable_mbox_intr(ioceth);
+		break;
+
+	case IOCETH_E_IOC_FAILED:
+		disable_mbox_intr(ioceth);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_ioc_ready_wait_entry(struct bna_ioceth *ioceth)
+{
+	/**
+	 * Do not call bfa_nw_ioc_enable() here. It must be called in the
+	 * previous state due to failed -> ioc_ready_wait transition.
+	 */
+}
+
+static void
+bna_ioceth_sm_ioc_ready_wait(struct bna_ioceth *ioceth,
+				enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_DISABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	case IOCETH_E_IOC_RESET:
+		enable_mbox_intr(ioceth);
+		break;
+
+	case IOCETH_E_IOC_FAILED:
+		disable_mbox_intr(ioceth);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+		break;
+
+	case IOCETH_E_IOC_READY:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_enet_attr_wait);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_enet_attr_wait_entry(struct bna_ioceth *ioceth)
+{
+	bna_bfi_attr_get(ioceth);
+}
+
+static void
+bna_ioceth_sm_enet_attr_wait(struct bna_ioceth *ioceth,
+				enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_DISABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_last_resp_wait);
+		break;
+
+	case IOCETH_E_IOC_FAILED:
+		disable_mbox_intr(ioceth);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+		break;
+
+	case IOCETH_E_ENET_ATTR_RESP:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ready);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_ready_entry(struct bna_ioceth *ioceth)
+{
+	bna_enet_start(&ioceth->bna->enet);
+	bna_stats_mod_start(&ioceth->bna->stats_mod);
+	bnad_cb_ioceth_ready(ioceth->bna->bnad);
+}
+
+static void
+bna_ioceth_sm_ready(struct bna_ioceth *ioceth, enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_DISABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_enet_stop_wait);
+		break;
+
+	case IOCETH_E_IOC_FAILED:
+		disable_mbox_intr(ioceth);
+		bna_enet_fail(&ioceth->bna->enet);
+		bna_stats_mod_fail(&ioceth->bna->stats_mod);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_failed);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_last_resp_wait_entry(struct bna_ioceth *ioceth)
+{
+}
+
+static void
+bna_ioceth_sm_last_resp_wait(struct bna_ioceth *ioceth,
+				enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_IOC_FAILED:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		disable_mbox_intr(ioceth);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	case IOCETH_E_ENET_ATTR_RESP:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_enet_stop_wait_entry(struct bna_ioceth *ioceth)
+{
+	bna_stats_mod_stop(&ioceth->bna->stats_mod);
+	bna_enet_stop(&ioceth->bna->enet);
+}
+
+static void
+bna_ioceth_sm_enet_stop_wait(struct bna_ioceth *ioceth,
+				enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_IOC_FAILED:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		disable_mbox_intr(ioceth);
+		bna_enet_fail(&ioceth->bna->enet);
+		bna_stats_mod_fail(&ioceth->bna->stats_mod);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	case IOCETH_E_ENET_STOPPED:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_ioc_disable_wait_entry(struct bna_ioceth *ioceth)
+{
+}
+
+static void
+bna_ioceth_sm_ioc_disable_wait(struct bna_ioceth *ioceth,
+				enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_IOC_DISABLED:
+		disable_mbox_intr(ioceth);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+		break;
+
+	case IOCETH_E_ENET_STOPPED:
+		/* This event is received due to enet failing */
+		/* No-op */
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_ioceth_sm_failed_entry(struct bna_ioceth *ioceth)
+{
+	bnad_cb_ioceth_failed(ioceth->bna->bnad);
+}
+
+static void
+bna_ioceth_sm_failed(struct bna_ioceth *ioceth,
+			enum bna_ioceth_event event)
+{
+	switch (event) {
+	case IOCETH_E_DISABLE:
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_disable_wait);
+		bfa_nw_ioc_disable(&ioceth->ioc);
+		break;
+
+	case IOCETH_E_IOC_RESET:
+		enable_mbox_intr(ioceth);
+		bfa_fsm_set_state(ioceth, bna_ioceth_sm_ioc_ready_wait);
+		break;
+
+	case IOCETH_E_IOC_FAILED:
+		break;
+
+	default:
+		bfa_sm_fault(event);
+	}
+}
+
+static void
+bna_bfi_attr_get(struct bna_ioceth *ioceth)
+{
+	struct bfi_enet_attr_req *attr_req = &ioceth->attr_req;
+
+	bfi_msgq_mhdr_set(attr_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_GET_ATTR_REQ, 0, 0);
+	attr_req->mh.num_entries = htons(
+	bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_attr_req)));
+	bfa_msgq_cmd_set(&ioceth->msgq_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_attr_req), &attr_req->mh);
+	bfa_msgq_cmd_post(&ioceth->bna->msgq, &ioceth->msgq_cmd);
+}
+
+/* IOC callback functions */
+
+static void
+bna_cb_ioceth_enable(void *arg, enum bfa_status error)
+{
+	struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+	if (error)
+		bfa_fsm_send_event(ioceth, IOCETH_E_IOC_FAILED);
+	else
+		bfa_fsm_send_event(ioceth, IOCETH_E_IOC_READY);
+}
+
+static void
+bna_cb_ioceth_disable(void *arg)
+{
+	struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_IOC_DISABLED);
+}
+
+static void
+bna_cb_ioceth_hbfail(void *arg)
+{
+	struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_IOC_FAILED);
+}
+
+static void
+bna_cb_ioceth_reset(void *arg)
+{
+	struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_IOC_RESET);
+}
+
+static struct bfa_ioc_cbfn bna_ioceth_cbfn = {
+	bna_cb_ioceth_enable,
+	bna_cb_ioceth_disable,
+	bna_cb_ioceth_hbfail,
+	bna_cb_ioceth_reset
+};
+
+void
+bna_ioceth_init(struct bna_ioceth *ioceth, struct bna *bna,
+		struct bna_res_info *res_info)
+{
+	u64 dma;
+	u8 *kva;
+
+	ioceth->bna = bna;
+
+	/**
+	 * Attach IOC and claim:
+	 *	1. DMA memory for IOC attributes
+	 *	2. Kernel memory for FW trace
+	 */
+	bfa_nw_ioc_attach(&ioceth->ioc, ioceth, &bna_ioceth_cbfn);
+	bfa_nw_ioc_pci_init(&ioceth->ioc, &bna->pcidev, BFI_PCIFN_CLASS_ETH);
+
+	BNA_GET_DMA_ADDR(
+		&res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].dma, dma);
+	kva = res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mdl[0].kva;
+	bfa_nw_ioc_mem_claim(&ioceth->ioc, kva, dma);
+
+	kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva;
+
+	/**
+	 * Attach common modules (Diag, SFP, CEE, Port) and claim respective
+	 * DMA memory.
+	 */
+	BNA_GET_DMA_ADDR(
+		&res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].dma, dma);
+	kva = res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mdl[0].kva;
+	bfa_nw_cee_attach(&bna->cee, &ioceth->ioc, bna);
+	bfa_nw_cee_mem_claim(&bna->cee, kva, dma);
+	kva += bfa_nw_cee_meminfo();
+	dma += bfa_nw_cee_meminfo();
+
+	bfa_msgq_attach(&bna->msgq, &ioceth->ioc);
+	bfa_msgq_memclaim(&bna->msgq, kva, dma);
+	bfa_msgq_regisr(&bna->msgq, BFI_MC_ENET, bna_msgq_rsp_handler, bna);
+	kva += bfa_msgq_meminfo();
+	dma += bfa_msgq_meminfo();
+
+	ioceth->stop_cbfn = NULL;
+	ioceth->stop_cbarg = NULL;
+
+	bfa_fsm_set_state(ioceth, bna_ioceth_sm_stopped);
+}
+
+void
+bna_ioceth_uninit(struct bna_ioceth *ioceth)
+{
+	bfa_nw_ioc_detach(&ioceth->ioc);
+
+	ioceth->bna = NULL;
+}
+
+void
+bna_bfi_attr_get_rsp(struct bna_ioceth *ioceth,
+			struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_attr_rsp *rsp = (struct bfi_enet_attr_rsp *)msghdr;
+
+	/**
+	 * Store only if not set earlier, since BNAD can override the HW
+	 * attributes
+	 */
+	if (!ioceth->attr.num_txq)
+		ioceth->attr.num_txq = ntohl(rsp->max_cfg);
+	if (!ioceth->attr.num_rxp)
+		ioceth->attr.num_rxp = ntohl(rsp->max_cfg);
+	ioceth->attr.num_ucmac = ntohl(rsp->max_ucmac);
+	ioceth->attr.num_mcmac = BFI_ENET_MAX_MCAM;
+	ioceth->attr.max_rit_size = ntohl(rsp->rit_size);
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_ENET_ATTR_RESP);
+}
+
+void
+bna_ioceth_cb_enet_stopped(void *arg)
+{
+	struct bna_ioceth *ioceth = (struct bna_ioceth *)arg;
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_ENET_STOPPED);
+}
+
+void
+bna_ioceth_enable(struct bna_ioceth *ioceth)
+{
+	if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_ready) {
+		bnad_cb_ioceth_ready(ioceth->bna->bnad);
+		return;
+	}
+
+	if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_stopped)
+		bfa_fsm_send_event(ioceth, IOCETH_E_ENABLE);
+}
+
+void
+bna_ioceth_disable(struct bna_ioceth *ioceth, enum bna_cleanup_type type)
+{
+	if (type == BNA_SOFT_CLEANUP) {
+		bnad_cb_ioceth_disabled(ioceth->bna->bnad);
+		return;
+	}
+
+	ioceth->stop_cbfn = bnad_cb_ioceth_disabled;
+	ioceth->stop_cbarg = ioceth->bna->bnad;
+
+	bfa_fsm_send_event(ioceth, IOCETH_E_DISABLE);
+}
+
+bool
+bna_ioceth_state_is_failed(struct bna_ioceth *ioceth)
+{
+	return (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_failed) ?
+		true : false;
+}
+
+static void
+bna_ucam_mod_init(struct bna_ucam_mod *ucam_mod, struct bna *bna,
+		  struct bna_res_info *res_info)
+{
+	int i;
+
+	ucam_mod->ucmac = (struct bna_mac *)
+	res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
+
+	INIT_LIST_HEAD(&ucam_mod->free_q);
+	for (i = 0; i < bna->ioceth.attr.num_ucmac; i++) {
+		bfa_q_qe_init(&ucam_mod->ucmac[i].qe);
+		list_add_tail(&ucam_mod->ucmac[i].qe, &ucam_mod->free_q);
+	}
+
+	ucam_mod->bna = bna;
+}
+
+static void
+bna_ucam_mod_uninit(struct bna_ucam_mod *ucam_mod)
+{
+	struct list_head *qe;
+	int i = 0;
+
+	list_for_each(qe, &ucam_mod->free_q)
+		i++;
+
+	ucam_mod->bna = NULL;
+}
+
+static void
+bna_mcam_mod_init(struct bna_mcam_mod *mcam_mod, struct bna *bna,
+		  struct bna_res_info *res_info)
+{
+	int i;
+
+	mcam_mod->mcmac = (struct bna_mac *)
+	res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mdl[0].kva;
+
+	INIT_LIST_HEAD(&mcam_mod->free_q);
+	for (i = 0; i < bna->ioceth.attr.num_mcmac; i++) {
+		bfa_q_qe_init(&mcam_mod->mcmac[i].qe);
+		list_add_tail(&mcam_mod->mcmac[i].qe, &mcam_mod->free_q);
+	}
+
+	mcam_mod->mchandle = (struct bna_mcam_handle *)
+	res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.mdl[0].kva;
+
+	INIT_LIST_HEAD(&mcam_mod->free_handle_q);
+	for (i = 0; i < bna->ioceth.attr.num_mcmac; i++) {
+		bfa_q_qe_init(&mcam_mod->mchandle[i].qe);
+		list_add_tail(&mcam_mod->mchandle[i].qe,
+				&mcam_mod->free_handle_q);
+	}
+
+	mcam_mod->bna = bna;
+}
+
+static void
+bna_mcam_mod_uninit(struct bna_mcam_mod *mcam_mod)
+{
+	struct list_head *qe;
+	int i;
+
+	i = 0;
+	list_for_each(qe, &mcam_mod->free_q) i++;
+
+	i = 0;
+	list_for_each(qe, &mcam_mod->free_handle_q) i++;
+
+	mcam_mod->bna = NULL;
+}
+
+static void
+bna_bfi_stats_get(struct bna *bna)
+{
+	struct bfi_enet_stats_req *stats_req = &bna->stats_mod.stats_get;
+
+	bna->stats_mod.stats_get_busy = true;
+
+	bfi_msgq_mhdr_set(stats_req->mh, BFI_MC_ENET,
+		BFI_ENET_H2I_STATS_GET_REQ, 0, 0);
+	stats_req->mh.num_entries = htons(
+		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_stats_req)));
+	stats_req->stats_mask = htons(BFI_ENET_STATS_ALL);
+	stats_req->tx_enet_mask = htonl(bna->tx_mod.rid_mask);
+	stats_req->rx_enet_mask = htonl(bna->rx_mod.rid_mask);
+	stats_req->host_buffer.a32.addr_hi = bna->stats.hw_stats_dma.msb;
+	stats_req->host_buffer.a32.addr_lo = bna->stats.hw_stats_dma.lsb;
+
+	bfa_msgq_cmd_set(&bna->stats_mod.stats_get_cmd, NULL, NULL,
+		sizeof(struct bfi_enet_stats_req), &stats_req->mh);
+	bfa_msgq_cmd_post(&bna->msgq, &bna->stats_mod.stats_get_cmd);
+}
+
+#define bna_stats_copy(_name, _type)					\
+do {									\
+	count = sizeof(struct bfi_enet_stats_ ## _type) / sizeof(u64);	\
+	stats_src = (u64 *)&bna->stats.hw_stats_kva->_name ## _stats;	\
+	stats_dst = (u64 *)&bna->stats.hw_stats._name ## _stats;	\
+	for (i = 0; i < count; i++)					\
+		stats_dst[i] = be64_to_cpu(stats_src[i]);		\
+} while (0)								\
+
+void
+bna_bfi_stats_get_rsp(struct bna *bna, struct bfi_msgq_mhdr *msghdr)
+{
+	struct bfi_enet_stats_req *stats_req = &bna->stats_mod.stats_get;
+	u64 *stats_src;
+	u64 *stats_dst;
+	u32 tx_enet_mask = ntohl(stats_req->tx_enet_mask);
+	u32 rx_enet_mask = ntohl(stats_req->rx_enet_mask);
+	int count;
+	int i;
+
+	bna_stats_copy(mac, mac);
+	bna_stats_copy(bpc, bpc);
+	bna_stats_copy(rad, rad);
+	bna_stats_copy(rlb, rad);
+	bna_stats_copy(fc_rx, fc_rx);
+	bna_stats_copy(fc_tx, fc_tx);
+
+	stats_src = (u64 *)&(bna->stats.hw_stats_kva->rxf_stats[0]);
+
+	/* Copy Rxf stats to SW area, scatter them while copying */
+	for (i = 0; i < BFI_ENET_CFG_MAX; i++) {
+		stats_dst = (u64 *)&(bna->stats.hw_stats.rxf_stats[i]);
+		memset(stats_dst, 0, sizeof(struct bfi_enet_stats_rxf));
+		if (rx_enet_mask & ((u32)(1 << i))) {
+			int k;
+			count = sizeof(struct bfi_enet_stats_rxf) /
+				sizeof(u64);
+			for (k = 0; k < count; k++) {
+				stats_dst[k] = be64_to_cpu(*stats_src);
+				stats_src++;
+			}
+		}
+	}
+
+	/* Copy Txf stats to SW area, scatter them while copying */
+	for (i = 0; i < BFI_ENET_CFG_MAX; i++) {
+		stats_dst = (u64 *)&(bna->stats.hw_stats.txf_stats[i]);
+		memset(stats_dst, 0, sizeof(struct bfi_enet_stats_txf));
+		if (tx_enet_mask & ((u32)(1 << i))) {
+			int k;
+			count = sizeof(struct bfi_enet_stats_txf) /
+				sizeof(u64);
+			for (k = 0; k < count; k++) {
+				stats_dst[k] = be64_to_cpu(*stats_src);
+				stats_src++;
+			}
+		}
+	}
+
+	bna->stats_mod.stats_get_busy = false;
+	bnad_cb_stats_get(bna->bnad, BNA_CB_SUCCESS, &bna->stats);
+}
+
+void
+bna_res_req(struct bna_res_info *res_info)
+{
+	/* DMA memory for COMMON_MODULE */
+	res_info[BNA_RES_MEM_T_COM].res_type = BNA_RES_T_MEM;
+	res_info[BNA_RES_MEM_T_COM].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+	res_info[BNA_RES_MEM_T_COM].res_u.mem_info.num = 1;
+	res_info[BNA_RES_MEM_T_COM].res_u.mem_info.len = ALIGN(
+				(bfa_nw_cee_meminfo() +
+				bfa_msgq_meminfo()), PAGE_SIZE);
+
+	/* DMA memory for retrieving IOC attributes */
+	res_info[BNA_RES_MEM_T_ATTR].res_type = BNA_RES_T_MEM;
+	res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+	res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.num = 1;
+	res_info[BNA_RES_MEM_T_ATTR].res_u.mem_info.len =
+				ALIGN(bfa_nw_ioc_meminfo(), PAGE_SIZE);
+
+	/* Virtual memory for retreiving fw_trc */
+	res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM;
+	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA;
+	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 0;
+	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = 0;
+
+	/* DMA memory for retreiving stats */
+	res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM;
+	res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mem_type = BNA_MEM_T_DMA;
+	res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.num = 1;
+	res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.len =
+				ALIGN(sizeof(struct bfi_enet_stats),
+					PAGE_SIZE);
+}
+
+void
+bna_mod_res_req(struct bna *bna, struct bna_res_info *res_info)
+{
+	struct bna_attr *attr = &bna->ioceth.attr;
+
+	/* Virtual memory for Tx objects - stored by Tx module */
+	res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_TX_ARRAY].res_u.mem_info.len =
+		attr->num_txq * sizeof(struct bna_tx);
+
+	/* Virtual memory for TxQ - stored by Tx module */
+	res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_TXQ_ARRAY].res_u.mem_info.len =
+		attr->num_txq * sizeof(struct bna_txq);
+
+	/* Virtual memory for Rx objects - stored by Rx module */
+	res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_RX_ARRAY].res_u.mem_info.len =
+		attr->num_rxp * sizeof(struct bna_rx);
+
+	/* Virtual memory for RxPath - stored by Rx module */
+	res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_RXP_ARRAY].res_u.mem_info.len =
+		attr->num_rxp * sizeof(struct bna_rxp);
+
+	/* Virtual memory for RxQ - stored by Rx module */
+	res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_RXQ_ARRAY].res_u.mem_info.len =
+		(attr->num_rxp * 2) * sizeof(struct bna_rxq);
+
+	/* Virtual memory for Unicast MAC address - stored by ucam module */
+	res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_UCMAC_ARRAY].res_u.mem_info.len =
+		attr->num_ucmac * sizeof(struct bna_mac);
+
+	/* Virtual memory for Multicast MAC address - stored by mcam module */
+	res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_MCMAC_ARRAY].res_u.mem_info.len =
+		attr->num_mcmac * sizeof(struct bna_mac);
+
+	/* Virtual memory for Multicast handle - stored by mcam module */
+	res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_type = BNA_RES_T_MEM;
+	res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.mem_type =
+		BNA_MEM_T_KVA;
+	res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.num = 1;
+	res_info[BNA_MOD_RES_MEM_T_MCHANDLE_ARRAY].res_u.mem_info.len =
+		attr->num_mcmac * sizeof(struct bna_mcam_handle);
+}
+
+void
+bna_init(struct bna *bna, struct bnad *bnad,
+		struct bfa_pcidev *pcidev, struct bna_res_info *res_info)
+{
+	bna->bnad = bnad;
+	bna->pcidev = *pcidev;
+
+	bna->stats.hw_stats_kva = (struct bfi_enet_stats *)
+		res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].kva;
+	bna->stats.hw_stats_dma.msb =
+		res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.msb;
+	bna->stats.hw_stats_dma.lsb =
+		res_info[BNA_RES_MEM_T_STATS].res_u.mem_info.mdl[0].dma.lsb;
+
+	bna_reg_addr_init(bna, &bna->pcidev);
+
+	/* Also initializes diag, cee, sfp, phy_port, msgq */
+	bna_ioceth_init(&bna->ioceth, bna, res_info);
+
+	bna_enet_init(&bna->enet, bna);
+	bna_ethport_init(&bna->ethport, bna);
+}
+
+void
+bna_mod_init(struct bna *bna, struct bna_res_info *res_info)
+{
+	bna_tx_mod_init(&bna->tx_mod, bna, res_info);
+
+	bna_rx_mod_init(&bna->rx_mod, bna, res_info);
+
+	bna_ucam_mod_init(&bna->ucam_mod, bna, res_info);
+
+	bna_mcam_mod_init(&bna->mcam_mod, bna, res_info);
+
+	bna->default_mode_rid = BFI_INVALID_RID;
+	bna->promisc_rid = BFI_INVALID_RID;
+
+	bna->mod_flags |= BNA_MOD_F_INIT_DONE;
+}
+
+void
+bna_uninit(struct bna *bna)
+{
+	if (bna->mod_flags & BNA_MOD_F_INIT_DONE) {
+		bna_mcam_mod_uninit(&bna->mcam_mod);
+		bna_ucam_mod_uninit(&bna->ucam_mod);
+		bna_rx_mod_uninit(&bna->rx_mod);
+		bna_tx_mod_uninit(&bna->tx_mod);
+		bna->mod_flags &= ~BNA_MOD_F_INIT_DONE;
+	}
+
+	bna_stats_mod_uninit(&bna->stats_mod);
+	bna_ethport_uninit(&bna->ethport);
+	bna_enet_uninit(&bna->enet);
+
+	bna_ioceth_uninit(&bna->ioceth);
+
+	bna->bnad = NULL;
+}
+
+int
+bna_num_txq_set(struct bna *bna, int num_txq)
+{
+	if (num_txq > 0 && (num_txq <= bna->ioceth.attr.num_txq)) {
+		bna->ioceth.attr.num_txq = num_txq;
+		return BNA_CB_SUCCESS;
+	}
+
+	return BNA_CB_FAIL;
+}
+
+int
+bna_num_rxp_set(struct bna *bna, int num_rxp)
+{
+	if (num_rxp > 0 && (num_rxp <= bna->ioceth.attr.num_rxp)) {
+		bna->ioceth.attr.num_rxp = num_rxp;
+		return BNA_CB_SUCCESS;
+	}
+
+	return BNA_CB_FAIL;
+}
+
+struct bna_mac *
+bna_ucam_mod_mac_get(struct bna_ucam_mod *ucam_mod)
+{
+	struct list_head *qe;
+
+	if (list_empty(&ucam_mod->free_q))
+		return NULL;
+
+	bfa_q_deq(&ucam_mod->free_q, &qe);
+
+	return (struct bna_mac *)qe;
+}
+
+void
+bna_ucam_mod_mac_put(struct bna_ucam_mod *ucam_mod, struct bna_mac *mac)
+{
+	list_add_tail(&mac->qe, &ucam_mod->free_q);
+}
+
+struct bna_mac *
+bna_mcam_mod_mac_get(struct bna_mcam_mod *mcam_mod)
+{
+	struct list_head *qe;
+
+	if (list_empty(&mcam_mod->free_q))
+		return NULL;
+
+	bfa_q_deq(&mcam_mod->free_q, &qe);
+
+	return (struct bna_mac *)qe;
+}
+
+void
+bna_mcam_mod_mac_put(struct bna_mcam_mod *mcam_mod, struct bna_mac *mac)
+{
+	list_add_tail(&mac->qe, &mcam_mod->free_q);
+}
+
+struct bna_mcam_handle *
+bna_mcam_mod_handle_get(struct bna_mcam_mod *mcam_mod)
+{
+	struct list_head *qe;
+
+	if (list_empty(&mcam_mod->free_handle_q))
+		return NULL;
+
+	bfa_q_deq(&mcam_mod->free_handle_q, &qe);
+
+	return (struct bna_mcam_handle *)qe;
+}
+
+void
+bna_mcam_mod_handle_put(struct bna_mcam_mod *mcam_mod,
+			struct bna_mcam_handle *handle)
+{
+	list_add_tail(&handle->qe, &mcam_mod->free_handle_q);
+}
+
+void
+bna_hw_stats_get(struct bna *bna)
+{
+	if (!bna->stats_mod.ioc_ready) {
+		bnad_cb_stats_get(bna->bnad, BNA_CB_FAIL, &bna->stats);
+		return;
+	}
+	if (bna->stats_mod.stats_get_busy) {
+		bnad_cb_stats_get(bna->bnad, BNA_CB_BUSY, &bna->stats);
+		return;
+	}
+
+	bna_bfi_stats_get(bna);
+}
-- 
1.7.1


^ permalink raw reply related

* [PATCH 6/8] bna: HW Interface Init Update
From: Rasesh Mody @ 2011-07-27  2:10 UTC (permalink / raw)
  To: davem, netdev; +Cc: adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1311732648-29876-1-git-send-email-rmody@brocade.com>

Change details:
 - Split the hw interface into common and asic specific to support new asic
   in the future.
 - Fix bfa_ioc_ct_isr_mode_set() to also include the case that we are already
   in the desired msix mode.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/bna/bfa_ioc_ct.c |   28 +++++++++++++++++-----------
 1 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/drivers/net/bna/bfa_ioc_ct.c b/drivers/net/bna/bfa_ioc_ct.c
index 4de1ad8..209f1f3 100644
--- a/drivers/net/bna/bfa_ioc_ct.c
+++ b/drivers/net/bna/bfa_ioc_ct.c
@@ -50,26 +50,32 @@ static enum bfa_status bfa_ioc_ct_pll_init(void __iomem *rb, bool fcmode);
 
 static struct bfa_ioc_hwif nw_hwif_ct;
 
+static void
+bfa_ioc_set_ctx_hwif(struct bfa_ioc *ioc, struct bfa_ioc_hwif *hwif)
+{
+	hwif->ioc_firmware_lock = bfa_ioc_ct_firmware_lock;
+	hwif->ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock;
+	hwif->ioc_notify_fail = bfa_ioc_ct_notify_fail;
+	hwif->ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
+	hwif->ioc_sync_start = bfa_ioc_ct_sync_start;
+	hwif->ioc_sync_join = bfa_ioc_ct_sync_join;
+	hwif->ioc_sync_leave = bfa_ioc_ct_sync_leave;
+	hwif->ioc_sync_ack = bfa_ioc_ct_sync_ack;
+	hwif->ioc_sync_complete = bfa_ioc_ct_sync_complete;
+}
+
 /**
  * Called from bfa_ioc_attach() to map asic specific calls.
  */
 void
 bfa_nw_ioc_set_ct_hwif(struct bfa_ioc *ioc)
 {
+	bfa_ioc_set_ctx_hwif(ioc, &nw_hwif_ct);
+
 	nw_hwif_ct.ioc_pll_init = bfa_ioc_ct_pll_init;
-	nw_hwif_ct.ioc_firmware_lock = bfa_ioc_ct_firmware_lock;
-	nw_hwif_ct.ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock;
 	nw_hwif_ct.ioc_reg_init = bfa_ioc_ct_reg_init;
 	nw_hwif_ct.ioc_map_port = bfa_ioc_ct_map_port;
 	nw_hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set;
-	nw_hwif_ct.ioc_notify_fail = bfa_ioc_ct_notify_fail;
-	nw_hwif_ct.ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
-	nw_hwif_ct.ioc_sync_start = bfa_ioc_ct_sync_start;
-	nw_hwif_ct.ioc_sync_join = bfa_ioc_ct_sync_join;
-	nw_hwif_ct.ioc_sync_leave = bfa_ioc_ct_sync_leave;
-	nw_hwif_ct.ioc_sync_ack = bfa_ioc_ct_sync_ack;
-	nw_hwif_ct.ioc_sync_complete = bfa_ioc_ct_sync_complete;
-
 	ioc->ioc_hwif = &nw_hwif_ct;
 }
 
@@ -297,7 +303,7 @@ bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix)
 	/**
 	 * If already in desired mode, do not change anything
 	 */
-	if (!msix && mode)
+	if ((!msix && mode) || (msix && !mode))
 		return;
 
 	if (msix)
-- 
1.7.1


^ 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