diff options
author | Roland Dreier <rolandd@cisco.com> | 2007-10-09 22:59:15 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-10-09 22:59:15 -0400 |
commit | a394f83bdfec10b09d8cb111e622556b2e6fd0de (patch) | |
tree | bc9735ed3ccda810634173f01528741cefc71a6c /drivers/infiniband | |
parent | 2be8e3ee8efd6f99ce454115c29d09750915021a (diff) |
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>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/user_mad.c | 49 |
1 files changed, 40 insertions, 9 deletions
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index aee29139368c..b53eac4611de 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <linux/poll.h> | 44 | #include <linux/poll.h> |
45 | #include <linux/rwsem.h> | 45 | #include <linux/rwsem.h> |
46 | #include <linux/kref.h> | 46 | #include <linux/kref.h> |
47 | #include <linux/compat.h> | ||
47 | 48 | ||
48 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
49 | #include <asm/semaphore.h> | 50 | #include <asm/semaphore.h> |
@@ -607,7 +608,8 @@ static unsigned int ib_umad_poll(struct file *filp, struct poll_table_struct *wa | |||
607 | return mask; | 608 | return mask; |
608 | } | 609 | } |
609 | 610 | ||
610 | static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg) | 611 | static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg, |
612 | int compat_method_mask) | ||
611 | { | 613 | { |
612 | struct ib_user_mad_reg_req ureq; | 614 | struct ib_user_mad_reg_req ureq; |
613 | struct ib_mad_reg_req req; | 615 | struct ib_mad_reg_req req; |
@@ -622,7 +624,7 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg) | |||
622 | goto out; | 624 | goto out; |
623 | } | 625 | } |
624 | 626 | ||
625 | if (copy_from_user(&ureq, (void __user *) arg, sizeof ureq)) { | 627 | if (copy_from_user(&ureq, arg, sizeof ureq)) { |
626 | ret = -EFAULT; | 628 | ret = -EFAULT; |
627 | goto out; | 629 | goto out; |
628 | } | 630 | } |
@@ -643,8 +645,18 @@ found: | |||
643 | if (ureq.mgmt_class) { | 645 | if (ureq.mgmt_class) { |
644 | req.mgmt_class = ureq.mgmt_class; | 646 | req.mgmt_class = ureq.mgmt_class; |
645 | req.mgmt_class_version = ureq.mgmt_class_version; | 647 | req.mgmt_class_version = ureq.mgmt_class_version; |
646 | memcpy(req.method_mask, ureq.method_mask, sizeof req.method_mask); | 648 | memcpy(req.oui, ureq.oui, sizeof req.oui); |
647 | memcpy(req.oui, ureq.oui, sizeof req.oui); | 649 | |
650 | if (compat_method_mask) { | ||
651 | u32 *umm = (u32 *) ureq.method_mask; | ||
652 | int i; | ||
653 | |||
654 | for (i = 0; i < BITS_TO_LONGS(IB_MGMT_MAX_METHODS); ++i) | ||
655 | req.method_mask[i] = | ||
656 | umm[i * 2] | ((u64) umm[i * 2 + 1] << 32); | ||
657 | } else | ||
658 | memcpy(req.method_mask, ureq.method_mask, | ||
659 | sizeof req.method_mask); | ||
648 | } | 660 | } |
649 | 661 | ||
650 | agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num, | 662 | agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num, |
@@ -682,13 +694,13 @@ out: | |||
682 | return ret; | 694 | return ret; |
683 | } | 695 | } |
684 | 696 | ||
685 | static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg) | 697 | static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg) |
686 | { | 698 | { |
687 | struct ib_mad_agent *agent = NULL; | 699 | struct ib_mad_agent *agent = NULL; |
688 | u32 id; | 700 | u32 id; |
689 | int ret = 0; | 701 | int ret = 0; |
690 | 702 | ||
691 | if (get_user(id, (u32 __user *) arg)) | 703 | if (get_user(id, arg)) |
692 | return -EFAULT; | 704 | return -EFAULT; |
693 | 705 | ||
694 | down_write(&file->port->mutex); | 706 | down_write(&file->port->mutex); |
@@ -729,9 +741,9 @@ static long ib_umad_ioctl(struct file *filp, unsigned int cmd, | |||
729 | { | 741 | { |
730 | switch (cmd) { | 742 | switch (cmd) { |
731 | case IB_USER_MAD_REGISTER_AGENT: | 743 | case IB_USER_MAD_REGISTER_AGENT: |
732 | return ib_umad_reg_agent(filp->private_data, arg); | 744 | return ib_umad_reg_agent(filp->private_data, (void __user *) arg, 0); |
733 | case IB_USER_MAD_UNREGISTER_AGENT: | 745 | case IB_USER_MAD_UNREGISTER_AGENT: |
734 | return ib_umad_unreg_agent(filp->private_data, arg); | 746 | return ib_umad_unreg_agent(filp->private_data, (__u32 __user *) arg); |
735 | case IB_USER_MAD_ENABLE_PKEY: | 747 | case IB_USER_MAD_ENABLE_PKEY: |
736 | return ib_umad_enable_pkey(filp->private_data); | 748 | return ib_umad_enable_pkey(filp->private_data); |
737 | default: | 749 | default: |
@@ -739,6 +751,23 @@ static long ib_umad_ioctl(struct file *filp, unsigned int cmd, | |||
739 | } | 751 | } |
740 | } | 752 | } |
741 | 753 | ||
754 | #ifdef CONFIG_COMPAT | ||
755 | static long ib_umad_compat_ioctl(struct file *filp, unsigned int cmd, | ||
756 | unsigned long arg) | ||
757 | { | ||
758 | switch (cmd) { | ||
759 | case IB_USER_MAD_REGISTER_AGENT: | ||
760 | return ib_umad_reg_agent(filp->private_data, compat_ptr(arg), 1); | ||
761 | case IB_USER_MAD_UNREGISTER_AGENT: | ||
762 | return ib_umad_unreg_agent(filp->private_data, compat_ptr(arg)); | ||
763 | case IB_USER_MAD_ENABLE_PKEY: | ||
764 | return ib_umad_enable_pkey(filp->private_data); | ||
765 | default: | ||
766 | return -ENOIOCTLCMD; | ||
767 | } | ||
768 | } | ||
769 | #endif | ||
770 | |||
742 | static int ib_umad_open(struct inode *inode, struct file *filp) | 771 | static int ib_umad_open(struct inode *inode, struct file *filp) |
743 | { | 772 | { |
744 | struct ib_umad_port *port; | 773 | struct ib_umad_port *port; |
@@ -826,7 +855,9 @@ static const struct file_operations umad_fops = { | |||
826 | .write = ib_umad_write, | 855 | .write = ib_umad_write, |
827 | .poll = ib_umad_poll, | 856 | .poll = ib_umad_poll, |
828 | .unlocked_ioctl = ib_umad_ioctl, | 857 | .unlocked_ioctl = ib_umad_ioctl, |
829 | .compat_ioctl = ib_umad_ioctl, | 858 | #ifdef CONFIG_COMPAT |
859 | .compat_ioctl = ib_umad_compat_ioctl, | ||
860 | #endif | ||
830 | .open = ib_umad_open, | 861 | .open = ib_umad_open, |
831 | .release = ib_umad_close | 862 | .release = ib_umad_close |
832 | }; | 863 | }; |