From: Leon Romanovsky <leonro@xxxxxxxxxxxx> There is no separation between RDMA-CM wire format as it is declared in IBTA and kernel logic which implements needed support. Such situation causes to many mistakes in conversion between big-endian (wire format) and CPU format used by kernel. It also mixes RDMA core code with combination of uXX and beXX variables. The idea that every converted variable will have two fields which describes the byte offset and mask for the access, e.g. #define CM_REP_LOCAL_QPN_OFFSET 12 #define CM_REP_LOCAL_QPN_MASK GENMASK(23, 0) Such format will allow us to use same GET/SET macros for all be16/be32 variables and bitfields too. Separate wire protocol from kernel logic by special GET/SET macros. Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxxxx> --- drivers/infiniband/core/cm_msgs.h | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h index 92d7260ac913..99e35a4610f1 100644 --- a/drivers/infiniband/core/cm_msgs.h +++ b/drivers/infiniband/core/cm_msgs.h @@ -8,6 +8,7 @@ #ifndef CM_MSGS_H #define CM_MSGS_H +#include <linux/bitfield.h> #include <rdma/ib_mad.h> #include <rdma/ib_cm.h> @@ -17,6 +18,76 @@ */ #define IB_CM_CLASS_VERSION 2 /* IB specification 1.2 */ +#define _CM_SET(p, offset, mask, value) \ + ({ \ + void *field = (u8 *)p + sizeof(struct ib_mad_hdr) + offset; \ + u8 bytes = \ + DIV_ROUND_UP(__builtin_popcount(mask), BITS_PER_BYTE); \ + switch (bytes) { \ + case 1: { \ + *(u8 *)field &= ~mask; \ + *(u8 *)field |= FIELD_PREP(mask, value); \ + } break; \ + case 2: { \ + u16 val = ntohs(*(__be16 *)field) & ~mask; \ + val |= FIELD_PREP(mask, value); \ + *(__be16 *)field = htons(val); \ + } break; \ + case 3: { \ + u32 val = ntohl(*(__be32 *)field) & ~(mask << 8); \ + val |= FIELD_PREP(mask, value) << 8; \ + *(__be32 *)field = htonl(val); \ + } break; \ + default: { \ + u32 val = ntohl(*(__be32 *)field) & ~mask; \ + val |= FIELD_PREP(mask, value); \ + *(__be32 *)field = htonl(val); \ + } break; \ + } \ + }) +#define CM_SET(field, p, value) \ + _CM_SET(p, CM_##field##_OFFSET, CM_##field##_MASK, value) +#define CM_SET64(field, p, value) \ + ({ \ + _CM_SET(p, CM_##field##_OFFSET, \ + lower_32_bits(CM_##field##_MASK), \ + lower_32_bits(value)); \ + _CM_SET(p, CM_##field##_OFFSET + sizeof(__be32), \ + upper_32_bits(CM_##field##_MASK), \ + upper_32_bits(value)); \ + }) + +#define _CM_GET(p, offset, mask) \ + ({ \ + void *field = (u8 *)p + sizeof(struct ib_mad_hdr) + offset; \ + u8 bytes = \ + DIV_ROUND_UP(__builtin_popcount(mask), BITS_PER_BYTE); \ + u32 ret; \ + switch (bytes) { \ + case 1: \ + ret = FIELD_GET(mask, *(u8 *)field); \ + break; \ + case 2: \ + ret = FIELD_GET(mask, ntohs(*(__be16 *)field)); \ + break; \ + case 3: \ + ret = FIELD_GET(mask, ntohl(*(__be32 *)field) >> 8); \ + break; \ + default: \ + ret = FIELD_GET(mask, ntohl(*(__be32 *)field)); \ + } \ + ret; \ + }) + +#define CM_GET(field, p) _CM_GET(p, CM_##field##_OFFSET, CM_##field##_MASK) +#define CM_GET64(field, p) \ + ({ \ + u64 a = _CM_GET(p, CM_##field##_OFFSET, \ + lower_32_bits(CM_##field##_MASK)); \ + u64 b = _CM_GET(p, CM_##field##_OFFSET + sizeof(__be32), \ + upper_32_bits(CM_##field##_MASK)); \ + a | (b << 32); \ + }) struct cm_req_msg { struct ib_mad_hdr hdr; -- 2.20.1