Hello,
here's a set of patches that implement user space notification of file
system run-rim errors through sysfs/uevents. The first approach was
discussed here:
http://marc.info/?t=124404183600003&r=1&w=2
Reasons behind the feature are summarized here:
http://marc.info/?l=linux-kernel&m=124409549418926&w=2
Hand-held device with large (large => lengthy/expensive fsck process)
FAT volumes on MMC that are error prone, especially in the scenario
when these volumes are exported through g_file_storage as USB mass
storage to be used externally; instead of just remounting read-only
on 'run-rime' error, notify user space and expect it to do something
about fixing the FS.
Implementation summary:
- add sysfs support for FAT fs: per-mounted-volume kobject and sysfs
hierarchy under /sys/fs/fat. Same approach as used by ext4 and fuse.
(PATCH 1)
- introduce kobject attribute 'fs_fault' (/sys/fs/fat/<volume>/fs_fault);
the attribute is '0' on (re)mount and set to '1' upon an error. (PATCH 3)
FAT error reporting facilities had to be re-factored (PATCH 2) in
order to simplify sending error notifications. (PATCH 2)
- provide mechanism to optionally notify userspace of FAT fs volume
kobject's attribute changes with uevents. An uevent to be sent is of
tyme KOBJ_CHANGE, with environment variable 'NAME=value', where NAME
is capitalized name of the attribute.
(PATCH 4)
- add mount option 'notify', which will eneble sending uevents on a FAT
kobjects attributes; use it for 'fs_faults' attribute. (PATCH 5)
There were several objections to uevent usage, mostly with argument that
uevents are meant to be used within a device context. One might argue that
uevents are tied to kobjects that are not only used to represent devices,
but also for other things (/sys/modules, /sys/fs, /sys/firmware).
What is wrong with using uevents otherwise? What would be another way to
asyncronousely notify userspace of things happening in kernel, other
than though pseudo filesystem files (procfs, sysfs)?
Denis
Implement FAT fs mount option 'notify'. The effect of this option
is that user space is notified of file system's run-rime errors
with an uevent.
The uevent is associated with fs kobject's attribute value 'fs_fault'
(/sys/fs/fat/<partition>/fs_fault).
Signed-off-by: Denis Karpov <[email protected]>
---
Documentation/filesystems/vfat.txt | 4 ++++
fs/fat/fat.h | 3 ++-
fs/fat/inode.c | 11 +++++++++--
fs/fat/misc.c | 4 ++--
4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt
index 3a5ddc9..16cfcb3 100644
--- a/Documentation/filesystems/vfat.txt
+++ b/Documentation/filesystems/vfat.txt
@@ -132,6 +132,10 @@ rodir -- FAT has the ATTR_RO (read-only) attribute. But on Windows,
If you want to use ATTR_RO as read-only flag even for
the directory, set this option.
+notify -- notify userspace of filesystem faults using sysfs interface
+ /sys/fs/fat/<partition>/fs_fault and uevent
+ (KOBJ_CHANGE, uevent's environment variable FS_FAULT=1).
+
<bool>: 0,1,yes,no,true,false
TODO
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index ebf4c62..84030ac 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -39,7 +39,8 @@ struct fat_mount_options {
nocase:1, /* Does this need case conversion? 0=need case conversion*/
usefree:1, /* Use free_clusters for FAT32 */
tz_utc:1, /* Filesystem timestamps are in UTC */
- rodir:1; /* allow ATTR_RO for directory */
+ rodir:1, /* allow ATTR_RO for directory */
+ err_notify:1; /* Notify userspace on fs errors */
};
#define FAT_HASH_BITS 8
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index b1a0c8d..559ccea 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -844,6 +844,8 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt)
seq_puts(m, ",flush");
if (opts->tz_utc)
seq_puts(m, ",tz=UTC");
+ if (opts->err_notify)
+ seq_puts(m, ",notify");
return 0;
}
@@ -856,7 +858,7 @@ enum {
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
- Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
+ Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_notify, Opt_err,
};
static const match_table_t fat_tokens = {
@@ -892,6 +894,7 @@ static const match_table_t fat_tokens = {
{Opt_obsolate, "posix"},
{Opt_flush, "flush"},
{Opt_tz_utc, "tz=UTC"},
+ {Opt_err_notify, "notify"},
{Opt_err, NULL},
};
static const match_table_t msdos_tokens = {
@@ -961,6 +964,7 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
opts->numtail = 1;
opts->usefree = opts->nocase = 0;
opts->tz_utc = 0;
+ opts->err_notify = 0;
*debug = 0;
if (!options)
@@ -1053,6 +1057,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
case Opt_tz_utc:
opts->tz_utc = 1;
break;
+ case Opt_err_notify:
+ opts->err_notify = 1;
+ break;
/* msdos specific */
case Opt_dots:
@@ -1226,7 +1233,7 @@ static struct fat_attr fat_attr_##_name = { \
#define ATTR_LIST(name) (&fat_attr_ ##name.attr)
-FAT_SBI_RO_ATTR(fs_fault);
+FAT_SBI_RO_ATTR_NOTIFY(fs_fault);
static struct attribute *fat_attrs[] = {
ATTR_LIST(fs_fault),
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 8cdefd8..7a84104 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -35,7 +35,7 @@ void fat_fs_error(struct super_block *s, const char *function,
printk(KERN_ERR " File system has been set read-only\n");
}
- FAT_FS_FAULT_SET(sbi, 1, 0);
+ FAT_FS_FAULT_SET(sbi, 1, sbi->options.err_notify);
}
EXPORT_SYMBOL_GPL(fat_fs_error);
@@ -58,7 +58,7 @@ void fat_fs_warning(struct super_block *s, const char * function,
printk("\n");
va_end(args);
- FAT_FS_FAULT_SET(sbi, 1, 0);
+ FAT_FS_FAULT_SET(sbi, 1, sbi->options.err_notify);
}
EXPORT_SYMBOL_GPL(fat_fs_warning);
--
1.6.3.1
Generalize FAT errors and warnings reporting through
fat_fs_error() and fat_fs_warning().
Signed-off-by: Denis Karpov <[email protected]>
---
fs/fat/cache.c | 14 ++++++------
fs/fat/dir.c | 11 +++++----
fs/fat/fat.h | 8 +++++-
fs/fat/fatent.c | 15 ++++++++-----
fs/fat/file.c | 6 ++--
fs/fat/inode.c | 51 ++++++++++++++++++++++++++-----------------------
fs/fat/misc.c | 42 +++++++++++++++++++++++++++++++---------
fs/fat/namei_msdos.c | 6 ++--
fs/fat/namei_vfat.c | 6 ++--
9 files changed, 96 insertions(+), 63 deletions(-)
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index b426022..b1cf11d 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -241,9 +241,9 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
while (*fclus < cluster) {
/* prevent the infinite loop of cluster chain */
if (*fclus > limit) {
- fat_fs_panic(sb, "%s: detected the cluster chain loop"
- " (i_pos %lld)", __func__,
- MSDOS_I(inode)->i_pos);
+ fat_fs_error(sb, __func__, "detected the cluster "
+ "chain loop (i_pos %lld)",
+ MSDOS_I(inode)->i_pos);
nr = -EIO;
goto out;
}
@@ -252,8 +252,8 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
if (nr < 0)
goto out;
else if (nr == FAT_ENT_FREE) {
- fat_fs_panic(sb, "%s: invalid cluster chain"
- " (i_pos %lld)", __func__,
+ fat_fs_error(sb, __func__, "invalid cluster chain"
+ " (i_pos %lld)",
MSDOS_I(inode)->i_pos);
nr = -EIO;
goto out;
@@ -285,8 +285,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
if (ret < 0)
return ret;
else if (ret == FAT_ENT_EOF) {
- fat_fs_panic(sb, "%s: request beyond EOF (i_pos %lld)",
- __func__, MSDOS_I(inode)->i_pos);
+ fat_fs_error(sb, __func__, "request beyond EOF (i_pos %lld)",
+ MSDOS_I(inode)->i_pos);
return -EIO;
}
return dclus;
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 3a7f603..390a984 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -85,8 +85,9 @@ next:
*bh = sb_bread(sb, phys);
if (*bh == NULL) {
- printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",
- (llu)phys);
+ fat_fs_warning(sb, __func__, "Directory bread(block %llu) "
+ "failed",
+ (llu)phys);
/* skip this block */
*pos = (iblock + 1) << sb->s_blocksize_bits;
goto next;
@@ -1269,8 +1270,8 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
if (sbi->fat_bits != 32)
goto error;
} else if (MSDOS_I(dir)->i_start == 0) {
- printk(KERN_ERR "FAT: Corrupted directory (i_pos %lld)\n",
- MSDOS_I(dir)->i_pos);
+ fat_fs_warning(sb, __func__, "Corrupted directory (i_pos %lld)",
+ MSDOS_I(dir)->i_pos);
err = -EIO;
goto error;
}
@@ -1334,7 +1335,7 @@ found:
goto error_remove;
}
if (dir->i_size & (sbi->cluster_size - 1)) {
- fat_fs_panic(sb, "Odd directory size");
+ fat_fs_error(sb, __func__, "Odd directory size");
dir->i_size = (dir->i_size + sbi->cluster_size - 1)
& ~((loff_t)sbi->cluster_size - 1);
}
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index d1cec33..1b64bae 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -316,8 +316,12 @@ extern int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
unsigned long offset, unsigned long val);
/* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3))) __cold;
+extern void fat_fs_error(struct super_block *s, const char *function,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4))) __cold;
+extern void fat_fs_warning(struct super_block *s, const char *function,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4))) __cold;
extern void fat_clusters_flush(struct super_block *sb);
extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index da6eea4..13f2de9 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -93,7 +93,8 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
err_brelse:
brelse(bhs[0]);
err:
- printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", (llu)blocknr);
+ fat_fs_warning(sb, __func__, "FAT read failed (blocknr %llu)",
+ (llu)blocknr);
return -EIO;
}
@@ -105,8 +106,9 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
fatent->bhs[0] = sb_bread(sb, blocknr);
if (!fatent->bhs[0]) {
- printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
- (llu)blocknr);
+ fat_fs_warning(sb, __func__, "FAT: FAT read failed "
+ "(blocknr %llu)",
+ (llu)blocknr);
return -EIO;
}
fatent->nr_bhs = 1;
@@ -345,7 +347,8 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
fatent_brelse(fatent);
- fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
+ fat_fs_error(sb, __func__, "invalid access to FAT "
+ "(entry 0x%08x)", entry);
return -EIO;
}
@@ -557,8 +560,8 @@ int fat_free_clusters(struct inode *inode, int cluster)
err = cluster;
goto error;
} else if (cluster == FAT_ENT_FREE) {
- fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
- __func__);
+ fat_fs_error(sb, __func__, "deleting FAT entry beyond "
+ "EOF");
err = -EIO;
goto error;
}
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 0a7f4a9..df65446 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -213,9 +213,9 @@ static int fat_free(struct inode *inode, int skip)
fatent_brelse(&fatent);
return 0;
} else if (ret == FAT_ENT_FREE) {
- fat_fs_panic(sb,
- "%s: invalid cluster chain (i_pos %lld)",
- __func__, MSDOS_I(inode)->i_pos);
+ fat_fs_error(sb, __func__,
+ "invalid cluster chain (i_pos %lld)",
+ MSDOS_I(inode)->i_pos);
ret = -EIO;
} else if (ret > 0) {
err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 82708fc..61d52d1 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -79,7 +79,8 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
return 0;
if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
- fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
+ fat_fs_error(sb, __func__, "corrupted file size "
+ "(i_pos %lld, %lld)",
MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
return -EIO;
}
@@ -587,8 +588,8 @@ retry:
bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
if (!bh) {
- printk(KERN_ERR "FAT: unable to read inode block "
- "for updating (i_pos %lld)\n", i_pos);
+ fat_fs_warning(sb, __func__, "unable to read inode block "
+ "for updating (i_pos %lld)", i_pos);
return -EIO;
}
spin_lock(&sbi->inode_hash_lock);
@@ -1115,9 +1116,8 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
/* unknown option */
default:
if (!silent) {
- printk(KERN_ERR
- "FAT: Unrecognized mount option \"%s\" "
- "or missing value\n", p);
+ printk(KERN_ERR "FAT: Unrecognized mount "
+ "option \"%s\" or missing value\n", p);
}
return -EINVAL;
}
@@ -1126,9 +1126,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
out:
/* UTF-8 doesn't provide FAT semantics */
if (!strcmp(opts->iocharset, "utf8")) {
- printk(KERN_ERR "FAT: utf8 is not a recommended IO charset"
- " for FAT filesystems, filesystem will be "
- "case sensitive!\n");
+ printk(KERN_WARNING "FAT: utf8 is not a recommended IO "
+ "charset for FAT filesystems, filesystem will be "
+ "case sensitive!");
}
/* If user doesn't specify allow_utime, it's initialized from dmask. */
@@ -1345,7 +1345,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
b = (struct fat_boot_sector *) bh->b_data;
if (!b->reserved) {
if (!silent)
- printk(KERN_ERR "FAT: bogus number of reserved sectors\n");
+ printk(KERN_ERR "FAT: bogus number of reserved "
+ "sectors\n");
brelse(bh);
goto out_invalid;
}
@@ -1365,7 +1366,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
if (!fat_valid_media(media)) {
if (!silent)
printk(KERN_ERR "FAT: invalid media value (0x%02x)\n",
- media);
+ media);
brelse(bh);
goto out_invalid;
}
@@ -1375,7 +1376,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
|| (logical_sector_size > 4096)) {
if (!silent)
printk(KERN_ERR "FAT: bogus logical sector size %u\n",
- logical_sector_size);
+ logical_sector_size);
brelse(bh);
goto out_invalid;
}
@@ -1383,14 +1384,15 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
if (!is_power_of_2(sbi->sec_per_clus)) {
if (!silent)
printk(KERN_ERR "FAT: bogus sectors per cluster %u\n",
- sbi->sec_per_clus);
+ sbi->sec_per_clus);
brelse(bh);
goto out_invalid;
}
if (logical_sector_size < sb->s_blocksize) {
printk(KERN_ERR "FAT: logical sector size too small for device"
- " (logical sector size = %u)\n", logical_sector_size);
+ " (logical sector size = %u)\n",
+ logical_sector_size);
brelse(bh);
goto out_fail;
}
@@ -1399,14 +1401,14 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
if (!sb_set_blocksize(sb, logical_sector_size)) {
printk(KERN_ERR "FAT: unable to set blocksize %u\n",
- logical_sector_size);
+ logical_sector_size);
goto out_fail;
}
bh = sb_bread(sb, 0);
if (bh == NULL) {
- printk(KERN_ERR "FAT: unable to read boot sector"
- " (logical sector size = %lu)\n",
- sb->s_blocksize);
+ printk(KERN_ERR "FAT: unable to read boot sector "
+ "(logical sector size = %lu)\n",
+ sb->s_blocksize);
goto out_fail;
}
b = (struct fat_boot_sector *) bh->b_data;
@@ -1441,8 +1443,9 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
fsinfo_bh = sb_bread(sb, sbi->fsinfo_sector);
if (fsinfo_bh == NULL) {
- printk(KERN_ERR "FAT: bread failed, FSINFO block"
- " (sector = %lu)\n", sbi->fsinfo_sector);
+ printk(KERN_ERR "FAT: bread failed, FSINFO "
+ "block (sector = %lu)\n",
+ sbi->fsinfo_sector);
brelse(bh);
goto out_fail;
}
@@ -1471,8 +1474,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
sbi->dir_entries = get_unaligned_le16(&b->dir_entries);
if (sbi->dir_entries & (sbi->dir_per_block - 1)) {
if (!silent)
- printk(KERN_ERR "FAT: bogus directroy-entries per block"
- " (%u)\n", sbi->dir_entries);
+ printk(KERN_ERR "FAT: bogus directroy-entries"
+ " per block (%u)\n", sbi->dir_entries);
brelse(bh);
goto out_invalid;
}
@@ -1495,7 +1498,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
if (total_clusters > MAX_FAT(sb)) {
if (!silent)
printk(KERN_ERR "FAT: count of clusters too big (%u)\n",
- total_clusters);
+ total_clusters);
brelse(bh);
goto out_invalid;
}
@@ -1536,7 +1539,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
sbi->nls_io = load_nls(sbi->options.iocharset);
if (!sbi->nls_io) {
printk(KERN_ERR "FAT: IO charset %s not found\n",
- sbi->options.iocharset);
+ sbi->options.iocharset);
goto out_fail;
}
}
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index ac39ebc..be9cb03 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -15,11 +15,13 @@
* fat_fs_panic reports a severe file system problem and sets the file system
* read-only. The file system can be made writable again by remounting it.
*/
-void fat_fs_panic(struct super_block *s, const char *fmt, ...)
+void fat_fs_error(struct super_block *s, const char *function,
+ const char *fmt, ...)
{
va_list args;
- printk(KERN_ERR "FAT: Filesystem panic (dev %s)\n", s->s_id);
+ printk(KERN_ERR "FAT: Filesystem error (dev %s): %s:\n", s->s_id,
+ function);
printk(KERN_ERR " ");
va_start(args, fmt);
@@ -32,8 +34,27 @@ void fat_fs_panic(struct super_block *s, const char *fmt, ...)
printk(KERN_ERR " File system has been set read-only\n");
}
}
+EXPORT_SYMBOL_GPL(fat_fs_error);
-EXPORT_SYMBOL_GPL(fat_fs_panic);
+/*
+ * fat_fs_warning reports a file system non-critical problem that stil
+ * might indicate fs data corruption/inconsistency.
+ */
+void fat_fs_warning(struct super_block *s, const char * function,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ printk(KERN_ERR "FAT: Filesystem warning (dev %s): %s:\n", s->s_id,
+ function);
+
+ printk(KERN_ERR " ");
+ va_start(args, fmt);
+ vprintk(fmt, args);
+ printk("\n");
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(fat_fs_warning);
/* Flushes the number of free clusters on FAT32 */
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
@@ -48,15 +69,15 @@ void fat_clusters_flush(struct super_block *sb)
bh = sb_bread(sb, sbi->fsinfo_sector);
if (bh == NULL) {
- printk(KERN_ERR "FAT: bread failed in fat_clusters_flush\n");
+ fat_fs_warning(sb, __func__, "bread failed");
return;
}
fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
/* Sanity check */
if (!IS_FSINFO(fsinfo)) {
- printk(KERN_ERR "FAT: Invalid FSINFO signature: "
- "0x%08x, 0x%08x (sector = %lu)\n",
+ fat_fs_warning(sb, __func__, "Invalid FSINFO signature: "
+ "0x%08x, 0x%08x (sector = %lu)",
le32_to_cpu(fsinfo->signature1),
le32_to_cpu(fsinfo->signature2),
sbi->fsinfo_sector);
@@ -124,10 +145,11 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
mark_inode_dirty(inode);
}
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
- fat_fs_panic(sb, "clusters badly computed (%d != %llu)",
- new_fclus,
- (llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
- fat_cache_inval_inode(inode);
+ fat_fs_error(sb, __func__, "clusters badly computed "
+ "(%d != %llu)",
+ new_fclus,
+ (llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
+ fat_cache_inval_inode(inode);
}
inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index da3f361..964f378 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -608,9 +608,9 @@ error_inode:
sinfo.bh = NULL;
}
if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
- "%s: Filesystem corrupted (i_pos %lld)",
- __func__, sinfo.i_pos);
+ fat_fs_error(new_dir->i_sb, __func__,
+ "Filesystem corrupted (i_pos %lld)",
+ sinfo.i_pos);
}
goto out;
}
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index a0e00e3..91601d6 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -1030,9 +1030,9 @@ error_inode:
sinfo.bh = NULL;
}
if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
- "%s: Filesystem corrupted (i_pos %lld)",
- __func__, sinfo.i_pos);
+ fat_fs_error(new_dir->i_sb, __func__,
+ "Filesystem corrupted (i_pos %lld)",
+ sinfo.i_pos);
}
goto out;
}
--
1.6.3.1
Report file system' errors through fs kobject's sysfs interface:
/sys/fs/fat/<part_bdev>/fs_fault.
Signed-off-by: Denis Karpov <[email protected]>
---
fs/fat/fat.h | 7 +++++++
fs/fat/inode.c | 9 +++++++++
fs/fat/misc.c | 6 ++++++
3 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 1b64bae..480be11 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -72,6 +72,8 @@ struct msdos_sb_info {
int dir_per_block; /* dir entries per block */
int dir_per_block_bits; /* log2(dir_per_block) */
+ unsigned long fs_fault; /* Mark filesystem's run-time errors */
+
struct kobject s_kobj; /* kobject corresponfing to fs volume */
struct completion s_kobj_unregister;
@@ -84,6 +86,11 @@ struct msdos_sb_info {
#define FAT_CACHE_VALID 0 /* special case for valid cache */
+/* Mark FAT run-time errors */
+#define FAT_FS_FAULT_SET(sbi, val) \
+ fat_sbi_attr_set_notify(sbi, offsetof(struct msdos_sb_info, fs_fault), \
+ val)
+
/*
* MS-DOS file system inode data in memory
*/
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 61d52d1..4613343 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -527,6 +527,7 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
*flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
+ FAT_FS_FAULT_SET(sbi, 0);
return 0;
}
@@ -1219,7 +1220,10 @@ static struct fat_attr fat_attr_##_name = { \
#define ATTR_LIST(name) (&fat_attr_ ##name.attr)
+FAT_SBI_RO_ATTR(fs_fault);
+
static struct attribute *fat_attrs[] = {
+ ATTR_LIST(fs_fault),
NULL
};
@@ -1565,6 +1569,11 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
init_completion(&sbi->s_kobj_unregister);
error = kobject_init_and_add(&sbi->s_kobj, &fat_ktype, NULL,
"%s", sb->s_id);
+ if (error) {
+ printk(KERN_ERR "FAT: create fs kobject failed\n");
+ goto out_fail;
+ }
+ FAT_FS_FAULT_SET(sbi, 0);
return 0;
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index be9cb03..ce478be 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -19,6 +19,7 @@ void fat_fs_error(struct super_block *s, const char *function,
const char *fmt, ...)
{
va_list args;
+ struct msdos_sb_info *sbi = MSDOS_SB(s);
printk(KERN_ERR "FAT: Filesystem error (dev %s): %s:\n", s->s_id,
function);
@@ -33,6 +34,8 @@ void fat_fs_error(struct super_block *s, const char *function,
s->s_flags |= MS_RDONLY;
printk(KERN_ERR " File system has been set read-only\n");
}
+
+ FAT_FS_FAULT_SET(sbi, 1);
}
EXPORT_SYMBOL_GPL(fat_fs_error);
@@ -44,6 +47,7 @@ void fat_fs_warning(struct super_block *s, const char * function,
const char *fmt, ...)
{
va_list args;
+ struct msdos_sb_info *sbi = MSDOS_SB(s);
printk(KERN_ERR "FAT: Filesystem warning (dev %s): %s:\n", s->s_id,
function);
@@ -53,6 +57,8 @@ void fat_fs_warning(struct super_block *s, const char * function,
vprintk(fmt, args);
printk("\n");
va_end(args);
+
+ FAT_FS_FAULT_SET(sbi, 1);
}
EXPORT_SYMBOL_GPL(fat_fs_warning);
--
1.6.3.1
Add mechnism to optionally notify userspace about fs errors with
uevents associated with fs kobject's attribute:
/sys/fs/fat/<part_bdev>/fs_fault
uevent's type is KOBJ_CHANGE, uevent's environment variable FS_FAULT=1.
Signed-off-by: Denis Karpov <[email protected]>
---
fs/fat/fat.h | 6 +++---
fs/fat/inode.c | 37 +++++++++++++++++++++++++++++++------
fs/fat/misc.c | 4 ++--
3 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 480be11..ebf4c62 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -87,9 +87,9 @@ struct msdos_sb_info {
#define FAT_CACHE_VALID 0 /* special case for valid cache */
/* Mark FAT run-time errors */
-#define FAT_FS_FAULT_SET(sbi, val) \
+#define FAT_FS_FAULT_SET(sbi, val, notify) \
fat_sbi_attr_set_notify(sbi, offsetof(struct msdos_sb_info, fs_fault), \
- val)
+ val, notify)
/*
* MS-DOS file system inode data in memory
@@ -320,7 +320,7 @@ extern int fat_fill_super(struct super_block *sb, void *data, int silent,
extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
struct inode *i2);
extern int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
- unsigned long offset, unsigned long val);
+ unsigned long offset, unsigned long val, int notify);
/* fat/misc.c */
extern void fat_fs_error(struct super_block *s, const char *function,
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 4613343..b1a0c8d 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -527,7 +527,7 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
*flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
- FAT_FS_FAULT_SET(sbi, 0);
+ FAT_FS_FAULT_SET(sbi, 0, 0);
return 0;
}
@@ -1185,6 +1185,7 @@ struct fat_attr {
ssize_t (*store)(struct fat_attr *, struct msdos_sb_info *,
const char *, size_t);
unsigned short offset;
+ unsigned short uevent;
};
static ssize_t fat_sbi_attr_show(struct fat_attr *a, struct msdos_sb_info *sbi,
@@ -1206,17 +1207,22 @@ static ssize_t fat_sbi_attr_store(struct fat_attr *a,
return count;
}
-#define FAT_SBI_ATTR(_name, _mode, _show, _store) \
+#define FAT_SBI_ATTR(_name, _mode, _show, _store, _uevent) \
static struct fat_attr fat_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
.offset = offsetof(struct msdos_sb_info, _name), \
+ .uevent = _uevent, \
}
#define FAT_SBI_RO_ATTR(name) FAT_SBI_ATTR(name, 0444, \
- fat_sbi_attr_show, NULL)
+ fat_sbi_attr_show, NULL, 0)
#define FAT_SBI_RW_ATTR(name) FAT_SBI_ATTR(name, 0644 \
- fat_sbi_attr_show, fat_sbi_attr_store)
+ fat_sbi_attr_show, fat_sbi_attr_store, 0)
+#define FAT_SBI_RO_ATTR_NOTIFY(name) FAT_SBI_ATTR(name, 0444, \
+ fat_sbi_attr_show, NULL, 1)
+#define FAT_SBI_RW_ATTR_NOTIFY(name) FAT_SBI_ATTR(name, 0644, \
+ fat_sbi_attr_show, fat_sbi_attr_store, 1)
#define ATTR_LIST(name) (&fat_attr_ ##name.attr)
@@ -1240,11 +1246,15 @@ static struct attribute *find_attr_by_offset(struct kobject *kobj,
int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
unsigned long offset,
- unsigned long val)
+ unsigned long val,
+ int notify)
{
struct fat_attr *a =
(struct fat_attr *) find_attr_by_offset(&sbi->s_kobj, offset);
unsigned long *attr_val;
+ int ev_len;
+ char *t_str;
+ char *envp[] = { NULL, NULL };
if (!a)
return -EINVAL;
@@ -1257,6 +1267,21 @@ int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
sysfs_notify(&sbi->s_kobj, NULL, a->attr.name);
+ if (!notify || !a->uevent)
+ return 0;
+
+ /* dec MAX_UINT == 10 char string */
+ ev_len = strlen(a->attr.name) + 1 + 10 + 1;
+ envp[0] = t_str = kzalloc(ev_len, GFP_KERNEL);
+ if (!t_str)
+ return -ENOMEM;
+
+ snprintf(t_str, ev_len, "%s=%lu", a->attr.name, val);
+ while ((*t_str++ = toupper(*t_str)))
+ ;
+ kobject_uevent_env(&sbi->s_kobj, KOBJ_CHANGE, envp);
+ kfree(envp[0]);
+
return 0;
}
EXPORT_SYMBOL(fat_sbi_attr_set_notify);
@@ -1573,7 +1598,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
printk(KERN_ERR "FAT: create fs kobject failed\n");
goto out_fail;
}
- FAT_FS_FAULT_SET(sbi, 0);
+ FAT_FS_FAULT_SET(sbi, 0, 0);
return 0;
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index ce478be..8cdefd8 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -35,7 +35,7 @@ void fat_fs_error(struct super_block *s, const char *function,
printk(KERN_ERR " File system has been set read-only\n");
}
- FAT_FS_FAULT_SET(sbi, 1);
+ FAT_FS_FAULT_SET(sbi, 1, 0);
}
EXPORT_SYMBOL_GPL(fat_fs_error);
@@ -58,7 +58,7 @@ void fat_fs_warning(struct super_block *s, const char * function,
printk("\n");
va_end(args);
- FAT_FS_FAULT_SET(sbi, 1);
+ FAT_FS_FAULT_SET(sbi, 1, 0);
}
EXPORT_SYMBOL_GPL(fat_fs_warning);
--
1.6.3.1
Add basic sysfs support so that information about the mounted
filesystem can be accessed via /sys/fs/fat/<dev>/*.
Signed-off-by: Denis Karpov <[email protected]>
---
fs/fat/fat.h | 6 +++
fs/fat/inode.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 144 insertions(+), 0 deletions(-)
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index ea440d6..d1cec33 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -72,6 +72,9 @@ struct msdos_sb_info {
int dir_per_block; /* dir entries per block */
int dir_per_block_bits; /* log2(dir_per_block) */
+ struct kobject s_kobj; /* kobject corresponfing to fs volume */
+ struct completion s_kobj_unregister;
+
int fatent_shift;
struct fatent_operations *fatent_ops;
@@ -309,6 +312,9 @@ extern int fat_fill_super(struct super_block *sb, void *data, int silent,
extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
struct inode *i2);
+extern int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
+ unsigned long offset, unsigned long val);
+
/* fat/misc.c */
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3))) __cold;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 296785a..82708fc 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -27,6 +27,7 @@
#include <linux/writeback.h>
#include <linux/log2.h>
#include <linux/hash.h>
+#include <linux/ctype.h>
#include <asm/unaligned.h>
#include "fat.h"
@@ -35,6 +36,8 @@
#define CONFIG_FAT_DEFAULT_IOCHARSET ""
#endif
+static struct kset *fat_kset;
+
static int fat_default_codepage = CONFIG_FAT_DEFAULT_CODEPAGE;
static char fat_default_iocharset[] = CONFIG_FAT_DEFAULT_IOCHARSET;
@@ -451,6 +454,7 @@ static void fat_put_super(struct super_block *sb)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ kobject_del(&sbi->s_kobj);
if (sbi->nls_disk) {
unload_nls(sbi->nls_disk);
sbi->nls_disk = NULL;
@@ -466,6 +470,10 @@ static void fat_put_super(struct super_block *sb)
}
sb->s_fs_info = NULL;
+
+ kobject_put(&sbi->s_kobj);
+ wait_for_completion(&sbi->s_kobj_unregister);
+
kfree(sbi);
}
@@ -1168,6 +1176,126 @@ static int fat_read_root(struct inode *inode)
return 0;
}
+/* sysfs support */
+
+struct fat_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct fat_attr *, struct msdos_sb_info *, char *);
+ ssize_t (*store)(struct fat_attr *, struct msdos_sb_info *,
+ const char *, size_t);
+ unsigned short offset;
+};
+
+static ssize_t fat_sbi_attr_show(struct fat_attr *a, struct msdos_sb_info *sbi,
+ char *buf)
+{
+ unsigned long attr_val = *(unsigned long *)(((char *) sbi) + a->offset);
+ return snprintf(buf, PAGE_SIZE, "%lu\n", attr_val);
+}
+
+static ssize_t fat_sbi_attr_store(struct fat_attr *a,
+ struct msdos_sb_info *sbi,
+ const char *buf, size_t count)
+{
+ unsigned long *attr_val = (unsigned long *)(((char *) sbi) + a->offset);
+
+ if (strict_strtoul(buf, 10, attr_val))
+ return -EINVAL;
+
+ return count;
+}
+
+#define FAT_SBI_ATTR(_name, _mode, _show, _store) \
+static struct fat_attr fat_attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+ .offset = offsetof(struct msdos_sb_info, _name), \
+}
+#define FAT_SBI_RO_ATTR(name) FAT_SBI_ATTR(name, 0444, \
+ fat_sbi_attr_show, NULL)
+#define FAT_SBI_RW_ATTR(name) FAT_SBI_ATTR(name, 0644 \
+ fat_sbi_attr_show, fat_sbi_attr_store)
+
+#define ATTR_LIST(name) (&fat_attr_ ##name.attr)
+
+static struct attribute *fat_attrs[] = {
+ NULL
+};
+
+static struct attribute *find_attr_by_offset(struct kobject *kobj,
+ unsigned long offset)
+{
+ struct fat_attr **a = (struct fat_attr **)kobj->ktype->default_attrs;
+
+ for (; *a && (*a)->offset != offset; a++)
+ ;
+
+ return (struct attribute *)*a;
+}
+
+int fat_sbi_attr_set_notify(struct msdos_sb_info *sbi,
+ unsigned long offset,
+ unsigned long val)
+{
+ struct fat_attr *a =
+ (struct fat_attr *) find_attr_by_offset(&sbi->s_kobj, offset);
+ unsigned long *attr_val;
+
+ if (!a)
+ return -EINVAL;
+
+ attr_val = (unsigned long *) (((char *) sbi) + a->offset);
+ if (*attr_val == val)
+ return 0;
+
+ *attr_val = val;
+
+ sysfs_notify(&sbi->s_kobj, NULL, a->attr.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(fat_sbi_attr_set_notify);
+
+static ssize_t fat_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct msdos_sb_info *sbi = container_of(kobj, struct msdos_sb_info,
+ s_kobj);
+ struct fat_attr *a = container_of(attr, struct fat_attr, attr);
+
+ return a->show ? a->show(a, sbi, buf) : 0;
+}
+
+static ssize_t fat_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t len)
+{
+ struct msdos_sb_info *sbi = container_of(kobj, struct msdos_sb_info,
+ s_kobj);
+ struct fat_attr *a = container_of(attr, struct fat_attr, attr);
+
+ return a->store ? a->store(a, sbi, buf, len) : 0;
+}
+
+static void fat_sb_release(struct kobject *kobj)
+{
+ struct msdos_sb_info *sbi = container_of(kobj, struct msdos_sb_info,
+ s_kobj);
+ complete(&sbi->s_kobj_unregister);
+}
+
+static struct sysfs_ops fat_attr_ops = {
+ .show = fat_attr_show,
+ .store = fat_attr_store,
+};
+
+static struct kobj_type fat_ktype = {
+ .default_attrs = fat_attrs,
+ .sysfs_ops = &fat_attr_ops,
+ .release = fat_sb_release,
+};
+
/*
* Read the super block of an MS-DOS FS.
*/
@@ -1430,6 +1558,11 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
goto out_fail;
}
+ sbi->s_kobj.kset = fat_kset;
+ init_completion(&sbi->s_kobj_unregister);
+ error = kobject_init_and_add(&sbi->s_kobj, &fat_ktype, NULL,
+ "%s", sb->s_id);
+
return 0;
out_invalid:
@@ -1508,6 +1641,10 @@ static int __init init_fat_fs(void)
{
int err;
+ fat_kset = kset_create_and_add("fat", NULL, fs_kobj);
+ if (!fat_kset)
+ return -ENOMEM;
+
err = fat_cache_init();
if (err)
return err;
@@ -1527,6 +1664,7 @@ static void __exit exit_fat_fs(void)
{
fat_cache_destroy();
fat_destroy_inodecache();
+ kset_unregister(fat_kset);
}
module_init(init_fat_fs)
--
1.6.3.1
On Thu, Jun 11, 2009 at 14:24, Denis Karpov<[email protected]> wrote:
> There were several objections to uevent usage, mostly with argument that
> uevents are meant to be used within a device context. One might argue that
> uevents are tied to kobjects that are not only used to represent devices,
> but also for other things (/sys/modules, /sys/fs, /sys/firmware).
No, the objection was to use the underlying *device* as the source to
raise filesystem events. I don't think there are problems in general
with objects in /sys/fs/ sending events.
But, the remaining questions here are:
Do we really want superblocks exported in the global,
non-namespace-aware /sys? It might be wrong to export stuff from other
namespaces for filesytems which are irrelevant, and not even visible.
I'm not sure about this, and it needs careful evaluation. We once had
filesystem mount/umount uevents on block devices, and we needed to
remove them for that namespace reason.
If we go that road, do we want all filesystems export their
superblocks here, like e.g. /sys/fs/super/<maj>:<min>/, or is this
custom fs-specific, like this patch does.
> What is wrong with using uevents otherwise? What would be another way to
> asyncronousely notify userspace of things happening in kernel, other
> than though pseudo filesystem files (procfs, sysfs)?
The question is how to relay error details, and how to transport that
information to userpace. Uevents have no state, and the information is
lost after the event. Uevents can not block, they need to finish in
userspace immediately, you can not queue the up or anything else, it
would block other operations. Uevents can _never_ be used to transport
high frequent event streams. They might render the entire system
unusable, if you have lots of devices and many errors.
They could be used to get attention when a superblock does a one-time
transition from "clean" to "error", everything else would just get us
into serious trouble later.
Thanks,
Kay
Kay Sievers wrote:
> No, the objection was to use the underlying *device* as the source to
> raise filesystem events. I don't think there are problems in general
> with objects in /sys/fs/ sending events.
>
> But, the remaining questions here are:
> Do we really want superblocks exported in the global,
> non-namespace-aware /sys? It might be wrong to export stuff from other
> namespaces for filesytems which are irrelevant, and not even visible.
> I'm not sure about this, and it needs careful evaluation. We once had
> filesystem mount/umount uevents on block devices, and we needed to
> remove them for that namespace reason.
Is there no namespace awareness in /sys?
So how do namespaced guests deal with not having access to /sys, given
modern distros make extensive use of it?
-- Jamie
On Thu, Jun 11, 2009 at 18:41, Jamie Lokier<[email protected]> wrote:
> Kay Sievers wrote:
>> No, the objection was to use the underlying *device* as the source to
>> raise filesystem events. I don't think there are problems in general
>> with objects in /sys/fs/ sending events.
>>
>> But, the remaining questions here are:
>> Do we really want superblocks exported in the global,
>> non-namespace-aware /sys? It might be wrong to export stuff from other
>> namespaces for filesytems which are irrelevant, and not even visible.
>> I'm not sure about this, and it needs careful evaluation. We once had
>> filesystem mount/umount uevents on block devices, and we needed to
>> remove them for that namespace reason.
>
> Is there no namespace awareness in /sys?
No, not at all. It is the straight export of the core kernel objects.
> So how do namespaced guests deal with not having access to /sys, given
> modern distros make extensive use of it?
They ignore it and don't use it, or get confused, I guess. :)
Thanks,
Kay
On Thu, Jun 11, 2009 at 02:24:48PM +0200, Karpov Denis.2 (EXT-Teleca/Helsinki) wrote:
> Add mechnism to optionally notify userspace about fs errors with
> uevents associated with fs kobject's attribute:
> /sys/fs/fat/<part_bdev>/fs_fault
>
> uevent's type is KOBJ_CHANGE, uevent's environment variable FS_FAULT=1.
As Jan and Artem pointed out in the earlier thread, 'notify' mount option
that would make fs use this mechanism doesn't make much sense.
So, send notification uevents unconditionally, fixed patch is attached
(nevermind patch5/5).
Denis