On Thu, 21 Apr 2011, John Calixto wrote:
<snip>
> +static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
> + struct mmc_ioc_cmd __user *user)
> +{
> + struct mmc_blk_ioc_data *idata;
> + int err;
> +
> + idata = kzalloc(sizeof(*idata), GFP_KERNEL);
> + if (!idata) {
> + err = -ENOMEM;
> + goto copy_err;
> + }
> +
> + if (copy_from_user(&idata->ic, user, sizeof(idata->ic))) {
> + err = -EFAULT;
> + goto copy_err;
> + }
> +
> + idata->buf_bytes = (u64) idata->ic.blksz * idata->ic.blocks;
> + if (idata->buf_bytes > MMC_IOC_MAX_BYTES) {
> + err = -EOVERFLOW;
> + goto copy_err;
> + }
> +
> + idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
> + if (!idata->buf) {
> + err = -ENOMEM;
> + goto copy_err;
> + }
> +
Per Arnd's recommendation, I just cast the ``data_ptr`` to
``(void *)(unsigned long)`` to solve the compatibility issue with the
buffer pointer in struct mmc_ioc_cmd.
> + if (copy_from_user(idata->buf, (void __user *)(unsigned long)
> + idata->ic.data_ptr, idata->buf_bytes)) {
> + err = -EFAULT;
> + goto copy_err;
> + }
> +
> + return idata;
> +
> +copy_err:
> + kfree(idata->buf);
> + kfree(idata);
> + return ERR_PTR(err);
> +
> +}
> +
<snip>
I also left mmc_blk_ioctl() and mmc_blk_compat_ioctl() as they were in
v6. IMHO, a __mmc_blk_ioctl() function that accepts a compat32 boolean
arg does not do much to enhance readability or functionality in this
case. With the implementation in the 2 functions below, I intended to
make it clear that:
- mmc_blk_ioctl() is responsible for delegating to the appropriate
handler based on the incoming ioctl cmd. The ``if`` would be
replaced with a ``switch`` when adding other values for cmd.
- mmc_blk_compat_ioctl() is responsible for converting the ioctl arg
to something suitable for consumtption by mmc_blk_ioctl().
- when CONFIG_COMPAT is undefined, you get no code related to
compatibility.
> +
> +static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
> + unsigned int cmd, unsigned long arg)
> +{
> + int ret = -EINVAL;
> + if (cmd == MMC_IOC_CMD)
> + ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
> + return ret;
> +}
> +
> +#ifdef CONFIG_COMPAT
> +static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode,
> + unsigned int cmd, unsigned long arg)
> +{
> + return mmc_blk_ioctl(bdev, mode, cmd, (unsigned long) compat_ptr(arg));
> +}
> +#endif
> +
> static const struct block_device_operations mmc_bdops = {
> .open = mmc_blk_open,
> .release = mmc_blk_release,
> .getgeo = mmc_blk_getgeo,
> .owner = THIS_MODULE,
> + .ioctl = mmc_blk_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = mmc_blk_compat_ioctl,
> +#endif
> };
>
<snip>
It makes a lot of sense to me to make sure that struct mmc_ioc_cmd is
the same size on 32-bit and 64-bit machines, since it is used by _IOWR()
to derive the value for MMC_IOC_CMD. Any change in its size requires
having other compatibility logic to deal with a MMC_IOC_CMD32.
Note that as this struct has evolved, I also needed to add a ``__pad``
member to the struct to achieve the goal of keeping it the same size on
32-bit and 64-bit. I could have used packing or alignment compiler
directives, but this seemed simplest to me (I can add up the bytes in my
head!):
> diff --git a/include/linux/mmc/ioctl.h b/include/linux/mmc/ioctl.h
> new file mode 100644
> index 0000000..225e1e1
> --- /dev/null
> +++ b/include/linux/mmc/ioctl.h
> @@ -0,0 +1,54 @@
> +#ifndef LINUX_MMC_IOCTL_H
> +#define LINUX_MMC_IOCTL_H
> +struct mmc_ioc_cmd {
> + /* Implies direction of data. true = write, false = read */
> + int write_flag;
> +
> + /* Application-specific command. true = precede with CMD55 */
> + int is_acmd;
> +
> + __u32 opcode;
> + __u32 arg;
> + __u32 response[4]; /* CMD response */
> + unsigned int flags;
> + unsigned int blksz;
> + unsigned int blocks;
> +
> + /*
> + * Sleep at least postsleep_min_us useconds, and at most
> + * postsleep_max_us useconds *after* issuing command. Needed for some
> + * read commands for which cards have no other way of indicating
> + * they're ready for the next command (i.e. there is no equivalent of a
> + * "busy" indicator for read operations).
> + */
> + unsigned int postsleep_min_us;
> + unsigned int postsleep_max_us;
> +
> + /*
> + * Override driver-computed timeouts. Note the difference in units!
> + */
> + unsigned int data_timeout_ns;
> + unsigned int cmd_timeout_ms;
> +
> + /*
> + * For 64-bit machines, the next member, ``__u64 data_ptr``, wants to
> + * be 8-byte aligned. Make sure this struct is the same size when
> + * built for 32-bit.
> + */
> + __u32 __pad;
> +
> + /* DAT buffer */
> + __u64 data_ptr;
> +};
To help with the casting in 32-bit userspace, I also provided
``mmc_ioc_cmd_set_data(ic, ptr)`` as a helper macro:
> +#define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long)
> ptr
> +
> +#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
> +
> +/*
> + * Since this ioctl is only meant to enhance (and not replace) normal access
> to
> + * the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES is
> + * enforced per ioctl call. For larger data transfers, use the normal block
> + * device operations.
> + */
> +#define MMC_IOC_MAX_BYTES (512L * 256)
> +#endif /* LINUX_MMC_IOCTL_H */
> --
> 1.7.4.1
>
John
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html