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, ...@@ -349,6 +349,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
uint64_t start, uint32_t length, void __user *ptr, uint64_t start, uint32_t length, void __user *ptr,
uint32_t __user *retp) uint32_t __user *retp)
{ {
struct mtd_info *master = mtd_get_master(mtd);
struct mtd_file_info *mfi = file->private_data; struct mtd_file_info *mfi = file->private_data;
struct mtd_oob_ops ops = {}; struct mtd_oob_ops ops = {};
uint32_t retlen; uint32_t retlen;
...@@ -360,7 +361,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, ...@@ -360,7 +361,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
if (length > 4096) if (length > 4096)
return -EINVAL; return -EINVAL;
if (!mtd->_write_oob) if (!master->_write_oob)
return -EOPNOTSUPP; return -EOPNOTSUPP;
ops.ooblen = length; ops.ooblen = length;
...@@ -586,6 +587,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, ...@@ -586,6 +587,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
static int mtdchar_write_ioctl(struct mtd_info *mtd, static int mtdchar_write_ioctl(struct mtd_info *mtd,
struct mtd_write_req __user *argp) struct mtd_write_req __user *argp)
{ {
struct mtd_info *master = mtd_get_master(mtd);
struct mtd_write_req req; struct mtd_write_req req;
struct mtd_oob_ops ops = {}; struct mtd_oob_ops ops = {};
const void __user *usr_data, *usr_oob; const void __user *usr_data, *usr_oob;
...@@ -597,9 +599,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, ...@@ -597,9 +599,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
usr_data = (const void __user *)(uintptr_t)req.usr_data; usr_data = (const void __user *)(uintptr_t)req.usr_data;
usr_oob = (const void __user *)(uintptr_t)req.usr_oob; usr_oob = (const void __user *)(uintptr_t)req.usr_oob;
if (!mtd->_write_oob) if (!master->_write_oob)
return -EOPNOTSUPP; return -EOPNOTSUPP;
ops.mode = req.mode; ops.mode = req.mode;
ops.len = (size_t)req.len; ops.len = (size_t)req.len;
ops.ooblen = (size_t)req.ooblen; ops.ooblen = (size_t)req.ooblen;
...@@ -635,6 +636,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) ...@@ -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_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd; struct mtd_info *mtd = mfi->mtd;
struct mtd_info *master = mtd_get_master(mtd);
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
int ret = 0; int ret = 0;
struct mtd_info_user info; struct mtd_info_user info;
...@@ -824,7 +826,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) ...@@ -824,7 +826,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{ {
struct nand_oobinfo oi; struct nand_oobinfo oi;
if (!mtd->ooblayout) if (!master->ooblayout)
return -EOPNOTSUPP; return -EOPNOTSUPP;
ret = get_oobinfo(mtd, &oi); ret = get_oobinfo(mtd, &oi);
...@@ -918,7 +920,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) ...@@ -918,7 +920,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{ {
struct nand_ecclayout_user *usrlay; struct nand_ecclayout_user *usrlay;
if (!mtd->ooblayout) if (!master->ooblayout)
return -EOPNOTSUPP; return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/list.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h> #include <linux/of.h>
...@@ -194,10 +195,43 @@ struct mtd_debug_info { ...@@ -194,10 +195,43 @@ struct mtd_debug_info {
const char *partid; 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 { struct mtd_info {
u_char type; u_char type;
uint32_t flags; uint32_t flags;
uint32_t orig_flags; /* Flags as before running mtd checks */
uint64_t size; // Total size of the MTD uint64_t size; // Total size of the MTD
/* "Major" erase size for the device. Naïve users may take this /* "Major" erase size for the device. Naïve users may take this
...@@ -339,8 +373,52 @@ struct mtd_info { ...@@ -339,8 +373,52 @@ struct mtd_info {
int usecount; int usecount;
struct mtd_debug_info dbg; struct mtd_debug_info dbg;
struct nvmem_device *nvmem; 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, int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc); struct mtd_oob_region *oobecc);
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, 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) ...@@ -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, static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
loff_t ofs, size_t len) 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; return -ENOTSUPP;
if (mtd->size < (len + ofs) || ofs < 0) if (mtd->size < (len + ofs) || ofs < 0)
return -EINVAL; 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, 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, ...@@ -439,8 +520,10 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
static inline void mtd_sync(struct mtd_info *mtd) static inline void mtd_sync(struct mtd_info *mtd)
{ {
if (mtd->_sync) struct mtd_info *master = mtd_get_master(mtd);
mtd->_sync(mtd);
if (master->_sync)
master->_sync(master);
} }
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 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); ...@@ -452,13 +535,31 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
static inline int mtd_suspend(struct mtd_info *mtd) 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) static inline void mtd_resume(struct mtd_info *mtd)
{ {
if (mtd->_resume) struct mtd_info *master = mtd_get_master(mtd);
mtd->_resume(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) 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, ...@@ -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) 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) 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) ...@@ -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) 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 */ /* Kernel-side ioctl definitions */
......
...@@ -105,7 +105,6 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser); ...@@ -105,7 +105,6 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser);
module_driver(__mtd_part_parser, register_mtd_parser, \ module_driver(__mtd_part_parser, register_mtd_parser, \
deregister_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, int mtd_add_partition(struct mtd_info *master, const char *name,
long long offset, long long length); long long offset, long long length);
int mtd_del_partition(struct mtd_info *master, int partno); 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