summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/logfs/dev_bdev.c88
-rw-r--r--fs/logfs/dev_mtd.c3
-rw-r--r--fs/logfs/journal.c2
-rw-r--r--fs/logfs/logfs.h6
-rw-r--r--fs/logfs/segment.c6
-rw-r--r--fs/logfs/super.c12
6 files changed, 97 insertions, 20 deletions
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
index 58a057b6e1af..9718c22f186d 100644
--- a/fs/logfs/dev_bdev.c
+++ b/fs/logfs/dev_bdev.c
@@ -167,27 +167,91 @@ static void bdev_writeseg(struct super_block *sb, u64 ofs, size_t len)
generic_unplug_device(bdev_get_queue(logfs_super(sb)->s_bdev));
}
-static int bdev_erase(struct super_block *sb, loff_t to, size_t len)
+
+static void erase_end_io(struct bio *bio, int err)
+{
+ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+ struct super_block *sb = bio->bi_private;
+ struct logfs_super *super = logfs_super(sb);
+
+ BUG_ON(!uptodate); /* FIXME: Retry io or write elsewhere */
+ BUG_ON(err);
+ BUG_ON(bio->bi_vcnt == 0);
+ bio_put(bio);
+ if (atomic_dec_and_test(&super->s_pending_writes))
+ wake_up(&wq);
+}
+
+static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
+ size_t nr_pages)
+{
+ struct logfs_super *super = logfs_super(sb);
+ struct bio *bio;
+ struct request_queue *q = bdev_get_queue(sb->s_bdev);
+ unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+ int i;
+
+ bio = bio_alloc(GFP_NOFS, max_pages);
+ BUG_ON(!bio); /* FIXME: handle this */
+
+ for (i = 0; i < nr_pages; i++) {
+ if (i >= max_pages) {
+ /* Block layer cannot split bios :( */
+ bio->bi_vcnt = i;
+ bio->bi_idx = 0;
+ bio->bi_size = i * PAGE_SIZE;
+ bio->bi_bdev = super->s_bdev;
+ bio->bi_sector = ofs >> 9;
+ bio->bi_private = sb;
+ bio->bi_end_io = erase_end_io;
+ atomic_inc(&super->s_pending_writes);
+ submit_bio(WRITE, bio);
+
+ ofs += i * PAGE_SIZE;
+ index += i;
+ nr_pages -= i;
+ i = 0;
+
+ bio = bio_alloc(GFP_NOFS, max_pages);
+ BUG_ON(!bio);
+ }
+ bio->bi_io_vec[i].bv_page = super->s_erase_page;
+ bio->bi_io_vec[i].bv_len = PAGE_SIZE;
+ bio->bi_io_vec[i].bv_offset = 0;
+ }
+ bio->bi_vcnt = nr_pages;
+ bio->bi_idx = 0;
+ bio->bi_size = nr_pages * PAGE_SIZE;
+ bio->bi_bdev = super->s_bdev;
+ bio->bi_sector = ofs >> 9;
+ bio->bi_private = sb;
+ bio->bi_end_io = erase_end_io;
+ atomic_inc(&super->s_pending_writes);
+ submit_bio(WRITE, bio);
+ return 0;
+}
+
+static int bdev_erase(struct super_block *sb, loff_t to, size_t len,
+ int ensure_write)
{
struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- struct page *page;
- pgoff_t index = to >> PAGE_SHIFT;
- int i, nr_pages = len >> PAGE_SHIFT;
BUG_ON(to & (PAGE_SIZE - 1));
BUG_ON(len & (PAGE_SIZE - 1));
- if (logfs_super(sb)->s_flags & LOGFS_SB_FLAG_RO)
+ if (super->s_flags & LOGFS_SB_FLAG_RO)
return -EROFS;
- for (i = 0; i < nr_pages; i++) {
- page = find_get_page(mapping, index + i);
- if (page) {
- memset(page_address(page), 0xFF, PAGE_SIZE);
- page_cache_release(page);
- }
+ if (ensure_write) {
+ /*
+ * Object store doesn't care whether erases happen or not.
+ * But for the journal they are required. Otherwise a scan
+ * can find an old commit entry and assume it is the current
+ * one, travelling back in time.
+ */
+ do_erase(sb, to, to >> PAGE_SHIFT, len >> PAGE_SHIFT);
}
+
return 0;
}
diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c
index 68e99d046c23..cafb6ef2e05b 100644
--- a/fs/logfs/dev_mtd.c
+++ b/fs/logfs/dev_mtd.c
@@ -83,7 +83,8 @@ static int mtd_erase_mapping(struct super_block *sb, loff_t ofs, size_t len)
return 0;
}
-static int mtd_erase(struct super_block *sb, loff_t ofs, size_t len)
+static int mtd_erase(struct super_block *sb, loff_t ofs, size_t len,
+ int ensure_write)
{
struct mtd_info *mtd = logfs_super(sb)->s_mtd;
struct erase_info ei;
diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c
index 2f2e8e4fd02d..c0e7d63221d4 100644
--- a/fs/logfs/journal.c
+++ b/fs/logfs/journal.c
@@ -392,7 +392,7 @@ static int journal_erase_segment(struct logfs_area *area)
u64 ofs;
int err;
- err = logfs_erase_segment(sb, area->a_segno);
+ err = logfs_erase_segment(sb, area->a_segno, 1);
if (err)
return err;
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h
index e3082abe9e3b..72592114a28f 100644
--- a/fs/logfs/logfs.h
+++ b/fs/logfs/logfs.h
@@ -151,7 +151,8 @@ struct logfs_device_ops {
int (*write_sb)(struct super_block *sb, struct page *page);
int (*readpage)(void *_sb, struct page *page);
void (*writeseg)(struct super_block *sb, u64 ofs, size_t len);
- int (*erase)(struct super_block *sb, loff_t ofs, size_t len);
+ int (*erase)(struct super_block *sb, loff_t ofs, size_t len,
+ int ensure_write);
void (*sync)(struct super_block *sb);
void (*put_device)(struct super_block *sb);
};
@@ -327,6 +328,7 @@ struct logfs_super {
u64 s_feature_compat;
u64 s_feature_flags;
u64 s_sb_ofs[2];
+ struct page *s_erase_page; /* for dev_bdev.c */
/* alias.c fields */
struct btree_head32 s_segment_alias; /* remapped segments */
int s_no_object_aliases;
@@ -572,7 +574,7 @@ int get_page_reserve(struct inode *inode, struct page *page);
extern struct logfs_block_ops indirect_block_ops;
/* segment.c */
-int logfs_erase_segment(struct super_block *sb, u32 ofs);
+int logfs_erase_segment(struct super_block *sb, u32 ofs, int ensure_erase);
int wbuf_read(struct super_block *sb, u64 ofs, size_t len, void *buf);
int logfs_segment_read(struct inode *inode, struct page *page, u64 ofs, u64 bix,
level_t level);
diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c
index 5f58b74516ca..664cd0dd3576 100644
--- a/fs/logfs/segment.c
+++ b/fs/logfs/segment.c
@@ -25,14 +25,14 @@ static int logfs_mark_segment_bad(struct super_block *sb, u32 segno)
return 0;
}
-int logfs_erase_segment(struct super_block *sb, u32 segno)
+int logfs_erase_segment(struct super_block *sb, u32 segno, int ensure_erase)
{
struct logfs_super *super = logfs_super(sb);
super->s_gec++;
return super->s_devops->erase(sb, (u64)segno << super->s_segshift,
- super->s_segsize);
+ super->s_segsize, ensure_erase);
}
static s64 logfs_get_free_bytes(struct logfs_area *area, size_t bytes)
@@ -798,7 +798,7 @@ static int ostore_erase_segment(struct logfs_area *area)
u64 ofs;
int err;
- err = logfs_erase_segment(sb, area->a_segno);
+ err = logfs_erase_segment(sb, area->a_segno, 0);
if (err)
return err;
diff --git a/fs/logfs/super.c b/fs/logfs/super.c
index d128a2c1c8d1..94d80f7ee7c2 100644
--- a/fs/logfs/super.c
+++ b/fs/logfs/super.c
@@ -317,6 +317,7 @@ static int logfs_make_writeable(struct super_block *sb)
static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt)
{
+ struct logfs_super *super = logfs_super(sb);
struct inode *rootdir;
int err;
@@ -329,15 +330,22 @@ static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt)
if (!sb->s_root)
goto fail;
+ super->s_erase_page = alloc_pages(GFP_KERNEL, 0);
+ if (!super->s_erase_page)
+ goto fail2;
+ memset(page_address(super->s_erase_page), 0xFF, PAGE_SIZE);
+
/* FIXME: check for read-only mounts */
err = logfs_make_writeable(sb);
if (err)
- goto fail2;
+ goto fail3;
log_super("LogFS: Finished mounting\n");
simple_set_mnt(mnt, sb);
return 0;
+fail3:
+ __free_page(super->s_erase_page);
fail2:
iput(rootdir);
fail:
@@ -498,6 +506,8 @@ static void logfs_kill_sb(struct super_block *sb)
logfs_cleanup_journal(sb);
logfs_cleanup_areas(sb);
logfs_cleanup_rw(sb);
+ if (super->s_erase_page)
+ __free_page(super->s_erase_page);
super->s_devops->put_device(sb);
mempool_destroy(super->s_btree_pool);
mempool_destroy(super->s_alias_pool);