Skip to content

Commit adbbc3b

Browse files
author
Boris Brezillon
committed
mtd: create an mtd_ooblayout_ops struct to ease ECC layout definition
ECC layout definitions are currently exposed using the nand_ecclayout struct which embeds oobfree and eccpos arrays with predefined size. This approach was acceptable when NAND chips were providing relatively small OOB regions, but MLC and TLC now provide OOB regions of several hundreds of bytes, which implies a non negligible overhead for everybody even those who only need to support legacy NANDs. Create an mtd_ooblayout_ops interface providing the same functionality (expose the ECC and oobfree layout) without the need for this huge structure. The mtd->ecclayout is now deprecated and should be replaced by the equivalent mtd_ooblayout_ops. In the meantime we provide a wrapper around the ->ecclayout field to ease migration to this new model. Signed-off-by: Boris Brezillon <[email protected]>
1 parent 06af3b0 commit adbbc3b

File tree

5 files changed

+174
-52
lines changed

5 files changed

+174
-52
lines changed

drivers/mtd/mtdchar.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
888888
{
889889
struct nand_oobinfo oi;
890890

891-
if (!mtd->ecclayout)
891+
if (!mtd->ooblayout)
892892
return -EOPNOTSUPP;
893893

894894
ret = get_oobinfo(mtd, &oi);
@@ -982,7 +982,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
982982
{
983983
struct nand_ecclayout_user *usrlay;
984984

985-
if (!mtd->ecclayout)
985+
if (!mtd->ooblayout)
986986
return -EOPNOTSUPP;
987987

988988
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);

drivers/mtd/mtdconcat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
777777

778778
}
779779

780-
mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout);
780+
mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
781781

782782
concat->num_subdev = num_devs;
783783
concat->mtd.name = name;

drivers/mtd/mtdcore.c

Lines changed: 121 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,49 +1035,15 @@ EXPORT_SYMBOL_GPL(mtd_write_oob);
10351035
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
10361036
struct mtd_oob_region *oobecc)
10371037
{
1038-
int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
1039-
10401038
memset(oobecc, 0, sizeof(*oobecc));
10411039

10421040
if (!mtd || section < 0)
10431041
return -EINVAL;
10441042

1045-
if (!mtd->ecclayout)
1043+
if (!mtd->ooblayout || !mtd->ooblayout->ecc)
10461044
return -ENOTSUPP;
10471045

1048-
/*
1049-
* This logic allows us to reuse the ->ecclayout information and
1050-
* expose them as ECC regions (as done for the OOB free regions).
1051-
*
1052-
* TODO: this should be dropped as soon as we get rid of the
1053-
* ->ecclayout field.
1054-
*/
1055-
for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
1056-
eccpos = mtd->ecclayout->eccpos[eccbyte];
1057-
1058-
if (eccbyte < mtd->ecclayout->eccbytes - 1) {
1059-
int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
1060-
1061-
if (eccpos + 1 == neccpos) {
1062-
length++;
1063-
continue;
1064-
}
1065-
}
1066-
1067-
if (section == cursection)
1068-
break;
1069-
1070-
length = 0;
1071-
cursection++;
1072-
}
1073-
1074-
if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
1075-
return -ERANGE;
1076-
1077-
oobecc->length = length + 1;
1078-
oobecc->offset = eccpos - length;
1079-
1080-
return 0;
1046+
return mtd->ooblayout->ecc(mtd, section, oobecc);
10811047
}
10821048
EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
10831049

@@ -1106,16 +1072,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section,
11061072
if (!mtd || section < 0)
11071073
return -EINVAL;
11081074

1109-
if (!mtd->ecclayout)
1075+
if (!mtd->ooblayout || !mtd->ooblayout->free)
11101076
return -ENOTSUPP;
11111077

1112-
if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE)
1113-
return -ERANGE;
1114-
1115-
oobfree->offset = mtd->ecclayout->oobfree[section].offset;
1116-
oobfree->length = mtd->ecclayout->oobfree[section].length;
1117-
1118-
return 0;
1078+
return mtd->ooblayout->free(mtd, section, oobfree);
11191079
}
11201080
EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
11211081

@@ -1416,6 +1376,123 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
14161376
}
14171377
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
14181378

1379+
/**
1380+
* mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation
1381+
* @mtd: MTD device structure
1382+
* @section: ECC section. Depending on the layout you may have all the ECC
1383+
* bytes stored in a single contiguous section, or one section
1384+
* per ECC chunk (and sometime several sections for a single ECC
1385+
* ECC chunk)
1386+
* @oobecc: OOB region struct filled with the appropriate ECC position
1387+
* information
1388+
*
1389+
* This function is just a wrapper around the mtd->ecclayout field and is
1390+
* here to ease the transition to the mtd_ooblayout_ops approach.
1391+
* All it does is convert the layout->eccpos information into proper oob
1392+
* region definitions.
1393+
*
1394+
* Returns zero on success, a negative error code otherwise.
1395+
*/
1396+
static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section,
1397+
struct mtd_oob_region *oobecc)
1398+
{
1399+
int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
1400+
1401+
if (!mtd->ecclayout)
1402+
return -ENOTSUPP;
1403+
1404+
/*
1405+
* This logic allows us to reuse the ->ecclayout information and
1406+
* expose them as ECC regions (as done for the OOB free regions).
1407+
*
1408+
* TODO: this should be dropped as soon as we get rid of the
1409+
* ->ecclayout field.
1410+
*/
1411+
for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
1412+
eccpos = mtd->ecclayout->eccpos[eccbyte];
1413+
1414+
if (eccbyte < mtd->ecclayout->eccbytes - 1) {
1415+
int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
1416+
1417+
if (eccpos + 1 == neccpos) {
1418+
length++;
1419+
continue;
1420+
}
1421+
}
1422+
1423+
if (section == cursection)
1424+
break;
1425+
1426+
length = 0;
1427+
cursection++;
1428+
}
1429+
1430+
if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
1431+
return -ERANGE;
1432+
1433+
oobecc->length = length + 1;
1434+
oobecc->offset = eccpos - length;
1435+
1436+
return 0;
1437+
}
1438+
1439+
/**
1440+
* mtd_ecclayout_ecc - Default ooblayout_free iterator implementation
1441+
* @mtd: MTD device structure
1442+
* @section: Free section. Depending on the layout you may have all the free
1443+
* bytes stored in a single contiguous section, or one section
1444+
* per ECC chunk (and sometime several sections for a single ECC
1445+
* ECC chunk)
1446+
* @oobfree: OOB region struct filled with the appropriate free position
1447+
* information
1448+
*
1449+
* This function is just a wrapper around the mtd->ecclayout field and is
1450+
* here to ease the transition to the mtd_ooblayout_ops approach.
1451+
* All it does is convert the layout->oobfree information into proper oob
1452+
* region definitions.
1453+
*
1454+
* Returns zero on success, a negative error code otherwise.
1455+
*/
1456+
static int mtd_ecclayout_free(struct mtd_info *mtd, int section,
1457+
struct mtd_oob_region *oobfree)
1458+
{
1459+
struct nand_ecclayout *layout = mtd->ecclayout;
1460+
1461+
if (!layout)
1462+
return -ENOTSUPP;
1463+
1464+
if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE ||
1465+
!layout->oobfree[section].length)
1466+
return -ERANGE;
1467+
1468+
oobfree->offset = layout->oobfree[section].offset;
1469+
oobfree->length = layout->oobfree[section].length;
1470+
1471+
return 0;
1472+
}
1473+
1474+
static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = {
1475+
.ecc = mtd_ecclayout_ecc,
1476+
.free = mtd_ecclayout_free,
1477+
};
1478+
1479+
/**
1480+
* mtd_set_ecclayout - Attach an ecclayout to an MTD device
1481+
* @mtd: MTD device structure
1482+
* @ecclayout: The ecclayout to attach to the device
1483+
*
1484+
* Returns zero on success, a negative error code otherwise.
1485+
*/
1486+
void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout)
1487+
{
1488+
if (!mtd || !ecclayout)
1489+
return;
1490+
1491+
mtd->ecclayout = ecclayout;
1492+
mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops);
1493+
}
1494+
EXPORT_SYMBOL_GPL(mtd_set_ecclayout);
1495+
14191496
/*
14201497
* Method to access the protection register area, present in some flash
14211498
* devices. The user data is one time programmable but the factory data is read

drivers/mtd/mtdpart.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
317317
return res;
318318
}
319319

320+
static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
321+
struct mtd_oob_region *oobregion)
322+
{
323+
struct mtd_part *part = mtd_to_part(mtd);
324+
325+
return mtd_ooblayout_ecc(part->master, section, oobregion);
326+
}
327+
328+
static int part_ooblayout_free(struct mtd_info *mtd, int section,
329+
struct mtd_oob_region *oobregion)
330+
{
331+
struct mtd_part *part = mtd_to_part(mtd);
332+
333+
return mtd_ooblayout_free(part->master, section, oobregion);
334+
}
335+
336+
static const struct mtd_ooblayout_ops part_ooblayout_ops = {
337+
.ecc = part_ooblayout_ecc,
338+
.free = part_ooblayout_free,
339+
};
340+
320341
static inline void free_partition(struct mtd_part *p)
321342
{
322343
kfree(p->mtd.name);
@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
533554
part->name);
534555
}
535556

536-
mtd_set_ecclayout(&slave->mtd, master->ecclayout);
557+
mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
537558
slave->mtd.ecc_step_size = master->ecc_step_size;
538559
slave->mtd.ecc_strength = master->ecc_strength;
539560
slave->mtd.bitflip_threshold = master->bitflip_threshold;

include/linux/mtd/mtd.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ struct mtd_oob_ops {
101101
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
102102
* for export to user-space via the ECCGETLAYOUT ioctl.
103103
* nand_ecclayout should be expandable in the future simply by the above macros.
104+
*
105+
* This structure is now deprecated, you should use struct nand_ecclayout_ops
106+
* to describe your OOB layout.
104107
*/
105108
struct nand_ecclayout {
106109
__u32 eccbytes;
@@ -123,6 +126,22 @@ struct mtd_oob_region {
123126
u32 length;
124127
};
125128

129+
/*
130+
* struct mtd_ooblayout_ops - NAND OOB layout operations
131+
* @ecc: function returning an ECC region in the OOB area.
132+
* Should return -ERANGE if %section exceeds the total number of
133+
* ECC sections.
134+
* @free: function returning a free region in the OOB area.
135+
* Should return -ERANGE if %section exceeds the total number of
136+
* free sections.
137+
*/
138+
struct mtd_ooblayout_ops {
139+
int (*ecc)(struct mtd_info *mtd, int section,
140+
struct mtd_oob_region *oobecc);
141+
int (*free)(struct mtd_info *mtd, int section,
142+
struct mtd_oob_region *oobfree);
143+
};
144+
126145
struct module; /* only needed for owner field in mtd_info */
127146

128147
struct mtd_info {
@@ -181,9 +200,12 @@ struct mtd_info {
181200
const char *name;
182201
int index;
183202

184-
/* ECC layout structure pointer - read only! */
203+
/* [Deprecated] ECC layout structure pointer - read only! */
185204
struct nand_ecclayout *ecclayout;
186205

206+
/* OOB layout description */
207+
const struct mtd_ooblayout_ops *ooblayout;
208+
187209
/* the ecc step size. */
188210
unsigned int ecc_step_size;
189211

@@ -286,10 +308,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
286308
int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
287309
int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
288310

289-
static inline void mtd_set_ecclayout(struct mtd_info *mtd,
290-
struct nand_ecclayout *ecclayout)
311+
void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout);
312+
313+
static inline void mtd_set_ooblayout(struct mtd_info *mtd,
314+
const struct mtd_ooblayout_ops *ooblayout)
291315
{
292-
mtd->ecclayout = ecclayout;
316+
mtd->ooblayout = ooblayout;
293317
}
294318

295319
static inline void mtd_set_of_node(struct mtd_info *mtd,

0 commit comments

Comments
 (0)