IB/umad: Fix bit ordering and 32-on-64 problems on big endian systems
The declaration of struct ib_user_mad_reg_req.method_mask[] exported
to userspace was an array of __u32, but the kernel internally treated
it as a bitmap made up of longs. This makes a difference for 64-bit
big-endian kernels, where numbering the bits in an array of__u32 gives:
|31.....0|63....31|95....64|127...96|
while numbering the bits in an array of longs gives:
|63..............0|127............64|
64-bit userspace can handle this by just treating method_mask[] as an
array of longs, but 32-bit userspace is really stuck: the meaning of
the bits in method_mask[] depends on whether the kernel is 32-bit or
64-bit, and there's no sane way for userspace to know that.
Fix this by updating <rdma/ib_user_mad.h> to make it clear that
method_mask[] is an array of longs, and using a compat_ioctl method to
convert to an array of 64-bit longs to handle the 32-on-64 problem.
This fixes the interface description to match existing behavior (so
working binaries continue to work) in almost all situations, and gives
consistent semantics in the case of 32-bit userspace that can run on
either a 32-bit or 64-bit kernel, so that the same binary can work for
both 32-on-32 and 32-on-64 systems.
Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
@@ -147,6 +147,26 @@ struct ib_user_mad {
|
||||
__u64 data[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* Earlier versions of this interface definition declared the
|
||||
* method_mask[] member as an array of __u32 but treated it as a
|
||||
* bitmap made up of longs in the kernel. This ambiguity meant that
|
||||
* 32-bit big-endian applications that can run on both 32-bit and
|
||||
* 64-bit kernels had no consistent ABI to rely on, and 64-bit
|
||||
* big-endian applications that treated method_mask as being made up
|
||||
* of 32-bit words would have their bitmap misinterpreted.
|
||||
*
|
||||
* To clear up this confusion, we change the declaration of
|
||||
* method_mask[] to use unsigned long and handle the conversion from
|
||||
* 32-bit userspace to 64-bit kernel for big-endian systems in the
|
||||
* compat_ioctl method. Unfortunately, to keep the structure layout
|
||||
* the same, we need the method_mask[] array to be aligned only to 4
|
||||
* bytes even when long is 64 bits, which forces us into this ugly
|
||||
* typedef.
|
||||
*/
|
||||
typedef unsigned long __attribute__((aligned(4))) packed_ulong;
|
||||
#define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof (long)))
|
||||
|
||||
/**
|
||||
* ib_user_mad_reg_req - MAD registration request
|
||||
* @id - Set by the kernel; used to identify agent in future requests.
|
||||
@@ -165,7 +185,7 @@ struct ib_user_mad {
|
||||
*/
|
||||
struct ib_user_mad_reg_req {
|
||||
__u32 id;
|
||||
__u32 method_mask[4];
|
||||
packed_ulong method_mask[IB_USER_MAD_LONGS_PER_METHOD_MASK];
|
||||
__u8 qpn;
|
||||
__u8 mgmt_class;
|
||||
__u8 mgmt_class_version;
|
||||
|
||||
Reference in New Issue
Block a user