Commit 16c791a5 authored by NeilBrown's avatar NeilBrown

md/bad-block-log: add sysfs interface for accessing bad-block-log.

This can show the log (providing it fits in one page) and
allows bad blocks to be 'acknowledged' meaning that they
have safely been recorded in metadata.

Clearing bad blocks is not allowed via sysfs (except for
code testing).  A bad block can only be cleared when
a write to the block succeeds.
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Reviewed-by: default avatarNamhyung Kim <namhyung@gmail.com>
parent 2230dfe4
...@@ -2711,6 +2711,35 @@ static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t le ...@@ -2711,6 +2711,35 @@ static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t le
static struct rdev_sysfs_entry rdev_recovery_start = static struct rdev_sysfs_entry rdev_recovery_start =
__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store); __ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
static ssize_t
badblocks_show(struct badblocks *bb, char *page, int unack);
static ssize_t
badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack);
static ssize_t bb_show(mdk_rdev_t *rdev, char *page)
{
return badblocks_show(&rdev->badblocks, page, 0);
}
static ssize_t bb_store(mdk_rdev_t *rdev, const char *page, size_t len)
{
return badblocks_store(&rdev->badblocks, page, len, 0);
}
static struct rdev_sysfs_entry rdev_bad_blocks =
__ATTR(bad_blocks, S_IRUGO|S_IWUSR, bb_show, bb_store);
static ssize_t ubb_show(mdk_rdev_t *rdev, char *page)
{
return badblocks_show(&rdev->badblocks, page, 1);
}
static ssize_t ubb_store(mdk_rdev_t *rdev, const char *page, size_t len)
{
return badblocks_store(&rdev->badblocks, page, len, 1);
}
static struct rdev_sysfs_entry rdev_unack_bad_blocks =
__ATTR(unacknowledged_bad_blocks, S_IRUGO|S_IWUSR, ubb_show, ubb_store);
static struct attribute *rdev_default_attrs[] = { static struct attribute *rdev_default_attrs[] = {
&rdev_state.attr, &rdev_state.attr,
&rdev_errors.attr, &rdev_errors.attr,
...@@ -2718,6 +2747,8 @@ static struct attribute *rdev_default_attrs[] = { ...@@ -2718,6 +2747,8 @@ static struct attribute *rdev_default_attrs[] = {
&rdev_offset.attr, &rdev_offset.attr,
&rdev_size.attr, &rdev_size.attr,
&rdev_recovery_start.attr, &rdev_recovery_start.attr,
&rdev_bad_blocks.attr,
&rdev_unack_bad_blocks.attr,
NULL, NULL,
}; };
static ssize_t static ssize_t
...@@ -7736,6 +7767,98 @@ void md_ack_all_badblocks(struct badblocks *bb) ...@@ -7736,6 +7767,98 @@ void md_ack_all_badblocks(struct badblocks *bb)
} }
EXPORT_SYMBOL_GPL(md_ack_all_badblocks); EXPORT_SYMBOL_GPL(md_ack_all_badblocks);
/* sysfs access to bad-blocks list.
* We present two files.
* 'bad-blocks' lists sector numbers and lengths of ranges that
* are recorded as bad. The list is truncated to fit within
* the one-page limit of sysfs.
* Writing "sector length" to this file adds an acknowledged
* bad block list.
* 'unacknowledged-bad-blocks' lists bad blocks that have not yet
* been acknowledged. Writing to this file adds bad blocks
* without acknowledging them. This is largely for testing.
*/
static ssize_t
badblocks_show(struct badblocks *bb, char *page, int unack)
{
size_t len;
int i;
u64 *p = bb->page;
unsigned seq;
if (bb->shift < 0)
return 0;
retry:
seq = read_seqbegin(&bb->lock);
len = 0;
i = 0;
while (len < PAGE_SIZE && i < bb->count) {
sector_t s = BB_OFFSET(p[i]);
unsigned int length = BB_LEN(p[i]);
int ack = BB_ACK(p[i]);
i++;
if (unack && ack)
continue;
len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
(unsigned long long)s << bb->shift,
length << bb->shift);
}
if (read_seqretry(&bb->lock, seq))
goto retry;
return len;
}
#define DO_DEBUG 1
static ssize_t
badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack)
{
unsigned long long sector;
int length;
char newline;
#ifdef DO_DEBUG
/* Allow clearing via sysfs *only* for testing/debugging.
* Normally only a successful write may clear a badblock
*/
int clear = 0;
if (page[0] == '-') {
clear = 1;
page++;
}
#endif /* DO_DEBUG */
switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
case 3:
if (newline != '\n')
return -EINVAL;
case 2:
if (length <= 0)
return -EINVAL;
break;
default:
return -EINVAL;
}
#ifdef DO_DEBUG
if (clear) {
md_clear_badblocks(bb, sector, length);
return len;
}
#endif /* DO_DEBUG */
if (md_set_badblocks(bb, sector, length, !unack))
return len;
else
return -ENOSPC;
}
static int md_notify_reboot(struct notifier_block *this, static int md_notify_reboot(struct notifier_block *this,
unsigned long code, void *x) unsigned long code, void *x)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment