Commit 46b5889c authored by Miquel Raynal's avatar Miquel Raynal

mtd: implement proper partition handling

Instead of collecting partitions in a flat list, create a hierarchy
within the mtd_info structure: use a partitions list to keep track of
the partitions of an MTD device (which might be itself a partition of
another MTD device), a pointer to the parent device (NULL when the MTD
device is the root one, not a partition).

By also saving directly in mtd_info the offset of the partition, we
can get rid of the mtd_part structure.

While at it, be consistent in the naming of the mtd_info structures to
ease the understanding of the new hierarchy: these structures are
usually called 'mtd', unless there are multiple instances of the same
structure. In this case, there is usually a parent/child bound so we
will call them 'parent' and 'child'.
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20200114090952.11232-1-miquel.raynal@bootlin.com
parent 98d54f81
......@@ -349,6 +349,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
uint64_t start, uint32_t length, void __user *ptr,
uint32_t __user *retp)
{
struct mtd_info *master = mtd_get_master(mtd);
struct mtd_file_info *mfi = file->private_data;
struct mtd_oob_ops ops = {};
uint32_t retlen;
......@@ -360,7 +361,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
if (length > 4096)
return -EINVAL;
if (!mtd->_write_oob)
if (!master->_write_oob)
return -EOPNOTSUPP;
ops.ooblen = length;
......@@ -586,6 +587,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
static int mtdchar_write_ioctl(struct mtd_info *mtd,
struct mtd_write_req __user *argp)
{
struct mtd_info *master = mtd_get_master(mtd);
struct mtd_write_req req;
struct mtd_oob_ops ops = {};
const void __user *usr_data, *usr_oob;
......@@ -597,9 +599,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
usr_data = (const void __user *)(uintptr_t)req.usr_data;
usr_oob = (const void __user *)(uintptr_t)req.usr_oob;
if (!mtd->_write_oob)
if (!master->_write_oob)
return -EOPNOTSUPP;
ops.mode = req.mode;
ops.len = (size_t)req.len;
ops.ooblen = (size_t)req.ooblen;
......@@ -635,6 +636,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct mtd_info *master = mtd_get_master(mtd);
void __user *argp = (void __user *)arg;
int ret = 0;
struct mtd_info_user info;
......@@ -824,7 +826,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_oobinfo oi;
if (!mtd->ooblayout)
if (!master->ooblayout)
return -EOPNOTSUPP;
ret = get_oobinfo(mtd, &oi);
......@@ -918,7 +920,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_ecclayout_user *usrlay;
if (!mtd->ooblayout)
if (!master->ooblayout)
return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
......
This diff is collapsed.
This diff is collapsed.
......@@ -8,6 +8,7 @@
#include <linux/types.h>
#include <linux/uio.h>
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/of.h>
......@@ -194,10 +195,43 @@ struct mtd_debug_info {
const char *partid;
};
/**
* struct mtd_part - MTD partition specific fields
*
* @node: list node used to add an MTD partition to the parent partition list
* @offset: offset of the partition relatively to the parent offset
* @flags: original flags (before the mtdpart logic decided to tweak them based
* on flash constraints, like eraseblock/pagesize alignment)
*
* This struct is embedded in mtd_info and contains partition-specific
* properties/fields.
*/
struct mtd_part {
struct list_head node;
u64 offset;
u32 flags;
};
/**
* struct mtd_master - MTD master specific fields
*
* @partitions_lock: lock protecting accesses to the partition list. Protects
* not only the master partition list, but also all
* sub-partitions.
* @suspended: et to 1 when the device is suspended, 0 otherwise
*
* This struct is embedded in mtd_info and contains master-specific
* properties/fields. The master is the root MTD device from the MTD partition
* point of view.
*/
struct mtd_master {
struct mutex partitions_lock;
unsigned int suspended : 1;
};
struct mtd_info {
u_char type;
uint32_t flags;
uint32_t orig_flags; /* Flags as before running mtd checks */
uint64_t size; // Total size of the MTD
/* "Major" erase size for the device. Naïve users may take this
......@@ -339,8 +373,52 @@ struct mtd_info {
int usecount;
struct mtd_debug_info dbg;
struct nvmem_device *nvmem;
/*
* Parent device from the MTD partition point of view.
*
* MTD masters do not have any parent, MTD partitions do. The parent
* MTD device can itself be a partition.
*/
struct mtd_info *parent;
/* List of partitions attached to this MTD device */
struct list_head partitions;
union {
struct mtd_part part;
struct mtd_master master;
};
};
static inline struct mtd_info *mtd_get_master(struct mtd_info *mtd)
{
while (mtd->parent)
mtd = mtd->parent;
return mtd;
}
static inline u64 mtd_get_master_ofs(struct mtd_info *mtd, u64 ofs)
{
while (mtd->parent) {
ofs += mtd->part.offset;
mtd = mtd->parent;
}
return ofs;
}
static inline bool mtd_is_partition(const struct mtd_info *mtd)
{
return mtd->parent;
}
static inline bool mtd_has_partitions(const struct mtd_info *mtd)
{
return !list_empty(&mtd->partitions);
}
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc);
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
......@@ -392,13 +470,16 @@ static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
loff_t ofs, size_t len)
{
if (!mtd->_max_bad_blocks)
struct mtd_info *master = mtd_get_master(mtd);
if (!master->_max_bad_blocks)
return -ENOTSUPP;
if (mtd->size < (len + ofs) || ofs < 0)
return -EINVAL;
return mtd->_max_bad_blocks(mtd, ofs, len);
return master->_max_bad_blocks(master, mtd_get_master_ofs(mtd, ofs),
len);
}
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
......@@ -439,8 +520,10 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
static inline void mtd_sync(struct mtd_info *mtd)
{
if (mtd->_sync)
mtd->_sync(mtd);
struct mtd_info *master = mtd_get_master(mtd);
if (master->_sync)
master->_sync(master);
}
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
......@@ -452,13 +535,31 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
static inline int mtd_suspend(struct mtd_info *mtd)
{
return mtd->_suspend ? mtd->_suspend(mtd) : 0;
struct mtd_info *master = mtd_get_master(mtd);
int ret;
if (master->master.suspended)
return 0;
ret = master->_suspend ? master->_suspend(master) : 0;
if (ret)
return ret;
master->master.suspended = 1;
return 0;
}
static inline void mtd_resume(struct mtd_info *mtd)
{
if (mtd->_resume)
mtd->_resume(mtd);
struct mtd_info *master = mtd_get_master(mtd);
if (!master->master.suspended)
return;
if (master->_resume)
master->_resume(master);
master->master.suspended = 0;
}
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
......@@ -538,7 +639,9 @@ static inline loff_t mtd_wunit_to_offset(struct mtd_info *mtd, loff_t base,
static inline int mtd_has_oob(const struct mtd_info *mtd)
{
return mtd->_read_oob && mtd->_write_oob;
struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd);
return master->_read_oob && master->_write_oob;
}
static inline int mtd_type_is_nand(const struct mtd_info *mtd)
......@@ -548,7 +651,9 @@ static inline int mtd_type_is_nand(const struct mtd_info *mtd)
static inline int mtd_can_have_bb(const struct mtd_info *mtd)
{
return !!mtd->_block_isbad;
struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd);
return !!master->_block_isbad;
}
/* Kernel-side ioctl definitions */
......
......@@ -105,7 +105,6 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser);
module_driver(__mtd_part_parser, register_mtd_parser, \
deregister_mtd_parser)
int mtd_is_partition(const struct mtd_info *mtd);
int mtd_add_partition(struct mtd_info *master, const char *name,
long long offset, long long length);
int mtd_del_partition(struct mtd_info *master, int partno);
......
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