Skip to content

Commit

Permalink
UBI: add UBI devices reference counting
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Artem Bityutskiy authored and Artem Bityutskiy committed Dec 26, 2007
1 parent 9f961b5 commit e73f445
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 63 deletions.
142 changes: 127 additions & 15 deletions drivers/mtd/ubi/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down
34 changes: 10 additions & 24 deletions drivers/mtd/ubi/cdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -695,6 +680,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
break;
}

ubi_put_device(ubi);
return err;
}

Expand Down
5 changes: 5 additions & 0 deletions drivers/mtd/ubi/eba.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit e73f445

Please sign in to comment.