This is v2 of the patch-set. Please, see v1 for the high-level description.
v1->v2:
Address review comments from Jan Kara:
* use BH lock for locking the superblock while calculating its checksum;
* stop setting bm_flags
v1: http://lkml.org/lkml/2012/6/5/162
fs/affs/affs.h | 7 +++++
fs/affs/bitmap.c | 4 +-
fs/affs/super.c | 68 +++++++++++++++++++++++++++++++++++-------------------
3 files changed, 53 insertions(+), 26 deletions(-)
Thanks,
Artem.
From: Artem Bityutskiy <[email protected]>
AFFS stores values '1' and '2' in 'bm_flags', and I fail to see any logic when
it prefers one or another. AFFS writes '1' only from '->put_super()', while
'->sync_fs()' and '->write_super()' store value '2'. So on the first glance,
it looks like we want to have '1' if we unmount. However, this does not really
happen in these cases:
1. superblock is written via 'write_super()' then we unmount;
2. we re-mount R/O, then unmount.
which are quite typical.
I could not find good documentation describing this field, except of one random
piece of documentation in the internet which says that -1 means that the root
block is valid, which is not consistent with what we have in the Linux AFFS
driver.
Jan Kara commented on this: "I have some vague recollection that on Amiga
boolean was usually encoded as: 0 == false, ~0 == -1 == true. But it has been
ages..."
Thus, my conclusion is that value of '1' is as good as value of '2' and we can
just always use '2'. An Jan Kara suggested to go further: "generally bm_flags
handling looks strange. If they are 0, we mount fs read only and thus cannot
change them. If they are != 0, we write 2 there. So IMHO if you just removed
bm_flags setting, nothing will really happen."
So this patch removes the bm_flags setting completely. This makes the "clean"
argument of the 'affs_commit_super()' function unneeded, so it is also removed.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/super.c | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 0782653..1d42e46 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -25,13 +25,12 @@ static int affs_statfs(struct dentry *dentry, struct kstatfs *buf);
static int affs_remount (struct super_block *sb, int *flags, char *data);
static void
-affs_commit_super(struct super_block *sb, int wait, int clean)
+affs_commit_super(struct super_block *sb, int wait)
{
struct affs_sb_info *sbi = AFFS_SB(sb);
struct buffer_head *bh = sbi->s_root_bh;
struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);
- tail->bm_flag = cpu_to_be32(clean);
secs_to_datestamp(get_seconds(), &tail->disk_change);
affs_fix_checksum(sb, bh);
mark_buffer_dirty(bh);
@@ -46,7 +45,7 @@ affs_put_super(struct super_block *sb)
pr_debug("AFFS: put_super()\n");
if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt)
- affs_commit_super(sb, 1, 1);
+ affs_commit_super(sb, 1);
kfree(sbi->s_prefix);
affs_free_bitmap(sb);
@@ -60,7 +59,7 @@ affs_write_super(struct super_block *sb)
{
lock_super(sb);
if (!(sb->s_flags & MS_RDONLY))
- affs_commit_super(sb, 1, 2);
+ affs_commit_super(sb, 1);
sb->s_dirt = 0;
unlock_super(sb);
@@ -71,7 +70,7 @@ static int
affs_sync_fs(struct super_block *sb, int wait)
{
lock_super(sb);
- affs_commit_super(sb, wait, 2);
+ affs_commit_super(sb, wait);
sb->s_dirt = 0;
unlock_super(sb);
return 0;
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
We do not need to write out the superblock from '->put_super()' because VFS has
already called '->sync_fs()' by this time and the superblock has already been
written out. Thus, remove the 'affs_commit_super()' infocation from
'affs_put_super()'.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/super.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 1d42e46..12b4f58 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -44,9 +44,6 @@ affs_put_super(struct super_block *sb)
struct affs_sb_info *sbi = AFFS_SB(sb);
pr_debug("AFFS: put_super()\n");
- if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt)
- affs_commit_super(sb, 1);
-
kfree(sbi->s_prefix);
affs_free_bitmap(sb);
affs_brelse(sbi->s_root_bh);
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
Add an 'sb' VFS superblock back-reference to the 'struct affs_sb_info' data
structure - we will need to find the VFS superblock from a 'struct
affs_sb_info' object in the next patch, so this change is jut a preparation.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/affs.h | 1 +
fs/affs/super.c | 1 +
2 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index 1fceb32..5a726e9 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -100,6 +100,7 @@ struct affs_sb_info {
char *s_prefix; /* Prefix for volumes and assigns. */
char s_volume[32]; /* Volume prefix for absolute symlinks. */
spinlock_t symlink_lock; /* protects the previous two */
+ struct super_block *sb; /* the VFS superblock object */
};
#define SF_INTL 0x0001 /* International filesystem. */
diff --git a/fs/affs/super.c b/fs/affs/super.c
index da7498d..0496cbb 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -299,6 +299,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
return -ENOMEM;
sb->s_fs_info = sbi;
+ sbi->sb = sb;
mutex_init(&sbi->s_bmlock);
spin_lock_init(&sbi->symlink_lock);
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
AFFS wants to serialize the superblock (the root block in AFFS terms) updates
and uses 'lock_super()/unlock_super()' for these purposes. This patch pushes the
locking down to the 'affs_commit_super()' from the callers.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/super.c | 7 ++-----
1 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/fs/affs/super.c b/fs/affs/super.c
index c837e43..4ceec56 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -31,11 +31,13 @@ affs_commit_super(struct super_block *sb, int wait)
struct buffer_head *bh = sbi->s_root_bh;
struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);
+ lock_super(sb);
secs_to_datestamp(get_seconds(), &tail->disk_change);
affs_fix_checksum(sb, bh);
mark_buffer_dirty(bh);
if (wait)
sync_dirty_buffer(bh);
+ unlock_super(sb);
}
static void
@@ -54,22 +56,17 @@ affs_put_super(struct super_block *sb)
static void
affs_write_super(struct super_block *sb)
{
- lock_super(sb);
if (!(sb->s_flags & MS_RDONLY))
affs_commit_super(sb, 1);
sb->s_dirt = 0;
- unlock_super(sb);
-
pr_debug("AFFS: write_super() at %lu, clean=2\n", get_seconds());
}
static int
affs_sync_fs(struct super_block *sb, int wait)
{
- lock_super(sb);
affs_commit_super(sb, wait);
sb->s_dirt = 0;
- unlock_super(sb);
return 0;
}
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
This patch makes affs stop using the VFS '->write_super()' method along with
the 's_dirt' superblock flag, because they are on their way out.
The whole "superblock write-out" VFS infrastructure is served by the
'sync_supers()' kernel thread, which wakes up every 5 (by default) seconds and
writes out all dirty superblocks using the '->write_super()' call-back. But the
problem with this thread is that it wastes power by waking up the system every
5 seconds, even if there are no diry superblocks, or there are no client
file-systems which would need this (e.g., btrfs does not use
'->write_super()'). So we want to kill it completely and thus, we need to make
file-systems to stop using the '->write_super()' VFS service, and then remove
it together with the kernel thread.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/affs.h | 6 ++++++
fs/affs/bitmap.c | 4 ++--
fs/affs/super.c | 48 +++++++++++++++++++++++++++++++++++++-----------
3 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index 5a726e9..3a130e2 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -3,6 +3,7 @@
#include <linux/buffer_head.h>
#include <linux/amigaffs.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
/* AmigaOS allows file names with up to 30 characters length.
* Names longer than that will be silently truncated. If you
@@ -101,6 +102,9 @@ struct affs_sb_info {
char s_volume[32]; /* Volume prefix for absolute symlinks. */
spinlock_t symlink_lock; /* protects the previous two */
struct super_block *sb; /* the VFS superblock object */
+ int work_queued; /* non-zero delayed work is queued */
+ struct delayed_work sb_work; /* superblock flush delayed work */
+ spinlock_t work_lock; /* protects sb_work and work_queued */
};
#define SF_INTL 0x0001 /* International filesystem. */
@@ -121,6 +125,8 @@ static inline struct affs_sb_info *AFFS_SB(struct super_block *sb)
return sb->s_fs_info;
}
+void affs_mark_sb_dirty(struct super_block *sb);
+
/* amigaffs.c */
extern int affs_insert_hash(struct inode *inode, struct buffer_head *bh);
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
index 3e26271..6e0be43 100644
--- a/fs/affs/bitmap.c
+++ b/fs/affs/bitmap.c
@@ -103,7 +103,7 @@ affs_free_block(struct super_block *sb, u32 block)
*(__be32 *)bh->b_data = cpu_to_be32(tmp - mask);
mark_buffer_dirty(bh);
- sb->s_dirt = 1;
+ affs_mark_sb_dirty(sb);
bm->bm_free++;
mutex_unlock(&sbi->s_bmlock);
@@ -248,7 +248,7 @@ find_bit:
*(__be32 *)bh->b_data = cpu_to_be32(tmp + mask);
mark_buffer_dirty(bh);
- sb->s_dirt = 1;
+ affs_mark_sb_dirty(sb);
mutex_unlock(&sbi->s_bmlock);
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 0496cbb..c70f1e5 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -17,6 +17,7 @@
#include <linux/magic.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/writeback.h>
#include "affs.h"
extern struct timezone sys_tz;
@@ -47,6 +48,7 @@ affs_put_super(struct super_block *sb)
struct affs_sb_info *sbi = AFFS_SB(sb);
pr_debug("AFFS: put_super()\n");
+ cancel_delayed_work_sync(&sbi->sb_work);
kfree(sbi->s_prefix);
affs_free_bitmap(sb);
affs_brelse(sbi->s_root_bh);
@@ -54,23 +56,45 @@ affs_put_super(struct super_block *sb)
sb->s_fs_info = NULL;
}
-static void
-affs_write_super(struct super_block *sb)
-{
- if (!(sb->s_flags & MS_RDONLY))
- affs_commit_super(sb, 1);
- sb->s_dirt = 0;
- pr_debug("AFFS: write_super() at %lu, clean=2\n", get_seconds());
-}
-
static int
affs_sync_fs(struct super_block *sb, int wait)
{
affs_commit_super(sb, wait);
- sb->s_dirt = 0;
return 0;
}
+static void flush_superblock(struct work_struct *work)
+{
+ struct affs_sb_info *sbi;
+ struct super_block *sb;
+
+ sbi = container_of(work, struct affs_sb_info, sb_work.work);
+ sb = sbi->sb;
+
+ spin_lock(&sbi->work_lock);
+ sbi->work_queued = 0;
+ spin_unlock(&sbi->work_lock);
+
+ affs_commit_super(sb, 1);
+}
+
+void affs_mark_sb_dirty(struct super_block *sb)
+{
+ struct affs_sb_info *sbi = AFFS_SB(sb);
+ unsigned long delay;
+
+ if (sb->s_flags & MS_RDONLY)
+ return;
+
+ spin_lock(&sbi->work_lock);
+ if (!sbi->work_queued) {
+ delay = msecs_to_jiffies(dirty_writeback_interval * 10);
+ queue_delayed_work(system_long_wq, &sbi->sb_work, delay);
+ sbi->work_queued = 1;
+ }
+ spin_unlock(&sbi->work_lock);
+}
+
static struct kmem_cache * affs_inode_cachep;
static struct inode *affs_alloc_inode(struct super_block *sb)
@@ -132,7 +156,6 @@ static const struct super_operations affs_sops = {
.write_inode = affs_write_inode,
.evict_inode = affs_evict_inode,
.put_super = affs_put_super,
- .write_super = affs_write_super,
.sync_fs = affs_sync_fs,
.statfs = affs_statfs,
.remount_fs = affs_remount,
@@ -302,6 +325,8 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
sbi->sb = sb;
mutex_init(&sbi->s_bmlock);
spin_lock_init(&sbi->symlink_lock);
+ spin_lock_init(&sbi->work_lock);
+ INIT_DELAYED_WORK(&sbi->sb_work, flush_superblock);
if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
&blocksize,&sbi->s_prefix,
@@ -526,6 +551,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
return -EINVAL;
}
+ flush_delayed_work_sync(&sbi->sb_work);
replace_mount_options(sb, new_opts);
sbi->s_flags = mount_flags;
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
The VFS's 'lock_super()' and 'unlock_super()' calls are deprecated and unwanted
and just wait for a brave knight who'd kill them. This patch makes AFFS stop
using them and use the buffer-head's own lock instead.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/super.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 4ceec56..da7498d 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -31,13 +31,14 @@ affs_commit_super(struct super_block *sb, int wait)
struct buffer_head *bh = sbi->s_root_bh;
struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);
- lock_super(sb);
+ lock_buffer(bh);
secs_to_datestamp(get_seconds(), &tail->disk_change);
affs_fix_checksum(sb, bh);
+ unlock_buffer(bh);
+
mark_buffer_dirty(bh);
if (wait)
sync_dirty_buffer(bh);
- unlock_super(sb);
}
static void
--
1.7.7.6
From: Artem Bityutskiy <[email protected]>
We do not need to write out the superblock from '->remount_fs()' because
VFS has already called '->sync_fs()' by this time and the superblock has
already been written out. Thus, remove the 'affs_write_super()'
infocation from 'affs_remount()'.
Signed-off-by: Artem Bityutskiy <[email protected]>
---
fs/affs/super.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 12b4f58..c837e43 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -545,10 +545,9 @@ affs_remount(struct super_block *sb, int *flags, char *data)
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
- if (*flags & MS_RDONLY) {
- affs_write_super(sb);
+ if (*flags & MS_RDONLY)
affs_free_bitmap(sb);
- } else
+ else
res = affs_init_bitmap(sb, flags);
return res;
--
1.7.7.6
On Wed, Jun 6, 2012 at 5:56 PM, Artem Bityutskiy <[email protected]> wrote:
> From: Artem Bityutskiy <[email protected]>
>
> AFFS stores values '1' and '2' in 'bm_flags', and I fail to see any logic when
> it prefers one or another. AFFS writes '1' only from '->put_super()', while
> '->sync_fs()' and '->write_super()' store value '2'. So on the first glance,
> it looks like we want to have '1' if we unmount. However, this does not really
> happen in these cases:
> 1. superblock is written via 'write_super()' then we unmount;
> 2. we re-mount R/O, then unmount.
> which are quite typical.
>
> I could not find good documentation describing this field, except of one random
> piece of documentation in the internet which says that -1 means that the root
> block is valid, which is not consistent with what we have in the Linux AFFS
> driver.
The book "Amiga Intern" (German version on the 'net, which is faster to access
than the Dutch version somewhere in my attic ;-) agrees with this ("Dieses Flag
enthält -1 (TRUE), wenn die Bit-Map der Diskette gültig ist").
I checked some file systems I had lying around, and they all have 0xffffffff in
bm_flag (byte offset 0x138 in the block at the middle of the file system).
At bit to my surprise, as I expected two of them (very old backups of file
systems) to have been written to from Linux at least once, but of course I can
be mistaken, and never have trusted Linux's affs with them ;-).
Mounting (copies of) them on Ubuntu and writing to them changes this flag to 1.
After digging in the affs sources, it seems we had this behavior since at least
2.0.29 (which is 1997 or so), so I think it's safe to assume it's been like that
forever.
> Jan Kara commented on this: "I have some vague recollection that on Amiga
> boolean was usually encoded as: 0 == false, ~0 == -1 == true. But it has been
> ages..."
include/exec/types.h:
#define TRUE 1
#define FALSE 0
However, include/dos/dos.h:
#define DOSTRUE (-1L)
#define DOSFALSE (0L)
So AmigaOS will probably always use 0xffffffff.
> Thus, my conclusion is that value of '1' is as good as value of '2' and we can
> just always use '2'. An Jan Kara suggested to go further: "generally bm_flags
> handling looks strange. If they are 0, we mount fs read only and thus cannot
> change them. If they are != 0, we write 2 there. So IMHO if you just removed
> bm_flags setting, nothing will really happen."
As no one ever complained, 1 or 2 or whatever non-zero value probably doesn't
matter at all to AmigaOS.
> So this patch removes the bm_flags setting completely. This makes the "clean"
> argument of the 'affs_commit_super()' function unneeded, so it is also removed.
>
> Signed-off-by: Artem Bityutskiy <[email protected]>
> ---
> fs/affs/super.c | 9 ++++-----
> 1 files changed, 4 insertions(+), 5 deletions(-)
>
> diff --git a/fs/affs/super.c b/fs/affs/super.c
> index 0782653..1d42e46 100644
> --- a/fs/affs/super.c
> +++ b/fs/affs/super.c
> @@ -25,13 +25,12 @@ static int affs_statfs(struct dentry *dentry, struct kstatfs *buf);
> static int affs_remount (struct super_block *sb, int *flags, char *data);
>
> static void
> -affs_commit_super(struct super_block *sb, int wait, int clean)
> +affs_commit_super(struct super_block *sb, int wait)
> {
> struct affs_sb_info *sbi = AFFS_SB(sb);
> struct buffer_head *bh = sbi->s_root_bh;
> struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);
>
> - tail->bm_flag = cpu_to_be32(clean);
> secs_to_datestamp(get_seconds(), &tail->disk_change);
> affs_fix_checksum(sb, bh);
> mark_buffer_dirty(bh);
> @@ -46,7 +45,7 @@ affs_put_super(struct super_block *sb)
> pr_debug("AFFS: put_super()\n");
>
> if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt)
> - affs_commit_super(sb, 1, 1);
> + affs_commit_super(sb, 1);
>
> kfree(sbi->s_prefix);
> affs_free_bitmap(sb);
> @@ -60,7 +59,7 @@ affs_write_super(struct super_block *sb)
> {
> lock_super(sb);
> if (!(sb->s_flags & MS_RDONLY))
> - affs_commit_super(sb, 1, 2);
> + affs_commit_super(sb, 1);
> sb->s_dirt = 0;
> unlock_super(sb);
>
> @@ -71,7 +70,7 @@ static int
> affs_sync_fs(struct super_block *sb, int wait)
> {
> lock_super(sb);
> - affs_commit_super(sb, wait, 2);
> + affs_commit_super(sb, wait);
> sb->s_dirt = 0;
> unlock_super(sb);
> return 0;
> --
> 1.7.7.6
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Sat, 2012-06-16 at 20:49 +0200, Geert Uytterhoeven wrote:
> > I could not find good documentation describing this field, except of one random
> > piece of documentation in the internet which says that -1 means that the root
> > block is valid, which is not consistent with what we have in the Linux AFFS
> > driver.
>
> The book "Amiga Intern" (German version on the 'net, which is faster to access
> than the Dutch version somewhere in my attic ;-) agrees with this ("Dieses Flag
> enthält -1 (TRUE), wenn die Bit-Map der Diskette gültig ist").
Thangs Geert for confirming.
--
Best Regards,
Artem Bityutskiy