aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/md.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-07-27 21:31:47 -0400
committerNeilBrown <neilb@suse.de>2011-07-27 21:31:47 -0400
commit16c791a5af3e50d0c11760485fd68e5829f3be4d (patch)
tree24182e37b278a55a9b05b19154ff52597d220714 /drivers/md/md.c
parent2230dfe4ccc3add340dc6d437965b2de1d269fde (diff)
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: NeilBrown <neilb@suse.de> Reviewed-by: Namhyung Kim <namhyung@gmail.com>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r--drivers/md/md.c123
1 files changed, 123 insertions, 0 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 463a392c0705..7f5ffc51c28e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -2711,6 +2711,35 @@ static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t le
2711static struct rdev_sysfs_entry rdev_recovery_start = 2711static struct rdev_sysfs_entry rdev_recovery_start =
2712__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store); 2712__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
2713 2713
2714
2715static ssize_t
2716badblocks_show(struct badblocks *bb, char *page, int unack);
2717static ssize_t
2718badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack);
2719
2720static ssize_t bb_show(mdk_rdev_t *rdev, char *page)
2721{
2722 return badblocks_show(&rdev->badblocks, page, 0);
2723}
2724static ssize_t bb_store(mdk_rdev_t *rdev, const char *page, size_t len)
2725{
2726 return badblocks_store(&rdev->badblocks, page, len, 0);
2727}
2728static struct rdev_sysfs_entry rdev_bad_blocks =
2729__ATTR(bad_blocks, S_IRUGO|S_IWUSR, bb_show, bb_store);
2730
2731
2732static ssize_t ubb_show(mdk_rdev_t *rdev, char *page)
2733{
2734 return badblocks_show(&rdev->badblocks, page, 1);
2735}
2736static ssize_t ubb_store(mdk_rdev_t *rdev, const char *page, size_t len)
2737{
2738 return badblocks_store(&rdev->badblocks, page, len, 1);
2739}
2740static struct rdev_sysfs_entry rdev_unack_bad_blocks =
2741__ATTR(unacknowledged_bad_blocks, S_IRUGO|S_IWUSR, ubb_show, ubb_store);
2742
2714static struct attribute *rdev_default_attrs[] = { 2743static struct attribute *rdev_default_attrs[] = {
2715 &rdev_state.attr, 2744 &rdev_state.attr,
2716 &rdev_errors.attr, 2745 &rdev_errors.attr,
@@ -2718,6 +2747,8 @@ static struct attribute *rdev_default_attrs[] = {
2718 &rdev_offset.attr, 2747 &rdev_offset.attr,
2719 &rdev_size.attr, 2748 &rdev_size.attr,
2720 &rdev_recovery_start.attr, 2749 &rdev_recovery_start.attr,
2750 &rdev_bad_blocks.attr,
2751 &rdev_unack_bad_blocks.attr,
2721 NULL, 2752 NULL,
2722}; 2753};
2723static ssize_t 2754static ssize_t
@@ -7736,6 +7767,98 @@ void md_ack_all_badblocks(struct badblocks *bb)
7736} 7767}
7737EXPORT_SYMBOL_GPL(md_ack_all_badblocks); 7768EXPORT_SYMBOL_GPL(md_ack_all_badblocks);
7738 7769
7770/* sysfs access to bad-blocks list.
7771 * We present two files.
7772 * 'bad-blocks' lists sector numbers and lengths of ranges that
7773 * are recorded as bad. The list is truncated to fit within
7774 * the one-page limit of sysfs.
7775 * Writing "sector length" to this file adds an acknowledged
7776 * bad block list.
7777 * 'unacknowledged-bad-blocks' lists bad blocks that have not yet
7778 * been acknowledged. Writing to this file adds bad blocks
7779 * without acknowledging them. This is largely for testing.
7780 */
7781
7782static ssize_t
7783badblocks_show(struct badblocks *bb, char *page, int unack)
7784{
7785 size_t len;
7786 int i;
7787 u64 *p = bb->page;
7788 unsigned seq;
7789
7790 if (bb->shift < 0)
7791 return 0;
7792
7793retry:
7794 seq = read_seqbegin(&bb->lock);
7795
7796 len = 0;
7797 i = 0;
7798
7799 while (len < PAGE_SIZE && i < bb->count) {
7800 sector_t s = BB_OFFSET(p[i]);
7801 unsigned int length = BB_LEN(p[i]);
7802 int ack = BB_ACK(p[i]);
7803 i++;
7804
7805 if (unack && ack)
7806 continue;
7807
7808 len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
7809 (unsigned long long)s << bb->shift,
7810 length << bb->shift);
7811 }
7812
7813 if (read_seqretry(&bb->lock, seq))
7814 goto retry;
7815
7816 return len;
7817}
7818
7819#define DO_DEBUG 1
7820
7821static ssize_t
7822badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack)
7823{
7824 unsigned long long sector;
7825 int length;
7826 char newline;
7827#ifdef DO_DEBUG
7828 /* Allow clearing via sysfs *only* for testing/debugging.
7829 * Normally only a successful write may clear a badblock
7830 */
7831 int clear = 0;
7832 if (page[0] == '-') {
7833 clear = 1;
7834 page++;
7835 }
7836#endif /* DO_DEBUG */
7837
7838 switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
7839 case 3:
7840 if (newline != '\n')
7841 return -EINVAL;
7842 case 2:
7843 if (length <= 0)
7844 return -EINVAL;
7845 break;
7846 default:
7847 return -EINVAL;
7848 }
7849
7850#ifdef DO_DEBUG
7851 if (clear) {
7852 md_clear_badblocks(bb, sector, length);
7853 return len;
7854 }
7855#endif /* DO_DEBUG */
7856 if (md_set_badblocks(bb, sector, length, !unack))
7857 return len;
7858 else
7859 return -ENOSPC;
7860}
7861
7739static int md_notify_reboot(struct notifier_block *this, 7862static int md_notify_reboot(struct notifier_block *this,
7740 unsigned long code, void *x) 7863 unsigned long code, void *x)
7741{ 7864{