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: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20200114090952.11232-1-miquel.raynal@bootlin.com
This commit is contained in:
@@ -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 */
|
||||
|
Reference in New Issue
Block a user