- info and mount options for the JFS filesystem.
locks.txt
- info on file locking implementations, flock() vs. fcntl(), etc.
-logfs.txt
- - info on the LogFS flash filesystem.
mandatory-locking.txt
- info on the Linux implementation of Sys V mandatory file locking.
ncpfs.txt
+++ /dev/null
-
-The LogFS Flash Filesystem
-==========================
-
-Specification
-=============
-
-Superblocks
------------
-
-Two superblocks exist at the beginning and end of the filesystem.
-Each superblock is 256 Bytes large, with another 3840 Bytes reserved
-for future purposes, making a total of 4096 Bytes.
-
-Superblock locations may differ for MTD and block devices. On MTD the
-first non-bad block contains a superblock in the first 4096 Bytes and
-the last non-bad block contains a superblock in the last 4096 Bytes.
-On block devices, the first 4096 Bytes of the device contain the first
-superblock and the last aligned 4096 Byte-block contains the second
-superblock.
-
-For the most part, the superblocks can be considered read-only. They
-are written only to correct errors detected within the superblocks,
-move the journal and change the filesystem parameters through tunefs.
-As a result, the superblock does not contain any fields that require
-constant updates, like the amount of free space, etc.
-
-Segments
---------
-
-The space in the device is split up into equal-sized segments.
-Segments are the primary write unit of LogFS. Within each segments,
-writes happen from front (low addresses) to back (high addresses. If
-only a partial segment has been written, the segment number, the
-current position within and optionally a write buffer are stored in
-the journal.
-
-Segments are erased as a whole. Therefore Garbage Collection may be
-required to completely free a segment before doing so.
-
-Journal
---------
-
-The journal contains all global information about the filesystem that
-is subject to frequent change. At mount time, it has to be scanned
-for the most recent commit entry, which contains a list of pointers to
-all currently valid entries.
-
-Object Store
-------------
-
-All space except for the superblocks and journal is part of the object
-store. Each segment contains a segment header and a number of
-objects, each consisting of the object header and the payload.
-Objects are either inodes, directory entries (dentries), file data
-blocks or indirect blocks.
-
-Levels
-------
-
-Garbage collection (GC) may fail if all data is written
-indiscriminately. One requirement of GC is that data is separated
-roughly according to the distance between the tree root and the data.
-Effectively that means all file data is on level 0, indirect blocks
-are on levels 1, 2, 3 4 or 5 for 1x, 2x, 3x, 4x or 5x indirect blocks,
-respectively. Inode file data is on level 6 for the inodes and 7-11
-for indirect blocks.
-
-Each segment contains objects of a single level only. As a result,
-each level requires its own separate segment to be open for writing.
-
-Inode File
-----------
-
-All inodes are stored in a special file, the inode file. Single
-exception is the inode file's inode (master inode) which for obvious
-reasons is stored in the journal instead. Instead of data blocks, the
-leaf nodes of the inode files are inodes.
-
-Aliases
--------
-
-Writes in LogFS are done by means of a wandering tree. A naïve
-implementation would require that for each write or a block, all
-parent blocks are written as well, since the block pointers have
-changed. Such an implementation would not be very efficient.
-
-In LogFS, the block pointer changes are cached in the journal by means
-of alias entries. Each alias consists of its logical address - inode
-number, block index, level and child number (index into block) - and
-the changed data. Any 8-byte word can be changes in this manner.
-
-Currently aliases are used for block pointers, file size, file used
-bytes and the height of an inodes indirect tree.
-
-Segment Aliases
----------------
-
-Related to regular aliases, these are used to handle bad blocks.
-Initially, bad blocks are handled by moving the affected segment
-content to a spare segment and noting this move in the journal with a
-segment alias, a simple (to, from) tupel. GC will later empty this
-segment and the alias can be removed again. This is used on MTD only.
-
-Vim
----
-
-By cleverly predicting the life time of data, it is possible to
-separate long-living data from short-living data and thereby reduce
-the GC overhead later. Each type of distinc life expectency (vim) can
-have a separate segment open for writing. Each (level, vim) tupel can
-be open just once. If an open segment with unknown vim is encountered
-at mount time, it is closed and ignored henceforth.
-
-Indirect Tree
--------------
-
-Inodes in LogFS are similar to FFS-style filesystems with direct and
-indirect block pointers. One difference is that LogFS uses a single
-indirect pointer that can be either a 1x, 2x, etc. indirect pointer.
-A height field in the inode defines the height of the indirect tree
-and thereby the indirection of the pointer.
-
-Another difference is the addressing of indirect blocks. In LogFS,
-the first 16 pointers in the first indirect block are left empty,
-corresponding to the 16 direct pointers in the inode. In ext2 (maybe
-others as well) the first pointer in the first indirect block
-corresponds to logical block 12, skipping the 12 direct pointers.
-So where ext2 is using arithmetic to better utilize space, LogFS keeps
-arithmetic simple and uses compression to save space.
-
-Compression
------------
-
-Both file data and metadata can be compressed. Compression for file
-data can be enabled with chattr +c and disabled with chattr -c. Doing
-so has no effect on existing data, but new data will be stored
-accordingly. New inodes will inherit the compression flag of the
-parent directory.
-
-Metadata is always compressed. However, the space accounting ignores
-this and charges for the uncompressed size. Failing to do so could
-result in GC failures when, after moving some data, indirect blocks
-compress worse than previously. Even on a 100% full medium, GC may
-not consume any extra space, so the compression gains are lost space
-to the user.
-
-However, they are not lost space to the filesystem internals. By
-cheating the user for those bytes, the filesystem gained some slack
-space and GC will run less often and faster.
-
-Garbage Collection and Wear Leveling
-------------------------------------
-
-Garbage collection is invoked whenever the number of free segments
-falls below a threshold. The best (known) candidate is picked based
-on the least amount of valid data contained in the segment. All
-remaining valid data is copied elsewhere, thereby invalidating it.
-
-The GC code also checks for aliases and writes then back if their
-number gets too large.
-
-Wear leveling is done by occasionally picking a suboptimal segment for
-garbage collection. If a stale segments erase count is significantly
-lower than the active segments' erase counts, it will be picked. Wear
-leveling is rate limited, so it will never monopolize the device for
-more than one segment worth at a time.
-
-Values for "occasionally", "significantly lower" are compile time
-constants.
-
-Hashed directories
-------------------
-
-To satisfy efficient lookup(), directory entries are hashed and
-located based on the hash. In order to both support large directories
-and not be overly inefficient for small directories, several hash
-tables of increasing size are used. For each table, the hash value
-modulo the table size gives the table index.
-
-Tables sizes are chosen to limit the number of indirect blocks with a
-fully populated table to 0, 1, 2 or 3 respectively. So the first
-table contains 16 entries, the second 512-16, etc.
-
-The last table is special in several ways. First its size depends on
-the effective 32bit limit on telldir/seekdir cookies. Since logfs
-uses the upper half of the address space for indirect blocks, the size
-is limited to 2^31. Secondly the table contains hash buckets with 16
-entries each.
-
-Using single-entry buckets would result in birthday "attacks". At
-just 2^16 used entries, hash collisions would be likely (P >= 0.5).
-My math skills are insufficient to do the combinatorics for the 17x
-collisions necessary to overflow a bucket, but testing showed that in
-10,000 runs the lowest directory fill before a bucket overflow was
-188,057,130 entries with an average of 315,149,915 entries. So for
-directory sizes of up to a million, bucket overflows should be
-virtually impossible under normal circumstances.
-
-With carefully chosen filenames, it is obviously possible to cause an
-overflow with just 21 entries (4 higher tables + 16 entries + 1). So
-there may be a security concern if a malicious user has write access
-to a directory.
-
-Open For Discussion
-===================
-
-Device Address Space
---------------------
-
-A device address space is used for caching. Both block devices and
-MTD provide functions to either read a single page or write a segment.
-Partial segments may be written for data integrity, but where possible
-complete segments are written for performance on simple block device
-flash media.
-
-Meta Inodes
------------
-
-Inodes are stored in the inode file, which is just a regular file for
-most purposes. At umount time, however, the inode file needs to
-remain open until all dirty inodes are written. So
-generic_shutdown_super() may not close this inode, but shouldn't
-complain about remaining inodes due to the inode file either. Same
-goes for mapping inode of the device address space.
-
-Currently logfs uses a hack that essentially copies part of fs/inode.c
-code over. A general solution would be preferred.
-
-Indirect block mapping
-----------------------
-
-With compression, the block device (or mapping inode) cannot be used
-to cache indirect blocks. Some other place is required. Currently
-logfs uses the top half of each inode's address space. The low 8TB
-(on 32bit) are filled with file data, the high 8TB are used for
-indirect blocks.
-
-One problem is that 16TB files created on 64bit systems actually have
-data in the top 8TB. But files >16TB would cause problems anyway, so
-only the limit has changed.
F: Documentation/ldm.txt
F: block/partitions/ldm.*
-LogFS
-M: Joern Engel <joern@logfs.org>
-M: Prasad Joshi <prasadjoshi.linux@gmail.com>
-L: logfs@logfs.org
-W: logfs.org
-S: Maintained
-F: fs/logfs/
-
LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
M: Sathya Prakash <sathya.prakash@broadcom.com>
M: Chaitra P B <chaitra.basappa@broadcom.com>
source "fs/jffs2/Kconfig"
# UBIFS File system configuration
source "fs/ubifs/Kconfig"
-source "fs/logfs/Kconfig"
source "fs/cramfs/Kconfig"
source "fs/squashfs/Kconfig"
source "fs/freevxfs/Kconfig"
obj-$(CONFIG_UFS_FS) += ufs/
obj-$(CONFIG_EFS_FS) += efs/
obj-$(CONFIG_JFFS2_FS) += jffs2/
-obj-$(CONFIG_LOGFS) += logfs/
obj-$(CONFIG_UBIFS_FS) += ubifs/
obj-$(CONFIG_AFFS_FS) += affs/
obj-$(CONFIG_ROMFS_FS) += romfs/
+++ /dev/null
-config LOGFS
- tristate "LogFS file system"
- depends on MTD || (!MTD && BLOCK)
- select ZLIB_INFLATE
- select ZLIB_DEFLATE
- select CRC32
- select BTREE
- help
- Flash filesystem aimed to scale efficiently to large devices.
- In comparison to JFFS2 it offers significantly faster mount
- times and potentially less RAM usage, although the latter has
- not been measured yet.
-
- In its current state it is still very experimental and should
- not be used for other than testing purposes.
-
- If unsure, say N.
+++ /dev/null
-obj-$(CONFIG_LOGFS) += logfs.o
-
-logfs-y += compr.o
-logfs-y += dir.o
-logfs-y += file.o
-logfs-y += gc.o
-logfs-y += inode.o
-logfs-y += journal.o
-logfs-y += readwrite.o
-logfs-y += segment.o
-logfs-y += super.o
-logfs-$(CONFIG_BLOCK) += dev_bdev.o
-logfs-$(CONFIG_MTD) += dev_mtd.o
+++ /dev/null
-/*
- * fs/logfs/compr.c - compression routines
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/vmalloc.h>
-#include <linux/zlib.h>
-
-#define COMPR_LEVEL 3
-
-static DEFINE_MUTEX(compr_mutex);
-static struct z_stream_s stream;
-
-int logfs_compress(void *in, void *out, size_t inlen, size_t outlen)
-{
- int err, ret;
-
- ret = -EIO;
- mutex_lock(&compr_mutex);
- err = zlib_deflateInit(&stream, COMPR_LEVEL);
- if (err != Z_OK)
- goto error;
-
- stream.next_in = in;
- stream.avail_in = inlen;
- stream.total_in = 0;
- stream.next_out = out;
- stream.avail_out = outlen;
- stream.total_out = 0;
-
- err = zlib_deflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END)
- goto error;
-
- err = zlib_deflateEnd(&stream);
- if (err != Z_OK)
- goto error;
-
- if (stream.total_out >= stream.total_in)
- goto error;
-
- ret = stream.total_out;
-error:
- mutex_unlock(&compr_mutex);
- return ret;
-}
-
-int logfs_uncompress(void *in, void *out, size_t inlen, size_t outlen)
-{
- int err, ret;
-
- ret = -EIO;
- mutex_lock(&compr_mutex);
- err = zlib_inflateInit(&stream);
- if (err != Z_OK)
- goto error;
-
- stream.next_in = in;
- stream.avail_in = inlen;
- stream.total_in = 0;
- stream.next_out = out;
- stream.avail_out = outlen;
- stream.total_out = 0;
-
- err = zlib_inflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END)
- goto error;
-
- err = zlib_inflateEnd(&stream);
- if (err != Z_OK)
- goto error;
-
- ret = 0;
-error:
- mutex_unlock(&compr_mutex);
- return ret;
-}
-
-int __init logfs_compr_init(void)
-{
- size_t size = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
- zlib_inflate_workspacesize());
- stream.workspace = vmalloc(size);
- if (!stream.workspace)
- return -ENOMEM;
- return 0;
-}
-
-void logfs_compr_exit(void)
-{
- vfree(stream.workspace);
-}
+++ /dev/null
-/*
- * fs/logfs/dev_bdev.c - Device access methods for block devices
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/bio.h>
-#include <linux/blkdev.h>
-#include <linux/buffer_head.h>
-#include <linux/gfp.h>
-#include <linux/prefetch.h>
-
-#define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1))
-
-static int sync_request(struct page *page, struct block_device *bdev, int op)
-{
- struct bio bio;
- struct bio_vec bio_vec;
-
- bio_init(&bio);
- bio.bi_max_vecs = 1;
- bio.bi_io_vec = &bio_vec;
- bio_vec.bv_page = page;
- bio_vec.bv_len = PAGE_SIZE;
- bio_vec.bv_offset = 0;
- bio.bi_vcnt = 1;
- bio.bi_bdev = bdev;
- bio.bi_iter.bi_sector = page->index * (PAGE_SIZE >> 9);
- bio.bi_iter.bi_size = PAGE_SIZE;
- bio_set_op_attrs(&bio, op, 0);
-
- return submit_bio_wait(&bio);
-}
-
-static int bdev_readpage(void *_sb, struct page *page)
-{
- struct super_block *sb = _sb;
- struct block_device *bdev = logfs_super(sb)->s_bdev;
- int err;
-
- err = sync_request(page, bdev, READ);
- if (err) {
- ClearPageUptodate(page);
- SetPageError(page);
- } else {
- SetPageUptodate(page);
- ClearPageError(page);
- }
- unlock_page(page);
- return err;
-}
-
-static DECLARE_WAIT_QUEUE_HEAD(wq);
-
-static void writeseg_end_io(struct bio *bio)
-{
- struct bio_vec *bvec;
- int i;
- struct super_block *sb = bio->bi_private;
- struct logfs_super *super = logfs_super(sb);
-
- BUG_ON(bio->bi_error); /* FIXME: Retry io or write elsewhere */
-
- bio_for_each_segment_all(bvec, bio, i) {
- end_page_writeback(bvec->bv_page);
- put_page(bvec->bv_page);
- }
- bio_put(bio);
- if (atomic_dec_and_test(&super->s_pending_writes))
- wake_up(&wq);
-}
-
-static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
- size_t nr_pages)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- struct bio *bio;
- struct page *page;
- unsigned int max_pages;
- int i;
-
- max_pages = min_t(size_t, nr_pages, BIO_MAX_PAGES);
-
- bio = bio_alloc(GFP_NOFS, max_pages);
- BUG_ON(!bio);
-
- for (i = 0; i < nr_pages; i++) {
- if (i >= max_pages) {
- /* Block layer cannot split bios :( */
- bio->bi_vcnt = i;
- bio->bi_iter.bi_size = i * PAGE_SIZE;
- bio->bi_bdev = super->s_bdev;
- bio->bi_iter.bi_sector = ofs >> 9;
- bio->bi_private = sb;
- bio->bi_end_io = writeseg_end_io;
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- atomic_inc(&super->s_pending_writes);
- submit_bio(bio);
-
- ofs += i * PAGE_SIZE;
- index += i;
- nr_pages -= i;
- i = 0;
-
- bio = bio_alloc(GFP_NOFS, max_pages);
- BUG_ON(!bio);
- }
- page = find_lock_page(mapping, index + i);
- BUG_ON(!page);
- bio->bi_io_vec[i].bv_page = page;
- bio->bi_io_vec[i].bv_len = PAGE_SIZE;
- bio->bi_io_vec[i].bv_offset = 0;
-
- BUG_ON(PageWriteback(page));
- set_page_writeback(page);
- unlock_page(page);
- }
- bio->bi_vcnt = nr_pages;
- bio->bi_iter.bi_size = nr_pages * PAGE_SIZE;
- bio->bi_bdev = super->s_bdev;
- bio->bi_iter.bi_sector = ofs >> 9;
- bio->bi_private = sb;
- bio->bi_end_io = writeseg_end_io;
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- atomic_inc(&super->s_pending_writes);
- submit_bio(bio);
- return 0;
-}
-
-static void bdev_writeseg(struct super_block *sb, u64 ofs, size_t len)
-{
- struct logfs_super *super = logfs_super(sb);
- int head;
-
- BUG_ON(super->s_flags & LOGFS_SB_FLAG_RO);
-
- if (len == 0) {
- /* This can happen when the object fit perfectly into a
- * segment, the segment gets written per sync and subsequently
- * closed.
- */
- return;
- }
- head = ofs & (PAGE_SIZE - 1);
- if (head) {
- ofs -= head;
- len += head;
- }
- len = PAGE_ALIGN(len);
- __bdev_writeseg(sb, ofs, ofs >> PAGE_SHIFT, len >> PAGE_SHIFT);
-}
-
-
-static void erase_end_io(struct bio *bio)
-{
- struct super_block *sb = bio->bi_private;
- struct logfs_super *super = logfs_super(sb);
-
- BUG_ON(bio->bi_error); /* FIXME: Retry io or write elsewhere */
- 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;
- unsigned int max_pages;
- int i;
-
- max_pages = min_t(size_t, nr_pages, BIO_MAX_PAGES);
-
- bio = bio_alloc(GFP_NOFS, max_pages);
- BUG_ON(!bio);
-
- for (i = 0; i < nr_pages; i++) {
- if (i >= max_pages) {
- /* Block layer cannot split bios :( */
- bio->bi_vcnt = i;
- bio->bi_iter.bi_size = i * PAGE_SIZE;
- bio->bi_bdev = super->s_bdev;
- bio->bi_iter.bi_sector = ofs >> 9;
- bio->bi_private = sb;
- bio->bi_end_io = erase_end_io;
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- atomic_inc(&super->s_pending_writes);
- submit_bio(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_iter.bi_size = nr_pages * PAGE_SIZE;
- bio->bi_bdev = super->s_bdev;
- bio->bi_iter.bi_sector = ofs >> 9;
- bio->bi_private = sb;
- bio->bi_end_io = erase_end_io;
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- atomic_inc(&super->s_pending_writes);
- submit_bio(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);
-
- BUG_ON(to & (PAGE_SIZE - 1));
- BUG_ON(len & (PAGE_SIZE - 1));
-
- if (super->s_flags & LOGFS_SB_FLAG_RO)
- return -EROFS;
-
- 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;
-}
-
-static void bdev_sync(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- wait_event(wq, atomic_read(&super->s_pending_writes) == 0);
-}
-
-static struct page *bdev_find_first_sb(struct super_block *sb, u64 *ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- filler_t *filler = bdev_readpage;
-
- *ofs = 0;
- return read_cache_page(mapping, 0, filler, sb);
-}
-
-static struct page *bdev_find_last_sb(struct super_block *sb, u64 *ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- filler_t *filler = bdev_readpage;
- u64 pos = (super->s_bdev->bd_inode->i_size & ~0xfffULL) - 0x1000;
- pgoff_t index = pos >> PAGE_SHIFT;
-
- *ofs = pos;
- return read_cache_page(mapping, index, filler, sb);
-}
-
-static int bdev_write_sb(struct super_block *sb, struct page *page)
-{
- struct block_device *bdev = logfs_super(sb)->s_bdev;
-
- /* Nothing special to do for block devices. */
- return sync_request(page, bdev, WRITE);
-}
-
-static void bdev_put_device(struct logfs_super *s)
-{
- blkdev_put(s->s_bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
-}
-
-static int bdev_can_write_buf(struct super_block *sb, u64 ofs)
-{
- return 0;
-}
-
-static const struct logfs_device_ops bd_devops = {
- .find_first_sb = bdev_find_first_sb,
- .find_last_sb = bdev_find_last_sb,
- .write_sb = bdev_write_sb,
- .readpage = bdev_readpage,
- .writeseg = bdev_writeseg,
- .erase = bdev_erase,
- .can_write_buf = bdev_can_write_buf,
- .sync = bdev_sync,
- .put_device = bdev_put_device,
-};
-
-int logfs_get_sb_bdev(struct logfs_super *p, struct file_system_type *type,
- const char *devname)
-{
- struct block_device *bdev;
-
- bdev = blkdev_get_by_path(devname, FMODE_READ|FMODE_WRITE|FMODE_EXCL,
- type);
- if (IS_ERR(bdev))
- return PTR_ERR(bdev);
-
- if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
- int mtdnr = MINOR(bdev->bd_dev);
- blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
- return logfs_get_sb_mtd(p, mtdnr);
- }
-
- p->s_bdev = bdev;
- p->s_mtd = NULL;
- p->s_devops = &bd_devops;
- return 0;
-}
+++ /dev/null
-/*
- * fs/logfs/dev_mtd.c - Device access methods for MTD
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/completion.h>
-#include <linux/mount.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1))
-
-static int logfs_mtd_read(struct super_block *sb, loff_t ofs, size_t len,
- void *buf)
-{
- struct mtd_info *mtd = logfs_super(sb)->s_mtd;
- size_t retlen;
- int ret;
-
- ret = mtd_read(mtd, ofs, len, &retlen, buf);
- BUG_ON(ret == -EINVAL);
- if (ret)
- return ret;
-
- /* Not sure if we should loop instead. */
- if (retlen != len)
- return -EIO;
-
- return 0;
-}
-
-static int loffs_mtd_write(struct super_block *sb, loff_t ofs, size_t len,
- void *buf)
-{
- struct logfs_super *super = logfs_super(sb);
- struct mtd_info *mtd = super->s_mtd;
- size_t retlen;
- loff_t page_start, page_end;
- int ret;
-
- if (super->s_flags & LOGFS_SB_FLAG_RO)
- return -EROFS;
-
- BUG_ON((ofs >= mtd->size) || (len > mtd->size - ofs));
- BUG_ON(ofs != (ofs >> super->s_writeshift) << super->s_writeshift);
- BUG_ON(len > PAGE_SIZE);
- page_start = ofs & PAGE_MASK;
- page_end = PAGE_ALIGN(ofs + len) - 1;
- ret = mtd_write(mtd, ofs, len, &retlen, buf);
- if (ret || (retlen != len))
- return -EIO;
-
- return 0;
-}
-
-/*
- * For as long as I can remember (since about 2001) mtd->erase has been an
- * asynchronous interface lacking the first driver to actually use the
- * asynchronous properties. So just to prevent the first implementor of such
- * a thing from breaking logfs in 2350, we do the usual pointless dance to
- * declare a completion variable and wait for completion before returning
- * from logfs_mtd_erase(). What an exercise in futility!
- */
-static void logfs_erase_callback(struct erase_info *ei)
-{
- complete((struct completion *)ei->priv);
-}
-
-static int logfs_mtd_erase_mapping(struct super_block *sb, loff_t ofs,
- size_t len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- struct page *page;
- pgoff_t index = ofs >> PAGE_SHIFT;
-
- for (index = ofs >> PAGE_SHIFT; index < (ofs + len) >> PAGE_SHIFT; index++) {
- page = find_get_page(mapping, index);
- if (!page)
- continue;
- memset(page_address(page), 0xFF, PAGE_SIZE);
- put_page(page);
- }
- return 0;
-}
-
-static int logfs_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;
- DECLARE_COMPLETION_ONSTACK(complete);
- int ret;
-
- BUG_ON(len % mtd->erasesize);
- if (logfs_super(sb)->s_flags & LOGFS_SB_FLAG_RO)
- return -EROFS;
-
- memset(&ei, 0, sizeof(ei));
- ei.mtd = mtd;
- ei.addr = ofs;
- ei.len = len;
- ei.callback = logfs_erase_callback;
- ei.priv = (long)&complete;
- ret = mtd_erase(mtd, &ei);
- if (ret)
- return -EIO;
-
- wait_for_completion(&complete);
- if (ei.state != MTD_ERASE_DONE)
- return -EIO;
- return logfs_mtd_erase_mapping(sb, ofs, len);
-}
-
-static void logfs_mtd_sync(struct super_block *sb)
-{
- struct mtd_info *mtd = logfs_super(sb)->s_mtd;
-
- mtd_sync(mtd);
-}
-
-static int logfs_mtd_readpage(void *_sb, struct page *page)
-{
- struct super_block *sb = _sb;
- int err;
-
- err = logfs_mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
- page_address(page));
- if (err == -EUCLEAN || err == -EBADMSG) {
- /* -EBADMSG happens regularly on power failures */
- err = 0;
- /* FIXME: force GC this segment */
- }
- if (err) {
- ClearPageUptodate(page);
- SetPageError(page);
- } else {
- SetPageUptodate(page);
- ClearPageError(page);
- }
- unlock_page(page);
- return err;
-}
-
-static struct page *logfs_mtd_find_first_sb(struct super_block *sb, u64 *ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- filler_t *filler = logfs_mtd_readpage;
- struct mtd_info *mtd = super->s_mtd;
-
- *ofs = 0;
- while (mtd_block_isbad(mtd, *ofs)) {
- *ofs += mtd->erasesize;
- if (*ofs >= mtd->size)
- return NULL;
- }
- BUG_ON(*ofs & ~PAGE_MASK);
- return read_cache_page(mapping, *ofs >> PAGE_SHIFT, filler, sb);
-}
-
-static struct page *logfs_mtd_find_last_sb(struct super_block *sb, u64 *ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- filler_t *filler = logfs_mtd_readpage;
- struct mtd_info *mtd = super->s_mtd;
-
- *ofs = mtd->size - mtd->erasesize;
- while (mtd_block_isbad(mtd, *ofs)) {
- *ofs -= mtd->erasesize;
- if (*ofs <= 0)
- return NULL;
- }
- *ofs = *ofs + mtd->erasesize - 0x1000;
- BUG_ON(*ofs & ~PAGE_MASK);
- return read_cache_page(mapping, *ofs >> PAGE_SHIFT, filler, sb);
-}
-
-static int __logfs_mtd_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
- size_t nr_pages)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- struct page *page;
- int i, err;
-
- for (i = 0; i < nr_pages; i++) {
- page = find_lock_page(mapping, index + i);
- BUG_ON(!page);
-
- err = loffs_mtd_write(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
- page_address(page));
- unlock_page(page);
- put_page(page);
- if (err)
- return err;
- }
- return 0;
-}
-
-static void logfs_mtd_writeseg(struct super_block *sb, u64 ofs, size_t len)
-{
- struct logfs_super *super = logfs_super(sb);
- int head;
-
- if (super->s_flags & LOGFS_SB_FLAG_RO)
- return;
-
- if (len == 0) {
- /* This can happen when the object fit perfectly into a
- * segment, the segment gets written per sync and subsequently
- * closed.
- */
- return;
- }
- head = ofs & (PAGE_SIZE - 1);
- if (head) {
- ofs -= head;
- len += head;
- }
- len = PAGE_ALIGN(len);
- __logfs_mtd_writeseg(sb, ofs, ofs >> PAGE_SHIFT, len >> PAGE_SHIFT);
-}
-
-static void logfs_mtd_put_device(struct logfs_super *s)
-{
- put_mtd_device(s->s_mtd);
-}
-
-static int logfs_mtd_can_write_buf(struct super_block *sb, u64 ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- void *buf;
- int err;
-
- buf = kmalloc(super->s_writesize, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- err = logfs_mtd_read(sb, ofs, super->s_writesize, buf);
- if (err)
- goto out;
- if (memchr_inv(buf, 0xff, super->s_writesize))
- err = -EIO;
- kfree(buf);
-out:
- return err;
-}
-
-static const struct logfs_device_ops mtd_devops = {
- .find_first_sb = logfs_mtd_find_first_sb,
- .find_last_sb = logfs_mtd_find_last_sb,
- .readpage = logfs_mtd_readpage,
- .writeseg = logfs_mtd_writeseg,
- .erase = logfs_mtd_erase,
- .can_write_buf = logfs_mtd_can_write_buf,
- .sync = logfs_mtd_sync,
- .put_device = logfs_mtd_put_device,
-};
-
-int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr)
-{
- struct mtd_info *mtd = get_mtd_device(NULL, mtdnr);
- if (IS_ERR(mtd))
- return PTR_ERR(mtd);
-
- s->s_bdev = NULL;
- s->s_mtd = mtd;
- s->s_devops = &mtd_devops;
- return 0;
-}
+++ /dev/null
-/*
- * fs/logfs/dir.c - directory-related code
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/slab.h>
-
-/*
- * Atomic dir operations
- *
- * Directory operations are by default not atomic. Dentries and Inodes are
- * created/removed/altered in separate operations. Therefore we need to do
- * a small amount of journaling.
- *
- * Create, link, mkdir, mknod and symlink all share the same function to do
- * the work: __logfs_create. This function works in two atomic steps:
- * 1. allocate inode (remember in journal)
- * 2. allocate dentry (clear journal)
- *
- * As we can only get interrupted between the two, when the inode we just
- * created is simply stored in the anchor. On next mount, if we were
- * interrupted, we delete the inode. From a users point of view the
- * operation never happened.
- *
- * Unlink and rmdir also share the same function: unlink. Again, this
- * function works in two atomic steps
- * 1. remove dentry (remember inode in journal)
- * 2. unlink inode (clear journal)
- *
- * And again, on the next mount, if we were interrupted, we delete the inode.
- * From a users point of view the operation succeeded.
- *
- * Rename is the real pain to deal with, harder than all the other methods
- * combined. Depending on the circumstances we can run into three cases.
- * A "target rename" where the target dentry already existed, a "local
- * rename" where both parent directories are identical or a "cross-directory
- * rename" in the remaining case.
- *
- * Local rename is atomic, as the old dentry is simply rewritten with a new
- * name.
- *
- * Cross-directory rename works in two steps, similar to __logfs_create and
- * logfs_unlink:
- * 1. Write new dentry (remember old dentry in journal)
- * 2. Remove old dentry (clear journal)
- *
- * Here we remember a dentry instead of an inode. On next mount, if we were
- * interrupted, we delete the dentry. From a users point of view, the
- * operation succeeded.
- *
- * Target rename works in three atomic steps:
- * 1. Attach old inode to new dentry (remember old dentry and new inode)
- * 2. Remove old dentry (still remember the new inode)
- * 3. Remove victim inode
- *
- * Here we remember both an inode an a dentry. If we get interrupted
- * between steps 1 and 2, we delete both the dentry and the inode. If
- * we get interrupted between steps 2 and 3, we delete just the inode.
- * In either case, the remaining objects are deleted on next mount. From
- * a users point of view, the operation succeeded.
- */
-
-static int write_dir(struct inode *dir, struct logfs_disk_dentry *dd,
- loff_t pos)
-{
- return logfs_inode_write(dir, dd, sizeof(*dd), pos, WF_LOCK, NULL);
-}
-
-static int write_inode(struct inode *inode)
-{
- return __logfs_write_inode(inode, NULL, WF_LOCK);
-}
-
-static s64 dir_seek_data(struct inode *inode, s64 pos)
-{
- s64 new_pos = logfs_seek_data(inode, pos);
-
- return max(pos, new_pos - 1);
-}
-
-static int beyond_eof(struct inode *inode, loff_t bix)
-{
- loff_t pos = bix << inode->i_sb->s_blocksize_bits;
- return pos >= i_size_read(inode);
-}
-
-/*
- * Prime value was chosen to be roughly 256 + 26. r5 hash uses 11,
- * so short names (len <= 9) don't even occupy the complete 32bit name
- * space. A prime >256 ensures short names quickly spread the 32bit
- * name space. Add about 26 for the estimated amount of information
- * of each character and pick a prime nearby, preferably a bit-sparse
- * one.
- */
-static u32 logfs_hash_32(const char *s, int len, u32 seed)
-{
- u32 hash = seed;
- int i;
-
- for (i = 0; i < len; i++)
- hash = hash * 293 + s[i];
- return hash;
-}
-
-/*
- * We have to satisfy several conflicting requirements here. Small
- * directories should stay fairly compact and not require too many
- * indirect blocks. The number of possible locations for a given hash
- * should be small to make lookup() fast. And we should try hard not
- * to overflow the 32bit name space or nfs and 32bit host systems will
- * be unhappy.
- *
- * So we use the following scheme. First we reduce the hash to 0..15
- * and try a direct block. If that is occupied we reduce the hash to
- * 16..255 and try an indirect block. Same for 2x and 3x indirect
- * blocks. Lastly we reduce the hash to 0x800_0000 .. 0xffff_ffff,
- * but use buckets containing eight entries instead of a single one.
- *
- * Using 16 entries should allow for a reasonable amount of hash
- * collisions, so the 32bit name space can be packed fairly tight
- * before overflowing. Oh and currently we don't overflow but return
- * and error.
- *
- * How likely are collisions? Doing the appropriate math is beyond me
- * and the Bronstein textbook. But running a test program to brute
- * force collisions for a couple of days showed that on average the
- * first collision occurs after 598M entries, with 290M being the
- * smallest result. Obviously 21 entries could already cause a
- * collision if all entries are carefully chosen.
- */
-static pgoff_t hash_index(u32 hash, int round)
-{
- u32 i0_blocks = I0_BLOCKS;
- u32 i1_blocks = I1_BLOCKS;
- u32 i2_blocks = I2_BLOCKS;
- u32 i3_blocks = I3_BLOCKS;
-
- switch (round) {
- case 0:
- return hash % i0_blocks;
- case 1:
- return i0_blocks + hash % (i1_blocks - i0_blocks);
- case 2:
- return i1_blocks + hash % (i2_blocks - i1_blocks);
- case 3:
- return i2_blocks + hash % (i3_blocks - i2_blocks);
- case 4 ... 19:
- return i3_blocks + 16 * (hash % (((1<<31) - i3_blocks) / 16))
- + round - 4;
- }
- BUG();
-}
-
-static struct page *logfs_get_dd_page(struct inode *dir, struct dentry *dentry)
-{
- const struct qstr *name = &dentry->d_name;
- struct page *page;
- struct logfs_disk_dentry *dd;
- u32 hash = logfs_hash_32(name->name, name->len, 0);
- pgoff_t index;
- int round;
-
- if (name->len > LOGFS_MAX_NAMELEN)
- return ERR_PTR(-ENAMETOOLONG);
-
- for (round = 0; round < 20; round++) {
- index = hash_index(hash, round);
-
- if (beyond_eof(dir, index))
- return NULL;
- if (!logfs_exist_block(dir, index))
- continue;
- page = read_cache_page(dir->i_mapping, index,
- (filler_t *)logfs_readpage, NULL);
- if (IS_ERR(page))
- return page;
- dd = kmap_atomic(page);
- BUG_ON(dd->namelen == 0);
-
- if (name->len != be16_to_cpu(dd->namelen) ||
- memcmp(name->name, dd->name, name->len)) {
- kunmap_atomic(dd);
- put_page(page);
- continue;
- }
-
- kunmap_atomic(dd);
- return page;
- }
- return NULL;
-}
-
-static int logfs_remove_inode(struct inode *inode)
-{
- int ret;
-
- drop_nlink(inode);
- ret = write_inode(inode);
- LOGFS_BUG_ON(ret, inode->i_sb);
- return ret;
-}
-
-static void abort_transaction(struct inode *inode, struct logfs_transaction *ta)
-{
- if (logfs_inode(inode)->li_block)
- logfs_inode(inode)->li_block->ta = NULL;
- kfree(ta);
-}
-
-static int logfs_unlink(struct inode *dir, struct dentry *dentry)
-{
- struct logfs_super *super = logfs_super(dir->i_sb);
- struct inode *inode = d_inode(dentry);
- struct logfs_transaction *ta;
- struct page *page;
- pgoff_t index;
- int ret;
-
- ta = kzalloc(sizeof(*ta), GFP_KERNEL);
- if (!ta)
- return -ENOMEM;
-
- ta->state = UNLINK_1;
- ta->ino = inode->i_ino;
-
- inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
-
- page = logfs_get_dd_page(dir, dentry);
- if (!page) {
- kfree(ta);
- return -ENOENT;
- }
- if (IS_ERR(page)) {
- kfree(ta);
- return PTR_ERR(page);
- }
- index = page->index;
- put_page(page);
-
- mutex_lock(&super->s_dirop_mutex);
- logfs_add_transaction(dir, ta);
-
- ret = logfs_delete(dir, index, NULL);
- if (!ret)
- ret = write_inode(dir);
-
- if (ret) {
- abort_transaction(dir, ta);
- printk(KERN_ERR"LOGFS: unable to delete inode\n");
- goto out;
- }
-
- ta->state = UNLINK_2;
- logfs_add_transaction(inode, ta);
- ret = logfs_remove_inode(inode);
-out:
- mutex_unlock(&super->s_dirop_mutex);
- return ret;
-}
-
-static inline int logfs_empty_dir(struct inode *dir)
-{
- u64 data;
-
- data = logfs_seek_data(dir, 0) << dir->i_sb->s_blocksize_bits;
- return data >= i_size_read(dir);
-}
-
-static int logfs_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = d_inode(dentry);
-
- if (!logfs_empty_dir(inode))
- return -ENOTEMPTY;
-
- return logfs_unlink(dir, dentry);
-}
-
-/* FIXME: readdir currently has it's own dir_walk code. I don't see a good
- * way to combine the two copies */
-static int logfs_readdir(struct file *file, struct dir_context *ctx)
-{
- struct inode *dir = file_inode(file);
- loff_t pos;
- struct page *page;
- struct logfs_disk_dentry *dd;
-
- if (ctx->pos < 0)
- return -EINVAL;
-
- if (!dir_emit_dots(file, ctx))
- return 0;
-
- pos = ctx->pos - 2;
- BUG_ON(pos < 0);
- for (;; pos++, ctx->pos++) {
- bool full;
- if (beyond_eof(dir, pos))
- break;
- if (!logfs_exist_block(dir, pos)) {
- /* deleted dentry */
- pos = dir_seek_data(dir, pos);
- continue;
- }
- page = read_cache_page(dir->i_mapping, pos,
- (filler_t *)logfs_readpage, NULL);
- if (IS_ERR(page))
- return PTR_ERR(page);
- dd = kmap(page);
- BUG_ON(dd->namelen == 0);
-
- full = !dir_emit(ctx, (char *)dd->name,
- be16_to_cpu(dd->namelen),
- be64_to_cpu(dd->ino), dd->type);
- kunmap(page);
- put_page(page);
- if (full)
- break;
- }
- return 0;
-}
-
-static void logfs_set_name(struct logfs_disk_dentry *dd, const struct qstr *name)
-{
- dd->namelen = cpu_to_be16(name->len);
- memcpy(dd->name, name->name, name->len);
-}
-
-static struct dentry *logfs_lookup(struct inode *dir, struct dentry *dentry,
- unsigned int flags)
-{
- struct page *page;
- struct logfs_disk_dentry *dd;
- pgoff_t index;
- u64 ino = 0;
- struct inode *inode;
-
- page = logfs_get_dd_page(dir, dentry);
- if (IS_ERR(page))
- return ERR_CAST(page);
- if (!page) {
- d_add(dentry, NULL);
- return NULL;
- }
- index = page->index;
- dd = kmap_atomic(page);
- ino = be64_to_cpu(dd->ino);
- kunmap_atomic(dd);
- put_page(page);
-
- inode = logfs_iget(dir->i_sb, ino);
- if (IS_ERR(inode))
- printk(KERN_ERR"LogFS: Cannot read inode #%llx for dentry (%lx, %lx)n",
- ino, dir->i_ino, index);
- return d_splice_alias(inode, dentry);
-}
-
-static void grow_dir(struct inode *dir, loff_t index)
-{
- index = (index + 1) << dir->i_sb->s_blocksize_bits;
- if (i_size_read(dir) < index)
- i_size_write(dir, index);
-}
-
-static int logfs_write_dir(struct inode *dir, struct dentry *dentry,
- struct inode *inode)
-{
- struct page *page;
- struct logfs_disk_dentry *dd;
- u32 hash = logfs_hash_32(dentry->d_name.name, dentry->d_name.len, 0);
- pgoff_t index;
- int round, err;
-
- for (round = 0; round < 20; round++) {
- index = hash_index(hash, round);
-
- if (logfs_exist_block(dir, index))
- continue;
- page = find_or_create_page(dir->i_mapping, index, GFP_KERNEL);
- if (!page)
- return -ENOMEM;
-
- dd = kmap_atomic(page);
- memset(dd, 0, sizeof(*dd));
- dd->ino = cpu_to_be64(inode->i_ino);
- dd->type = logfs_type(inode);
- logfs_set_name(dd, &dentry->d_name);
- kunmap_atomic(dd);
-
- err = logfs_write_buf(dir, page, WF_LOCK);
- unlock_page(page);
- put_page(page);
- if (!err)
- grow_dir(dir, index);
- return err;
- }
- /* FIXME: Is there a better return value? In most cases neither
- * the filesystem nor the directory are full. But we have had
- * too many collisions for this particular hash and no fallback.
- */
- return -ENOSPC;
-}
-
-static int __logfs_create(struct inode *dir, struct dentry *dentry,
- struct inode *inode, const char *dest, long destlen)
-{
- struct logfs_super *super = logfs_super(dir->i_sb);
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_transaction *ta;
- int ret;
-
- ta = kzalloc(sizeof(*ta), GFP_KERNEL);
- if (!ta) {
- drop_nlink(inode);
- iput(inode);
- return -ENOMEM;
- }
-
- ta->state = CREATE_1;
- ta->ino = inode->i_ino;
- mutex_lock(&super->s_dirop_mutex);
- logfs_add_transaction(inode, ta);
-
- if (dest) {
- /* symlink */
- ret = logfs_inode_write(inode, dest, destlen, 0, WF_LOCK, NULL);
- if (!ret)
- ret = write_inode(inode);
- } else {
- /* creat/mkdir/mknod */
- ret = write_inode(inode);
- }
- if (ret) {
- abort_transaction(inode, ta);
- li->li_flags |= LOGFS_IF_STILLBORN;
- /* FIXME: truncate symlink */
- drop_nlink(inode);
- iput(inode);
- goto out;
- }
-
- ta->state = CREATE_2;
- logfs_add_transaction(dir, ta);
- ret = logfs_write_dir(dir, dentry, inode);
- /* sync directory */
- if (!ret)
- ret = write_inode(dir);
-
- if (ret) {
- logfs_del_transaction(dir, ta);
- ta->state = CREATE_2;
- logfs_add_transaction(inode, ta);
- logfs_remove_inode(inode);
- iput(inode);
- goto out;
- }
- d_instantiate(dentry, inode);
-out:
- mutex_unlock(&super->s_dirop_mutex);
- return ret;
-}
-
-static int logfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
-{
- struct inode *inode;
-
- /*
- * FIXME: why do we have to fill in S_IFDIR, while the mode is
- * correct for mknod, creat, etc.? Smells like the vfs *should*
- * do it for us but for some reason fails to do so.
- */
- inode = logfs_new_inode(dir, S_IFDIR | mode);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- inode->i_op = &logfs_dir_iops;
- inode->i_fop = &logfs_dir_fops;
-
- return __logfs_create(dir, dentry, inode, NULL, 0);
-}
-
-static int logfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool excl)
-{
- struct inode *inode;
-
- inode = logfs_new_inode(dir, mode);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- inode->i_op = &logfs_reg_iops;
- inode->i_fop = &logfs_reg_fops;
- inode->i_mapping->a_ops = &logfs_reg_aops;
-
- return __logfs_create(dir, dentry, inode, NULL, 0);
-}
-
-static int logfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
- dev_t rdev)
-{
- struct inode *inode;
-
- if (dentry->d_name.len > LOGFS_MAX_NAMELEN)
- return -ENAMETOOLONG;
-
- inode = logfs_new_inode(dir, mode);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- init_special_inode(inode, mode, rdev);
-
- return __logfs_create(dir, dentry, inode, NULL, 0);
-}
-
-static int logfs_symlink(struct inode *dir, struct dentry *dentry,
- const char *target)
-{
- struct inode *inode;
- size_t destlen = strlen(target) + 1;
-
- if (destlen > dir->i_sb->s_blocksize)
- return -ENAMETOOLONG;
-
- inode = logfs_new_inode(dir, S_IFLNK | 0777);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- inode->i_op = &page_symlink_inode_operations;
- inode_nohighmem(inode);
- inode->i_mapping->a_ops = &logfs_reg_aops;
-
- return __logfs_create(dir, dentry, inode, target, destlen);
-}
-
-static int logfs_link(struct dentry *old_dentry, struct inode *dir,
- struct dentry *dentry)
-{
- struct inode *inode = d_inode(old_dentry);
-
- inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
- ihold(inode);
- inc_nlink(inode);
- mark_inode_dirty_sync(inode);
-
- return __logfs_create(dir, dentry, inode, NULL, 0);
-}
-
-static int logfs_get_dd(struct inode *dir, struct dentry *dentry,
- struct logfs_disk_dentry *dd, loff_t *pos)
-{
- struct page *page;
- void *map;
-
- page = logfs_get_dd_page(dir, dentry);
- if (IS_ERR(page))
- return PTR_ERR(page);
- *pos = page->index;
- map = kmap_atomic(page);
- memcpy(dd, map, sizeof(*dd));
- kunmap_atomic(map);
- put_page(page);
- return 0;
-}
-
-static int logfs_delete_dd(struct inode *dir, loff_t pos)
-{
- /*
- * Getting called with pos somewhere beyond eof is either a goofup
- * within this file or means someone maliciously edited the
- * (crc-protected) journal.
- */
- BUG_ON(beyond_eof(dir, pos));
- dir->i_ctime = dir->i_mtime = current_time(dir);
- log_dir(" Delete dentry (%lx, %llx)\n", dir->i_ino, pos);
- return logfs_delete(dir, pos, NULL);
-}
-
-/*
- * Cross-directory rename, target does not exist. Just a little nasty.
- * Create a new dentry in the target dir, then remove the old dentry,
- * all the while taking care to remember our operation in the journal.
- */
-static int logfs_rename_cross(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-{
- struct logfs_super *super = logfs_super(old_dir->i_sb);
- struct logfs_disk_dentry dd;
- struct logfs_transaction *ta;
- loff_t pos;
- int err;
-
- /* 1. locate source dd */
- err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
- if (err)
- return err;
-
- ta = kzalloc(sizeof(*ta), GFP_KERNEL);
- if (!ta)
- return -ENOMEM;
-
- ta->state = CROSS_RENAME_1;
- ta->dir = old_dir->i_ino;
- ta->pos = pos;
-
- /* 2. write target dd */
- mutex_lock(&super->s_dirop_mutex);
- logfs_add_transaction(new_dir, ta);
- err = logfs_write_dir(new_dir, new_dentry, d_inode(old_dentry));
- if (!err)
- err = write_inode(new_dir);
-
- if (err) {
- super->s_rename_dir = 0;
- super->s_rename_pos = 0;
- abort_transaction(new_dir, ta);
- goto out;
- }
-
- /* 3. remove source dd */
- ta->state = CROSS_RENAME_2;
- logfs_add_transaction(old_dir, ta);
- err = logfs_delete_dd(old_dir, pos);
- if (!err)
- err = write_inode(old_dir);
- LOGFS_BUG_ON(err, old_dir->i_sb);
-out:
- mutex_unlock(&super->s_dirop_mutex);
- return err;
-}
-
-static int logfs_replace_inode(struct inode *dir, struct dentry *dentry,
- struct logfs_disk_dentry *dd, struct inode *inode)
-{
- loff_t pos;
- int err;
-
- err = logfs_get_dd(dir, dentry, dd, &pos);
- if (err)
- return err;
- dd->ino = cpu_to_be64(inode->i_ino);
- dd->type = logfs_type(inode);
-
- err = write_dir(dir, dd, pos);
- if (err)
- return err;
- log_dir("Replace dentry (%lx, %llx) %s -> %llx\n", dir->i_ino, pos,
- dd->name, be64_to_cpu(dd->ino));
- return write_inode(dir);
-}
-
-/* Target dentry exists - the worst case. We need to attach the source
- * inode to the target dentry, then remove the orphaned target inode and
- * source dentry.
- */
-static int logfs_rename_target(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-{
- struct logfs_super *super = logfs_super(old_dir->i_sb);
- struct inode *old_inode = d_inode(old_dentry);
- struct inode *new_inode = d_inode(new_dentry);
- int isdir = S_ISDIR(old_inode->i_mode);
- struct logfs_disk_dentry dd;
- struct logfs_transaction *ta;
- loff_t pos;
- int err;
-
- BUG_ON(isdir != S_ISDIR(new_inode->i_mode));
- if (isdir) {
- if (!logfs_empty_dir(new_inode))
- return -ENOTEMPTY;
- }
-
- /* 1. locate source dd */
- err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
- if (err)
- return err;
-
- ta = kzalloc(sizeof(*ta), GFP_KERNEL);
- if (!ta)
- return -ENOMEM;
-
- ta->state = TARGET_RENAME_1;
- ta->dir = old_dir->i_ino;
- ta->pos = pos;
- ta->ino = new_inode->i_ino;
-
- /* 2. attach source inode to target dd */
- mutex_lock(&super->s_dirop_mutex);
- logfs_add_transaction(new_dir, ta);
- err = logfs_replace_inode(new_dir, new_dentry, &dd, old_inode);
- if (err) {
- super->s_rename_dir = 0;
- super->s_rename_pos = 0;
- super->s_victim_ino = 0;
- abort_transaction(new_dir, ta);
- goto out;
- }
-
- /* 3. remove source dd */
- ta->state = TARGET_RENAME_2;
- logfs_add_transaction(old_dir, ta);
- err = logfs_delete_dd(old_dir, pos);
- if (!err)
- err = write_inode(old_dir);
- LOGFS_BUG_ON(err, old_dir->i_sb);
-
- /* 4. remove target inode */
- ta->state = TARGET_RENAME_3;
- logfs_add_transaction(new_inode, ta);
- err = logfs_remove_inode(new_inode);
-
-out:
- mutex_unlock(&super->s_dirop_mutex);
- return err;
-}
-
-static int logfs_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- unsigned int flags)
-{
- if (flags & ~RENAME_NOREPLACE)
- return -EINVAL;
-
- if (d_really_is_positive(new_dentry))
- return logfs_rename_target(old_dir, old_dentry,
- new_dir, new_dentry);
- return logfs_rename_cross(old_dir, old_dentry, new_dir, new_dentry);
-}
-
-/* No locking done here, as this is called before .get_sb() returns. */
-int logfs_replay_journal(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode;
- u64 ino, pos;
- int err;
-
- if (super->s_victim_ino) {
- /* delete victim inode */
- ino = super->s_victim_ino;
- printk(KERN_INFO"LogFS: delete unmapped inode #%llx\n", ino);
- inode = logfs_iget(sb, ino);
- if (IS_ERR(inode))
- goto fail;
-
- LOGFS_BUG_ON(i_size_read(inode) > 0, sb);
- super->s_victim_ino = 0;
- err = logfs_remove_inode(inode);
- iput(inode);
- if (err) {
- super->s_victim_ino = ino;
- goto fail;
- }
- }
- if (super->s_rename_dir) {
- /* delete old dd from rename */
- ino = super->s_rename_dir;
- pos = super->s_rename_pos;
- printk(KERN_INFO"LogFS: delete unbacked dentry (%llx, %llx)\n",
- ino, pos);
- inode = logfs_iget(sb, ino);
- if (IS_ERR(inode))
- goto fail;
-
- super->s_rename_dir = 0;
- super->s_rename_pos = 0;
- err = logfs_delete_dd(inode, pos);
- iput(inode);
- if (err) {
- super->s_rename_dir = ino;
- super->s_rename_pos = pos;
- goto fail;
- }
- }
- return 0;
-fail:
- LOGFS_BUG(sb);
- return -EIO;
-}
-
-const struct inode_operations logfs_dir_iops = {
- .create = logfs_create,
- .link = logfs_link,
- .lookup = logfs_lookup,
- .mkdir = logfs_mkdir,
- .mknod = logfs_mknod,
- .rename = logfs_rename,
- .rmdir = logfs_rmdir,
- .symlink = logfs_symlink,
- .unlink = logfs_unlink,
-};
-const struct file_operations logfs_dir_fops = {
- .fsync = logfs_fsync,
- .unlocked_ioctl = logfs_ioctl,
- .iterate_shared = logfs_readdir,
- .read = generic_read_dir,
- .llseek = generic_file_llseek,
-};
+++ /dev/null
-/*
- * fs/logfs/file.c - prepare_write, commit_write and friends
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/sched.h>
-#include <linux/writeback.h>
-
-static int logfs_write_begin(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, unsigned flags,
- struct page **pagep, void **fsdata)
-{
- struct inode *inode = mapping->host;
- struct page *page;
- pgoff_t index = pos >> PAGE_SHIFT;
-
- page = grab_cache_page_write_begin(mapping, index, flags);
- if (!page)
- return -ENOMEM;
- *pagep = page;
-
- if ((len == PAGE_SIZE) || PageUptodate(page))
- return 0;
- if ((pos & PAGE_MASK) >= i_size_read(inode)) {
- unsigned start = pos & (PAGE_SIZE - 1);
- unsigned end = start + len;
-
- /* Reading beyond i_size is simple: memset to zero */
- zero_user_segments(page, 0, start, end, PAGE_SIZE);
- return 0;
- }
- return logfs_readpage_nolock(page);
-}
-
-static int logfs_write_end(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, unsigned copied, struct page *page,
- void *fsdata)
-{
- struct inode *inode = mapping->host;
- pgoff_t index = page->index;
- unsigned start = pos & (PAGE_SIZE - 1);
- unsigned end = start + copied;
- int ret = 0;
-
- BUG_ON(PAGE_SIZE != inode->i_sb->s_blocksize);
- BUG_ON(page->index > I3_BLOCKS);
-
- if (copied < len) {
- /*
- * Short write of a non-initialized paged. Just tell userspace
- * to retry the entire page.
- */
- if (!PageUptodate(page)) {
- copied = 0;
- goto out;
- }
- }
- if (copied == 0)
- goto out; /* FIXME: do we need to update inode? */
-
- if (i_size_read(inode) < (index << PAGE_SHIFT) + end) {
- i_size_write(inode, (index << PAGE_SHIFT) + end);
- mark_inode_dirty_sync(inode);
- }
-
- SetPageUptodate(page);
- if (!PageDirty(page)) {
- if (!get_page_reserve(inode, page))
- __set_page_dirty_nobuffers(page);
- else
- ret = logfs_write_buf(inode, page, WF_LOCK);
- }
-out:
- unlock_page(page);
- put_page(page);
- return ret ? ret : copied;
-}
-
-int logfs_readpage(struct file *file, struct page *page)
-{
- int ret;
-
- ret = logfs_readpage_nolock(page);
- unlock_page(page);
- return ret;
-}
-
-/* Clear the page's dirty flag in the radix tree. */
-/* TODO: mucking with PageWriteback is silly. Add a generic function to clear
- * the dirty bit from the radix tree for filesystems that don't have to wait
- * for page writeback to finish (i.e. any compressing filesystem).
- */
-static void clear_radix_tree_dirty(struct page *page)
-{
- BUG_ON(PagePrivate(page) || page->private);
- set_page_writeback(page);
- end_page_writeback(page);
-}
-
-static int __logfs_writepage(struct page *page)
-{
- struct inode *inode = page->mapping->host;
- int err;
-
- err = logfs_write_buf(inode, page, WF_LOCK);
- if (err)
- set_page_dirty(page);
- else
- clear_radix_tree_dirty(page);
- unlock_page(page);
- return err;
-}
-
-static int logfs_writepage(struct page *page, struct writeback_control *wbc)
-{
- struct inode *inode = page->mapping->host;
- loff_t i_size = i_size_read(inode);
- pgoff_t end_index = i_size >> PAGE_SHIFT;
- unsigned offset;
- u64 bix;
- level_t level;
-
- log_file("logfs_writepage(%lx, %lx, %p)\n", inode->i_ino, page->index,
- page);
-
- logfs_unpack_index(page->index, &bix, &level);
-
- /* Indirect blocks are never truncated */
- if (level != 0)
- return __logfs_writepage(page);
-
- /*
- * TODO: everything below is a near-verbatim copy of nobh_writepage().
- * The relevant bits should be factored out after logfs is merged.
- */
-
- /* Is the page fully inside i_size? */
- if (bix < end_index)
- return __logfs_writepage(page);
-
- /* Is the page fully outside i_size? (truncate in progress) */
- offset = i_size & (PAGE_SIZE-1);
- if (bix > end_index || offset == 0) {
- unlock_page(page);
- return 0; /* don't care */
- }
-
- /*
- * The page straddles i_size. It must be zeroed out on each and every
- * writepage invokation because it may be mmapped. "A file is mapped
- * in multiples of the page size. For a file that is not a multiple of
- * the page size, the remaining memory is zeroed when mapped, and
- * writes to that region are not written out to the file."
- */
- zero_user_segment(page, offset, PAGE_SIZE);
- return __logfs_writepage(page);
-}
-
-static void logfs_invalidatepage(struct page *page, unsigned int offset,
- unsigned int length)
-{
- struct logfs_block *block = logfs_block(page);
-
- if (block->reserved_bytes) {
- struct super_block *sb = page->mapping->host->i_sb;
- struct logfs_super *super = logfs_super(sb);
-
- super->s_dirty_pages -= block->reserved_bytes;
- block->ops->free_block(sb, block);
- BUG_ON(bitmap_weight(block->alias_map, LOGFS_BLOCK_FACTOR));
- } else
- move_page_to_btree(page);
- BUG_ON(PagePrivate(page) || page->private);
-}
-
-static int logfs_releasepage(struct page *page, gfp_t only_xfs_uses_this)
-{
- return 0; /* None of these are easy to release */
-}
-
-
-long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct inode *inode = file_inode(file);
- struct logfs_inode *li = logfs_inode(inode);
- unsigned int oldflags, flags;
- int err;
-
- switch (cmd) {
- case FS_IOC_GETFLAGS:
- flags = li->li_flags & LOGFS_FL_USER_VISIBLE;
- return put_user(flags, (int __user *)arg);
- case FS_IOC_SETFLAGS:
- if (IS_RDONLY(inode))
- return -EROFS;
-
- if (!inode_owner_or_capable(inode))
- return -EACCES;
-
- err = get_user(flags, (int __user *)arg);
- if (err)
- return err;
-
- inode_lock(inode);
- oldflags = li->li_flags;
- flags &= LOGFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~LOGFS_FL_USER_MODIFIABLE;
- li->li_flags = flags;
- inode_unlock(inode);
-
- inode->i_ctime = current_time(inode);
- mark_inode_dirty_sync(inode);
- return 0;
-
- default:
- return -ENOTTY;
- }
-}
-
-int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- struct super_block *sb = file->f_mapping->host->i_sb;
- struct inode *inode = file->f_mapping->host;
- int ret;
-
- ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
- if (ret)
- return ret;
-
- inode_lock(inode);
- logfs_get_wblocks(sb, NULL, WF_LOCK);
- logfs_write_anchor(sb);
- logfs_put_wblocks(sb, NULL, WF_LOCK);
- inode_unlock(inode);
-
- return 0;
-}
-
-static int logfs_setattr(struct dentry *dentry, struct iattr *attr)
-{
- struct inode *inode = d_inode(dentry);
- int err = 0;
-
- err = setattr_prepare(dentry, attr);
- if (err)
- return err;
-
- if (attr->ia_valid & ATTR_SIZE) {
- err = logfs_truncate(inode, attr->ia_size);
- if (err)
- return err;
- }
-
- setattr_copy(inode, attr);
- mark_inode_dirty(inode);
- return 0;
-}
-
-const struct inode_operations logfs_reg_iops = {
- .setattr = logfs_setattr,
-};
-
-const struct file_operations logfs_reg_fops = {
- .read_iter = generic_file_read_iter,
- .write_iter = generic_file_write_iter,
- .fsync = logfs_fsync,
- .unlocked_ioctl = logfs_ioctl,
- .llseek = generic_file_llseek,
- .mmap = generic_file_readonly_mmap,
- .open = generic_file_open,
-};
-
-const struct address_space_operations logfs_reg_aops = {
- .invalidatepage = logfs_invalidatepage,
- .readpage = logfs_readpage,
- .releasepage = logfs_releasepage,
- .set_page_dirty = __set_page_dirty_nobuffers,
- .writepage = logfs_writepage,
- .writepages = generic_writepages,
- .write_begin = logfs_write_begin,
- .write_end = logfs_write_end,
-};
+++ /dev/null
-/*
- * fs/logfs/gc.c - garbage collection code
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-/*
- * Wear leveling needs to kick in when the difference between low erase
- * counts and high erase counts gets too big. A good value for "too big"
- * may be somewhat below 10% of maximum erase count for the device.
- * Why not 397, to pick a nice round number with no specific meaning? :)
- *
- * WL_RATELIMIT is the minimum time between two wear level events. A huge
- * number of segments may fulfil the requirements for wear leveling at the
- * same time. If that happens we don't want to cause a latency from hell,
- * but just gently pick one segment every so often and minimize overhead.
- */
-#define WL_DELTA 397
-#define WL_RATELIMIT 100
-#define MAX_OBJ_ALIASES 2600
-#define SCAN_RATIO 512 /* number of scanned segments per gc'd segment */
-#define LIST_SIZE 64 /* base size of candidate lists */
-#define SCAN_ROUNDS 128 /* maximum number of complete medium scans */
-#define SCAN_ROUNDS_HIGH 4 /* maximum number of higher-level scans */
-
-static int no_free_segments(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- return super->s_free_list.count;
-}
-
-/* journal has distance -1, top-most ifile layer distance 0 */
-static u8 root_distance(struct super_block *sb, gc_level_t __gc_level)
-{
- struct logfs_super *super = logfs_super(sb);
- u8 gc_level = (__force u8)__gc_level;
-
- switch (gc_level) {
- case 0: /* fall through */
- case 1: /* fall through */
- case 2: /* fall through */
- case 3:
- /* file data or indirect blocks */
- return super->s_ifile_levels + super->s_iblock_levels - gc_level;
- case 6: /* fall through */
- case 7: /* fall through */
- case 8: /* fall through */
- case 9:
- /* inode file data or indirect blocks */
- return super->s_ifile_levels - (gc_level - 6);
- default:
- printk(KERN_ERR"LOGFS: segment of unknown level %x found\n",
- gc_level);
- WARN_ON(1);
- return super->s_ifile_levels + super->s_iblock_levels;
- }
-}
-
-static int segment_is_reserved(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area;
- void *reserved;
- int i;
-
- /* Some segments are reserved. Just pretend they were all valid */
- reserved = btree_lookup32(&super->s_reserved_segments, segno);
- if (reserved)
- return 1;
-
- /* Currently open segments */
- for_each_area(i) {
- area = super->s_area[i];
- if (area->a_is_open && area->a_segno == segno)
- return 1;
- }
-
- return 0;
-}
-
-static void logfs_mark_segment_bad(struct super_block *sb, u32 segno)
-{
- BUG();
-}
-
-/*
- * Returns the bytes consumed by valid objects in this segment. Object headers
- * are counted, the segment header is not.
- */
-static u32 logfs_valid_bytes(struct super_block *sb, u32 segno, u32 *ec,
- gc_level_t *gc_level)
-{
- struct logfs_segment_entry se;
- u32 ec_level;
-
- logfs_get_segment_entry(sb, segno, &se);
- if (se.ec_level == cpu_to_be32(BADSEG) ||
- se.valid == cpu_to_be32(RESERVED))
- return RESERVED;
-
- ec_level = be32_to_cpu(se.ec_level);
- *ec = ec_level >> 4;
- *gc_level = GC_LEVEL(ec_level & 0xf);
- return be32_to_cpu(se.valid);
-}
-
-static void logfs_cleanse_block(struct super_block *sb, u64 ofs, u64 ino,
- u64 bix, gc_level_t gc_level)
-{
- struct inode *inode;
- int err, cookie;
-
- inode = logfs_safe_iget(sb, ino, &cookie);
- err = logfs_rewrite_block(inode, bix, ofs, gc_level, 0);
- BUG_ON(err);
- logfs_safe_iput(inode, cookie);
-}
-
-static u32 logfs_gc_segment(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_segment_header sh;
- struct logfs_object_header oh;
- u64 ofs, ino, bix;
- u32 seg_ofs, logical_segno, cleaned = 0;
- int err, len, valid;
- gc_level_t gc_level;
-
- LOGFS_BUG_ON(segment_is_reserved(sb, segno), sb);
-
- btree_insert32(&super->s_reserved_segments, segno, (void *)1, GFP_NOFS);
- err = wbuf_read(sb, dev_ofs(sb, segno, 0), sizeof(sh), &sh);
- BUG_ON(err);
- gc_level = GC_LEVEL(sh.level);
- logical_segno = be32_to_cpu(sh.segno);
- if (sh.crc != logfs_crc32(&sh, sizeof(sh), 4)) {
- logfs_mark_segment_bad(sb, segno);
- cleaned = -1;
- goto out;
- }
-
- for (seg_ofs = LOGFS_SEGMENT_HEADERSIZE;
- seg_ofs + sizeof(oh) < super->s_segsize; ) {
- ofs = dev_ofs(sb, logical_segno, seg_ofs);
- err = wbuf_read(sb, dev_ofs(sb, segno, seg_ofs), sizeof(oh),
- &oh);
- BUG_ON(err);
-
- if (!memchr_inv(&oh, 0xff, sizeof(oh)))
- break;
-
- if (oh.crc != logfs_crc32(&oh, sizeof(oh) - 4, 4)) {
- logfs_mark_segment_bad(sb, segno);
- cleaned = super->s_segsize - 1;
- goto out;
- }
-
- ino = be64_to_cpu(oh.ino);
- bix = be64_to_cpu(oh.bix);
- len = sizeof(oh) + be16_to_cpu(oh.len);
- valid = logfs_is_valid_block(sb, ofs, ino, bix, gc_level);
- if (valid == 1) {
- logfs_cleanse_block(sb, ofs, ino, bix, gc_level);
- cleaned += len;
- } else if (valid == 2) {
- /* Will be invalid upon journal commit */
- cleaned += len;
- }
- seg_ofs += len;
- }
-out:
- btree_remove32(&super->s_reserved_segments, segno);
- return cleaned;
-}
-
-static struct gc_candidate *add_list(struct gc_candidate *cand,
- struct candidate_list *list)
-{
- struct rb_node **p = &list->rb_tree.rb_node;
- struct rb_node *parent = NULL;
- struct gc_candidate *cur;
- int comp;
-
- cand->list = list;
- while (*p) {
- parent = *p;
- cur = rb_entry(parent, struct gc_candidate, rb_node);
-
- if (list->sort_by_ec)
- comp = cand->erase_count < cur->erase_count;
- else
- comp = cand->valid < cur->valid;
-
- if (comp)
- p = &parent->rb_left;
- else
- p = &parent->rb_right;
- }
- rb_link_node(&cand->rb_node, parent, p);
- rb_insert_color(&cand->rb_node, &list->rb_tree);
-
- if (list->count <= list->maxcount) {
- list->count++;
- return NULL;
- }
- cand = rb_entry(rb_last(&list->rb_tree), struct gc_candidate, rb_node);
- rb_erase(&cand->rb_node, &list->rb_tree);
- cand->list = NULL;
- return cand;
-}
-
-static void remove_from_list(struct gc_candidate *cand)
-{
- struct candidate_list *list = cand->list;
-
- rb_erase(&cand->rb_node, &list->rb_tree);
- list->count--;
-}
-
-static void free_candidate(struct super_block *sb, struct gc_candidate *cand)
-{
- struct logfs_super *super = logfs_super(sb);
-
- btree_remove32(&super->s_cand_tree, cand->segno);
- kfree(cand);
-}
-
-u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec)
-{
- struct gc_candidate *cand;
- u32 segno;
-
- BUG_ON(list->count == 0);
-
- cand = rb_entry(rb_first(&list->rb_tree), struct gc_candidate, rb_node);
- remove_from_list(cand);
- segno = cand->segno;
- if (ec)
- *ec = cand->erase_count;
- free_candidate(sb, cand);
- return segno;
-}
-
-/*
- * We have several lists to manage segments with. The reserve_list is used to
- * deal with bad blocks. We try to keep the best (lowest ec) segments on this
- * list.
- * The free_list contains free segments for normal usage. It usually gets the
- * second pick after the reserve_list. But when the free_list is running short
- * it is more important to keep the free_list full than to keep a reserve.
- *
- * Segments that are not free are put onto a per-level low_list. If we have
- * to run garbage collection, we pick a candidate from there. All segments on
- * those lists should have at least some free space so GC will make progress.
- *
- * And last we have the ec_list, which is used to pick segments for wear
- * leveling.
- *
- * If all appropriate lists are full, we simply free the candidate and forget
- * about that segment for a while. We have better candidates for each purpose.
- */
-static void __add_candidate(struct super_block *sb, struct gc_candidate *cand)
-{
- struct logfs_super *super = logfs_super(sb);
- u32 full = super->s_segsize - LOGFS_SEGMENT_RESERVE;
-
- if (cand->valid == 0) {
- /* 100% free segments */
- log_gc_noisy("add reserve segment %x (ec %x) at %llx\n",
- cand->segno, cand->erase_count,
- dev_ofs(sb, cand->segno, 0));
- cand = add_list(cand, &super->s_reserve_list);
- if (cand) {
- log_gc_noisy("add free segment %x (ec %x) at %llx\n",
- cand->segno, cand->erase_count,
- dev_ofs(sb, cand->segno, 0));
- cand = add_list(cand, &super->s_free_list);
- }
- } else {
- /* good candidates for Garbage Collection */
- if (cand->valid < full)
- cand = add_list(cand, &super->s_low_list[cand->dist]);
- /* good candidates for wear leveling,
- * segments that were recently written get ignored */
- if (cand)
- cand = add_list(cand, &super->s_ec_list);
- }
- if (cand)
- free_candidate(sb, cand);
-}
-
-static int add_candidate(struct super_block *sb, u32 segno, u32 valid, u32 ec,
- u8 dist)
-{
- struct logfs_super *super = logfs_super(sb);
- struct gc_candidate *cand;
-
- cand = kmalloc(sizeof(*cand), GFP_NOFS);
- if (!cand)
- return -ENOMEM;
-
- cand->segno = segno;
- cand->valid = valid;
- cand->erase_count = ec;
- cand->dist = dist;
-
- btree_insert32(&super->s_cand_tree, segno, cand, GFP_NOFS);
- __add_candidate(sb, cand);
- return 0;
-}
-
-static void remove_segment_from_lists(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct gc_candidate *cand;
-
- cand = btree_lookup32(&super->s_cand_tree, segno);
- if (cand) {
- remove_from_list(cand);
- free_candidate(sb, cand);
- }
-}
-
-static void scan_segment(struct super_block *sb, u32 segno)
-{
- u32 valid, ec = 0;
- gc_level_t gc_level = 0;
- u8 dist;
-
- if (segment_is_reserved(sb, segno))
- return;
-
- remove_segment_from_lists(sb, segno);
- valid = logfs_valid_bytes(sb, segno, &ec, &gc_level);
- if (valid == RESERVED)
- return;
-
- dist = root_distance(sb, gc_level);
- add_candidate(sb, segno, valid, ec, dist);
-}
-
-static struct gc_candidate *first_in_list(struct candidate_list *list)
-{
- if (list->count == 0)
- return NULL;
- return rb_entry(rb_first(&list->rb_tree), struct gc_candidate, rb_node);
-}
-
-/*
- * Find the best segment for garbage collection. Main criterion is
- * the segment requiring the least effort to clean. Secondary
- * criterion is to GC on the lowest level available.
- *
- * So we search the least effort segment on the lowest level first,
- * then move up and pick another segment iff is requires significantly
- * less effort. Hence the LOGFS_MAX_OBJECTSIZE in the comparison.
- */
-static struct gc_candidate *get_candidate(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i, max_dist;
- struct gc_candidate *cand = NULL, *this;
-
- max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS - 1);
-
- for (i = max_dist; i >= 0; i--) {
- this = first_in_list(&super->s_low_list[i]);
- if (!this)
- continue;
- if (!cand)
- cand = this;
- if (this->valid + LOGFS_MAX_OBJECTSIZE <= cand->valid)
- cand = this;
- }
- return cand;
-}
-
-static int __logfs_gc_once(struct super_block *sb, struct gc_candidate *cand)
-{
- struct logfs_super *super = logfs_super(sb);
- gc_level_t gc_level;
- u32 cleaned, valid, segno, ec;
- u8 dist;
-
- if (!cand) {
- log_gc("GC attempted, but no candidate found\n");
- return 0;
- }
-
- segno = cand->segno;
- dist = cand->dist;
- valid = logfs_valid_bytes(sb, segno, &ec, &gc_level);
- free_candidate(sb, cand);
- log_gc("GC segment #%02x at %llx, %x required, %x free, %x valid, %llx free\n",
- segno, (u64)segno << super->s_segshift,
- dist, no_free_segments(sb), valid,
- super->s_free_bytes);
- cleaned = logfs_gc_segment(sb, segno);
- log_gc("GC segment #%02x complete - now %x valid\n", segno,
- valid - cleaned);
- BUG_ON(cleaned != valid);
- return 1;
-}
-
-static int logfs_gc_once(struct super_block *sb)
-{
- struct gc_candidate *cand;
-
- cand = get_candidate(sb);
- if (cand)
- remove_from_list(cand);
- return __logfs_gc_once(sb, cand);
-}
-
-/* returns 1 if a wrap occurs, 0 otherwise */
-static int logfs_scan_some(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- u32 segno;
- int i, ret = 0;
-
- segno = super->s_sweeper;
- for (i = SCAN_RATIO; i > 0; i--) {
- segno++;
- if (segno >= super->s_no_segs) {
- segno = 0;
- ret = 1;
- /* Break out of the loop. We want to read a single
- * block from the segment size on next invocation if
- * SCAN_RATIO is set to match block size
- */
- break;
- }
-
- scan_segment(sb, segno);
- }
- super->s_sweeper = segno;
- return ret;
-}
-
-/*
- * In principle, this function should loop forever, looking for GC candidates
- * and moving data. LogFS is designed in such a way that this loop is
- * guaranteed to terminate.
- *
- * Limiting the loop to some iterations serves purely to catch cases when
- * these guarantees have failed. An actual endless loop is an obvious bug
- * and should be reported as such.
- */
-static void __logfs_gc_pass(struct super_block *sb, int target)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_block *block;
- int round, progress, last_progress = 0;
-
- /*
- * Doing too many changes to the segfile at once would result
- * in a large number of aliases. Write the journal before
- * things get out of hand.
- */
- if (super->s_shadow_tree.no_shadowed_segments >= MAX_OBJ_ALIASES)
- logfs_write_anchor(sb);
-
- if (no_free_segments(sb) >= target &&
- super->s_no_object_aliases < MAX_OBJ_ALIASES)
- return;
-
- log_gc("__logfs_gc_pass(%x)\n", target);
- for (round = 0; round < SCAN_ROUNDS; ) {
- if (no_free_segments(sb) >= target)
- goto write_alias;
-
- /* Sync in-memory state with on-medium state in case they
- * diverged */
- logfs_write_anchor(sb);
- round += logfs_scan_some(sb);
- if (no_free_segments(sb) >= target)
- goto write_alias;
- progress = logfs_gc_once(sb);
- if (progress)
- last_progress = round;
- else if (round - last_progress > 2)
- break;
- continue;
-
- /*
- * The goto logic is nasty, I just don't know a better way to
- * code it. GC is supposed to ensure two things:
- * 1. Enough free segments are available.
- * 2. The number of aliases is bounded.
- * When 1. is achieved, we take a look at 2. and write back
- * some alias-containing blocks, if necessary. However, after
- * each such write we need to go back to 1., as writes can
- * consume free segments.
- */
-write_alias:
- if (super->s_no_object_aliases < MAX_OBJ_ALIASES)
- return;
- if (list_empty(&super->s_object_alias)) {
- /* All aliases are still in btree */
- return;
- }
- log_gc("Write back one alias\n");
- block = list_entry(super->s_object_alias.next,
- struct logfs_block, alias_list);
- block->ops->write_block(block);
- /*
- * To round off the nasty goto logic, we reset round here. It
- * is a safety-net for GC not making any progress and limited
- * to something reasonably small. If incremented it for every
- * single alias, the loop could terminate rather quickly.
- */
- round = 0;
- }
- LOGFS_BUG(sb);
-}
-
-static int wl_ratelimit(struct super_block *sb, u64 *next_event)
-{
- struct logfs_super *super = logfs_super(sb);
-
- if (*next_event < super->s_gec) {
- *next_event = super->s_gec + WL_RATELIMIT;
- return 0;
- }
- return 1;
-}
-
-static void logfs_wl_pass(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct gc_candidate *wl_cand, *free_cand;
-
- if (wl_ratelimit(sb, &super->s_wl_gec_ostore))
- return;
-
- wl_cand = first_in_list(&super->s_ec_list);
- if (!wl_cand)
- return;
- free_cand = first_in_list(&super->s_free_list);
- if (!free_cand)
- return;
-
- if (wl_cand->erase_count < free_cand->erase_count + WL_DELTA) {
- remove_from_list(wl_cand);
- __logfs_gc_once(sb, wl_cand);
- }
-}
-
-/*
- * The journal needs wear leveling as well. But moving the journal is an
- * expensive operation so we try to avoid it as much as possible. And if we
- * have to do it, we move the whole journal, not individual segments.
- *
- * Ratelimiting is not strictly necessary here, it mainly serves to avoid the
- * calculations. First we check whether moving the journal would be a
- * significant improvement. That means that a) the current journal segments
- * have more wear than the future journal segments and b) the current journal
- * segments have more wear than normal ostore segments.
- * Rationale for b) is that we don't have to move the journal if it is aging
- * less than the ostore, even if the reserve segments age even less (they are
- * excluded from wear leveling, after all).
- * Next we check that the superblocks have less wear than the journal. Since
- * moving the journal requires writing the superblocks, we have to protect the
- * superblocks even more than the journal.
- *
- * Also we double the acceptable wear difference, compared to ostore wear
- * leveling. Journal data is read and rewritten rapidly, comparatively. So
- * soft errors have much less time to accumulate and we allow the journal to
- * be a bit worse than the ostore.
- */
-static void logfs_journal_wl_pass(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct gc_candidate *cand;
- u32 min_journal_ec = -1, max_reserve_ec = 0;
- int i;
-
- if (wl_ratelimit(sb, &super->s_wl_gec_journal))
- return;
-
- if (super->s_reserve_list.count < super->s_no_journal_segs) {
- /* Reserve is not full enough to move complete journal */
- return;
- }
-
- journal_for_each(i)
- if (super->s_journal_seg[i])
- min_journal_ec = min(min_journal_ec,
- super->s_journal_ec[i]);
- cand = rb_entry(rb_first(&super->s_free_list.rb_tree),
- struct gc_candidate, rb_node);
- max_reserve_ec = cand->erase_count;
- for (i = 0; i < 2; i++) {
- struct logfs_segment_entry se;
- u32 segno = seg_no(sb, super->s_sb_ofs[i]);
- u32 ec;
-
- logfs_get_segment_entry(sb, segno, &se);
- ec = be32_to_cpu(se.ec_level) >> 4;
- max_reserve_ec = max(max_reserve_ec, ec);
- }
-
- if (min_journal_ec > max_reserve_ec + 2 * WL_DELTA) {
- do_logfs_journal_wl_pass(sb);
- }
-}
-
-void logfs_gc_pass(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- //BUG_ON(mutex_trylock(&logfs_super(sb)->s_w_mutex));
- /* Write journal before free space is getting saturated with dirty
- * objects.
- */
- if (super->s_dirty_used_bytes + super->s_dirty_free_bytes
- + LOGFS_MAX_OBJECTSIZE >= super->s_free_bytes)
- logfs_write_anchor(sb);
- __logfs_gc_pass(sb, super->s_total_levels);
- logfs_wl_pass(sb);
- logfs_journal_wl_pass(sb);
-}
-
-static int check_area(struct super_block *sb, int i)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_area[i];
- gc_level_t gc_level;
- u32 cleaned, valid, ec;
- u32 segno = area->a_segno;
- u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
-
- if (!area->a_is_open)
- return 0;
-
- if (super->s_devops->can_write_buf(sb, ofs) == 0)
- return 0;
-
- printk(KERN_INFO"LogFS: Possibly incomplete write at %llx\n", ofs);
- /*
- * The device cannot write back the write buffer. Most likely the
- * wbuf was already written out and the system crashed at some point
- * before the journal commit happened. In that case we wouldn't have
- * to do anything. But if the crash happened before the wbuf was
- * written out correctly, we must GC this segment. So assume the
- * worst and always do the GC run.
- */
- area->a_is_open = 0;
- valid = logfs_valid_bytes(sb, segno, &ec, &gc_level);
- cleaned = logfs_gc_segment(sb, segno);
- if (cleaned != valid)
- return -EIO;
- return 0;
-}
-
-int logfs_check_areas(struct super_block *sb)
-{
- int i, err;
-
- for_each_area(i) {
- err = check_area(sb, i);
- if (err)
- return err;
- }
- return 0;
-}
-
-static void logfs_init_candlist(struct candidate_list *list, int maxcount,
- int sort_by_ec)
-{
- list->count = 0;
- list->maxcount = maxcount;
- list->sort_by_ec = sort_by_ec;
- list->rb_tree = RB_ROOT;
-}
-
-int logfs_init_gc(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i;
-
- btree_init_mempool32(&super->s_cand_tree, super->s_btree_pool);
- logfs_init_candlist(&super->s_free_list, LIST_SIZE + SCAN_RATIO, 1);
- logfs_init_candlist(&super->s_reserve_list,
- super->s_bad_seg_reserve, 1);
- for_each_area(i)
- logfs_init_candlist(&super->s_low_list[i], LIST_SIZE, 0);
- logfs_init_candlist(&super->s_ec_list, LIST_SIZE, 1);
- return 0;
-}
-
-static void logfs_cleanup_list(struct super_block *sb,
- struct candidate_list *list)
-{
- struct gc_candidate *cand;
-
- while (list->count) {
- cand = rb_entry(list->rb_tree.rb_node, struct gc_candidate,
- rb_node);
- remove_from_list(cand);
- free_candidate(sb, cand);
- }
- BUG_ON(list->rb_tree.rb_node);
-}
-
-void logfs_cleanup_gc(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i;
-
- if (!super->s_free_list.count)
- return;
-
- /*
- * FIXME: The btree may still contain a single empty node. So we
- * call the grim visitor to clean up that mess. Btree code should
- * do it for us, really.
- */
- btree_grim_visitor32(&super->s_cand_tree, 0, NULL);
- logfs_cleanup_list(sb, &super->s_free_list);
- logfs_cleanup_list(sb, &super->s_reserve_list);
- for_each_area(i)
- logfs_cleanup_list(sb, &super->s_low_list[i]);
- logfs_cleanup_list(sb, &super->s_ec_list);
-}
+++ /dev/null
-/*
- * fs/logfs/inode.c - inode handling code
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/slab.h>
-#include <linux/writeback.h>
-#include <linux/backing-dev.h>
-
-/*
- * How soon to reuse old inode numbers? LogFS doesn't store deleted inodes
- * on the medium. It therefore also lacks a method to store the previous
- * generation number for deleted inodes. Instead a single generation number
- * is stored which will be used for new inodes. Being just a 32bit counter,
- * this can obvious wrap relatively quickly. So we only reuse inodes if we
- * know that a fair number of inodes can be created before we have to increment
- * the generation again - effectively adding some bits to the counter.
- * But being too aggressive here means we keep a very large and very sparse
- * inode file, wasting space on indirect blocks.
- * So what is a good value? Beats me. 64k seems moderately bad on both
- * fronts, so let's use that for now...
- *
- * NFS sucks, as everyone already knows.
- */
-#define INOS_PER_WRAP (0x10000)
-
-/*
- * Logfs' requirement to read inodes for garbage collection makes life a bit
- * harder. GC may have to read inodes that are in I_FREEING state, when they
- * are being written out - and waiting for GC to make progress, naturally.
- *
- * So we cannot just call iget() or some variant of it, but first have to check
- * whether the inode in question might be in I_FREEING state. Therefore we
- * maintain our own per-sb list of "almost deleted" inodes and check against
- * that list first. Normally this should be at most 1-2 entries long.
- *
- * Also, inodes have logfs-specific reference counting on top of what the vfs
- * does. When .destroy_inode is called, normally the reference count will drop
- * to zero and the inode gets deleted. But if GC accessed the inode, its
- * refcount will remain nonzero and final deletion will have to wait.
- *
- * As a result we have two sets of functions to get/put inodes:
- * logfs_safe_iget/logfs_safe_iput - safe to call from GC context
- * logfs_iget/iput - normal version
- */
-static struct kmem_cache *logfs_inode_cache;
-
-static DEFINE_SPINLOCK(logfs_inode_lock);
-
-static void logfs_inode_setops(struct inode *inode)
-{
- switch (inode->i_mode & S_IFMT) {
- case S_IFDIR:
- inode->i_op = &logfs_dir_iops;
- inode->i_fop = &logfs_dir_fops;
- inode->i_mapping->a_ops = &logfs_reg_aops;
- break;
- case S_IFREG:
- inode->i_op = &logfs_reg_iops;
- inode->i_fop = &logfs_reg_fops;
- inode->i_mapping->a_ops = &logfs_reg_aops;
- break;
- case S_IFLNK:
- inode->i_op = &page_symlink_inode_operations;
- inode_nohighmem(inode);
- inode->i_mapping->a_ops = &logfs_reg_aops;
- break;
- case S_IFSOCK: /* fall through */
- case S_IFBLK: /* fall through */
- case S_IFCHR: /* fall through */
- case S_IFIFO:
- init_special_inode(inode, inode->i_mode, inode->i_rdev);
- break;
- default:
- BUG();
- }
-}
-
-static struct inode *__logfs_iget(struct super_block *sb, ino_t ino)
-{
- struct inode *inode = iget_locked(sb, ino);
- int err;
-
- if (!inode)
- return ERR_PTR(-ENOMEM);
- if (!(inode->i_state & I_NEW))
- return inode;
-
- err = logfs_read_inode(inode);
- if (err || inode->i_nlink == 0) {
- /* inode->i_nlink == 0 can be true when called from
- * block validator */
- /* set i_nlink to 0 to prevent caching */
- clear_nlink(inode);
- logfs_inode(inode)->li_flags |= LOGFS_IF_ZOMBIE;
- iget_failed(inode);
- if (!err)
- err = -ENOENT;
- return ERR_PTR(err);
- }
-
- logfs_inode_setops(inode);
- unlock_new_inode(inode);
- return inode;
-}
-
-struct inode *logfs_iget(struct super_block *sb, ino_t ino)
-{
- BUG_ON(ino == LOGFS_INO_MASTER);
- BUG_ON(ino == LOGFS_INO_SEGFILE);
- return __logfs_iget(sb, ino);
-}
-
-/*
- * is_cached is set to 1 if we hand out a cached inode, 0 otherwise.
- * this allows logfs_iput to do the right thing later
- */
-struct inode *logfs_safe_iget(struct super_block *sb, ino_t ino, int *is_cached)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_inode *li;
-
- if (ino == LOGFS_INO_MASTER)
- return super->s_master_inode;
- if (ino == LOGFS_INO_SEGFILE)
- return super->s_segfile_inode;
-
- spin_lock(&logfs_inode_lock);
- list_for_each_entry(li, &super->s_freeing_list, li_freeing_list)
- if (li->vfs_inode.i_ino == ino) {
- li->li_refcount++;
- spin_unlock(&logfs_inode_lock);
- *is_cached = 1;
- return &li->vfs_inode;
- }
- spin_unlock(&logfs_inode_lock);
-
- *is_cached = 0;
- return __logfs_iget(sb, ino);
-}
-
-static void logfs_i_callback(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(logfs_inode_cache, logfs_inode(inode));
-}
-
-static void __logfs_destroy_inode(struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- BUG_ON(li->li_block);
- list_del(&li->li_freeing_list);
- call_rcu(&inode->i_rcu, logfs_i_callback);
-}
-
-static void __logfs_destroy_meta_inode(struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- BUG_ON(li->li_block);
- call_rcu(&inode->i_rcu, logfs_i_callback);
-}
-
-static void logfs_destroy_inode(struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if (inode->i_ino < LOGFS_RESERVED_INOS) {
- /*
- * The reserved inodes are never destroyed unless we are in
- * unmont path.
- */
- __logfs_destroy_meta_inode(inode);
- return;
- }
-
- BUG_ON(list_empty(&li->li_freeing_list));
- spin_lock(&logfs_inode_lock);
- li->li_refcount--;
- if (li->li_refcount == 0)
- __logfs_destroy_inode(inode);
- spin_unlock(&logfs_inode_lock);
-}
-
-void logfs_safe_iput(struct inode *inode, int is_cached)
-{
- if (inode->i_ino == LOGFS_INO_MASTER)
- return;
- if (inode->i_ino == LOGFS_INO_SEGFILE)
- return;
-
- if (is_cached) {
- logfs_destroy_inode(inode);
- return;
- }
-
- iput(inode);
-}
-
-static void logfs_init_inode(struct super_block *sb, struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- int i;
-
- li->li_flags = 0;
- li->li_height = 0;
- li->li_used_bytes = 0;
- li->li_block = NULL;
- i_uid_write(inode, 0);
- i_gid_write(inode, 0);
- inode->i_size = 0;
- inode->i_blocks = 0;
- inode->i_ctime = current_time(inode);
- inode->i_mtime = current_time(inode);
- li->li_refcount = 1;
- INIT_LIST_HEAD(&li->li_freeing_list);
-
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- li->li_data[i] = 0;
-
- return;
-}
-
-static struct inode *logfs_alloc_inode(struct super_block *sb)
-{
- struct logfs_inode *li;
-
- li = kmem_cache_alloc(logfs_inode_cache, GFP_NOFS);
- if (!li)
- return NULL;
- logfs_init_inode(sb, &li->vfs_inode);
- return &li->vfs_inode;
-}
-
-/*
- * In logfs inodes are written to an inode file. The inode file, like any
- * other file, is managed with a inode. The inode file's inode, aka master
- * inode, requires special handling in several respects. First, it cannot be
- * written to the inode file, so it is stored in the journal instead.
- *
- * Secondly, this inode cannot be written back and destroyed before all other
- * inodes have been written. The ordering is important. Linux' VFS is happily
- * unaware of the ordering constraint and would ordinarily destroy the master
- * inode at umount time while other inodes are still in use and dirty. Not
- * good.
- *
- * So logfs makes sure the master inode is not written until all other inodes
- * have been destroyed. Sadly, this method has another side-effect. The VFS
- * will notice one remaining inode and print a frightening warning message.
- * Worse, it is impossible to judge whether such a warning was caused by the
- * master inode or any other inodes have leaked as well.
- *
- * Our attempt of solving this is with logfs_new_meta_inode() below. Its
- * purpose is to create a new inode that will not trigger the warning if such
- * an inode is still in use. An ugly hack, no doubt. Suggections for
- * improvement are welcome.
- *
- * AV: that's what ->put_super() is for...
- */
-struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino)
-{
- struct inode *inode;
-
- inode = new_inode(sb);
- if (!inode)
- return ERR_PTR(-ENOMEM);
-
- inode->i_mode = S_IFREG;
- inode->i_ino = ino;
- inode->i_data.a_ops = &logfs_reg_aops;
- mapping_set_gfp_mask(&inode->i_data, GFP_NOFS);
-
- return inode;
-}
-
-struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino)
-{
- struct inode *inode;
- int err;
-
- inode = logfs_new_meta_inode(sb, ino);
- if (IS_ERR(inode))
- return inode;
-
- err = logfs_read_inode(inode);
- if (err) {
- iput(inode);
- return ERR_PTR(err);
- }
- logfs_inode_setops(inode);
- return inode;
-}
-
-static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc)
-{
- int ret;
- long flags = WF_LOCK;
-
- /* Can only happen if creat() failed. Safe to skip. */
- if (logfs_inode(inode)->li_flags & LOGFS_IF_STILLBORN)
- return 0;
-
- ret = __logfs_write_inode(inode, NULL, flags);
- LOGFS_BUG_ON(ret, inode->i_sb);
- return ret;
-}
-
-/* called with inode->i_lock held */
-static int logfs_drop_inode(struct inode *inode)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- struct logfs_inode *li = logfs_inode(inode);
-
- spin_lock(&logfs_inode_lock);
- list_move(&li->li_freeing_list, &super->s_freeing_list);
- spin_unlock(&logfs_inode_lock);
- return generic_drop_inode(inode);
-}
-
-static void logfs_set_ino_generation(struct super_block *sb,
- struct inode *inode)
-{
- struct logfs_super *super = logfs_super(sb);
- u64 ino;
-
- mutex_lock(&super->s_journal_mutex);
- ino = logfs_seek_hole(super->s_master_inode, super->s_last_ino + 1);
- super->s_last_ino = ino;
- super->s_inos_till_wrap--;
- if (super->s_inos_till_wrap < 0) {
- super->s_last_ino = LOGFS_RESERVED_INOS;
- super->s_generation++;
- super->s_inos_till_wrap = INOS_PER_WRAP;
- }
- inode->i_ino = ino;
- inode->i_generation = super->s_generation;
- mutex_unlock(&super->s_journal_mutex);
-}
-
-struct inode *logfs_new_inode(struct inode *dir, umode_t mode)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *inode;
-
- inode = new_inode(sb);
- if (!inode)
- return ERR_PTR(-ENOMEM);
-
- logfs_init_inode(sb, inode);
-
- /* inherit parent flags */
- logfs_inode(inode)->li_flags |=
- logfs_inode(dir)->li_flags & LOGFS_FL_INHERITED;
-
- inode->i_mode = mode;
- logfs_set_ino_generation(sb, inode);
-
- inode_init_owner(inode, dir, mode);
- logfs_inode_setops(inode);
- insert_inode_hash(inode);
-
- return inode;
-}
-
-static void logfs_init_once(void *_li)
-{
- struct logfs_inode *li = _li;
- int i;
-
- li->li_flags = 0;
- li->li_used_bytes = 0;
- li->li_refcount = 1;
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- li->li_data[i] = 0;
- inode_init_once(&li->vfs_inode);
-}
-
-static int logfs_sync_fs(struct super_block *sb, int wait)
-{
- logfs_get_wblocks(sb, NULL, WF_LOCK);
- logfs_write_anchor(sb);
- logfs_put_wblocks(sb, NULL, WF_LOCK);
- return 0;
-}
-
-static void logfs_put_super(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- /* kill the meta-inodes */
- iput(super->s_segfile_inode);
- iput(super->s_master_inode);
- iput(super->s_mapping_inode);
-}
-
-const struct super_operations logfs_super_operations = {
- .alloc_inode = logfs_alloc_inode,
- .destroy_inode = logfs_destroy_inode,
- .evict_inode = logfs_evict_inode,
- .drop_inode = logfs_drop_inode,
- .put_super = logfs_put_super,
- .write_inode = logfs_write_inode,
- .statfs = logfs_statfs,
- .sync_fs = logfs_sync_fs,
-};
-
-int logfs_init_inode_cache(void)
-{
- logfs_inode_cache = kmem_cache_create("logfs_inode_cache",
- sizeof(struct logfs_inode), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
- logfs_init_once);
- if (!logfs_inode_cache)
- return -ENOMEM;
- return 0;
-}
-
-void logfs_destroy_inode_cache(void)
-{
- /*
- * Make sure all delayed rcu free inodes are flushed before we
- * destroy cache.
- */
- rcu_barrier();
- kmem_cache_destroy(logfs_inode_cache);
-}
+++ /dev/null
-/*
- * fs/logfs/journal.c - journal handling code
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- */
-#include "logfs.h"
-#include <linux/slab.h>
-
-static void logfs_calc_free(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- u64 reserve, no_segs = super->s_no_segs;
- s64 free;
- int i;
-
- /* superblock segments */
- no_segs -= 2;
- super->s_no_journal_segs = 0;
- /* journal */
- journal_for_each(i)
- if (super->s_journal_seg[i]) {
- no_segs--;
- super->s_no_journal_segs++;
- }
-
- /* open segments plus one extra per level for GC */
- no_segs -= 2 * super->s_total_levels;
-
- free = no_segs * (super->s_segsize - LOGFS_SEGMENT_RESERVE);
- free -= super->s_used_bytes;
- /* just a bit extra */
- free -= super->s_total_levels * 4096;
-
- /* Bad blocks are 'paid' for with speed reserve - the filesystem
- * simply gets slower as bad blocks accumulate. Until the bad blocks
- * exceed the speed reserve - then the filesystem gets smaller.
- */
- reserve = super->s_bad_segments + super->s_bad_seg_reserve;
- reserve *= super->s_segsize - LOGFS_SEGMENT_RESERVE;
- reserve = max(reserve, super->s_speed_reserve);
- free -= reserve;
- if (free < 0)
- free = 0;
-
- super->s_free_bytes = free;
-}
-
-static void reserve_sb_and_journal(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct btree_head32 *head = &super->s_reserved_segments;
- int i, err;
-
- err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[0]), (void *)1,
- GFP_KERNEL);
- BUG_ON(err);
-
- err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[1]), (void *)1,
- GFP_KERNEL);
- BUG_ON(err);
-
- journal_for_each(i) {
- if (!super->s_journal_seg[i])
- continue;
- err = btree_insert32(head, super->s_journal_seg[i], (void *)1,
- GFP_KERNEL);
- BUG_ON(err);
- }
-}
-
-static void read_dynsb(struct super_block *sb,
- struct logfs_je_dynsb *dynsb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- super->s_gec = be64_to_cpu(dynsb->ds_gec);
- super->s_sweeper = be64_to_cpu(dynsb->ds_sweeper);
- super->s_victim_ino = be64_to_cpu(dynsb->ds_victim_ino);
- super->s_rename_dir = be64_to_cpu(dynsb->ds_rename_dir);
- super->s_rename_pos = be64_to_cpu(dynsb->ds_rename_pos);
- super->s_used_bytes = be64_to_cpu(dynsb->ds_used_bytes);
- super->s_generation = be32_to_cpu(dynsb->ds_generation);
-}
-
-static void read_anchor(struct super_block *sb,
- struct logfs_je_anchor *da)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode = super->s_master_inode;
- struct logfs_inode *li = logfs_inode(inode);
- int i;
-
- super->s_last_ino = be64_to_cpu(da->da_last_ino);
- li->li_flags = 0;
- li->li_height = da->da_height;
- i_size_write(inode, be64_to_cpu(da->da_size));
- li->li_used_bytes = be64_to_cpu(da->da_used_bytes);
-
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- li->li_data[i] = be64_to_cpu(da->da_data[i]);
-}
-
-static void read_erasecount(struct super_block *sb,
- struct logfs_je_journal_ec *ec)
-{
- struct logfs_super *super = logfs_super(sb);
- int i;
-
- journal_for_each(i)
- super->s_journal_ec[i] = be32_to_cpu(ec->ec[i]);
-}
-
-static int read_area(struct super_block *sb, struct logfs_je_area *a)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_area[a->gc_level];
- u64 ofs;
- u32 writemask = ~(super->s_writesize - 1);
-
- if (a->gc_level >= LOGFS_NO_AREAS)
- return -EIO;
- if (a->vim != VIM_DEFAULT)
- return -EIO; /* TODO: close area and continue */
-
- area->a_used_bytes = be32_to_cpu(a->used_bytes);
- area->a_written_bytes = area->a_used_bytes & writemask;
- area->a_segno = be32_to_cpu(a->segno);
- if (area->a_segno)
- area->a_is_open = 1;
-
- ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
- if (super->s_writesize > 1)
- return logfs_buf_recover(area, ofs, a + 1, super->s_writesize);
- else
- return logfs_buf_recover(area, ofs, NULL, 0);
-}
-
-static void *unpack(void *from, void *to)
-{
- struct logfs_journal_header *jh = from;
- void *data = from + sizeof(struct logfs_journal_header);
- int err;
- size_t inlen, outlen;
-
- inlen = be16_to_cpu(jh->h_len);
- outlen = be16_to_cpu(jh->h_datalen);
-
- if (jh->h_compr == COMPR_NONE)
- memcpy(to, data, inlen);
- else {
- err = logfs_uncompress(data, to, inlen, outlen);
- BUG_ON(err);
- }
- return to;
-}
-
-static int __read_je_header(struct super_block *sb, u64 ofs,
- struct logfs_journal_header *jh)
-{
- struct logfs_super *super = logfs_super(sb);
- size_t bufsize = max_t(size_t, sb->s_blocksize, super->s_writesize)
- + MAX_JOURNAL_HEADER;
- u16 type, len, datalen;
- int err;
-
- /* read header only */
- err = wbuf_read(sb, ofs, sizeof(*jh), jh);
- if (err)
- return err;
- type = be16_to_cpu(jh->h_type);
- len = be16_to_cpu(jh->h_len);
- datalen = be16_to_cpu(jh->h_datalen);
- if (len > sb->s_blocksize)
- return -EIO;
- if ((type < JE_FIRST) || (type > JE_LAST))
- return -EIO;
- if (datalen > bufsize)
- return -EIO;
- return 0;
-}
-
-static int __read_je_payload(struct super_block *sb, u64 ofs,
- struct logfs_journal_header *jh)
-{
- u16 len;
- int err;
-
- len = be16_to_cpu(jh->h_len);
- err = wbuf_read(sb, ofs + sizeof(*jh), len, jh + 1);
- if (err)
- return err;
- if (jh->h_crc != logfs_crc32(jh, len + sizeof(*jh), 4)) {
- /* Old code was confused. It forgot about the header length
- * and stopped calculating the crc 16 bytes before the end
- * of data - ick!
- * FIXME: Remove this hack once the old code is fixed.
- */
- if (jh->h_crc == logfs_crc32(jh, len, 4))
- WARN_ON_ONCE(1);
- else
- return -EIO;
- }
- return 0;
-}
-
-/*
- * jh needs to be large enough to hold the complete entry, not just the header
- */
-static int __read_je(struct super_block *sb, u64 ofs,
- struct logfs_journal_header *jh)
-{
- int err;
-
- err = __read_je_header(sb, ofs, jh);
- if (err)
- return err;
- return __read_je_payload(sb, ofs, jh);
-}
-
-static int read_je(struct super_block *sb, u64 ofs)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_journal_header *jh = super->s_compressed_je;
- void *scratch = super->s_je;
- u16 type, datalen;
- int err;
-
- err = __read_je(sb, ofs, jh);
- if (err)
- return err;
- type = be16_to_cpu(jh->h_type);
- datalen = be16_to_cpu(jh->h_datalen);
-
- switch (type) {
- case JE_DYNSB:
- read_dynsb(sb, unpack(jh, scratch));
- break;
- case JE_ANCHOR:
- read_anchor(sb, unpack(jh, scratch));
- break;
- case JE_ERASECOUNT:
- read_erasecount(sb, unpack(jh, scratch));
- break;
- case JE_AREA:
- err = read_area(sb, unpack(jh, scratch));
- break;
- case JE_OBJ_ALIAS:
- err = logfs_load_object_aliases(sb, unpack(jh, scratch),
- datalen);
- break;
- default:
- WARN_ON_ONCE(1);
- return -EIO;
- }
- return err;
-}
-
-static int logfs_read_segment(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_journal_header *jh = super->s_compressed_je;
- u64 ofs, seg_ofs = dev_ofs(sb, segno, 0);
- u32 h_ofs, last_ofs = 0;
- u16 len, datalen, last_len = 0;
- int i, err;
-
- /* search for most recent commit */
- for (h_ofs = 0; h_ofs < super->s_segsize; h_ofs += sizeof(*jh)) {
- ofs = seg_ofs + h_ofs;
- err = __read_je_header(sb, ofs, jh);
- if (err)
- continue;
- if (jh->h_type != cpu_to_be16(JE_COMMIT))
- continue;
- err = __read_je_payload(sb, ofs, jh);
- if (err)
- continue;
- len = be16_to_cpu(jh->h_len);
- datalen = be16_to_cpu(jh->h_datalen);
- if ((datalen > sizeof(super->s_je_array)) ||
- (datalen % sizeof(__be64)))
- continue;
- last_ofs = h_ofs;
- last_len = datalen;
- h_ofs += ALIGN(len, sizeof(*jh)) - sizeof(*jh);
- }
- /* read commit */
- if (last_ofs == 0)
- return -ENOENT;
- ofs = seg_ofs + last_ofs;
- log_journal("Read commit from %llx\n", ofs);
- err = __read_je(sb, ofs, jh);
- BUG_ON(err); /* We should have caught it in the scan loop already */
- if (err)
- return err;
- /* uncompress */
- unpack(jh, super->s_je_array);
- super->s_no_je = last_len / sizeof(__be64);
- /* iterate over array */
- for (i = 0; i < super->s_no_je; i++) {
- err = read_je(sb, be64_to_cpu(super->s_je_array[i]));
- if (err)
- return err;
- }
- super->s_journal_area->a_segno = segno;
- return 0;
-}
-
-static u64 read_gec(struct super_block *sb, u32 segno)
-{
- struct logfs_segment_header sh;
- __be32 crc;
- int err;
-
- if (!segno)
- return 0;
- err = wbuf_read(sb, dev_ofs(sb, segno, 0), sizeof(sh), &sh);
- if (err)
- return 0;
- crc = logfs_crc32(&sh, sizeof(sh), 4);
- if (crc != sh.crc) {
- WARN_ON(sh.gec != cpu_to_be64(0xffffffffffffffffull));
- /* Most likely it was just erased */
- return 0;
- }
- return be64_to_cpu(sh.gec);
-}
-
-static int logfs_read_journal(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- u64 gec[LOGFS_JOURNAL_SEGS], max;
- u32 segno;
- int i, max_i;
-
- max = 0;
- max_i = -1;
- journal_for_each(i) {
- segno = super->s_journal_seg[i];
- gec[i] = read_gec(sb, super->s_journal_seg[i]);
- if (gec[i] > max) {
- max = gec[i];
- max_i = i;
- }
- }
- if (max_i == -1)
- return -EIO;
- /* FIXME: Try older segments in case of error */
- return logfs_read_segment(sb, super->s_journal_seg[max_i]);
-}
-
-/*
- * First search the current segment (outer loop), then pick the next segment
- * in the array, skipping any zero entries (inner loop).
- */
-static void journal_get_free_segment(struct logfs_area *area)
-{
- struct logfs_super *super = logfs_super(area->a_sb);
- int i;
-
- journal_for_each(i) {
- if (area->a_segno != super->s_journal_seg[i])
- continue;
-
- do {
- i++;
- if (i == LOGFS_JOURNAL_SEGS)
- i = 0;
- } while (!super->s_journal_seg[i]);
-
- area->a_segno = super->s_journal_seg[i];
- area->a_erase_count = ++(super->s_journal_ec[i]);
- log_journal("Journal now at %x (ec %x)\n", area->a_segno,
- area->a_erase_count);
- return;
- }
- BUG();
-}
-
-static void journal_get_erase_count(struct logfs_area *area)
-{
- /* erase count is stored globally and incremented in
- * journal_get_free_segment() - nothing to do here */
-}
-
-static int journal_erase_segment(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- union {
- struct logfs_segment_header sh;
- unsigned char c[ALIGN(sizeof(struct logfs_segment_header), 16)];
- } u;
- u64 ofs;
- int err;
-
- err = logfs_erase_segment(sb, area->a_segno, 1);
- if (err)
- return err;
-
- memset(&u, 0, sizeof(u));
- u.sh.pad = 0;
- u.sh.type = SEG_JOURNAL;
- u.sh.level = 0;
- u.sh.segno = cpu_to_be32(area->a_segno);
- u.sh.ec = cpu_to_be32(area->a_erase_count);
- u.sh.gec = cpu_to_be64(logfs_super(sb)->s_gec);
- u.sh.crc = logfs_crc32(&u.sh, sizeof(u.sh), 4);
-
- /* This causes a bug in segment.c. Not yet. */
- //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0);
-
- ofs = dev_ofs(sb, area->a_segno, 0);
- area->a_used_bytes = sizeof(u);
- logfs_buf_write(area, ofs, &u, sizeof(u));
- return 0;
-}
-
-static size_t __logfs_write_header(struct logfs_super *super,
- struct logfs_journal_header *jh, size_t len, size_t datalen,
- u16 type, u8 compr)
-{
- jh->h_len = cpu_to_be16(len);
- jh->h_type = cpu_to_be16(type);
- jh->h_datalen = cpu_to_be16(datalen);
- jh->h_compr = compr;
- jh->h_pad[0] = 'H';
- jh->h_pad[1] = 'E';
- jh->h_pad[2] = 'A';
- jh->h_pad[3] = 'D';
- jh->h_pad[4] = 'R';
- jh->h_crc = logfs_crc32(jh, len + sizeof(*jh), 4);
- return ALIGN(len, 16) + sizeof(*jh);
-}
-
-static size_t logfs_write_header(struct logfs_super *super,
- struct logfs_journal_header *jh, size_t datalen, u16 type)
-{
- size_t len = datalen;
-
- return __logfs_write_header(super, jh, len, datalen, type, COMPR_NONE);
-}
-
-static inline size_t logfs_journal_erasecount_size(struct logfs_super *super)
-{
- return LOGFS_JOURNAL_SEGS * sizeof(__be32);
-}
-
-static void *logfs_write_erasecount(struct super_block *sb, void *_ec,
- u16 *type, size_t *len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_je_journal_ec *ec = _ec;
- int i;
-
- journal_for_each(i)
- ec->ec[i] = cpu_to_be32(super->s_journal_ec[i]);
- *type = JE_ERASECOUNT;
- *len = logfs_journal_erasecount_size(super);
- return ec;
-}
-
-static void account_shadow(void *_shadow, unsigned long _sb, u64 ignore,
- size_t ignore2)
-{
- struct logfs_shadow *shadow = _shadow;
- struct super_block *sb = (void *)_sb;
- struct logfs_super *super = logfs_super(sb);
-
- /* consume new space */
- super->s_free_bytes -= shadow->new_len;
- super->s_used_bytes += shadow->new_len;
- super->s_dirty_used_bytes -= shadow->new_len;
-
- /* free up old space */
- super->s_free_bytes += shadow->old_len;
- super->s_used_bytes -= shadow->old_len;
- super->s_dirty_free_bytes -= shadow->old_len;
-
- logfs_set_segment_used(sb, shadow->old_ofs, -shadow->old_len);
- logfs_set_segment_used(sb, shadow->new_ofs, shadow->new_len);
-
- log_journal("account_shadow(%llx, %llx, %x) %llx->%llx %x->%x\n",
- shadow->ino, shadow->bix, shadow->gc_level,
- shadow->old_ofs, shadow->new_ofs,
- shadow->old_len, shadow->new_len);
- mempool_free(shadow, super->s_shadow_pool);
-}
-
-static void account_shadows(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode = super->s_master_inode;
- struct logfs_inode *li = logfs_inode(inode);
- struct shadow_tree *tree = &super->s_shadow_tree;
-
- btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow);
- btree_grim_visitor64(&tree->old, (unsigned long)sb, account_shadow);
- btree_grim_visitor32(&tree->segment_map, 0, NULL);
- tree->no_shadowed_segments = 0;
-
- if (li->li_block) {
- /*
- * We never actually use the structure, when attached to the
- * master inode. But it is easier to always free it here than
- * to have checks in several places elsewhere when allocating
- * it.
- */
- li->li_block->ops->free_block(sb, li->li_block);
- }
- BUG_ON((s64)li->li_used_bytes < 0);
-}
-
-static void *__logfs_write_anchor(struct super_block *sb, void *_da,
- u16 *type, size_t *len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_je_anchor *da = _da;
- struct inode *inode = super->s_master_inode;
- struct logfs_inode *li = logfs_inode(inode);
- int i;
-
- da->da_height = li->li_height;
- da->da_last_ino = cpu_to_be64(super->s_last_ino);
- da->da_size = cpu_to_be64(i_size_read(inode));
- da->da_used_bytes = cpu_to_be64(li->li_used_bytes);
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- da->da_data[i] = cpu_to_be64(li->li_data[i]);
- *type = JE_ANCHOR;
- *len = sizeof(*da);
- return da;
-}
-
-static void *logfs_write_dynsb(struct super_block *sb, void *_dynsb,
- u16 *type, size_t *len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_je_dynsb *dynsb = _dynsb;
-
- dynsb->ds_gec = cpu_to_be64(super->s_gec);
- dynsb->ds_sweeper = cpu_to_be64(super->s_sweeper);
- dynsb->ds_victim_ino = cpu_to_be64(super->s_victim_ino);
- dynsb->ds_rename_dir = cpu_to_be64(super->s_rename_dir);
- dynsb->ds_rename_pos = cpu_to_be64(super->s_rename_pos);
- dynsb->ds_used_bytes = cpu_to_be64(super->s_used_bytes);
- dynsb->ds_generation = cpu_to_be32(super->s_generation);
- *type = JE_DYNSB;
- *len = sizeof(*dynsb);
- return dynsb;
-}
-
-static void write_wbuf(struct super_block *sb, struct logfs_area *area,
- void *wbuf)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- u64 ofs;
- pgoff_t index;
- int page_ofs;
- struct page *page;
-
- ofs = dev_ofs(sb, area->a_segno,
- area->a_used_bytes & ~(super->s_writesize - 1));
- index = ofs >> PAGE_SHIFT;
- page_ofs = ofs & (PAGE_SIZE - 1);
-
- page = find_or_create_page(mapping, index, GFP_NOFS);
- BUG_ON(!page);
- memcpy(wbuf, page_address(page) + page_ofs, super->s_writesize);
- unlock_page(page);
-}
-
-static void *logfs_write_area(struct super_block *sb, void *_a,
- u16 *type, size_t *len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_area[super->s_sum_index];
- struct logfs_je_area *a = _a;
-
- a->vim = VIM_DEFAULT;
- a->gc_level = super->s_sum_index;
- a->used_bytes = cpu_to_be32(area->a_used_bytes);
- a->segno = cpu_to_be32(area->a_segno);
- if (super->s_writesize > 1)
- write_wbuf(sb, area, a + 1);
-
- *type = JE_AREA;
- *len = sizeof(*a) + super->s_writesize;
- return a;
-}
-
-static void *logfs_write_commit(struct super_block *sb, void *h,
- u16 *type, size_t *len)
-{
- struct logfs_super *super = logfs_super(sb);
-
- *type = JE_COMMIT;
- *len = super->s_no_je * sizeof(__be64);
- return super->s_je_array;
-}
-
-static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type,
- size_t len)
-{
- struct logfs_super *super = logfs_super(sb);
- void *header = super->s_compressed_je;
- void *data = header + sizeof(struct logfs_journal_header);
- ssize_t compr_len, pad_len;
- u8 compr = COMPR_ZLIB;
-
- if (len == 0)
- return logfs_write_header(super, header, 0, type);
-
- compr_len = logfs_compress(buf, data, len, sb->s_blocksize);
- if (compr_len < 0 || type == JE_ANCHOR) {
- memcpy(data, buf, len);
- compr_len = len;
- compr = COMPR_NONE;
- }
-
- pad_len = ALIGN(compr_len, 16);
- memset(data + compr_len, 0, pad_len - compr_len);
-
- return __logfs_write_header(super, header, compr_len, len, type, compr);
-}
-
-static s64 logfs_get_free_bytes(struct logfs_area *area, size_t *bytes,
- int must_pad)
-{
- u32 writesize = logfs_super(area->a_sb)->s_writesize;
- s32 ofs;
- int ret;
-
- ret = logfs_open_area(area, *bytes);
- if (ret)
- return -EAGAIN;
-
- ofs = area->a_used_bytes;
- area->a_used_bytes += *bytes;
-
- if (must_pad) {
- area->a_used_bytes = ALIGN(area->a_used_bytes, writesize);
- *bytes = area->a_used_bytes - ofs;
- }
-
- return dev_ofs(area->a_sb, area->a_segno, ofs);
-}
-
-static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type,
- size_t buf_len)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_journal_area;
- struct logfs_journal_header *jh = super->s_compressed_je;
- size_t len;
- int must_pad = 0;
- s64 ofs;
-
- len = __logfs_write_je(sb, buf, type, buf_len);
- if (jh->h_type == cpu_to_be16(JE_COMMIT))
- must_pad = 1;
-
- ofs = logfs_get_free_bytes(area, &len, must_pad);
- if (ofs < 0)
- return ofs;
- logfs_buf_write(area, ofs, super->s_compressed_je, len);
- BUG_ON(super->s_no_je >= MAX_JOURNAL_ENTRIES);
- super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs);
- return 0;
-}
-
-static int logfs_write_je(struct super_block *sb,
- void* (*write)(struct super_block *sb, void *scratch,
- u16 *type, size_t *len))
-{
- void *buf;
- size_t len;
- u16 type;
-
- buf = write(sb, logfs_super(sb)->s_je, &type, &len);
- return logfs_write_je_buf(sb, buf, type, len);
-}
-
-int write_alias_journal(struct super_block *sb, u64 ino, u64 bix,
- level_t level, int child_no, __be64 val)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_obj_alias *oa = super->s_je;
- int err = 0, fill = super->s_je_fill;
-
- log_aliases("logfs_write_obj_aliases #%x(%llx, %llx, %x, %x) %llx\n",
- fill, ino, bix, level, child_no, be64_to_cpu(val));
- oa[fill].ino = cpu_to_be64(ino);
- oa[fill].bix = cpu_to_be64(bix);
- oa[fill].val = val;
- oa[fill].level = (__force u8)level;
- oa[fill].child_no = cpu_to_be16(child_no);
- fill++;
- if (fill >= sb->s_blocksize / sizeof(*oa)) {
- err = logfs_write_je_buf(sb, oa, JE_OBJ_ALIAS, sb->s_blocksize);
- fill = 0;
- }
-
- super->s_je_fill = fill;
- return err;
-}
-
-static int logfs_write_obj_aliases(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int err;
-
- log_journal("logfs_write_obj_aliases: %d aliases to write\n",
- super->s_no_object_aliases);
- super->s_je_fill = 0;
- err = logfs_write_obj_aliases_pagecache(sb);
- if (err)
- return err;
-
- if (super->s_je_fill)
- err = logfs_write_je_buf(sb, super->s_je, JE_OBJ_ALIAS,
- super->s_je_fill
- * sizeof(struct logfs_obj_alias));
- return err;
-}
-
-/*
- * Write all journal entries. The goto logic ensures that all journal entries
- * are written whenever a new segment is used. It is ugly and potentially a
- * bit wasteful, but robustness is more important. With this we can *always*
- * erase all journal segments except the one containing the most recent commit.
- */
-void logfs_write_anchor(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_journal_area;
- int i, err;
-
- if (!(super->s_flags & LOGFS_SB_FLAG_DIRTY))
- return;
- super->s_flags &= ~LOGFS_SB_FLAG_DIRTY;
-
- BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
- mutex_lock(&super->s_journal_mutex);
-
- /* Do this first or suffer corruption */
- logfs_sync_segments(sb);
- account_shadows(sb);
-
-again:
- super->s_no_je = 0;
- for_each_area(i) {
- if (!super->s_area[i]->a_is_open)
- continue;
- super->s_sum_index = i;
- err = logfs_write_je(sb, logfs_write_area);
- if (err)
- goto again;
- }
- err = logfs_write_obj_aliases(sb);
- if (err)
- goto again;
- err = logfs_write_je(sb, logfs_write_erasecount);
- if (err)
- goto again;
- err = logfs_write_je(sb, __logfs_write_anchor);
- if (err)
- goto again;
- err = logfs_write_je(sb, logfs_write_dynsb);
- if (err)
- goto again;
- /*
- * Order is imperative. First we sync all writes, including the
- * non-committed journal writes. Then we write the final commit and
- * sync the current journal segment.
- * There is a theoretical bug here. Syncing the journal segment will
- * write a number of journal entries and the final commit. All these
- * are written in a single operation. If the device layer writes the
- * data back-to-front, the commit will precede the other journal
- * entries, leaving a race window.
- * Two fixes are possible. Preferred is to fix the device layer to
- * ensure writes happen front-to-back. Alternatively we can insert
- * another logfs_sync_area() super->s_devops->sync() combo before
- * writing the commit.
- */
- /*
- * On another subject, super->s_devops->sync is usually not necessary.
- * Unless called from sys_sync or friends, a barrier would suffice.
- */
- super->s_devops->sync(sb);
- err = logfs_write_je(sb, logfs_write_commit);
- if (err)
- goto again;
- log_journal("Write commit to %llx\n",
- be64_to_cpu(super->s_je_array[super->s_no_je - 1]));
- logfs_sync_area(area);
- BUG_ON(area->a_used_bytes != area->a_written_bytes);
- super->s_devops->sync(sb);
-
- mutex_unlock(&super->s_journal_mutex);
- return;
-}
-
-void do_logfs_journal_wl_pass(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_area *area = super->s_journal_area;
- struct btree_head32 *head = &super->s_reserved_segments;
- u32 segno, ec;
- int i, err;
-
- log_journal("Journal requires wear-leveling.\n");
- /* Drop old segments */
- journal_for_each(i)
- if (super->s_journal_seg[i]) {
- btree_remove32(head, super->s_journal_seg[i]);
- logfs_set_segment_unreserved(sb,
- super->s_journal_seg[i],
- super->s_journal_ec[i]);
- super->s_journal_seg[i] = 0;
- super->s_journal_ec[i] = 0;
- }
- /* Get new segments */
- for (i = 0; i < super->s_no_journal_segs; i++) {
- segno = get_best_cand(sb, &super->s_reserve_list, &ec);
- super->s_journal_seg[i] = segno;
- super->s_journal_ec[i] = ec;
- logfs_set_segment_reserved(sb, segno);
- err = btree_insert32(head, segno, (void *)1, GFP_NOFS);
- BUG_ON(err); /* mempool should prevent this */
- err = logfs_erase_segment(sb, segno, 1);
- BUG_ON(err); /* FIXME: remount-ro would be nicer */
- }
- /* Manually move journal_area */
- freeseg(sb, area->a_segno);
- area->a_segno = super->s_journal_seg[0];
- area->a_is_open = 0;
- area->a_used_bytes = 0;
- /* Write journal */
- logfs_write_anchor(sb);
- /* Write superblocks */
- err = logfs_write_sb(sb);
- BUG_ON(err);
-}
-
-static const struct logfs_area_ops journal_area_ops = {
- .get_free_segment = journal_get_free_segment,
- .get_erase_count = journal_get_erase_count,
- .erase_segment = journal_erase_segment,
-};
-
-int logfs_init_journal(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- size_t bufsize = max_t(size_t, sb->s_blocksize, super->s_writesize)
- + MAX_JOURNAL_HEADER;
- int ret = -ENOMEM;
-
- mutex_init(&super->s_journal_mutex);
- btree_init_mempool32(&super->s_reserved_segments, super->s_btree_pool);
-
- super->s_je = kzalloc(bufsize, GFP_KERNEL);
- if (!super->s_je)
- return ret;
-
- super->s_compressed_je = kzalloc(bufsize, GFP_KERNEL);
- if (!super->s_compressed_je)
- return ret;
-
- super->s_master_inode = logfs_new_meta_inode(sb, LOGFS_INO_MASTER);
- if (IS_ERR(super->s_master_inode))
- return PTR_ERR(super->s_master_inode);
-
- ret = logfs_read_journal(sb);
- if (ret)
- return -EIO;
-
- reserve_sb_and_journal(sb);
- logfs_calc_free(sb);
-
- super->s_journal_area->a_ops = &journal_area_ops;
- return 0;
-}
-
-void logfs_cleanup_journal(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- btree_grim_visitor32(&super->s_reserved_segments, 0, NULL);
-
- kfree(super->s_compressed_je);
- kfree(super->s_je);
-}
+++ /dev/null
-/*
- * fs/logfs/logfs.h
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- *
- * Private header for logfs.
- */
-#ifndef FS_LOGFS_LOGFS_H
-#define FS_LOGFS_LOGFS_H
-
-#undef __CHECK_ENDIAN__
-#define __CHECK_ENDIAN__
-
-#include <linux/btree.h>
-#include <linux/crc32.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/mempool.h>
-#include <linux/pagemap.h>
-#include <linux/mtd/mtd.h>
-#include "logfs_abi.h"
-
-#define LOGFS_DEBUG_SUPER (0x0001)
-#define LOGFS_DEBUG_SEGMENT (0x0002)
-#define LOGFS_DEBUG_JOURNAL (0x0004)
-#define LOGFS_DEBUG_DIR (0x0008)
-#define LOGFS_DEBUG_FILE (0x0010)
-#define LOGFS_DEBUG_INODE (0x0020)
-#define LOGFS_DEBUG_READWRITE (0x0040)
-#define LOGFS_DEBUG_GC (0x0080)
-#define LOGFS_DEBUG_GC_NOISY (0x0100)
-#define LOGFS_DEBUG_ALIASES (0x0200)
-#define LOGFS_DEBUG_BLOCKMOVE (0x0400)
-#define LOGFS_DEBUG_ALL (0xffffffff)
-
-#define LOGFS_DEBUG (0x01)
-/*
- * To enable specific log messages, simply define LOGFS_DEBUG to match any
- * or all of the above.
- */
-#ifndef LOGFS_DEBUG
-#define LOGFS_DEBUG (0)
-#endif
-
-#define log_cond(cond, fmt, arg...) do { \
- if (cond) \
- printk(KERN_DEBUG fmt, ##arg); \
-} while (0)
-
-#define log_super(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_SUPER, fmt, ##arg)
-#define log_segment(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_SEGMENT, fmt, ##arg)
-#define log_journal(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_JOURNAL, fmt, ##arg)
-#define log_dir(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_DIR, fmt, ##arg)
-#define log_file(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_FILE, fmt, ##arg)
-#define log_inode(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_INODE, fmt, ##arg)
-#define log_readwrite(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_READWRITE, fmt, ##arg)
-#define log_gc(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_GC, fmt, ##arg)
-#define log_gc_noisy(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_GC_NOISY, fmt, ##arg)
-#define log_aliases(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_ALIASES, fmt, ##arg)
-#define log_blockmove(fmt, arg...) \
- log_cond(LOGFS_DEBUG & LOGFS_DEBUG_BLOCKMOVE, fmt, ##arg)
-
-#define PG_pre_locked PG_owner_priv_1
-#define PagePreLocked(page) test_bit(PG_pre_locked, &(page)->flags)
-#define SetPagePreLocked(page) set_bit(PG_pre_locked, &(page)->flags)
-#define ClearPagePreLocked(page) clear_bit(PG_pre_locked, &(page)->flags)
-
-/* FIXME: This should really be somewhere in the 64bit area. */
-#define LOGFS_LINK_MAX (1<<30)
-
-/* Read-only filesystem */
-#define LOGFS_SB_FLAG_RO 0x0001
-#define LOGFS_SB_FLAG_DIRTY 0x0002
-#define LOGFS_SB_FLAG_OBJ_ALIAS 0x0004
-#define LOGFS_SB_FLAG_SHUTDOWN 0x0008
-
-/* Write Control Flags */
-#define WF_LOCK 0x01 /* take write lock */
-#define WF_WRITE 0x02 /* write block */
-#define WF_DELETE 0x04 /* delete old block */
-
-typedef u8 __bitwise level_t;
-typedef u8 __bitwise gc_level_t;
-
-#define LEVEL(level) ((__force level_t)(level))
-#define GC_LEVEL(gc_level) ((__force gc_level_t)(gc_level))
-
-#define SUBLEVEL(level) ( (void)((level) == LEVEL(1)), \
- (__force level_t)((__force u8)(level) - 1) )
-
-/**
- * struct logfs_area - area management information
- *
- * @a_sb: the superblock this area belongs to
- * @a_is_open: 1 if the area is currently open, else 0
- * @a_segno: segment number of area
- * @a_written_bytes: number of bytes already written back
- * @a_used_bytes: number of used bytes
- * @a_ops: area operations (either journal or ostore)
- * @a_erase_count: erase count
- * @a_level: GC level
- */
-struct logfs_area { /* a segment open for writing */
- struct super_block *a_sb;
- int a_is_open;
- u32 a_segno;
- u32 a_written_bytes;
- u32 a_used_bytes;
- const struct logfs_area_ops *a_ops;
- u32 a_erase_count;
- gc_level_t a_level;
-};
-
-/**
- * struct logfs_area_ops - area operations
- *
- * @get_free_segment: fill area->ofs with the offset of a free segment
- * @get_erase_count: fill area->erase_count (needs area->ofs)
- * @erase_segment: erase and setup segment
- */
-struct logfs_area_ops {
- void (*get_free_segment)(struct logfs_area *area);
- void (*get_erase_count)(struct logfs_area *area);
- int (*erase_segment)(struct logfs_area *area);
-};
-
-struct logfs_super; /* forward */
-/**
- * struct logfs_device_ops - device access operations
- *
- * @readpage: read one page (mm page)
- * @writeseg: write one segment. may be a partial segment
- * @erase: erase one segment
- * @read: read from the device
- * @erase: erase part of the device
- * @can_write_buf: decide whether wbuf can be written to ofs
- */
-struct logfs_device_ops {
- struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs);
- struct page *(*find_last_sb)(struct super_block *sb, u64 *ofs);
- 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 ensure_write);
- int (*can_write_buf)(struct super_block *sb, u64 ofs);
- void (*sync)(struct super_block *sb);
- void (*put_device)(struct logfs_super *s);
-};
-
-/**
- * struct candidate_list - list of similar candidates
- */
-struct candidate_list {
- struct rb_root rb_tree;
- int count;
- int maxcount;
- int sort_by_ec;
-};
-
-/**
- * struct gc_candidate - "candidate" segment to be garbage collected next
- *
- * @list: list (either free of low)
- * @segno: segment number
- * @valid: number of valid bytes
- * @erase_count: erase count of segment
- * @dist: distance from tree root
- *
- * Candidates can be on two lists. The free list contains electees rather
- * than candidates - segments that no longer contain any valid data. The
- * low list contains candidates to be picked for GC. It should be kept
- * short. It is not required to always pick a perfect candidate. In the
- * worst case GC will have to move more data than absolutely necessary.
- */
-struct gc_candidate {
- struct rb_node rb_node;
- struct candidate_list *list;
- u32 segno;
- u32 valid;
- u32 erase_count;
- u8 dist;
-};
-
-/**
- * struct logfs_journal_entry - temporary structure used during journal scan
- *
- * @used:
- * @version: normalized version
- * @len: length
- * @offset: offset
- */
-struct logfs_journal_entry {
- int used;
- s16 version;
- u16 len;
- u16 datalen;
- u64 offset;
-};
-
-enum transaction_state {
- CREATE_1 = 1,
- CREATE_2,
- UNLINK_1,
- UNLINK_2,
- CROSS_RENAME_1,
- CROSS_RENAME_2,
- TARGET_RENAME_1,
- TARGET_RENAME_2,
- TARGET_RENAME_3
-};
-
-/**
- * struct logfs_transaction - essential fields to support atomic dirops
- *
- * @ino: target inode
- * @dir: inode of directory containing dentry
- * @pos: pos of dentry in directory
- */
-struct logfs_transaction {
- enum transaction_state state;
- u64 ino;
- u64 dir;
- u64 pos;
-};
-
-/**
- * struct logfs_shadow - old block in the shadow of a not-yet-committed new one
- * @old_ofs: offset of old block on medium
- * @new_ofs: offset of new block on medium
- * @ino: inode number
- * @bix: block index
- * @old_len: size of old block, including header
- * @new_len: size of new block, including header
- * @level: block level
- */
-struct logfs_shadow {
- u64 old_ofs;
- u64 new_ofs;
- u64 ino;
- u64 bix;
- int old_len;
- int new_len;
- gc_level_t gc_level;
-};
-
-/**
- * struct shadow_tree
- * @new: shadows where old_ofs==0, indexed by new_ofs
- * @old: shadows where old_ofs!=0, indexed by old_ofs
- * @segment_map: bitfield of segments containing shadows
- * @no_shadowed_segment: number of segments containing shadows
- */
-struct shadow_tree {
- struct btree_head64 new;
- struct btree_head64 old;
- struct btree_head32 segment_map;
- int no_shadowed_segments;
-};
-
-struct object_alias_item {
- struct list_head list;
- __be64 val;
- int child_no;
-};
-
-/**
- * struct logfs_block - contains any block state
- * @type: indirect block or inode
- * @full: number of fully populated children
- * @partial: number of partially populated children
- *
- * Most blocks are directly represented by page cache pages. But when a block
- * becomes dirty, is part of a transaction, contains aliases or is otherwise
- * special, a struct logfs_block is allocated to track the additional state.
- * Inodes are very similar to indirect blocks, so they can also get one of
- * these structures added when appropriate.
- */
-#define BLOCK_INDIRECT 1 /* Indirect block */
-#define BLOCK_INODE 2 /* Inode */
-struct logfs_block_ops;
-struct logfs_block {
- struct list_head alias_list;
- struct list_head item_list;
- struct super_block *sb;
- u64 ino;
- u64 bix;
- level_t level;
- struct page *page;
- struct inode *inode;
- struct logfs_transaction *ta;
- unsigned long alias_map[LOGFS_BLOCK_FACTOR / BITS_PER_LONG];
- const struct logfs_block_ops *ops;
- int full;
- int partial;
- int reserved_bytes;
-};
-
-typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix,
- level_t level, int child_no, __be64 val);
-struct logfs_block_ops {
- void (*write_block)(struct logfs_block *block);
- void (*free_block)(struct super_block *sb, struct logfs_block*block);
- int (*write_alias)(struct super_block *sb,
- struct logfs_block *block,
- write_alias_t *write_one_alias);
-};
-
-#define MAX_JOURNAL_ENTRIES 256
-
-struct logfs_super {
- struct mtd_info *s_mtd; /* underlying device */
- struct block_device *s_bdev; /* underlying device */
- const struct logfs_device_ops *s_devops;/* device access */
- struct inode *s_master_inode; /* inode file */
- struct inode *s_segfile_inode; /* segment file */
- struct inode *s_mapping_inode; /* device mapping */
- atomic_t s_pending_writes; /* outstanting bios */
- long s_flags;
- mempool_t *s_btree_pool; /* for btree nodes */
- mempool_t *s_alias_pool; /* aliases in segment.c */
- u64 s_feature_incompat;
- u64 s_feature_ro_compat;
- 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;
- struct list_head s_object_alias; /* remapped objects */
- struct btree_head128 s_object_alias_tree; /* remapped objects */
- struct mutex s_object_alias_mutex;
- /* dir.c fields */
- struct mutex s_dirop_mutex; /* for creat/unlink/rename */
- u64 s_victim_ino; /* used for atomic dir-ops */
- u64 s_rename_dir; /* source directory ino */
- u64 s_rename_pos; /* position of source dd */
- /* gc.c fields */
- long s_segsize; /* size of a segment */
- int s_segshift; /* log2 of segment size */
- long s_segmask; /* 1 << s_segshift - 1 */
- long s_no_segs; /* segments on device */
- long s_no_journal_segs; /* segments used for journal */
- long s_no_blocks; /* blocks per segment */
- long s_writesize; /* minimum write size */
- int s_writeshift; /* log2 of write size */
- u64 s_size; /* filesystem size */
- struct logfs_area *s_area[LOGFS_NO_AREAS]; /* open segment array */
- u64 s_gec; /* global erase count */
- u64 s_wl_gec_ostore; /* time of last wl event */
- u64 s_wl_gec_journal; /* time of last wl event */
- u64 s_sweeper; /* current sweeper pos */
- u8 s_ifile_levels; /* max level of ifile */
- u8 s_iblock_levels; /* max level of regular files */
- u8 s_data_levels; /* # of segments to leaf block*/
- u8 s_total_levels; /* sum of above three */
- struct btree_head32 s_cand_tree; /* all candidates */
- struct candidate_list s_free_list; /* 100% free segments */
- struct candidate_list s_reserve_list; /* Bad segment reserve */
- struct candidate_list s_low_list[LOGFS_NO_AREAS];/* good candidates */
- struct candidate_list s_ec_list; /* wear level candidates */
- struct btree_head32 s_reserved_segments;/* sb, journal, bad, etc. */
- /* inode.c fields */
- u64 s_last_ino; /* highest ino used */
- long s_inos_till_wrap;
- u32 s_generation; /* i_generation for new files */
- struct list_head s_freeing_list; /* inodes being freed */
- /* journal.c fields */
- struct mutex s_journal_mutex;
- void *s_je; /* journal entry to compress */
- void *s_compressed_je; /* block to write to journal */
- u32 s_journal_seg[LOGFS_JOURNAL_SEGS]; /* journal segments */
- u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */
- u64 s_last_version;
- struct logfs_area *s_journal_area; /* open journal segment */
- __be64 s_je_array[MAX_JOURNAL_ENTRIES];
- int s_no_je;
-
- int s_sum_index; /* for the 12 summaries */
- struct shadow_tree s_shadow_tree;
- int s_je_fill; /* index of current je */
- /* readwrite.c fields */
- struct mutex s_write_mutex;
- int s_lock_count;
- mempool_t *s_block_pool; /* struct logfs_block pool */
- mempool_t *s_shadow_pool; /* struct logfs_shadow pool */
- struct list_head s_writeback_list; /* writeback pages */
- /*
- * Space accounting:
- * - s_used_bytes specifies space used to store valid data objects.
- * - s_dirty_used_bytes is space used to store non-committed data
- * objects. Those objects have already been written themselves,
- * but they don't become valid until all indirect blocks up to the
- * journal have been written as well.
- * - s_dirty_free_bytes is space used to store the old copy of a
- * replaced object, as long as the replacement is non-committed.
- * In other words, it is the amount of space freed when all dirty
- * blocks are written back.
- * - s_free_bytes is the amount of free space available for any
- * purpose.
- * - s_root_reserve is the amount of free space available only to
- * the root user. Non-privileged users can no longer write once
- * this watermark has been reached.
- * - s_speed_reserve is space which remains unused to speed up
- * garbage collection performance.
- * - s_dirty_pages is the space reserved for currently dirty pages.
- * It is a pessimistic estimate, so some/most will get freed on
- * page writeback.
- *
- * s_used_bytes + s_free_bytes + s_speed_reserve = total usable size
- */
- u64 s_free_bytes;
- u64 s_used_bytes;
- u64 s_dirty_free_bytes;
- u64 s_dirty_used_bytes;
- u64 s_root_reserve;
- u64 s_speed_reserve;
- u64 s_dirty_pages;
- /* Bad block handling:
- * - s_bad_seg_reserve is a number of segments usually kept
- * free. When encountering bad blocks, the affected segment's data
- * is _temporarily_ moved to a reserved segment.
- * - s_bad_segments is the number of known bad segments.
- */
- u32 s_bad_seg_reserve;
- u32 s_bad_segments;
-};
-
-/**
- * struct logfs_inode - in-memory inode
- *
- * @vfs_inode: struct inode
- * @li_data: data pointers
- * @li_used_bytes: number of used bytes
- * @li_freeing_list: used to track inodes currently being freed
- * @li_flags: inode flags
- * @li_refcount: number of internal (GC-induced) references
- */
-struct logfs_inode {
- struct inode vfs_inode;
- u64 li_data[LOGFS_EMBEDDED_FIELDS];
- u64 li_used_bytes;
- struct list_head li_freeing_list;
- struct logfs_block *li_block;
- u32 li_flags;
- u8 li_height;
- int li_refcount;
-};
-
-#define journal_for_each(__i) for (__i = 0; __i < LOGFS_JOURNAL_SEGS; __i++)
-#define for_each_area(__i) for (__i = 0; __i < LOGFS_NO_AREAS; __i++)
-#define for_each_area_down(__i) for (__i = LOGFS_NO_AREAS - 1; __i >= 0; __i--)
-
-/* compr.c */
-int logfs_compress(void *in, void *out, size_t inlen, size_t outlen);
-int logfs_uncompress(void *in, void *out, size_t inlen, size_t outlen);
-int __init logfs_compr_init(void);
-void logfs_compr_exit(void);
-
-/* dev_bdev.c */
-#ifdef CONFIG_BLOCK
-int logfs_get_sb_bdev(struct logfs_super *s,
- struct file_system_type *type,
- const char *devname);
-#else
-static inline int logfs_get_sb_bdev(struct logfs_super *s,
- struct file_system_type *type,
- const char *devname)
-{
- return -ENODEV;
-}
-#endif
-
-/* dev_mtd.c */
-#if IS_ENABLED(CONFIG_MTD)
-int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr);
-#else
-static inline int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr)
-{
- return -ENODEV;
-}
-#endif
-
-/* dir.c */
-extern const struct inode_operations logfs_dir_iops;
-extern const struct file_operations logfs_dir_fops;
-int logfs_replay_journal(struct super_block *sb);
-
-/* file.c */
-extern const struct inode_operations logfs_reg_iops;
-extern const struct file_operations logfs_reg_fops;
-extern const struct address_space_operations logfs_reg_aops;
-int logfs_readpage(struct file *file, struct page *page);
-long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
-
-/* gc.c */
-u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec);
-void logfs_gc_pass(struct super_block *sb);
-int logfs_check_areas(struct super_block *sb);
-int logfs_init_gc(struct super_block *sb);
-void logfs_cleanup_gc(struct super_block *sb);
-
-/* inode.c */
-extern const struct super_operations logfs_super_operations;
-struct inode *logfs_iget(struct super_block *sb, ino_t ino);
-struct inode *logfs_safe_iget(struct super_block *sb, ino_t ino, int *cookie);
-void logfs_safe_iput(struct inode *inode, int cookie);
-struct inode *logfs_new_inode(struct inode *dir, umode_t mode);
-struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino);
-struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino);
-int logfs_init_inode_cache(void);
-void logfs_destroy_inode_cache(void);
-void logfs_set_blocks(struct inode *inode, u64 no);
-/* these logically belong into inode.c but actually reside in readwrite.c */
-int logfs_read_inode(struct inode *inode);
-int __logfs_write_inode(struct inode *inode, struct page *, long flags);
-void logfs_evict_inode(struct inode *inode);
-
-/* journal.c */
-void logfs_write_anchor(struct super_block *sb);
-int logfs_init_journal(struct super_block *sb);
-void logfs_cleanup_journal(struct super_block *sb);
-int write_alias_journal(struct super_block *sb, u64 ino, u64 bix,
- level_t level, int child_no, __be64 val);
-void do_logfs_journal_wl_pass(struct super_block *sb);
-
-/* readwrite.c */
-pgoff_t logfs_pack_index(u64 bix, level_t level);
-void logfs_unpack_index(pgoff_t index, u64 *bix, level_t *level);
-int logfs_inode_write(struct inode *inode, const void *buf, size_t count,
- loff_t bix, long flags, struct shadow_tree *shadow_tree);
-int logfs_readpage_nolock(struct page *page);
-int logfs_write_buf(struct inode *inode, struct page *page, long flags);
-int logfs_delete(struct inode *inode, pgoff_t index,
- struct shadow_tree *shadow_tree);
-int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
- gc_level_t gc_level, long flags);
-int logfs_is_valid_block(struct super_block *sb, u64 ofs, u64 ino, u64 bix,
- gc_level_t gc_level);
-int logfs_truncate(struct inode *inode, u64 size);
-u64 logfs_seek_hole(struct inode *inode, u64 bix);
-u64 logfs_seek_data(struct inode *inode, u64 bix);
-int logfs_open_segfile(struct super_block *sb);
-int logfs_init_rw(struct super_block *sb);
-void logfs_cleanup_rw(struct super_block *sb);
-void logfs_add_transaction(struct inode *inode, struct logfs_transaction *ta);
-void logfs_del_transaction(struct inode *inode, struct logfs_transaction *ta);
-void logfs_write_block(struct logfs_block *block, long flags);
-int logfs_write_obj_aliases_pagecache(struct super_block *sb);
-void logfs_get_segment_entry(struct super_block *sb, u32 segno,
- struct logfs_segment_entry *se);
-void logfs_set_segment_used(struct super_block *sb, u64 ofs, int increment);
-void logfs_set_segment_erased(struct super_block *sb, u32 segno, u32 ec,
- gc_level_t gc_level);
-void logfs_set_segment_reserved(struct super_block *sb, u32 segno);
-void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec);
-struct logfs_block *__alloc_block(struct super_block *sb,
- u64 ino, u64 bix, level_t level);
-void __free_block(struct super_block *sb, struct logfs_block *block);
-void btree_write_block(struct logfs_block *block);
-void initialize_block_counters(struct page *page, struct logfs_block *block,
- __be64 *array, int page_is_empty);
-int logfs_exist_block(struct inode *inode, u64 bix);
-int get_page_reserve(struct inode *inode, struct page *page);
-void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock);
-void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock);
-extern const struct logfs_block_ops indirect_block_ops;
-
-/* segment.c */
-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);
-int logfs_segment_write(struct inode *inode, struct page *page,
- struct logfs_shadow *shadow);
-int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow);
-int logfs_load_object_aliases(struct super_block *sb,
- struct logfs_obj_alias *oa, int count);
-void move_page_to_btree(struct page *page);
-int logfs_init_mapping(struct super_block *sb);
-void logfs_sync_area(struct logfs_area *area);
-void logfs_sync_segments(struct super_block *sb);
-void freeseg(struct super_block *sb, u32 segno);
-void free_areas(struct super_block *sb);
-
-/* area handling */
-int logfs_init_areas(struct super_block *sb);
-void logfs_cleanup_areas(struct super_block *sb);
-int logfs_open_area(struct logfs_area *area, size_t bytes);
-int __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
- int use_filler);
-
-static inline int logfs_buf_write(struct logfs_area *area, u64 ofs,
- void *buf, size_t len)
-{
- return __logfs_buf_write(area, ofs, buf, len, 0);
-}
-
-static inline int logfs_buf_recover(struct logfs_area *area, u64 ofs,
- void *buf, size_t len)
-{
- return __logfs_buf_write(area, ofs, buf, len, 1);
-}
-
-/* super.c */
-struct page *emergency_read_begin(struct address_space *mapping, pgoff_t index);
-void emergency_read_end(struct page *page);
-void logfs_crash_dump(struct super_block *sb);
-int logfs_statfs(struct dentry *dentry, struct kstatfs *stats);
-int logfs_check_ds(struct logfs_disk_super *ds);
-int logfs_write_sb(struct super_block *sb);
-
-static inline struct logfs_super *logfs_super(struct super_block *sb)
-{
- return sb->s_fs_info;
-}
-
-static inline struct logfs_inode *logfs_inode(struct inode *inode)
-{
- return container_of(inode, struct logfs_inode, vfs_inode);
-}
-
-static inline void logfs_set_ro(struct super_block *sb)
-{
- logfs_super(sb)->s_flags |= LOGFS_SB_FLAG_RO;
-}
-
-#define LOGFS_BUG(sb) do { \
- struct super_block *__sb = sb; \
- logfs_crash_dump(__sb); \
- logfs_super(__sb)->s_flags |= LOGFS_SB_FLAG_RO; \
- BUG(); \
-} while (0)
-
-#define LOGFS_BUG_ON(condition, sb) \
- do { if (unlikely(condition)) LOGFS_BUG((sb)); } while (0)
-
-static inline __be32 logfs_crc32(void *data, size_t len, size_t skip)
-{
- return cpu_to_be32(crc32(~0, data+skip, len-skip));
-}
-
-static inline u8 logfs_type(struct inode *inode)
-{
- return (inode->i_mode >> 12) & 15;
-}
-
-static inline pgoff_t logfs_index(struct super_block *sb, u64 pos)
-{
- return pos >> sb->s_blocksize_bits;
-}
-
-static inline u64 dev_ofs(struct super_block *sb, u32 segno, u32 ofs)
-{
- return ((u64)segno << logfs_super(sb)->s_segshift) + ofs;
-}
-
-static inline u32 seg_no(struct super_block *sb, u64 ofs)
-{
- return ofs >> logfs_super(sb)->s_segshift;
-}
-
-static inline u32 seg_ofs(struct super_block *sb, u64 ofs)
-{
- return ofs & logfs_super(sb)->s_segmask;
-}
-
-static inline u64 seg_align(struct super_block *sb, u64 ofs)
-{
- return ofs & ~logfs_super(sb)->s_segmask;
-}
-
-static inline struct logfs_block *logfs_block(struct page *page)
-{
- return (void *)page->private;
-}
-
-static inline level_t shrink_level(gc_level_t __level)
-{
- u8 level = (__force u8)__level;
-
- if (level >= LOGFS_MAX_LEVELS)
- level -= LOGFS_MAX_LEVELS;
- return (__force level_t)level;
-}
-
-static inline gc_level_t expand_level(u64 ino, level_t __level)
-{
- u8 level = (__force u8)__level;
-
- if (ino == LOGFS_INO_MASTER) {
- /* ifile has separate areas */
- level += LOGFS_MAX_LEVELS;
- }
- return (__force gc_level_t)level;
-}
-
-static inline int logfs_block_shift(struct super_block *sb, level_t level)
-{
- level = shrink_level((__force gc_level_t)level);
- return (__force int)level * (sb->s_blocksize_bits - 3);
-}
-
-static inline u64 logfs_block_mask(struct super_block *sb, level_t level)
-{
- return ~0ull << logfs_block_shift(sb, level);
-}
-
-static inline struct logfs_area *get_area(struct super_block *sb,
- gc_level_t gc_level)
-{
- return logfs_super(sb)->s_area[(__force u8)gc_level];
-}
-
-static inline void logfs_mempool_destroy(mempool_t *pool)
-{
- if (pool)
- mempool_destroy(pool);
-}
-
-#endif
+++ /dev/null
-/*
- * fs/logfs/logfs_abi.h
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- *
- * Public header for logfs.
- */
-#ifndef FS_LOGFS_LOGFS_ABI_H
-#define FS_LOGFS_LOGFS_ABI_H
-
-/* For out-of-kernel compiles */
-#ifndef BUILD_BUG_ON
-#define BUILD_BUG_ON(condition) /**/
-#endif
-
-#define SIZE_CHECK(type, size) \
-static inline void check_##type(void) \
-{ \
- BUILD_BUG_ON(sizeof(struct type) != (size)); \
-}
-
-/*
- * Throughout the logfs code, we're constantly dealing with blocks at
- * various positions or offsets. To remove confusion, we stricly
- * distinguish between a "position" - the logical position within a
- * file and an "offset" - the physical location within the device.
- *
- * Any usage of the term offset for a logical location or position for
- * a physical one is a bug and should get fixed.
- */
-
-/*
- * Block are allocated in one of several segments depending on their
- * level. The following levels are used:
- * 0 - regular data block
- * 1 - i1 indirect blocks
- * 2 - i2 indirect blocks
- * 3 - i3 indirect blocks
- * 4 - i4 indirect blocks
- * 5 - i5 indirect blocks
- * 6 - ifile data blocks
- * 7 - ifile i1 indirect blocks
- * 8 - ifile i2 indirect blocks
- * 9 - ifile i3 indirect blocks
- * 10 - ifile i4 indirect blocks
- * 11 - ifile i5 indirect blocks
- * Potential levels to be used in the future:
- * 12 - gc recycled blocks, long-lived data
- * 13 - replacement blocks, short-lived data
- *
- * Levels 1-11 are necessary for robust gc operations and help separate
- * short-lived metadata from longer-lived file data. In the future,
- * file data should get separated into several segments based on simple
- * heuristics. Old data recycled during gc operation is expected to be
- * long-lived. New data is of uncertain life expectancy. New data
- * used to replace older blocks in existing files is expected to be
- * short-lived.
- */
-
-
-/* Magic numbers. 64bit for superblock, 32bit for statfs f_type */
-#define LOGFS_MAGIC 0x7a3a8e5cb9d5bf67ull
-#define LOGFS_MAGIC_U32 0xc97e8168u
-
-/*
- * Various blocksize related macros. Blocksize is currently fixed at 4KiB.
- * Sooner or later that should become configurable and the macros replaced
- * by something superblock-dependent. Pointers in indirect blocks are and
- * will remain 64bit.
- *
- * LOGFS_BLOCKSIZE - self-explaining
- * LOGFS_BLOCK_FACTOR - number of pointers per indirect block
- * LOGFS_BLOCK_BITS - log2 of LOGFS_BLOCK_FACTOR, used for shifts
- */
-#define LOGFS_BLOCKSIZE (4096ull)
-#define LOGFS_BLOCK_FACTOR (LOGFS_BLOCKSIZE / sizeof(u64))
-#define LOGFS_BLOCK_BITS (9)
-
-/*
- * Number of blocks at various levels of indirection. There are 16 direct
- * block pointers plus a single indirect pointer.
- */
-#define I0_BLOCKS (16)
-#define I1_BLOCKS LOGFS_BLOCK_FACTOR
-#define I2_BLOCKS (LOGFS_BLOCK_FACTOR * I1_BLOCKS)
-#define I3_BLOCKS (LOGFS_BLOCK_FACTOR * I2_BLOCKS)
-#define I4_BLOCKS (LOGFS_BLOCK_FACTOR * I3_BLOCKS)
-#define I5_BLOCKS (LOGFS_BLOCK_FACTOR * I4_BLOCKS)
-
-#define INDIRECT_INDEX I0_BLOCKS
-#define LOGFS_EMBEDDED_FIELDS (I0_BLOCKS + 1)
-
-/*
- * Sizes at which files require another level of indirection. Files smaller
- * than LOGFS_EMBEDDED_SIZE can be completely stored in the inode itself,
- * similar like ext2 fast symlinks.
- *
- * Data at a position smaller than LOGFS_I0_SIZE is accessed through the
- * direct pointers, else through the 1x indirect pointer and so forth.
- */
-#define LOGFS_EMBEDDED_SIZE (LOGFS_EMBEDDED_FIELDS * sizeof(u64))
-#define LOGFS_I0_SIZE (I0_BLOCKS * LOGFS_BLOCKSIZE)
-#define LOGFS_I1_SIZE (I1_BLOCKS * LOGFS_BLOCKSIZE)
-#define LOGFS_I2_SIZE (I2_BLOCKS * LOGFS_BLOCKSIZE)
-#define LOGFS_I3_SIZE (I3_BLOCKS * LOGFS_BLOCKSIZE)
-#define LOGFS_I4_SIZE (I4_BLOCKS * LOGFS_BLOCKSIZE)
-#define LOGFS_I5_SIZE (I5_BLOCKS * LOGFS_BLOCKSIZE)
-
-/*
- * Each indirect block pointer must have this flag set, if all block pointers
- * behind it are set, i.e. there is no hole hidden in the shadow of this
- * indirect block pointer.
- */
-#define LOGFS_FULLY_POPULATED (1ULL << 63)
-#define pure_ofs(ofs) (ofs & ~LOGFS_FULLY_POPULATED)
-
-/*
- * LogFS needs to separate data into levels. Each level is defined as the
- * maximal possible distance from the master inode (inode of the inode file).
- * Data blocks reside on level 0, 1x indirect block on level 1, etc.
- * Inodes reside on level 6, indirect blocks for the inode file on levels 7-11.
- * This effort is necessary to guarantee garbage collection to always make
- * progress.
- *
- * LOGFS_MAX_INDIRECT is the maximal indirection through indirect blocks,
- * LOGFS_MAX_LEVELS is one more for the actual data level of a file. It is
- * the maximal number of levels for one file.
- * LOGFS_NO_AREAS is twice that, as the inode file and regular files are
- * effectively stacked on top of each other.
- */
-#define LOGFS_MAX_INDIRECT (5)
-#define LOGFS_MAX_LEVELS (LOGFS_MAX_INDIRECT + 1)
-#define LOGFS_NO_AREAS (2 * LOGFS_MAX_LEVELS)
-
-/* Maximum size of filenames */
-#define LOGFS_MAX_NAMELEN (255)
-
-/* Number of segments in the primary journal. */
-#define LOGFS_JOURNAL_SEGS (16)
-
-/* Maximum number of free/erased/etc. segments in journal entries */
-#define MAX_CACHED_SEGS (64)
-
-
-/*
- * LOGFS_OBJECT_HEADERSIZE is the size of a single header in the object store,
- * LOGFS_MAX_OBJECTSIZE the size of the largest possible object, including
- * its header,
- * LOGFS_SEGMENT_RESERVE is the amount of space reserved for each segment for
- * its segment header and the padded space at the end when no further objects
- * fit.
- */
-#define LOGFS_OBJECT_HEADERSIZE (0x1c)
-#define LOGFS_SEGMENT_HEADERSIZE (0x18)
-#define LOGFS_MAX_OBJECTSIZE (LOGFS_OBJECT_HEADERSIZE + LOGFS_BLOCKSIZE)
-#define LOGFS_SEGMENT_RESERVE \
- (LOGFS_SEGMENT_HEADERSIZE + LOGFS_MAX_OBJECTSIZE - 1)
-
-/*
- * Segment types:
- * SEG_SUPER - Data or indirect block
- * SEG_JOURNAL - Inode
- * SEG_OSTORE - Dentry
- */
-enum {
- SEG_SUPER = 0x01,
- SEG_JOURNAL = 0x02,
- SEG_OSTORE = 0x03,
-};
-
-/**
- * struct logfs_segment_header - per-segment header in the ostore
- *
- * @crc: crc32 of header (there is no data)
- * @pad: unused, must be 0
- * @type: segment type, see above
- * @level: GC level for all objects in this segment
- * @segno: segment number
- * @ec: erase count for this segment
- * @gec: global erase count at time of writing
- */
-struct logfs_segment_header {
- __be32 crc;
- __be16 pad;
- __u8 type;
- __u8 level;
- __be32 segno;
- __be32 ec;
- __be64 gec;
-};
-
-SIZE_CHECK(logfs_segment_header, LOGFS_SEGMENT_HEADERSIZE);
-
-#define LOGFS_FEATURES_INCOMPAT (0ull)
-#define LOGFS_FEATURES_RO_COMPAT (0ull)
-#define LOGFS_FEATURES_COMPAT (0ull)
-
-/**
- * struct logfs_disk_super - on-medium superblock
- *
- * @ds_magic: magic number, must equal LOGFS_MAGIC
- * @ds_crc: crc32 of structure starting with the next field
- * @ds_ifile_levels: maximum number of levels for ifile
- * @ds_iblock_levels: maximum number of levels for regular files
- * @ds_data_levels: number of separate levels for data
- * @pad0: reserved, must be 0
- * @ds_feature_incompat: incompatible filesystem features
- * @ds_feature_ro_compat: read-only compatible filesystem features
- * @ds_feature_compat: compatible filesystem features
- * @ds_flags: flags
- * @ds_segment_shift: log2 of segment size
- * @ds_block_shift: log2 of block size
- * @ds_write_shift: log2 of write size
- * @pad1: reserved, must be 0
- * @ds_journal_seg: segments used by primary journal
- * @ds_root_reserve: bytes reserved for the superuser
- * @ds_speed_reserve: bytes reserved to speed up GC
- * @ds_bad_seg_reserve: number of segments reserved to handle bad blocks
- * @pad2: reserved, must be 0
- * @pad3: reserved, must be 0
- *
- * Contains only read-only fields. Read-write fields like the amount of used
- * space is tracked in the dynamic superblock, which is stored in the journal.
- */
-struct logfs_disk_super {
- struct logfs_segment_header ds_sh;
- __be64 ds_magic;
-
- __be32 ds_crc;
- __u8 ds_ifile_levels;
- __u8 ds_iblock_levels;
- __u8 ds_data_levels;
- __u8 ds_segment_shift;
- __u8 ds_block_shift;
- __u8 ds_write_shift;
- __u8 pad0[6];
-
- __be64 ds_filesystem_size;
- __be32 ds_segment_size;
- __be32 ds_bad_seg_reserve;
-
- __be64 ds_feature_incompat;
- __be64 ds_feature_ro_compat;
-
- __be64 ds_feature_compat;
- __be64 ds_feature_flags;
-
- __be64 ds_root_reserve;
- __be64 ds_speed_reserve;
-
- __be32 ds_journal_seg[LOGFS_JOURNAL_SEGS];
-
- __be64 ds_super_ofs[2];
- __be64 pad3[8];
-};
-
-SIZE_CHECK(logfs_disk_super, 256);
-
-/*
- * Object types:
- * OBJ_BLOCK - Data or indirect block
- * OBJ_INODE - Inode
- * OBJ_DENTRY - Dentry
- */
-enum {
- OBJ_BLOCK = 0x04,
- OBJ_INODE = 0x05,
- OBJ_DENTRY = 0x06,
-};
-
-/**
- * struct logfs_object_header - per-object header in the ostore
- *
- * @crc: crc32 of header, excluding data_crc
- * @len: length of data
- * @type: object type, see above
- * @compr: compression type
- * @ino: inode number
- * @bix: block index
- * @data_crc: crc32 of payload
- */
-struct logfs_object_header {
- __be32 crc;
- __be16 len;
- __u8 type;
- __u8 compr;
- __be64 ino;
- __be64 bix;
- __be32 data_crc;
-} __attribute__((packed));
-
-SIZE_CHECK(logfs_object_header, LOGFS_OBJECT_HEADERSIZE);
-
-/*
- * Reserved inode numbers:
- * LOGFS_INO_MASTER - master inode (for inode file)
- * LOGFS_INO_ROOT - root directory
- * LOGFS_INO_SEGFILE - per-segment used bytes and erase count
- */
-enum {
- LOGFS_INO_MAPPING = 0x00,
- LOGFS_INO_MASTER = 0x01,
- LOGFS_INO_ROOT = 0x02,
- LOGFS_INO_SEGFILE = 0x03,
- LOGFS_RESERVED_INOS = 0x10,
-};
-
-/*
- * Inode flags. High bits should never be written to the medium. They are
- * reserved for in-memory usage.
- * Low bits should either remain in sync with the corresponding FS_*_FL or
- * reuse slots that obviously don't make sense for logfs.
- *
- * LOGFS_IF_DIRTY Inode must be written back
- * LOGFS_IF_ZOMBIE Inode has been deleted
- * LOGFS_IF_STILLBORN -ENOSPC happened when creating inode
- */
-#define LOGFS_IF_COMPRESSED 0x00000004 /* == FS_COMPR_FL */
-#define LOGFS_IF_DIRTY 0x20000000
-#define LOGFS_IF_ZOMBIE 0x40000000
-#define LOGFS_IF_STILLBORN 0x80000000
-
-/* Flags available to chattr */
-#define LOGFS_FL_USER_VISIBLE (LOGFS_IF_COMPRESSED)
-#define LOGFS_FL_USER_MODIFIABLE (LOGFS_IF_COMPRESSED)
-/* Flags inherited from parent directory on file/directory creation */
-#define LOGFS_FL_INHERITED (LOGFS_IF_COMPRESSED)
-
-/**
- * struct logfs_disk_inode - on-medium inode
- *
- * @di_mode: file mode
- * @di_pad: reserved, must be 0
- * @di_flags: inode flags, see above
- * @di_uid: user id
- * @di_gid: group id
- * @di_ctime: change time
- * @di_mtime: modify time
- * @di_refcount: reference count (aka nlink or link count)
- * @di_generation: inode generation, for nfs
- * @di_used_bytes: number of bytes used
- * @di_size: file size
- * @di_data: data pointers
- */
-struct logfs_disk_inode {
- __be16 di_mode;
- __u8 di_height;
- __u8 di_pad;
- __be32 di_flags;
- __be32 di_uid;
- __be32 di_gid;
-
- __be64 di_ctime;
- __be64 di_mtime;
-
- __be64 di_atime;
- __be32 di_refcount;
- __be32 di_generation;
-
- __be64 di_used_bytes;
- __be64 di_size;
-
- __be64 di_data[LOGFS_EMBEDDED_FIELDS];
-};
-
-SIZE_CHECK(logfs_disk_inode, 200);
-
-#define INODE_POINTER_OFS \
- (offsetof(struct logfs_disk_inode, di_data) / sizeof(__be64))
-#define INODE_USED_OFS \
- (offsetof(struct logfs_disk_inode, di_used_bytes) / sizeof(__be64))
-#define INODE_SIZE_OFS \
- (offsetof(struct logfs_disk_inode, di_size) / sizeof(__be64))
-#define INODE_HEIGHT_OFS (0)
-
-/**
- * struct logfs_disk_dentry - on-medium dentry structure
- *
- * @ino: inode number
- * @namelen: length of file name
- * @type: file type, identical to bits 12..15 of mode
- * @name: file name
- */
-/* FIXME: add 6 bytes of padding to remove the __packed */
-struct logfs_disk_dentry {
- __be64 ino;
- __be16 namelen;
- __u8 type;
- __u8 name[LOGFS_MAX_NAMELEN];
-} __attribute__((packed));
-
-SIZE_CHECK(logfs_disk_dentry, 266);
-
-#define RESERVED 0xffffffff
-#define BADSEG 0xffffffff
-/**
- * struct logfs_segment_entry - segment file entry
- *
- * @ec_level: erase count and level
- * @valid: number of valid bytes
- *
- * Segment file contains one entry for every segment. ec_level contains the
- * erasecount in the upper 28 bits and the level in the lower 4 bits. An
- * ec_level of BADSEG (-1) identifies bad segments. valid contains the number
- * of valid bytes or RESERVED (-1 again) if the segment is used for either the
- * superblock or the journal, or when the segment is bad.
- */
-struct logfs_segment_entry {
- __be32 ec_level;
- __be32 valid;
-};
-
-SIZE_CHECK(logfs_segment_entry, 8);
-
-/**
- * struct logfs_journal_header - header for journal entries (JEs)
- *
- * @h_crc: crc32 of journal entry
- * @h_len: length of compressed journal entry,
- * not including header
- * @h_datalen: length of uncompressed data
- * @h_type: JE type
- * @h_compr: compression type
- * @h_pad: reserved
- */
-struct logfs_journal_header {
- __be32 h_crc;
- __be16 h_len;
- __be16 h_datalen;
- __be16 h_type;
- __u8 h_compr;
- __u8 h_pad[5];
-};
-
-SIZE_CHECK(logfs_journal_header, 16);
-
-/*
- * Life expectency of data.
- * VIM_DEFAULT - default vim
- * VIM_SEGFILE - for segment file only - very short-living
- * VIM_GC - GC'd data - likely long-living
- */
-enum logfs_vim {
- VIM_DEFAULT = 0,
- VIM_SEGFILE = 1,
-};
-
-/**
- * struct logfs_je_area - wbuf header
- *
- * @segno: segment number of area
- * @used_bytes: number of bytes already used
- * @gc_level: GC level
- * @vim: life expectancy of data
- *
- * "Areas" are segments currently being used for writing. There is at least
- * one area per GC level. Several may be used to separate long-living from
- * short-living data. If an area with unknown vim is encountered, it can
- * simply be closed.
- * The write buffer immediately follow this header.
- */
-struct logfs_je_area {
- __be32 segno;
- __be32 used_bytes;
- __u8 gc_level;
- __u8 vim;
-} __attribute__((packed));
-
-SIZE_CHECK(logfs_je_area, 10);
-
-#define MAX_JOURNAL_HEADER \
- (sizeof(struct logfs_journal_header) + sizeof(struct logfs_je_area))
-
-/**
- * struct logfs_je_dynsb - dynamic superblock
- *
- * @ds_gec: global erase count
- * @ds_sweeper: current position of GC "sweeper"
- * @ds_rename_dir: source directory ino (see dir.c documentation)
- * @ds_rename_pos: position of source dd (see dir.c documentation)
- * @ds_victim_ino: victims of incomplete dir operation (see dir.c)
- * @ds_victim_ino: parent inode of victim (see dir.c)
- * @ds_used_bytes: number of used bytes
- */
-struct logfs_je_dynsb {
- __be64 ds_gec;
- __be64 ds_sweeper;
-
- __be64 ds_rename_dir;
- __be64 ds_rename_pos;
-
- __be64 ds_victim_ino;
- __be64 ds_victim_parent; /* XXX */
-
- __be64 ds_used_bytes;
- __be32 ds_generation;
- __be32 pad;
-};
-
-SIZE_CHECK(logfs_je_dynsb, 64);
-
-/**
- * struct logfs_je_anchor - anchor of filesystem tree, aka master inode
- *
- * @da_size: size of inode file
- * @da_last_ino: last created inode
- * @da_used_bytes: number of bytes used
- * @da_data: data pointers
- */
-struct logfs_je_anchor {
- __be64 da_size;
- __be64 da_last_ino;
-
- __be64 da_used_bytes;
- u8 da_height;
- u8 pad[7];
-
- __be64 da_data[LOGFS_EMBEDDED_FIELDS];
-};
-
-SIZE_CHECK(logfs_je_anchor, 168);
-
-/**
- * struct logfs_je_spillout - spillout entry (from 1st to 2nd journal)
- *
- * @so_segment: segments used for 2nd journal
- *
- * Length of the array is given by h_len field in the header.
- */
-struct logfs_je_spillout {
- __be64 so_segment[0];
-};
-
-SIZE_CHECK(logfs_je_spillout, 0);
-
-/**
- * struct logfs_je_journal_ec - erase counts for all journal segments
- *
- * @ec: erase count
- *
- * Length of the array is given by h_len field in the header.
- */
-struct logfs_je_journal_ec {
- __be32 ec[0];
-};
-
-SIZE_CHECK(logfs_je_journal_ec, 0);
-
-/**
- * struct logfs_je_free_segments - list of free segmetns with erase count
- */
-struct logfs_je_free_segments {
- __be32 segno;
- __be32 ec;
-};
-
-SIZE_CHECK(logfs_je_free_segments, 8);
-
-/**
- * struct logfs_seg_alias - list of segment aliases
- */
-struct logfs_seg_alias {
- __be32 old_segno;
- __be32 new_segno;
-};
-
-SIZE_CHECK(logfs_seg_alias, 8);
-
-/**
- * struct logfs_obj_alias - list of object aliases
- */
-struct logfs_obj_alias {
- __be64 ino;
- __be64 bix;
- __be64 val;
- u8 level;
- u8 pad[5];
- __be16 child_no;
-};
-
-SIZE_CHECK(logfs_obj_alias, 32);
-
-/**
- * Compression types.
- *
- * COMPR_NONE - uncompressed
- * COMPR_ZLIB - compressed with zlib
- */
-enum {
- COMPR_NONE = 0,
- COMPR_ZLIB = 1,
-};
-
-/*
- * Journal entries come in groups of 16. First group contains unique
- * entries, next groups contain one entry per level
- *
- * JE_FIRST - smallest possible journal entry number
- *
- * JEG_BASE - base group, containing unique entries
- * JE_COMMIT - commit entry, validates all previous entries
- * JE_DYNSB - dynamic superblock, anything that ought to be in the
- * superblock but cannot because it is read-write data
- * JE_ANCHOR - anchor aka master inode aka inode file's inode
- * JE_ERASECOUNT erasecounts for all journal segments
- * JE_SPILLOUT - unused
- * JE_SEG_ALIAS - aliases segments
- * JE_AREA - area description
- *
- * JE_LAST - largest possible journal entry number
- */
-enum {
- JE_FIRST = 0x01,
-
- JEG_BASE = 0x00,
- JE_COMMIT = 0x02,
- JE_DYNSB = 0x03,
- JE_ANCHOR = 0x04,
- JE_ERASECOUNT = 0x05,
- JE_SPILLOUT = 0x06,
- JE_OBJ_ALIAS = 0x0d,
- JE_AREA = 0x0e,
-
- JE_LAST = 0x0e,
-};
-
-#endif
+++ /dev/null
-/*
- * fs/logfs/readwrite.c
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- *
- *
- * Actually contains five sets of very similar functions:
- * read read blocks from a file
- * seek_hole find next hole
- * seek_data find next data block
- * valid check whether a block still belongs to a file
- * write write blocks to a file
- * delete delete a block (for directories and ifile)
- * rewrite move existing blocks of a file to a new location (gc helper)
- * truncate truncate a file
- */
-#include "logfs.h"
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-static u64 adjust_bix(u64 bix, level_t level)
-{
- switch (level) {
- case 0:
- return bix;
- case LEVEL(1):
- return max_t(u64, bix, I0_BLOCKS);
- case LEVEL(2):
- return max_t(u64, bix, I1_BLOCKS);
- case LEVEL(3):
- return max_t(u64, bix, I2_BLOCKS);
- case LEVEL(4):
- return max_t(u64, bix, I3_BLOCKS);
- case LEVEL(5):
- return max_t(u64, bix, I4_BLOCKS);
- default:
- WARN_ON(1);
- return bix;
- }
-}
-
-static inline u64 maxbix(u8 height)
-{
- return 1ULL << (LOGFS_BLOCK_BITS * height);
-}
-
-/**
- * The inode address space is cut in two halves. Lower half belongs to data
- * pages, upper half to indirect blocks. If the high bit (INDIRECT_BIT) is
- * set, the actual block index (bix) and level can be derived from the page
- * index.
- *
- * The lowest three bits of the block index are set to 0 after packing and
- * unpacking. Since the lowest n bits (9 for 4KiB blocksize) are ignored
- * anyway this is harmless.
- */
-#define ARCH_SHIFT (BITS_PER_LONG - 32)
-#define INDIRECT_BIT (0x80000000UL << ARCH_SHIFT)
-#define LEVEL_SHIFT (28 + ARCH_SHIFT)
-static inline pgoff_t first_indirect_block(void)
-{
- return INDIRECT_BIT | (1ULL << LEVEL_SHIFT);
-}
-
-pgoff_t logfs_pack_index(u64 bix, level_t level)
-{
- pgoff_t index;
-
- BUG_ON(bix >= INDIRECT_BIT);
- if (level == 0)
- return bix;
-
- index = INDIRECT_BIT;
- index |= (__force long)level << LEVEL_SHIFT;
- index |= bix >> ((__force u8)level * LOGFS_BLOCK_BITS);
- return index;
-}
-
-void logfs_unpack_index(pgoff_t index, u64 *bix, level_t *level)
-{
- u8 __level;
-
- if (!(index & INDIRECT_BIT)) {
- *bix = index;
- *level = 0;
- return;
- }
-
- __level = (index & ~INDIRECT_BIT) >> LEVEL_SHIFT;
- *level = LEVEL(__level);
- *bix = (index << (__level * LOGFS_BLOCK_BITS)) & ~INDIRECT_BIT;
- *bix = adjust_bix(*bix, *level);
- return;
-}
-#undef ARCH_SHIFT
-#undef INDIRECT_BIT
-#undef LEVEL_SHIFT
-
-/*
- * Time is stored as nanoseconds since the epoch.
- */
-static struct timespec be64_to_timespec(__be64 betime)
-{
- return ns_to_timespec(be64_to_cpu(betime));
-}
-
-static __be64 timespec_to_be64(struct timespec tsp)
-{
- return cpu_to_be64((u64)tsp.tv_sec * NSEC_PER_SEC + tsp.tv_nsec);
-}
-
-static void logfs_disk_to_inode(struct logfs_disk_inode *di, struct inode*inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- int i;
-
- inode->i_mode = be16_to_cpu(di->di_mode);
- li->li_height = di->di_height;
- li->li_flags = be32_to_cpu(di->di_flags);
- i_uid_write(inode, be32_to_cpu(di->di_uid));
- i_gid_write(inode, be32_to_cpu(di->di_gid));
- inode->i_size = be64_to_cpu(di->di_size);
- logfs_set_blocks(inode, be64_to_cpu(di->di_used_bytes));
- inode->i_atime = be64_to_timespec(di->di_atime);
- inode->i_ctime = be64_to_timespec(di->di_ctime);
- inode->i_mtime = be64_to_timespec(di->di_mtime);
- set_nlink(inode, be32_to_cpu(di->di_refcount));
- inode->i_generation = be32_to_cpu(di->di_generation);
-
- switch (inode->i_mode & S_IFMT) {
- case S_IFSOCK: /* fall through */
- case S_IFBLK: /* fall through */
- case S_IFCHR: /* fall through */
- case S_IFIFO:
- inode->i_rdev = be64_to_cpu(di->di_data[0]);
- break;
- case S_IFDIR: /* fall through */
- case S_IFREG: /* fall through */
- case S_IFLNK:
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- li->li_data[i] = be64_to_cpu(di->di_data[i]);
- break;
- default:
- BUG();
- }
-}
-
-static void logfs_inode_to_disk(struct inode *inode, struct logfs_disk_inode*di)
-{
- struct logfs_inode *li = logfs_inode(inode);
- int i;
-
- di->di_mode = cpu_to_be16(inode->i_mode);
- di->di_height = li->li_height;
- di->di_pad = 0;
- di->di_flags = cpu_to_be32(li->li_flags);
- di->di_uid = cpu_to_be32(i_uid_read(inode));
- di->di_gid = cpu_to_be32(i_gid_read(inode));
- di->di_size = cpu_to_be64(i_size_read(inode));
- di->di_used_bytes = cpu_to_be64(li->li_used_bytes);
- di->di_atime = timespec_to_be64(inode->i_atime);
- di->di_ctime = timespec_to_be64(inode->i_ctime);
- di->di_mtime = timespec_to_be64(inode->i_mtime);
- di->di_refcount = cpu_to_be32(inode->i_nlink);
- di->di_generation = cpu_to_be32(inode->i_generation);
-
- switch (inode->i_mode & S_IFMT) {
- case S_IFSOCK: /* fall through */
- case S_IFBLK: /* fall through */
- case S_IFCHR: /* fall through */
- case S_IFIFO:
- di->di_data[0] = cpu_to_be64(inode->i_rdev);
- break;
- case S_IFDIR: /* fall through */
- case S_IFREG: /* fall through */
- case S_IFLNK:
- for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
- di->di_data[i] = cpu_to_be64(li->li_data[i]);
- break;
- default:
- BUG();
- }
-}
-
-static void __logfs_set_blocks(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_inode *li = logfs_inode(inode);
-
- inode->i_blocks = ULONG_MAX;
- if (li->li_used_bytes >> sb->s_blocksize_bits < ULONG_MAX)
- inode->i_blocks = ALIGN(li->li_used_bytes, 512) >> 9;
-}
-
-void logfs_set_blocks(struct inode *inode, u64 bytes)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- li->li_used_bytes = bytes;
- __logfs_set_blocks(inode);
-}
-
-static void prelock_page(struct super_block *sb, struct page *page, int lock)
-{
- struct logfs_super *super = logfs_super(sb);
-
- BUG_ON(!PageLocked(page));
- if (lock) {
- BUG_ON(PagePreLocked(page));
- SetPagePreLocked(page);
- } else {
- /* We are in GC path. */
- if (PagePreLocked(page))
- super->s_lock_count++;
- else
- SetPagePreLocked(page);
- }
-}
-
-static void preunlock_page(struct super_block *sb, struct page *page, int lock)
-{
- struct logfs_super *super = logfs_super(sb);
-
- BUG_ON(!PageLocked(page));
- if (lock)
- ClearPagePreLocked(page);
- else {
- /* We are in GC path. */
- BUG_ON(!PagePreLocked(page));
- if (super->s_lock_count)
- super->s_lock_count--;
- else
- ClearPagePreLocked(page);
- }
-}
-
-/*
- * Logfs is prone to an AB-BA deadlock where one task tries to acquire
- * s_write_mutex with a locked page and GC tries to get that page while holding
- * s_write_mutex.
- * To solve this issue logfs will ignore the page lock iff the page in question
- * is waiting for s_write_mutex. We annotate this fact by setting PG_pre_locked
- * in addition to PG_locked.
- */
-void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock)
-{
- struct logfs_super *super = logfs_super(sb);
-
- if (page)
- prelock_page(sb, page, lock);
-
- if (lock) {
- mutex_lock(&super->s_write_mutex);
- logfs_gc_pass(sb);
- /* FIXME: We also have to check for shadowed space
- * and mempool fill grade */
- }
-}
-
-void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock)
-{
- struct logfs_super *super = logfs_super(sb);
-
- if (page)
- preunlock_page(sb, page, lock);
- /* Order matters - we must clear PG_pre_locked before releasing
- * s_write_mutex or we could race against another task. */
- if (lock)
- mutex_unlock(&super->s_write_mutex);
-}
-
-static struct page *logfs_get_read_page(struct inode *inode, u64 bix,
- level_t level)
-{
- return find_or_create_page(inode->i_mapping,
- logfs_pack_index(bix, level), GFP_NOFS);
-}
-
-static void logfs_put_read_page(struct page *page)
-{
- unlock_page(page);
- put_page(page);
-}
-
-static void logfs_lock_write_page(struct page *page)
-{
- int loop = 0;
-
- while (unlikely(!trylock_page(page))) {
- if (loop++ > 0x1000) {
- /* Has been observed once so far... */
- printk(KERN_ERR "stack at %p\n", &loop);
- BUG();
- }
- if (PagePreLocked(page)) {
- /* Holder of page lock is waiting for us, it
- * is safe to use this page. */
- break;
- }
- /* Some other process has this page locked and has
- * nothing to do with us. Wait for it to finish.
- */
- schedule();
- }
- BUG_ON(!PageLocked(page));
-}
-
-static struct page *logfs_get_write_page(struct inode *inode, u64 bix,
- level_t level)
-{
- struct address_space *mapping = inode->i_mapping;
- pgoff_t index = logfs_pack_index(bix, level);
- struct page *page;
- int err;
-
-repeat:
- page = find_get_page(mapping, index);
- if (!page) {
- page = __page_cache_alloc(GFP_NOFS);
- if (!page)
- return NULL;
- err = add_to_page_cache_lru(page, mapping, index, GFP_NOFS);
- if (unlikely(err)) {
- put_page(page);
- if (err == -EEXIST)
- goto repeat;
- return NULL;
- }
- } else logfs_lock_write_page(page);
- BUG_ON(!PageLocked(page));
- return page;
-}
-
-static void logfs_unlock_write_page(struct page *page)
-{
- if (!PagePreLocked(page))
- unlock_page(page);
-}
-
-static void logfs_put_write_page(struct page *page)
-{
- logfs_unlock_write_page(page);
- put_page(page);
-}
-
-static struct page *logfs_get_page(struct inode *inode, u64 bix, level_t level,
- int rw)
-{
- if (rw == READ)
- return logfs_get_read_page(inode, bix, level);
- else
- return logfs_get_write_page(inode, bix, level);
-}
-
-static void logfs_put_page(struct page *page, int rw)
-{
- if (rw == READ)
- logfs_put_read_page(page);
- else
- logfs_put_write_page(page);
-}
-
-static unsigned long __get_bits(u64 val, int skip, int no)
-{
- u64 ret = val;
-
- ret >>= skip * no;
- ret <<= 64 - no;
- ret >>= 64 - no;
- return ret;
-}
-
-static unsigned long get_bits(u64 val, level_t skip)
-{
- return __get_bits(val, (__force int)skip, LOGFS_BLOCK_BITS);
-}
-
-static inline void init_shadow_tree(struct super_block *sb,
- struct shadow_tree *tree)
-{
- struct logfs_super *super = logfs_super(sb);
-
- btree_init_mempool64(&tree->new, super->s_btree_pool);
- btree_init_mempool64(&tree->old, super->s_btree_pool);
-}
-
-static void indirect_write_block(struct logfs_block *block)
-{
- struct page *page;
- struct inode *inode;
- int ret;
-
- page = block->page;
- inode = page->mapping->host;
- logfs_lock_write_page(page);
- ret = logfs_write_buf(inode, page, 0);
- logfs_unlock_write_page(page);
- /*
- * This needs some rework. Unless you want your filesystem to run
- * completely synchronously (you don't), the filesystem will always
- * report writes as 'successful' before the actual work has been
- * done. The actual work gets done here and this is where any errors
- * will show up. And there isn't much we can do about it, really.
- *
- * Some attempts to fix the errors (move from bad blocks, retry io,...)
- * have already been done, so anything left should be either a broken
- * device or a bug somewhere in logfs itself. Being relatively new,
- * the odds currently favor a bug, so for now the line below isn't
- * entirely tasteles.
- */
- BUG_ON(ret);
-}
-
-static void inode_write_block(struct logfs_block *block)
-{
- struct inode *inode;
- int ret;
-
- inode = block->inode;
- if (inode->i_ino == LOGFS_INO_MASTER)
- logfs_write_anchor(inode->i_sb);
- else {
- ret = __logfs_write_inode(inode, NULL, 0);
- /* see indirect_write_block comment */
- BUG_ON(ret);
- }
-}
-
-/*
- * This silences a false, yet annoying gcc warning. I hate it when my editor
- * jumps into bitops.h each time I recompile this file.
- * TODO: Complain to gcc folks about this and upgrade compiler.
- */
-static unsigned long fnb(const unsigned long *addr,
- unsigned long size, unsigned long offset)
-{
- return find_next_bit(addr, size, offset);
-}
-
-static __be64 inode_val0(struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- u64 val;
-
- /*
- * Explicit shifting generates good code, but must match the format
- * of the structure. Add some paranoia just in case.
- */
- BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_mode) != 0);
- BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_height) != 2);
- BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_flags) != 4);
-
- val = (u64)inode->i_mode << 48 |
- (u64)li->li_height << 40 |
- (u64)li->li_flags;
- return cpu_to_be64(val);
-}
-
-static int inode_write_alias(struct super_block *sb,
- struct logfs_block *block, write_alias_t *write_one_alias)
-{
- struct inode *inode = block->inode;
- struct logfs_inode *li = logfs_inode(inode);
- unsigned long pos;
- u64 ino , bix;
- __be64 val;
- level_t level;
- int err;
-
- for (pos = 0; ; pos++) {
- pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
- if (pos >= LOGFS_EMBEDDED_FIELDS + INODE_POINTER_OFS)
- return 0;
-
- switch (pos) {
- case INODE_HEIGHT_OFS:
- val = inode_val0(inode);
- break;
- case INODE_USED_OFS:
- val = cpu_to_be64(li->li_used_bytes);
- break;
- case INODE_SIZE_OFS:
- val = cpu_to_be64(i_size_read(inode));
- break;
- case INODE_POINTER_OFS ... INODE_POINTER_OFS + LOGFS_EMBEDDED_FIELDS - 1:
- val = cpu_to_be64(li->li_data[pos - INODE_POINTER_OFS]);
- break;
- default:
- BUG();
- }
-
- ino = LOGFS_INO_MASTER;
- bix = inode->i_ino;
- level = LEVEL(0);
- err = write_one_alias(sb, ino, bix, level, pos, val);
- if (err)
- return err;
- }
-}
-
-static int indirect_write_alias(struct super_block *sb,
- struct logfs_block *block, write_alias_t *write_one_alias)
-{
- unsigned long pos;
- struct page *page = block->page;
- u64 ino , bix;
- __be64 *child, val;
- level_t level;
- int err;
-
- for (pos = 0; ; pos++) {
- pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
- if (pos >= LOGFS_BLOCK_FACTOR)
- return 0;
-
- ino = page->mapping->host->i_ino;
- logfs_unpack_index(page->index, &bix, &level);
- child = kmap_atomic(page);
- val = child[pos];
- kunmap_atomic(child);
- err = write_one_alias(sb, ino, bix, level, pos, val);
- if (err)
- return err;
- }
-}
-
-int logfs_write_obj_aliases_pagecache(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_block *block;
- int err;
-
- list_for_each_entry(block, &super->s_object_alias, alias_list) {
- err = block->ops->write_alias(sb, block, write_alias_journal);
- if (err)
- return err;
- }
- return 0;
-}
-
-void __free_block(struct super_block *sb, struct logfs_block *block)
-{
- BUG_ON(!list_empty(&block->item_list));
- list_del(&block->alias_list);
- mempool_free(block, logfs_super(sb)->s_block_pool);
-}
-
-static void inode_free_block(struct super_block *sb, struct logfs_block *block)
-{
- struct inode *inode = block->inode;
-
- logfs_inode(inode)->li_block = NULL;
- __free_block(sb, block);
-}
-
-static void indirect_free_block(struct super_block *sb,
- struct logfs_block *block)
-{
- struct page *page = block->page;
-
- if (PagePrivate(page)) {
- ClearPagePrivate(page);
- put_page(page);
- set_page_private(page, 0);
- }
- __free_block(sb, block);
-}
-
-
-static const struct logfs_block_ops inode_block_ops = {
- .write_block = inode_write_block,
- .free_block = inode_free_block,
- .write_alias = inode_write_alias,
-};
-
-const struct logfs_block_ops indirect_block_ops = {
- .write_block = indirect_write_block,
- .free_block = indirect_free_block,
- .write_alias = indirect_write_alias,
-};
-
-struct logfs_block *__alloc_block(struct super_block *sb,
- u64 ino, u64 bix, level_t level)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_block *block;
-
- block = mempool_alloc(super->s_block_pool, GFP_NOFS);
- memset(block, 0, sizeof(*block));
- INIT_LIST_HEAD(&block->alias_list);
- INIT_LIST_HEAD(&block->item_list);
- block->sb = sb;
- block->ino = ino;
- block->bix = bix;
- block->level = level;
- return block;
-}
-
-static void alloc_inode_block(struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_block *block;
-
- if (li->li_block)
- return;
-
- block = __alloc_block(inode->i_sb, LOGFS_INO_MASTER, inode->i_ino, 0);
- block->inode = inode;
- li->li_block = block;
- block->ops = &inode_block_ops;
-}
-
-void initialize_block_counters(struct page *page, struct logfs_block *block,
- __be64 *array, int page_is_empty)
-{
- u64 ptr;
- int i, start;
-
- block->partial = 0;
- block->full = 0;
- start = 0;
- if (page->index < first_indirect_block()) {
- /* Counters are pointless on level 0 */
- return;
- }
- if (page->index == first_indirect_block()) {
- /* Skip unused pointers */
- start = I0_BLOCKS;
- block->full = I0_BLOCKS;
- }
- if (!page_is_empty) {
- for (i = start; i < LOGFS_BLOCK_FACTOR; i++) {
- ptr = be64_to_cpu(array[i]);
- if (ptr)
- block->partial++;
- if (ptr & LOGFS_FULLY_POPULATED)
- block->full++;
- }
- }
-}
-
-static void alloc_data_block(struct inode *inode, struct page *page)
-{
- struct logfs_block *block;
- u64 bix;
- level_t level;
-
- if (PagePrivate(page))
- return;
-
- logfs_unpack_index(page->index, &bix, &level);
- block = __alloc_block(inode->i_sb, inode->i_ino, bix, level);
- block->page = page;
-
- SetPagePrivate(page);
- get_page(page);
- set_page_private(page, (unsigned long) block);
-
- block->ops = &indirect_block_ops;
-}
-
-static void alloc_indirect_block(struct inode *inode, struct page *page,
- int page_is_empty)
-{
- struct logfs_block *block;
- __be64 *array;
-
- if (PagePrivate(page))
- return;
-
- alloc_data_block(inode, page);
-
- block = logfs_block(page);
- array = kmap_atomic(page);
- initialize_block_counters(page, block, array, page_is_empty);
- kunmap_atomic(array);
-}
-
-static void block_set_pointer(struct page *page, int index, u64 ptr)
-{
- struct logfs_block *block = logfs_block(page);
- __be64 *array;
- u64 oldptr;
-
- BUG_ON(!block);
- array = kmap_atomic(page);
- oldptr = be64_to_cpu(array[index]);
- array[index] = cpu_to_be64(ptr);
- kunmap_atomic(array);
- SetPageUptodate(page);
-
- block->full += !!(ptr & LOGFS_FULLY_POPULATED)
- - !!(oldptr & LOGFS_FULLY_POPULATED);
- block->partial += !!ptr - !!oldptr;
-}
-
-static u64 block_get_pointer(struct page *page, int index)
-{
- __be64 *block;
- u64 ptr;
-
- block = kmap_atomic(page);
- ptr = be64_to_cpu(block[index]);
- kunmap_atomic(block);
- return ptr;
-}
-
-static int logfs_read_empty(struct page *page)
-{
- zero_user_segment(page, 0, PAGE_SIZE);
- return 0;
-}
-
-static int logfs_read_direct(struct inode *inode, struct page *page)
-{
- struct logfs_inode *li = logfs_inode(inode);
- pgoff_t index = page->index;
- u64 block;
-
- block = li->li_data[index];
- if (!block)
- return logfs_read_empty(page);
-
- return logfs_segment_read(inode, page, block, index, 0);
-}
-
-static int logfs_read_loop(struct inode *inode, struct page *page,
- int rw_context)
-{
- struct logfs_inode *li = logfs_inode(inode);
- u64 bix, bofs = li->li_data[INDIRECT_INDEX];
- level_t level, target_level;
- int ret;
- struct page *ipage;
-
- logfs_unpack_index(page->index, &bix, &target_level);
- if (!bofs)
- return logfs_read_empty(page);
-
- if (bix >= maxbix(li->li_height))
- return logfs_read_empty(page);
-
- for (level = LEVEL(li->li_height);
- (__force u8)level > (__force u8)target_level;
- level = SUBLEVEL(level)){
- ipage = logfs_get_page(inode, bix, level, rw_context);
- if (!ipage)
- return -ENOMEM;
-
- ret = logfs_segment_read(inode, ipage, bofs, bix, level);
- if (ret) {
- logfs_put_read_page(ipage);
- return ret;
- }
-
- bofs = block_get_pointer(ipage, get_bits(bix, SUBLEVEL(level)));
- logfs_put_page(ipage, rw_context);
- if (!bofs)
- return logfs_read_empty(page);
- }
-
- return logfs_segment_read(inode, page, bofs, bix, 0);
-}
-
-static int logfs_read_block(struct inode *inode, struct page *page,
- int rw_context)
-{
- pgoff_t index = page->index;
-
- if (index < I0_BLOCKS)
- return logfs_read_direct(inode, page);
- return logfs_read_loop(inode, page, rw_context);
-}
-
-static int logfs_exist_loop(struct inode *inode, u64 bix)
-{
- struct logfs_inode *li = logfs_inode(inode);
- u64 bofs = li->li_data[INDIRECT_INDEX];
- level_t level;
- int ret;
- struct page *ipage;
-
- if (!bofs)
- return 0;
- if (bix >= maxbix(li->li_height))
- return 0;
-
- for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)) {
- ipage = logfs_get_read_page(inode, bix, level);
- if (!ipage)
- return -ENOMEM;
-
- ret = logfs_segment_read(inode, ipage, bofs, bix, level);
- if (ret) {
- logfs_put_read_page(ipage);
- return ret;
- }
-
- bofs = block_get_pointer(ipage, get_bits(bix, SUBLEVEL(level)));
- logfs_put_read_page(ipage);
- if (!bofs)
- return 0;
- }
-
- return 1;
-}
-
-int logfs_exist_block(struct inode *inode, u64 bix)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if (bix < I0_BLOCKS)
- return !!li->li_data[bix];
- return logfs_exist_loop(inode, bix);
-}
-
-static u64 seek_holedata_direct(struct inode *inode, u64 bix, int data)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- for (; bix < I0_BLOCKS; bix++)
- if (data ^ (li->li_data[bix] == 0))
- return bix;
- return I0_BLOCKS;
-}
-
-static u64 seek_holedata_loop(struct inode *inode, u64 bix, int data)
-{
- struct logfs_inode *li = logfs_inode(inode);
- __be64 *rblock;
- u64 increment, bofs = li->li_data[INDIRECT_INDEX];
- level_t level;
- int ret, slot;
- struct page *page;
-
- BUG_ON(!bofs);
-
- for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)) {
- increment = 1 << (LOGFS_BLOCK_BITS * ((__force u8)level-1));
- page = logfs_get_read_page(inode, bix, level);
- if (!page)
- return bix;
-
- ret = logfs_segment_read(inode, page, bofs, bix, level);
- if (ret) {
- logfs_put_read_page(page);
- return bix;
- }
-
- slot = get_bits(bix, SUBLEVEL(level));
- rblock = kmap_atomic(page);
- while (slot < LOGFS_BLOCK_FACTOR) {
- if (data && (rblock[slot] != 0))
- break;
- if (!data && !(be64_to_cpu(rblock[slot]) & LOGFS_FULLY_POPULATED))
- break;
- slot++;
- bix += increment;
- bix &= ~(increment - 1);
- }
- if (slot >= LOGFS_BLOCK_FACTOR) {
- kunmap_atomic(rblock);
- logfs_put_read_page(page);
- return bix;
- }
- bofs = be64_to_cpu(rblock[slot]);
- kunmap_atomic(rblock);
- logfs_put_read_page(page);
- if (!bofs) {
- BUG_ON(data);
- return bix;
- }
- }
- return bix;
-}
-
-/**
- * logfs_seek_hole - find next hole starting at a given block index
- * @inode: inode to search in
- * @bix: block index to start searching
- *
- * Returns next hole. If the file doesn't contain any further holes, the
- * block address next to eof is returned instead.
- */
-u64 logfs_seek_hole(struct inode *inode, u64 bix)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if (bix < I0_BLOCKS) {
- bix = seek_holedata_direct(inode, bix, 0);
- if (bix < I0_BLOCKS)
- return bix;
- }
-
- if (!li->li_data[INDIRECT_INDEX])
- return bix;
- else if (li->li_data[INDIRECT_INDEX] & LOGFS_FULLY_POPULATED)
- bix = maxbix(li->li_height);
- else if (bix >= maxbix(li->li_height))
- return bix;
- else {
- bix = seek_holedata_loop(inode, bix, 0);
- if (bix < maxbix(li->li_height))
- return bix;
- /* Should not happen anymore. But if some port writes semi-
- * corrupt images (as this one used to) we might run into it.
- */
- WARN_ON_ONCE(bix == maxbix(li->li_height));
- }
-
- return bix;
-}
-
-static u64 __logfs_seek_data(struct inode *inode, u64 bix)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if (bix < I0_BLOCKS) {
- bix = seek_holedata_direct(inode, bix, 1);
- if (bix < I0_BLOCKS)
- return bix;
- }
-
- if (bix < maxbix(li->li_height)) {
- if (!li->li_data[INDIRECT_INDEX])
- bix = maxbix(li->li_height);
- else
- return seek_holedata_loop(inode, bix, 1);
- }
-
- return bix;
-}
-
-/**
- * logfs_seek_data - find next data block after a given block index
- * @inode: inode to search in
- * @bix: block index to start searching
- *
- * Returns next data block. If the file doesn't contain any further data
- * blocks, the last block in the file is returned instead.
- */
-u64 logfs_seek_data(struct inode *inode, u64 bix)
-{
- struct super_block *sb = inode->i_sb;
- u64 ret, end;
-
- ret = __logfs_seek_data(inode, bix);
- end = i_size_read(inode) >> sb->s_blocksize_bits;
- if (ret >= end)
- ret = max(bix, end);
- return ret;
-}
-
-static int logfs_is_valid_direct(struct logfs_inode *li, u64 bix, u64 ofs)
-{
- return pure_ofs(li->li_data[bix]) == ofs;
-}
-
-static int __logfs_is_valid_loop(struct inode *inode, u64 bix,
- u64 ofs, u64 bofs)
-{
- struct logfs_inode *li = logfs_inode(inode);
- level_t level;
- int ret;
- struct page *page;
-
- for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)){
- page = logfs_get_write_page(inode, bix, level);
- BUG_ON(!page);
-
- ret = logfs_segment_read(inode, page, bofs, bix, level);
- if (ret) {
- logfs_put_write_page(page);
- return 0;
- }
-
- bofs = block_get_pointer(page, get_bits(bix, SUBLEVEL(level)));
- logfs_put_write_page(page);
- if (!bofs)
- return 0;
-
- if (pure_ofs(bofs) == ofs)
- return 1;
- }
- return 0;
-}
-
-static int logfs_is_valid_loop(struct inode *inode, u64 bix, u64 ofs)
-{
- struct logfs_inode *li = logfs_inode(inode);
- u64 bofs = li->li_data[INDIRECT_INDEX];
-
- if (!bofs)
- return 0;
-
- if (bix >= maxbix(li->li_height))
- return 0;
-
- if (pure_ofs(bofs) == ofs)
- return 1;
-
- return __logfs_is_valid_loop(inode, bix, ofs, bofs);
-}
-
-static int __logfs_is_valid_block(struct inode *inode, u64 bix, u64 ofs)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if ((inode->i_nlink == 0) && atomic_read(&inode->i_count) == 1)
- return 0;
-
- if (bix < I0_BLOCKS)
- return logfs_is_valid_direct(li, bix, ofs);
- return logfs_is_valid_loop(inode, bix, ofs);
-}
-
-/**
- * logfs_is_valid_block - check whether this block is still valid
- *
- * @sb: superblock
- * @ofs: block physical offset
- * @ino: block inode number
- * @bix: block index
- * @gc_level: block level
- *
- * Returns 0 if the block is invalid, 1 if it is valid and 2 if it will
- * become invalid once the journal is written.
- */
-int logfs_is_valid_block(struct super_block *sb, u64 ofs, u64 ino, u64 bix,
- gc_level_t gc_level)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode;
- int ret, cookie;
-
- /* Umount closes a segment with free blocks remaining. Those
- * blocks are by definition invalid. */
- if (ino == -1)
- return 0;
-
- LOGFS_BUG_ON((u64)(u_long)ino != ino, sb);
-
- inode = logfs_safe_iget(sb, ino, &cookie);
- if (IS_ERR(inode))
- goto invalid;
-
- ret = __logfs_is_valid_block(inode, bix, ofs);
- logfs_safe_iput(inode, cookie);
- if (ret)
- return ret;
-
-invalid:
- /* Block is nominally invalid, but may still sit in the shadow tree,
- * waiting for a journal commit.
- */
- if (btree_lookup64(&super->s_shadow_tree.old, ofs))
- return 2;
- return 0;
-}
-
-int logfs_readpage_nolock(struct page *page)
-{
- struct inode *inode = page->mapping->host;
- int ret = -EIO;
-
- ret = logfs_read_block(inode, page, READ);
-
- if (ret) {
- ClearPageUptodate(page);
- SetPageError(page);
- } else {
- SetPageUptodate(page);
- ClearPageError(page);
- }
- flush_dcache_page(page);
-
- return ret;
-}
-
-static int logfs_reserve_bytes(struct inode *inode, int bytes)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- u64 available = super->s_free_bytes + super->s_dirty_free_bytes
- - super->s_dirty_used_bytes - super->s_dirty_pages;
-
- if (!bytes)
- return 0;
-
- if (available < bytes)
- return -ENOSPC;
-
- if (available < bytes + super->s_root_reserve &&
- !capable(CAP_SYS_RESOURCE))
- return -ENOSPC;
-
- return 0;
-}
-
-int get_page_reserve(struct inode *inode, struct page *page)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- struct logfs_block *block = logfs_block(page);
- int ret;
-
- if (block && block->reserved_bytes)
- return 0;
-
- logfs_get_wblocks(inode->i_sb, page, WF_LOCK);
- while ((ret = logfs_reserve_bytes(inode, 6 * LOGFS_MAX_OBJECTSIZE)) &&
- !list_empty(&super->s_writeback_list)) {
- block = list_entry(super->s_writeback_list.next,
- struct logfs_block, alias_list);
- block->ops->write_block(block);
- }
- if (!ret) {
- alloc_data_block(inode, page);
- block = logfs_block(page);
- block->reserved_bytes += 6 * LOGFS_MAX_OBJECTSIZE;
- super->s_dirty_pages += 6 * LOGFS_MAX_OBJECTSIZE;
- list_move_tail(&block->alias_list, &super->s_writeback_list);
- }
- logfs_put_wblocks(inode->i_sb, page, WF_LOCK);
- return ret;
-}
-
-/*
- * We are protected by write lock. Push victims up to superblock level
- * and release transaction when appropriate.
- */
-/* FIXME: This is currently called from the wrong spots. */
-static void logfs_handle_transaction(struct inode *inode,
- struct logfs_transaction *ta)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
-
- if (!ta)
- return;
- logfs_inode(inode)->li_block->ta = NULL;
-
- if (inode->i_ino != LOGFS_INO_MASTER) {
- BUG(); /* FIXME: Yes, this needs more thought */
- /* just remember the transaction until inode is written */
- //BUG_ON(logfs_inode(inode)->li_transaction);
- //logfs_inode(inode)->li_transaction = ta;
- return;
- }
-
- switch (ta->state) {
- case CREATE_1: /* fall through */
- case UNLINK_1:
- BUG_ON(super->s_victim_ino);
- super->s_victim_ino = ta->ino;
- break;
- case CREATE_2: /* fall through */
- case UNLINK_2:
- BUG_ON(super->s_victim_ino != ta->ino);
- super->s_victim_ino = 0;
- /* transaction ends here - free it */
- kfree(ta);
- break;
- case CROSS_RENAME_1:
- BUG_ON(super->s_rename_dir);
- BUG_ON(super->s_rename_pos);
- super->s_rename_dir = ta->dir;
- super->s_rename_pos = ta->pos;
- break;
- case CROSS_RENAME_2:
- BUG_ON(super->s_rename_dir != ta->dir);
- BUG_ON(super->s_rename_pos != ta->pos);
- super->s_rename_dir = 0;
- super->s_rename_pos = 0;
- kfree(ta);
- break;
- case TARGET_RENAME_1:
- BUG_ON(super->s_rename_dir);
- BUG_ON(super->s_rename_pos);
- BUG_ON(super->s_victim_ino);
- super->s_rename_dir = ta->dir;
- super->s_rename_pos = ta->pos;
- super->s_victim_ino = ta->ino;
- break;
- case TARGET_RENAME_2:
- BUG_ON(super->s_rename_dir != ta->dir);
- BUG_ON(super->s_rename_pos != ta->pos);
- BUG_ON(super->s_victim_ino != ta->ino);
- super->s_rename_dir = 0;
- super->s_rename_pos = 0;
- break;
- case TARGET_RENAME_3:
- BUG_ON(super->s_rename_dir);
- BUG_ON(super->s_rename_pos);
- BUG_ON(super->s_victim_ino != ta->ino);
- super->s_victim_ino = 0;
- kfree(ta);
- break;
- default:
- BUG();
- }
-}
-
-/*
- * Not strictly a reservation, but rather a check that we still have enough
- * space to satisfy the write.
- */
-static int logfs_reserve_blocks(struct inode *inode, int blocks)
-{
- return logfs_reserve_bytes(inode, blocks * LOGFS_MAX_OBJECTSIZE);
-}
-
-struct write_control {
- u64 ofs;
- long flags;
-};
-
-static struct logfs_shadow *alloc_shadow(struct inode *inode, u64 bix,
- level_t level, u64 old_ofs)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- struct logfs_shadow *shadow;
-
- shadow = mempool_alloc(super->s_shadow_pool, GFP_NOFS);
- memset(shadow, 0, sizeof(*shadow));
- shadow->ino = inode->i_ino;
- shadow->bix = bix;
- shadow->gc_level = expand_level(inode->i_ino, level);
- shadow->old_ofs = old_ofs & ~LOGFS_FULLY_POPULATED;
- return shadow;
-}
-
-static void free_shadow(struct inode *inode, struct logfs_shadow *shadow)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
-
- mempool_free(shadow, super->s_shadow_pool);
-}
-
-static void mark_segment(struct shadow_tree *tree, u32 segno)
-{
- int err;
-
- if (!btree_lookup32(&tree->segment_map, segno)) {
- err = btree_insert32(&tree->segment_map, segno, (void *)1,
- GFP_NOFS);
- BUG_ON(err);
- tree->no_shadowed_segments++;
- }
-}
-
-/**
- * fill_shadow_tree - Propagate shadow tree changes due to a write
- * @inode: Inode owning the page
- * @page: Struct page that was written
- * @shadow: Shadow for the current write
- *
- * Writes in logfs can result in two semi-valid objects. The old object
- * is still valid as long as it can be reached by following pointers on
- * the medium. Only when writes propagate all the way up to the journal
- * has the new object safely replaced the old one.
- *
- * To handle this problem, a struct logfs_shadow is used to represent
- * every single write. It is attached to the indirect block, which is
- * marked dirty. When the indirect block is written, its shadows are
- * handed up to the next indirect block (or inode). Untimately they
- * will reach the master inode and be freed upon journal commit.
- *
- * This function handles a single step in the propagation. It adds the
- * shadow for the current write to the tree, along with any shadows in
- * the page's tree, in case it was an indirect block. If a page is
- * written, the inode parameter is left NULL, if an inode is written,
- * the page parameter is left NULL.
- */
-static void fill_shadow_tree(struct inode *inode, struct page *page,
- struct logfs_shadow *shadow)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- struct logfs_block *block = logfs_block(page);
- struct shadow_tree *tree = &super->s_shadow_tree;
-
- if (PagePrivate(page)) {
- if (block->alias_map)
- super->s_no_object_aliases -= bitmap_weight(
- block->alias_map, LOGFS_BLOCK_FACTOR);
- logfs_handle_transaction(inode, block->ta);
- block->ops->free_block(inode->i_sb, block);
- }
- if (shadow) {
- if (shadow->old_ofs)
- btree_insert64(&tree->old, shadow->old_ofs, shadow,
- GFP_NOFS);
- else
- btree_insert64(&tree->new, shadow->new_ofs, shadow,
- GFP_NOFS);
-
- super->s_dirty_used_bytes += shadow->new_len;
- super->s_dirty_free_bytes += shadow->old_len;
- mark_segment(tree, shadow->old_ofs >> super->s_segshift);
- mark_segment(tree, shadow->new_ofs >> super->s_segshift);
- }
-}
-
-static void logfs_set_alias(struct super_block *sb, struct logfs_block *block,
- long child_no)
-{
- struct logfs_super *super = logfs_super(sb);
-
- if (block->inode && block->inode->i_ino == LOGFS_INO_MASTER) {
- /* Aliases in the master inode are pointless. */
- return;
- }
-
- if (!test_bit(child_no, block->alias_map)) {
- set_bit(child_no, block->alias_map);
- super->s_no_object_aliases++;
- }
- list_move_tail(&block->alias_list, &super->s_object_alias);
-}
-
-/*
- * Object aliases can and often do change the size and occupied space of a
- * file. So not only do we have to change the pointers, we also have to
- * change inode->i_size and li->li_used_bytes. Which is done by setting
- * another two object aliases for the inode itself.
- */
-static void set_iused(struct inode *inode, struct logfs_shadow *shadow)
-{
- struct logfs_inode *li = logfs_inode(inode);
-
- if (shadow->new_len == shadow->old_len)
- return;
-
- alloc_inode_block(inode);
- li->li_used_bytes += shadow->new_len - shadow->old_len;
- __logfs_set_blocks(inode);
- logfs_set_alias(inode->i_sb, li->li_block, INODE_USED_OFS);
- logfs_set_alias(inode->i_sb, li->li_block, INODE_SIZE_OFS);
-}
-
-static int logfs_write_i0(struct inode *inode, struct page *page,
- struct write_control *wc)
-{
- struct logfs_shadow *shadow;
- u64 bix;
- level_t level;
- int full, err = 0;
-
- logfs_unpack_index(page->index, &bix, &level);
- if (wc->ofs == 0)
- if (logfs_reserve_blocks(inode, 1))
- return -ENOSPC;
-
- shadow = alloc_shadow(inode, bix, level, wc->ofs);
- if (wc->flags & WF_WRITE)
- err = logfs_segment_write(inode, page, shadow);
- if (wc->flags & WF_DELETE)
- logfs_segment_delete(inode, shadow);
- if (err) {
- free_shadow(inode, shadow);
- return err;
- }
-
- set_iused(inode, shadow);
- full = 1;
- if (level != 0) {
- alloc_indirect_block(inode, page, 0);
- full = logfs_block(page)->full == LOGFS_BLOCK_FACTOR;
- }
- fill_shadow_tree(inode, page, shadow);
- wc->ofs = shadow->new_ofs;
- if (wc->ofs && full)
- wc->ofs |= LOGFS_FULLY_POPULATED;
- return 0;
-}
-
-static int logfs_write_direct(struct inode *inode, struct page *page,
- long flags)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct write_control wc = {
- .ofs = li->li_data[page->index],
- .flags = flags,
- };
- int err;
-
- alloc_inode_block(inode);
-
- err = logfs_write_i0(inode, page, &wc);
- if (err)
- return err;
-
- li->li_data[page->index] = wc.ofs;
- logfs_set_alias(inode->i_sb, li->li_block,
- page->index + INODE_POINTER_OFS);
- return 0;
-}
-
-static int ptr_change(u64 ofs, struct page *page)
-{
- struct logfs_block *block = logfs_block(page);
- int empty0, empty1, full0, full1;
-
- empty0 = ofs == 0;
- empty1 = block->partial == 0;
- if (empty0 != empty1)
- return 1;
-
- /* The !! is necessary to shrink result to int */
- full0 = !!(ofs & LOGFS_FULLY_POPULATED);
- full1 = block->full == LOGFS_BLOCK_FACTOR;
- if (full0 != full1)
- return 1;
- return 0;
-}
-
-static int __logfs_write_rec(struct inode *inode, struct page *page,
- struct write_control *this_wc,
- pgoff_t bix, level_t target_level, level_t level)
-{
- int ret, page_empty = 0;
- int child_no = get_bits(bix, SUBLEVEL(level));
- struct page *ipage;
- struct write_control child_wc = {
- .flags = this_wc->flags,
- };
-
- ipage = logfs_get_write_page(inode, bix, level);
- if (!ipage)
- return -ENOMEM;
-
- if (this_wc->ofs) {
- ret = logfs_segment_read(inode, ipage, this_wc->ofs, bix, level);
- if (ret)
- goto out;
- } else if (!PageUptodate(ipage)) {
- page_empty = 1;
- logfs_read_empty(ipage);
- }
-
- child_wc.ofs = block_get_pointer(ipage, child_no);
-
- if ((__force u8)level-1 > (__force u8)target_level)
- ret = __logfs_write_rec(inode, page, &child_wc, bix,
- target_level, SUBLEVEL(level));
- else
- ret = logfs_write_i0(inode, page, &child_wc);
-
- if (ret)
- goto out;
-
- alloc_indirect_block(inode, ipage, page_empty);
- block_set_pointer(ipage, child_no, child_wc.ofs);
- /* FIXME: first condition seems superfluous */
- if (child_wc.ofs || logfs_block(ipage)->partial)
- this_wc->flags |= WF_WRITE;
- /* the condition on this_wc->ofs ensures that we won't consume extra
- * space for indirect blocks in the future, which we cannot reserve */
- if (!this_wc->ofs || ptr_change(this_wc->ofs, ipage))
- ret = logfs_write_i0(inode, ipage, this_wc);
- else
- logfs_set_alias(inode->i_sb, logfs_block(ipage), child_no);
-out:
- logfs_put_write_page(ipage);
- return ret;
-}
-
-static int logfs_write_rec(struct inode *inode, struct page *page,
- pgoff_t bix, level_t target_level, long flags)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct write_control wc = {
- .ofs = li->li_data[INDIRECT_INDEX],
- .flags = flags,
- };
- int ret;
-
- alloc_inode_block(inode);
-
- if (li->li_height > (__force u8)target_level)
- ret = __logfs_write_rec(inode, page, &wc, bix, target_level,
- LEVEL(li->li_height));
- else
- ret = logfs_write_i0(inode, page, &wc);
- if (ret)
- return ret;
-
- if (li->li_data[INDIRECT_INDEX] != wc.ofs) {
- li->li_data[INDIRECT_INDEX] = wc.ofs;
- logfs_set_alias(inode->i_sb, li->li_block,
- INDIRECT_INDEX + INODE_POINTER_OFS);
- }
- return ret;
-}
-
-void logfs_add_transaction(struct inode *inode, struct logfs_transaction *ta)
-{
- alloc_inode_block(inode);
- logfs_inode(inode)->li_block->ta = ta;
-}
-
-void logfs_del_transaction(struct inode *inode, struct logfs_transaction *ta)
-{
- struct logfs_block *block = logfs_inode(inode)->li_block;
-
- if (block && block->ta)
- block->ta = NULL;
-}
-
-static int grow_inode(struct inode *inode, u64 bix, level_t level)
-{
- struct logfs_inode *li = logfs_inode(inode);
- u8 height = (__force u8)level;
- struct page *page;
- struct write_control wc = {
- .flags = WF_WRITE,
- };
- int err;
-
- BUG_ON(height > 5 || li->li_height > 5);
- while (height > li->li_height || bix >= maxbix(li->li_height)) {
- page = logfs_get_write_page(inode, I0_BLOCKS + 1,
- LEVEL(li->li_height + 1));
- if (!page)
- return -ENOMEM;
- logfs_read_empty(page);
- alloc_indirect_block(inode, page, 1);
- block_set_pointer(page, 0, li->li_data[INDIRECT_INDEX]);
- err = logfs_write_i0(inode, page, &wc);
- logfs_put_write_page(page);
- if (err)
- return err;
- li->li_data[INDIRECT_INDEX] = wc.ofs;
- wc.ofs = 0;
- li->li_height++;
- logfs_set_alias(inode->i_sb, li->li_block, INODE_HEIGHT_OFS);
- }
- return 0;
-}
-
-static int __logfs_write_buf(struct inode *inode, struct page *page, long flags)
-{
- struct logfs_super *super = logfs_super(inode->i_sb);
- pgoff_t index = page->index;
- u64 bix;
- level_t level;
- int err;
-
- flags |= WF_WRITE | WF_DELETE;
- inode->i_ctime = inode->i_mtime = current_time(inode);
-
- logfs_unpack_index(index, &bix, &level);
- if (logfs_block(page) && logfs_block(page)->reserved_bytes)
- super->s_dirty_pages -= logfs_block(page)->reserved_bytes;
-
- if (index < I0_BLOCKS)
- return logfs_write_direct(inode, page, flags);
-
- bix = adjust_bix(bix, level);
- err = grow_inode(inode, bix, level);
- if (err)
- return err;
- return logfs_write_rec(inode, page, bix, level, flags);
-}
-
-int logfs_write_buf(struct inode *inode, struct page *page, long flags)
-{
- struct super_block *sb = inode->i_sb;
- int ret;
-
- logfs_get_wblocks(sb, page, flags & WF_LOCK);
- ret = __logfs_write_buf(inode, page, flags);
- logfs_put_wblocks(sb, page, flags & WF_LOCK);
- return ret;
-}
-
-static int __logfs_delete(struct inode *inode, struct page *page)
-{
- long flags = WF_DELETE;
- int err;
-
- inode->i_ctime = inode->i_mtime = current_time(inode);
-
- if (page->index < I0_BLOCKS)
- return logfs_write_direct(inode, page, flags);
- err = grow_inode(inode, page->index, 0);
- if (err)
- return err;
- return logfs_write_rec(inode, page, page->index, 0, flags);
-}
-
-int logfs_delete(struct inode *inode, pgoff_t index,
- struct shadow_tree *shadow_tree)
-{
- struct super_block *sb = inode->i_sb;
- struct page *page;
- int ret;
-
- page = logfs_get_read_page(inode, index, 0);
- if (!page)
- return -ENOMEM;
-
- logfs_get_wblocks(sb, page, 1);
- ret = __logfs_delete(inode, page);
- logfs_put_wblocks(sb, page, 1);
-
- logfs_put_read_page(page);
-
- return ret;
-}
-
-int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
- gc_level_t gc_level, long flags)
-{
- level_t level = shrink_level(gc_level);
- struct page *page;
- int err;
-
- page = logfs_get_write_page(inode, bix, level);
- if (!page)
- return -ENOMEM;
-
- err = logfs_segment_read(inode, page, ofs, bix, level);
- if (!err) {
- if (level != 0)
- alloc_indirect_block(inode, page, 0);
- err = logfs_write_buf(inode, page, flags);
- if (!err && shrink_level(gc_level) == 0) {
- /* Rewrite cannot mark the inode dirty but has to
- * write it immediately.
- * Q: Can't we just create an alias for the inode
- * instead? And if not, why not?
- */
- if (inode->i_ino == LOGFS_INO_MASTER)
- logfs_write_anchor(inode->i_sb);
- else {
- err = __logfs_write_inode(inode, page, flags);
- }
- }
- }
- logfs_put_write_page(page);
- return err;
-}
-
-static int truncate_data_block(struct inode *inode, struct page *page,
- u64 ofs, struct logfs_shadow *shadow, u64 size)
-{
- loff_t pageofs = page->index << inode->i_sb->s_blocksize_bits;
- u64 bix;
- level_t level;
- int err;
-
- /* Does truncation happen within this page? */
- if (size <= pageofs || size - pageofs >= PAGE_SIZE)
- return 0;
-
- logfs_unpack_index(page->index, &bix, &level);
- BUG_ON(level != 0);
-
- err = logfs_segment_read(inode, page, ofs, bix, level);
- if (err)
- return err;
-
- zero_user_segment(page, size - pageofs, PAGE_SIZE);
- return logfs_segment_write(inode, page, shadow);
-}
-
-static int logfs_truncate_i0(struct inode *inode, struct page *page,
- struct write_control *wc, u64 size)
-{
- struct logfs_shadow *shadow;
- u64 bix;
- level_t level;
- int err = 0;
-
- logfs_unpack_index(page->index, &bix, &level);
- BUG_ON(level != 0);
- shadow = alloc_shadow(inode, bix, level, wc->ofs);
-
- err = truncate_data_block(inode, page, wc->ofs, shadow, size);
- if (err) {
- free_shadow(inode, shadow);
- return err;
- }
-
- logfs_segment_delete(inode, shadow);
- set_iused(inode, shadow);
- fill_shadow_tree(inode, page, shadow);
- wc->ofs = shadow->new_ofs;
- return 0;
-}
-
-static int logfs_truncate_direct(struct inode *inode, u64 size)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct write_control wc;
- struct page *page;
- int e;
- int err;
-
- alloc_inode_block(inode);
-
- for (e = I0_BLOCKS - 1; e >= 0; e--) {
- if (size > (e+1) * LOGFS_BLOCKSIZE)
- break;
-
- wc.ofs = li->li_data[e];
- if (!wc.ofs)
- continue;
-
- page = logfs_get_write_page(inode, e, 0);
- if (!page)
- return -ENOMEM;
- err = logfs_segment_read(inode, page, wc.ofs, e, 0);
- if (err) {
- logfs_put_write_page(page);
- return err;
- }
- err = logfs_truncate_i0(inode, page, &wc, size);
- logfs_put_write_page(page);
- if (err)
- return err;
-
- li->li_data[e] = wc.ofs;
- }
- return 0;
-}
-
-/* FIXME: these need to become per-sb once we support different blocksizes */
-static u64 __logfs_step[] = {
- 1,
- I1_BLOCKS,
- I2_BLOCKS,
- I3_BLOCKS,
-};
-
-static u64 __logfs_start_index[] = {
- I0_BLOCKS,
- I1_BLOCKS,
- I2_BLOCKS,
- I3_BLOCKS
-};
-
-static inline u64 logfs_step(level_t level)
-{
- return __logfs_step[(__force u8)level];
-}
-
-static inline u64 logfs_factor(u8 level)
-{
- return __logfs_step[level] * LOGFS_BLOCKSIZE;
-}
-
-static inline u64 logfs_start_index(level_t level)
-{
- return __logfs_start_index[(__force u8)level];
-}
-
-static void logfs_unpack_raw_index(pgoff_t index, u64 *bix, level_t *level)
-{
- logfs_unpack_index(index, bix, level);
- if (*bix <= logfs_start_index(SUBLEVEL(*level)))
- *bix = 0;
-}
-
-static int __logfs_truncate_rec(struct inode *inode, struct page *ipage,
- struct write_control *this_wc, u64 size)
-{
- int truncate_happened = 0;
- int e, err = 0;
- u64 bix, child_bix, next_bix;
- level_t level;
- struct page *page;
- struct write_control child_wc = { /* FIXME: flags */ };
-
- logfs_unpack_raw_index(ipage->index, &bix, &level);
- err = logfs_segment_read(inode, ipage, this_wc->ofs, bix, level);
- if (err)
- return err;
-
- for (e = LOGFS_BLOCK_FACTOR - 1; e >= 0; e--) {
- child_bix = bix + e * logfs_step(SUBLEVEL(level));
- next_bix = child_bix + logfs_step(SUBLEVEL(level));
- if (size > next_bix * LOGFS_BLOCKSIZE)
- break;
-
- child_wc.ofs = pure_ofs(block_get_pointer(ipage, e));
- if (!child_wc.ofs)
- continue;
-
- page = logfs_get_write_page(inode, child_bix, SUBLEVEL(level));
- if (!page)
- return -ENOMEM;
-
- if ((__force u8)level > 1)
- err = __logfs_truncate_rec(inode, page, &child_wc, size);
- else
- err = logfs_truncate_i0(inode, page, &child_wc, size);
- logfs_put_write_page(page);
- if (err)
- return err;
-
- truncate_happened = 1;
- alloc_indirect_block(inode, ipage, 0);
- block_set_pointer(ipage, e, child_wc.ofs);
- }
-
- if (!truncate_happened) {
- printk("ineffectual truncate (%lx, %lx, %llx)\n", inode->i_ino, ipage->index, size);
- return 0;
- }
-
- this_wc->flags = WF_DELETE;
- if (logfs_block(ipage)->partial)
- this_wc->flags |= WF_WRITE;
-
- return logfs_write_i0(inode, ipage, this_wc);
-}
-
-static int logfs_truncate_rec(struct inode *inode, u64 size)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct write_control wc = {
- .ofs = li->li_data[INDIRECT_INDEX],
- };
- struct page *page;
- int err;
-
- alloc_inode_block(inode);
-
- if (!wc.ofs)
- return 0;
-
- page = logfs_get_write_page(inode, 0, LEVEL(li->li_height));
- if (!page)
- return -ENOMEM;
-
- err = __logfs_truncate_rec(inode, page, &wc, size);
- logfs_put_write_page(page);
- if (err)
- return err;
-
- if (li->li_data[INDIRECT_INDEX] != wc.ofs)
- li->li_data[INDIRECT_INDEX] = wc.ofs;
- return 0;
-}
-
-static int __logfs_truncate(struct inode *inode, u64 size)
-{
- int ret;
-
- if (size >= logfs_factor(logfs_inode(inode)->li_height))
- return 0;
-
- ret = logfs_truncate_rec(inode, size);
- if (ret)
- return ret;
-
- return logfs_truncate_direct(inode, size);
-}
-
-/*
- * Truncate, by changing the segment file, can consume a fair amount
- * of resources. So back off from time to time and do some GC.
- * 8 or 2048 blocks should be well within safety limits even if
- * every single block resided in a different segment.
- */
-#define TRUNCATE_STEP (8 * 1024 * 1024)
-int logfs_truncate(struct inode *inode, u64 target)
-{
- struct super_block *sb = inode->i_sb;
- u64 size = i_size_read(inode);
- int err = 0;
-
- size = ALIGN(size, TRUNCATE_STEP);
- while (size > target) {
- if (size > TRUNCATE_STEP)
- size -= TRUNCATE_STEP;
- else
- size = 0;
- if (size < target)
- size = target;
-
- logfs_get_wblocks(sb, NULL, 1);
- err = __logfs_truncate(inode, size);
- if (!err)
- err = __logfs_write_inode(inode, NULL, 0);
- logfs_put_wblocks(sb, NULL, 1);
- }
-
- if (!err) {
- err = inode_newsize_ok(inode, target);
- if (err)
- goto out;
-
- truncate_setsize(inode, target);
- }
-
- out:
- /* I don't trust error recovery yet. */
- WARN_ON(err);
- return err;
-}
-
-static void move_page_to_inode(struct inode *inode, struct page *page)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_block *block = logfs_block(page);
-
- if (!block)
- return;
-
- log_blockmove("move_page_to_inode(%llx, %llx, %x)\n",
- block->ino, block->bix, block->level);
- BUG_ON(li->li_block);
- block->ops = &inode_block_ops;
- block->inode = inode;
- li->li_block = block;
-
- block->page = NULL;
- if (PagePrivate(page)) {
- ClearPagePrivate(page);
- put_page(page);
- set_page_private(page, 0);
- }
-}
-
-static void move_inode_to_page(struct page *page, struct inode *inode)
-{
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_block *block = li->li_block;
-
- if (!block)
- return;
-
- log_blockmove("move_inode_to_page(%llx, %llx, %x)\n",
- block->ino, block->bix, block->level);
- BUG_ON(PagePrivate(page));
- block->ops = &indirect_block_ops;
- block->page = page;
-
- if (!PagePrivate(page)) {
- SetPagePrivate(page);
- get_page(page);
- set_page_private(page, (unsigned long) block);
- }
-
- block->inode = NULL;
- li->li_block = NULL;
-}
-
-int logfs_read_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_super *super = logfs_super(sb);
- struct inode *master_inode = super->s_master_inode;
- struct page *page;
- struct logfs_disk_inode *di;
- u64 ino = inode->i_ino;
-
- if (ino << sb->s_blocksize_bits > i_size_read(master_inode))
- return -ENODATA;
- if (!logfs_exist_block(master_inode, ino))
- return -ENODATA;
-
- page = read_cache_page(master_inode->i_mapping, ino,
- (filler_t *)logfs_readpage, NULL);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- di = kmap_atomic(page);
- logfs_disk_to_inode(di, inode);
- kunmap_atomic(di);
- move_page_to_inode(inode, page);
- put_page(page);
- return 0;
-}
-
-/* Caller must logfs_put_write_page(page); */
-static struct page *inode_to_page(struct inode *inode)
-{
- struct inode *master_inode = logfs_super(inode->i_sb)->s_master_inode;
- struct logfs_disk_inode *di;
- struct page *page;
-
- BUG_ON(inode->i_ino == LOGFS_INO_MASTER);
-
- page = logfs_get_write_page(master_inode, inode->i_ino, 0);
- if (!page)
- return NULL;
-
- di = kmap_atomic(page);
- logfs_inode_to_disk(inode, di);
- kunmap_atomic(di);
- move_inode_to_page(page, inode);
- return page;
-}
-
-static int do_write_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct inode *master_inode = logfs_super(sb)->s_master_inode;
- loff_t size = (inode->i_ino + 1) << inode->i_sb->s_blocksize_bits;
- struct page *page;
- int err;
-
- BUG_ON(inode->i_ino == LOGFS_INO_MASTER);
- /* FIXME: lock inode */
-
- if (i_size_read(master_inode) < size)
- i_size_write(master_inode, size);
-
- /* TODO: Tell vfs this inode is clean now */
-
- page = inode_to_page(inode);
- if (!page)
- return -ENOMEM;
-
- /* FIXME: transaction is part of logfs_block now. Is that enough? */
- err = logfs_write_buf(master_inode, page, 0);
- if (err)
- move_page_to_inode(inode, page);
-
- logfs_put_write_page(page);
- return err;
-}
-
-static void logfs_mod_segment_entry(struct super_block *sb, u32 segno,
- int write,
- void (*change_se)(struct logfs_segment_entry *, long),
- long arg)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode;
- struct page *page;
- struct logfs_segment_entry *se;
- pgoff_t page_no;
- int child_no;
-
- page_no = segno >> (sb->s_blocksize_bits - 3);
- child_no = segno & ((sb->s_blocksize >> 3) - 1);
-
- inode = super->s_segfile_inode;
- page = logfs_get_write_page(inode, page_no, 0);
- BUG_ON(!page); /* FIXME: We need some reserve page for this case */
- if (!PageUptodate(page))
- logfs_read_block(inode, page, WRITE);
-
- if (write)
- alloc_indirect_block(inode, page, 0);
- se = kmap_atomic(page);
- change_se(se + child_no, arg);
- if (write) {
- logfs_set_alias(sb, logfs_block(page), child_no);
- BUG_ON((int)be32_to_cpu(se[child_no].valid) > super->s_segsize);
- }
- kunmap_atomic(se);
-
- logfs_put_write_page(page);
-}
-
-static void __get_segment_entry(struct logfs_segment_entry *se, long _target)
-{
- struct logfs_segment_entry *target = (void *)_target;
-
- *target = *se;
-}
-
-void logfs_get_segment_entry(struct super_block *sb, u32 segno,
- struct logfs_segment_entry *se)
-{
- logfs_mod_segment_entry(sb, segno, 0, __get_segment_entry, (long)se);
-}
-
-static void __set_segment_used(struct logfs_segment_entry *se, long increment)
-{
- u32 valid;
-
- valid = be32_to_cpu(se->valid);
- valid += increment;
- se->valid = cpu_to_be32(valid);
-}
-
-void logfs_set_segment_used(struct super_block *sb, u64 ofs, int increment)
-{
- struct logfs_super *super = logfs_super(sb);
- u32 segno = ofs >> super->s_segshift;
-
- if (!increment)
- return;
-
- logfs_mod_segment_entry(sb, segno, 1, __set_segment_used, increment);
-}
-
-static void __set_segment_erased(struct logfs_segment_entry *se, long ec_level)
-{
- se->ec_level = cpu_to_be32(ec_level);
-}
-
-void logfs_set_segment_erased(struct super_block *sb, u32 segno, u32 ec,
- gc_level_t gc_level)
-{
- u32 ec_level = ec << 4 | (__force u8)gc_level;
-
- logfs_mod_segment_entry(sb, segno, 1, __set_segment_erased, ec_level);
-}
-
-static void __set_segment_reserved(struct logfs_segment_entry *se, long ignore)
-{
- se->valid = cpu_to_be32(RESERVED);
-}
-
-void logfs_set_segment_reserved(struct super_block *sb, u32 segno)
-{
- logfs_mod_segment_entry(sb, segno, 1, __set_segment_reserved, 0);
-}
-
-static void __set_segment_unreserved(struct logfs_segment_entry *se,
- long ec_level)
-{
- se->valid = 0;
- se->ec_level = cpu_to_be32(ec_level);
-}
-
-void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec)
-{
- u32 ec_level = ec << 4;
-
- logfs_mod_segment_entry(sb, segno, 1, __set_segment_unreserved,
- ec_level);
-}
-
-int __logfs_write_inode(struct inode *inode, struct page *page, long flags)
-{
- struct super_block *sb = inode->i_sb;
- int ret;
-
- logfs_get_wblocks(sb, page, flags & WF_LOCK);
- ret = do_write_inode(inode);
- logfs_put_wblocks(sb, page, flags & WF_LOCK);
- return ret;
-}
-
-static int do_delete_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct inode *master_inode = logfs_super(sb)->s_master_inode;
- struct page *page;
- int ret;
-
- page = logfs_get_write_page(master_inode, inode->i_ino, 0);
- if (!page)
- return -ENOMEM;
-
- move_inode_to_page(page, inode);
-
- logfs_get_wblocks(sb, page, 1);
- ret = __logfs_delete(master_inode, page);
- logfs_put_wblocks(sb, page, 1);
-
- logfs_put_write_page(page);
- return ret;
-}
-
-/*
- * ZOMBIE inodes have already been deleted before and should remain dead,
- * if it weren't for valid checking. No need to kill them again here.
- */
-void logfs_evict_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_block *block = li->li_block;
- struct page *page;
-
- if (!inode->i_nlink) {
- if (!(li->li_flags & LOGFS_IF_ZOMBIE)) {
- li->li_flags |= LOGFS_IF_ZOMBIE;
- if (i_size_read(inode) > 0)
- logfs_truncate(inode, 0);
- do_delete_inode(inode);
- }
- }
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
-
- /* Cheaper version of write_inode. All changes are concealed in
- * aliases, which are moved back. No write to the medium happens.
- */
- /* Only deleted files may be dirty at this point */
- BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink);
- if (!block)
- return;
- if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) {
- block->ops->free_block(inode->i_sb, block);
- return;
- }
-
- page = inode_to_page(inode);
- BUG_ON(!page); /* FIXME: Use emergency page */
- logfs_put_write_page(page);
-}
-
-void btree_write_block(struct logfs_block *block)
-{
- struct inode *inode;
- struct page *page;
- int err, cookie;
-
- inode = logfs_safe_iget(block->sb, block->ino, &cookie);
- page = logfs_get_write_page(inode, block->bix, block->level);
-
- err = logfs_readpage_nolock(page);
- BUG_ON(err);
- BUG_ON(!PagePrivate(page));
- BUG_ON(logfs_block(page) != block);
- err = __logfs_write_buf(inode, page, 0);
- BUG_ON(err);
- BUG_ON(PagePrivate(page) || page->private);
-
- logfs_put_write_page(page);
- logfs_safe_iput(inode, cookie);
-}
-
-/**
- * logfs_inode_write - write inode or dentry objects
- *
- * @inode: parent inode (ifile or directory)
- * @buf: object to write (inode or dentry)
- * @count: object size
- * @bix: block index
- * @flags: write flags
- * @shadow_tree: shadow below this inode
- *
- * FIXME: All caller of this put a 200-300 byte variable on the stack,
- * only to call here and do a memcpy from that stack variable. A good
- * example of wasted performance and stack space.
- */
-int logfs_inode_write(struct inode *inode, const void *buf, size_t count,
- loff_t bix, long flags, struct shadow_tree *shadow_tree)
-{
- loff_t pos = bix << inode->i_sb->s_blocksize_bits;
- int err;
- struct page *page;
- void *pagebuf;
-
- BUG_ON(pos & (LOGFS_BLOCKSIZE-1));
- BUG_ON(count > LOGFS_BLOCKSIZE);
- page = logfs_get_write_page(inode, bix, 0);
- if (!page)
- return -ENOMEM;
-
- pagebuf = kmap_atomic(page);
- memcpy(pagebuf, buf, count);
- flush_dcache_page(page);
- kunmap_atomic(pagebuf);
-
- if (i_size_read(inode) < pos + LOGFS_BLOCKSIZE)
- i_size_write(inode, pos + LOGFS_BLOCKSIZE);
-
- err = logfs_write_buf(inode, page, flags);
- logfs_put_write_page(page);
- return err;
-}
-
-int logfs_open_segfile(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *inode;
-
- inode = logfs_read_meta_inode(sb, LOGFS_INO_SEGFILE);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
- super->s_segfile_inode = inode;
- return 0;
-}
-
-int logfs_init_rw(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int min_fill = 3 * super->s_no_blocks;
-
- INIT_LIST_HEAD(&super->s_object_alias);
- INIT_LIST_HEAD(&super->s_writeback_list);
- mutex_init(&super->s_write_mutex);
- super->s_block_pool = mempool_create_kmalloc_pool(min_fill,
- sizeof(struct logfs_block));
- super->s_shadow_pool = mempool_create_kmalloc_pool(min_fill,
- sizeof(struct logfs_shadow));
- return 0;
-}
-
-void logfs_cleanup_rw(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- logfs_mempool_destroy(super->s_block_pool);
- logfs_mempool_destroy(super->s_shadow_pool);
-}
+++ /dev/null
-/*
- * fs/logfs/segment.c - Handling the Object Store
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- *
- * Object store or ostore makes up the complete device with exception of
- * the superblock and journal areas. Apart from its own metadata it stores
- * three kinds of objects: inodes, dentries and blocks, both data and indirect.
- */
-#include "logfs.h"
-#include <linux/slab.h>
-
-static int logfs_mark_segment_bad(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct btree_head32 *head = &super->s_reserved_segments;
- int err;
-
- err = btree_insert32(head, segno, (void *)1, GFP_NOFS);
- if (err)
- return err;
- logfs_super(sb)->s_bad_segments++;
- /* FIXME: write to journal */
- return 0;
-}
-
-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, ensure_erase);
-}
-
-static s64 logfs_get_free_bytes(struct logfs_area *area, size_t bytes)
-{
- s32 ofs;
-
- logfs_open_area(area, bytes);
-
- ofs = area->a_used_bytes;
- area->a_used_bytes += bytes;
- BUG_ON(area->a_used_bytes >= logfs_super(area->a_sb)->s_segsize);
-
- return dev_ofs(area->a_sb, area->a_segno, ofs);
-}
-
-static struct page *get_mapping_page(struct super_block *sb, pgoff_t index,
- int use_filler)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- filler_t *filler = super->s_devops->readpage;
- struct page *page;
-
- BUG_ON(mapping_gfp_constraint(mapping, __GFP_FS));
- if (use_filler)
- page = read_cache_page(mapping, index, filler, sb);
- else {
- page = find_or_create_page(mapping, index, GFP_NOFS);
- if (page)
- unlock_page(page);
- }
- return page;
-}
-
-int __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
- int use_filler)
-{
- pgoff_t index = ofs >> PAGE_SHIFT;
- struct page *page;
- long offset = ofs & (PAGE_SIZE-1);
- long copylen;
-
- /* Only logfs_wbuf_recover may use len==0 */
- BUG_ON(!len && !use_filler);
- do {
- copylen = min((ulong)len, PAGE_SIZE - offset);
-
- page = get_mapping_page(area->a_sb, index, use_filler);
- if (IS_ERR(page))
- return PTR_ERR(page);
- BUG_ON(!page); /* FIXME: reserve a pool */
- SetPageUptodate(page);
- memcpy(page_address(page) + offset, buf, copylen);
-
- if (!PagePrivate(page)) {
- SetPagePrivate(page);
- get_page(page);
- }
- put_page(page);
-
- buf += copylen;
- len -= copylen;
- offset = 0;
- index++;
- } while (len);
- return 0;
-}
-
-static void pad_partial_page(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- struct page *page;
- u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
- pgoff_t index = ofs >> PAGE_SHIFT;
- long offset = ofs & (PAGE_SIZE-1);
- u32 len = PAGE_SIZE - offset;
-
- if (len % PAGE_SIZE) {
- page = get_mapping_page(sb, index, 0);
- BUG_ON(!page); /* FIXME: reserve a pool */
- memset(page_address(page) + offset, 0xff, len);
- if (!PagePrivate(page)) {
- SetPagePrivate(page);
- get_page(page);
- }
- put_page(page);
- }
-}
-
-static void pad_full_pages(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- struct logfs_super *super = logfs_super(sb);
- u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
- u32 len = super->s_segsize - area->a_used_bytes;
- pgoff_t index = PAGE_ALIGN(ofs) >> PAGE_SHIFT;
- pgoff_t no_indizes = len >> PAGE_SHIFT;
- struct page *page;
-
- while (no_indizes) {
- page = get_mapping_page(sb, index, 0);
- BUG_ON(!page); /* FIXME: reserve a pool */
- SetPageUptodate(page);
- memset(page_address(page), 0xff, PAGE_SIZE);
- if (!PagePrivate(page)) {
- SetPagePrivate(page);
- get_page(page);
- }
- put_page(page);
- index++;
- no_indizes--;
- }
-}
-
-/*
- * bdev_writeseg will write full pages. Memset the tail to prevent data leaks.
- * Also make sure we allocate (and memset) all pages for final writeout.
- */
-static void pad_wbuf(struct logfs_area *area, int final)
-{
- pad_partial_page(area);
- if (final)
- pad_full_pages(area);
-}
-
-/*
- * We have to be careful with the alias tree. Since lookup is done by bix,
- * it needs to be normalized, so 14, 15, 16, etc. all match when dealing with
- * indirect blocks. So always use it through accessor functions.
- */
-static void *alias_tree_lookup(struct super_block *sb, u64 ino, u64 bix,
- level_t level)
-{
- struct btree_head128 *head = &logfs_super(sb)->s_object_alias_tree;
- pgoff_t index = logfs_pack_index(bix, level);
-
- return btree_lookup128(head, ino, index);
-}
-
-static int alias_tree_insert(struct super_block *sb, u64 ino, u64 bix,
- level_t level, void *val)
-{
- struct btree_head128 *head = &logfs_super(sb)->s_object_alias_tree;
- pgoff_t index = logfs_pack_index(bix, level);
-
- return btree_insert128(head, ino, index, val, GFP_NOFS);
-}
-
-static int btree_write_alias(struct super_block *sb, struct logfs_block *block,
- write_alias_t *write_one_alias)
-{
- struct object_alias_item *item;
- int err;
-
- list_for_each_entry(item, &block->item_list, list) {
- err = write_alias_journal(sb, block->ino, block->bix,
- block->level, item->child_no, item->val);
- if (err)
- return err;
- }
- return 0;
-}
-
-static const struct logfs_block_ops btree_block_ops = {
- .write_block = btree_write_block,
- .free_block = __free_block,
- .write_alias = btree_write_alias,
-};
-
-int logfs_load_object_aliases(struct super_block *sb,
- struct logfs_obj_alias *oa, int count)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_block *block;
- struct object_alias_item *item;
- u64 ino, bix;
- level_t level;
- int i, err;
-
- super->s_flags |= LOGFS_SB_FLAG_OBJ_ALIAS;
- count /= sizeof(*oa);
- for (i = 0; i < count; i++) {
- item = mempool_alloc(super->s_alias_pool, GFP_NOFS);
- if (!item)
- return -ENOMEM;
- memset(item, 0, sizeof(*item));
-
- super->s_no_object_aliases++;
- item->val = oa[i].val;
- item->child_no = be16_to_cpu(oa[i].child_no);
-
- ino = be64_to_cpu(oa[i].ino);
- bix = be64_to_cpu(oa[i].bix);
- level = LEVEL(oa[i].level);
-
- log_aliases("logfs_load_object_aliases(%llx, %llx, %x, %x) %llx\n",
- ino, bix, level, item->child_no,
- be64_to_cpu(item->val));
- block = alias_tree_lookup(sb, ino, bix, level);
- if (!block) {
- block = __alloc_block(sb, ino, bix, level);
- block->ops = &btree_block_ops;
- err = alias_tree_insert(sb, ino, bix, level, block);
- BUG_ON(err); /* mempool empty */
- }
- if (test_and_set_bit(item->child_no, block->alias_map)) {
- printk(KERN_ERR"LogFS: Alias collision detected\n");
- return -EIO;
- }
- list_move_tail(&block->alias_list, &super->s_object_alias);
- list_add(&item->list, &block->item_list);
- }
- return 0;
-}
-
-static void kill_alias(void *_block, unsigned long ignore0,
- u64 ignore1, u64 ignore2, size_t ignore3)
-{
- struct logfs_block *block = _block;
- struct super_block *sb = block->sb;
- struct logfs_super *super = logfs_super(sb);
- struct object_alias_item *item;
-
- while (!list_empty(&block->item_list)) {
- item = list_entry(block->item_list.next, typeof(*item), list);
- list_del(&item->list);
- mempool_free(item, super->s_alias_pool);
- }
- block->ops->free_block(sb, block);
-}
-
-static int obj_type(struct inode *inode, level_t level)
-{
- if (level == 0) {
- if (S_ISDIR(inode->i_mode))
- return OBJ_DENTRY;
- if (inode->i_ino == LOGFS_INO_MASTER)
- return OBJ_INODE;
- }
- return OBJ_BLOCK;
-}
-
-static int obj_len(struct super_block *sb, int obj_type)
-{
- switch (obj_type) {
- case OBJ_DENTRY:
- return sizeof(struct logfs_disk_dentry);
- case OBJ_INODE:
- return sizeof(struct logfs_disk_inode);
- case OBJ_BLOCK:
- return sb->s_blocksize;
- default:
- BUG();
- }
-}
-
-static int __logfs_segment_write(struct inode *inode, void *buf,
- struct logfs_shadow *shadow, int type, int len, int compr)
-{
- struct logfs_area *area;
- struct super_block *sb = inode->i_sb;
- s64 ofs;
- struct logfs_object_header h;
- int acc_len;
-
- if (shadow->gc_level == 0)
- acc_len = len;
- else
- acc_len = obj_len(sb, type);
-
- area = get_area(sb, shadow->gc_level);
- ofs = logfs_get_free_bytes(area, len + LOGFS_OBJECT_HEADERSIZE);
- LOGFS_BUG_ON(ofs <= 0, sb);
- /*
- * Order is important. logfs_get_free_bytes(), by modifying the
- * segment file, may modify the content of the very page we're about
- * to write now. Which is fine, as long as the calculated crc and
- * written data still match. So do the modifications _before_
- * calculating the crc.
- */
-
- h.len = cpu_to_be16(len);
- h.type = type;
- h.compr = compr;
- h.ino = cpu_to_be64(inode->i_ino);
- h.bix = cpu_to_be64(shadow->bix);
- h.crc = logfs_crc32(&h, sizeof(h) - 4, 4);
- h.data_crc = logfs_crc32(buf, len, 0);
-
- logfs_buf_write(area, ofs, &h, sizeof(h));
- logfs_buf_write(area, ofs + LOGFS_OBJECT_HEADERSIZE, buf, len);
-
- shadow->new_ofs = ofs;
- shadow->new_len = acc_len + LOGFS_OBJECT_HEADERSIZE;
-
- return 0;
-}
-
-static s64 logfs_segment_write_compress(struct inode *inode, void *buf,
- struct logfs_shadow *shadow, int type, int len)
-{
- struct super_block *sb = inode->i_sb;
- void *compressor_buf = logfs_super(sb)->s_compressed_je;
- ssize_t compr_len;
- int ret;
-
- mutex_lock(&logfs_super(sb)->s_journal_mutex);
- compr_len = logfs_compress(buf, compressor_buf, len, len);
-
- if (compr_len >= 0) {
- ret = __logfs_segment_write(inode, compressor_buf, shadow,
- type, compr_len, COMPR_ZLIB);
- } else {
- ret = __logfs_segment_write(inode, buf, shadow, type, len,
- COMPR_NONE);
- }
- mutex_unlock(&logfs_super(sb)->s_journal_mutex);
- return ret;
-}
-
-/**
- * logfs_segment_write - write data block to object store
- * @inode: inode containing data
- *
- * Returns an errno or zero.
- */
-int logfs_segment_write(struct inode *inode, struct page *page,
- struct logfs_shadow *shadow)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_super *super = logfs_super(sb);
- int do_compress, type, len;
- int ret;
- void *buf;
-
- super->s_flags |= LOGFS_SB_FLAG_DIRTY;
- BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
- do_compress = logfs_inode(inode)->li_flags & LOGFS_IF_COMPRESSED;
- if (shadow->gc_level != 0) {
- /* temporarily disable compression for indirect blocks */
- do_compress = 0;
- }
-
- type = obj_type(inode, shrink_level(shadow->gc_level));
- len = obj_len(sb, type);
- buf = kmap(page);
- if (do_compress)
- ret = logfs_segment_write_compress(inode, buf, shadow, type,
- len);
- else
- ret = __logfs_segment_write(inode, buf, shadow, type, len,
- COMPR_NONE);
- kunmap(page);
-
- log_segment("logfs_segment_write(%llx, %llx, %x) %llx->%llx %x->%x\n",
- shadow->ino, shadow->bix, shadow->gc_level,
- shadow->old_ofs, shadow->new_ofs,
- shadow->old_len, shadow->new_len);
- /* this BUG_ON did catch a locking bug. useful */
- BUG_ON(!(shadow->new_ofs & (super->s_segsize - 1)));
- return ret;
-}
-
-int wbuf_read(struct super_block *sb, u64 ofs, size_t len, void *buf)
-{
- pgoff_t index = ofs >> PAGE_SHIFT;
- struct page *page;
- long offset = ofs & (PAGE_SIZE-1);
- long copylen;
-
- while (len) {
- copylen = min((ulong)len, PAGE_SIZE - offset);
-
- page = get_mapping_page(sb, index, 1);
- if (IS_ERR(page))
- return PTR_ERR(page);
- memcpy(buf, page_address(page) + offset, copylen);
- put_page(page);
-
- buf += copylen;
- len -= copylen;
- offset = 0;
- index++;
- }
- return 0;
-}
-
-/*
- * The "position" of indirect blocks is ambiguous. It can be the position
- * of any data block somewhere behind this indirect block. So we need to
- * normalize the positions through logfs_block_mask() before comparing.
- */
-static int check_pos(struct super_block *sb, u64 pos1, u64 pos2, level_t level)
-{
- return (pos1 & logfs_block_mask(sb, level)) !=
- (pos2 & logfs_block_mask(sb, level));
-}
-
-#if 0
-static int read_seg_header(struct super_block *sb, u64 ofs,
- struct logfs_segment_header *sh)
-{
- __be32 crc;
- int err;
-
- err = wbuf_read(sb, ofs, sizeof(*sh), sh);
- if (err)
- return err;
- crc = logfs_crc32(sh, sizeof(*sh), 4);
- if (crc != sh->crc) {
- printk(KERN_ERR"LOGFS: header crc error at %llx: expected %x, "
- "got %x\n", ofs, be32_to_cpu(sh->crc),
- be32_to_cpu(crc));
- return -EIO;
- }
- return 0;
-}
-#endif
-
-static int read_obj_header(struct super_block *sb, u64 ofs,
- struct logfs_object_header *oh)
-{
- __be32 crc;
- int err;
-
- err = wbuf_read(sb, ofs, sizeof(*oh), oh);
- if (err)
- return err;
- crc = logfs_crc32(oh, sizeof(*oh) - 4, 4);
- if (crc != oh->crc) {
- printk(KERN_ERR"LOGFS: header crc error at %llx: expected %x, "
- "got %x\n", ofs, be32_to_cpu(oh->crc),
- be32_to_cpu(crc));
- return -EIO;
- }
- return 0;
-}
-
-static void move_btree_to_page(struct inode *inode, struct page *page,
- __be64 *data)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_super *super = logfs_super(sb);
- struct btree_head128 *head = &super->s_object_alias_tree;
- struct logfs_block *block;
- struct object_alias_item *item, *next;
-
- if (!(super->s_flags & LOGFS_SB_FLAG_OBJ_ALIAS))
- return;
-
- block = btree_remove128(head, inode->i_ino, page->index);
- if (!block)
- return;
-
- log_blockmove("move_btree_to_page(%llx, %llx, %x)\n",
- block->ino, block->bix, block->level);
- list_for_each_entry_safe(item, next, &block->item_list, list) {
- data[item->child_no] = item->val;
- list_del(&item->list);
- mempool_free(item, super->s_alias_pool);
- }
- block->page = page;
-
- if (!PagePrivate(page)) {
- SetPagePrivate(page);
- get_page(page);
- set_page_private(page, (unsigned long) block);
- }
- block->ops = &indirect_block_ops;
- initialize_block_counters(page, block, data, 0);
-}
-
-/*
- * This silences a false, yet annoying gcc warning. I hate it when my editor
- * jumps into bitops.h each time I recompile this file.
- * TODO: Complain to gcc folks about this and upgrade compiler.
- */
-static unsigned long fnb(const unsigned long *addr,
- unsigned long size, unsigned long offset)
-{
- return find_next_bit(addr, size, offset);
-}
-
-void move_page_to_btree(struct page *page)
-{
- struct logfs_block *block = logfs_block(page);
- struct super_block *sb = block->sb;
- struct logfs_super *super = logfs_super(sb);
- struct object_alias_item *item;
- unsigned long pos;
- __be64 *child;
- int err;
-
- if (super->s_flags & LOGFS_SB_FLAG_SHUTDOWN) {
- block->ops->free_block(sb, block);
- return;
- }
- log_blockmove("move_page_to_btree(%llx, %llx, %x)\n",
- block->ino, block->bix, block->level);
- super->s_flags |= LOGFS_SB_FLAG_OBJ_ALIAS;
-
- for (pos = 0; ; pos++) {
- pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
- if (pos >= LOGFS_BLOCK_FACTOR)
- break;
-
- item = mempool_alloc(super->s_alias_pool, GFP_NOFS);
- BUG_ON(!item); /* mempool empty */
- memset(item, 0, sizeof(*item));
-
- child = kmap_atomic(page);
- item->val = child[pos];
- kunmap_atomic(child);
- item->child_no = pos;
- list_add(&item->list, &block->item_list);
- }
- block->page = NULL;
-
- if (PagePrivate(page)) {
- ClearPagePrivate(page);
- put_page(page);
- set_page_private(page, 0);
- }
- block->ops = &btree_block_ops;
- err = alias_tree_insert(block->sb, block->ino, block->bix, block->level,
- block);
- BUG_ON(err); /* mempool empty */
- ClearPageUptodate(page);
-}
-
-static int __logfs_segment_read(struct inode *inode, void *buf,
- u64 ofs, u64 bix, level_t level)
-{
- struct super_block *sb = inode->i_sb;
- void *compressor_buf = logfs_super(sb)->s_compressed_je;
- struct logfs_object_header oh;
- __be32 crc;
- u16 len;
- int err, block_len;
-
- block_len = obj_len(sb, obj_type(inode, level));
- err = read_obj_header(sb, ofs, &oh);
- if (err)
- goto out_err;
-
- err = -EIO;
- if (be64_to_cpu(oh.ino) != inode->i_ino
- || check_pos(sb, be64_to_cpu(oh.bix), bix, level)) {
- printk(KERN_ERR"LOGFS: (ino, bix) don't match at %llx: "
- "expected (%lx, %llx), got (%llx, %llx)\n",
- ofs, inode->i_ino, bix,
- be64_to_cpu(oh.ino), be64_to_cpu(oh.bix));
- goto out_err;
- }
-
- len = be16_to_cpu(oh.len);
-
- switch (oh.compr) {
- case COMPR_NONE:
- err = wbuf_read(sb, ofs + LOGFS_OBJECT_HEADERSIZE, len, buf);
- if (err)
- goto out_err;
- crc = logfs_crc32(buf, len, 0);
- if (crc != oh.data_crc) {
- printk(KERN_ERR"LOGFS: uncompressed data crc error at "
- "%llx: expected %x, got %x\n", ofs,
- be32_to_cpu(oh.data_crc),
- be32_to_cpu(crc));
- goto out_err;
- }
- break;
- case COMPR_ZLIB:
- mutex_lock(&logfs_super(sb)->s_journal_mutex);
- err = wbuf_read(sb, ofs + LOGFS_OBJECT_HEADERSIZE, len,
- compressor_buf);
- if (err) {
- mutex_unlock(&logfs_super(sb)->s_journal_mutex);
- goto out_err;
- }
- crc = logfs_crc32(compressor_buf, len, 0);
- if (crc != oh.data_crc) {
- printk(KERN_ERR"LOGFS: compressed data crc error at "
- "%llx: expected %x, got %x\n", ofs,
- be32_to_cpu(oh.data_crc),
- be32_to_cpu(crc));
- mutex_unlock(&logfs_super(sb)->s_journal_mutex);
- goto out_err;
- }
- err = logfs_uncompress(compressor_buf, buf, len, block_len);
- mutex_unlock(&logfs_super(sb)->s_journal_mutex);
- if (err) {
- printk(KERN_ERR"LOGFS: uncompress error at %llx\n", ofs);
- goto out_err;
- }
- break;
- default:
- LOGFS_BUG(sb);
- err = -EIO;
- goto out_err;
- }
- return 0;
-
-out_err:
- logfs_set_ro(sb);
- printk(KERN_ERR"LOGFS: device is read-only now\n");
- LOGFS_BUG(sb);
- return err;
-}
-
-/**
- * logfs_segment_read - read data block from object store
- * @inode: inode containing data
- * @buf: data buffer
- * @ofs: physical data offset
- * @bix: block index
- * @level: block level
- *
- * Returns 0 on success or a negative errno.
- */
-int logfs_segment_read(struct inode *inode, struct page *page,
- u64 ofs, u64 bix, level_t level)
-{
- int err;
- void *buf;
-
- if (PageUptodate(page))
- return 0;
-
- ofs &= ~LOGFS_FULLY_POPULATED;
-
- buf = kmap(page);
- err = __logfs_segment_read(inode, buf, ofs, bix, level);
- if (!err) {
- move_btree_to_page(inode, page, buf);
- SetPageUptodate(page);
- }
- kunmap(page);
- log_segment("logfs_segment_read(%lx, %llx, %x) %llx (%d)\n",
- inode->i_ino, bix, level, ofs, err);
- return err;
-}
-
-int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_super *super = logfs_super(sb);
- struct logfs_object_header h;
- u16 len;
- int err;
-
- super->s_flags |= LOGFS_SB_FLAG_DIRTY;
- BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
- BUG_ON(shadow->old_ofs & LOGFS_FULLY_POPULATED);
- if (!shadow->old_ofs)
- return 0;
-
- log_segment("logfs_segment_delete(%llx, %llx, %x) %llx->%llx %x->%x\n",
- shadow->ino, shadow->bix, shadow->gc_level,
- shadow->old_ofs, shadow->new_ofs,
- shadow->old_len, shadow->new_len);
- err = read_obj_header(sb, shadow->old_ofs, &h);
- LOGFS_BUG_ON(err, sb);
- LOGFS_BUG_ON(be64_to_cpu(h.ino) != inode->i_ino, sb);
- LOGFS_BUG_ON(check_pos(sb, shadow->bix, be64_to_cpu(h.bix),
- shrink_level(shadow->gc_level)), sb);
-
- if (shadow->gc_level == 0)
- len = be16_to_cpu(h.len);
- else
- len = obj_len(sb, h.type);
- shadow->old_len = len + sizeof(h);
- return 0;
-}
-
-void freeseg(struct super_block *sb, u32 segno)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping = super->s_mapping_inode->i_mapping;
- struct page *page;
- u64 ofs, start, end;
-
- start = dev_ofs(sb, segno, 0);
- end = dev_ofs(sb, segno + 1, 0);
- for (ofs = start; ofs < end; ofs += PAGE_SIZE) {
- page = find_get_page(mapping, ofs >> PAGE_SHIFT);
- if (!page)
- continue;
- if (PagePrivate(page)) {
- ClearPagePrivate(page);
- put_page(page);
- }
- put_page(page);
- }
-}
-
-int logfs_open_area(struct logfs_area *area, size_t bytes)
-{
- struct super_block *sb = area->a_sb;
- struct logfs_super *super = logfs_super(sb);
- int err, closed = 0;
-
- if (area->a_is_open && area->a_used_bytes + bytes <= super->s_segsize)
- return 0;
-
- if (area->a_is_open) {
- u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
- u32 len = super->s_segsize - area->a_written_bytes;
-
- log_gc("logfs_close_area(%x)\n", area->a_segno);
- pad_wbuf(area, 1);
- super->s_devops->writeseg(area->a_sb, ofs, len);
- freeseg(sb, area->a_segno);
- closed = 1;
- }
-
- area->a_used_bytes = 0;
- area->a_written_bytes = 0;
-again:
- area->a_ops->get_free_segment(area);
- area->a_ops->get_erase_count(area);
-
- log_gc("logfs_open_area(%x, %x)\n", area->a_segno, area->a_level);
- err = area->a_ops->erase_segment(area);
- if (err) {
- printk(KERN_WARNING "LogFS: Error erasing segment %x\n",
- area->a_segno);
- logfs_mark_segment_bad(sb, area->a_segno);
- goto again;
- }
- area->a_is_open = 1;
- return closed;
-}
-
-void logfs_sync_area(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- struct logfs_super *super = logfs_super(sb);
- u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
- u32 len = (area->a_used_bytes - area->a_written_bytes);
-
- if (super->s_writesize)
- len &= ~(super->s_writesize - 1);
- if (len == 0)
- return;
- pad_wbuf(area, 0);
- super->s_devops->writeseg(sb, ofs, len);
- area->a_written_bytes += len;
-}
-
-void logfs_sync_segments(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i;
-
- for_each_area(i)
- logfs_sync_area(super->s_area[i]);
-}
-
-/*
- * Pick a free segment to be used for this area. Effectively takes a
- * candidate from the free list (not really a candidate anymore).
- */
-static void ostore_get_free_segment(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- struct logfs_super *super = logfs_super(sb);
-
- if (super->s_free_list.count == 0) {
- printk(KERN_ERR"LOGFS: ran out of free segments\n");
- LOGFS_BUG(sb);
- }
-
- area->a_segno = get_best_cand(sb, &super->s_free_list, NULL);
-}
-
-static void ostore_get_erase_count(struct logfs_area *area)
-{
- struct logfs_segment_entry se;
- u32 ec_level;
-
- logfs_get_segment_entry(area->a_sb, area->a_segno, &se);
- BUG_ON(se.ec_level == cpu_to_be32(BADSEG) ||
- se.valid == cpu_to_be32(RESERVED));
-
- ec_level = be32_to_cpu(se.ec_level);
- area->a_erase_count = (ec_level >> 4) + 1;
-}
-
-static int ostore_erase_segment(struct logfs_area *area)
-{
- struct super_block *sb = area->a_sb;
- struct logfs_segment_header sh;
- u64 ofs;
- int err;
-
- err = logfs_erase_segment(sb, area->a_segno, 0);
- if (err)
- return err;
-
- sh.pad = 0;
- sh.type = SEG_OSTORE;
- sh.level = (__force u8)area->a_level;
- sh.segno = cpu_to_be32(area->a_segno);
- sh.ec = cpu_to_be32(area->a_erase_count);
- sh.gec = cpu_to_be64(logfs_super(sb)->s_gec);
- sh.crc = logfs_crc32(&sh, sizeof(sh), 4);
-
- logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count,
- area->a_level);
-
- ofs = dev_ofs(sb, area->a_segno, 0);
- area->a_used_bytes = sizeof(sh);
- logfs_buf_write(area, ofs, &sh, sizeof(sh));
- return 0;
-}
-
-static const struct logfs_area_ops ostore_area_ops = {
- .get_free_segment = ostore_get_free_segment,
- .get_erase_count = ostore_get_erase_count,
- .erase_segment = ostore_erase_segment,
-};
-
-static void free_area(struct logfs_area *area)
-{
- if (area)
- freeseg(area->a_sb, area->a_segno);
- kfree(area);
-}
-
-void free_areas(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i;
-
- for_each_area(i)
- free_area(super->s_area[i]);
- free_area(super->s_journal_area);
-}
-
-static struct logfs_area *alloc_area(struct super_block *sb)
-{
- struct logfs_area *area;
-
- area = kzalloc(sizeof(*area), GFP_KERNEL);
- if (!area)
- return NULL;
-
- area->a_sb = sb;
- return area;
-}
-
-static void map_invalidatepage(struct page *page, unsigned int o,
- unsigned int l)
-{
- return;
-}
-
-static int map_releasepage(struct page *page, gfp_t g)
-{
- /* Don't release these pages */
- return 0;
-}
-
-static const struct address_space_operations mapping_aops = {
- .invalidatepage = map_invalidatepage,
- .releasepage = map_releasepage,
- .set_page_dirty = __set_page_dirty_nobuffers,
-};
-
-int logfs_init_mapping(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct address_space *mapping;
- struct inode *inode;
-
- inode = logfs_new_meta_inode(sb, LOGFS_INO_MAPPING);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
- super->s_mapping_inode = inode;
- mapping = inode->i_mapping;
- mapping->a_ops = &mapping_aops;
- /* Would it be possible to use __GFP_HIGHMEM as well? */
- mapping_set_gfp_mask(mapping, GFP_NOFS);
- return 0;
-}
-
-int logfs_init_areas(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int i = -1;
-
- super->s_alias_pool = mempool_create_kmalloc_pool(600,
- sizeof(struct object_alias_item));
- if (!super->s_alias_pool)
- return -ENOMEM;
-
- super->s_journal_area = alloc_area(sb);
- if (!super->s_journal_area)
- goto err;
-
- for_each_area(i) {
- super->s_area[i] = alloc_area(sb);
- if (!super->s_area[i])
- goto err;
- super->s_area[i]->a_level = GC_LEVEL(i);
- super->s_area[i]->a_ops = &ostore_area_ops;
- }
- btree_init_mempool128(&super->s_object_alias_tree,
- super->s_btree_pool);
- return 0;
-
-err:
- for (i--; i >= 0; i--)
- free_area(super->s_area[i]);
- free_area(super->s_journal_area);
- logfs_mempool_destroy(super->s_alias_pool);
- return -ENOMEM;
-}
-
-void logfs_cleanup_areas(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- btree_grim_visitor128(&super->s_object_alias_tree, 0, kill_alias);
-}
+++ /dev/null
-/*
- * fs/logfs/super.c
- *
- * As should be obvious for Linux kernel code, license is GPLv2
- *
- * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
- *
- * Generally contains mount/umount code and also serves as a dump area for
- * any functions that don't fit elsewhere and neither justify a file of their
- * own.
- */
-#include "logfs.h"
-#include <linux/bio.h>
-#include <linux/slab.h>
-#include <linux/blkdev.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/statfs.h>
-#include <linux/buffer_head.h>
-
-static DEFINE_MUTEX(emergency_mutex);
-static struct page *emergency_page;
-
-struct page *emergency_read_begin(struct address_space *mapping, pgoff_t index)
-{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
- struct page *page;
- int err;
-
- page = read_cache_page(mapping, index, filler, NULL);
- if (page)
- return page;
-
- /* No more pages available, switch to emergency page */
- printk(KERN_INFO"Logfs: Using emergency page\n");
- mutex_lock(&emergency_mutex);
- err = filler(NULL, emergency_page);
- if (err) {
- mutex_unlock(&emergency_mutex);
- printk(KERN_EMERG"Logfs: Error reading emergency page\n");
- return ERR_PTR(err);
- }
- return emergency_page;
-}
-
-void emergency_read_end(struct page *page)
-{
- if (page == emergency_page)
- mutex_unlock(&emergency_mutex);
- else
- put_page(page);
-}
-
-static void dump_segfile(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_segment_entry se;
- u32 segno;
-
- for (segno = 0; segno < super->s_no_segs; segno++) {
- logfs_get_segment_entry(sb, segno, &se);
- printk("%3x: %6x %8x", segno, be32_to_cpu(se.ec_level),
- be32_to_cpu(se.valid));
- if (++segno < super->s_no_segs) {
- logfs_get_segment_entry(sb, segno, &se);
- printk(" %6x %8x", be32_to_cpu(se.ec_level),
- be32_to_cpu(se.valid));
- }
- if (++segno < super->s_no_segs) {
- logfs_get_segment_entry(sb, segno, &se);
- printk(" %6x %8x", be32_to_cpu(se.ec_level),
- be32_to_cpu(se.valid));
- }
- if (++segno < super->s_no_segs) {
- logfs_get_segment_entry(sb, segno, &se);
- printk(" %6x %8x", be32_to_cpu(se.ec_level),
- be32_to_cpu(se.valid));
- }
- printk("\n");
- }
-}
-
-/*
- * logfs_crash_dump - dump debug information to device
- *
- * The LogFS superblock only occupies part of a segment. This function will
- * write as much debug information as it can gather into the spare space.
- */
-void logfs_crash_dump(struct super_block *sb)
-{
- dump_segfile(sb);
-}
-
-/*
- * FIXME: There should be a reserve for root, similar to ext2.
- */
-int logfs_statfs(struct dentry *dentry, struct kstatfs *stats)
-{
- struct super_block *sb = dentry->d_sb;
- struct logfs_super *super = logfs_super(sb);
-
- stats->f_type = LOGFS_MAGIC_U32;
- stats->f_bsize = sb->s_blocksize;
- stats->f_blocks = super->s_size >> LOGFS_BLOCK_BITS >> 3;
- stats->f_bfree = super->s_free_bytes >> sb->s_blocksize_bits;
- stats->f_bavail = super->s_free_bytes >> sb->s_blocksize_bits;
- stats->f_files = 0;
- stats->f_ffree = 0;
- stats->f_namelen = LOGFS_MAX_NAMELEN;
- return 0;
-}
-
-static int logfs_sb_set(struct super_block *sb, void *_super)
-{
- struct logfs_super *super = _super;
-
- sb->s_fs_info = super;
- sb->s_mtd = super->s_mtd;
- sb->s_bdev = super->s_bdev;
-#ifdef CONFIG_BLOCK
- if (sb->s_bdev)
- sb->s_bdi = &bdev_get_queue(sb->s_bdev)->backing_dev_info;
-#endif
-#ifdef CONFIG_MTD
- if (sb->s_mtd)
- sb->s_bdi = sb->s_mtd->backing_dev_info;
-#endif
- return 0;
-}
-
-static int logfs_sb_test(struct super_block *sb, void *_super)
-{
- struct logfs_super *super = _super;
- struct mtd_info *mtd = super->s_mtd;
-
- if (mtd && sb->s_mtd == mtd)
- return 1;
- if (super->s_bdev && sb->s_bdev == super->s_bdev)
- return 1;
- return 0;
-}
-
-static void set_segment_header(struct logfs_segment_header *sh, u8 type,
- u8 level, u32 segno, u32 ec)
-{
- sh->pad = 0;
- sh->type = type;
- sh->level = level;
- sh->segno = cpu_to_be32(segno);
- sh->ec = cpu_to_be32(ec);
- sh->gec = cpu_to_be64(segno);
- sh->crc = logfs_crc32(sh, LOGFS_SEGMENT_HEADERSIZE, 4);
-}
-
-static void logfs_write_ds(struct super_block *sb, struct logfs_disk_super *ds,
- u32 segno, u32 ec)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_segment_header *sh = &ds->ds_sh;
- int i;
-
- memset(ds, 0, sizeof(*ds));
- set_segment_header(sh, SEG_SUPER, 0, segno, ec);
-
- ds->ds_ifile_levels = super->s_ifile_levels;
- ds->ds_iblock_levels = super->s_iblock_levels;
- ds->ds_data_levels = super->s_data_levels; /* XXX: Remove */
- ds->ds_segment_shift = super->s_segshift;
- ds->ds_block_shift = sb->s_blocksize_bits;
- ds->ds_write_shift = super->s_writeshift;
- ds->ds_filesystem_size = cpu_to_be64(super->s_size);
- ds->ds_segment_size = cpu_to_be32(super->s_segsize);
- ds->ds_bad_seg_reserve = cpu_to_be32(super->s_bad_seg_reserve);
- ds->ds_feature_incompat = cpu_to_be64(super->s_feature_incompat);
- ds->ds_feature_ro_compat= cpu_to_be64(super->s_feature_ro_compat);
- ds->ds_feature_compat = cpu_to_be64(super->s_feature_compat);
- ds->ds_feature_flags = cpu_to_be64(super->s_feature_flags);
- ds->ds_root_reserve = cpu_to_be64(super->s_root_reserve);
- ds->ds_speed_reserve = cpu_to_be64(super->s_speed_reserve);
- journal_for_each(i)
- ds->ds_journal_seg[i] = cpu_to_be32(super->s_journal_seg[i]);
- ds->ds_magic = cpu_to_be64(LOGFS_MAGIC);
- ds->ds_crc = logfs_crc32(ds, sizeof(*ds),
- LOGFS_SEGMENT_HEADERSIZE + 12);
-}
-
-static int write_one_sb(struct super_block *sb,
- struct page *(*find_sb)(struct super_block *sb, u64 *ofs))
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_disk_super *ds;
- struct logfs_segment_entry se;
- struct page *page;
- u64 ofs;
- u32 ec, segno;
- int err;
-
- page = find_sb(sb, &ofs);
- if (!page)
- return -EIO;
- ds = page_address(page);
- segno = seg_no(sb, ofs);
- logfs_get_segment_entry(sb, segno, &se);
- ec = be32_to_cpu(se.ec_level) >> 4;
- ec++;
- logfs_set_segment_erased(sb, segno, ec, 0);
- logfs_write_ds(sb, ds, segno, ec);
- err = super->s_devops->write_sb(sb, page);
- put_page(page);
- return err;
-}
-
-int logfs_write_sb(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- int err;
-
- /* First superblock */
- err = write_one_sb(sb, super->s_devops->find_first_sb);
- if (err)
- return err;
-
- /* Last superblock */
- err = write_one_sb(sb, super->s_devops->find_last_sb);
- if (err)
- return err;
- return 0;
-}
-
-static int ds_cmp(const void *ds0, const void *ds1)
-{
- size_t len = sizeof(struct logfs_disk_super);
-
- /* We know the segment headers differ, so ignore them */
- len -= LOGFS_SEGMENT_HEADERSIZE;
- ds0 += LOGFS_SEGMENT_HEADERSIZE;
- ds1 += LOGFS_SEGMENT_HEADERSIZE;
- return memcmp(ds0, ds1, len);
-}
-
-static int logfs_recover_sb(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct logfs_disk_super _ds0, *ds0 = &_ds0;
- struct logfs_disk_super _ds1, *ds1 = &_ds1;
- int err, valid0, valid1;
-
- /* read first superblock */
- err = wbuf_read(sb, super->s_sb_ofs[0], sizeof(*ds0), ds0);
- if (err)
- return err;
- /* read last superblock */
- err = wbuf_read(sb, super->s_sb_ofs[1], sizeof(*ds1), ds1);
- if (err)
- return err;
- valid0 = logfs_check_ds(ds0) == 0;
- valid1 = logfs_check_ds(ds1) == 0;
-
- if (!valid0 && valid1) {
- printk(KERN_INFO"First superblock is invalid - fixing.\n");
- return write_one_sb(sb, super->s_devops->find_first_sb);
- }
- if (valid0 && !valid1) {
- printk(KERN_INFO"Last superblock is invalid - fixing.\n");
- return write_one_sb(sb, super->s_devops->find_last_sb);
- }
- if (valid0 && valid1 && ds_cmp(ds0, ds1)) {
- printk(KERN_INFO"Superblocks don't match - fixing.\n");
- return logfs_write_sb(sb);
- }
- /* If neither is valid now, something's wrong. Didn't we properly
- * check them before?!? */
- BUG_ON(!valid0 && !valid1);
- return 0;
-}
-
-static int logfs_make_writeable(struct super_block *sb)
-{
- int err;
-
- err = logfs_open_segfile(sb);
- if (err)
- return err;
-
- /* Repair any broken superblock copies */
- err = logfs_recover_sb(sb);
- if (err)
- return err;
-
- /* Check areas for trailing unaccounted data */
- err = logfs_check_areas(sb);
- if (err)
- return err;
-
- /* Do one GC pass before any data gets dirtied */
- logfs_gc_pass(sb);
-
- /* after all initializations are done, replay the journal
- * for rw-mounts, if necessary */
- err = logfs_replay_journal(sb);
- if (err)
- return err;
-
- return 0;
-}
-
-static int logfs_get_sb_final(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct inode *rootdir;
- int err;
-
- /* root dir */
- rootdir = logfs_iget(sb, LOGFS_INO_ROOT);
- if (IS_ERR(rootdir))
- goto fail;
-
- sb->s_root = d_make_root(rootdir);
- if (!sb->s_root)
- goto fail;
-
- /* at that point we know that ->put_super() will be called */
- super->s_erase_page = alloc_pages(GFP_KERNEL, 0);
- if (!super->s_erase_page)
- return -ENOMEM;
- memset(page_address(super->s_erase_page), 0xFF, PAGE_SIZE);
-
- /* FIXME: check for read-only mounts */
- err = logfs_make_writeable(sb);
- if (err) {
- __free_page(super->s_erase_page);
- return err;
- }
-
- log_super("LogFS: Finished mounting\n");
- return 0;
-
-fail:
- iput(super->s_master_inode);
- iput(super->s_segfile_inode);
- iput(super->s_mapping_inode);
- return -EIO;
-}
-
-int logfs_check_ds(struct logfs_disk_super *ds)
-{
- struct logfs_segment_header *sh = &ds->ds_sh;
-
- if (ds->ds_magic != cpu_to_be64(LOGFS_MAGIC))
- return -EINVAL;
- if (sh->crc != logfs_crc32(sh, LOGFS_SEGMENT_HEADERSIZE, 4))
- return -EINVAL;
- if (ds->ds_crc != logfs_crc32(ds, sizeof(*ds),
- LOGFS_SEGMENT_HEADERSIZE + 12))
- return -EINVAL;
- return 0;
-}
-
-static struct page *find_super_block(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct page *first, *last;
-
- first = super->s_devops->find_first_sb(sb, &super->s_sb_ofs[0]);
- if (!first || IS_ERR(first))
- return NULL;
- last = super->s_devops->find_last_sb(sb, &super->s_sb_ofs[1]);
- if (!last || IS_ERR(last)) {
- put_page(first);
- return NULL;
- }
-
- if (!logfs_check_ds(page_address(first))) {
- put_page(last);
- return first;
- }
-
- /* First one didn't work, try the second superblock */
- if (!logfs_check_ds(page_address(last))) {
- put_page(first);
- return last;
- }
-
- /* Neither worked, sorry folks */
- put_page(first);
- put_page(last);
- return NULL;
-}
-
-static int __logfs_read_sb(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
- struct page *page;
- struct logfs_disk_super *ds;
- int i;
-
- page = find_super_block(sb);
- if (!page)
- return -EINVAL;
-
- ds = page_address(page);
- super->s_size = be64_to_cpu(ds->ds_filesystem_size);
- super->s_root_reserve = be64_to_cpu(ds->ds_root_reserve);
- super->s_speed_reserve = be64_to_cpu(ds->ds_speed_reserve);
- super->s_bad_seg_reserve = be32_to_cpu(ds->ds_bad_seg_reserve);
- super->s_segsize = 1 << ds->ds_segment_shift;
- super->s_segmask = (1 << ds->ds_segment_shift) - 1;
- super->s_segshift = ds->ds_segment_shift;
- sb->s_blocksize = 1 << ds->ds_block_shift;
- sb->s_blocksize_bits = ds->ds_block_shift;
- super->s_writesize = 1 << ds->ds_write_shift;
- super->s_writeshift = ds->ds_write_shift;
- super->s_no_segs = super->s_size >> super->s_segshift;
- super->s_no_blocks = super->s_segsize >> sb->s_blocksize_bits;
- super->s_feature_incompat = be64_to_cpu(ds->ds_feature_incompat);
- super->s_feature_ro_compat = be64_to_cpu(ds->ds_feature_ro_compat);
- super->s_feature_compat = be64_to_cpu(ds->ds_feature_compat);
- super->s_feature_flags = be64_to_cpu(ds->ds_feature_flags);
-
- journal_for_each(i)
- super->s_journal_seg[i] = be32_to_cpu(ds->ds_journal_seg[i]);
-
- super->s_ifile_levels = ds->ds_ifile_levels;
- super->s_iblock_levels = ds->ds_iblock_levels;
- super->s_data_levels = ds->ds_data_levels;
- super->s_total_levels = super->s_ifile_levels + super->s_iblock_levels
- + super->s_data_levels;
- put_page(page);
- return 0;
-}
-
-static int logfs_read_sb(struct super_block *sb, int read_only)
-{
- struct logfs_super *super = logfs_super(sb);
- int ret;
-
- super->s_btree_pool = mempool_create(32, btree_alloc, btree_free, NULL);
- if (!super->s_btree_pool)
- return -ENOMEM;
-
- btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool);
- btree_init_mempool64(&super->s_shadow_tree.old, super->s_btree_pool);
- btree_init_mempool32(&super->s_shadow_tree.segment_map,
- super->s_btree_pool);
-
- ret = logfs_init_mapping(sb);
- if (ret)
- return ret;
-
- ret = __logfs_read_sb(sb);
- if (ret)
- return ret;
-
- if (super->s_feature_incompat & ~LOGFS_FEATURES_INCOMPAT)
- return -EIO;
- if ((super->s_feature_ro_compat & ~LOGFS_FEATURES_RO_COMPAT) &&
- !read_only)
- return -EIO;
-
- ret = logfs_init_rw(sb);
- if (ret)
- return ret;
-
- ret = logfs_init_areas(sb);
- if (ret)
- return ret;
-
- ret = logfs_init_gc(sb);
- if (ret)
- return ret;
-
- ret = logfs_init_journal(sb);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static void logfs_kill_sb(struct super_block *sb)
-{
- struct logfs_super *super = logfs_super(sb);
-
- log_super("LogFS: Start unmounting\n");
- /* Alias entries slow down mount, so evict as many as possible */
- sync_filesystem(sb);
- logfs_write_anchor(sb);
- free_areas(sb);
-
- /*
- * From this point on alias entries are simply dropped - and any
- * writes to the object store are considered bugs.
- */
- log_super("LogFS: Now in shutdown\n");
- generic_shutdown_super(sb);
- super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN;
-
- BUG_ON(super->s_dirty_used_bytes || super->s_dirty_free_bytes);
-
- logfs_cleanup_gc(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(super);
- logfs_mempool_destroy(super->s_btree_pool);
- logfs_mempool_destroy(super->s_alias_pool);
- kfree(super);
- log_super("LogFS: Finished unmounting\n");
-}
-
-static struct dentry *logfs_get_sb_device(struct logfs_super *super,
- struct file_system_type *type, int flags)
-{
- struct super_block *sb;
- int err = -ENOMEM;
- static int mount_count;
-
- log_super("LogFS: Start mount %x\n", mount_count++);
-
- err = -EINVAL;
- sb = sget(type, logfs_sb_test, logfs_sb_set, flags | MS_NOATIME, super);
- if (IS_ERR(sb)) {
- super->s_devops->put_device(super);
- kfree(super);
- return ERR_CAST(sb);
- }
-
- if (sb->s_root) {
- /* Device is already in use */
- super->s_devops->put_device(super);
- kfree(super);
- return dget(sb->s_root);
- }
-
- /*
- * sb->s_maxbytes is limited to 8TB. On 32bit systems, the page cache
- * only covers 16TB and the upper 8TB are used for indirect blocks.
- * On 64bit system we could bump up the limit, but that would make
- * the filesystem incompatible with 32bit systems.
- */
- sb->s_maxbytes = (1ull << 43) - 1;
- sb->s_max_links = LOGFS_LINK_MAX;
- sb->s_op = &logfs_super_operations;
-
- err = logfs_read_sb(sb, sb->s_flags & MS_RDONLY);
- if (err)
- goto err1;
-
- sb->s_flags |= MS_ACTIVE;
- err = logfs_get_sb_final(sb);
- if (err) {
- deactivate_locked_super(sb);
- return ERR_PTR(err);
- }
- return dget(sb->s_root);
-
-err1:
- /* no ->s_root, no ->put_super() */
- iput(super->s_master_inode);
- iput(super->s_segfile_inode);
- iput(super->s_mapping_inode);
- deactivate_locked_super(sb);
- return ERR_PTR(err);
-}
-
-static struct dentry *logfs_mount(struct file_system_type *type, int flags,
- const char *devname, void *data)
-{
- ulong mtdnr;
- struct logfs_super *super;
- int err;
-
- super = kzalloc(sizeof(*super), GFP_KERNEL);
- if (!super)
- return ERR_PTR(-ENOMEM);
-
- mutex_init(&super->s_dirop_mutex);
- mutex_init(&super->s_object_alias_mutex);
- INIT_LIST_HEAD(&super->s_freeing_list);
-
- if (!devname)
- err = logfs_get_sb_bdev(super, type, devname);
- else if (strncmp(devname, "mtd", 3))
- err = logfs_get_sb_bdev(super, type, devname);
- else {
- char *garbage;
- mtdnr = simple_strtoul(devname+3, &garbage, 0);
- if (*garbage)
- err = -EINVAL;
- else
- err = logfs_get_sb_mtd(super, mtdnr);
- }
-
- if (err) {
- kfree(super);
- return ERR_PTR(err);
- }
-
- return logfs_get_sb_device(super, type, flags);
-}
-
-static struct file_system_type logfs_fs_type = {
- .owner = THIS_MODULE,
- .name = "logfs",
- .mount = logfs_mount,
- .kill_sb = logfs_kill_sb,
- .fs_flags = FS_REQUIRES_DEV,
-
-};
-MODULE_ALIAS_FS("logfs");
-
-static int __init logfs_init(void)
-{
- int ret;
-
- emergency_page = alloc_pages(GFP_KERNEL, 0);
- if (!emergency_page)
- return -ENOMEM;
-
- ret = logfs_compr_init();
- if (ret)
- goto out1;
-
- ret = logfs_init_inode_cache();
- if (ret)
- goto out2;
-
- ret = register_filesystem(&logfs_fs_type);
- if (!ret)
- return 0;
- logfs_destroy_inode_cache();
-out2:
- logfs_compr_exit();
-out1:
- __free_pages(emergency_page, 0);
- return ret;
-}
-
-static void __exit logfs_exit(void)
-{
- unregister_filesystem(&logfs_fs_type);
- logfs_destroy_inode_cache();
- logfs_compr_exit();
- __free_pages(emergency_page, 0);
-}
-
-module_init(logfs_init);
-module_exit(logfs_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Joern Engel <joern@logfs.org>");
-MODULE_DESCRIPTION("scalable flash filesystem");