UBI: handle attach ioctl
Actually implement the MTD device attach/detach handlers.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 403c10a..70c0b9a 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -621,18 +621,19 @@
/**
* ubi_attach_mtd_dev - attach an MTD device.
* @mtd_dev: MTD device description object
+ * @ubi_num: number to assign to the new UBI device
* @vid_hdr_offset: VID header offset
*
- * This function attaches an MTD device to UBI. It first treats @mtd_dev as the
- * MTD device name, and tries to open it by this name. If it is unable to open,
- * it tries to convert @mtd_dev to an integer and open the MTD device by its
- * number. Returns new UBI device's number in case of success and a negative
- * error code in case of failure.
+ * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
+ * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
+ * which case this function finds a vacant device nubert and assings it
+ * automatically. Returns the new UBI device number in case of success and a
+ * negative error code in case of failure.
*
* Note, the invocations of this function has to be serialized by the
* @ubi_devices_mutex.
*/
-int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
{
struct ubi_device *ubi;
int i, err;
@@ -643,22 +644,47 @@
* Note, this function assumes that UBI devices creations and deletions
* are serialized, so it does not take the &ubi_devices_lock.
*/
- for (i = 0; i < UBI_MAX_DEVICES; i++)
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
ubi = ubi_devices[i];
if (ubi && mtd->index == ubi->mtd->index) {
- ubi_err("mtd%d is already attached to ubi%d",
+ dbg_err("mtd%d is already attached to ubi%d",
mtd->index, i);
- return -EINVAL;
+ return -EEXIST;
}
+ }
- /* Search for an empty slot in the @ubi_devices array */
- for (i = 0; i < UBI_MAX_DEVICES; i++)
- if (!ubi_devices[i])
- break;
+ /*
+ * Make sure this MTD device is not emulated on top of an UBI volume
+ * already. Well, generally this recursion works fine, but there are
+ * different problems like the UBI module takes a reference to itself
+ * by attaching (and thus, opening) the emulated MTD device. This
+ * results in inability to unload the module. And in general it makes
+ * no sense to attach emulated MTD devices, so we prohibit this.
+ */
+ if (mtd->type == MTD_UBIVOLUME) {
+ ubi_err("refuse attaching mtd%d - it is already emulated on "
+ "top of UBI", mtd->index);
+ return -EINVAL;
+ }
- if (i == UBI_MAX_DEVICES) {
- ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
- return -ENFILE;
+ if (ubi_num == UBI_DEV_NUM_AUTO) {
+ /* Search for an empty slot in the @ubi_devices array */
+ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++)
+ if (!ubi_devices[ubi_num])
+ break;
+ if (ubi_num == UBI_MAX_DEVICES) {
+ dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
+ return -ENFILE;
+ }
+ } else {
+ if (ubi_num >= UBI_MAX_DEVICES)
+ return -EINVAL;
+
+ /* Make sure ubi_num is not busy */
+ if (ubi_devices[ubi_num]) {
+ dbg_err("ubi%d already exists", ubi_num);
+ return -EEXIST;
+ }
}
ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL);
@@ -666,11 +692,11 @@
return -ENOMEM;
ubi->mtd = mtd;
- ubi->ubi_num = i;
+ ubi->ubi_num = ubi_num;
ubi->vid_hdr_offset = vid_hdr_offset;
dbg_msg("attaching mtd%d to ubi%d: VID header offset %d",
- mtd->index, ubi->ubi_num, vid_hdr_offset);
+ mtd->index, ubi_num, vid_hdr_offset);
err = io_init(ubi);
if (err)
@@ -710,7 +736,7 @@
goto out_uif;
}
- ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi->ubi_num);
+ ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
ubi_msg("MTD device name: \"%s\"", mtd->name);
ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
@@ -739,8 +765,8 @@
wake_up_process(ubi->bgt_thread);
}
- ubi_devices[ubi->ubi_num] = ubi;
- return ubi->ubi_num;
+ ubi_devices[ubi_num] = ubi;
+ return ubi_num;
out_uif:
uif_close(ubi);
@@ -781,23 +807,24 @@
spin_lock(&ubi_devices_lock);
ubi = ubi_devices[ubi_num];
if (!ubi) {
- spin_lock(&ubi_devices_lock);
+ spin_unlock(&ubi_devices_lock);
return -EINVAL;
}
if (ubi->ref_count) {
if (!anyway) {
- spin_lock(&ubi_devices_lock);
+ spin_unlock(&ubi_devices_lock);
return -EBUSY;
}
/* This may only happen if there is a bug */
ubi_err("%s reference count %d, destroy anyway",
ubi->ubi_name, ubi->ref_count);
}
- ubi_devices[ubi->ubi_num] = NULL;
+ ubi_devices[ubi_num] = NULL;
spin_unlock(&ubi_devices_lock);
- dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi->ubi_num);
+ ubi_assert(ubi_num == ubi->ubi_num);
+ dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
/*
* Before freeing anything, we have to stop the background thread to
@@ -935,7 +962,8 @@
}
mutex_lock(&ubi_devices_mutex);
- err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs);
+ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO,
+ p->vid_hdr_offs);
mutex_unlock(&ubi_devices_mutex);
if (err < 0) {
put_mtd_device(mtd);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 01978b5..a60a3a2 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -44,17 +44,6 @@
#include <asm/div64.h>
#include "ubi.h"
-/*
- * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
- * logical eraseblock erase is a debug-only feature).
- */
-#define UBI_CDEV_IOC_MAX_SEQ 2
-#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
-#define VOL_CDEV_IOC_MAX_SEQ 1
-#else
-#define VOL_CDEV_IOC_MAX_SEQ 2
-#endif
-
/**
* get_exclusive - get exclusive access to an UBI volume.
* @desc: volume descriptor
@@ -582,8 +571,7 @@
struct ubi_mkvol_req req;
dbg_msg("create volume");
- err = copy_from_user(&req, argp,
- sizeof(struct ubi_mkvol_req));
+ err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req));
if (err) {
err = -EFAULT;
break;
@@ -647,8 +635,7 @@
struct ubi_rsvol_req req;
dbg_msg("re-size volume");
- err = copy_from_user(&req, argp,
- sizeof(struct ubi_rsvol_req));
+ err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req));
if (err) {
err = -EFAULT;
break;
@@ -684,8 +671,86 @@
return err;
}
+static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ switch (cmd) {
+ /* Attach an MTD device command */
+ case UBI_IOCATT:
+ {
+ struct ubi_attach_req req;
+ struct mtd_info *mtd;
+
+ dbg_msg("attach MTD device");
+ err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req));
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (req.mtd_num < 0 ||
+ (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) {
+ err = -EINVAL;
+ break;
+ }
+
+ mtd = get_mtd_device(NULL, req.mtd_num);
+ if (IS_ERR(mtd)) {
+ err = PTR_ERR(mtd);
+ break;
+ }
+
+ /*
+ * Note, further request verification is done by
+ * 'ubi_attach_mtd_dev()'.
+ */
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset);
+ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0)
+ put_mtd_device(mtd);
+ else
+ /* @err contains UBI device number */
+ err = put_user(err, (__user int32_t *)argp);
+
+ break;
+ }
+
+ /* Detach an MTD device command */
+ case UBI_IOCDET:
+ {
+ int ubi_num;
+
+ dbg_msg("dettach MTD device");
+ err = get_user(ubi_num, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_detach_mtd_dev(ubi_num, 0);
+ mutex_unlock(&ubi_devices_mutex);
+ break;
+ }
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
/* UBI control character device operations */
struct file_operations ubi_ctrl_cdev_operations = {
+ .ioctl = ctrl_cdev_ioctl,
.owner = THIS_MODULE,
};
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 4c3607e..2a61712 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -483,7 +483,7 @@
struct ubi_vid_hdr *vid_hdr);
/* build.c */
-int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset);
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset);
int ubi_detach_mtd_dev(int ubi_num, int anyway);
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);