Commit e73f4459 authored by Artem Bityutskiy's avatar Artem Bityutskiy
Browse files

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: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent 9f961b57
Loading
Loading
Loading
Loading
+127 −15
Original line number Diff line number Diff line
@@ -64,9 +64,6 @@ static int mtd_devs = 0;
/* MTD devices specification parameters */
static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];

/* All UBI devices in system */
struct ubi_device *ubi_devices[UBI_MAX_DEVICES];

/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
struct class *ubi_class;

@@ -83,6 +80,12 @@ static struct miscdevice ubi_ctrl_cdev = {
	.fops = &ubi_ctrl_cdev_operations,
};

/* All UBI devices in system */
static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];

/* Protects @ubi_devices and @ubi->ref_count */
static DEFINE_SPINLOCK(ubi_devices_lock);

/* "Show" method for files in '/<sysfs>/class/ubi/' */
static ssize_t ubi_version_show(struct class *class, char *buf)
{
@@ -118,37 +121,145 @@ static struct device_attribute dev_min_io_size =
static struct device_attribute dev_bgt_enabled =
	__ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);

/**
 * ubi_get_device - get UBI device.
 * @ubi_num: UBI device number
 *
 * This function returns UBI device description object for UBI device number
 * @ubi_num, or %NULL if the device does not exist. This function increases the
 * device reference count to prevent removal of the device. In other words, the
 * device cannot be removed if its reference count is not zero.
 */
struct ubi_device *ubi_get_device(int ubi_num)
{
	struct ubi_device *ubi;

	spin_lock(&ubi_devices_lock);
	ubi = ubi_devices[ubi_num];
	if (ubi) {
		ubi_assert(ubi->ref_count >= 0);
		ubi->ref_count += 1;
		get_device(&ubi->dev);
	}
	spin_unlock(&ubi_devices_lock);

	return ubi;
}

/**
 * ubi_put_device - drop an UBI device reference.
 * @ubi: UBI device description object
 */
void ubi_put_device(struct ubi_device *ubi)
{
	spin_lock(&ubi_devices_lock);
	ubi->ref_count -= 1;
	put_device(&ubi->dev);
	spin_unlock(&ubi_devices_lock);
}

/**
 * ubi_get_by_major - get UBI device description object by character device
 *                    major number.
 * @major: major number
 *
 * This function is similar to 'ubi_get_device()', but it searches the device
 * by its major number.
 */
struct ubi_device *ubi_get_by_major(int major)
{
	int i;
	struct ubi_device *ubi;

	spin_lock(&ubi_devices_lock);
	for (i = 0; i < UBI_MAX_DEVICES; i++) {
		ubi = ubi_devices[i];
		if (ubi && MAJOR(ubi->cdev.dev) == major) {
			ubi_assert(ubi->ref_count >= 0);
			ubi->ref_count += 1;
			get_device(&ubi->dev);
			spin_unlock(&ubi_devices_lock);
			return ubi;
		}
	}
	spin_unlock(&ubi_devices_lock);

	return NULL;
}

/**
 * ubi_major2num - get UBI device number by character device major number.
 * @major: major number
 *
 * This function searches UBI device number object by its major number. If UBI
 * device was not found, this function returns -ENODEV, othewise the UBI device
 * number is returned.
 */
int ubi_major2num(int major)
{
	int i, ubi_num = -ENODEV;

	spin_lock(&ubi_devices_lock);
	for (i = 0; i < UBI_MAX_DEVICES; i++) {
		struct ubi_device *ubi = ubi_devices[i];

		if (ubi && MAJOR(ubi->cdev.dev) == major) {
			ubi_num = ubi->ubi_num;
			break;
		}
	}
	spin_unlock(&ubi_devices_lock);

	return ubi_num;
}

/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
static ssize_t dev_attribute_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	const struct ubi_device *ubi;
	ssize_t ret;
	struct ubi_device *ubi;

	/*
	 * The below code looks weird, but it actually makes sense. We get the
	 * UBI device reference from the contained 'struct ubi_device'. But it
	 * is unclear if the device was removed or not yet. Indeed, if the
	 * device was removed before we increased its reference count,
	 * 'ubi_get_device()' will return -ENODEV and we fail.
	 *
	 * Remember, 'struct ubi_device' is freed in the release function, so
	 * we still can use 'ubi->ubi_num'.
	 */
	ubi = container_of(dev, struct ubi_device, dev);
	ubi = ubi_get_device(ubi->ubi_num);
	if (!ubi)
		return -ENODEV;

	if (attr == &dev_eraseblock_size)
		return sprintf(buf, "%d\n", ubi->leb_size);
		ret = sprintf(buf, "%d\n", ubi->leb_size);
	else if (attr == &dev_avail_eraseblocks)
		return sprintf(buf, "%d\n", ubi->avail_pebs);
		ret = sprintf(buf, "%d\n", ubi->avail_pebs);
	else if (attr == &dev_total_eraseblocks)
		return sprintf(buf, "%d\n", ubi->good_peb_count);
		ret = sprintf(buf, "%d\n", ubi->good_peb_count);
	else if (attr == &dev_volumes_count)
		return sprintf(buf, "%d\n", ubi->vol_count);
		ret = sprintf(buf, "%d\n", ubi->vol_count);
	else if (attr == &dev_max_ec)
		return sprintf(buf, "%d\n", ubi->max_ec);
		ret = sprintf(buf, "%d\n", ubi->max_ec);
	else if (attr == &dev_reserved_for_bad)
		return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
		ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
	else if (attr == &dev_bad_peb_count)
		return sprintf(buf, "%d\n", ubi->bad_peb_count);
		ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
	else if (attr == &dev_max_vol_count)
		return sprintf(buf, "%d\n", ubi->vtbl_slots);
		ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
	else if (attr == &dev_min_io_size)
		return sprintf(buf, "%d\n", ubi->min_io_size);
		ret = sprintf(buf, "%d\n", ubi->min_io_size);
	else if (attr == &dev_bgt_enabled)
		return sprintf(buf, "%d\n", ubi->thread_enabled);
		ret = sprintf(buf, "%d\n", ubi->thread_enabled);
	else
		BUG();

	return 0;
	ubi_put_device(ubi);
	return ret;
}

/* Fake "release" method for UBI devices */
@@ -670,6 +781,7 @@ static void detach_mtd_dev(struct ubi_device *ubi)
	int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index;

	dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
	ubi_assert(ubi->ref_count == 0);
	uif_close(ubi);
	ubi_eba_close(ubi);
	ubi_wl_close(ubi);
+10 −24
Original line number Diff line number Diff line
@@ -55,23 +55,6 @@
#define VOL_CDEV_IOC_MAX_SEQ 2
#endif

/**
 * major_to_device - get UBI device object by character device major number.
 * @major: major number
 *
 * This function returns a pointer to the UBI device object.
 */
static struct ubi_device *major_to_device(int major)
{
	int i;

	for (i = 0; i < UBI_MAX_DEVICES; i++)
		if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major)
			return ubi_devices[i];
	BUG();
	return NULL;
}

/**
 * get_exclusive - get exclusive access to an UBI volume.
 * @desc: volume descriptor
@@ -129,9 +112,11 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
static int vol_cdev_open(struct inode *inode, struct file *file)
{
	struct ubi_volume_desc *desc;
	const struct ubi_device *ubi = major_to_device(imajor(inode));
	int vol_id = iminor(inode) - 1;
	int mode;
	int vol_id = iminor(inode) - 1, mode, ubi_num;

	ubi_num = ubi_major2num(imajor(inode));
	if (ubi_num < 0)
		return ubi_num;

	if (file->f_mode & FMODE_WRITE)
		mode = UBI_READWRITE;
@@ -140,7 +125,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file)

	dbg_msg("open volume %d, mode %d", vol_id, mode);

	desc = ubi_open_volume(ubi->ubi_num, vol_id, mode);
	desc = ubi_open_volume(ubi_num, vol_id, mode);
	if (IS_ERR(desc))
		return PTR_ERR(desc);

@@ -586,9 +571,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
	if (!capable(CAP_SYS_RESOURCE))
		return -EPERM;

	ubi = major_to_device(imajor(inode));
	if (IS_ERR(ubi))
		return PTR_ERR(ubi);
	ubi = ubi_get_by_major(imajor(inode));
	if (!ubi)
		return -ENODEV;

	switch (cmd) {
	/* Create volume command */
@@ -695,6 +680,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
		break;
	}

	ubi_put_device(ubi);
	return err;
}

+5 −0
Original line number Diff line number Diff line
@@ -339,6 +339,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
{
	int err, pnum, vol_id = vol->vol_id;

	ubi_assert(ubi->ref_count > 0);
	ubi_assert(vol->ref_count > 0);

	if (ubi->ro_mode)
@@ -389,6 +390,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
	struct ubi_vid_hdr *vid_hdr;
	uint32_t uninitialized_var(crc);

	ubi_assert(ubi->ref_count > 0);
	ubi_assert(vol->ref_count > 0);

	err = leb_read_lock(ubi, vol_id, lnum);
@@ -614,6 +616,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
	int err, pnum, tries = 0, vol_id = vol->vol_id;
	struct ubi_vid_hdr *vid_hdr;

	ubi_assert(ubi->ref_count > 0);
	ubi_assert(vol->ref_count > 0);

	if (ubi->ro_mode)
@@ -749,6 +752,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
	struct ubi_vid_hdr *vid_hdr;
	uint32_t crc;

	ubi_assert(ubi->ref_count > 0);
	ubi_assert(vol->ref_count > 0);

	if (ubi->ro_mode)
@@ -865,6 +869,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
	struct ubi_vid_hdr *vid_hdr;
	uint32_t crc;

	ubi_assert(ubi->ref_count > 0);
	ubi_assert(vol->ref_count > 0);

	if (ubi->ro_mode)
+41 −18
Original line number Diff line number Diff line
@@ -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 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
	    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 @@ out_unlock:
	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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
{
	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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
	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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
	}
	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 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
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 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
		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);
+8 −1
Original line number Diff line number Diff line
@@ -245,6 +245,7 @@ struct ubi_wl_entry;
 *                @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count,
 *                @vol->readers, @vol->writers, @vol->exclusive,
 *                @vol->ref_count, @vol->mapping and @vol->eba_tbl.
 * @ref_count: count of references on the UBI device
 *
 * @rsvd_pebs: count of reserved physical eraseblocks
 * @avail_pebs: count of available physical eraseblocks
@@ -325,6 +326,7 @@ struct ubi_device {
	int vol_count;
	struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
	spinlock_t volumes_lock;
	int ref_count;

	int rsvd_pebs;
	int avail_pebs;
@@ -401,7 +403,6 @@ extern struct kmem_cache *ubi_wl_entry_slab;
extern struct file_operations ubi_ctrl_cdev_operations;
extern struct file_operations ubi_cdev_operations;
extern struct file_operations ubi_vol_cdev_operations;
extern struct ubi_device *ubi_devices[];
extern struct class *ubi_class;

/* vtbl.c */
@@ -479,6 +480,12 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
			 struct ubi_vid_hdr *vid_hdr);

/* build.c */
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);
struct ubi_device *ubi_get_by_major(int major);
int ubi_major2num(int major);

/*
 * ubi_rb_for_each_entry - walk an RB-tree.
 * @rb: a pointer to type 'struct rb_node' to to use as a loop counter
Loading