Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[MTD] [NAND] remove len/ooblen confusion.

As was discussed between Ricard Wanderlöf, David Woodhouse, Artem
Bityutskiy and me, the current API for reading/writing OOB is confusing.

The thing that introduces confusion is the need to specify ops.len
together with ops.ooblen for reads/writes that concern only OOB not data
area. So, ops.len is overloaded: when ops.datbuf != NULL it serves to
specify the length of the data read, and when ops.datbuf == NULL, it
serves to specify the full OOB read length.

The patch inlined below is the slightly updated version of the previous
patch serving the same purpose, but with the new Artem's comments taken
into account.

Artem, BTW, thanks a lot for your valuable input!

Signed-off-by: Vitaly Wool <vwool@ru.mvista.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>

authored by

Vitaly Wool and committed by
David Woodhouse
7014568b 19187672

+88 -73
+2 -4
drivers/mtd/inftlcore.c
··· 163 163 ops.ooblen = len; 164 164 ops.oobbuf = buf; 165 165 ops.datbuf = NULL; 166 - ops.len = len; 167 166 168 167 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 169 - *retlen = ops.retlen; 168 + *retlen = ops.oobretlen; 170 169 return res; 171 170 } 172 171 ··· 183 184 ops.ooblen = len; 184 185 ops.oobbuf = buf; 185 186 ops.datbuf = NULL; 186 - ops.len = len; 187 187 188 188 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 189 - *retlen = ops.retlen; 189 + *retlen = ops.oobretlen; 190 190 return res; 191 191 } 192 192
+5 -7
drivers/mtd/mtdchar.c
··· 499 499 if (ret) 500 500 return ret; 501 501 502 - ops.len = buf.length; 503 502 ops.ooblen = buf.length; 504 503 ops.ooboffs = buf.start & (mtd->oobsize - 1); 505 504 ops.datbuf = NULL; 506 505 ops.mode = MTD_OOB_PLACE; 507 506 508 - if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) 507 + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 509 508 return -EINVAL; 510 509 511 510 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); ··· 519 520 buf.start &= ~(mtd->oobsize - 1); 520 521 ret = mtd->write_oob(mtd, buf.start, &ops); 521 522 522 - if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, 523 + if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, 523 524 sizeof(uint32_t))) 524 525 ret = -EFAULT; 525 526 ··· 547 548 if (ret) 548 549 return ret; 549 550 550 - ops.len = buf.length; 551 551 ops.ooblen = buf.length; 552 552 ops.ooboffs = buf.start & (mtd->oobsize - 1); 553 553 ops.datbuf = NULL; ··· 562 564 buf.start &= ~(mtd->oobsize - 1); 563 565 ret = mtd->read_oob(mtd, buf.start, &ops); 564 566 565 - if (put_user(ops.retlen, (uint32_t __user *)argp)) 567 + if (put_user(ops.oobretlen, (uint32_t __user *)argp)) 566 568 ret = -EFAULT; 567 - else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, 568 - ops.retlen)) 569 + else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, 570 + ops.oobretlen)) 569 571 ret = -EFAULT; 570 572 571 573 kfree(ops.oobbuf);
+24 -15
drivers/mtd/mtdconcat.c
··· 247 247 struct mtd_oob_ops devops = *ops; 248 248 int i, err, ret = 0; 249 249 250 - ops->retlen = 0; 250 + ops->retlen = ops->oobretlen = 0; 251 251 252 252 for (i = 0; i < concat->num_subdev; i++) { 253 253 struct mtd_info *subdev = concat->subdev[i]; ··· 263 263 264 264 err = subdev->read_oob(subdev, from, &devops); 265 265 ops->retlen += devops.retlen; 266 + ops->oobretlen += devops.oobretlen; 266 267 267 268 /* Save information about bitflips! */ 268 269 if (unlikely(err)) { ··· 279 278 return err; 280 279 } 281 280 282 - devops.len = ops->len - ops->retlen; 283 - if (!devops.len) 284 - return ret; 285 - 286 - if (devops.datbuf) 281 + if (devops.datbuf) { 282 + devops.len = ops->len - ops->retlen; 283 + if (!devops.len) 284 + return ret; 287 285 devops.datbuf += devops.retlen; 288 - if (devops.oobbuf) 289 - devops.oobbuf += devops.ooblen; 286 + } 287 + if (devops.oobbuf) { 288 + devops.ooblen = ops->ooblen - ops->oobretlen; 289 + if (!devops.ooblen) 290 + return ret; 291 + devops.oobbuf += ops->oobretlen; 292 + } 290 293 291 294 from = 0; 292 295 } ··· 326 321 if (err) 327 322 return err; 328 323 329 - devops.len = ops->len - ops->retlen; 330 - if (!devops.len) 331 - return 0; 332 - 333 - if (devops.datbuf) 324 + if (devops.datbuf) { 325 + devops.len = ops->len - ops->retlen; 326 + if (!devops.len) 327 + return 0; 334 328 devops.datbuf += devops.retlen; 335 - if (devops.oobbuf) 336 - devops.oobbuf += devops.ooblen; 329 + } 330 + if (devops.oobbuf) { 331 + devops.ooblen = ops->ooblen - ops->oobretlen; 332 + if (!devops.ooblen) 333 + return 0; 334 + devops.oobbuf += devops.oobretlen; 335 + } 337 336 to = 0; 338 337 } 339 338 return -EINVAL;
+2 -2
drivers/mtd/mtdpart.c
··· 94 94 95 95 if (from >= mtd->size) 96 96 return -EINVAL; 97 - if (from + ops->len > mtd->size) 97 + if (ops->datbuf && from + ops->len > mtd->size) 98 98 return -EINVAL; 99 99 res = part->master->read_oob(part->master, from + part->offset, ops); 100 100 ··· 161 161 162 162 if (to >= mtd->size) 163 163 return -EINVAL; 164 - if (to + ops->len > mtd->size) 164 + if (ops->datbuf && to + ops->len > mtd->size) 165 165 return -EINVAL; 166 166 return part->master->write_oob(part->master, to + part->offset, ops); 167 167 }
+35 -16
drivers/mtd/nand/nand_base.c
··· 897 897 * @chip: nand chip structure 898 898 * @oob: oob destination address 899 899 * @ops: oob ops structure 900 + * @len: size of oob to transfer 900 901 */ 901 902 static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, 902 - struct mtd_oob_ops *ops) 903 + struct mtd_oob_ops *ops, size_t len) 903 904 { 904 - size_t len = ops->ooblen; 905 - 906 905 switch(ops->mode) { 907 906 908 907 case MTD_OOB_PLACE: ··· 959 960 int sndcmd = 1; 960 961 int ret = 0; 961 962 uint32_t readlen = ops->len; 963 + uint32_t oobreadlen = ops->ooblen; 962 964 uint8_t *bufpoi, *oob, *buf; 963 965 964 966 stats = mtd->ecc_stats; ··· 1006 1006 1007 1007 if (unlikely(oob)) { 1008 1008 /* Raw mode does data:oob:data:oob */ 1009 - if (ops->mode != MTD_OOB_RAW) 1010 - oob = nand_transfer_oob(chip, oob, ops); 1011 - else 1012 - buf = nand_transfer_oob(chip, buf, ops); 1009 + if (ops->mode != MTD_OOB_RAW) { 1010 + int toread = min(oobreadlen, 1011 + chip->ecc.layout->oobavail); 1012 + if (toread) { 1013 + oob = nand_transfer_oob(chip, 1014 + oob, ops, toread); 1015 + oobreadlen -= toread; 1016 + } 1017 + } else 1018 + buf = nand_transfer_oob(chip, 1019 + buf, ops, mtd->oobsize); 1013 1020 } 1014 1021 1015 1022 if (!(chip->options & NAND_NO_READRDY)) { ··· 1063 1056 } 1064 1057 1065 1058 ops->retlen = ops->len - (size_t) readlen; 1059 + if (oob) 1060 + ops->oobretlen = ops->ooblen - oobreadlen; 1066 1061 1067 1062 if (ret) 1068 1063 return ret; ··· 1265 1256 int page, realpage, chipnr, sndcmd = 1; 1266 1257 struct nand_chip *chip = mtd->priv; 1267 1258 int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; 1268 - int readlen = ops->len; 1259 + int readlen = ops->ooblen; 1260 + int len; 1269 1261 uint8_t *buf = ops->oobbuf; 1270 1262 1271 1263 DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", 1272 1264 (unsigned long long)from, readlen); 1265 + 1266 + if (ops->mode == MTD_OOB_RAW) 1267 + len = mtd->oobsize; 1268 + else 1269 + len = chip->ecc.layout->oobavail; 1273 1270 1274 1271 chipnr = (int)(from >> chip->chip_shift); 1275 1272 chip->select_chip(mtd, chipnr); ··· 1286 1271 1287 1272 while(1) { 1288 1273 sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); 1289 - buf = nand_transfer_oob(chip, buf, ops); 1274 + 1275 + len = min(len, readlen); 1276 + buf = nand_transfer_oob(chip, buf, ops, len); 1290 1277 1291 1278 if (!(chip->options & NAND_NO_READRDY)) { 1292 1279 /* ··· 1303 1286 nand_wait_ready(mtd); 1304 1287 } 1305 1288 1306 - readlen -= ops->ooblen; 1289 + readlen -= len; 1307 1290 if (!readlen) 1308 1291 break; 1309 1292 ··· 1325 1308 sndcmd = 1; 1326 1309 } 1327 1310 1328 - ops->retlen = ops->len; 1311 + ops->oobretlen = ops->ooblen; 1329 1312 return 0; 1330 1313 } 1331 1314 ··· 1346 1329 ops->retlen = 0; 1347 1330 1348 1331 /* Do not allow reads past end of device */ 1349 - if ((from + ops->len) > mtd->size) { 1332 + if (ops->datbuf && (from + ops->len) > mtd->size) { 1350 1333 DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " 1351 1334 "Attempt read beyond end of device\n"); 1352 1335 return -EINVAL; ··· 1671 1654 } 1672 1655 1673 1656 ops->retlen = ops->len - writelen; 1657 + if (unlikely(oob)) 1658 + ops->oobretlen = ops->ooblen; 1674 1659 return ret; 1675 1660 } 1676 1661 ··· 1728 1709 struct nand_chip *chip = mtd->priv; 1729 1710 1730 1711 DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", 1731 - (unsigned int)to, (int)ops->len); 1712 + (unsigned int)to, (int)ops->ooblen); 1732 1713 1733 1714 /* Do not allow write past end of page */ 1734 - if ((ops->ooboffs + ops->len) > mtd->oobsize) { 1715 + if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { 1735 1716 DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " 1736 1717 "Attempt to write past end of page\n"); 1737 1718 return -EINVAL; ··· 1767 1748 if (status) 1768 1749 return status; 1769 1750 1770 - ops->retlen = ops->len; 1751 + ops->oobretlen = ops->ooblen; 1771 1752 1772 1753 return 0; 1773 1754 } ··· 1787 1768 ops->retlen = 0; 1788 1769 1789 1770 /* Do not allow writes past end of device */ 1790 - if ((to + ops->len) > mtd->size) { 1771 + if (ops->datbuf && (to + ops->len) > mtd->size) { 1791 1772 DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " 1792 1773 "Attempt read beyond end of device\n"); 1793 1774 return -EINVAL;
+2 -3
drivers/mtd/nand/nand_bbt.c
··· 333 333 struct mtd_oob_ops ops; 334 334 int j, ret; 335 335 336 - ops.len = mtd->oobsize; 337 336 ops.ooblen = mtd->oobsize; 338 337 ops.oobbuf = buf; 339 338 ops.ooboffs = 0; ··· 675 676 "bad block table\n"); 676 677 } 677 678 /* Read oob data */ 678 - ops.len = (len >> this->page_shift) * mtd->oobsize; 679 + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; 679 680 ops.oobbuf = &buf[len]; 680 681 res = mtd->read_oob(mtd, to + mtd->writesize, &ops); 681 - if (res < 0 || ops.retlen != ops.len) 682 + if (res < 0 || ops.oobretlen != ops.ooblen) 682 683 goto outerr; 683 684 684 685 /* Calc the byte offset in the buffer */
+2 -4
drivers/mtd/nftlcore.c
··· 147 147 ops.ooblen = len; 148 148 ops.oobbuf = buf; 149 149 ops.datbuf = NULL; 150 - ops.len = len; 151 150 152 151 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 153 - *retlen = ops.retlen; 152 + *retlen = ops.oobretlen; 154 153 return res; 155 154 } 156 155 ··· 167 168 ops.ooblen = len; 168 169 ops.oobbuf = buf; 169 170 ops.datbuf = NULL; 170 - ops.len = len; 171 171 172 172 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); 173 - *retlen = ops.retlen; 173 + *retlen = ops.oobretlen; 174 174 return res; 175 175 } 176 176
+2 -3
drivers/mtd/ssfdc.c
··· 172 172 173 173 ops.mode = MTD_OOB_RAW; 174 174 ops.ooboffs = 0; 175 - ops.ooblen = mtd->oobsize; 176 - ops.len = OOB_SIZE; 175 + ops.ooblen = OOB_SIZE; 177 176 ops.oobbuf = buf; 178 177 ops.datbuf = NULL; 179 178 180 179 ret = mtd->read_oob(mtd, offs, &ops); 181 - if (ret < 0 || ops.retlen != OOB_SIZE) 180 + if (ret < 0 || ops.oobretlen != OOB_SIZE) 182 181 return -1; 183 182 184 183 return 0;
+9 -12
fs/jffs2/wbuf.c
··· 968 968 int oobsize = c->mtd->oobsize; 969 969 struct mtd_oob_ops ops; 970 970 971 - ops.len = NR_OOB_SCAN_PAGES * oobsize; 972 - ops.ooblen = oobsize; 971 + ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; 973 972 ops.oobbuf = c->oobbuf; 974 973 ops.ooboffs = 0; 975 974 ops.datbuf = NULL; ··· 981 982 return ret; 982 983 } 983 984 984 - if (ops.retlen < ops.len) { 985 + if (ops.oobretlen < ops.ooblen) { 985 986 D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " 986 987 "returned short read (%zd bytes not %d) for block " 987 - "at %08x\n", ops.retlen, ops.len, jeb->offset)); 988 + "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); 988 989 return -EIO; 989 990 } 990 991 ··· 1003 1004 } 1004 1005 1005 1006 /* we know, we are aligned :) */ 1006 - for (page = oobsize; page < ops.len; page += sizeof(long)) { 1007 + for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { 1007 1008 long dat = *(long *)(&ops.oobbuf[page]); 1008 1009 if(dat != -1) 1009 1010 return 1; ··· 1031 1032 return 2; 1032 1033 } 1033 1034 1034 - ops.len = oobsize; 1035 1035 ops.ooblen = oobsize; 1036 1036 ops.oobbuf = c->oobbuf; 1037 1037 ops.ooboffs = 0; ··· 1045 1047 return ret; 1046 1048 } 1047 1049 1048 - if (ops.retlen < ops.len) { 1050 + if (ops.oobretlen < ops.ooblen) { 1049 1051 D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " 1050 1052 "Read OOB return short read (%zd bytes not %d) " 1051 - "for block at %08x\n", ops.retlen, ops.len, 1053 + "for block at %08x\n", ops.oobretlen, ops.ooblen, 1052 1054 jeb->offset)); 1053 1055 return -EIO; 1054 1056 } ··· 1087 1089 n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); 1088 1090 n.totlen = cpu_to_je32(8); 1089 1091 1090 - ops.len = c->fsdata_len; 1091 - ops.ooblen = c->fsdata_len;; 1092 + ops.ooblen = c->fsdata_len; 1092 1093 ops.oobbuf = (uint8_t *)&n; 1093 1094 ops.ooboffs = c->fsdata_pos; 1094 1095 ops.datbuf = NULL; ··· 1101 1104 jeb->offset, ret)); 1102 1105 return ret; 1103 1106 } 1104 - if (ops.retlen != ops.len) { 1107 + if (ops.oobretlen != ops.ooblen) { 1105 1108 D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " 1106 1109 "Short write for block at %08x: %zd not %d\n", 1107 - jeb->offset, ops.retlen, ops.len)); 1110 + jeb->offset, ops.oobretlen, ops.ooblen)); 1108 1111 return -EIO; 1109 1112 } 1110 1113 return 0;
+5 -7
include/linux/mtd/mtd.h
··· 75 75 * struct mtd_oob_ops - oob operation operands 76 76 * @mode: operation mode 77 77 * 78 - * @len: number of bytes to write/read. When a data buffer is given 79 - * (datbuf != NULL) this is the number of data bytes. When 80 - * no data buffer is available this is the number of oob bytes. 78 + * @len: number of data bytes to write/read 81 79 * 82 - * @retlen: number of bytes written/read. When a data buffer is given 83 - * (datbuf != NULL) this is the number of data bytes. When 84 - * no data buffer is available this is the number of oob bytes. 80 + * @retlen: number of data bytes written/read 85 81 * 86 - * @ooblen: number of oob bytes per page 82 + * @ooblen: number of oob bytes to write/read 83 + * @oobretlen: number of oob bytes written/read 87 84 * @ooboffs: offset of oob data in the oob area (only relevant when 88 85 * mode = MTD_OOB_PLACE) 89 86 * @datbuf: data buffer - if NULL only oob data are read/written ··· 91 94 size_t len; 92 95 size_t retlen; 93 96 size_t ooblen; 97 + size_t oobretlen; 94 98 uint32_t ooboffs; 95 99 uint8_t *datbuf; 96 100 uint8_t *oobbuf;