Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=a394f83bdfec10b09d8cb111e622556b2e6fd0de
Commit:     a394f83bdfec10b09d8cb111e622556b2e6fd0de
Parent:     2be8e3ee8efd6f99ce454115c29d09750915021a
Author:     Roland Dreier <[EMAIL PROTECTED]>
AuthorDate: Tue Oct 9 19:59:15 2007 -0700
Committer:  Roland Dreier <[EMAIL PROTECTED]>
CommitDate: Tue Oct 9 19:59:15 2007 -0700

    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 <[EMAIL PROTECTED]>
---
 drivers/infiniband/core/user_mad.c |   49 +++++++++++++++++++++++++++++------
 include/rdma/ib_user_mad.h         |   22 +++++++++++++++-
 2 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/drivers/infiniband/core/user_mad.c 
b/drivers/infiniband/core/user_mad.c
index aee2913..b53eac4 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -44,6 +44,7 @@
 #include <linux/poll.h>
 #include <linux/rwsem.h>
 #include <linux/kref.h>
+#include <linux/compat.h>
 
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
@@ -607,7 +608,8 @@ static unsigned int ib_umad_poll(struct file *filp, struct 
poll_table_struct *wa
        return mask;
 }
 
-static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
+static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
+                            int compat_method_mask)
 {
        struct ib_user_mad_reg_req ureq;
        struct ib_mad_reg_req req;
@@ -622,7 +624,7 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, 
unsigned long arg)
                goto out;
        }
 
-       if (copy_from_user(&ureq, (void __user *) arg, sizeof ureq)) {
+       if (copy_from_user(&ureq, arg, sizeof ureq)) {
                ret = -EFAULT;
                goto out;
        }
@@ -643,8 +645,18 @@ found:
        if (ureq.mgmt_class) {
                req.mgmt_class         = ureq.mgmt_class;
                req.mgmt_class_version = ureq.mgmt_class_version;
-               memcpy(req.method_mask, ureq.method_mask, sizeof 
req.method_mask);
-               memcpy(req.oui,         ureq.oui,         sizeof req.oui);
+               memcpy(req.oui, ureq.oui, sizeof req.oui);
+
+               if (compat_method_mask) {
+                       u32 *umm = (u32 *) ureq.method_mask;
+                       int i;
+
+                       for (i = 0; i < BITS_TO_LONGS(IB_MGMT_MAX_METHODS); ++i)
+                               req.method_mask[i] =
+                                       umm[i * 2] | ((u64) umm[i * 2 + 1] << 
32);
+               } else
+                       memcpy(req.method_mask, ureq.method_mask,
+                              sizeof req.method_mask);
        }
 
        agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num,
@@ -682,13 +694,13 @@ out:
        return ret;
 }
 
-static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
+static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
 {
        struct ib_mad_agent *agent = NULL;
        u32 id;
        int ret = 0;
 
-       if (get_user(id, (u32 __user *) arg))
+       if (get_user(id, arg))
                return -EFAULT;
 
        down_write(&file->port->mutex);
@@ -729,9 +741,9 @@ static long ib_umad_ioctl(struct file *filp, unsigned int 
cmd,
 {
        switch (cmd) {
        case IB_USER_MAD_REGISTER_AGENT:
-               return ib_umad_reg_agent(filp->private_data, arg);
+               return ib_umad_reg_agent(filp->private_data, (void __user *) 
arg, 0);
        case IB_USER_MAD_UNREGISTER_AGENT:
-               return ib_umad_unreg_agent(filp->private_data, arg);
+               return ib_umad_unreg_agent(filp->private_data, (__u32 __user *) 
arg);
        case IB_USER_MAD_ENABLE_PKEY:
                return ib_umad_enable_pkey(filp->private_data);
        default:
@@ -739,6 +751,23 @@ static long ib_umad_ioctl(struct file *filp, unsigned int 
cmd,
        }
 }
 
+#ifdef CONFIG_COMPAT
+static long ib_umad_compat_ioctl(struct file *filp, unsigned int cmd,
+                                unsigned long arg)
+{
+       switch (cmd) {
+       case IB_USER_MAD_REGISTER_AGENT:
+               return ib_umad_reg_agent(filp->private_data, compat_ptr(arg), 
1);
+       case IB_USER_MAD_UNREGISTER_AGENT:
+               return ib_umad_unreg_agent(filp->private_data, compat_ptr(arg));
+       case IB_USER_MAD_ENABLE_PKEY:
+               return ib_umad_enable_pkey(filp->private_data);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+#endif
+
 static int ib_umad_open(struct inode *inode, struct file *filp)
 {
        struct ib_umad_port *port;
@@ -826,7 +855,9 @@ static const struct file_operations umad_fops = {
        .write          = ib_umad_write,
        .poll           = ib_umad_poll,
        .unlocked_ioctl = ib_umad_ioctl,
-       .compat_ioctl   = ib_umad_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ib_umad_compat_ioctl,
+#endif
        .open           = ib_umad_open,
        .release        = ib_umad_close
 };
diff --git a/include/rdma/ib_user_mad.h b/include/rdma/ib_user_mad.h
index 2a32043..29d2c72 100644
--- a/include/rdma/ib_user_mad.h
+++ b/include/rdma/ib_user_mad.h
@@ -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;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to