Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c600636b authored by Amir Hanania's avatar Amir Hanania Committed by Jeff Kirsher
Browse files

ixgbe: work around for DDP last buffer size



A HW limitation was recently discovered where the last buffer in a DDP offload
cannot be a full buffer size in length. Fix the issue with a work around by
adding another buffer with size = 1.

Signed-off-by: default avatarAmir Hanania <amir.hanania@intel.com>
Tested-by: default avatarRoss Brattain <ross.b.brattain@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 4c7e604b
Loading
Loading
Loading
Loading
+50 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
	struct scatterlist *sg;
	unsigned int i, j, dmacount;
	unsigned int len;
	static const unsigned int bufflen = 4096;
	static const unsigned int bufflen = IXGBE_FCBUFF_MIN;
	unsigned int firstoff = 0;
	unsigned int lastsize;
	unsigned int thisoff = 0;
@@ -254,6 +254,24 @@ int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
	/* only the last buffer may have non-full bufflen */
	lastsize = thisoff + thislen;

	/*
	 * lastsize can not be buffer len.
	 * If it is then adding another buffer with lastsize = 1.
	 */
	if (lastsize == bufflen) {
		if (j >= IXGBE_BUFFCNT_MAX) {
			e_err(drv, "xid=%x:%d,%d,%d:addr=%llx "
				"not enough user buffers. We need an extra "
				"buffer because lastsize is bufflen.\n",
				xid, i, j, dmacount, (u64)addr);
			goto out_noddp_free;
		}

		ddp->udl[j] = (u64)(fcoe->extra_ddp_buffer_dma);
		j++;
		lastsize = 1;
	}

	fcbuff = (IXGBE_FCBUFF_4KB << IXGBE_FCBUFF_BUFFSIZE_SHIFT);
	fcbuff |= ((j & 0xff) << IXGBE_FCBUFF_BUFFCNT_SHIFT);
	fcbuff |= (firstoff << IXGBE_FCBUFF_OFFSET_SHIFT);
@@ -532,6 +550,24 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
			e_err(drv, "failed to allocated FCoE DDP pool\n");

		spin_lock_init(&fcoe->lock);

		/* Extra buffer to be shared by all DDPs for HW work around */
		fcoe->extra_ddp_buffer = kmalloc(IXGBE_FCBUFF_MIN, GFP_ATOMIC);
		if (fcoe->extra_ddp_buffer == NULL) {
			e_err(drv, "failed to allocated extra DDP buffer\n");
			goto out_extra_ddp_buffer_alloc;
		}

		fcoe->extra_ddp_buffer_dma =
			dma_map_single(&adapter->pdev->dev,
				       fcoe->extra_ddp_buffer,
				       IXGBE_FCBUFF_MIN,
				       DMA_FROM_DEVICE);
		if (dma_mapping_error(&adapter->pdev->dev,
				      fcoe->extra_ddp_buffer_dma)) {
			e_err(drv, "failed to map extra DDP buffer\n");
			goto out_extra_ddp_buffer_dma;
		}
	}

	/* Enable L2 eth type filter for FCoE */
@@ -581,6 +617,14 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
		}
	}
#endif

	return;

out_extra_ddp_buffer_dma:
	kfree(fcoe->extra_ddp_buffer);
out_extra_ddp_buffer_alloc:
	pci_pool_destroy(fcoe->pool);
	fcoe->pool = NULL;
}

/**
@@ -600,6 +644,11 @@ void ixgbe_cleanup_fcoe(struct ixgbe_adapter *adapter)
	if (fcoe->pool) {
		for (i = 0; i < IXGBE_FCOE_DDP_MAX; i++)
			ixgbe_fcoe_ddp_put(adapter->netdev, i);
		dma_unmap_single(&adapter->pdev->dev,
				 fcoe->extra_ddp_buffer_dma,
				 IXGBE_FCBUFF_MIN,
				 DMA_FROM_DEVICE);
		kfree(fcoe->extra_ddp_buffer);
		pci_pool_destroy(fcoe->pool);
		fcoe->pool = NULL;
	}
+2 −0
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@ struct ixgbe_fcoe {
	spinlock_t lock;
	struct pci_pool *pool;
	struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX];
	unsigned char *extra_ddp_buffer;
	dma_addr_t extra_ddp_buffer_dma;
};

#endif /* _IXGBE_FCOE_H */