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

mtd: nand: expand nand_ecc_layout, deprecate ioctl ECCGETLAYOUT

struct nand_ecclayout is too small for many new chips; OOB regions can be as
large as 448 bytes and may increase more in the future. Thus, copying that
struct to user-space with the ECCGETLAYOUT ioctl is not a good idea; the ioctl
would have to be updated every time there's a change to the current largest
size.

Instead, the old nand_ecclayout is renamed to nand_ecclayout_user and a
new struct nand_ecclayout is created that can accomodate larger sizes and
expand without affecting the user-space. struct nand_ecclayout can still
be used in board drivers without modification -- at least for now.

A new function is provided to convert from the new to the old in order to
allow the deprecated ioctl to continue to work with truncated data. Perhaps
the ioctl, the conversion process, and the struct nand_ecclayout_user can be
removed altogether in the future.

Note: There are comments in nand/davinci_nand.c::nand_davinci_probe()
regarding this issue; this driver (and maybe others) can be updated to
account for extra space. All kernel drivers can use the expanded
nand_ecclayout as a drop-in replacement and ignore its benefits.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

Brian Norris and committed by
David Woodhouse
cc26c3cd f4a2da0c

+67 -7
+45 -3
drivers/mtd/mtdchar.c
··· 477 477 return ret; 478 478 } 479 479 480 + /* 481 + * Copies (and truncates, if necessary) data from the larger struct, 482 + * nand_ecclayout, to the smaller, deprecated layout struct, 483 + * nand_ecclayout_user. This is necessary only to suppport the deprecated 484 + * API ioctl ECCGETLAYOUT while allowing all new functionality to use 485 + * nand_ecclayout flexibly (i.e. the struct may change size in new 486 + * releases without requiring major rewrites). 487 + */ 488 + static int shrink_ecclayout(const struct nand_ecclayout *from, 489 + struct nand_ecclayout_user *to) 490 + { 491 + int i; 492 + 493 + if (!from || !to) 494 + return -EINVAL; 495 + 496 + memset(to, 0, sizeof(*to)); 497 + 498 + to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES_OLD); 499 + for (i = 0; i < to->eccbytes; i++) 500 + to->eccpos[i] = from->eccpos[i]; 501 + 502 + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { 503 + if (from->oobfree[i].length == 0 && 504 + from->oobfree[i].offset == 0) 505 + break; 506 + to->oobavail += from->oobfree[i].length; 507 + to->oobfree[i] = from->oobfree[i]; 508 + } 509 + 510 + return 0; 511 + } 512 + 480 513 static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) 481 514 { 482 515 struct mtd_file_info *mfi = file->private_data; ··· 845 812 } 846 813 #endif 847 814 815 + /* This ioctl is being deprecated - it truncates the ecc layout */ 848 816 case ECCGETLAYOUT: 849 817 { 818 + struct nand_ecclayout_user *usrlay; 819 + 850 820 if (!mtd->ecclayout) 851 821 return -EOPNOTSUPP; 852 822 853 - if (copy_to_user(argp, mtd->ecclayout, 854 - sizeof(struct nand_ecclayout))) 855 - return -EFAULT; 823 + usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); 824 + if (!usrlay) 825 + return -ENOMEM; 826 + 827 + shrink_ecclayout(mtd->ecclayout, usrlay); 828 + 829 + if (copy_to_user(argp, usrlay, sizeof(*usrlay))) 830 + ret = -EFAULT; 831 + kfree(usrlay); 856 832 break; 857 833 } 858 834
+3
drivers/mtd/nand/davinci_nand.c
··· 749 749 * breaks userspace ioctl interface with mtd-utils. Once we 750 750 * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used 751 751 * for the 4KiB page chips. 752 + * 753 + * TODO: Note that nand_ecclayout has now been expanded and can 754 + * hold plenty of OOB entries. 752 755 */ 753 756 dev_warn(&pdev->dev, "no 4-bit ECC support yet " 754 757 "for 4KiB-page NAND\n");
+15
include/linux/mtd/mtd.h
··· 110 110 uint8_t *oobbuf; 111 111 }; 112 112 113 + #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32 114 + #define MTD_MAX_ECCPOS_ENTRIES_LARGE 448 115 + #define MTD_MAX_ECCPOS_ENTRIES_OLD 64 /* Previous maximum */ 116 + /* 117 + * Correct ECC layout control structure. This replaces old nand_ecclayout 118 + * (mtd-abi.h) that is exported via ECCGETLAYOUT ioctl. It should be expandable 119 + * in the future simply by the above macros. 120 + */ 121 + struct nand_ecclayout { 122 + __u32 eccbytes; 123 + __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; 124 + __u32 oobavail; 125 + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; 126 + }; 127 + 113 128 struct mtd_info { 114 129 u_char type; 115 130 uint32_t flags;
+1 -1
include/linux/mtd/partitions.h
··· 39 39 uint64_t size; /* partition size */ 40 40 uint64_t offset; /* offset within the master MTD space */ 41 41 uint32_t mask_flags; /* master MTD flags to mask out for this partition */ 42 - struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ 42 + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */ 43 43 }; 44 44 45 45 #define MTDPART_OFS_NXTBLK (-2)
+2 -2
include/mtd/mtd-abi.h
··· 119 119 #define OTPGETREGIONCOUNT _IOW('M', 14, int) 120 120 #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) 121 121 #define OTPLOCK _IOR('M', 16, struct otp_info) 122 - #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) 122 + #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user) 123 123 #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) 124 124 #define MTDFILEMODE _IO('M', 19) 125 125 #define MEMERASE64 _IOW('M', 20, struct erase_info_user64) ··· 148 148 * ECC layout control structure. Exported to userspace for 149 149 * diagnosis and to allow creation of raw images 150 150 */ 151 - struct nand_ecclayout { 151 + struct nand_ecclayout_user { 152 152 __u32 eccbytes; 153 153 __u32 eccpos[64]; 154 154 __u32 oobavail;
+1 -1
include/mtd/mtd-user.h
··· 29 29 typedef struct erase_info_user erase_info_t; 30 30 typedef struct region_info_user region_info_t; 31 31 typedef struct nand_oobinfo nand_oobinfo_t; 32 - typedef struct nand_ecclayout nand_ecclayout_t; 32 + typedef struct nand_ecclayout_user nand_ecclayout_t; 33 33 34 34 #endif /* __MTD_USER_H__ */