UBI: add UBI devices reference counting

This is one more step on the way to "removable" UBI devices. It
adds reference counting for UBI devices. Every time a volume on
this device is opened - the device's refcount is increased. It
is also increased if someone is reading any sysfs file of this
UBI device or of one of its volumes.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 780c273..4ec3a33 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -30,23 +30,27 @@
  * @ubi_num: UBI device number
  * @di: the information is stored here
  *
- * This function returns %0 in case of success and a %-ENODEV if there is no
- * such UBI device.
+ * This function returns %0 in case of success, %-EINVAL if the UBI device
+ * number is invalid, and %-ENODEV if there is no such UBI device.
  */
 int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
 {
-	const struct ubi_device *ubi;
+	struct ubi_device *ubi;
 
-	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES ||
-	    !ubi_devices[ubi_num])
+	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
+		return -EINVAL;
+
+	ubi = ubi_get_device(ubi_num);
+	if (!ubi)
 		return -ENODEV;
 
-	ubi = ubi_devices[ubi_num];
 	di->ubi_num = ubi->ubi_num;
 	di->leb_size = ubi->leb_size;
 	di->min_io_size = ubi->min_io_size;
 	di->ro_mode = ubi->ro_mode;
 	di->cdev = ubi->cdev.dev;
+
+	ubi_put_device(ubi);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(ubi_get_device_info);
@@ -111,16 +115,23 @@
 	    mode != UBI_EXCLUSIVE)
 		return ERR_PTR(-EINVAL);
 
-	ubi = ubi_devices[ubi_num];
+	/*
+	 * First of all, we have to get the UBI device to prevent its removal.
+	 */
+	ubi = ubi_get_device(ubi_num);
 	if (!ubi)
 		return ERR_PTR(-ENODEV);
 
-	if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
-		return ERR_PTR(-EINVAL);
+	if (vol_id < 0 || vol_id >= ubi->vtbl_slots) {
+		err = -EINVAL;
+		goto out_put_ubi;
+	}
 
 	desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL);
-	if (!desc)
-		return ERR_PTR(-ENOMEM);
+	if (!desc) {
+		err = -ENOMEM;
+		goto out_put_ubi;
+	}
 
 	err = -ENODEV;
 	if (!try_module_get(THIS_MODULE))
@@ -188,6 +199,8 @@
 	module_put(THIS_MODULE);
 out_free:
 	kfree(desc);
+out_put_ubi:
+	ubi_put_device(ubi);
 	return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(ubi_open_volume);
@@ -205,6 +218,7 @@
 {
 	int i, vol_id = -1, len;
 	struct ubi_device *ubi;
+	struct ubi_volume_desc *ret;
 
 	dbg_msg("open volume %s, mode %d", name, mode);
 
@@ -218,7 +232,7 @@
 	if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
 		return ERR_PTR(-EINVAL);
 
-	ubi = ubi_devices[ubi_num];
+	ubi = ubi_get_device(ubi_num);
 	if (!ubi)
 		return ERR_PTR(-ENODEV);
 
@@ -234,10 +248,17 @@
 	}
 	spin_unlock(&ubi->volumes_lock);
 
-	if (vol_id < 0)
-		return ERR_PTR(-ENODEV);
+	if (vol_id >= 0)
+		ret = ubi_open_volume(ubi_num, vol_id, mode);
+	else
+		ret = ERR_PTR(-ENODEV);
 
-	return ubi_open_volume(ubi_num, vol_id, mode);
+	/*
+	 * We should put the UBI device even in case of success, because
+	 * 'ubi_open_volume()' took a reference as well.
+	 */
+	ubi_put_device(ubi);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
 
@@ -248,10 +269,11 @@
 void ubi_close_volume(struct ubi_volume_desc *desc)
 {
 	struct ubi_volume *vol = desc->vol;
+	struct ubi_device *ubi = vol->ubi;
 
 	dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
 
-	spin_lock(&vol->ubi->volumes_lock);
+	spin_lock(&ubi->volumes_lock);
 	switch (desc->mode) {
 	case UBI_READONLY:
 		vol->readers -= 1;
@@ -263,10 +285,11 @@
 		vol->exclusive = 0;
 	}
 	vol->ref_count -= 1;
-	spin_unlock(&vol->ubi->volumes_lock);
+	spin_unlock(&ubi->volumes_lock);
 
-	put_device(&vol->dev);
 	kfree(desc);
+	put_device(&vol->dev);
+	ubi_put_device(ubi);
 	module_put(THIS_MODULE);
 }
 EXPORT_SYMBOL_GPL(ubi_close_volume);