UBI: handle attach ioctl
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tue, 18 Dec 2007 16:23:39 +0000 (18:23 +0200)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Wed, 26 Dec 2007 17:15:17 +0000 (19:15 +0200)
Actually implement the MTD device attach/detach handlers.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
drivers/mtd/ubi/build.c
drivers/mtd/ubi/cdev.c
drivers/mtd/ubi/ubi.h

index 403c10a668bdcb182be191bafceae4cab3a44ab6..70c0b9a9e6e35ea6b04fdd143f2e3fe9f19c2981 100644 (file)
@@ -621,18 +621,19 @@ static int io_init(struct ubi_device *ubi)
 /**
  * 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 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
         * 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 (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;
 
-       if (i == UBI_MAX_DEVICES) {
-               ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
-               return -ENFILE;
+               /* 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 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
                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 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
                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 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
                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 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
        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 @@ static int __init ubi_init(void)
                }
 
                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);
index 01978b57e9cb99197e632c53ad98284ba69fae07..a60a3a24c2a1d6a21408f04e8a1391ed603064f1 100644 (file)
 #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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
        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,
 };
 
index 4c3607e5743ef5be9b5c541c7ccfb757133d1910..2a6171226f1f36a2a551800633507565cdf0e746 100644 (file)
@@ -483,7 +483,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
                         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);