aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishal Verma <vishal.l.verma@intel.com>2019-02-27 19:06:27 -0500
committerDan Williams <dan.j.williams@intel.com>2019-02-28 12:57:39 -0500
commit9dedc73a4658ebcc0c9b58c3cb84e9ac80122213 (patch)
tree99e5a2f80c5820914f912b43713ff13e983dd1c9
parent2f8c9011151337d0bc106693f272f9bddbccfab2 (diff)
libnvdimm/btt: Fix LBA masking during 'free list' population
The Linux BTT implementation assumes that log entries will never have the 'zero' flag set, and indeed it never sets that flag for log entries itself. However, the UEFI spec is ambiguous on the exact format of the LBA field of a log entry, specifically as to whether it should include the additional flag bits or not. While a zero bit doesn't make sense in the context of a log entry, other BTT implementations might still have it set. If an implementation does happen to have it set, we would happily read it in as the next block to write to for writes. Since a high bit is set, it pushes the block number out of the range of an 'arena', and we fail such a write with an EIO. Follow the robustness principle, and tolerate such implementations by stripping out the zero flag when populating the free list during initialization. Additionally, use the same stripped out entries for detection of incomplete writes and map restoration that happens at this stage. Add a sysfs file 'log_zero_flags' that indicates the ability to accept such a layout to userspace applications. This enables 'ndctl check-namespace' to recognize whether the kernel is able to handle zero flags, or whether it should attempt a fix-up under the --repair option. Cc: Dan Williams <dan.j.williams@intel.com> Reported-by: Dexuan Cui <decui@microsoft.com> Reported-by: Pedro d'Aquino Filocre F S Barbuda <pbarbuda@microsoft.com> Tested-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/nvdimm/btt.c25
-rw-r--r--drivers/nvdimm/btt.h2
-rw-r--r--drivers/nvdimm/btt_devs.c8
3 files changed, 29 insertions, 6 deletions
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index cd4fa87ea48c..4671776f5623 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -542,8 +542,8 @@ static int arena_clear_freelist_error(struct arena_info *arena, u32 lane)
542static int btt_freelist_init(struct arena_info *arena) 542static int btt_freelist_init(struct arena_info *arena)
543{ 543{
544 int new, ret; 544 int new, ret;
545 u32 i, map_entry;
546 struct log_entry log_new; 545 struct log_entry log_new;
546 u32 i, map_entry, log_oldmap, log_newmap;
547 547
548 arena->freelist = kcalloc(arena->nfree, sizeof(struct free_entry), 548 arena->freelist = kcalloc(arena->nfree, sizeof(struct free_entry),
549 GFP_KERNEL); 549 GFP_KERNEL);
@@ -555,16 +555,22 @@ static int btt_freelist_init(struct arena_info *arena)
555 if (new < 0) 555 if (new < 0)
556 return new; 556 return new;
557 557
558 /* old and new map entries with any flags stripped out */
559 log_oldmap = ent_lba(le32_to_cpu(log_new.old_map));
560 log_newmap = ent_lba(le32_to_cpu(log_new.new_map));
561
558 /* sub points to the next one to be overwritten */ 562 /* sub points to the next one to be overwritten */
559 arena->freelist[i].sub = 1 - new; 563 arena->freelist[i].sub = 1 - new;
560 arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq)); 564 arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq));
561 arena->freelist[i].block = le32_to_cpu(log_new.old_map); 565 arena->freelist[i].block = log_oldmap;
562 566
563 /* 567 /*
564 * FIXME: if error clearing fails during init, we want to make 568 * FIXME: if error clearing fails during init, we want to make
565 * the BTT read-only 569 * the BTT read-only
566 */ 570 */
567 if (ent_e_flag(log_new.old_map)) { 571 if (ent_e_flag(log_new.old_map) &&
572 !ent_normal(log_new.old_map)) {
573 arena->freelist[i].has_err = 1;
568 ret = arena_clear_freelist_error(arena, i); 574 ret = arena_clear_freelist_error(arena, i);
569 if (ret) 575 if (ret)
570 dev_err_ratelimited(to_dev(arena), 576 dev_err_ratelimited(to_dev(arena),
@@ -572,7 +578,7 @@ static int btt_freelist_init(struct arena_info *arena)
572 } 578 }
573 579
574 /* This implies a newly created or untouched flog entry */ 580 /* This implies a newly created or untouched flog entry */
575 if (log_new.old_map == log_new.new_map) 581 if (log_oldmap == log_newmap)
576 continue; 582 continue;
577 583
578 /* Check if map recovery is needed */ 584 /* Check if map recovery is needed */
@@ -580,8 +586,15 @@ static int btt_freelist_init(struct arena_info *arena)
580 NULL, NULL, 0); 586 NULL, NULL, 0);
581 if (ret) 587 if (ret)
582 return ret; 588 return ret;
583 if ((le32_to_cpu(log_new.new_map) != map_entry) && 589
584 (le32_to_cpu(log_new.old_map) == map_entry)) { 590 /*
591 * The map_entry from btt_read_map is stripped of any flag bits,
592 * so use the stripped out versions from the log as well for
593 * testing whether recovery is needed. For restoration, use the
594 * 'raw' version of the log entries as that captured what we
595 * were going to write originally.
596 */
597 if ((log_newmap != map_entry) && (log_oldmap == map_entry)) {
585 /* 598 /*
586 * Last transaction wrote the flog, but wasn't able 599 * Last transaction wrote the flog, but wasn't able
587 * to complete the map write. So fix up the map. 600 * to complete the map write. So fix up the map.
diff --git a/drivers/nvdimm/btt.h b/drivers/nvdimm/btt.h
index db3cb6d4d0d4..ddff49c707b0 100644
--- a/drivers/nvdimm/btt.h
+++ b/drivers/nvdimm/btt.h
@@ -44,6 +44,8 @@
44#define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK)) 44#define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK))
45#define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK)) 45#define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK))
46#define set_e_flag(ent) (ent |= MAP_ERR_MASK) 46#define set_e_flag(ent) (ent |= MAP_ERR_MASK)
47/* 'normal' is both e and z flags set */
48#define ent_normal(ent) (ent_e_flag(ent) && ent_z_flag(ent))
47 49
48enum btt_init_state { 50enum btt_init_state {
49 INIT_UNCHECKED = 0, 51 INIT_UNCHECKED = 0,
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index 795ad4ff35ca..b72a303176c7 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -159,11 +159,19 @@ static ssize_t size_show(struct device *dev,
159} 159}
160static DEVICE_ATTR_RO(size); 160static DEVICE_ATTR_RO(size);
161 161
162static ssize_t log_zero_flags_show(struct device *dev,
163 struct device_attribute *attr, char *buf)
164{
165 return sprintf(buf, "Y\n");
166}
167static DEVICE_ATTR_RO(log_zero_flags);
168
162static struct attribute *nd_btt_attributes[] = { 169static struct attribute *nd_btt_attributes[] = {
163 &dev_attr_sector_size.attr, 170 &dev_attr_sector_size.attr,
164 &dev_attr_namespace.attr, 171 &dev_attr_namespace.attr,
165 &dev_attr_uuid.attr, 172 &dev_attr_uuid.attr,
166 &dev_attr_size.attr, 173 &dev_attr_size.attr,
174 &dev_attr_log_zero_flags.attr,
167 NULL, 175 NULL,
168}; 176};
169 177