2009-04-03 00:51:06

by Kevin Cernekee

[permalink] [raw]
Subject: [PATCHv5] MTD: New ioctl calls for >4GiB device support

Extend the MTD user ABI to access >4GiB devices using 64-bit offsets.

New ioctls: MEMERASE64 MEMWRITEOOB64 MEMREADOOB64 MEMLOCK64 MEMUNLOCK64

Delta from v4 @ http://tinyurl.com/d6g2ee :

1) Remove MEMGETINFO64, MEMGETREGIONINFO64, in the hopes that they can
be handled through sysfs instead.

2) Use __u64 to store the MEMREADOOB64/MEMWRITEOOB64 user pointer, so
that no compat_ioctl handler is needed.

Signed-off-by: Kevin Cernekee <[email protected]>
---
drivers/mtd/mtdchar.c | 131 +++++++++++++++++++++++++++++++++++++++--------
fs/compat_ioctl.c | 5 ++
include/mtd/mtd-abi.h | 20 +++++++
include/mtd/mtd-user.h | 2 +
4 files changed, 135 insertions(+), 23 deletions(-)

diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 763d3f0..625dc3b 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -417,6 +417,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
break;

case MEMERASE:
+ case MEMERASE64:
{
struct erase_info *erase;

@@ -427,20 +428,32 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
if (!erase)
ret = -ENOMEM;
else {
- struct erase_info_user einfo;
-
wait_queue_head_t waitq;
DECLARE_WAITQUEUE(wait, current);

init_waitqueue_head(&waitq);

- if (copy_from_user(&einfo, argp,
- sizeof(struct erase_info_user))) {
- kfree(erase);
- return -EFAULT;
+ if (cmd == MEMERASE64) {
+ struct erase_info_user64 einfo64;
+
+ if (copy_from_user(&einfo64, argp,
+ sizeof(struct erase_info_user64))) {
+ kfree(erase);
+ return -EFAULT;
+ }
+ erase->addr = einfo64.start;
+ erase->len = einfo64.length;
+ } else {
+ struct erase_info_user einfo32;
+
+ if (copy_from_user(&einfo32, argp,
+ sizeof(struct erase_info_user))) {
+ kfree(erase);
+ return -EFAULT;
+ }
+ erase->addr = einfo32.start;
+ erase->len = einfo32.length;
}
- erase->addr = einfo.start;
- erase->len = einfo.length;
erase->mtd = mtd;
erase->callback = mtdchar_erase_callback;
erase->priv = (unsigned long)&waitq;
@@ -472,17 +485,38 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
}

case MEMWRITEOOB:
+ case MEMWRITEOOB64:
{
- struct mtd_oob_buf buf;
+ struct mtd_oob_buf64 buf;
struct mtd_oob_ops ops;
- struct mtd_oob_buf __user *user_buf = argp;
uint32_t retlen;
+ uint32_t __user *retp, *usr_ptr;

if(!(file->f_mode & FMODE_WRITE))
return -EPERM;

- if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
- return -EFAULT;
+ if (cmd == MEMWRITEOOB64) {
+ struct mtd_oob_buf64 __user *user_buf = argp;
+
+ if (copy_from_user(&buf, argp,
+ sizeof(struct mtd_oob_buf64)))
+ return -EFAULT;
+ retp = &user_buf->length;
+ usr_ptr = (void __user *)(uintptr_t)buf.usr_ptr;
+ } else {
+ struct mtd_oob_buf __user *user_buf = argp;
+ struct mtd_oob_buf buf32;
+
+ if (copy_from_user(&buf32, argp,
+ sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
+
+ buf.start = buf32.start;
+ buf.length = buf32.length;
+
+ retp = &user_buf->length;
+ usr_ptr = (void __user *)buf32.ptr;
+ }

if (buf.length > 4096)
return -EINVAL;
@@ -490,7 +524,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
else
- ret = access_ok(VERIFY_READ, buf.ptr,
+ ret = access_ok(VERIFY_READ, usr_ptr,
buf.length) ? 0 : EFAULT;

if (ret)
@@ -508,18 +542,18 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
if (!ops.oobbuf)
return -ENOMEM;

- if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
+ if (copy_from_user(ops.oobbuf, usr_ptr, buf.length)) {
kfree(ops.oobbuf);
return -EFAULT;
}

- buf.start &= ~(mtd->oobsize - 1);
+ buf.start &= ~((uint64_t)mtd->oobsize - 1);
ret = mtd->write_oob(mtd, buf.start, &ops);

if (ops.oobretlen > 0xFFFFFFFFU)
ret = -EOVERFLOW;
retlen = ops.oobretlen;
- if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
+ if (copy_to_user(retp, &retlen, sizeof(buf.length)))
ret = -EFAULT;

kfree(ops.oobbuf);
@@ -528,12 +562,35 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
}

case MEMREADOOB:
+ case MEMREADOOB64:
{
- struct mtd_oob_buf buf;
+ struct mtd_oob_buf64 buf;
struct mtd_oob_ops ops;
+ uint32_t __user *retp, *usr_ptr;

- if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
- return -EFAULT;
+ if (cmd == MEMREADOOB64) {
+ struct mtd_oob_buf64 __user *user_buf = argp;
+
+ if (copy_from_user(&buf, user_buf,
+ sizeof(struct mtd_oob_buf64)))
+ return -EFAULT;
+
+ retp = &user_buf->length;
+ usr_ptr = (void __user *)(uintptr_t)buf.usr_ptr;
+ } else {
+ struct mtd_oob_buf __user *user_buf = argp;
+ struct mtd_oob_buf buf32;
+
+ if (copy_from_user(&buf32, user_buf,
+ sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
+ buf.start = buf32.start;
+ buf.length = buf32.length;
+
+ /* MEMREADOOB returned length goes in "start" field */
+ retp = &user_buf->start;
+ usr_ptr = (void __user *)buf32.ptr;
+ }

if (buf.length > 4096)
return -EINVAL;
@@ -541,7 +598,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
if (!mtd->read_oob)
ret = -EOPNOTSUPP;
else
- ret = access_ok(VERIFY_WRITE, buf.ptr,
+ ret = access_ok(VERIFY_WRITE, usr_ptr,
buf.length) ? 0 : -EFAULT;
if (ret)
return ret;
@@ -558,12 +615,12 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
if (!ops.oobbuf)
return -ENOMEM;

- buf.start &= ~(mtd->oobsize - 1);
+ buf.start &= ~((uint64_t)mtd->oobsize - 1);
ret = mtd->read_oob(mtd, buf.start, &ops);

- if (put_user(ops.oobretlen, (uint32_t __user *)argp))
+ if (put_user(ops.oobretlen, retp))
ret = -EFAULT;
- else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
+ else if (ops.oobretlen && copy_to_user(usr_ptr, ops.oobbuf,
ops.oobretlen))
ret = -EFAULT;

@@ -585,6 +642,20 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
break;
}

+ case MEMLOCK64:
+ {
+ struct erase_info_user64 einfo64;
+
+ if (copy_from_user(&einfo64, argp, sizeof(einfo64)))
+ return -EFAULT;
+
+ if (!mtd->lock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->lock(mtd, einfo64.start, einfo64.length);
+ break;
+ }
+
case MEMUNLOCK:
{
struct erase_info_user einfo;
@@ -599,6 +670,20 @@ static int mtd_ioctl(struct inode *inode, struct
file *file,
break;
}

+ case MEMUNLOCK64:
+ {
+ struct erase_info_user64 einfo64;
+
+ if (copy_from_user(&einfo64, argp, sizeof(einfo64)))
+ return -EFAULT;
+
+ if (!mtd->unlock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->unlock(mtd, einfo64.start, einfo64.length);
+ break;
+ }
+
/* Legacy interface */
case MEMGETOOBSEL:
{
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 45e59d3..d7be0ea 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -2434,6 +2434,11 @@ COMPATIBLE_IOCTL(MEMGETREGIONCOUNT)
COMPATIBLE_IOCTL(MEMGETREGIONINFO)
COMPATIBLE_IOCTL(MEMGETBADBLOCK)
COMPATIBLE_IOCTL(MEMSETBADBLOCK)
+COMPATIBLE_IOCTL(MEMERASE64)
+COMPATIBLE_IOCTL(MEMWRITEOOB64)
+COMPATIBLE_IOCTL(MEMREADOOB64)
+COMPATIBLE_IOCTL(MEMLOCK64)
+COMPATIBLE_IOCTL(MEMUNLOCK64)
/* NBD */
ULONG_IOCTL(NBD_SET_SOCK)
ULONG_IOCTL(NBD_SET_BLKSIZE)
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index c6c61cd..6414dc6 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -5,17 +5,31 @@
#ifndef __MTD_ABI_H__
#define __MTD_ABI_H__

+#include <linux/types.h>
+
struct erase_info_user {
uint32_t start;
uint32_t length;
};

+struct erase_info_user64 {
+ __u64 start;
+ __u64 length;
+};
+
struct mtd_oob_buf {
uint32_t start;
uint32_t length;
unsigned char __user *ptr;
};

+struct mtd_oob_buf64 {
+ __u64 start;
+ __u32 res0;
+ __u32 length;
+ __u64 usr_ptr;
+};
+
#define MTD_ABSENT 0
#define MTD_RAM 1
#define MTD_ROM 2
@@ -94,6 +108,12 @@ struct otp_info {
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
#define MTDFILEMODE _IO('M', 19)

+#define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
+#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64)
+#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64)
+#define MEMLOCK64 _IOW('M', 23, struct erase_info_user64)
+#define MEMUNLOCK64 _IOW('M', 24, struct erase_info_user64)
+
/*
* Obsolete legacy interface. Keep it in order not to break userspace
* interfaces
diff --git a/include/mtd/mtd-user.h b/include/mtd/mtd-user.h
index 170ceca..a5ec18e 100644
--- a/include/mtd/mtd-user.h
+++ b/include/mtd/mtd-user.h
@@ -16,4 +16,6 @@ typedef struct region_info_user region_info_t;
typedef struct nand_oobinfo nand_oobinfo_t;
typedef struct nand_ecclayout nand_ecclayout_t;

+typedef struct erase_info_user64 erase_info64_t;
+
#endif /* __MTD_USER_H__ */
--
1.5.6.3


2009-04-03 11:52:41

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCHv5] MTD: New ioctl calls for >4GiB device support

On Friday 03 April 2009, Kevin Cernekee wrote:
> 2) Use __u64 to store the MEMREADOOB64/MEMWRITEOOB64 user pointer, so
> that no compat_ioctl handler is needed.


Thanks for the change,

Acked-by: Arnd Bergmann <[email protected]>