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

lightnvm: introduce mlc lower page table mappings

NAND MLC memories have both lower and upper pages. When programming,
both of these must be written, before data can be read. However,
these lower and upper pages might not placed at even and odd flash
pages, but can be skipped. Therefore each flash memory has its lower
pages defined, which can then be used when programming and to know when
padding are necessary.

This patch implements the lower page definition in the specification,
and exposes it through a simple lookup table at dev->lptbl.

Signed-off-by: Matias Bjørling <m@bjorling.me>
Signed-off-by: Jens Axboe <axboe@fb.com>

authored by

Matias Bjørling and committed by
Jens Axboe
ca5927e7 f9a99950

+101 -2
+60 -1
drivers/lightnvm/core.c
··· 362 362 } 363 363 EXPORT_SYMBOL(nvm_submit_ppa); 364 364 365 + static int nvm_init_slc_tbl(struct nvm_dev *dev, struct nvm_id_group *grp) 366 + { 367 + int i; 368 + 369 + dev->lps_per_blk = dev->pgs_per_blk; 370 + dev->lptbl = kcalloc(dev->lps_per_blk, sizeof(int), GFP_KERNEL); 371 + if (!dev->lptbl) 372 + return -ENOMEM; 373 + 374 + /* Just a linear array */ 375 + for (i = 0; i < dev->lps_per_blk; i++) 376 + dev->lptbl[i] = i; 377 + 378 + return 0; 379 + } 380 + 381 + static int nvm_init_mlc_tbl(struct nvm_dev *dev, struct nvm_id_group *grp) 382 + { 383 + int i, p; 384 + struct nvm_id_lp_mlc *mlc = &grp->lptbl.mlc; 385 + 386 + if (!mlc->num_pairs) 387 + return 0; 388 + 389 + dev->lps_per_blk = mlc->num_pairs; 390 + dev->lptbl = kcalloc(dev->lps_per_blk, sizeof(int), GFP_KERNEL); 391 + if (!dev->lptbl) 392 + return -ENOMEM; 393 + 394 + /* The lower page table encoding consists of a list of bytes, where each 395 + * has a lower and an upper half. The first half byte maintains the 396 + * increment value and every value after is an offset added to the 397 + * previous incrementation value */ 398 + dev->lptbl[0] = mlc->pairs[0] & 0xF; 399 + for (i = 1; i < dev->lps_per_blk; i++) { 400 + p = mlc->pairs[i >> 1]; 401 + if (i & 0x1) /* upper */ 402 + dev->lptbl[i] = dev->lptbl[i - 1] + ((p & 0xF0) >> 4); 403 + else /* lower */ 404 + dev->lptbl[i] = dev->lptbl[i - 1] + (p & 0xF); 405 + } 406 + 407 + return 0; 408 + } 409 + 365 410 static int nvm_core_init(struct nvm_dev *dev) 366 411 { 367 412 struct nvm_id *id = &dev->identity; ··· 432 387 return -EINVAL; 433 388 } 434 389 435 - if (grp->fmtype != 0 && grp->fmtype != 1) { 390 + switch (grp->fmtype) { 391 + case NVM_ID_FMTYPE_SLC: 392 + if (nvm_init_slc_tbl(dev, grp)) 393 + return -ENOMEM; 394 + break; 395 + case NVM_ID_FMTYPE_MLC: 396 + if (nvm_init_mlc_tbl(dev, grp)) 397 + return -ENOMEM; 398 + break; 399 + default: 436 400 pr_err("nvm: flash type not supported\n"); 437 401 return -EINVAL; 438 402 } 403 + 404 + if (!dev->lps_per_blk) 405 + pr_info("nvm: lower page programming table missing\n"); 439 406 440 407 if (grp->mpos & 0x020202) 441 408 dev->plane_mode = NVM_PLANE_DOUBLE; ··· 477 420 478 421 if (dev->mt) 479 422 dev->mt->unregister_mgr(dev); 423 + 424 + kfree(dev->lptbl); 480 425 } 481 426 482 427 static int nvm_init(struct nvm_dev *dev)
+21 -1
drivers/nvme/host/lightnvm.c
··· 146 146 }; 147 147 }; 148 148 149 + struct nvme_nvm_lp_mlc { 150 + __u16 num_pairs; 151 + __u8 pairs[886]; 152 + }; 153 + 154 + struct nvme_nvm_lp_tbl { 155 + __u8 id[8]; 156 + struct nvme_nvm_lp_mlc mlc; 157 + }; 158 + 149 159 struct nvme_nvm_id_group { 150 160 __u8 mtype; 151 161 __u8 fmtype; ··· 179 169 __le32 mpos; 180 170 __le32 mccap; 181 171 __le16 cpar; 182 - __u8 reserved[906]; 172 + __u8 reserved[10]; 173 + struct nvme_nvm_lp_tbl lptbl; 183 174 } __packed; 184 175 185 176 struct nvme_nvm_addr_format { ··· 277 266 dst->mccap = le32_to_cpu(src->mccap); 278 267 279 268 dst->cpar = le16_to_cpu(src->cpar); 269 + 270 + if (dst->fmtype == NVM_ID_FMTYPE_MLC) { 271 + memcpy(dst->lptbl.id, src->lptbl.id, 8); 272 + dst->lptbl.mlc.num_pairs = 273 + le16_to_cpu(src->lptbl.mlc.num_pairs); 274 + /* 4 bits per pair */ 275 + memcpy(dst->lptbl.mlc.pairs, src->lptbl.mlc.pairs, 276 + dst->lptbl.mlc.num_pairs >> 1); 277 + } 280 278 } 281 279 282 280 return 0;
+20
include/linux/lightnvm.h
··· 67 67 NVM_ID_CAP_CMD_SUSPEND = 0x2, 68 68 NVM_ID_CAP_SCRAMBLE = 0x4, 69 69 NVM_ID_CAP_ENCRYPT = 0x8, 70 + 71 + /* Memory types */ 72 + NVM_ID_FMTYPE_SLC = 0, 73 + NVM_ID_FMTYPE_MLC = 1, 74 + }; 75 + 76 + struct nvm_id_lp_mlc { 77 + u16 num_pairs; 78 + u8 pairs[886]; 79 + }; 80 + 81 + struct nvm_id_lp_tbl { 82 + __u8 id[8]; 83 + struct nvm_id_lp_mlc mlc; 70 84 }; 71 85 72 86 struct nvm_id_group { ··· 103 89 u32 mpos; 104 90 u32 mccap; 105 91 u16 cpar; 92 + 93 + struct nvm_id_lp_tbl lptbl; 106 94 }; 107 95 108 96 struct nvm_addr_format { ··· 312 296 int sec_per_pl; /* all sectors across planes */ 313 297 int sec_per_blk; 314 298 int sec_per_lun; 299 + 300 + /* lower page table */ 301 + int lps_per_blk; 302 + int *lptbl; 315 303 316 304 unsigned long total_pages; 317 305 unsigned long total_blocks;