Commit 0d280884 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'linux-next' of git://git.infradead.org/ubi-2.6

* 'linux-next' of git://git.infradead.org/ubi-2.6:
  UBI: allow direct user-space I/O
  UBI: fix resource de-allocation
  UBI: remove unused variable
  UBI: use nicer 64-bit math
  UBI: add ioctl compatibility
  UBI: constify file operations
  UBI: allow all ioctls
  UBI: remove unnecessry header inclusion
  UBI: improve ioctl commentaries
  UBI: add ioctl for is_mapped operation
  UBI: add ioctl for unmap operation
  UBI: add ioctl for map operation
parents 9b4d142d 766fb95b
...@@ -33,16 +33,6 @@ config MTD_UBI_DEBUG_DISABLE_BGT ...@@ -33,16 +33,6 @@ config MTD_UBI_DEBUG_DISABLE_BGT
This option switches the background thread off by default. The thread This option switches the background thread off by default. The thread
may be also be enabled/disabled via UBI sysfs. may be also be enabled/disabled via UBI sysfs.
config MTD_UBI_DEBUG_USERSPACE_IO
bool "Direct user-space write/erase support"
default n
depends on MTD_UBI_DEBUG
help
By default, users cannot directly write and erase individual
eraseblocks of dynamic volumes, and have to use update operation
instead. This option enables this capability - it is very useful for
debugging and testing.
config MTD_UBI_DEBUG_EMULATE_BITFLIPS config MTD_UBI_DEBUG_EMULATE_BITFLIPS
bool "Emulate flash bit-flips" bool "Emulate flash bit-flips"
depends on MTD_UBI_DEBUG depends on MTD_UBI_DEBUG
......
...@@ -263,8 +263,12 @@ static ssize_t dev_attribute_show(struct device *dev, ...@@ -263,8 +263,12 @@ static ssize_t dev_attribute_show(struct device *dev,
return ret; return ret;
} }
/* Fake "release" method for UBI devices */ static void dev_release(struct device *dev)
static void dev_release(struct device *dev) { } {
struct ubi_device *ubi = container_of(dev, struct ubi_device, dev);
kfree(ubi);
}
/** /**
* ubi_sysfs_init - initialize sysfs for an UBI device. * ubi_sysfs_init - initialize sysfs for an UBI device.
...@@ -380,7 +384,7 @@ static void free_user_volumes(struct ubi_device *ubi) ...@@ -380,7 +384,7 @@ static void free_user_volumes(struct ubi_device *ubi)
*/ */
static int uif_init(struct ubi_device *ubi) static int uif_init(struct ubi_device *ubi)
{ {
int i, err, do_free = 0; int i, err;
dev_t dev; dev_t dev;
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
...@@ -427,13 +431,10 @@ static int uif_init(struct ubi_device *ubi) ...@@ -427,13 +431,10 @@ static int uif_init(struct ubi_device *ubi)
out_volumes: out_volumes:
kill_volumes(ubi); kill_volumes(ubi);
do_free = 0;
out_sysfs: out_sysfs:
ubi_sysfs_close(ubi); ubi_sysfs_close(ubi);
cdev_del(&ubi->cdev); cdev_del(&ubi->cdev);
out_unreg: out_unreg:
if (do_free)
free_user_volumes(ubi);
unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err);
return err; return err;
...@@ -947,6 +948,12 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ...@@ -947,6 +948,12 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
if (ubi->bgt_thread) if (ubi->bgt_thread)
kthread_stop(ubi->bgt_thread); kthread_stop(ubi->bgt_thread);
/*
* Get a reference to the device in order to prevent 'dev_release()'
* from freeing @ubi object.
*/
get_device(&ubi->dev);
uif_close(ubi); uif_close(ubi);
ubi_wl_close(ubi); ubi_wl_close(ubi);
free_internal_volumes(ubi); free_internal_volumes(ubi);
...@@ -958,7 +965,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ...@@ -958,7 +965,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
vfree(ubi->dbg_peb_buf); vfree(ubi->dbg_peb_buf);
#endif #endif
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
kfree(ubi); put_device(&ubi->dev);
return 0; return 0;
} }
......
...@@ -40,9 +40,9 @@ ...@@ -40,9 +40,9 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/smp_lock.h> #include <linux/compat.h>
#include <linux/math64.h>
#include <mtd/ubi-user.h> #include <mtd/ubi-user.h>
#include <asm/div64.h>
#include "ubi.h" #include "ubi.h"
/** /**
...@@ -195,7 +195,6 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, ...@@ -195,7 +195,6 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
int err, lnum, off, len, tbuf_size; int err, lnum, off, len, tbuf_size;
size_t count_save = count; size_t count_save = count;
void *tbuf; void *tbuf;
uint64_t tmp;
dbg_gen("read %zd bytes from offset %lld of volume %d", dbg_gen("read %zd bytes from offset %lld of volume %d",
count, *offp, vol->vol_id); count, *offp, vol->vol_id);
...@@ -225,10 +224,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, ...@@ -225,10 +224,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
return -ENOMEM; return -ENOMEM;
len = count > tbuf_size ? tbuf_size : count; len = count > tbuf_size ? tbuf_size : count;
lnum = div_u64_rem(*offp, vol->usable_leb_size, &off);
tmp = *offp;
off = do_div(tmp, vol->usable_leb_size);
lnum = tmp;
do { do {
cond_resched(); cond_resched();
...@@ -263,12 +259,9 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, ...@@ -263,12 +259,9 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
return err ? err : count_save - count; return err ? err : count_save - count;
} }
#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
/* /*
* This function allows to directly write to dynamic UBI volumes, without * This function allows to directly write to dynamic UBI volumes, without
* issuing the volume update operation. Available only as a debugging feature. * issuing the volume update operation.
* Very useful for testing UBI.
*/ */
static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
size_t count, loff_t *offp) size_t count, loff_t *offp)
...@@ -279,7 +272,9 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, ...@@ -279,7 +272,9 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
int lnum, off, len, tbuf_size, err = 0; int lnum, off, len, tbuf_size, err = 0;
size_t count_save = count; size_t count_save = count;
char *tbuf; char *tbuf;
uint64_t tmp;
if (!vol->direct_writes)
return -EPERM;
dbg_gen("requested: write %zd bytes to offset %lld of volume %u", dbg_gen("requested: write %zd bytes to offset %lld of volume %u",
count, *offp, vol->vol_id); count, *offp, vol->vol_id);
...@@ -287,10 +282,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, ...@@ -287,10 +282,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
if (vol->vol_type == UBI_STATIC_VOLUME) if (vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
tmp = *offp; lnum = div_u64_rem(*offp, vol->usable_leb_size, &off);
off = do_div(tmp, vol->usable_leb_size);
lnum = tmp;
if (off & (ubi->min_io_size - 1)) { if (off & (ubi->min_io_size - 1)) {
dbg_err("unaligned position"); dbg_err("unaligned position");
return -EINVAL; return -EINVAL;
...@@ -347,10 +339,6 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, ...@@ -347,10 +339,6 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
return err ? err : count_save - count; return err ? err : count_save - count;
} }
#else
#define vol_cdev_direct_write(file, buf, count, offp) (-EPERM)
#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */
static ssize_t vol_cdev_write(struct file *file, const char __user *buf, static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offp) size_t count, loff_t *offp)
{ {
...@@ -402,8 +390,8 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, ...@@ -402,8 +390,8 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
return count; return count;
} }
static int vol_cdev_ioctl(struct inode *inode, struct file *file, static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
int err = 0; int err = 0;
struct ubi_volume_desc *desc = file->private_data; struct ubi_volume_desc *desc = file->private_data;
...@@ -487,7 +475,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -487,7 +475,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
break; break;
} }
#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
/* Logical eraseblock erasure command */ /* Logical eraseblock erasure command */
case UBI_IOCEBER: case UBI_IOCEBER:
{ {
...@@ -518,13 +505,77 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -518,13 +505,77 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
err = ubi_wl_flush(ubi); err = ubi_wl_flush(ubi);
break; break;
} }
#endif
/* Logical eraseblock map command */
case UBI_IOCEBMAP:
{
struct ubi_map_req req;
err = copy_from_user(&req, argp, sizeof(struct ubi_map_req));
if (err) {
err = -EFAULT;
break;
}
err = ubi_leb_map(desc, req.lnum, req.dtype);
break;
}
/* Logical eraseblock un-map command */
case UBI_IOCEBUNMAP:
{
int32_t lnum;
err = get_user(lnum, (__user int32_t *)argp);
if (err) {
err = -EFAULT;
break;
}
err = ubi_leb_unmap(desc, lnum);
break;
}
/* Check if logical eraseblock is mapped command */
case UBI_IOCEBISMAP:
{
int32_t lnum;
err = get_user(lnum, (__user int32_t *)argp);
if (err) {
err = -EFAULT;
break;
}
err = ubi_is_mapped(desc, lnum);
break;
}
/* Set volume property command*/
case UBI_IOCSETPROP:
{
struct ubi_set_prop_req req;
err = copy_from_user(&req, argp,
sizeof(struct ubi_set_prop_req));
if (err) {
err = -EFAULT;
break;
}
switch (req.property) {
case UBI_PROP_DIRECT_WRITE:
mutex_lock(&ubi->volumes_mutex);
desc->vol->direct_writes = !!req.value;
mutex_unlock(&ubi->volumes_mutex);
break;
default: default:
err = -ENOTTY; err = -EINVAL;
break;
}
break; break;
} }
default:
err = -ENOTTY;
break;
}
return err; return err;
} }
...@@ -762,8 +813,8 @@ static int rename_volumes(struct ubi_device *ubi, ...@@ -762,8 +813,8 @@ static int rename_volumes(struct ubi_device *ubi,
return err; return err;
} }
static int ubi_cdev_ioctl(struct inode *inode, struct file *file, static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
int err = 0; int err = 0;
struct ubi_device *ubi; struct ubi_device *ubi;
...@@ -773,7 +824,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -773,7 +824,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
if (!capable(CAP_SYS_RESOURCE)) if (!capable(CAP_SYS_RESOURCE))
return -EPERM; return -EPERM;
ubi = ubi_get_by_major(imajor(inode)); ubi = ubi_get_by_major(imajor(file->f_mapping->host));
if (!ubi) if (!ubi)
return -ENODEV; return -ENODEV;
...@@ -843,7 +894,6 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -843,7 +894,6 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
case UBI_IOCRSVOL: case UBI_IOCRSVOL:
{ {
int pebs; int pebs;
uint64_t tmp;
struct ubi_rsvol_req req; struct ubi_rsvol_req req;
dbg_gen("re-size volume"); dbg_gen("re-size volume");
...@@ -863,9 +913,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -863,9 +913,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
break; break;
} }
tmp = req.bytes; pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1,
pebs = !!do_div(tmp, desc->vol->usable_leb_size); desc->vol->usable_leb_size);
pebs += tmp;
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->volumes_mutex);
err = ubi_resize_volume(desc, pebs); err = ubi_resize_volume(desc, pebs);
...@@ -909,8 +958,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -909,8 +958,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
return err; return err;
} }
static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
int err = 0; int err = 0;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
...@@ -986,26 +1035,59 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, ...@@ -986,26 +1035,59 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
return err; return err;
} }
/* UBI control character device operations */ #ifdef CONFIG_COMPAT
struct file_operations ubi_ctrl_cdev_operations = { static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd,
.ioctl = ctrl_cdev_ioctl, unsigned long arg)
.owner = THIS_MODULE, {
}; unsigned long translated_arg = (unsigned long)compat_ptr(arg);
/* UBI character device operations */ return vol_cdev_ioctl(file, cmd, translated_arg);
struct file_operations ubi_cdev_operations = { }
.owner = THIS_MODULE,
.ioctl = ubi_cdev_ioctl, static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd,
.llseek = no_llseek, unsigned long arg)
}; {
unsigned long translated_arg = (unsigned long)compat_ptr(arg);
return ubi_cdev_ioctl(file, cmd, translated_arg);
}
static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long translated_arg = (unsigned long)compat_ptr(arg);
return ctrl_cdev_ioctl(file, cmd, translated_arg);
}
#else
#define vol_cdev_compat_ioctl NULL
#define ubi_cdev_compat_ioctl NULL
#define ctrl_cdev_compat_ioctl NULL
#endif
/* UBI volume character device operations */ /* UBI volume character device operations */
struct file_operations ubi_vol_cdev_operations = { const struct file_operations ubi_vol_cdev_operations = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = vol_cdev_open, .open = vol_cdev_open,
.release = vol_cdev_release, .release = vol_cdev_release,
.llseek = vol_cdev_llseek, .llseek = vol_cdev_llseek,
.read = vol_cdev_read, .read = vol_cdev_read,
.write = vol_cdev_write, .write = vol_cdev_write,
.ioctl = vol_cdev_ioctl, .unlocked_ioctl = vol_cdev_ioctl,
.compat_ioctl = vol_cdev_compat_ioctl,
};
/* UBI character device operations */
const struct file_operations ubi_cdev_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = ubi_cdev_ioctl,
.compat_ioctl = ubi_cdev_compat_ioctl,
};
/* UBI control character device operations */
const struct file_operations ubi_ctrl_cdev_operations = {
.owner = THIS_MODULE,
.unlocked_ioctl = ctrl_cdev_ioctl,
.compat_ioctl = ctrl_cdev_compat_ioctl,
}; };
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
* eraseblock size is equivalent to the logical eraseblock size of the volume. * eraseblock size is equivalent to the logical eraseblock size of the volume.
*/ */
#include <asm/div64.h> #include <linux/math64.h>
#include "ubi.h" #include "ubi.h"
/** /**
...@@ -109,7 +109,6 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -109,7 +109,6 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
int err = 0, lnum, offs, total_read; int err = 0, lnum, offs, total_read;
struct ubi_volume *vol; struct ubi_volume *vol;
struct ubi_device *ubi; struct ubi_device *ubi;
uint64_t tmp = from;
dbg_gen("read %zd bytes from offset %lld", len, from); dbg_gen("read %zd bytes from offset %lld", len, from);
...@@ -119,9 +118,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -119,9 +118,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
vol = container_of(mtd, struct ubi_volume, gluebi_mtd); vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
ubi = vol->ubi; ubi = vol->ubi;
offs = do_div(tmp, mtd->erasesize); lnum = div_u64_rem(from, mtd->erasesize, &offs);
lnum = tmp;
total_read = len; total_read = len;
while (total_read) { while (total_read) {
size_t to_read = mtd->erasesize - offs; size_t to_read = mtd->erasesize - offs;
...@@ -160,7 +157,6 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -160,7 +157,6 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
int err = 0, lnum, offs, total_written; int err = 0, lnum, offs, total_written;
struct ubi_volume *vol; struct ubi_volume *vol;
struct ubi_device *ubi; struct ubi_device *ubi;
uint64_t tmp = to;
dbg_gen("write %zd bytes to offset %lld", len, to); dbg_gen("write %zd bytes to offset %lld", len, to);
...@@ -173,8 +169,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -173,8 +169,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
offs = do_div(tmp, mtd->erasesize); lnum = div_u64_rem(to, mtd->erasesize, &offs);
lnum = tmp;
if (len % mtd->writesize || offs % mtd->writesize) if (len % mtd->writesize || offs % mtd->writesize)
return -EINVAL; return -EINVAL;
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <asm/div64.h> #include <linux/math64.h>
#include "ubi.h" #include "ubi.h"
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
...@@ -904,10 +904,8 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) ...@@ -904,10 +904,8 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
dbg_msg("scanning is finished"); dbg_msg("scanning is finished");
/* Calculate mean erase counter */ /* Calculate mean erase counter */
if (si->ec_count) { if (si->ec_count)
do_div(si->ec_sum, si->ec_count); si->mean_ec = div_u64(si->ec_sum, si->ec_count);
si->mean_ec = si->ec_sum;
}
if (si->is_empty) if (si->is_empty)
ubi_msg("empty MTD device detected"); ubi_msg("empty MTD device detected");
......
...@@ -206,6 +206,7 @@ struct ubi_volume_desc; ...@@ -206,6 +206,7 @@ struct ubi_volume_desc;
* @upd_marker: %1 if the update marker is set for this volume * @upd_marker: %1 if the update marker is set for this volume
* @updating: %1 if the volume is being updated * @updating: %1 if the volume is being updated
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress * @changing_leb: %1 if the atomic LEB change ioctl command is in progress
* @direct_writes: %1 if direct writes are enabled for this volume
* *
* @gluebi_desc: gluebi UBI volume descriptor * @gluebi_desc: gluebi UBI volume descriptor
* @gluebi_refcount: reference count of the gluebi MTD device * @gluebi_refcount: reference count of the gluebi MTD device
...@@ -253,6 +254,7 @@ struct ubi_volume { ...@@ -253,6 +254,7 @@ struct ubi_volume {
unsigned int upd_marker:1; unsigned int upd_marker:1;
unsigned int updating:1; unsigned int updating:1;
unsigned int changing_leb:1; unsigned int changing_leb:1;
unsigned int direct_writes:1;
#ifdef CONFIG_MTD_UBI_GLUEBI #ifdef CONFIG_MTD_UBI_GLUEBI
/* /*
...@@ -304,7 +306,8 @@ struct ubi_wl_entry; ...@@ -304,7 +306,8 @@ struct ubi_wl_entry;
* @vtbl_size: size of the volume table in bytes * @vtbl_size: size of the volume table in bytes
* @vtbl: in-RAM volume table copy * @vtbl: in-RAM volume table copy
* @volumes_mutex: protects on-flash volume table and serializes volume * @volumes_mutex: protects on-flash volume table and serializes volume
* changes, like creation, deletion, update, re-size and re-name * changes, like creation, deletion, update, re-size,
* re-name and set property
* *
* @max_ec: current highest erase counter value * @max_ec: current highest erase counter value
* @mean_ec: current mean erase counter value * @mean_ec: current mean erase counter value
...@@ -449,9 +452,9 @@ struct ubi_device { ...@@ -449,9 +452,9 @@ struct ubi_device {
}; };
extern struct kmem_cache *ubi_wl_entry_slab; extern struct kmem_cache *ubi_wl_entry_slab;
extern struct file_operations ubi_ctrl_cdev_operations; extern const struct file_operations ubi_ctrl_cdev_operations;
extern struct file_operations ubi_cdev_operations; extern const struct file_operations ubi_cdev_operations;
extern struct file_operations ubi_vol_cdev_operations; extern const struct file_operations ubi_vol_cdev_operations;
extern struct class *ubi_class; extern struct class *ubi_class;
extern struct mutex ubi_devices_mutex; extern struct mutex ubi_devices_mutex;
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/div64.h> #include <linux/math64.h>
#include "ubi.h" #include "ubi.h"
/** /**
...@@ -89,7 +89,6 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -89,7 +89,6 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
long long bytes) long long bytes)
{ {
int err; int err;
uint64_t tmp;
struct ubi_vtbl_record vtbl_rec; struct ubi_vtbl_record vtbl_rec;
dbg_gen("clear update marker for volume %d", vol->vol_id); dbg_gen("clear update marker for volume %d", vol->vol_id);
...@@ -101,9 +100,9 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -101,9 +100,9 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
if (vol->vol_type == UBI_STATIC_VOLUME) { if (vol->vol_type == UBI_STATIC_VOLUME) {
vol->corrupted = 0; vol->corrupted = 0;
vol->used_bytes = tmp = bytes; vol->used_bytes = bytes;
vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); vol->used_ebs = div_u64_rem(bytes, vol->usable_leb_size,
vol->used_ebs = tmp; &vol->last_eb_bytes);
if (vol->last_eb_bytes) if (vol->last_eb_bytes)
vol->used_ebs += 1; vol->used_ebs += 1;
else else
...@@ -131,7 +130,6 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -131,7 +130,6 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
long long bytes) long long bytes)
{ {
int i, err; int i, err;
uint64_t tmp;
dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes); dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes);
ubi_assert(!vol->updating && !vol->changing_leb); ubi_assert(!vol->updating && !vol->changing_leb);
...@@ -161,9 +159,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -161,9 +159,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
if (!vol->upd_buf) if (!vol->upd_buf)
return -ENOMEM; return -ENOMEM;
tmp = bytes; vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1,
vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); vol->usable_leb_size);
vol->upd_ebs += tmp;
vol->upd_bytes = bytes; vol->upd_bytes = bytes;
vol->upd_received = 0; vol->upd_received = 0;
return 0; return 0;
...@@ -282,7 +279,6 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -282,7 +279,6 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
const void __user *buf, int count) const void __user *buf, int count)
{ {
uint64_t tmp;
int lnum, offs, err = 0, len, to_write = count; int lnum, offs, err = 0, len, to_write = count;
dbg_gen("write %d of %lld bytes, %lld already passed", dbg_gen("write %d of %lld bytes, %lld already passed",
...@@ -291,10 +287,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -291,10 +287,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
tmp = vol->upd_received; lnum = div_u64_rem(vol->upd_received, vol->usable_leb_size, &offs);
offs = do_div(tmp, vol->usable_leb_size);
lnum = tmp;
if (vol->upd_received + count > vol->upd_bytes) if (vol->upd_received + count > vol->upd_bytes)
to_write = count = vol->upd_bytes - vol->upd_received; to_write = count = vol->upd_bytes - vol->upd_received;
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <asm/div64.h> #include <linux/math64.h>
#include "ubi.h" #include "ubi.h"
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
...@@ -205,7 +205,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -205,7 +205,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
int i, err, vol_id = req->vol_id, do_free = 1; int i, err, vol_id = req->vol_id, do_free = 1;
struct ubi_volume *vol; struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec; struct ubi_vtbl_record vtbl_rec;
uint64_t bytes;
dev_t dev; dev_t dev;
if (ubi->ro_mode) if (ubi->ro_mode)
...@@ -255,10 +254,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -255,10 +254,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
/* Calculate how many eraseblocks are requested */ /* Calculate how many eraseblocks are requested */
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
bytes = req->bytes; vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1,
if (do_div(bytes, vol->usable_leb_size)) vol->usable_leb_size);
vol->reserved_pebs = 1;
vol->reserved_pebs += bytes;
/* Reserve physical eraseblocks */ /* Reserve physical eraseblocks */
if (vol->reserved_pebs > ubi->avail_pebs) { if (vol->reserved_pebs > ubi->avail_pebs) {
...@@ -301,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -301,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
vol->used_bytes = vol->used_bytes =
(long long)vol->used_ebs * vol->usable_leb_size; (long long)vol->used_ebs * vol->usable_leb_size;
} else { } else {
bytes = vol->used_bytes; vol->used_ebs = div_u64_rem(vol->used_bytes,
vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); vol->usable_leb_size,
vol->used_ebs = bytes; &vol->last_eb_bytes);
if (vol->last_eb_bytes) if (vol->last_eb_bytes != 0)
vol->used_ebs += 1; vol->used_ebs += 1;
else else
vol->last_eb_bytes = vol->usable_leb_size; vol->last_eb_bytes = vol->usable_leb_size;
......
...@@ -40,37 +40,37 @@ ...@@ -40,37 +40,37 @@
* UBI volume creation * UBI volume creation
* ~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~
* *
* UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character * UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character
* device. A &struct ubi_mkvol_req object has to be properly filled and a * device. A &struct ubi_mkvol_req object has to be properly filled and a
* pointer to it has to be passed to the IOCTL. * pointer to it has to be passed to the ioctl.
* *
* UBI volume deletion * UBI volume deletion
* ~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~
* *
* To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character * To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character
* device should be used. A pointer to the 32-bit volume ID hast to be passed * device should be used. A pointer to the 32-bit volume ID hast to be passed
* to the IOCTL. * to the ioctl.
* *
* UBI volume re-size * UBI volume re-size
* ~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~
* *
* To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character * To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character
* device should be used. A &struct ubi_rsvol_req object has to be properly * device should be used. A &struct ubi_rsvol_req object has to be properly
* filled and a pointer to it has to be passed to the IOCTL. * filled and a pointer to it has to be passed to the ioctl.
* *
* UBI volumes re-name * UBI volumes re-name
* ~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~
* *
* To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command * To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command
* of the UBI character device should be used. A &struct ubi_rnvol_req object * of the UBI character device should be used. A &struct ubi_rnvol_req object
* has to be properly filled and a pointer to it has to be passed to the IOCTL. * has to be properly filled and a pointer to it has to be passed to the ioctl.
* *
* UBI volume update * UBI volume update
* ~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~
* *
* Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the * Volume update should be done via the %UBI_IOCVOLUP ioctl command of the
* corresponding UBI volume character device. A pointer to a 64-bit update * corresponding UBI volume character device. A pointer to a 64-bit update
* size should be passed to the IOCTL. After this, UBI expects user to write * size should be passed to the ioctl. After this, UBI expects user to write
* this number of bytes to the volume character device. The update is finished * this number of bytes to the volume character device. The update is finished
* when the claimed number of bytes is passed. So, the volume update sequence * when the claimed number of bytes is passed. So, the volume update sequence
* is something like: * is something like:
...@@ -80,14 +80,58 @@ ...@@ -80,14 +80,58 @@
* write(fd, buf, image_size); * write(fd, buf, image_size);
* close(fd); * close(fd);
* *
* Atomic eraseblock change * Logical eraseblock erase
* ~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~
* *
* Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL * To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the
* command of the corresponding UBI volume character device. A pointer to * corresponding UBI volume character device should be used. This command
* &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is * unmaps the requested logical eraseblock, makes sure the corresponding
* expected to write the requested amount of bytes. This is similar to the * physical eraseblock is successfully erased, and returns.
* "volume update" IOCTL. *
* Atomic logical eraseblock change
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH
* ioctl command of the corresponding UBI volume character device. A pointer to
* a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the
* user is expected to write the requested amount of bytes (similarly to what
* should be done in case of the "volume update" ioctl).
*
* Logical eraseblock map
* ~~~~~~~~~~~~~~~~~~~~~
*
* To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP
* ioctl command should be used. A pointer to a &struct ubi_map_req object is
* expected to be passed. The ioctl maps the requested logical eraseblock to
* a physical eraseblock and returns. Only non-mapped logical eraseblocks can
* be mapped. If the logical eraseblock specified in the request is already
* mapped to a physical eraseblock, the ioctl fails and returns error.
*
* Logical eraseblock unmap
* ~~~~~~~~~~~~~~~~~~~~~~~~
*
* To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP
* ioctl command should be used. The ioctl unmaps the logical eraseblocks,
* schedules corresponding physical eraseblock for erasure, and returns. Unlike
* the "LEB erase" command, it does not wait for the physical eraseblock being
* erased. Note, the side effect of this is that if an unclean reboot happens
* after the unmap ioctl returns, you may find the LEB mapped again to the same
* physical eraseblock after the UBI is run again.
*
* Check if logical eraseblock is mapped
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To check if a logical eraseblock is mapped to a physical eraseblock, the
* %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is
* not mapped, and %1 if it is mapped.
*
* Set an UBI volume property
* ~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be
* used. A pointer to a &struct ubi_set_prop_req object is expected to be
* passed. The object describes which property should be set, and to which value
* it should be set.
*/ */
/* /*
...@@ -101,7 +145,7 @@ ...@@ -101,7 +145,7 @@
/* Maximum volume name length */ /* Maximum volume name length */
#define UBI_MAX_VOLUME_NAME 127 #define UBI_MAX_VOLUME_NAME 127
/* IOCTL commands of UBI character devices */ /* ioctl commands of UBI character devices */
#define UBI_IOC_MAGIC 'o' #define UBI_IOC_MAGIC 'o'
...@@ -114,7 +158,7 @@ ...@@ -114,7 +158,7 @@
/* Re-name volumes */ /* Re-name volumes */
#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req) #define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
/* IOCTL commands of the UBI control character device */ /* ioctl commands of the UBI control character device */
#define UBI_CTRL_IOC_MAGIC 'o' #define UBI_CTRL_IOC_MAGIC 'o'
...@@ -123,16 +167,24 @@ ...@@ -123,16 +167,24 @@
/* Detach an MTD device */ /* Detach an MTD device */
#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) #define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
/* IOCTL commands of UBI volume character devices */ /* ioctl commands of UBI volume character devices */
#define UBI_VOL_IOC_MAGIC 'O' #define UBI_VOL_IOC_MAGIC 'O'
/* Start UBI volume update */ /* Start UBI volume update */
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
/* An eraseblock erasure command, used for debugging, disabled by default */ /* LEB erasure command, used for debugging, disabled by default */
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
/* An atomic eraseblock change command */ /* Atomic LEB change command */
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) #define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
/* Map LEB command */
#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req)
/* Unmap LEB command */
#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, int32_t)
/* Check if LEB is mapped command */
#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, int32_t)
/* Set an UBI volume property */
#define UBI_IOCSETPROP _IOW(UBI_VOL_IOC_MAGIC, 6, struct ubi_set_prop_req)
/* Maximum MTD device name length supported by UBI */ /* Maximum MTD device name length supported by UBI */
#define MAX_UBI_MTD_NAME_LEN 127 #define MAX_UBI_MTD_NAME_LEN 127
...@@ -168,6 +220,16 @@ enum { ...@@ -168,6 +220,16 @@ enum {
UBI_STATIC_VOLUME = 4, UBI_STATIC_VOLUME = 4,
}; };
/*
* UBI set property ioctl constants
*
* @UBI_PROP_DIRECT_WRITE: allow / disallow user to directly write and
* erase individual eraseblocks on dynamic volumes
*/
enum {
UBI_PROP_DIRECT_WRITE = 1,
};
/** /**
* struct ubi_attach_req - attach MTD device request. * struct ubi_attach_req - attach MTD device request.
* @ubi_num: UBI device number to create * @ubi_num: UBI device number to create
...@@ -305,8 +367,8 @@ struct ubi_rnvol_req { ...@@ -305,8 +367,8 @@ struct ubi_rnvol_req {
} __attribute__ ((packed)); } __attribute__ ((packed));
/** /**
* struct ubi_leb_change_req - a data structure used in atomic logical * struct ubi_leb_change_req - a data structure used in atomic LEB change
* eraseblock change requests. * requests.
* @lnum: logical eraseblock number to change * @lnum: logical eraseblock number to change
* @bytes: how many bytes will be written to the logical eraseblock * @bytes: how many bytes will be written to the logical eraseblock
* @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
...@@ -319,4 +381,30 @@ struct ubi_leb_change_req { ...@@ -319,4 +381,30 @@ struct ubi_leb_change_req {
int8_t padding[7]; int8_t padding[7];
} __attribute__ ((packed)); } __attribute__ ((packed));
/**
* struct ubi_map_req - a data structure used in map LEB requests.
* @lnum: logical eraseblock number to unmap
* @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
* @padding: reserved for future, not used, has to be zeroed
*/
struct ubi_map_req {
int32_t lnum;
int8_t dtype;
int8_t padding[3];
} __attribute__ ((packed));
/**
* struct ubi_set_prop_req - a data structure used to set an ubi volume
* property.
* @property: property to set (%UBI_PROP_DIRECT_WRITE)
* @padding: reserved for future, not used, has to be zeroed
* @value: value to set
*/
struct ubi_set_prop_req {
uint8_t property;
uint8_t padding[7];
uint64_t value;
} __attribute__ ((packed));
#endif /* __UBI_USER_H__ */ #endif /* __UBI_USER_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment