netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
To: davem@davemloft.net
Cc: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>,
	netdev@vger.kernel.org, gospo@redhat.com, sassmann@redhat.com,
	Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Subject: [net-next 02/14] i40e/i40evf: Big endian fixes for handling HMC
Date: Thu, 26 Jun 2014 05:13:41 -0700	[thread overview]
Message-ID: <1403784834-23301-3-git-send-email-jeffrey.t.kirsher@intel.com> (raw)
In-Reply-To: <1403784834-23301-1-git-send-email-jeffrey.t.kirsher@intel.com>

From: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>

Fix HMC handling for big endian architectures.

Change-ID: Id8c46fc341815d47bfe0af8b819f0ab9a1e9e515
Signed-off-by: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c   | 247 ++++++++++++++++++-----
 drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h   |  28 ++-
 drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h |  28 ++-
 3 files changed, 236 insertions(+), 67 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
index 870ab1e..5a603a5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
@@ -747,6 +747,194 @@ static struct i40e_context_ele i40e_hmc_rxq_ce_info[] = {
 };
 
 /**
+ * i40e_write_byte - replace HMC context byte
+ * @hmc_bits: pointer to the HMC memory
+ * @ce_info: a description of the struct to be read from
+ * @src: the struct to be read from
+ **/
+static void i40e_write_byte(u8 *hmc_bits,
+			    struct i40e_context_ele *ce_info,
+			    u8 *src)
+{
+	u8 src_byte, dest_byte, mask;
+	u8 *from, *dest;
+	u16 shift_width;
+
+	/* copy from the next struct field */
+	from = src + ce_info->offset;
+
+	/* prepare the bits and mask */
+	shift_width = ce_info->lsb % 8;
+	mask = ((u8)1 << ce_info->width) - 1;
+
+	src_byte = *from;
+	src_byte &= mask;
+
+	/* shift to correct alignment */
+	mask <<= shift_width;
+	src_byte <<= shift_width;
+
+	/* get the current bits from the target bit string */
+	dest = hmc_bits + (ce_info->lsb / 8);
+
+	memcpy(&dest_byte, dest, sizeof(dest_byte));
+
+	dest_byte &= ~mask;	/* get the bits not changing */
+	dest_byte |= src_byte;	/* add in the new bits */
+
+	/* put it all back */
+	memcpy(dest, &dest_byte, sizeof(dest_byte));
+}
+
+/**
+ * i40e_write_word - replace HMC context word
+ * @hmc_bits: pointer to the HMC memory
+ * @ce_info: a description of the struct to be read from
+ * @src: the struct to be read from
+ **/
+static void i40e_write_word(u8 *hmc_bits,
+			    struct i40e_context_ele *ce_info,
+			    u8 *src)
+{
+	u16 src_word, mask;
+	u8 *from, *dest;
+	u16 shift_width;
+	__le16 dest_word;
+
+	/* copy from the next struct field */
+	from = src + ce_info->offset;
+
+	/* prepare the bits and mask */
+	shift_width = ce_info->lsb % 8;
+	mask = ((u16)1 << ce_info->width) - 1;
+
+	/* don't swizzle the bits until after the mask because the mask bits
+	 * will be in a different bit position on big endian machines
+	 */
+	src_word = *(u16 *)from;
+	src_word &= mask;
+
+	/* shift to correct alignment */
+	mask <<= shift_width;
+	src_word <<= shift_width;
+
+	/* get the current bits from the target bit string */
+	dest = hmc_bits + (ce_info->lsb / 8);
+
+	memcpy(&dest_word, dest, sizeof(dest_word));
+
+	dest_word &= ~(cpu_to_le16(mask));	/* get the bits not changing */
+	dest_word |= cpu_to_le16(src_word);	/* add in the new bits */
+
+	/* put it all back */
+	memcpy(dest, &dest_word, sizeof(dest_word));
+}
+
+/**
+ * i40e_write_dword - replace HMC context dword
+ * @hmc_bits: pointer to the HMC memory
+ * @ce_info: a description of the struct to be read from
+ * @src: the struct to be read from
+ **/
+static void i40e_write_dword(u8 *hmc_bits,
+			     struct i40e_context_ele *ce_info,
+			     u8 *src)
+{
+	u32 src_dword, mask;
+	u8 *from, *dest;
+	u16 shift_width;
+	__le32 dest_dword;
+
+	/* copy from the next struct field */
+	from = src + ce_info->offset;
+
+	/* prepare the bits and mask */
+	shift_width = ce_info->lsb % 8;
+
+	/* if the field width is exactly 32 on an x86 machine, then the shift
+	 * operation will not work because the SHL instructions count is masked
+	 * to 5 bits so the shift will do nothing
+	 */
+	if (ce_info->width < 32)
+		mask = ((u32)1 << ce_info->width) - 1;
+	else
+		mask = -1;
+
+	/* don't swizzle the bits until after the mask because the mask bits
+	 * will be in a different bit position on big endian machines
+	 */
+	src_dword = *(u32 *)from;
+	src_dword &= mask;
+
+	/* shift to correct alignment */
+	mask <<= shift_width;
+	src_dword <<= shift_width;
+
+	/* get the current bits from the target bit string */
+	dest = hmc_bits + (ce_info->lsb / 8);
+
+	memcpy(&dest_dword, dest, sizeof(dest_dword));
+
+	dest_dword &= ~(cpu_to_le32(mask));	/* get the bits not changing */
+	dest_dword |= cpu_to_le32(src_dword);	/* add in the new bits */
+
+	/* put it all back */
+	memcpy(dest, &dest_dword, sizeof(dest_dword));
+}
+
+/**
+ * i40e_write_qword - replace HMC context qword
+ * @hmc_bits: pointer to the HMC memory
+ * @ce_info: a description of the struct to be read from
+ * @src: the struct to be read from
+ **/
+static void i40e_write_qword(u8 *hmc_bits,
+			     struct i40e_context_ele *ce_info,
+			     u8 *src)
+{
+	u64 src_qword, mask;
+	u8 *from, *dest;
+	u16 shift_width;
+	__le64 dest_qword;
+
+	/* copy from the next struct field */
+	from = src + ce_info->offset;
+
+	/* prepare the bits and mask */
+	shift_width = ce_info->lsb % 8;
+
+	/* if the field width is exactly 64 on an x86 machine, then the shift
+	 * operation will not work because the SHL instructions count is masked
+	 * to 6 bits so the shift will do nothing
+	 */
+	if (ce_info->width < 64)
+		mask = ((u64)1 << ce_info->width) - 1;
+	else
+		mask = -1;
+
+	/* don't swizzle the bits until after the mask because the mask bits
+	 * will be in a different bit position on big endian machines
+	 */
+	src_qword = *(u64 *)from;
+	src_qword &= mask;
+
+	/* shift to correct alignment */
+	mask <<= shift_width;
+	src_qword <<= shift_width;
+
+	/* get the current bits from the target bit string */
+	dest = hmc_bits + (ce_info->lsb / 8);
+
+	memcpy(&dest_qword, dest, sizeof(dest_qword));
+
+	dest_qword &= ~(cpu_to_le64(mask));	/* get the bits not changing */
+	dest_qword |= cpu_to_le64(src_qword);	/* add in the new bits */
+
+	/* put it all back */
+	memcpy(dest, &dest_qword, sizeof(dest_qword));
+}
+
+/**
  * i40e_clear_hmc_context - zero out the HMC context bits
  * @hw:       the hardware struct
  * @context_bytes: pointer to the context bit array (DMA memory)
@@ -772,71 +960,28 @@ static i40e_status i40e_set_hmc_context(u8 *context_bytes,
 					struct i40e_context_ele *ce_info,
 					u8 *dest)
 {
-	u16 shift_width;
-	u64 bitfield;
-	u8 hi_byte;
-	u8 hi_mask;
-	u64 t_bits;
-	u64 mask;
-	u8 *p;
 	int f;
 
 	for (f = 0; ce_info[f].width != 0; f++) {
-		/* clear out the field */
-		bitfield = 0;
 
-		/* copy from the next struct field */
-		p = dest + ce_info[f].offset;
+		/* we have to deal with each element of the HMC using the
+		 * correct size so that we are correct regardless of the
+		 * endianness of the machine
+		 */
 		switch (ce_info[f].size_of) {
 		case 1:
-			bitfield = *p;
+			i40e_write_byte(context_bytes, &ce_info[f], dest);
 			break;
 		case 2:
-			bitfield = cpu_to_le16(*(u16 *)p);
+			i40e_write_word(context_bytes, &ce_info[f], dest);
 			break;
 		case 4:
-			bitfield = cpu_to_le32(*(u32 *)p);
+			i40e_write_dword(context_bytes, &ce_info[f], dest);
 			break;
 		case 8:
-			bitfield = cpu_to_le64(*(u64 *)p);
+			i40e_write_qword(context_bytes, &ce_info[f], dest);
 			break;
 		}
-
-		/* prepare the bits and mask */
-		shift_width = ce_info[f].lsb % 8;
-		mask = ((u64)1 << ce_info[f].width) - 1;
-
-		/* save upper bytes for special case */
-		hi_mask = (u8)((mask >> 56) & 0xff);
-		hi_byte = (u8)((bitfield >> 56) & 0xff);
-
-		/* shift to correct alignment */
-		mask <<= shift_width;
-		bitfield <<= shift_width;
-
-		/* get the current bits from the target bit string */
-		p = context_bytes + (ce_info[f].lsb / 8);
-		memcpy(&t_bits, p, sizeof(u64));
-
-		t_bits &= ~mask;          /* get the bits not changing */
-		t_bits |= bitfield;       /* add in the new bits */
-
-		/* put it all back */
-		memcpy(p, &t_bits, sizeof(u64));
-
-		/* deal with the special case if needed
-		 * example: 62 bit field that starts in bit 5 of first byte
-		 *          will overlap 3 bits into byte 9
-		 */
-		if ((shift_width + ce_info[f].width) > 64) {
-			u8 byte;
-
-			hi_mask >>= (8 - shift_width);
-			hi_byte >>= (8 - shift_width);
-			byte = p[8] & ~hi_mask;  /* get the bits not changing */
-			byte |= hi_byte;         /* add in the new bits */
-			p[8] = byte;             /* put it back */
-		}
 	}
 
 	return 0;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
index eb65fe2..e74128d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
@@ -32,16 +32,22 @@ struct i40e_hw;
 
 /* HMC element context information */
 
-/* Rx queue context data */
+/* Rx queue context data
+ *
+ * The sizes of the variables may be larger than needed due to crossing byte
+ * boundaries. If we do not have the width of the variable set to the correct
+ * size then we could end up shifting bits off the top of the variable when the
+ * variable is at the top of a byte and crosses over into the next byte.
+ */
 struct i40e_hmc_obj_rxq {
 	u16 head;
-	u8  cpuid;
+	u16 cpuid; /* bigger than needed, see above for reason */
 	u64 base;
 	u16 qlen;
 #define I40E_RXQ_CTX_DBUFF_SHIFT 7
-	u8  dbuff;
+	u16 dbuff; /* bigger than needed, see above for reason */
 #define I40E_RXQ_CTX_HBUFF_SHIFT 6
-	u8  hbuff;
+	u16 hbuff; /* bigger than needed, see above for reason */
 	u8  dtype;
 	u8  dsize;
 	u8  crcstrip;
@@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq {
 	u8  hsplit_0;
 	u8  hsplit_1;
 	u8  showiv;
-	u16 rxmax;
+	u32 rxmax; /* bigger than needed, see above for reason */
 	u8  tphrdesc_ena;
 	u8  tphwdesc_ena;
 	u8  tphdata_ena;
 	u8  tphhead_ena;
-	u8  lrxqthresh;
+	u16 lrxqthresh; /* bigger than needed, see above for reason */
 	u8  prefena;	/* NOTE: normally must be set to 1 at init */
 };
 
-/* Tx queue context data */
+/* Tx queue context data
+*
+* The sizes of the variables may be larger than needed due to crossing byte
+* boundaries. If we do not have the width of the variable set to the correct
+* size then we could end up shifting bits off the top of the variable when the
+* variable is at the top of a byte and crosses over into the next byte.
+*/
 struct i40e_hmc_obj_txq {
 	u16 head;
 	u8  new_context;
@@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq {
 	u8  fd_ena;
 	u8  alt_vlan_ena;
 	u16 thead_wb;
-	u16 cpuid;
+	u8  cpuid;
 	u8  head_wb_ena;
 	u16 qlen;
 	u8  tphrdesc_ena;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
index d6f7622..a5d7987 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
@@ -32,16 +32,22 @@ struct i40e_hw;
 
 /* HMC element context information */
 
-/* Rx queue context data */
+/* Rx queue context data
+ *
+ * The sizes of the variables may be larger than needed due to crossing byte
+ * boundaries. If we do not have the width of the variable set to the correct
+ * size then we could end up shifting bits off the top of the variable when the
+ * variable is at the top of a byte and crosses over into the next byte.
+ */
 struct i40e_hmc_obj_rxq {
 	u16 head;
-	u8  cpuid;
+	u16 cpuid; /* bigger than needed, see above for reason */
 	u64 base;
 	u16 qlen;
 #define I40E_RXQ_CTX_DBUFF_SHIFT 7
-	u8  dbuff;
+	u16 dbuff; /* bigger than needed, see above for reason */
 #define I40E_RXQ_CTX_HBUFF_SHIFT 6
-	u8  hbuff;
+	u16 hbuff; /* bigger than needed, see above for reason */
 	u8  dtype;
 	u8  dsize;
 	u8  crcstrip;
@@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq {
 	u8  hsplit_0;
 	u8  hsplit_1;
 	u8  showiv;
-	u16 rxmax;
+	u32 rxmax; /* bigger than needed, see above for reason */
 	u8  tphrdesc_ena;
 	u8  tphwdesc_ena;
 	u8  tphdata_ena;
 	u8  tphhead_ena;
-	u8  lrxqthresh;
+	u16 lrxqthresh; /* bigger than needed, see above for reason */
 	u8  prefena;	/* NOTE: normally must be set to 1 at init */
 };
 
-/* Tx queue context data */
+/* Tx queue context data
+*
+* The sizes of the variables may be larger than needed due to crossing byte
+* boundaries. If we do not have the width of the variable set to the correct
+* size then we could end up shifting bits off the top of the variable when the
+* variable is at the top of a byte and crosses over into the next byte.
+*/
 struct i40e_hmc_obj_txq {
 	u16 head;
 	u8  new_context;
@@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq {
 	u8  fd_ena;
 	u8  alt_vlan_ena;
 	u16 thead_wb;
-	u16 cpuid;
+	u8  cpuid;
 	u8  head_wb_ena;
 	u16 qlen;
 	u8  tphrdesc_ena;
-- 
1.9.3

  parent reply	other threads:[~2014-06-26 12:14 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-26 12:13 [net-next 00/14][pull request] Intel Wired LAN Driver Updates 2014-06-26 Jeff Kirsher
2014-06-26 12:13 ` [net-next 01/14] i40e: do not take NVM ownership for SR read Jeff Kirsher
2014-06-26 12:13 ` Jeff Kirsher [this message]
2014-06-26 12:13 ` [net-next 03/14] i40evf: don't stop watchdog if it hasn't started Jeff Kirsher
2014-06-26 12:13 ` [net-next 04/14] i40evf: return more useful error information Jeff Kirsher
2014-06-26 12:13 ` [net-next 05/14] i40evf: fix typo Jeff Kirsher
2014-06-26 12:13 ` [net-next 06/14] i40evf: resend FW request if no response Jeff Kirsher
2014-06-26 12:13 ` [net-next 07/14] i40e: rename i40e_ptp_enable to i40e_ptp_feature_enable Jeff Kirsher
2014-06-26 12:13 ` [net-next 08/14] i40e: break PTP hardware control from ioctl command for timestamp mode Jeff Kirsher
2014-06-26 12:13 ` [net-next 09/14] i40e: don't store user requested mode until we've validated it Jeff Kirsher
2014-06-26 12:13 ` [net-next 10/14] i40e: only create PTP device node once Jeff Kirsher
2014-06-26 12:13 ` [net-next 11/14] i40e: Avoid adding the TCP-IPv4 filter twice Jeff Kirsher
2014-06-26 12:13 ` [net-next 12/14] i40e: Fix the FD sideband logic to detect a FD table full condition Jeff Kirsher
2014-06-26 12:13 ` [net-next 13/14] i40e: Add debugfs hooks to print current total FD filter count Jeff Kirsher
2014-06-26 12:13 ` [net-next 14/14] i40e: fix fdir programming Jeff Kirsher
2014-06-26 12:13 ` [net-next 15/15] i40e/i40evf: Bump i40e to 0.4.18 and i40evf to 0.9.37 Jeff Kirsher
2014-06-27 19:59 ` [net-next 00/14][pull request] Intel Wired LAN Driver Updates 2014-06-26 David Miller

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=1403784834-23301-3-git-send-email-jeffrey.t.kirsher@intel.com \
    --to=jeffrey.t.kirsher@intel.com \
    --cc=davem@davemloft.net \
    --cc=gospo@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=paul.m.stillwell.jr@intel.com \
    --cc=sassmann@redhat.com \
    /path/to/YOUR_REPLY

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

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