Print this page
NEX-1890 update oce from source provided by Emulex
        
*** 17,52 ****
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
  
! /* Copyright © 2003-2011 Emulex. All rights reserved.  */
  
  /*
   * Source file containing the Receive Path handling
   * functions
   */
  #include <oce_impl.h>
  
  
  void oce_rx_pool_free(char *arg);
  static void oce_rqb_dtor(oce_rq_bdesc_t *rqbd);
- static int oce_rqb_ctor(oce_rq_bdesc_t *rqbd, struct oce_rq *rq,
-     size_t size, int flags);
  
  static inline mblk_t *oce_rx(struct oce_dev *dev, struct oce_rq *rq,
      struct oce_nic_rx_cqe *cqe);
  static inline mblk_t *oce_rx_bcopy(struct oce_dev *dev,
          struct oce_rq *rq, struct oce_nic_rx_cqe *cqe);
  static int oce_rq_charge(struct oce_rq *rq, uint32_t nbufs, boolean_t repost);
! static void oce_rx_insert_tag(mblk_t *mp, uint16_t vtag);
  static void oce_set_rx_oflags(mblk_t *mp, struct oce_nic_rx_cqe *cqe);
  static inline void oce_rx_drop_pkt(struct oce_rq *rq,
      struct oce_nic_rx_cqe *cqe);
  static oce_rq_bdesc_t *oce_rqb_alloc(struct oce_rq *rq);
  static void oce_rqb_free(struct oce_rq *rq, oce_rq_bdesc_t *rqbd);
  static void oce_rq_post_buffer(struct oce_rq *rq, int nbufs);
  
  #pragma inline(oce_rx)
  #pragma inline(oce_rx_bcopy)
  #pragma inline(oce_rq_charge)
  #pragma inline(oce_rx_insert_tag)
--- 17,58 ----
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
  
! /*
!  * Copyright (c) 2009-2012 Emulex. All rights reserved.
!  * Use is subject to license terms.
!  */
  
+ 
+ 
  /*
   * Source file containing the Receive Path handling
   * functions
   */
  #include <oce_impl.h>
  
  
  void oce_rx_pool_free(char *arg);
  static void oce_rqb_dtor(oce_rq_bdesc_t *rqbd);
  
  static inline mblk_t *oce_rx(struct oce_dev *dev, struct oce_rq *rq,
      struct oce_nic_rx_cqe *cqe);
  static inline mblk_t *oce_rx_bcopy(struct oce_dev *dev,
          struct oce_rq *rq, struct oce_nic_rx_cqe *cqe);
  static int oce_rq_charge(struct oce_rq *rq, uint32_t nbufs, boolean_t repost);
! static inline void oce_rx_insert_tag(struct oce_dev *dev, mblk_t *mp,
!     uint16_t vtag);
  static void oce_set_rx_oflags(mblk_t *mp, struct oce_nic_rx_cqe *cqe);
  static inline void oce_rx_drop_pkt(struct oce_rq *rq,
      struct oce_nic_rx_cqe *cqe);
  static oce_rq_bdesc_t *oce_rqb_alloc(struct oce_rq *rq);
  static void oce_rqb_free(struct oce_rq *rq, oce_rq_bdesc_t *rqbd);
  static void oce_rq_post_buffer(struct oce_rq *rq, int nbufs);
+ static boolean_t oce_check_tagged(struct oce_dev *dev,
+     struct oce_nic_rx_cqe *cqe);
  
  #pragma inline(oce_rx)
  #pragma inline(oce_rx_bcopy)
  #pragma inline(oce_rq_charge)
  #pragma inline(oce_rx_insert_tag)
*** 66,76 ****
          0x00000001,             /* minimum transfer size */
          0x00000000FFFFFFFFull,  /* maximum transfer size */
          0xFFFFFFFFFFFFFFFFull,  /* maximum segment size */
          1,                      /* scatter/gather list length */
          0x00000001,             /* granularity */
!         DDI_DMA_FLAGERR|DDI_DMA_RELAXED_ORDERING                /* DMA flags */
  };
  
  /*
   * function to create a DMA buffer pool for RQ
   *
--- 72,82 ----
          0x00000001,             /* minimum transfer size */
          0x00000000FFFFFFFFull,  /* maximum transfer size */
          0xFFFFFFFFFFFFFFFFull,  /* maximum segment size */
          1,                      /* scatter/gather list length */
          0x00000001,             /* granularity */
!         DDI_DMA_RELAXED_ORDERING                /* DMA flags */
  };
  
  /*
   * function to create a DMA buffer pool for RQ
   *
*** 81,112 ****
   * return DDI_SUCCESS => success, DDI_FAILURE otherwise
   */
  int
  oce_rqb_cache_create(struct oce_rq *rq, size_t buf_size)
  {
-         int size;
-         int cnt;
-         int ret;
          oce_rq_bdesc_t *rqbd;
  
-         _NOTE(ARGUNUSED(buf_size));
          rqbd = rq->rq_bdesc_array;
!         size = rq->cfg.frag_size + OCE_RQE_BUF_HEADROOM;
!         for (cnt = 0; cnt < rq->cfg.nbufs; cnt++, rqbd++) {
!                 rq->rqb_freelist[cnt] = rqbd;
!                 ret = oce_rqb_ctor(rqbd, rq,
!                     size, (DDI_DMA_RDWR|DDI_DMA_STREAMING));
                  if (ret != DDI_SUCCESS) {
!                         goto rqb_fail;
                  }
          }
          rq->rqb_free = rq->cfg.nbufs;
          rq->rqb_rc_head = 0;
          rq->rqb_next_free = 0;
          return (DDI_SUCCESS);
  
! rqb_fail:
          oce_rqb_cache_destroy(rq);
          return (DDI_FAILURE);
  } /* oce_rqb_cache_create */
  
  /*
--- 87,181 ----
   * return DDI_SUCCESS => success, DDI_FAILURE otherwise
   */
  int
  oce_rqb_cache_create(struct oce_rq *rq, size_t buf_size)
  {
          oce_rq_bdesc_t *rqbd;
+         struct oce_dev *dev;
+         uint32_t size;
+         uint64_t paddr;
+         caddr_t vaddr;
+         int ncookies = 0;
+         int bufs_per_cookie = 0;
+         int ridx = 0;
+         int i = 0;
+         ddi_dma_cookie_t cookie;
+         int ret;
  
          rqbd = rq->rq_bdesc_array;
!         size = buf_size * rq->cfg.nbufs;
!         dev = rq->parent;
! 
!         oce_rx_buf_attr.dma_attr_granular = (uint32_t)buf_size;
!         if (DDI_FM_DMA_ERR_CAP(dev->fm_caps)) {
!                 oce_rx_buf_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
!         }
! 
!         /* Try to get single big chunk With iommu normally cookie count is 1 */
!         oce_rx_buf_attr.dma_attr_sgllen = 1;
!         ret = oce_alloc_dma_buffer(dev, &rq->rqb, size, &oce_rx_buf_attr,
!             (DDI_DMA_RDWR|DDI_DMA_STREAMING));
!         /* retry with single page allocation */
          if (ret != DDI_SUCCESS) {
!                 oce_rx_buf_attr.dma_attr_sgllen =
!                     size/ddi_ptob(dev->dip, (ulong_t)1) + 2;
!                 ret = oce_alloc_dma_buffer(dev, &rq->rqb, size,
!                     &oce_rx_buf_attr, (DDI_DMA_RDWR | DDI_DMA_STREAMING));
!                 if (ret != DDI_SUCCESS) {
!                         return (DDI_FAILURE);
                  }
          }
+ 
+         ncookies = rq->rqb.ncookies;
+         /* Set the starting phys and vaddr */
+         /* paddr = rq->rqb.addr; */
+         vaddr = rq->rqb.base;
+         cookie = rq->rqb.cookie;
+ 
+         do {
+                 paddr = cookie.dmac_laddress;
+                 bufs_per_cookie = cookie.dmac_size/buf_size;
+                 for (i = 0; i < bufs_per_cookie; i++, rqbd++) {
+                         rqbd->mp = desballoc((uchar_t *)vaddr, buf_size, 0,
+                             &rqbd->fr_rtn);
+                         if (rqbd->mp == NULL) {
+                                 goto desb_fail;
+                         }
+                         /* Set the call back function parameters */
+                         rqbd->fr_rtn.free_func = (void (*)())oce_rx_pool_free;
+                         rqbd->fr_rtn.free_arg = (caddr_t)(void *)rqbd;
+                         /* Populate the DMA object for each buffer */
+                         rqbd->rqb.acc_handle = rq->rqb.acc_handle;
+                         rqbd->rqb.dma_handle = rq->rqb.dma_handle;
+                         rqbd->rqb.base = vaddr;
+                         rqbd->rqb.addr = paddr;
+                         rqbd->rqb.len  = buf_size;
+                         rqbd->rqb.size = buf_size;
+                         rqbd->rqb.off  = ridx * buf_size;
+                         rqbd->rq = rq;
+                         rqbd->frag_addr.dw.addr_lo = ADDR_LO(paddr);
+                         rqbd->frag_addr.dw.addr_hi = ADDR_HI(paddr);
+                         rq->rqb_freelist[ridx] = rqbd;
+                         /* increment the addresses */
+                         paddr += buf_size;
+                         vaddr += buf_size;
+                         ridx++;
+                         if (ridx >= rq->cfg.nbufs) {
+                                 break;
+                         }
+                 }
+                 if (--ncookies > 0) {
+                         (void) ddi_dma_nextcookie(rq->rqb.dma_handle, &cookie);
+                 }
+         } while (ncookies > 0);
+ 
          rq->rqb_free = rq->cfg.nbufs;
          rq->rqb_rc_head = 0;
          rq->rqb_next_free = 0;
          return (DDI_SUCCESS);
  
! desb_fail:
          oce_rqb_cache_destroy(rq);
          return (DDI_FAILURE);
  } /* oce_rqb_cache_create */
  
  /*
*** 124,133 ****
--- 193,204 ----
  
          rqbd = rq->rq_bdesc_array;
          for (cnt = 0; cnt < rq->cfg.nbufs; cnt++, rqbd++) {
                  oce_rqb_dtor(rqbd);
          }
+ 
+         oce_free_dma_buffer(rq->parent, &rq->rqb);
  } /* oce_rqb_cache_destroy */
  
  /*
   * RQ buffer destructor function
   *
*** 144,197 ****
          if (rqbd->mp != NULL) {
                  rqbd->fr_rtn.free_arg = NULL;
                  freemsg(rqbd->mp);
                  rqbd->mp = NULL;
          }
-         oce_free_dma_buffer(rqbd->rq->parent, rqbd->rqb);
  } /* oce_rqb_dtor */
  
- /*
-  * RQ buffer constructor function
-  *
-  * rqbd - pointer to rq buffer descriptor
-  * rq - pointer to RQ structure
-  * size - size of the buffer
-  * flags - KM_SLEEP OR KM_NOSLEEP
-  *
-  * return DDI_SUCCESS => success, DDI_FAILURE otherwise
-  */
- static int
- oce_rqb_ctor(oce_rq_bdesc_t *rqbd, struct oce_rq *rq, size_t size, int flags)
- {
-         struct oce_dev *dev;
-         oce_dma_buf_t *dbuf;
  
-         dev = rq->parent;
- 
-         dbuf  = oce_alloc_dma_buffer(dev, size, &oce_rx_buf_attr, flags);
-         if (dbuf == NULL) {
-                 return (DDI_FAILURE);
-         }
- 
-         /* Set the call back function parameters */
-         rqbd->fr_rtn.free_func = (void (*)())oce_rx_pool_free;
-         rqbd->fr_rtn.free_arg = (caddr_t)(void *)rqbd;
-         rqbd->mp = desballoc((uchar_t *)(dbuf->base),
-             dbuf->size, 0, &rqbd->fr_rtn);
-         if (rqbd->mp == NULL) {
-                 oce_free_dma_buffer(dev, dbuf);
-                 return (DDI_FAILURE);
-         }
-         rqbd->rqb = dbuf;
-         rqbd->rq = rq;
-         rqbd->frag_addr.dw.addr_lo = ADDR_LO(dbuf->addr + OCE_RQE_BUF_HEADROOM);
-         rqbd->frag_addr.dw.addr_hi = ADDR_HI(dbuf->addr + OCE_RQE_BUF_HEADROOM);
-         rqbd->mp->b_rptr = (uchar_t *)rqbd->rqb->base + OCE_RQE_BUF_HEADROOM;
- 
-         return (DDI_SUCCESS);
- } /* oce_rqb_ctor */
- 
  /*
   * RQ buffer allocator function
   *
   * rq - pointer to RQ structure
   *
--- 215,227 ----
*** 340,349 ****
--- 370,380 ----
          int frag_size;
          oce_rq_bdesc_t *rqbd;
          uint16_t cur_index;
          oce_ring_buffer_t *ring;
          int i;
+         uint32_t hdr_len;
  
          frag_cnt  = cqe->u0.s.num_fragments & 0x7;
          mblk_head = NULL;
          mblk_tail = &mblk_head;
  
*** 350,370 ****
          ring = rq->ring;
          cur_index = ring->cidx;
  
          /* Get the relevant Queue pointers */
          pkt_len = cqe->u0.s.pkt_size;
          for (i = 0; i < frag_cnt; i++) {
                  rqbd = rq->shadow_ring[cur_index];
                  if (rqbd->mp == NULL) {
!                         rqbd->mp = desballoc((uchar_t *)rqbd->rqb->base,
!                             rqbd->rqb->size, 0, &rqbd->fr_rtn);
                          if (rqbd->mp == NULL) {
                                  return (NULL);
                          }
- 
-                         rqbd->mp->b_rptr =
-                             (uchar_t *)rqbd->rqb->base + OCE_RQE_BUF_HEADROOM;
                  }
  
                  mp = rqbd->mp;
                  frag_size  = (pkt_len > rq->cfg.frag_size) ?
                      rq->cfg.frag_size : pkt_len;
--- 381,403 ----
          ring = rq->ring;
          cur_index = ring->cidx;
  
          /* Get the relevant Queue pointers */
          pkt_len = cqe->u0.s.pkt_size;
+ 
+         if (pkt_len == 0) {
+                 return (NULL);
+         }
+ 
          for (i = 0; i < frag_cnt; i++) {
                  rqbd = rq->shadow_ring[cur_index];
                  if (rqbd->mp == NULL) {
!                         rqbd->mp = desballoc((uchar_t *)rqbd->rqb.base,
!                             rqbd->rqb.size, 0, &rqbd->fr_rtn);
                          if (rqbd->mp == NULL) {
                                  return (NULL);
                          }
                  }
  
                  mp = rqbd->mp;
                  frag_size  = (pkt_len > rq->cfg.frag_size) ?
                      rq->cfg.frag_size : pkt_len;
*** 372,438 ****
                  pkt_len   -= frag_size;
                  mp->b_next = mp->b_cont = NULL;
                  /* Chain the message mblks */
                  *mblk_tail = mp;
                  mblk_tail = &mp->b_cont;
!                 (void) DBUF_SYNC(rqbd->rqb, DDI_DMA_SYNC_FORCPU);
                  cur_index = GET_Q_NEXT(cur_index, 1, ring->num_items);
          }
  
          if (mblk_head == NULL) {
                  oce_log(dev, CE_WARN, MOD_RX, "%s", "oce_rx:no frags?");
                  return (NULL);
          }
  
          /* replace the buffer with new ones */
          (void) oce_rq_charge(rq, frag_cnt, B_FALSE);
          atomic_add_32(&rq->pending, frag_cnt);
!         return (mblk_head);
  } /* oce_rx */
  
  static inline mblk_t *
  oce_rx_bcopy(struct oce_dev *dev, struct oce_rq *rq, struct oce_nic_rx_cqe *cqe)
  {
          mblk_t *mp;
          int pkt_len;
-         int alloc_len;
          int32_t frag_cnt = 0;
          int frag_size;
          oce_rq_bdesc_t *rqbd;
-         unsigned char  *rptr;
          uint32_t cur_index;
          oce_ring_buffer_t *ring;
          oce_rq_bdesc_t **shadow_rq;
          int cnt = 0;
- 
-         _NOTE(ARGUNUSED(dev));
- 
-         shadow_rq = rq->shadow_ring;
          pkt_len = cqe->u0.s.pkt_size;
-         alloc_len = pkt_len + OCE_RQE_BUF_HEADROOM;
-         frag_cnt = cqe->u0.s.num_fragments & 0x7;
  
!         mp = allocb(alloc_len, BPRI_HI);
          if (mp == NULL) {
                  return (NULL);
          }
  
-         mp->b_rptr += OCE_RQE_BUF_HEADROOM;
-         rptr = mp->b_rptr;
-         mp->b_wptr = mp->b_rptr + pkt_len;
          ring = rq->ring;
! 
          cur_index = ring->cidx;
-         for (cnt = 0; cnt < frag_cnt; cnt++) {
                  rqbd = shadow_rq[cur_index];
!                 frag_size  = (pkt_len > rq->cfg.frag_size) ?
!                     rq->cfg.frag_size : pkt_len;
!                 (void) DBUF_SYNC(rqbd->rqb, DDI_DMA_SYNC_FORCPU);
!                 bcopy(rqbd->rqb->base + OCE_RQE_BUF_HEADROOM, rptr, frag_size);
!                 rptr += frag_size;
!                 pkt_len   -= frag_size;
                  cur_index = GET_Q_NEXT(cur_index, 1, ring->num_items);
          }
          (void) oce_rq_charge(rq, frag_cnt, B_TRUE);
          return (mp);
  }
  
--- 405,516 ----
                  pkt_len   -= frag_size;
                  mp->b_next = mp->b_cont = NULL;
                  /* Chain the message mblks */
                  *mblk_tail = mp;
                  mblk_tail = &mp->b_cont;
!                 DBUF_SYNC(rqbd->rqb, rqbd->rqb.off, rqbd->rqb.len,
!                     DDI_DMA_SYNC_FORCPU);
                  cur_index = GET_Q_NEXT(cur_index, 1, ring->num_items);
          }
  
          if (mblk_head == NULL) {
                  oce_log(dev, CE_WARN, MOD_RX, "%s", "oce_rx:no frags?");
                  return (NULL);
          }
+         /* coallesce headers + Vtag  to first mblk */
+         mp = allocb(OCE_HDR_LEN, BPRI_HI);
+         if (mp == NULL) {
+                 return (NULL);
+         }
+         /* Align the IP header */
+         mp->b_rptr += OCE_IP_ALIGN;
  
+         if (oce_check_tagged(dev, cqe)) {
+                 hdr_len = min(MBLKL(mblk_head), OCE_HDR_LEN) -
+                     VTAG_SIZE - OCE_IP_ALIGN;
+                 (void) memcpy(mp->b_rptr, mblk_head->b_rptr, 2 * ETHERADDRL);
+                 oce_rx_insert_tag(dev, mp, cqe->u0.s.vlan_tag);
+                 (void) memcpy(mp->b_rptr + 16, mblk_head->b_rptr + 12,
+                     hdr_len - 12);
+                 mp->b_wptr = mp->b_rptr + VTAG_SIZE + hdr_len;
+         } else {
+ 
+                 hdr_len = min(MBLKL(mblk_head), OCE_HDR_LEN) - OCE_IP_ALIGN;
+                 (void) memcpy(mp->b_rptr, mblk_head->b_rptr, hdr_len);
+                 mp->b_wptr = mp->b_rptr + hdr_len;
+         }
+         mblk_head->b_rptr += hdr_len;
+         if (MBLKL(mblk_head) > 0) {
+                 mp->b_cont = mblk_head;
+         } else {
+                 mp->b_cont = mblk_head->b_cont;
+                 freeb(mblk_head);
+         }
          /* replace the buffer with new ones */
          (void) oce_rq_charge(rq, frag_cnt, B_FALSE);
          atomic_add_32(&rq->pending, frag_cnt);
!         return (mp);
  } /* oce_rx */
  
  static inline mblk_t *
  oce_rx_bcopy(struct oce_dev *dev, struct oce_rq *rq, struct oce_nic_rx_cqe *cqe)
  {
          mblk_t *mp;
          int pkt_len;
          int32_t frag_cnt = 0;
          int frag_size;
          oce_rq_bdesc_t *rqbd;
          uint32_t cur_index;
          oce_ring_buffer_t *ring;
          oce_rq_bdesc_t **shadow_rq;
          int cnt = 0;
          pkt_len = cqe->u0.s.pkt_size;
  
!         if (pkt_len == 0) {
!                 return (NULL);
!         }
! 
!         mp = allocb(pkt_len + OCE_RQE_BUF_HEADROOM, BPRI_HI);
          if (mp == NULL) {
                  return (NULL);
          }
  
          ring = rq->ring;
!         shadow_rq = rq->shadow_ring;
!         frag_cnt = cqe->u0.s.num_fragments & 0x7;
          cur_index = ring->cidx;
          rqbd = shadow_rq[cur_index];
!         frag_size  = min(pkt_len, rq->cfg.frag_size);
!         /* Align IP header */
!         mp->b_rptr += OCE_IP_ALIGN;
! 
!         /* Sync the first buffer */
!         DBUF_SYNC(rqbd->rqb, rqbd->rqb.off, rqbd->rqb.len,
!             DDI_DMA_SYNC_FORCPU);
! 
! 
!         if (oce_check_tagged(dev, cqe)) {
!                 (void) memcpy(mp->b_rptr, rqbd->rqb.base, 2  * ETHERADDRL);
!                 oce_rx_insert_tag(dev, mp, cqe->u0.s.vlan_tag);
!                 (void) memcpy(mp->b_rptr + 16, rqbd->rqb.base + 12,
!                     frag_size - 12);
!                 mp->b_wptr = mp->b_rptr + frag_size + VTAG_SIZE;
!         } else {
!                 (void) memcpy(mp->b_rptr, rqbd->rqb.base, frag_size);
!                 mp->b_wptr = mp->b_rptr + frag_size;
!         }
! 
!         for (cnt = 1; cnt < frag_cnt; cnt++) {
                  cur_index = GET_Q_NEXT(cur_index, 1, ring->num_items);
+                 pkt_len   -= frag_size;
+                 rqbd = shadow_rq[cur_index];
+                 frag_size  = min(rq->cfg.frag_size, pkt_len);
+                 DBUF_SYNC(rqbd->rqb, rqbd->rqb.off, rqbd->rqb.len,
+                     DDI_DMA_SYNC_FORCPU);
+ 
+                 (void) memcpy(mp->b_wptr, rqbd->rqb.base, frag_size);
+                 mp->b_wptr += frag_size;
          }
          (void) oce_rq_charge(rq, frag_cnt, B_TRUE);
          return (mp);
  }
  
*** 454,473 ****
                  (void) mac_hcksum_set(mp, 0, 0, 0, 0, csum_flags);
          }
  }
  
  static inline void
! oce_rx_insert_tag(mblk_t *mp, uint16_t vtag)
  {
          struct ether_vlan_header *ehp;
  
-         (void) memmove(mp->b_rptr - VTAG_SIZE,
-             mp->b_rptr, 2 * ETHERADDRL);
-         mp->b_rptr -= VTAG_SIZE;
          ehp = (struct ether_vlan_header *)voidptr(mp->b_rptr);
          ehp->ether_tpid = htons(ETHERTYPE_VLAN);
          ehp->ether_tci = LE_16(vtag);
  }
  
  static inline void
  oce_rx_drop_pkt(struct oce_rq *rq, struct oce_nic_rx_cqe *cqe)
  {
--- 532,552 ----
                  (void) mac_hcksum_set(mp, 0, 0, 0, 0, csum_flags);
          }
  }
  
  static inline void
! oce_rx_insert_tag(struct oce_dev *dev, mblk_t *mp, uint16_t vtag)
  {
          struct ether_vlan_header *ehp;
  
          ehp = (struct ether_vlan_header *)voidptr(mp->b_rptr);
          ehp->ether_tpid = htons(ETHERTYPE_VLAN);
+         if (LANCER_CHIP(dev))
+                 ehp->ether_tci = htons(vtag);
+         else
                  ehp->ether_tci = LE_16(vtag);
+ 
  }
  
  static inline void
  oce_rx_drop_pkt(struct oce_rq *rq, struct oce_nic_rx_cqe *cqe)
  {
*** 480,573 ****
                  oce_rqb_free(rq, rqbd);
                  RING_GET(rq->ring, 1);
          }
  }
  
! 
! /*
!  * function to process a Recieve queue
!  *
!  * arg - pointer to the RQ to charge
!  *
!  * return number of cqes processed
!  */
! uint16_t
! oce_drain_rq_cq(void *arg)
  {
-         struct oce_nic_rx_cqe *cqe;
          struct oce_rq *rq;
          mblk_t *mp = NULL;
-         mblk_t *mblk_head;
-         mblk_t **mblk_tail;
-         uint16_t num_cqe = 0;
          struct oce_cq  *cq;
-         struct oce_dev *dev;
          int32_t frag_cnt;
          uint32_t nbufs = 0;
  
          rq = (struct oce_rq *)arg;
-         dev = rq->parent;
          cq = rq->cq;
!         mblk_head = NULL;
!         mblk_tail = &mblk_head;
  
          cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe);
  
-         (void) DBUF_SYNC(cq->ring->dbuf, DDI_DMA_SYNC_FORKERNEL);
          /* dequeue till you reach an invalid cqe */
          while (RQ_CQE_VALID(cqe)) {
                  DW_SWAP(u32ptr(cqe), sizeof (struct oce_nic_rx_cqe));
                  frag_cnt = cqe->u0.s.num_fragments & 0x7;
                  /* if insufficient buffers to charge then do copy */
!                 if ((cqe->u0.s.pkt_size < dev->rx_bcopy_limit) ||
                      (oce_atomic_reserve(&rq->rqb_free, frag_cnt) < 0)) {
                          mp = oce_rx_bcopy(dev, rq, cqe);
                  } else {
                          mp = oce_rx(dev, rq, cqe);
                          if (mp == NULL) {
                                  atomic_add_32(&rq->rqb_free, frag_cnt);
                                  mp = oce_rx_bcopy(dev, rq, cqe);
                          }
                  }
                  if (mp != NULL) {
-                         if (dev->function_mode & FLEX10_MODE) {
-                                 if (cqe->u0.s.vlan_tag_present &&
-                                     cqe->u0.s.qnq) {
-                                         oce_rx_insert_tag(mp,
-                                             cqe->u0.s.vlan_tag);
-                                 }
-                         } else if (cqe->u0.s.vlan_tag_present) {
-                                 oce_rx_insert_tag(mp, cqe->u0.s.vlan_tag);
-                         }
                          oce_set_rx_oflags(mp, cqe);
  
!                         *mblk_tail = mp;
!                         mblk_tail = &mp->b_next;
                  } else {
                          (void) oce_rq_charge(rq, frag_cnt, B_TRUE);
                  }
                  RING_GET(rq->ring, frag_cnt);
                  rq->buf_avail -= frag_cnt;
                  nbufs += frag_cnt;
  
!                 oce_rq_post_buffer(rq, frag_cnt);
                  RQ_CQE_INVALIDATE(cqe);
                  RING_GET(cq->ring, 1);
-                 cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring,
-                     struct oce_nic_rx_cqe);
                  num_cqe++;
!                 /* process max ring size */
!                 if (num_cqe > dev->rx_pkt_per_intr) {
                          break;
                  }
          } /* for all valid CQEs */
  
!         if (mblk_head) {
!                 mac_rx(dev->mac_handle, NULL, mblk_head);
          }
!         oce_arm_cq(dev, cq->cq_id, num_cqe, B_TRUE);
!         return (num_cqe);
  } /* oce_drain_rq_cq */
  
  /*
   * function to free mblk databuffer to the RQ pool
   *
--- 559,692 ----
                  oce_rqb_free(rq, rqbd);
                  RING_GET(rq->ring, 1);
          }
  }
  
! void *
! oce_drain_rq_cq(void *arg, int nbytes, int npkts)
  {
          struct oce_rq *rq;
+         struct oce_dev *dev;
+         struct oce_nic_rx_cqe *cqe;
          mblk_t *mp = NULL;
          struct oce_cq  *cq;
          int32_t frag_cnt;
+         uint16_t num_cqe = 0;
+         uint16_t cqe_consumed = 0;
          uint32_t nbufs = 0;
+         int pkt_len;
+         uint32_t poll = (nbytes || 0);
+         mblk_t *mp_head = NULL;
+         mblk_t **mp_tail = &mp_head;
  
          rq = (struct oce_rq *)arg;
          cq = rq->cq;
!         dev = rq->parent;
  
+         if (!poll) {
+                 npkts = dev->rx_pkt_per_intr;
+         }
+ 
+         mutex_enter(&rq->rx_lock);
+         if ((!poll) && (rq->qmode == OCE_MODE_POLL)) {
+                 /* reject any interrupt call in poll mode */
+                 mutex_exit(&rq->rx_lock);
+                 return (NULL);
+         }
+ 
+         if (rq->qstate == QDELETED) {
+                 mutex_exit(&rq->rx_lock);
+                 return (NULL);
+         }
+ 
+         DBUF_SYNC(cq->ring->dbuf, 0, 0, DDI_DMA_SYNC_FORKERNEL);
          cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe);
  
          /* dequeue till you reach an invalid cqe */
          while (RQ_CQE_VALID(cqe)) {
                  DW_SWAP(u32ptr(cqe), sizeof (struct oce_nic_rx_cqe));
+ 
+                 pkt_len = cqe->u0.s.pkt_size;
+ 
+ 
+                 if (poll) {
+                         if (nbytes < pkt_len) {
+                                 DW_SWAP(u32ptr(cqe),
+                                     sizeof (struct oce_nic_rx_cqe));
+                                 break;
+                         }
+                         /* reduce the available budget */
+                         nbytes -= pkt_len;
+                 }
+ 
                  frag_cnt = cqe->u0.s.num_fragments & 0x7;
+ 
                  /* if insufficient buffers to charge then do copy */
!                 if ((pkt_len < dev->rx_bcopy_limit) ||
                      (oce_atomic_reserve(&rq->rqb_free, frag_cnt) < 0)) {
                          mp = oce_rx_bcopy(dev, rq, cqe);
                  } else {
                          mp = oce_rx(dev, rq, cqe);
                          if (mp == NULL) {
                                  atomic_add_32(&rq->rqb_free, frag_cnt);
                                  mp = oce_rx_bcopy(dev, rq, cqe);
                          }
                  }
+ 
                  if (mp != NULL) {
                          oce_set_rx_oflags(mp, cqe);
  
!                         *mp_tail = mp;
!                         mp_tail = &mp->b_next;
! 
                  } else {
                          (void) oce_rq_charge(rq, frag_cnt, B_TRUE);
                  }
                  RING_GET(rq->ring, frag_cnt);
                  rq->buf_avail -= frag_cnt;
                  nbufs += frag_cnt;
  
!                 /* update the ring stats */
!                 rq->stat_bytes += pkt_len;
!                 rq->stat_pkts++;
! 
                  RQ_CQE_INVALIDATE(cqe);
                  RING_GET(cq->ring, 1);
                  num_cqe++;
! 
!                 cqe_consumed++;
!                 if (nbufs >= OCE_DEFAULT_RECHARGE_THRESHOLD) {
!                         oce_arm_cq(dev, cq->cq_id, cqe_consumed, B_FALSE);
!                         oce_rq_post_buffer(rq, nbufs);
!                         nbufs = 0;
!                         cqe_consumed = 0;
!                 }
! 
!                 if (!poll && (--npkts <= 0)) {
                          break;
                  }
+                 cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring,
+                     struct oce_nic_rx_cqe);
+ 
          } /* for all valid CQEs */
  
!         if (cqe_consumed) {
!                 oce_arm_cq(dev, cq->cq_id, cqe_consumed, rq->qmode);
!                 oce_rq_post_buffer(rq, nbufs);
!         } else {
!                 oce_arm_cq(dev, cq->cq_id, 0, rq->qmode);
          }
! 
!         mutex_exit(&rq->rx_lock);
! 
!         if (!poll && mp_head) {
!                 mac_rx_ring(dev->mac_handle, rq->handle, mp_head,
!                     rq->gen_number);
!         }
! 
!         return (mp_head);
! 
  } /* oce_drain_rq_cq */
  
  /*
   * function to free mblk databuffer to the RQ pool
   *
*** 578,606 ****
  void
  oce_rx_pool_free(char *arg)
  {
          oce_rq_bdesc_t *rqbd;
          struct oce_rq  *rq;
  
          /* During destroy, arg will be NULL */
          if (arg == NULL) {
                  return;
          }
  
          /* retrieve the pointers from arg */
          rqbd = (oce_rq_bdesc_t *)(void *)arg;
          rq = rqbd->rq;
!         rqbd->mp = desballoc((uchar_t *)rqbd->rqb->base,
!             rqbd->rqb->size, 0, &rqbd->fr_rtn);
  
-         if (rqbd->mp) {
-                 rqbd->mp->b_rptr =
-                     (uchar_t *)rqbd->rqb->base + OCE_RQE_BUF_HEADROOM;
-         }
- 
          oce_rqb_free(rq, rqbd);
          (void) atomic_dec_32(&rq->pending);
  } /* rx_pool_free */
  
  /*
   * function to stop the RX
   *
--- 697,730 ----
  void
  oce_rx_pool_free(char *arg)
  {
          oce_rq_bdesc_t *rqbd;
          struct oce_rq  *rq;
+         struct oce_dev  *dev;
  
          /* During destroy, arg will be NULL */
          if (arg == NULL) {
                  return;
          }
  
          /* retrieve the pointers from arg */
          rqbd = (oce_rq_bdesc_t *)(void *)arg;
          rq = rqbd->rq;
!         dev = rq->parent;
!         rqbd->mp = desballoc((uchar_t *)rqbd->rqb.base,
!             rqbd->rqb.size, 0, &rqbd->fr_rtn);
  
          oce_rqb_free(rq, rqbd);
          (void) atomic_dec_32(&rq->pending);
+ 
+         if (rq->pending == 0) {
+                 mutex_enter(&rq->rq_fini_lock);
+                 if (rq->qstate == QFINI_PENDING) {
+                         oce_rq_fini(dev, rq);
+                 }
+                 mutex_exit(&rq->rq_fini_lock);
+         }
  } /* rx_pool_free */
  
  /*
   * function to stop the RX
   *
*** 614,632 ****
--- 738,764 ----
          uint16_t num_cqe = 0;
          struct oce_cq  *cq;
          struct oce_dev *dev;
          struct oce_nic_rx_cqe *cqe;
          int32_t ti = 0;
+         int frag_cnt;
  
          dev = rq->parent;
          cq = rq->cq;
          cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe);
          /* dequeue till you reach an invalid cqe */
          for (ti = 0; ti < DEFAULT_DRAIN_TIME; ti++) {
  
                  while (RQ_CQE_VALID(cqe)) {
                          DW_SWAP(u32ptr(cqe), sizeof (struct oce_nic_rx_cqe));
+                         frag_cnt = cqe->u0.s.num_fragments & 0x7;
+                         if (frag_cnt == 0) {
+                                 oce_log(dev, CE_NOTE, MOD_RX, "%s",
+                                     "Got Rx Completion Marble Returning ...\n");
+                                 RQ_CQE_INVALIDATE(cqe);
+                                 return;
+                         }
                          oce_rx_drop_pkt(rq, cqe);
                          atomic_add_32(&rq->buf_avail,
                              -(cqe->u0.s.num_fragments & 0x7));
                          oce_arm_cq(dev, cq->cq_id, 1, B_TRUE);
                          RQ_CQE_INVALIDATE(cqe);
*** 633,642 ****
--- 765,781 ----
                          RING_GET(cq->ring, 1);
                          cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring,
                              struct oce_nic_rx_cqe);
                          num_cqe++;
                  }
+                 if (num_cqe == 0) {
+                         /* arm the queue again to get completion marble */
+                         oce_arm_cq(dev, cq->cq_id, 0, 1);
+                 } else {
+                         /* reset counter to reap valid completions again */
+                         num_cqe = 0;
+                 }
                  OCE_MSDELAY(1);
          }
  } /* oce_clean_rq */
  
  /*
*** 656,665 ****
--- 795,805 ----
          to_charge = min(to_charge, rq->rqb_free);
          atomic_add_32(&rq->rqb_free, -to_charge);
          (void) oce_rq_charge(rq, to_charge, B_FALSE);
          /* ok to do it here since Rx has not even started */
          oce_rq_post_buffer(rq, to_charge);
+         rq->qmode = OCE_MODE_INTR;
          oce_arm_cq(dev, rq->cq->cq_id, 0, B_TRUE);
          return (ret);
  } /* oce_start_rq */
  
  /* Checks for pending rx buffers with Stack */
*** 672,682 ****
          for (ti = 0; ti < timeout; ti++) {
                  if (rq->pending > 0) {
                          OCE_MSDELAY(10);
                          continue;
                  } else {
-                         rq->pending = 0;
                          break;
                  }
          }
          return (rq->pending);
  }
--- 812,847 ----
          for (ti = 0; ti < timeout; ti++) {
                  if (rq->pending > 0) {
                          OCE_MSDELAY(10);
                          continue;
                  } else {
                          break;
                  }
          }
+ 
+         if (rq->pending != 0) {
+                 oce_log(dev, CE_NOTE, MOD_CONFIG,
+                     "%d pending RX buffers in rq=0x%p", rq->pending,
+                     (void *)rq);
+         }
          return (rq->pending);
+ }
+ 
+ static boolean_t
+ oce_check_tagged(struct oce_dev *dev, struct oce_nic_rx_cqe *cqe)
+ {
+         boolean_t tagged = B_FALSE;
+         if (((dev->drvfn_caps & DRVFN_CAPAB_BE3_NATIVE) &&
+             cqe->u0.s.vlan_tag_present) ||
+             (!(dev->drvfn_caps & DRVFN_CAPAB_BE3_NATIVE) &&
+             cqe->u0.v0.vlan_tag_present)) {
+                 if (dev->function_mode & FLEX10_MODE) {
+                         if (cqe->u0.s.qnq)
+                                 tagged = B_TRUE;
+                 } else if (dev->pvid != 0) {
+                         if (dev->pvid != cqe->u0.v0.vlan_tag)
+                                 tagged = B_TRUE;
+                 } else
+                         tagged = B_TRUE;
+         }
+         return (tagged);
  }