diff options
Diffstat (limited to 'fs')
111 files changed, 1333 insertions, 10356 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index 4bd03a2b0518..884653fc6a8b 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -235,7 +235,6 @@ source "fs/efs/Kconfig" 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" diff --git a/fs/Makefile b/fs/Makefile index ed2b63257ba9..7bbaca9c67b1 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -97,7 +97,6 @@ obj-$(CONFIG_NTFS_FS) += ntfs/ 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/ diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 2037e7a77a37..d764236072b1 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -91,11 +91,9 @@ static const struct afs_call_type afs_SRXCBTellMeAboutYourself = { */ bool afs_cm_incoming_call(struct afs_call *call) { - u32 operation_id = ntohl(call->operation_ID); + _enter("{CB.OP %u}", call->operation_ID); - _enter("{CB.OP %u}", operation_id); - - switch (operation_id) { + switch (call->operation_ID) { case CBCallBack: call->type = &afs_SRXCBCallBack; return true; diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 96f4d764d1a6..31c616ab9b40 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -364,7 +364,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) buffer = kmap(page); ret = afs_extract_data(call, buffer, call->count, true); - kunmap(buffer); + kunmap(page); if (ret < 0) return ret; } @@ -397,7 +397,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) page = call->reply3; buffer = kmap(page); memset(buffer + call->count, 0, PAGE_SIZE - call->count); - kunmap(buffer); + kunmap(page); } _leave(" = 0 [done]"); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5497c8496055..535a38d2c1d0 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -112,7 +112,7 @@ struct afs_call { bool need_attention; /* T if RxRPC poked us */ u16 service_id; /* RxRPC service ID to call */ __be16 port; /* target UDP port */ - __be32 operation_ID; /* operation ID for an incoming call */ + u32 operation_ID; /* operation ID for an incoming call */ u32 count; /* count for use in unmarshalling */ __be32 tmp; /* place to extract temporary data */ afs_dataversion_t store_version; /* updated version expected from store */ diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 477928b25940..25f05a8d21b1 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -676,10 +676,11 @@ static int afs_deliver_cm_op_id(struct afs_call *call) ASSERTCMP(call->offset, <, 4); /* the operation ID forms the first four bytes of the request data */ - ret = afs_extract_data(call, &call->operation_ID, 4, true); + ret = afs_extract_data(call, &call->tmp, 4, true); if (ret < 0) return ret; + call->operation_ID = ntohl(call->tmp); call->state = AFS_CALL_AWAIT_REQUEST; call->offset = 0; @@ -277,10 +277,10 @@ static void put_aio_ring_file(struct kioctx *ctx) struct address_space *i_mapping; if (aio_ring_file) { - truncate_setsize(aio_ring_file->f_inode, 0); + truncate_setsize(file_inode(aio_ring_file), 0); /* Prevent further access to the kioctx from migratepages */ - i_mapping = aio_ring_file->f_inode->i_mapping; + i_mapping = aio_ring_file->f_mapping; spin_lock(&i_mapping->private_lock); i_mapping->private_data = NULL; ctx->aio_ring_file = NULL; @@ -483,7 +483,7 @@ static int aio_setup_ring(struct kioctx *ctx) for (i = 0; i < nr_pages; i++) { struct page *page; - page = find_or_create_page(file->f_inode->i_mapping, + page = find_or_create_page(file->f_mapping, i, GFP_HIGHUSER | __GFP_ZERO); if (!page) break; @@ -1078,6 +1078,17 @@ static void aio_complete(struct kiocb *kiocb, long res, long res2) unsigned tail, pos, head; unsigned long flags; + if (kiocb->ki_flags & IOCB_WRITE) { + struct file *file = kiocb->ki_filp; + + /* + * Tell lockdep we inherited freeze protection from submission + * thread. + */ + __sb_writers_acquired(file_inode(file)->i_sb, SB_FREEZE_WRITE); + file_end_write(file); + } + /* * Special case handling for sync iocbs: * - events go directly into the iocb for fast handling @@ -1392,122 +1403,106 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) return -EINVAL; } -typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); - -static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len, - struct iovec **iovec, - bool compat, - struct iov_iter *iter) +static int aio_setup_rw(int rw, struct iocb *iocb, struct iovec **iovec, + bool vectored, bool compat, struct iov_iter *iter) { + void __user *buf = (void __user *)(uintptr_t)iocb->aio_buf; + size_t len = iocb->aio_nbytes; + + if (!vectored) { + ssize_t ret = import_single_range(rw, buf, len, *iovec, iter); + *iovec = NULL; + return ret; + } #ifdef CONFIG_COMPAT if (compat) - return compat_import_iovec(rw, - (struct compat_iovec __user *)buf, - len, UIO_FASTIOV, iovec, iter); + return compat_import_iovec(rw, buf, len, UIO_FASTIOV, iovec, + iter); #endif - return import_iovec(rw, (struct iovec __user *)buf, - len, UIO_FASTIOV, iovec, iter); + return import_iovec(rw, buf, len, UIO_FASTIOV, iovec, iter); } -/* - * aio_run_iocb: - * Performs the initial checks and io submission. - */ -static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, - char __user *buf, size_t len, bool compat) +static inline ssize_t aio_ret(struct kiocb *req, ssize_t ret) +{ + switch (ret) { + case -EIOCBQUEUED: + return ret; + case -ERESTARTSYS: + case -ERESTARTNOINTR: + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * There's no easy way to restart the syscall since other AIO's + * may be already running. Just fail this IO with EINTR. + */ + ret = -EINTR; + /*FALLTHRU*/ + default: + aio_complete(req, ret, 0); + return 0; + } +} + +static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored, + bool compat) { struct file *file = req->ki_filp; - ssize_t ret; - int rw; - fmode_t mode; - rw_iter_op *iter_op; struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct iov_iter iter; + ssize_t ret; - switch (opcode) { - case IOCB_CMD_PREAD: - case IOCB_CMD_PREADV: - mode = FMODE_READ; - rw = READ; - iter_op = file->f_op->read_iter; - goto rw_common; - - case IOCB_CMD_PWRITE: - case IOCB_CMD_PWRITEV: - mode = FMODE_WRITE; - rw = WRITE; - iter_op = file->f_op->write_iter; - goto rw_common; -rw_common: - if (unlikely(!(file->f_mode & mode))) - return -EBADF; - - if (!iter_op) - return -EINVAL; - - if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) - ret = aio_setup_vectored_rw(rw, buf, len, - &iovec, compat, &iter); - else { - ret = import_single_range(rw, buf, len, iovec, &iter); - iovec = NULL; - } - if (!ret) - ret = rw_verify_area(rw, file, &req->ki_pos, - iov_iter_count(&iter)); - if (ret < 0) { - kfree(iovec); - return ret; - } - - if (rw == WRITE) - file_start_write(file); - - ret = iter_op(req, &iter); - - if (rw == WRITE) - file_end_write(file); - kfree(iovec); - break; - - case IOCB_CMD_FDSYNC: - if (!file->f_op->aio_fsync) - return -EINVAL; - - ret = file->f_op->aio_fsync(req, 1); - break; + if (unlikely(!(file->f_mode & FMODE_READ))) + return -EBADF; + if (unlikely(!file->f_op->read_iter)) + return -EINVAL; - case IOCB_CMD_FSYNC: - if (!file->f_op->aio_fsync) - return -EINVAL; + ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter); + if (ret) + return ret; + ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) + ret = aio_ret(req, file->f_op->read_iter(req, &iter)); + kfree(iovec); + return ret; +} - ret = file->f_op->aio_fsync(req, 0); - break; +static ssize_t aio_write(struct kiocb *req, struct iocb *iocb, bool vectored, + bool compat) +{ + struct file *file = req->ki_filp; + struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; + struct iov_iter iter; + ssize_t ret; - default: - pr_debug("EINVAL: no operation provided\n"); + if (unlikely(!(file->f_mode & FMODE_WRITE))) + return -EBADF; + if (unlikely(!file->f_op->write_iter)) return -EINVAL; - } - if (ret != -EIOCBQUEUED) { + ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter); + if (ret) + return ret; + ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) { + req->ki_flags |= IOCB_WRITE; + file_start_write(file); + ret = aio_ret(req, file->f_op->write_iter(req, &iter)); /* - * There's no easy way to restart the syscall since other AIO's - * may be already running. Just fail this IO with EINTR. + * We release freeze protection in aio_complete(). Fool lockdep + * by telling it the lock got released so that it doesn't + * complain about held lock when we return to userspace. */ - if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR || - ret == -ERESTARTNOHAND || - ret == -ERESTART_RESTARTBLOCK)) - ret = -EINTR; - aio_complete(req, ret, 0); + __sb_writers_release(file_inode(file)->i_sb, SB_FREEZE_WRITE); } - - return 0; + kfree(iovec); + return ret; } static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb, bool compat) { struct aio_kiocb *req; + struct file *file; ssize_t ret; /* enforce forwards compatibility on users */ @@ -1530,7 +1525,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, if (unlikely(!req)) return -EAGAIN; - req->common.ki_filp = fget(iocb->aio_fildes); + req->common.ki_filp = file = fget(iocb->aio_fildes); if (unlikely(!req->common.ki_filp)) { ret = -EBADF; goto out_put_req; @@ -1565,13 +1560,29 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_user_iocb = user_iocb; req->ki_user_data = iocb->aio_data; - ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, - (char __user *)(unsigned long)iocb->aio_buf, - iocb->aio_nbytes, - compat); - if (ret) - goto out_put_req; + get_file(file); + switch (iocb->aio_lio_opcode) { + case IOCB_CMD_PREAD: + ret = aio_read(&req->common, iocb, false, compat); + break; + case IOCB_CMD_PWRITE: + ret = aio_write(&req->common, iocb, false, compat); + break; + case IOCB_CMD_PREADV: + ret = aio_read(&req->common, iocb, true, compat); + break; + case IOCB_CMD_PWRITEV: + ret = aio_write(&req->common, iocb, true, compat); + break; + default: + pr_debug("invalid aio operation %d\n", iocb->aio_lio_opcode); + ret = -EINVAL; + break; + } + fput(file); + if (ret && ret != -EIOCBQUEUED) + goto out_put_req; return 0; out_put_req: put_reqs_available(ctx, 1); diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 438b5bf675b6..09e7d68dff02 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -94,7 +94,7 @@ static int autofs4_show_options(struct seq_file *m, struct dentry *root) seq_printf(m, ",indirect"); #ifdef CONFIG_CHECKPOINT_RESTORE if (sbi->pipe) - seq_printf(m, ",pipe_ino=%ld", sbi->pipe->f_inode->i_ino); + seq_printf(m, ",pipe_ino=%ld", file_inode(sbi->pipe)->i_ino); else seq_printf(m, ",pipe_ino=-1"); #endif diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index ccc70d96958d..d4d8b7e36b2f 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -698,7 +698,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, ret = btrfs_map_bio(root, comp_bio, mirror_num, 0); if (ret) { - bio->bi_error = ret; + comp_bio->bi_error = ret; bio_endio(comp_bio); } @@ -728,7 +728,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, ret = btrfs_map_bio(root, comp_bio, mirror_num, 0); if (ret) { - bio->bi_error = ret; + comp_bio->bi_error = ret; bio_endio(comp_bio); } diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 210c94ac8818..4607af38c72e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2647,7 +2647,10 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, btrfs_free_delayed_extent_op(extent_op); if (ret) { + spin_lock(&delayed_refs->lock); locked_ref->processing = 0; + delayed_refs->num_heads_ready++; + spin_unlock(&delayed_refs->lock); btrfs_delayed_ref_unlock(locked_ref); btrfs_put_delayed_ref(ref); btrfs_debug(fs_info, "run_one_delayed_ref returned %d", diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 66a755150056..8ed05d95584a 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -5569,7 +5569,7 @@ void le_bitmap_set(u8 *map, unsigned int start, int len) *p |= mask_to_set; len -= bits_to_set; bits_to_set = BITS_PER_BYTE; - mask_to_set = ~(u8)0; + mask_to_set = ~0; p++; } if (len) { @@ -5589,7 +5589,7 @@ void le_bitmap_clear(u8 *map, unsigned int start, int len) *p &= ~mask_to_clear; len -= bits_to_clear; bits_to_clear = BITS_PER_BYTE; - mask_to_clear = ~(u8)0; + mask_to_clear = ~0; p++; } if (len) { @@ -5679,7 +5679,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start, kaddr[offset] |= mask_to_set; len -= bits_to_set; bits_to_set = BITS_PER_BYTE; - mask_to_set = ~(u8)0; + mask_to_set = ~0; if (++offset >= PAGE_SIZE && len > 0) { offset = 0; page = eb->pages[++i]; @@ -5721,7 +5721,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start, kaddr[offset] &= ~mask_to_clear; len -= bits_to_clear; bits_to_clear = BITS_PER_BYTE; - mask_to_clear = ~(u8)0; + mask_to_clear = ~0; if (++offset >= PAGE_SIZE && len > 0) { offset = 0; page = eb->pages[++i]; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2b790bda7998..8e3a5a266917 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4605,8 +4605,8 @@ delete: BUG_ON(ret); if (btrfs_should_throttle_delayed_refs(trans, root)) btrfs_async_run_delayed_refs(root, - trans->transid, - trans->delayed_ref_updates * 2, 0); + trans->delayed_ref_updates * 2, + trans->transid, 0); if (be_nice) { if (truncate_space_check(trans, root, extent_num_bytes)) { @@ -8931,9 +8931,14 @@ again: * So even we call qgroup_free_data(), it won't decrease reserved * space. * 2) Not written to disk - * This means the reserved space should be freed here. + * This means the reserved space should be freed here. However, + * if a truncate invalidates the page (by clearing PageDirty) + * and the page is accounted for while allocating extent + * in btrfs_check_data_free_space() we let delayed_ref to + * free the entire extent. */ - btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE); + if (PageDirty(page)) + btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE); if (!inode_evicting) { clear_extent_bit(tree, page_start, page_end, EXTENT_LOCKED | EXTENT_DIRTY | diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 18e1aa0f85f5..7acbd2cf6192 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3814,6 +3814,11 @@ process_slot: } btrfs_release_path(path); key.offset = next_key_min_offset; + + if (fatal_signal_pending(current)) { + ret = -EINTR; + goto out; + } } ret = 0; diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0ec8ffa37ab0..c4af0cdb783d 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2728,7 +2728,14 @@ static int do_relocation(struct btrfs_trans_handle *trans, bytenr = btrfs_node_blockptr(upper->eb, slot); if (lowest) { - BUG_ON(bytenr != node->bytenr); + if (bytenr != node->bytenr) { + btrfs_err(root->fs_info, + "lowest leaf/node mismatch: bytenr %llu node->bytenr %llu slot %d upper %llu", + bytenr, node->bytenr, slot, + upper->eb->start); + err = -EIO; + goto next; + } } else { if (node->eb->start == bytenr) goto next; diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 01bc36cec26e..71261b459863 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -5805,6 +5805,64 @@ static int changed_extent(struct send_ctx *sctx, int ret = 0; if (sctx->cur_ino != sctx->cmp_key->objectid) { + + if (result == BTRFS_COMPARE_TREE_CHANGED) { + struct extent_buffer *leaf_l; + struct extent_buffer *leaf_r; + struct btrfs_file_extent_item *ei_l; + struct btrfs_file_extent_item *ei_r; + + leaf_l = sctx->left_path->nodes[0]; + leaf_r = sctx->right_path->nodes[0]; + ei_l = btrfs_item_ptr(leaf_l, + sctx->left_path->slots[0], + struct btrfs_file_extent_item); + ei_r = btrfs_item_ptr(leaf_r, + sctx->right_path->slots[0], + struct btrfs_file_extent_item); + + /* + * We may have found an extent item that has changed + * only its disk_bytenr field and the corresponding + * inode item was not updated. This case happens due to + * very specific timings during relocation when a leaf + * that contains file extent items is COWed while + * relocation is ongoing and its in the stage where it + * updates data pointers. So when this happens we can + * safely ignore it since we know it's the same extent, + * but just at different logical and physical locations + * (when an extent is fully replaced with a new one, we + * know the generation number must have changed too, + * since snapshot creation implies committing the current + * transaction, and the inode item must have been updated + * as well). + * This replacement of the disk_bytenr happens at + * relocation.c:replace_file_extents() through + * relocation.c:btrfs_reloc_cow_block(). + */ + if (btrfs_file_extent_generation(leaf_l, ei_l) == + btrfs_file_extent_generation(leaf_r, ei_r) && + btrfs_file_extent_ram_bytes(leaf_l, ei_l) == + btrfs_file_extent_ram_bytes(leaf_r, ei_r) && + btrfs_file_extent_compression(leaf_l, ei_l) == + btrfs_file_extent_compression(leaf_r, ei_r) && + btrfs_file_extent_encryption(leaf_l, ei_l) == + btrfs_file_extent_encryption(leaf_r, ei_r) && + btrfs_file_extent_other_encoding(leaf_l, ei_l) == + btrfs_file_extent_other_encoding(leaf_r, ei_r) && + btrfs_file_extent_type(leaf_l, ei_l) == + btrfs_file_extent_type(leaf_r, ei_r) && + btrfs_file_extent_disk_bytenr(leaf_l, ei_l) != + btrfs_file_extent_disk_bytenr(leaf_r, ei_r) && + btrfs_file_extent_disk_num_bytes(leaf_l, ei_l) == + btrfs_file_extent_disk_num_bytes(leaf_r, ei_r) && + btrfs_file_extent_offset(leaf_l, ei_l) == + btrfs_file_extent_offset(leaf_r, ei_r) && + btrfs_file_extent_num_bytes(leaf_l, ei_l) == + btrfs_file_extent_num_bytes(leaf_r, ei_r)) + return 0; + } + inconsistent_snapshot_error(sctx, result, "extent"); return -EIO; } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 528cae123dc9..3d33c4e41e5f 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2713,14 +2713,12 @@ static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root, int index, int error) { struct btrfs_log_ctx *ctx; + struct btrfs_log_ctx *safe; - if (!error) { - INIT_LIST_HEAD(&root->log_ctxs[index]); - return; - } - - list_for_each_entry(ctx, &root->log_ctxs[index], list) + list_for_each_entry_safe(ctx, safe, &root->log_ctxs[index], list) { + list_del_init(&ctx->list); ctx->log_ret = error; + } INIT_LIST_HEAD(&root->log_ctxs[index]); } @@ -2961,13 +2959,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_unlock(&root->log_mutex); out_wake_log_root: - /* - * We needn't get log_mutex here because we are sure all - * the other tasks are blocked. - */ + mutex_lock(&log_root_tree->log_mutex); btrfs_remove_all_log_ctxs(log_root_tree, index2, ret); - mutex_lock(&log_root_tree->log_mutex); log_root_tree->log_transid_committed++; atomic_set(&log_root_tree->log_commit[index2], 0); mutex_unlock(&log_root_tree->log_mutex); @@ -2978,10 +2972,8 @@ out_wake_log_root: if (waitqueue_active(&log_root_tree->log_commit_wait[index2])) wake_up(&log_root_tree->log_commit_wait[index2]); out: - /* See above. */ - btrfs_remove_all_log_ctxs(root, index1, ret); - mutex_lock(&root->log_mutex); + btrfs_remove_all_log_ctxs(root, index1, ret); root->log_transid_committed++; atomic_set(&root->log_commit[index1], 0); mutex_unlock(&root->log_mutex); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 9d1554c7d036..159fc8f1a6a0 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -1268,7 +1268,8 @@ again: statret = __ceph_do_getattr(inode, page, CEPH_STAT_CAP_INLINE_DATA, !!page); if (statret < 0) { - __free_page(page); + if (page) + __free_page(page); if (statret == -ENODATA) { BUG_ON(retry_op != READ_INLINE); goto again; @@ -1765,7 +1766,6 @@ const struct file_operations ceph_file_fops = { .fsync = ceph_fsync, .lock = ceph_lock, .flock = ceph_flock, - .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .unlocked_ioctl = ceph_ioctl, .compat_ioctl = ceph_ioctl, diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 9d0522ba069c..284f0d807151 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1507,7 +1507,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ceph_fill_dirfrag(d_inode(parent), rinfo->dir_dir); } - if (ceph_frag_is_leftmost(frag) && req->r_readdir_offset == 2) { + if (ceph_frag_is_leftmost(frag) && req->r_readdir_offset == 2 && + !(rinfo->hash_order && req->r_path2)) { /* note dir version at start of readdir so we can tell * if any dentries get dropped */ req->r_dir_release_cnt = atomic64_read(&ci->i_release_count); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index fd2ea18a0ca3..f2f76696ddca 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -844,6 +844,8 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc) err = ceph_fs_debugfs_init(fsc); if (err < 0) goto fail; + } else { + root = dget(fsc->sb->s_root); } fsc->mount_state = CEPH_MOUNT_MOUNTED; diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 40b703217977..febc28f9e2c2 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -16,7 +16,7 @@ static int __remove_xattr(struct ceph_inode_info *ci, struct ceph_inode_xattr *xattr); -const struct xattr_handler ceph_other_xattr_handler; +static const struct xattr_handler ceph_other_xattr_handler; /* * List of handlers for synthetic system.* attributes. Other @@ -1086,7 +1086,7 @@ static int ceph_set_xattr_handler(const struct xattr_handler *handler, return __ceph_setxattr(inode, name, value, size, flags); } -const struct xattr_handler ceph_other_xattr_handler = { +static const struct xattr_handler ceph_other_xattr_handler = { .prefix = "", /* match any name => handlers called with full name */ .get = ceph_get_xattr_handler, .set = ceph_set_xattr_handler, diff --git a/fs/compat.c b/fs/compat.c index bd064a2c3550..543b48c29ac3 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -253,9 +253,9 @@ COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *, static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) { - if (sizeof ubuf->f_blocks == 4) { - if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail | - kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL) + if (sizeof(ubuf->f_bsize) == 4) { + if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen | + kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL) return -EOVERFLOW; /* f_files and f_ffree may be -1; it's okay * to stuff that into 32 bits */ diff --git a/fs/coredump.c b/fs/coredump.c index 281b768000e6..eb9c92c9b20f 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -1,6 +1,7 @@ #include <linux/slab.h> #include <linux/file.h> #include <linux/fdtable.h> +#include <linux/freezer.h> #include <linux/mm.h> #include <linux/stat.h> #include <linux/fcntl.h> @@ -423,7 +424,9 @@ static int coredump_wait(int exit_code, struct core_state *core_state) if (core_waiters > 0) { struct core_thread *ptr; + freezer_do_not_count(); wait_for_completion(&core_state->startup); + freezer_count(); /* * Wait for all the threads to become inactive, so that * all the thread context (extended register state, like diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 61057b7dbddb..98f87fe8f186 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -151,7 +151,10 @@ static int do_page_crypto(struct inode *inode, struct page *src_page, struct page *dest_page, gfp_t gfp_flags) { - u8 xts_tweak[FS_XTS_TWEAK_SIZE]; + struct { + __le64 index; + u8 padding[FS_XTS_TWEAK_SIZE - sizeof(__le64)]; + } xts_tweak; struct skcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist dst, src; @@ -171,17 +174,15 @@ static int do_page_crypto(struct inode *inode, req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, page_crypt_complete, &ecr); - BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index)); - memcpy(xts_tweak, &index, sizeof(index)); - memset(&xts_tweak[sizeof(index)], 0, - FS_XTS_TWEAK_SIZE - sizeof(index)); + BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE); + xts_tweak.index = cpu_to_le64(index); + memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding)); sg_init_table(&dst, 1); sg_set_page(&dst, dest_page, PAGE_SIZE, 0); sg_init_table(&src, 1); sg_set_page(&src, src_page, PAGE_SIZE, 0); - skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE, - xts_tweak); + skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE, &xts_tweak); if (rw == FS_DECRYPT) res = crypto_skcipher_decrypt(req); else diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 9a28133ac3b8..9b774f4b50c8 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -39,65 +39,54 @@ static void fname_crypt_complete(struct crypto_async_request *req, int res) static int fname_encrypt(struct inode *inode, const struct qstr *iname, struct fscrypt_str *oname) { - u32 ciphertext_len; struct skcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_skcipher *tfm = ci->ci_ctfm; int res = 0; char iv[FS_CRYPTO_BLOCK_SIZE]; - struct scatterlist src_sg, dst_sg; + struct scatterlist sg; int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); - char *workbuf, buf[32], *alloc_buf = NULL; - unsigned lim; + unsigned int lim; + unsigned int cryptlen; lim = inode->i_sb->s_cop->max_namelen(inode); if (iname->len <= 0 || iname->len > lim) return -EIO; - ciphertext_len = max(iname->len, (u32)FS_CRYPTO_BLOCK_SIZE); - ciphertext_len = round_up(ciphertext_len, padding); - ciphertext_len = min(ciphertext_len, lim); + /* + * Copy the filename to the output buffer for encrypting in-place and + * pad it with the needed number of NUL bytes. + */ + cryptlen = max_t(unsigned int, iname->len, FS_CRYPTO_BLOCK_SIZE); + cryptlen = round_up(cryptlen, padding); + cryptlen = min(cryptlen, lim); + memcpy(oname->name, iname->name, iname->len); + memset(oname->name + iname->len, 0, cryptlen - iname->len); - if (ciphertext_len <= sizeof(buf)) { - workbuf = buf; - } else { - alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); - if (!alloc_buf) - return -ENOMEM; - workbuf = alloc_buf; - } + /* Initialize the IV */ + memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); - /* Allocate request */ + /* Set up the encryption request */ req = skcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk_ratelimited(KERN_ERR - "%s: crypto_request_alloc() failed\n", __func__); - kfree(alloc_buf); + "%s: skcipher_request_alloc() failed\n", __func__); return -ENOMEM; } skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, fname_crypt_complete, &ecr); + sg_init_one(&sg, oname->name, cryptlen); + skcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv); - /* Copy the input */ - memcpy(workbuf, iname->name, iname->len); - if (iname->len < ciphertext_len) - memset(workbuf + iname->len, 0, ciphertext_len - iname->len); - - /* Initialize IV */ - memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); - - /* Create encryption request */ - sg_init_one(&src_sg, workbuf, ciphertext_len); - sg_init_one(&dst_sg, oname->name, ciphertext_len); - skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); + /* Do the encryption */ res = crypto_skcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { + /* Request is being completed asynchronously; wait for it */ wait_for_completion(&ecr.completion); res = ecr.res; } - kfree(alloc_buf); skcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR @@ -105,7 +94,7 @@ static int fname_encrypt(struct inode *inode, return res; } - oname->len = ciphertext_len; + oname->len = cryptlen; return 0; } diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 82f0285f5d08..67fb6d8876d0 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -185,7 +185,7 @@ int get_crypt_info(struct inode *inode) struct crypto_skcipher *ctfm; const char *cipher_str; int keysize; - u8 raw_key[FS_MAX_KEY_SIZE]; + u8 *raw_key = NULL; int res; res = fscrypt_initialize(); @@ -238,6 +238,15 @@ retry: if (res) goto out; + /* + * This cannot be a stack buffer because it is passed to the scatterlist + * crypto API as part of key derivation. + */ + res = -ENOMEM; + raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); + if (!raw_key) + goto out; + if (fscrypt_dummy_context_enabled(inode)) { memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE); goto got_key; @@ -276,7 +285,8 @@ got_key: if (res) goto out; - memzero_explicit(raw_key, sizeof(raw_key)); + kzfree(raw_key); + raw_key = NULL; if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) { put_crypt_info(crypt_info); goto retry; @@ -287,7 +297,7 @@ out: if (res == -ENOKEY) res = 0; put_crypt_info(crypt_info); - memzero_explicit(raw_key, sizeof(raw_key)); + kzfree(raw_key); return res; } diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index ed115acb5dee..6865663aac69 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -109,6 +109,8 @@ int fscrypt_process_policy(struct file *filp, if (ret) return ret; + inode_lock(inode); + if (!inode_has_encryption_context(inode)) { if (!S_ISDIR(inode->i_mode)) ret = -EINVAL; @@ -127,6 +129,8 @@ int fscrypt_process_policy(struct file *filp, ret = -EINVAL; } + inode_unlock(inode); + mnt_drop_write_file(filp); return ret; } diff --git a/fs/exec.c b/fs/exec.c index 6fcfb3f7b137..4e497b9ee71e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -191,6 +191,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, { struct page *page; int ret; + unsigned int gup_flags = FOLL_FORCE; #ifdef CONFIG_STACK_GROWSUP if (write) { @@ -199,12 +200,16 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, return NULL; } #endif + + if (write) + gup_flags |= FOLL_WRITE; + /* * We are doing an exec(). 'current' is the process * doing the exec and bprm->mm is the new process's mm. */ - ret = get_user_pages_remote(current, bprm->mm, pos, 1, write, - 1, &page, NULL); + ret = get_user_pages_remote(current, bprm->mm, pos, 1, gup_flags, + &page, NULL); if (ret <= 0) return NULL; diff --git a/fs/exofs/dir.c b/fs/exofs/dir.c index 79101651fe9e..42f9a0a0c4ca 100644 --- a/fs/exofs/dir.c +++ b/fs/exofs/dir.c @@ -137,7 +137,7 @@ Espan: bad_entry: EXOFS_ERR( "ERROR [exofs_check_page]: bad entry in directory(0x%lx): %s - " - "offset=%lu, inode=0x%llu, rec_len=%d, name_len=%d\n", + "offset=%lu, inode=0x%llx, rec_len=%d, name_len=%d\n", dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs, _LLU(le64_to_cpu(p->inode_no)), rec_len, p->name_len); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index d831e24dc885..41b8b44a391c 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -622,7 +622,7 @@ static int ext2_get_blocks(struct inode *inode, u32 *bno, bool *new, bool *boundary, int create) { - int err = -EIO; + int err; int offsets[4]; Indirect chain[4]; Indirect *partial; @@ -639,7 +639,7 @@ static int ext2_get_blocks(struct inode *inode, depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary); if (depth == 0) - return (err); + return -EIO; partial = ext2_get_branch(inode, depth, offsets, chain, &err); /* Simplest case - block found, no allocation needed */ @@ -761,7 +761,6 @@ static int ext2_get_blocks(struct inode *inode, ext2_splice_branch(inode, iblock, partial, indirect_blks, count); mutex_unlock(&ei->truncate_mutex); got_it: - *bno = le32_to_cpu(chain[depth-1].key); if (count > blocks_to_boundary) *boundary = true; err = count; @@ -772,6 +771,8 @@ cleanup: brelse(partial->bh); partial--; } + if (err > 0) + *bno = le32_to_cpu(chain[depth-1].key); return err; } diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 02ddec6d8a7d..fdb19543af1e 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -128,12 +128,12 @@ static void debug_print_tree(struct ext4_sb_info *sbi) node = rb_first(&sbi->system_blks); while (node) { entry = rb_entry(node, struct ext4_system_zone, node); - printk("%s%llu-%llu", first ? "" : ", ", + printk(KERN_CONT "%s%llu-%llu", first ? "" : ", ", entry->start_blk, entry->start_blk + entry->count - 1); first = 0; node = rb_next(node); } - printk("\n"); + printk(KERN_CONT "\n"); } int ext4_setup_system_zone(struct super_block *sb) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 282a51b07c57..a8a750f59621 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -235,6 +235,7 @@ struct ext4_io_submit { #define EXT4_MAX_BLOCK_SIZE 65536 #define EXT4_MIN_BLOCK_LOG_SIZE 10 #define EXT4_MAX_BLOCK_LOG_SIZE 16 +#define EXT4_MAX_CLUSTER_LOG_SIZE 30 #ifdef __KERNEL__ # define EXT4_BLOCK_SIZE(s) ((s)->s_blocksize) #else diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h index 3ef1df6ae9ec..1aba469f8220 100644 --- a/fs/ext4/mballoc.h +++ b/fs/ext4/mballoc.h @@ -27,16 +27,15 @@ #ifdef CONFIG_EXT4_DEBUG extern ushort ext4_mballoc_debug; -#define mb_debug(n, fmt, a...) \ - do { \ - if ((n) <= ext4_mballoc_debug) { \ - printk(KERN_DEBUG "(%s, %d): %s: ", \ - __FILE__, __LINE__, __func__); \ - printk(fmt, ## a); \ - } \ - } while (0) +#define mb_debug(n, fmt, ...) \ +do { \ + if ((n) <= ext4_mballoc_debug) { \ + printk(KERN_DEBUG "(%s, %d): %s: " fmt, \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + } \ +} while (0) #else -#define mb_debug(n, fmt, a...) no_printk(fmt, ## a) +#define mb_debug(n, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif #define EXT4_MB_HISTORY_ALLOC 1 /* allocation */ diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index f92f10d4f66a..104f8bfba718 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -577,12 +577,13 @@ static inline unsigned dx_node_limit(struct inode *dir) static void dx_show_index(char * label, struct dx_entry *entries) { int i, n = dx_get_count (entries); - printk(KERN_DEBUG "%s index ", label); + printk(KERN_DEBUG "%s index", label); for (i = 0; i < n; i++) { - printk("%x->%lu ", i ? dx_get_hash(entries + i) : - 0, (unsigned long)dx_get_block(entries + i)); + printk(KERN_CONT " %x->%lu", + i ? dx_get_hash(entries + i) : 0, + (unsigned long)dx_get_block(entries + i)); } - printk("\n"); + printk(KERN_CONT "\n"); } struct stats @@ -679,7 +680,7 @@ static struct stats dx_show_leaf(struct inode *dir, } de = ext4_next_entry(de, size); } - printk("(%i)\n", names); + printk(KERN_CONT "(%i)\n", names); return (struct stats) { names, space, 1 }; } @@ -798,7 +799,7 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, q = entries + count - 1; while (p <= q) { m = p + (q - p) / 2; - dxtrace(printk(".")); + dxtrace(printk(KERN_CONT ".")); if (dx_get_hash(m) > hash) q = m - 1; else @@ -810,7 +811,7 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, at = entries; while (n--) { - dxtrace(printk(",")); + dxtrace(printk(KERN_CONT ",")); if (dx_get_hash(++at) > hash) { at--; @@ -821,7 +822,8 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, } at = p - 1; - dxtrace(printk(" %x->%u\n", at == entries ? 0 : dx_get_hash(at), + dxtrace(printk(KERN_CONT " %x->%u\n", + at == entries ? 0 : dx_get_hash(at), dx_get_block(at))); frame->entries = entries; frame->at = at; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6db81fbcbaa6..52b0530c5d65 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -597,14 +597,15 @@ void __ext4_std_error(struct super_block *sb, const char *function, void __ext4_abort(struct super_block *sb, const char *function, unsigned int line, const char *fmt, ...) { + struct va_format vaf; va_list args; save_error_info(sb, function, line); va_start(args, fmt); - printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id, - function, line); - vprintk(fmt, args); - printk("\n"); + vaf.fmt = fmt; + vaf.va = &args; + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: %pV\n", + sb->s_id, function, line, &vaf); va_end(args); if ((sb->s_flags & MS_RDONLY) == 0) { @@ -2715,12 +2716,12 @@ static void print_daily_error_info(unsigned long arg) es->s_first_error_func, le32_to_cpu(es->s_first_error_line)); if (es->s_first_error_ino) - printk(": inode %u", + printk(KERN_CONT ": inode %u", le32_to_cpu(es->s_first_error_ino)); if (es->s_first_error_block) - printk(": block %llu", (unsigned long long) + printk(KERN_CONT ": block %llu", (unsigned long long) le64_to_cpu(es->s_first_error_block)); - printk("\n"); + printk(KERN_CONT "\n"); } if (es->s_last_error_time) { printk(KERN_NOTICE "EXT4-fs (%s): last error at time %u: %.*s:%d", @@ -2729,12 +2730,12 @@ static void print_daily_error_info(unsigned long arg) es->s_last_error_func, le32_to_cpu(es->s_last_error_line)); if (es->s_last_error_ino) - printk(": inode %u", + printk(KERN_CONT ": inode %u", le32_to_cpu(es->s_last_error_ino)); if (es->s_last_error_block) - printk(": block %llu", (unsigned long long) + printk(KERN_CONT ": block %llu", (unsigned long long) le64_to_cpu(es->s_last_error_block)); - printk("\n"); + printk(KERN_CONT "\n"); } mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */ } @@ -3564,7 +3565,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (blocksize < EXT4_MIN_BLOCK_SIZE || blocksize > EXT4_MAX_BLOCK_SIZE) { ext4_msg(sb, KERN_ERR, - "Unsupported filesystem blocksize %d", blocksize); + "Unsupported filesystem blocksize %d (%d log_block_size)", + blocksize, le32_to_cpu(es->s_log_block_size)); + goto failed_mount; + } + if (le32_to_cpu(es->s_log_block_size) > + (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) { + ext4_msg(sb, KERN_ERR, + "Invalid log block size: %u", + le32_to_cpu(es->s_log_block_size)); goto failed_mount; } @@ -3696,6 +3705,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "block size (%d)", clustersize, blocksize); goto failed_mount; } + if (le32_to_cpu(es->s_log_cluster_size) > + (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) { + ext4_msg(sb, KERN_ERR, + "Invalid log cluster size: %u", + le32_to_cpu(es->s_log_cluster_size)); + goto failed_mount; + } sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) - le32_to_cpu(es->s_log_block_size); sbi->s_clusters_per_group = diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index 73bcfd41f5f2..42145be5c6b4 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c @@ -223,14 +223,18 @@ static struct attribute *ext4_attrs[] = { EXT4_ATTR_FEATURE(lazy_itable_init); EXT4_ATTR_FEATURE(batched_discard); EXT4_ATTR_FEATURE(meta_bg_resize); +#ifdef CONFIG_EXT4_FS_ENCRYPTION EXT4_ATTR_FEATURE(encryption); +#endif EXT4_ATTR_FEATURE(metadata_csum_seed); static struct attribute *ext4_feat_attrs[] = { ATTR_LIST(lazy_itable_init), ATTR_LIST(batched_discard), ATTR_LIST(meta_bg_resize), +#ifdef CONFIG_EXT4_FS_ENCRYPTION ATTR_LIST(encryption), +#endif ATTR_LIST(metadata_csum_seed), NULL, }; diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index c15d63389957..d77be9e9f535 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -61,18 +61,12 @@ #include "acl.h" #ifdef EXT4_XATTR_DEBUG -# define ea_idebug(inode, f...) do { \ - printk(KERN_DEBUG "inode %s:%lu: ", \ - inode->i_sb->s_id, inode->i_ino); \ - printk(f); \ - printk("\n"); \ - } while (0) -# define ea_bdebug(bh, f...) do { \ - printk(KERN_DEBUG "block %pg:%lu: ", \ - bh->b_bdev, (unsigned long) bh->b_blocknr); \ - printk(f); \ - printk("\n"); \ - } while (0) +# define ea_idebug(inode, fmt, ...) \ + printk(KERN_DEBUG "inode %s:%lu: " fmt "\n", \ + inode->i_sb->s_id, inode->i_ino, ##__VA_ARGS__) +# define ea_bdebug(bh, fmt, ...) \ + printk(KERN_DEBUG "block %pg:%lu: " fmt "\n", \ + bh->b_bdev, (unsigned long)bh->b_blocknr, ##__VA_ARGS__) #else # define ea_idebug(inode, fmt, ...) no_printk(fmt, ##__VA_ARGS__) # define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__) @@ -241,7 +235,7 @@ __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, int error = -EFSCORRUPTED; if (((void *) header >= end) || - (header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC))) + (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC))) goto errout; error = ext4_xattr_check_names(entry, end, entry); errout: diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 93985c64d8a8..6f14ee923acd 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -852,16 +852,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, for (segno = start_segno; segno < end_segno; segno++) { - if (get_valid_blocks(sbi, segno, 1) == 0 || - unlikely(f2fs_cp_error(sbi))) - goto next; - /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), GET_SUM_BLOCK(sbi, segno)); - f2fs_bug_on(sbi, !PageUptodate(sum_page)); f2fs_put_page(sum_page, 0); + if (get_valid_blocks(sbi, segno, 1) == 0 || + !PageUptodate(sum_page) || + unlikely(f2fs_cp_error(sbi))) + goto next; + sum = page_address(sum_page); f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer))); diff --git a/fs/fcntl.c b/fs/fcntl.c index 350a2c8cfd28..6e2771c210f6 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -52,7 +52,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg) arg |= O_NONBLOCK; /* Pipe packetized mode is controlled by O_DIRECT flag */ - if (!S_ISFIFO(filp->f_inode->i_mode) && (arg & O_DIRECT)) { + if (!S_ISFIFO(inode->i_mode) && (arg & O_DIRECT)) { if (!filp->f_mapping || !filp->f_mapping->a_ops || !filp->f_mapping->a_ops->direct_IO) return -EINVAL; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6a4d0e5418a1..b3ebe512d64c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -286,6 +286,11 @@ const struct dentry_operations fuse_dentry_operations = { .d_release = fuse_dentry_release, }; +const struct dentry_operations fuse_root_dentry_operations = { + .d_init = fuse_dentry_init, + .d_release = fuse_dentry_release, +}; + int fuse_valid_type(int m) { return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || diff --git a/fs/fuse/file.c b/fs/fuse/file.c index abc66a6237fd..2401c5dabb2a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1985,6 +1985,10 @@ static int fuse_write_end(struct file *file, struct address_space *mapping, { struct inode *inode = page->mapping->host; + /* Haven't copied anything? Skip zeroing, size extending, dirtying. */ + if (!copied) + goto unlock; + if (!PageUptodate(page)) { /* Zero any unwritten bytes at the end of the page */ size_t endoff = (pos + copied) & ~PAGE_MASK; @@ -1995,6 +1999,8 @@ static int fuse_write_end(struct file *file, struct address_space *mapping, fuse_write_update_size(inode, pos + copied); set_page_dirty(page); + +unlock: unlock_page(page); put_page(page); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 0dfbb136e59a..91307940c8ac 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -692,6 +692,7 @@ static inline u64 get_node_id(struct inode *inode) extern const struct file_operations fuse_dev_operations; extern const struct dentry_operations fuse_dentry_operations; +extern const struct dentry_operations fuse_root_dentry_operations; /** * Inode to nodeid comparison. diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 17141099f2e7..6fe6a88ecb4a 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1131,10 +1131,11 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) err = -ENOMEM; root = fuse_get_root_inode(sb, d.rootmode); + sb->s_d_op = &fuse_root_dentry_operations; root_dentry = d_make_root(root); if (!root_dentry) goto err_dev_free; - /* only now - we want root dentry with NULL ->d_op */ + /* Root dentry doesn't have .d_revalidate */ sb->s_d_op = &fuse_dentry_operations; init_req = fuse_request_alloc(0); diff --git a/fs/iomap.c b/fs/iomap.c index 013d1d36fbbf..a8ee8c33ca78 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -433,8 +433,7 @@ iomap_page_mkwrite_actor(struct inode *inode, loff_t pos, loff_t length, struct page *page = data; int ret; - ret = __block_write_begin_int(page, pos & ~PAGE_MASK, length, - NULL, iomap); + ret = __block_write_begin_int(page, pos, length, NULL, iomap); if (ret) return ret; @@ -561,7 +560,7 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi, } while (len > 0) { - ret = iomap_apply(inode, start, len, 0, ops, &ctx, + ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx, iomap_fiemap_actor); /* inode with no (attribute) mapping will give ENOENT */ if (ret == -ENOENT) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index ad0c745ebad7..871c8b392099 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -687,6 +687,11 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) pri_bh = NULL; root_found: + /* We don't support read-write mounts */ + if (!(s->s_flags & MS_RDONLY)) { + error = -EACCES; + goto out_freebh; + } if (joliet_level && (pri == NULL || !opt.rock)) { /* This is the case of Joliet with the norock mount flag. @@ -1501,9 +1506,6 @@ struct inode *__isofs_iget(struct super_block *sb, static struct dentry *isofs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - /* We don't support read-write mounts */ - if (!(flags & MS_RDONLY)) - return ERR_PTR(-EACCES); return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super); } diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 3d8246a9faa4..e1652665bd93 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1149,6 +1149,7 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) JBUFFER_TRACE(jh, "file as BJ_Reserved"); spin_lock(&journal->j_list_lock); __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); + spin_unlock(&journal->j_list_lock); } else if (jh->b_transaction == journal->j_committing_transaction) { /* first access by this transaction */ jh->b_modified = 0; @@ -1156,8 +1157,8 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) JBUFFER_TRACE(jh, "set next transaction"); spin_lock(&journal->j_list_lock); jh->b_next_transaction = transaction; + spin_unlock(&journal->j_list_lock); } - spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); /* diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 2bcb86e6e6ca..78219d5644e9 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -911,6 +911,7 @@ const struct file_operations kernfs_file_fops = { .open = kernfs_fop_open, .release = kernfs_fop_release, .poll = kernfs_fop_poll, + .fsync = noop_fsync, }; /** diff --git a/fs/locks.c b/fs/locks.c index ce93b416b490..22c5b4aa4961 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1609,6 +1609,7 @@ int fcntl_getlease(struct file *filp) ctx = smp_load_acquire(&inode->i_flctx); if (ctx && !list_empty_careful(&ctx->flc_lease)) { + percpu_down_read_preempt_disable(&file_rwsem); spin_lock(&ctx->flc_lock); time_out_leases(inode, &dispose); list_for_each_entry(fl, &ctx->flc_lease, fl_list) { @@ -1618,6 +1619,8 @@ int fcntl_getlease(struct file *filp) break; } spin_unlock(&ctx->flc_lock); + percpu_up_read_preempt_enable(&file_rwsem); + locks_dispose_list(&dispose); } return type; @@ -2529,11 +2532,14 @@ locks_remove_lease(struct file *filp, struct file_lock_context *ctx) if (list_empty(&ctx->flc_lease)) return; + percpu_down_read_preempt_disable(&file_rwsem); spin_lock(&ctx->flc_lock); list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) if (filp == fl->fl_file) lease_modify(fl, F_UNLCK, &dispose); spin_unlock(&ctx->flc_lock); + percpu_up_read_preempt_enable(&file_rwsem); + locks_dispose_list(&dispose); } diff --git a/fs/logfs/Kconfig b/fs/logfs/Kconfig deleted file mode 100644 index 2b4503163930..000000000000 --- a/fs/logfs/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -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. diff --git a/fs/logfs/Makefile b/fs/logfs/Makefile deleted file mode 100644 index 4820027787ee..000000000000 --- a/fs/logfs/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/fs/logfs/compr.c b/fs/logfs/compr.c deleted file mode 100644 index 961f02b86d97..000000000000 --- a/fs/logfs/compr.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c deleted file mode 100644 index a8329cc47dec..000000000000 --- a/fs/logfs/dev_bdev.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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; -} diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c deleted file mode 100644 index b76a62b1978f..000000000000 --- a/fs/logfs/dev_mtd.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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; -} diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c deleted file mode 100644 index c87ea52de3d9..000000000000 --- a/fs/logfs/dir.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * 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, -}; diff --git a/fs/logfs/file.c b/fs/logfs/file.c deleted file mode 100644 index 1db04930ad57..000000000000 --- a/fs/logfs/file.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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, -}; diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c deleted file mode 100644 index d4efb061bdc5..000000000000 --- a/fs/logfs/gc.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c deleted file mode 100644 index f440a1525da8..000000000000 --- a/fs/logfs/inode.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c deleted file mode 100644 index 2a09b8d73989..000000000000 --- a/fs/logfs/journal.c +++ /dev/null @@ -1,894 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h deleted file mode 100644 index 27d040e35faa..000000000000 --- a/fs/logfs/logfs.h +++ /dev/null @@ -1,735 +0,0 @@ -/* - * 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 diff --git a/fs/logfs/logfs_abi.h b/fs/logfs/logfs_abi.h deleted file mode 100644 index ae960519c54a..000000000000 --- a/fs/logfs/logfs_abi.h +++ /dev/null @@ -1,629 +0,0 @@ -/* - * 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 diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c deleted file mode 100644 index bf19bf4a243f..000000000000 --- a/fs/logfs/readwrite.c +++ /dev/null @@ -1,2298 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c deleted file mode 100644 index 1efd6055f4b0..000000000000 --- a/fs/logfs/segment.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * 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); -} diff --git a/fs/logfs/super.c b/fs/logfs/super.c deleted file mode 100644 index 5751082dba52..000000000000 --- a/fs/logfs/super.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * 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"); diff --git a/fs/namei.c b/fs/namei.c index 5b4eed221530..092ac5667ec7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1725,30 +1725,35 @@ static int pick_link(struct nameidata *nd, struct path *link, return 1; } +enum {WALK_FOLLOW = 1, WALK_MORE = 2}; + /* * Do we need to follow links? We _really_ want to be able * to do this check without having to look at inode->i_op, * so we keep a cache of "no, this doesn't need follow_link" * for the common case. */ -static inline int should_follow_link(struct nameidata *nd, struct path *link, - int follow, - struct inode *inode, unsigned seq) +static inline int step_into(struct nameidata *nd, struct path *path, + int flags, struct inode *inode, unsigned seq) { - if (likely(!d_is_symlink(link->dentry))) - return 0; - if (!follow) + if (!(flags & WALK_MORE) && nd->depth) + put_link(nd); + if (likely(!d_is_symlink(path->dentry)) || + !(flags & WALK_FOLLOW || nd->flags & LOOKUP_FOLLOW)) { + /* not a symlink or should not follow */ + path_to_nameidata(path, nd); + nd->inode = inode; + nd->seq = seq; return 0; + } /* make sure that d_is_symlink above matches inode */ if (nd->flags & LOOKUP_RCU) { - if (read_seqcount_retry(&link->dentry->d_seq, seq)) + if (read_seqcount_retry(&path->dentry->d_seq, seq)) return -ECHILD; } - return pick_link(nd, link, inode, seq); + return pick_link(nd, path, inode, seq); } -enum {WALK_GET = 1, WALK_PUT = 2}; - static int walk_component(struct nameidata *nd, int flags) { struct path path; @@ -1762,7 +1767,7 @@ static int walk_component(struct nameidata *nd, int flags) */ if (unlikely(nd->last_type != LAST_NORM)) { err = handle_dots(nd, nd->last_type); - if (flags & WALK_PUT) + if (!(flags & WALK_MORE) && nd->depth) put_link(nd); return err; } @@ -1789,15 +1794,7 @@ static int walk_component(struct nameidata *nd, int flags) inode = d_backing_inode(path.dentry); } - if (flags & WALK_PUT) - put_link(nd); - err = should_follow_link(nd, &path, flags & WALK_GET, inode, seq); - if (unlikely(err)) - return err; - path_to_nameidata(&path, nd); - nd->inode = inode; - nd->seq = seq; - return 0; + return step_into(nd, &path, flags, inode, seq); } /* @@ -2104,9 +2101,10 @@ OK: if (!name) return 0; /* last component of nested symlink */ - err = walk_component(nd, WALK_GET | WALK_PUT); + err = walk_component(nd, WALK_FOLLOW); } else { - err = walk_component(nd, WALK_GET); + /* not the last component */ + err = walk_component(nd, WALK_FOLLOW | WALK_MORE); } if (err < 0) return err; @@ -2248,12 +2246,7 @@ static inline int lookup_last(struct nameidata *nd) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - return walk_component(nd, - nd->flags & LOOKUP_FOLLOW - ? nd->depth - ? WALK_PUT | WALK_GET - : WALK_GET - : 0); + return walk_component(nd, 0); } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ @@ -2558,28 +2551,9 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, } EXPORT_SYMBOL(user_path_at_empty); -/* - * NB: most callers don't do anything directly with the reference to the - * to struct filename, but the nd->last pointer points into the name string - * allocated by getname. So we must hold the reference to it until all - * path-walking is complete. - */ -static inline struct filename * -user_path_parent(int dfd, const char __user *path, - struct path *parent, - struct qstr *last, - int *type, - unsigned int flags) -{ - /* only LOOKUP_REVAL is allowed in extra flags */ - return filename_parentat(dfd, getname(path), flags & LOOKUP_REVAL, - parent, last, type); -} - /** * mountpoint_last - look up last component for umount * @nd: pathwalk nameidata - currently pointing at parent directory of "last" - * @path: pointer to container for result * * This is a special lookup_last function just for umount. In this case, we * need to resolve the path without doing any revalidation. @@ -2592,23 +2566,20 @@ user_path_parent(int dfd, const char __user *path, * * Returns: * -error: if there was an error during lookup. This includes -ENOENT if the - * lookup found a negative dentry. The nd->path reference will also be - * put in this case. + * lookup found a negative dentry. * - * 0: if we successfully resolved nd->path and found it to not to be a - * symlink that needs to be followed. "path" will also be populated. - * The nd->path reference will also be put. + * 0: if we successfully resolved nd->last and found it to not to be a + * symlink that needs to be followed. * * 1: if we successfully resolved nd->last and found it to be a symlink - * that needs to be followed. "path" will be populated with the path - * to the link, and nd->path will *not* be put. + * that needs to be followed. */ static int -mountpoint_last(struct nameidata *nd, struct path *path) +mountpoint_last(struct nameidata *nd) { int error = 0; - struct dentry *dentry; struct dentry *dir = nd->path.dentry; + struct path path; /* If we're in rcuwalk, drop out of it to handle last component */ if (nd->flags & LOOKUP_RCU) { @@ -2622,37 +2593,28 @@ mountpoint_last(struct nameidata *nd, struct path *path) error = handle_dots(nd, nd->last_type); if (error) return error; - dentry = dget(nd->path.dentry); + path.dentry = dget(nd->path.dentry); } else { - dentry = d_lookup(dir, &nd->last); - if (!dentry) { + path.dentry = d_lookup(dir, &nd->last); + if (!path.dentry) { /* * No cached dentry. Mounted dentries are pinned in the * cache, so that means that this dentry is probably * a symlink or the path doesn't actually point * to a mounted dentry. */ - dentry = lookup_slow(&nd->last, dir, + path.dentry = lookup_slow(&nd->last, dir, nd->flags | LOOKUP_NO_REVAL); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); + if (IS_ERR(path.dentry)) + return PTR_ERR(path.dentry); } } - if (d_is_negative(dentry)) { - dput(dentry); + if (d_is_negative(path.dentry)) { + dput(path.dentry); return -ENOENT; } - if (nd->depth) - put_link(nd); - path->dentry = dentry; - path->mnt = nd->path.mnt; - error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW, - d_backing_inode(dentry), 0); - if (unlikely(error)) - return error; - mntget(path->mnt); - follow_mount(path); - return 0; + path.mnt = nd->path.mnt; + return step_into(nd, &path, 0, d_backing_inode(path.dentry), 0); } /** @@ -2672,13 +2634,19 @@ path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path) if (IS_ERR(s)) return PTR_ERR(s); while (!(err = link_path_walk(s, nd)) && - (err = mountpoint_last(nd, path)) > 0) { + (err = mountpoint_last(nd)) > 0) { s = trailing_symlink(nd); if (IS_ERR(s)) { err = PTR_ERR(s); break; } } + if (!err) { + *path = nd->path; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + follow_mount(path); + } terminate_walk(nd); return err; } @@ -3335,18 +3303,11 @@ static int do_last(struct nameidata *nd, seq = 0; /* out of RCU mode, so the value doesn't matter */ inode = d_backing_inode(path.dentry); finish_lookup: - if (nd->depth) - put_link(nd); - error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW, - inode, seq); + error = step_into(nd, &path, 0, inode, seq); if (unlikely(error)) return error; - - path_to_nameidata(&path, nd); - nd->inode = inode; - nd->seq = seq; - /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ finish_open: + /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ error = complete_walk(nd); if (error) return error; @@ -3861,8 +3822,8 @@ static long do_rmdir(int dfd, const char __user *pathname) int type; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, - &path, &last, &type, lookup_flags); + name = filename_parentat(dfd, getname(pathname), lookup_flags, + &path, &last, &type); if (IS_ERR(name)) return PTR_ERR(name); @@ -3991,8 +3952,8 @@ static long do_unlinkat(int dfd, const char __user *pathname) struct inode *delegated_inode = NULL; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, - &path, &last, &type, lookup_flags); + name = filename_parentat(dfd, getname(pathname), lookup_flags, + &path, &last, &type); if (IS_ERR(name)) return PTR_ERR(name); @@ -4491,15 +4452,15 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, target_flags = 0; retry: - from = user_path_parent(olddfd, oldname, - &old_path, &old_last, &old_type, lookup_flags); + from = filename_parentat(olddfd, getname(oldname), lookup_flags, + &old_path, &old_last, &old_type); if (IS_ERR(from)) { error = PTR_ERR(from); goto exit; } - to = user_path_parent(newdfd, newname, - &new_path, &new_last, &new_type, lookup_flags); + to = filename_parentat(newdfd, getname(newname), lookup_flags, + &new_path, &new_last, &new_type); if (IS_ERR(to)) { error = PTR_ERR(to); goto exit1; diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index dd38ca1f2ecb..83ca77231707 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -203,7 +203,7 @@ ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) bufsize - (pos % bufsize), iov_iter_count(from)); - if (copy_from_iter(bouncebuffer, to_write, from) != to_write) { + if (!copy_from_iter_full(bouncebuffer, to_write, from)) { errno = -EFAULT; break; } diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 217847679f0e..2905479f214a 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -344,9 +344,10 @@ static void bl_write_cleanup(struct work_struct *work) u64 start = hdr->args.offset & (loff_t)PAGE_MASK; u64 end = (hdr->args.offset + hdr->args.count + PAGE_SIZE - 1) & (loff_t)PAGE_MASK; + u64 lwb = hdr->args.offset + hdr->args.count; ext_tree_mark_written(bl, start >> SECTOR_SHIFT, - (end - start) >> SECTOR_SHIFT, end); + (end - start) >> SECTOR_SHIFT, lwb); } pnfs_ld_write_done(hdr); diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 532d8e242d4d..484bebc20bca 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -197,7 +197,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, } ret = -EPROTONOSUPPORT; - if (minorversion == 0) + if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0) ret = nfs4_callback_up_net(serv, net); else if (xprt->ops->bc_up) ret = xprt->ops->bc_up(serv, net); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 7555ba889d1f..ebecfb8fba06 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -314,7 +314,8 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat /* Match the full socket address */ if (!rpc_cmp_addr_port(sap, clap)) /* Match all xprt_switch full socket addresses */ - if (!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, + if (IS_ERR(clp->cl_rpcclient) || + !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, sap)) continue; diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index c8162c660c44..5551e8ef67fd 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -98,7 +98,7 @@ rename_retry: return end; } namelen = strlen(base); - if (flags & NFS_PATH_CANONICAL) { + if (*end == '/') { /* Strip off excess slashes in base string */ while (namelen > 0 && base[namelen - 1] == '/') namelen--; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 9b3a82abab07..1452177c822d 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -542,6 +542,13 @@ static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state) return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0; } +static inline bool nfs4_state_match_open_stateid_other(const struct nfs4_state *state, + const nfs4_stateid *stateid) +{ + return test_bit(NFS_OPEN_STATE, &state->flags) && + nfs4_stateid_match_other(&state->open_stateid, stateid); +} + #else #define nfs4_close_state(a, b) do { } while (0) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ad917bd72b38..241da19b7da4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1451,7 +1451,6 @@ static void nfs_resync_open_stateid_locked(struct nfs4_state *state) } static void nfs_clear_open_stateid_locked(struct nfs4_state *state, - nfs4_stateid *arg_stateid, nfs4_stateid *stateid, fmode_t fmode) { clear_bit(NFS_O_RDWR_STATE, &state->flags); @@ -1469,10 +1468,9 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state, } if (stateid == NULL) return; - /* Handle races with OPEN */ - if (!nfs4_stateid_match_other(arg_stateid, &state->open_stateid) || - (nfs4_stateid_match_other(stateid, &state->open_stateid) && - !nfs4_stateid_is_newer(stateid, &state->open_stateid))) { + /* Handle OPEN+OPEN_DOWNGRADE races */ + if (nfs4_stateid_match_other(stateid, &state->open_stateid) && + !nfs4_stateid_is_newer(stateid, &state->open_stateid)) { nfs_resync_open_stateid_locked(state); return; } @@ -1486,7 +1484,9 @@ static void nfs_clear_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) { write_seqlock(&state->seqlock); - nfs_clear_open_stateid_locked(state, arg_stateid, stateid, fmode); + /* Ignore, if the CLOSE argment doesn't match the current stateid */ + if (nfs4_state_match_open_stateid_other(state, arg_stateid)) + nfs_clear_open_stateid_locked(state, stateid, fmode); write_sequnlock(&state->seqlock); if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) nfs4_schedule_state_manager(state->owner->so_server->nfs_client); @@ -1545,7 +1545,7 @@ static int update_open_stateid(struct nfs4_state *state, struct nfs_client *clp = server->nfs_client; struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_delegation *deleg_cur; - nfs4_stateid freeme = {0}; + nfs4_stateid freeme = { }; int ret = 0; fmode &= (FMODE_READ|FMODE_WRITE); @@ -2564,15 +2564,23 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state) static int nfs41_check_expired_locks(struct nfs4_state *state) { int status, ret = NFS_OK; - struct nfs4_lock_state *lsp; + struct nfs4_lock_state *lsp, *prev = NULL; struct nfs_server *server = NFS_SERVER(state->inode); if (!test_bit(LK_STATE_IN_USE, &state->flags)) goto out; + + spin_lock(&state->state_lock); list_for_each_entry(lsp, &state->lock_states, ls_locks) { if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { struct rpc_cred *cred = lsp->ls_state->owner->so_cred; + atomic_inc(&lsp->ls_count); + spin_unlock(&state->state_lock); + + nfs4_put_lock_state(prev); + prev = lsp; + status = nfs41_test_and_free_expired_stateid(server, &lsp->ls_stateid, cred); @@ -2585,10 +2593,14 @@ static int nfs41_check_expired_locks(struct nfs4_state *state) set_bit(NFS_LOCK_LOST, &lsp->ls_flags); } else if (status != NFS_OK) { ret = status; - break; + nfs4_put_lock_state(prev); + goto out; } + spin_lock(&state->state_lock); } - }; + } + spin_unlock(&state->state_lock); + nfs4_put_lock_state(prev); out: return ret; } @@ -3122,7 +3134,8 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) } else if (is_rdwr) calldata->arg.fmode |= FMODE_READ|FMODE_WRITE; - if (!nfs4_valid_open_stateid(state)) + if (!nfs4_valid_open_stateid(state) || + test_bit(NFS_OPEN_STATE, &state->flags) == 0) call_close = 0; spin_unlock(&state->owner->so_lock); @@ -5569,6 +5582,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) switch (task->tk_status) { case 0: renew_lease(data->res.server, data->timestamp); + break; case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_DELEG_REVOKED: case -NFS4ERR_EXPIRED: @@ -5579,8 +5593,6 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) case -NFS4ERR_OLD_STATEID: case -NFS4ERR_STALE_STATEID: task->tk_status = 0; - if (data->roc) - pnfs_roc_set_barrier(data->inode, data->roc_barrier); break; default: if (nfs4_async_handle_error(task, data->res.server, @@ -5590,6 +5602,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) } } data->rpc_status = task->tk_status; + if (data->roc && data->rpc_status == 0) + pnfs_roc_set_barrier(data->inode, data->roc_barrier); } static void nfs4_delegreturn_release(void *calldata) diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index b62973045a3e..a61350f75c74 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -178,12 +178,14 @@ static int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid, __must_hold(&tbl->slot_tbl_lock) { struct nfs4_slot *slot; + int ret; slot = nfs4_lookup_slot(tbl, slotid); - if (IS_ERR(slot)) - return PTR_ERR(slot); - *seq_nr = slot->seq_nr; - return 0; + ret = PTR_ERR_OR_ZERO(slot); + if (!ret) + *seq_nr = slot->seq_nr; + + return ret; } /* @@ -196,7 +198,7 @@ static int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid, static bool nfs4_slot_seqid_in_use(struct nfs4_slot_table *tbl, u32 slotid, u32 seq_nr) { - u32 cur_seq; + u32 cur_seq = 0; bool ret = false; spin_lock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 5f4281ec5f72..0959c9661662 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1547,6 +1547,7 @@ restart: ssleep(1); case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_OLD_STATEID: case -NFS4ERR_BAD_STATEID: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 56b2d96f9103..259ef85f435a 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -146,6 +146,8 @@ set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh, u32 id; int i; + if (fsinfo->nlayouttypes == 0) + goto out_no_driver; if (!(server->nfs_client->cl_exchange_flags & (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) { printk(KERN_ERR "NFS: %s: cl_exchange_flags 0x%x\n", diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index b10d557f9c9e..ee36efd5aece 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -84,6 +84,8 @@ struct nfsd_net { struct list_head client_lru; struct list_head close_lru; struct list_head del_recall_lru; + + /* protected by blocked_locks_lock */ struct list_head blocked_locks_lru; struct delayed_work laundromat_work; @@ -91,6 +93,9 @@ struct nfsd_net { /* client_lock protects the client lru list and session hash table */ spinlock_t client_lock; + /* protects blocked_locks_lru */ + spinlock_t blocked_locks_lock; + struct file *rec_file; bool in_grace; const struct nfsd4_client_tracking_ops *client_tracking_ops; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9752beb78659..4b4beaaa4eaa 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -217,7 +217,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh, { struct nfsd4_blocked_lock *cur, *found = NULL; - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); list_for_each_entry(cur, &lo->lo_blocked, nbl_list) { if (fh_match(fh, &cur->nbl_fh)) { list_del_init(&cur->nbl_list); @@ -226,7 +226,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh, break; } } - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); if (found) posix_unblock_lock(&found->nbl_lock); return found; @@ -1227,9 +1227,7 @@ static void put_ol_stateid_locked(struct nfs4_ol_stateid *stp, static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp) { - struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); - - lockdep_assert_held(&oo->oo_owner.so_client->cl_lock); + lockdep_assert_held(&stp->st_stid.sc_client->cl_lock); list_del_init(&stp->st_locks); nfs4_unhash_stid(&stp->st_stid); @@ -1238,12 +1236,12 @@ static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp) static void release_lock_stateid(struct nfs4_ol_stateid *stp) { - struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); + struct nfs4_client *clp = stp->st_stid.sc_client; bool unhashed; - spin_lock(&oo->oo_owner.so_client->cl_lock); + spin_lock(&clp->cl_lock); unhashed = unhash_lock_stateid(stp); - spin_unlock(&oo->oo_owner.so_client->cl_lock); + spin_unlock(&clp->cl_lock); if (unhashed) nfs4_put_stid(&stp->st_stid); } @@ -4665,7 +4663,7 @@ nfs4_laundromat(struct nfsd_net *nn) * indefinitely once the lock does become free. */ BUG_ON(!list_empty(&reaplist)); - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); while (!list_empty(&nn->blocked_locks_lru)) { nbl = list_first_entry(&nn->blocked_locks_lru, struct nfsd4_blocked_lock, nbl_lru); @@ -4678,7 +4676,7 @@ nfs4_laundromat(struct nfsd_net *nn) list_move(&nbl->nbl_lru, &reaplist); list_del_init(&nbl->nbl_list); } - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); while (!list_empty(&reaplist)) { nbl = list_first_entry(&nn->blocked_locks_lru, @@ -5439,13 +5437,13 @@ nfsd4_lm_notify(struct file_lock *fl) bool queue = false; /* An empty list means that something else is going to be using it */ - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); if (!list_empty(&nbl->nbl_list)) { list_del_init(&nbl->nbl_list); list_del_init(&nbl->nbl_lru); queue = true; } - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); if (queue) nfsd4_run_cb(&nbl->nbl_cb); @@ -5868,10 +5866,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (fl_flags & FL_SLEEP) { nbl->nbl_time = jiffies; - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked); list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru); - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); } err = vfs_lock_file(filp, F_SETLK, file_lock, conflock); @@ -5900,10 +5898,10 @@ out: if (nbl) { /* dequeue it if we queued it before */ if (fl_flags & FL_SLEEP) { - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); list_del_init(&nbl->nbl_list); list_del_init(&nbl->nbl_lru); - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); } free_blocked_lock(nbl); } @@ -6943,9 +6941,11 @@ static int nfs4_state_create_net(struct net *net) INIT_LIST_HEAD(&nn->client_lru); INIT_LIST_HEAD(&nn->close_lru); INIT_LIST_HEAD(&nn->del_recall_lru); - INIT_LIST_HEAD(&nn->blocked_locks_lru); spin_lock_init(&nn->client_lock); + spin_lock_init(&nn->blocked_locks_lock); + INIT_LIST_HEAD(&nn->blocked_locks_lru); + INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); get_net(net); @@ -7063,14 +7063,14 @@ nfs4_state_shutdown_net(struct net *net) } BUG_ON(!list_empty(&reaplist)); - spin_lock(&nn->client_lock); + spin_lock(&nn->blocked_locks_lock); while (!list_empty(&nn->blocked_locks_lru)) { nbl = list_first_entry(&nn->blocked_locks_lru, struct nfsd4_blocked_lock, nbl_lru); list_move(&nbl->nbl_lru, &reaplist); list_del_init(&nbl->nbl_list); } - spin_unlock(&nn->client_lock); + spin_unlock(&nn->blocked_locks_lock); while (!list_empty(&reaplist)) { nbl = list_first_entry(&nn->blocked_locks_lru, diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index a18613579001..0ee19ecc982d 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1544,8 +1544,6 @@ const struct file_operations ntfs_dir_ops = { .iterate = ntfs_readdir, /* Read directory contents. */ #ifdef NTFS_RW .fsync = ntfs_dir_fsync, /* Sync a directory to disk. */ - /*.aio_fsync = ,*/ /* Sync all outstanding async - i/o operations on a kiocb. */ #endif /* NTFS_RW */ /*.ioctl = ,*/ /* Perform function on the mounted filesystem. */ diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index e7054e2ac922..3ecb9f337b7d 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -3699,7 +3699,7 @@ static void ocfs2_dx_dir_transfer_leaf(struct inode *dir, u32 split_hash, static int ocfs2_dx_dir_rebalance_credits(struct ocfs2_super *osb, struct ocfs2_dx_root_block *dx_root) { - int credits = ocfs2_clusters_to_blocks(osb->sb, 2); + int credits = ocfs2_clusters_to_blocks(osb->sb, 3); credits += ocfs2_calc_extend_credits(osb->sb, &dx_root->dr_list); credits += ocfs2_quota_trans_credits(osb->sb); diff --git a/fs/orangefs/dcache.c b/fs/orangefs/dcache.c index 1e8fe844e69f..5355efba4bc8 100644 --- a/fs/orangefs/dcache.c +++ b/fs/orangefs/dcache.c @@ -73,7 +73,7 @@ static int orangefs_revalidate_lookup(struct dentry *dentry) } } - dentry->d_time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + orangefs_set_timeout(dentry); ret = 1; out_release_op: op_release(new_op); @@ -94,8 +94,9 @@ out_drop: static int orangefs_d_revalidate(struct dentry *dentry, unsigned int flags) { int ret; + unsigned long time = (unsigned long) dentry->d_fsdata; - if (time_before(jiffies, dentry->d_time)) + if (time_before(jiffies, time)) return 1; if (flags & LOOKUP_RCU) diff --git a/fs/orangefs/devorangefs-req.c b/fs/orangefs/devorangefs-req.c index 516ffb4dc9a0..b0ced669427e 100644 --- a/fs/orangefs/devorangefs-req.c +++ b/fs/orangefs/devorangefs-req.c @@ -355,7 +355,6 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb, __u64 tag; } head; int total = ret = iov_iter_count(iter); - int n; int downcall_size = sizeof(struct orangefs_downcall_s); int head_size = sizeof(head); @@ -372,8 +371,7 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb, return -EFAULT; } - n = copy_from_iter(&head, head_size, iter); - if (n < head_size) { + if (!copy_from_iter_full(&head, head_size, iter)) { gossip_err("%s: failed to copy head.\n", __func__); return -EFAULT; } @@ -407,8 +405,7 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb, return ret; } - n = copy_from_iter(&op->downcall, downcall_size, iter); - if (n != downcall_size) { + if (!copy_from_iter_full(&op->downcall, downcall_size, iter)) { gossip_err("%s: failed to copy downcall.\n", __func__); goto Efault; } @@ -462,10 +459,8 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb, goto Enomem; } memset(op->downcall.trailer_buf, 0, op->downcall.trailer_size); - n = copy_from_iter(op->downcall.trailer_buf, - op->downcall.trailer_size, - iter); - if (n != op->downcall.trailer_size) { + if (!copy_from_iter_full(op->downcall.trailer_buf, + op->downcall.trailer_size, iter)) { gossip_err("%s: failed to copy trailer.\n", __func__); vfree(op->downcall.trailer_buf); goto Efault; diff --git a/fs/orangefs/file.c b/fs/orangefs/file.c index 66ea0cc37b18..e6bbc8083d77 100644 --- a/fs/orangefs/file.c +++ b/fs/orangefs/file.c @@ -621,9 +621,9 @@ static int orangefs_file_release(struct inode *inode, struct file *file) * readahead cache (if any); this forces an expensive refresh of * data for the next caller of mmap (or 'get_block' accesses) */ - if (file->f_path.dentry->d_inode && - file->f_path.dentry->d_inode->i_mapping && - mapping_nrpages(&file->f_path.dentry->d_inode->i_data)) { + if (file_inode(file) && + file_inode(file)->i_mapping && + mapping_nrpages(&file_inode(file)->i_data)) { if (orangefs_features & ORANGEFS_FEATURE_READAHEAD) { gossip_debug(GOSSIP_INODE_DEBUG, "calling flush_racache on %pU\n", @@ -632,7 +632,7 @@ static int orangefs_file_release(struct inode *inode, struct file *file) gossip_debug(GOSSIP_INODE_DEBUG, "flush_racache finished\n"); } - truncate_inode_pages(file->f_path.dentry->d_inode->i_mapping, + truncate_inode_pages(file_inode(file)->i_mapping, 0); } return 0; @@ -648,7 +648,7 @@ static int orangefs_fsync(struct file *file, { int ret = -EINVAL; struct orangefs_inode_s *orangefs_inode = - ORANGEFS_I(file->f_path.dentry->d_inode); + ORANGEFS_I(file_inode(file)); struct orangefs_kernel_op_s *new_op = NULL; /* required call */ @@ -661,7 +661,7 @@ static int orangefs_fsync(struct file *file, ret = service_operation(new_op, "orangefs_fsync", - get_interruptible_flag(file->f_path.dentry->d_inode)); + get_interruptible_flag(file_inode(file))); gossip_debug(GOSSIP_FILE_DEBUG, "orangefs_fsync got return value of %d\n", @@ -669,7 +669,7 @@ static int orangefs_fsync(struct file *file, op_release(new_op); - orangefs_flush_inode(file->f_path.dentry->d_inode); + orangefs_flush_inode(file_inode(file)); return ret; } @@ -724,7 +724,7 @@ static int orangefs_lock(struct file *filp, int cmd, struct file_lock *fl) { int rc = -EINVAL; - if (ORANGEFS_SB(filp->f_inode->i_sb)->flags & ORANGEFS_OPT_LOCAL_LOCK) { + if (ORANGEFS_SB(file_inode(filp)->i_sb)->flags & ORANGEFS_OPT_LOCAL_LOCK) { if (cmd == F_GETLK) { rc = 0; posix_test_lock(filp, fl); diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c index d15d3d2dba62..a290ff6ec756 100644 --- a/fs/orangefs/namei.c +++ b/fs/orangefs/namei.c @@ -72,7 +72,7 @@ static int orangefs_create(struct inode *dir, d_instantiate(dentry, inode); unlock_new_inode(inode); - dentry->d_time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + orangefs_set_timeout(dentry); ORANGEFS_I(inode)->getattr_time = jiffies - 1; gossip_debug(GOSSIP_NAME_DEBUG, @@ -183,7 +183,7 @@ static struct dentry *orangefs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - dentry->d_time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + orangefs_set_timeout(dentry); inode = orangefs_iget(dir->i_sb, &new_op->downcall.resp.lookup.refn); if (IS_ERR(inode)) { @@ -322,7 +322,7 @@ static int orangefs_symlink(struct inode *dir, d_instantiate(dentry, inode); unlock_new_inode(inode); - dentry->d_time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + orangefs_set_timeout(dentry); ORANGEFS_I(inode)->getattr_time = jiffies - 1; gossip_debug(GOSSIP_NAME_DEBUG, @@ -386,7 +386,7 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode d_instantiate(dentry, inode); unlock_new_inode(inode); - dentry->d_time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + orangefs_set_timeout(dentry); ORANGEFS_I(inode)->getattr_time = jiffies - 1; gossip_debug(GOSSIP_NAME_DEBUG, diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index eb09aa026723..f1e824979aad 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -114,6 +114,7 @@ static const struct seq_operations help_debug_ops = { }; const struct file_operations debug_help_fops = { + .owner = THIS_MODULE, .open = orangefs_debug_help_open, .read = seq_read, .release = seq_release, @@ -121,6 +122,7 @@ const struct file_operations debug_help_fops = { }; static const struct file_operations kernel_debug_fops = { + .owner = THIS_MODULE, .open = orangefs_debug_open, .read = orangefs_debug_read, .write = orangefs_debug_write, @@ -141,6 +143,9 @@ static struct client_debug_mask client_debug_mask; */ static DEFINE_MUTEX(orangefs_debug_lock); +/* Used to protect data in ORANGEFS_KMOD_DEBUG_HELP_FILE */ +static DEFINE_MUTEX(orangefs_help_file_lock); + /* * initialize kmod debug operations, create orangefs debugfs dir and * ORANGEFS_KMOD_DEBUG_HELP_FILE. @@ -289,6 +294,8 @@ static void *help_start(struct seq_file *m, loff_t *pos) gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_start: start\n"); + mutex_lock(&orangefs_help_file_lock); + if (*pos == 0) payload = m->private; @@ -305,6 +312,7 @@ static void *help_next(struct seq_file *m, void *v, loff_t *pos) static void help_stop(struct seq_file *m, void *p) { gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_stop: start\n"); + mutex_unlock(&orangefs_help_file_lock); } static int help_show(struct seq_file *m, void *v) @@ -426,6 +434,7 @@ static ssize_t orangefs_debug_write(struct file *file, char *debug_string; struct orangefs_kernel_op_s *new_op = NULL; struct client_debug_mask c_mask = { NULL, 0, 0 }; + char *s; gossip_debug(GOSSIP_DEBUGFS_DEBUG, "orangefs_debug_write: %pD\n", @@ -513,8 +522,9 @@ static ssize_t orangefs_debug_write(struct file *file, } mutex_lock(&orangefs_debug_lock); - memset(file->f_inode->i_private, 0, ORANGEFS_MAX_DEBUG_STRING_LEN); - sprintf((char *)file->f_inode->i_private, "%s\n", debug_string); + s = file_inode(file)->i_private; + memset(s, 0, ORANGEFS_MAX_DEBUG_STRING_LEN); + sprintf(s, "%s\n", debug_string); mutex_unlock(&orangefs_debug_lock); *ppos += count; @@ -610,32 +620,54 @@ out: * /sys/kernel/debug/orangefs/debug-help can be catted to * see all the available kernel and client debug keywords. * - * When the kernel boots, we have no idea what keywords the + * When orangefs.ko initializes, we have no idea what keywords the * client supports, nor their associated masks. * - * We pass through this function once at boot and stamp a + * We pass through this function once at module-load and stamp a * boilerplate "we don't know" message for the client in the * debug-help file. We pass through here again when the client * starts and then we can fill out the debug-help file fully. * * The client might be restarted any number of times between - * reboots, we only build the debug-help file the first time. + * module reloads, we only build the debug-help file the first time. */ int orangefs_prepare_debugfs_help_string(int at_boot) { - int rc = -EINVAL; - int i; - int byte_count = 0; char *client_title = "Client Debug Keywords:\n"; char *kernel_title = "Kernel Debug Keywords:\n"; + size_t string_size = DEBUG_HELP_STRING_SIZE; + size_t result_size; + size_t i; + char *new; + int rc = -EINVAL; gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); - if (at_boot) { - byte_count += strlen(HELP_STRING_UNINITIALIZED); + if (at_boot) client_title = HELP_STRING_UNINITIALIZED; - } else { - /* + + /* build a new debug_help_string. */ + new = kzalloc(DEBUG_HELP_STRING_SIZE, GFP_KERNEL); + if (!new) { + rc = -ENOMEM; + goto out; + } + + /* + * strlcat(dst, src, size) will append at most + * "size - strlen(dst) - 1" bytes of src onto dst, + * null terminating the result, and return the total + * length of the string it tried to create. + * + * We'll just plow through here building our new debug + * help string and let strlcat take care of assuring that + * dst doesn't overflow. + */ + strlcat(new, client_title, string_size); + + if (!at_boot) { + + /* * fill the client keyword/mask array and remember * how many elements there were. */ @@ -644,64 +676,40 @@ int orangefs_prepare_debugfs_help_string(int at_boot) if (cdm_element_count <= 0) goto out; - /* Count the bytes destined for debug_help_string. */ - byte_count += strlen(client_title); - for (i = 0; i < cdm_element_count; i++) { - byte_count += strlen(cdm_array[i].keyword + 2); - if (byte_count >= DEBUG_HELP_STRING_SIZE) { - pr_info("%s: overflow 1!\n", __func__); - goto out; - } + strlcat(new, "\t", string_size); + strlcat(new, cdm_array[i].keyword, string_size); + strlcat(new, "\n", string_size); } - - gossip_debug(GOSSIP_UTILS_DEBUG, - "%s: cdm_element_count:%d:\n", - __func__, - cdm_element_count); } - byte_count += strlen(kernel_title); + strlcat(new, "\n", string_size); + strlcat(new, kernel_title, string_size); + for (i = 0; i < num_kmod_keyword_mask_map; i++) { - byte_count += - strlen(s_kmod_keyword_mask_map[i].keyword + 2); - if (byte_count >= DEBUG_HELP_STRING_SIZE) { - pr_info("%s: overflow 2!\n", __func__); - goto out; - } + strlcat(new, "\t", string_size); + strlcat(new, s_kmod_keyword_mask_map[i].keyword, string_size); + result_size = strlcat(new, "\n", string_size); } - /* build debug_help_string. */ - debug_help_string = kzalloc(DEBUG_HELP_STRING_SIZE, GFP_KERNEL); - if (!debug_help_string) { - rc = -ENOMEM; + /* See if we tried to put too many bytes into "new"... */ + if (result_size >= string_size) { + kfree(new); goto out; } - strcat(debug_help_string, client_title); - - if (!at_boot) { - for (i = 0; i < cdm_element_count; i++) { - strcat(debug_help_string, "\t"); - strcat(debug_help_string, cdm_array[i].keyword); - strcat(debug_help_string, "\n"); - } - } - - strcat(debug_help_string, "\n"); - strcat(debug_help_string, kernel_title); - - for (i = 0; i < num_kmod_keyword_mask_map; i++) { - strcat(debug_help_string, "\t"); - strcat(debug_help_string, s_kmod_keyword_mask_map[i].keyword); - strcat(debug_help_string, "\n"); + if (at_boot) { + debug_help_string = new; + } else { + mutex_lock(&orangefs_help_file_lock); + memset(debug_help_string, 0, DEBUG_HELP_STRING_SIZE); + strlcat(debug_help_string, new, string_size); + mutex_unlock(&orangefs_help_file_lock); } rc = 0; -out: - - return rc; +out: return rc; } @@ -959,8 +967,12 @@ int orangefs_debugfs_new_client_string(void __user *arg) ret = copy_from_user(&client_debug_array_string, (void __user *)arg, ORANGEFS_MAX_DEBUG_STRING_LEN); - if (ret != 0) + + if (ret != 0) { + pr_info("%s: CLIENT_STRING: copy_from_user failed\n", + __func__); return -EIO; + } /* * The real client-core makes an effort to ensure @@ -975,45 +987,18 @@ int orangefs_debugfs_new_client_string(void __user *arg) client_debug_array_string[ORANGEFS_MAX_DEBUG_STRING_LEN - 1] = '\0'; - if (ret != 0) { - pr_info("%s: CLIENT_STRING: copy_from_user failed\n", - __func__); - return -EIO; - } - pr_info("%s: client debug array string has been received.\n", __func__); if (!help_string_initialized) { - /* Free the "we don't know yet" default string... */ - kfree(debug_help_string); - - /* build a proper debug help string */ + /* Build a proper debug help string. */ if (orangefs_prepare_debugfs_help_string(0)) { gossip_err("%s: no debug help string \n", __func__); return -EIO; } - /* Replace the boilerplate boot-time debug-help file. */ - debugfs_remove(help_file_dentry); - - help_file_dentry = - debugfs_create_file( - ORANGEFS_KMOD_DEBUG_HELP_FILE, - 0444, - debug_dir, - debug_help_string, - &debug_help_fops); - - if (!help_file_dentry) { - gossip_err("%s: debugfs_create_file failed for" - " :%s:!\n", - __func__, - ORANGEFS_KMOD_DEBUG_HELP_FILE); - return -EIO; - } } debug_mask_to_string(&client_debug_mask, 1); diff --git a/fs/orangefs/orangefs-kernel.h b/fs/orangefs/orangefs-kernel.h index 0a82048f3aaf..3bf803d732c5 100644 --- a/fs/orangefs/orangefs-kernel.h +++ b/fs/orangefs/orangefs-kernel.h @@ -580,4 +580,11 @@ static inline void orangefs_i_size_write(struct inode *inode, loff_t i_size) #endif } +static inline void orangefs_set_timeout(struct dentry *dentry) +{ + unsigned long time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000; + + dentry->d_fsdata = (void *) time; +} + #endif /* __ORANGEFSKERNEL_H */ diff --git a/fs/orangefs/orangefs-mod.c b/fs/orangefs/orangefs-mod.c index 2e5b03065f34..4113eb0495bf 100644 --- a/fs/orangefs/orangefs-mod.c +++ b/fs/orangefs/orangefs-mod.c @@ -124,7 +124,7 @@ static int __init orangefs_init(void) * unknown at boot time. * * orangefs_prepare_debugfs_help_string will be used again - * later to rebuild the debug-help file after the client starts + * later to rebuild the debug-help-string after the client starts * and passes along the needed info. The argument signifies * which time orangefs_prepare_debugfs_help_string is being * called. @@ -152,7 +152,9 @@ static int __init orangefs_init(void) ret = register_filesystem(&orangefs_fs_type); if (ret == 0) { - pr_info("orangefs: module version %s loaded\n", ORANGEFS_VERSION); + pr_info("%s: module version %s loaded\n", + __func__, + ORANGEFS_VERSION); ret = 0; goto out; } diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index aeb60f791418..2838bddb1f91 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -33,7 +33,7 @@ static int ovl_check_fd(const void *data, struct file *f, unsigned int fd) { const struct dentry *dentry = data; - if (f->f_inode == d_inode(dentry)) + if (file_inode(f) == d_inode(dentry)) pr_warn_ratelimited("overlayfs: Warning: Copying up %pD, but open R/O on fd %u which will cease to be coherent [pid=%d %s]\n", f, fd, current->pid, current->comm); return 0; @@ -178,6 +178,8 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) len -= bytes; } + if (!error) + error = vfs_fsync(new_file, 0); fput(new_file); out_fput: fput(old_file); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index c58f01babf30..7fb53d055537 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -270,9 +270,6 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type) if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode)) return NULL; - if (!realinode->i_op->get_acl) - return NULL; - old_cred = ovl_override_creds(inode->i_sb); acl = get_acl(realinode, type); revert_creds(old_cred); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index bcf3965be819..edd46a0e951d 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1037,6 +1037,21 @@ ovl_posix_acl_xattr_set(const struct xattr_handler *handler, posix_acl_release(acl); + /* + * Check if sgid bit needs to be cleared (actual setacl operation will + * be done with mounter's capabilities and so that won't do it for us). + */ + if (unlikely(inode->i_mode & S_ISGID) && + handler->flags == ACL_TYPE_ACCESS && + !in_group_p(inode->i_gid) && + !capable_wrt_inode_uidgid(inode, CAP_FSETID)) { + struct iattr iattr = { .ia_valid = ATTR_KILL_SGID }; + + err = ovl_setattr(dentry, &iattr); + if (err) + return err; + } + err = ovl_xattr_set(dentry, handler->name, value, size, flags); if (!err) ovl_copyattr(ovl_inode_real(inode, NULL), inode); diff --git a/fs/proc/array.c b/fs/proc/array.c index 89600fd5963d..81818adb8e9e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -412,10 +412,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, mm = get_task_mm(task); if (mm) { vsize = task_vsize(mm); - if (permitted) { - eip = KSTK_EIP(task); - esp = KSTK_ESP(task); - } + /* + * esp and eip are intentionally zeroed out. There is no + * non-racy way to read them without freezing the task. + * Programs that need reliable values can use ptrace(2). + */ } get_task_comm(tcomm, task); diff --git a/fs/proc/base.c b/fs/proc/base.c index c2964d890c9a..ca651ac00660 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -832,6 +832,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf, unsigned long addr = *ppos; ssize_t copied; char *page; + unsigned int flags; if (!mm) return 0; @@ -844,6 +845,11 @@ static ssize_t mem_rw(struct file *file, char __user *buf, if (!atomic_inc_not_zero(&mm->mm_users)) goto free; + /* Maybe we should limit FOLL_FORCE to actual ptrace users? */ + flags = FOLL_FORCE; + if (write) + flags |= FOLL_WRITE; + while (count > 0) { int this_len = min_t(int, count, PAGE_SIZE); @@ -852,7 +858,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf, break; } - this_len = access_remote_vm(mm, addr, page, this_len, write); + this_len = access_remote_vm(mm, addr, page, this_len, flags); if (!this_len) { if (!copied) copied = -EIO; @@ -964,8 +970,7 @@ static ssize_t environ_read(struct file *file, char __user *buf, max_len = min_t(size_t, PAGE_SIZE, count); this_len = min(max_len, this_len); - retval = access_remote_vm(mm, (env_start + src), - page, this_len, 0); + retval = access_remote_vm(mm, (env_start + src), page, this_len, 0); if (retval <= 0) { ret = retval; @@ -1007,6 +1012,9 @@ static ssize_t auxv_read(struct file *file, char __user *buf, { struct mm_struct *mm = file->private_data; unsigned int nwords = 0; + + if (!mm) + return 0; do { nwords += 2; } while (mm->saved_auxv[nwords - 2] != 0); /* AT_NULL */ diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 6909582ce5e5..35b92d81692f 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -266,24 +266,15 @@ static int do_maps_open(struct inode *inode, struct file *file, * /proc/PID/maps that is the stack of the main task. */ static int is_stack(struct proc_maps_private *priv, - struct vm_area_struct *vma, int is_pid) + struct vm_area_struct *vma) { - int stack = 0; - - if (is_pid) { - stack = vma->vm_start <= vma->vm_mm->start_stack && - vma->vm_end >= vma->vm_mm->start_stack; - } else { - struct inode *inode = priv->inode; - struct task_struct *task; - - rcu_read_lock(); - task = pid_task(proc_pid(inode), PIDTYPE_PID); - if (task) - stack = vma_is_stack_for_task(vma, task); - rcu_read_unlock(); - } - return stack; + /* + * We make no effort to guess what a given thread considers to be + * its "stack". It's not even well-defined for programs written + * languages like Go. + */ + return vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack; } static void @@ -354,7 +345,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) goto done; } - if (is_stack(priv, vma, is_pid)) + if (is_stack(priv, vma)) name = "[stack]"; } @@ -1669,7 +1660,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) seq_file_path(m, file, "\n\t= "); } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { seq_puts(m, " heap"); - } else if (is_stack(proc_priv, vma, is_pid)) { + } else if (is_stack(proc_priv, vma)) { seq_puts(m, " stack"); } diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index faacb0c0d857..37175621e890 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -124,25 +124,17 @@ unsigned long task_statm(struct mm_struct *mm, } static int is_stack(struct proc_maps_private *priv, - struct vm_area_struct *vma, int is_pid) + struct vm_area_struct *vma) { struct mm_struct *mm = vma->vm_mm; - int stack = 0; - - if (is_pid) { - stack = vma->vm_start <= mm->start_stack && - vma->vm_end >= mm->start_stack; - } else { - struct inode *inode = priv->inode; - struct task_struct *task; - - rcu_read_lock(); - task = pid_task(proc_pid(inode), PIDTYPE_PID); - if (task) - stack = vma_is_stack_for_task(vma, task); - rcu_read_unlock(); - } - return stack; + + /* + * We make no effort to guess what a given thread considers to be + * its "stack". It's not even well-defined for programs written + * languages like Go. + */ + return vma->vm_start <= mm->start_stack && + vma->vm_end >= mm->start_stack; } /* @@ -184,7 +176,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, if (file) { seq_pad(m, ' '); seq_file_path(m, file, ""); - } else if (mm && is_stack(priv, vma, is_pid)) { + } else if (mm && is_stack(priv, vma)) { seq_pad(m, ' '); seq_printf(m, "[stack]"); } diff --git a/fs/splice.c b/fs/splice.c index 153d4f3bd441..5a7750bd2eea 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -299,13 +299,8 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, { struct iov_iter to; struct kiocb kiocb; - loff_t isize; int idx, ret; - isize = i_size_read(in->f_mapping->host); - if (unlikely(*ppos >= isize)) - return 0; - iov_iter_pipe(&to, ITER_PIPE | READ, pipe, len); idx = to.idx; init_sync_kiocb(&kiocb, in); @@ -413,7 +408,8 @@ static ssize_t default_file_splice_read(struct file *in, loff_t *ppos, if (res <= 0) return -ENOMEM; - nr_pages = res / PAGE_SIZE; + BUG_ON(dummy); + nr_pages = DIV_ROUND_UP(res, PAGE_SIZE); vec = __vec; if (nr_pages > PIPE_DEF_BUFFERS) { diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index c8f60df2733e..ca16c5d7bab1 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -439,7 +439,7 @@ static unsigned int vfs_dent_type(uint8_t type) */ static int ubifs_readdir(struct file *file, struct dir_context *ctx) { - int err; + int err = 0; struct qstr nm; union ubifs_key key; struct ubifs_dent_node *dent; @@ -541,14 +541,20 @@ out: kfree(file->private_data); file->private_data = NULL; - if (err != -ENOENT) { + if (err != -ENOENT) ubifs_err(c, "cannot find next direntry, error %d", err); - return err; - } + else + /* + * -ENOENT is a non-fatal error in this context, the TNC uses + * it to indicate that the cursor moved past the current directory + * and readdir() has to stop. + */ + err = 0; + /* 2 is a special value indicating that there are no more direntries */ ctx->pos = 2; - return 0; + return err; } /* Free saved readdir() state when the directory is closed */ @@ -1060,9 +1066,9 @@ static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, mutex_unlock(&ubifs_inode(inode1)->ui_mutex); } -static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) +static int do_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct inode *old_inode = d_inode(old_dentry); @@ -1323,7 +1329,7 @@ static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry, return err; } -static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry, +static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { @@ -1336,7 +1342,7 @@ static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry, if (flags & RENAME_EXCHANGE) return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry); - return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); + return do_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, @@ -1387,7 +1393,7 @@ const struct inode_operations ubifs_dir_inode_operations = { .mkdir = ubifs_mkdir, .rmdir = ubifs_rmdir, .mknod = ubifs_mknod, - .rename = ubifs_rename2, + .rename = ubifs_rename, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .listxattr = ubifs_listxattr, diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 6c2f4d41ed73..d9f9615bfd71 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -172,6 +172,7 @@ out_cancel: host_ui->xattr_cnt -= 1; host_ui->xattr_size -= CALC_DENT_SIZE(nm->len); host_ui->xattr_size -= CALC_XATTR_BYTES(size); + host_ui->xattr_names -= nm->len; mutex_unlock(&host_ui->ui_mutex); out_free: make_bad_inode(inode); @@ -478,6 +479,7 @@ out_cancel: host_ui->xattr_cnt += 1; host_ui->xattr_size += CALC_DENT_SIZE(nm->len); host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); + host_ui->xattr_names += nm->len; mutex_unlock(&host_ui->ui_mutex); ubifs_release_budget(c, &req); make_bad_inode(inode); diff --git a/fs/xattr.c b/fs/xattr.c index 3368659c471e..2d13b4e62fae 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -170,7 +170,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; - int error = -EOPNOTSUPP; + int error = -EAGAIN; int issec = !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN); @@ -183,15 +183,21 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, security_inode_post_setxattr(dentry, name, value, size, flags); } - } else if (issec) { - const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; - + } else { if (unlikely(is_bad_inode(inode))) return -EIO; - error = security_inode_setsecurity(inode, suffix, value, - size, flags); - if (!error) - fsnotify_xattr(dentry); + } + if (error == -EAGAIN) { + error = -EOPNOTSUPP; + + if (issec) { + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; + + error = security_inode_setsecurity(inode, suffix, value, + size, flags); + if (!error) + fsnotify_xattr(dentry); + } } return error; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index c27344cf38e1..c6eb21940783 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3974,9 +3974,6 @@ xfs_bmap_remap_alloc( * allocating, so skip that check by pretending to be freeing. */ error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING); - if (error) - goto error0; -error0: xfs_perag_put(args.pag); if (error) trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_); @@ -3999,6 +3996,39 @@ xfs_bmap_alloc( return xfs_bmap_btalloc(ap); } +/* Trim extent to fit a logical block range. */ +void +xfs_trim_extent( + struct xfs_bmbt_irec *irec, + xfs_fileoff_t bno, + xfs_filblks_t len) +{ + xfs_fileoff_t distance; + xfs_fileoff_t end = bno + len; + + if (irec->br_startoff + irec->br_blockcount <= bno || + irec->br_startoff >= end) { + irec->br_blockcount = 0; + return; + } + + if (irec->br_startoff < bno) { + distance = bno - irec->br_startoff; + if (isnullstartblock(irec->br_startblock)) + irec->br_startblock = DELAYSTARTBLOCK; + if (irec->br_startblock != DELAYSTARTBLOCK && + irec->br_startblock != HOLESTARTBLOCK) + irec->br_startblock += distance; + irec->br_startoff += distance; + irec->br_blockcount -= distance; + } + + if (end < irec->br_startoff + irec->br_blockcount) { + distance = irec->br_startoff + irec->br_blockcount - end; + irec->br_blockcount -= distance; + } +} + /* * Trim the returned map to the required bounds */ @@ -4829,6 +4859,219 @@ xfs_bmap_split_indlen( return stolen; } +int +xfs_bmap_del_extent_delay( + struct xfs_inode *ip, + int whichfork, + xfs_extnum_t *idx, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *del) +{ + struct xfs_mount *mp = ip->i_mount; + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_bmbt_irec new; + int64_t da_old, da_new, da_diff = 0; + xfs_fileoff_t del_endoff, got_endoff; + xfs_filblks_t got_indlen, new_indlen, stolen; + int error = 0, state = 0; + bool isrt; + + XFS_STATS_INC(mp, xs_del_exlist); + + isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); + del_endoff = del->br_startoff + del->br_blockcount; + got_endoff = got->br_startoff + got->br_blockcount; + da_old = startblockval(got->br_startblock); + da_new = 0; + + ASSERT(*idx >= 0); + ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec)); + ASSERT(del->br_blockcount > 0); + ASSERT(got->br_startoff <= del->br_startoff); + ASSERT(got_endoff >= del_endoff); + + if (isrt) { + int64_t rtexts = XFS_FSB_TO_B(mp, del->br_blockcount); + + do_div(rtexts, mp->m_sb.sb_rextsize); + xfs_mod_frextents(mp, rtexts); + } + + /* + * Update the inode delalloc counter now and wait to update the + * sb counters as we might have to borrow some blocks for the + * indirect block accounting. + */ + xfs_trans_reserve_quota_nblks(NULL, ip, -((long)del->br_blockcount), 0, + isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS); + ip->i_delayed_blks -= del->br_blockcount; + + if (whichfork == XFS_COW_FORK) + state |= BMAP_COWFORK; + + if (got->br_startoff == del->br_startoff) + state |= BMAP_LEFT_CONTIG; + if (got_endoff == del_endoff) + state |= BMAP_RIGHT_CONTIG; + + switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { + case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: + /* + * Matches the whole extent. Delete the entry. + */ + xfs_iext_remove(ip, *idx, 1, state); + --*idx; + break; + case BMAP_LEFT_CONTIG: + /* + * Deleting the first part of the extent. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + got->br_startoff = del_endoff; + got->br_blockcount -= del->br_blockcount; + da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, + got->br_blockcount), da_old); + got->br_startblock = nullstartblock((int)da_new); + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); + break; + case BMAP_RIGHT_CONTIG: + /* + * Deleting the last part of the extent. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + got->br_blockcount = got->br_blockcount - del->br_blockcount; + da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, + got->br_blockcount), da_old); + got->br_startblock = nullstartblock((int)da_new); + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); + break; + case 0: + /* + * Deleting the middle of the extent. + * + * Distribute the original indlen reservation across the two new + * extents. Steal blocks from the deleted extent if necessary. + * Stealing blocks simply fudges the fdblocks accounting below. + * Warn if either of the new indlen reservations is zero as this + * can lead to delalloc problems. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + + got->br_blockcount = del->br_startoff - got->br_startoff; + got_indlen = xfs_bmap_worst_indlen(ip, got->br_blockcount); + + new.br_blockcount = got_endoff - del_endoff; + new_indlen = xfs_bmap_worst_indlen(ip, new.br_blockcount); + + WARN_ON_ONCE(!got_indlen || !new_indlen); + stolen = xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen, + del->br_blockcount); + + got->br_startblock = nullstartblock((int)got_indlen); + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, 0, _THIS_IP_); + + new.br_startoff = del_endoff; + new.br_state = got->br_state; + new.br_startblock = nullstartblock((int)new_indlen); + + ++*idx; + xfs_iext_insert(ip, *idx, 1, &new, state); + + da_new = got_indlen + new_indlen - stolen; + del->br_blockcount -= stolen; + break; + } + + ASSERT(da_old >= da_new); + da_diff = da_old - da_new; + if (!isrt) + da_diff += del->br_blockcount; + if (da_diff) + xfs_mod_fdblocks(mp, da_diff, false); + return error; +} + +void +xfs_bmap_del_extent_cow( + struct xfs_inode *ip, + xfs_extnum_t *idx, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *del) +{ + struct xfs_mount *mp = ip->i_mount; + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_bmbt_irec new; + xfs_fileoff_t del_endoff, got_endoff; + int state = BMAP_COWFORK; + + XFS_STATS_INC(mp, xs_del_exlist); + + del_endoff = del->br_startoff + del->br_blockcount; + got_endoff = got->br_startoff + got->br_blockcount; + + ASSERT(*idx >= 0); + ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec)); + ASSERT(del->br_blockcount > 0); + ASSERT(got->br_startoff <= del->br_startoff); + ASSERT(got_endoff >= del_endoff); + ASSERT(!isnullstartblock(got->br_startblock)); + + if (got->br_startoff == del->br_startoff) + state |= BMAP_LEFT_CONTIG; + if (got_endoff == del_endoff) + state |= BMAP_RIGHT_CONTIG; + + switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { + case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: + /* + * Matches the whole extent. Delete the entry. + */ + xfs_iext_remove(ip, *idx, 1, state); + --*idx; + break; + case BMAP_LEFT_CONTIG: + /* + * Deleting the first part of the extent. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + got->br_startoff = del_endoff; + got->br_blockcount -= del->br_blockcount; + got->br_startblock = del->br_startblock + del->br_blockcount; + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); + break; + case BMAP_RIGHT_CONTIG: + /* + * Deleting the last part of the extent. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + got->br_blockcount -= del->br_blockcount; + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); + break; + case 0: + /* + * Deleting the middle of the extent. + */ + trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); + got->br_blockcount = del->br_startoff - got->br_startoff; + xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); + trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); + + new.br_startoff = del_endoff; + new.br_blockcount = got_endoff - del_endoff; + new.br_state = got->br_state; + new.br_startblock = del->br_startblock + del->br_blockcount; + + ++*idx; + xfs_iext_insert(ip, *idx, 1, &new, state); + break; + } +} + /* * Called by xfs_bmapi to update file extent records and the btree * after removing space (or undoing a delayed allocation). @@ -5171,175 +5414,6 @@ done: return error; } -/* Remove an extent from the CoW fork. Similar to xfs_bmap_del_extent. */ -int -xfs_bunmapi_cow( - struct xfs_inode *ip, - struct xfs_bmbt_irec *del) -{ - xfs_filblks_t da_new; - xfs_filblks_t da_old; - xfs_fsblock_t del_endblock = 0; - xfs_fileoff_t del_endoff; - int delay; - struct xfs_bmbt_rec_host *ep; - int error; - struct xfs_bmbt_irec got; - xfs_fileoff_t got_endoff; - struct xfs_ifork *ifp; - struct xfs_mount *mp; - xfs_filblks_t nblks; - struct xfs_bmbt_irec new; - /* REFERENCED */ - uint qfield; - xfs_filblks_t temp; - xfs_filblks_t temp2; - int state = BMAP_COWFORK; - int eof; - xfs_extnum_t eidx; - - mp = ip->i_mount; - XFS_STATS_INC(mp, xs_del_exlist); - - ep = xfs_bmap_search_extents(ip, del->br_startoff, XFS_COW_FORK, &eof, - &eidx, &got, &new); - - ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); ifp = ifp; - ASSERT((eidx >= 0) && (eidx < ifp->if_bytes / - (uint)sizeof(xfs_bmbt_rec_t))); - ASSERT(del->br_blockcount > 0); - ASSERT(got.br_startoff <= del->br_startoff); - del_endoff = del->br_startoff + del->br_blockcount; - got_endoff = got.br_startoff + got.br_blockcount; - ASSERT(got_endoff >= del_endoff); - delay = isnullstartblock(got.br_startblock); - ASSERT(isnullstartblock(del->br_startblock) == delay); - qfield = 0; - error = 0; - /* - * If deleting a real allocation, must free up the disk space. - */ - if (!delay) { - nblks = del->br_blockcount; - qfield = XFS_TRANS_DQ_BCOUNT; - /* - * Set up del_endblock and cur for later. - */ - del_endblock = del->br_startblock + del->br_blockcount; - da_old = da_new = 0; - } else { - da_old = startblockval(got.br_startblock); - da_new = 0; - nblks = 0; - } - qfield = qfield; - nblks = nblks; - - /* - * Set flag value to use in switch statement. - * Left-contig is 2, right-contig is 1. - */ - switch (((got.br_startoff == del->br_startoff) << 1) | - (got_endoff == del_endoff)) { - case 3: - /* - * Matches the whole extent. Delete the entry. - */ - xfs_iext_remove(ip, eidx, 1, BMAP_COWFORK); - --eidx; - break; - - case 2: - /* - * Deleting the first part of the extent. - */ - trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); - xfs_bmbt_set_startoff(ep, del_endoff); - temp = got.br_blockcount - del->br_blockcount; - xfs_bmbt_set_blockcount(ep, temp); - if (delay) { - temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), - da_old); - xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); - trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); - da_new = temp; - break; - } - xfs_bmbt_set_startblock(ep, del_endblock); - trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); - break; - - case 1: - /* - * Deleting the last part of the extent. - */ - temp = got.br_blockcount - del->br_blockcount; - trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); - xfs_bmbt_set_blockcount(ep, temp); - if (delay) { - temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), - da_old); - xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); - trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); - da_new = temp; - break; - } - trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); - break; - - case 0: - /* - * Deleting the middle of the extent. - */ - temp = del->br_startoff - got.br_startoff; - trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); - xfs_bmbt_set_blockcount(ep, temp); - new.br_startoff = del_endoff; - temp2 = got_endoff - del_endoff; - new.br_blockcount = temp2; - new.br_state = got.br_state; - if (!delay) { - new.br_startblock = del_endblock; - } else { - temp = xfs_bmap_worst_indlen(ip, temp); - xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); - temp2 = xfs_bmap_worst_indlen(ip, temp2); - new.br_startblock = nullstartblock((int)temp2); - da_new = temp + temp2; - while (da_new > da_old) { - if (temp) { - temp--; - da_new--; - xfs_bmbt_set_startblock(ep, - nullstartblock((int)temp)); - } - if (da_new == da_old) - break; - if (temp2) { - temp2--; - da_new--; - new.br_startblock = - nullstartblock((int)temp2); - } - } - } - trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); - xfs_iext_insert(ip, eidx + 1, 1, &new, state); - ++eidx; - break; - } - - /* - * Account for change in delayed indirect blocks. - * Nothing to do for disk quota accounting here. - */ - ASSERT(da_old >= da_new); - if (da_old > da_new) - xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false); - - return error; -} - /* * Unmap (remove) blocks from a file. * If nexts is nonzero then the number of extents to remove is limited to diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index f97db7132564..7cae6ec27fa6 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -190,6 +190,8 @@ void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt, #define XFS_BMAP_TRACE_EXLIST(ip,c,w) #endif +void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno, + xfs_filblks_t len); int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd); void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork); void xfs_bmap_add_free(struct xfs_mount *mp, struct xfs_defer_ops *dfops, @@ -221,7 +223,11 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip, xfs_fileoff_t bno, xfs_filblks_t len, int flags, xfs_extnum_t nexts, xfs_fsblock_t *firstblock, struct xfs_defer_ops *dfops, int *done); -int xfs_bunmapi_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *del); +int xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork, + xfs_extnum_t *idx, struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *del); +void xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx, + struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *del); int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx, xfs_extnum_t num); uint xfs_default_attroffset(struct xfs_inode *ip); diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index 5c8e6f2ce44f..0e80993c8a59 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -4826,7 +4826,7 @@ xfs_btree_calc_size( return rval; } -int +static int xfs_btree_count_blocks_helper( struct xfs_btree_cur *cur, int level, diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 613c5cf19436..5c2929f94bd3 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -199,9 +199,9 @@ xfs_defer_intake_work( struct xfs_defer_pending *dfp; list_for_each_entry(dfp, &dop->dop_intake, dfp_list) { - trace_xfs_defer_intake_work(tp->t_mountp, dfp); dfp->dfp_intent = dfp->dfp_type->create_intent(tp, dfp->dfp_count); + trace_xfs_defer_intake_work(tp->t_mountp, dfp); list_sort(tp->t_mountp, &dfp->dfp_work, dfp->dfp_type->diff_items); list_for_each(li, &dfp->dfp_work) @@ -221,21 +221,14 @@ xfs_defer_trans_abort( struct xfs_defer_pending *dfp; trace_xfs_defer_trans_abort(tp->t_mountp, dop); - /* - * If the transaction was committed, drop the intent reference - * since we're bailing out of here. The other reference is - * dropped when the intent hits the AIL. If the transaction - * was not committed, the intent is freed by the intent item - * unlock handler on abort. - */ - if (!dop->dop_committed) - return; - /* Abort intent items. */ + /* Abort intent items that don't have a done item. */ list_for_each_entry(dfp, &dop->dop_pending, dfp_list) { trace_xfs_defer_pending_abort(tp->t_mountp, dfp); - if (!dfp->dfp_done) + if (dfp->dfp_intent && !dfp->dfp_done) { dfp->dfp_type->abort_intent(dfp->dfp_intent); + dfp->dfp_intent = NULL; + } } /* Shut down FS. */ diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index 3cc3cf767474..ac9a003dd29a 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -191,8 +191,7 @@ xfs_dquot_buf_verify_crc( if (mp->m_quotainfo) ndquots = mp->m_quotainfo->qi_dqperchunk; else - ndquots = xfs_calc_dquots_per_chunk( - XFS_BB_TO_FSB(mp, bp->b_length)); + ndquots = xfs_calc_dquots_per_chunk(bp->b_length); for (i = 0; i < ndquots; i++, d++) { if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk), diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index f6547fc5e016..6b7579e7b60a 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -865,7 +865,6 @@ typedef struct xfs_timestamp { * padding field for v3 inodes. */ #define XFS_DINODE_MAGIC 0x494e /* 'IN' */ -#define XFS_DINODE_GOOD_VERSION(v) ((v) >= 1 && (v) <= 3) typedef struct xfs_dinode { __be16 di_magic; /* inode magic # = XFS_DINODE_MAGIC */ __be16 di_mode; /* mode and type of file */ diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 8de9a3a29589..134424fac434 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -57,6 +57,17 @@ xfs_inobp_check( } #endif +bool +xfs_dinode_good_version( + struct xfs_mount *mp, + __u8 version) +{ + if (xfs_sb_version_hascrc(&mp->m_sb)) + return version == 3; + + return version == 1 || version == 2; +} + /* * If we are doing readahead on an inode buffer, we might be in log recovery * reading an inode allocation buffer that hasn't yet been replayed, and hence @@ -91,7 +102,7 @@ xfs_inode_buf_verify( dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog)); di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) && - XFS_DINODE_GOOD_VERSION(dip->di_version); + xfs_dinode_good_version(mp, dip->di_version); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { diff --git a/fs/xfs/libxfs/xfs_inode_buf.h b/fs/xfs/libxfs/xfs_inode_buf.h index 62d9d4681c8c..3cfe12a4f58a 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.h +++ b/fs/xfs/libxfs/xfs_inode_buf.h @@ -74,6 +74,8 @@ void xfs_inode_from_disk(struct xfs_inode *ip, struct xfs_dinode *from); void xfs_log_dinode_to_disk(struct xfs_log_dinode *from, struct xfs_dinode *to); +bool xfs_dinode_good_version(struct xfs_mount *mp, __u8 version); + #if defined(DEBUG) void xfs_inobp_check(struct xfs_mount *, struct xfs_buf *); #else diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index a314fc7b56fa..6e4f7f900fea 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -249,6 +249,7 @@ xfs_file_dio_aio_read( struct xfs_inode *ip = XFS_I(inode); loff_t isize = i_size_read(inode); size_t count = iov_iter_count(to); + loff_t end = iocb->ki_pos + count - 1; struct iov_iter data; struct xfs_buftarg *target; ssize_t ret = 0; @@ -272,49 +273,21 @@ xfs_file_dio_aio_read( file_accessed(iocb->ki_filp); - /* - * Locking is a bit tricky here. If we take an exclusive lock for direct - * IO, we effectively serialise all new concurrent read IO to this file - * and block it behind IO that is currently in progress because IO in - * progress holds the IO lock shared. We only need to hold the lock - * exclusive to blow away the page cache, so only take lock exclusively - * if the page cache needs invalidation. This allows the normal direct - * IO case of no page cache pages to proceeed concurrently without - * serialisation. - */ xfs_rw_ilock(ip, XFS_IOLOCK_SHARED); if (mapping->nrpages) { - xfs_rw_iunlock(ip, XFS_IOLOCK_SHARED); - xfs_rw_ilock(ip, XFS_IOLOCK_EXCL); + ret = filemap_write_and_wait_range(mapping, iocb->ki_pos, end); + if (ret) + goto out_unlock; /* - * The generic dio code only flushes the range of the particular - * I/O. Because we take an exclusive lock here, this whole - * sequence is considerably more expensive for us. This has a - * noticeable performance impact for any file with cached pages, - * even when outside of the range of the particular I/O. - * - * Hence, amortize the cost of the lock against a full file - * flush and reduce the chances of repeated iolock cycles going - * forward. + * Invalidate whole pages. This can return an error if we fail + * to invalidate a page, but this should never happen on XFS. + * Warn if it does fail. */ - if (mapping->nrpages) { - ret = filemap_write_and_wait(mapping); - if (ret) { - xfs_rw_iunlock(ip, XFS_IOLOCK_EXCL); - return ret; - } - - /* - * Invalidate whole pages. This can return an error if - * we fail to invalidate a page, but this should never - * happen on XFS. Warn if it does fail. - */ - ret = invalidate_inode_pages2(mapping); - WARN_ON_ONCE(ret); - ret = 0; - } - xfs_rw_ilock_demote(ip, XFS_IOLOCK_EXCL); + ret = invalidate_inode_pages2_range(mapping, + iocb->ki_pos >> PAGE_SHIFT, end >> PAGE_SHIFT); + WARN_ON_ONCE(ret); + ret = 0; } data = *to; @@ -324,8 +297,9 @@ xfs_file_dio_aio_read( iocb->ki_pos += ret; iov_iter_advance(to, ret); } - xfs_rw_iunlock(ip, XFS_IOLOCK_SHARED); +out_unlock: + xfs_rw_iunlock(ip, XFS_IOLOCK_SHARED); return ret; } @@ -570,61 +544,49 @@ xfs_file_dio_aio_write( if ((iocb->ki_pos | count) & target->bt_logical_sectormask) return -EINVAL; - /* "unaligned" here means not aligned to a filesystem block */ - if ((iocb->ki_pos & mp->m_blockmask) || - ((iocb->ki_pos + count) & mp->m_blockmask)) - unaligned_io = 1; - /* - * We don't need to take an exclusive lock unless there page cache needs - * to be invalidated or unaligned IO is being executed. We don't need to - * consider the EOF extension case here because - * xfs_file_aio_write_checks() will relock the inode as necessary for - * EOF zeroing cases and fill out the new inode size as appropriate. + * Don't take the exclusive iolock here unless the I/O is unaligned to + * the file system block size. We don't need to consider the EOF + * extension case here because xfs_file_aio_write_checks() will relock + * the inode as necessary for EOF zeroing cases and fill out the new + * inode size as appropriate. */ - if (unaligned_io || mapping->nrpages) + if ((iocb->ki_pos & mp->m_blockmask) || + ((iocb->ki_pos + count) & mp->m_blockmask)) { + unaligned_io = 1; iolock = XFS_IOLOCK_EXCL; - else + } else { iolock = XFS_IOLOCK_SHARED; - xfs_rw_ilock(ip, iolock); - - /* - * Recheck if there are cached pages that need invalidate after we got - * the iolock to protect against other threads adding new pages while - * we were waiting for the iolock. - */ - if (mapping->nrpages && iolock == XFS_IOLOCK_SHARED) { - xfs_rw_iunlock(ip, iolock); - iolock = XFS_IOLOCK_EXCL; - xfs_rw_ilock(ip, iolock); } + xfs_rw_ilock(ip, iolock); + ret = xfs_file_aio_write_checks(iocb, from, &iolock); if (ret) goto out; count = iov_iter_count(from); end = iocb->ki_pos + count - 1; - /* - * See xfs_file_dio_aio_read() for why we do a full-file flush here. - */ if (mapping->nrpages) { - ret = filemap_write_and_wait(VFS_I(ip)->i_mapping); + ret = filemap_write_and_wait_range(mapping, iocb->ki_pos, end); if (ret) goto out; + /* * Invalidate whole pages. This can return an error if we fail * to invalidate a page, but this should never happen on XFS. * Warn if it does fail. */ - ret = invalidate_inode_pages2(VFS_I(ip)->i_mapping); + ret = invalidate_inode_pages2_range(mapping, + iocb->ki_pos >> PAGE_SHIFT, end >> PAGE_SHIFT); WARN_ON_ONCE(ret); ret = 0; } /* * If we are doing unaligned IO, wait for all other IO to drain, - * otherwise demote the lock if we had to flush cached pages + * otherwise demote the lock if we had to take the exclusive lock + * for other reasons in xfs_file_aio_write_checks. */ if (unaligned_io) inode_dio_wait(inode); @@ -947,134 +909,6 @@ out_unlock: return error; } -/* - * Flush all file writes out to disk. - */ -static int -xfs_file_wait_for_io( - struct inode *inode, - loff_t offset, - size_t len) -{ - loff_t rounding; - loff_t ioffset; - loff_t iendoffset; - loff_t bs; - int ret; - - bs = inode->i_sb->s_blocksize; - inode_dio_wait(inode); - - rounding = max_t(xfs_off_t, bs, PAGE_SIZE); - ioffset = round_down(offset, rounding); - iendoffset = round_up(offset + len, rounding) - 1; - ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, - iendoffset); - return ret; -} - -/* Hook up to the VFS reflink function */ -STATIC int -xfs_file_share_range( - struct file *file_in, - loff_t pos_in, - struct file *file_out, - loff_t pos_out, - u64 len, - bool is_dedupe) -{ - struct inode *inode_in; - struct inode *inode_out; - ssize_t ret; - loff_t bs; - loff_t isize; - int same_inode; - loff_t blen; - unsigned int flags = 0; - - inode_in = file_inode(file_in); - inode_out = file_inode(file_out); - bs = inode_out->i_sb->s_blocksize; - - /* Don't touch certain kinds of inodes */ - if (IS_IMMUTABLE(inode_out)) - return -EPERM; - if (IS_SWAPFILE(inode_in) || - IS_SWAPFILE(inode_out)) - return -ETXTBSY; - - /* Reflink only works within this filesystem. */ - if (inode_in->i_sb != inode_out->i_sb) - return -EXDEV; - same_inode = (inode_in->i_ino == inode_out->i_ino); - - /* Don't reflink dirs, pipes, sockets... */ - if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) - return -EISDIR; - if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode)) - return -EINVAL; - if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) - return -EINVAL; - - /* Don't share DAX file data for now. */ - if (IS_DAX(inode_in) || IS_DAX(inode_out)) - return -EINVAL; - - /* Are we going all the way to the end? */ - isize = i_size_read(inode_in); - if (isize == 0) - return 0; - if (len == 0) - len = isize - pos_in; - - /* Ensure offsets don't wrap and the input is inside i_size */ - if (pos_in + len < pos_in || pos_out + len < pos_out || - pos_in + len > isize) - return -EINVAL; - - /* Don't allow dedupe past EOF in the dest file */ - if (is_dedupe) { - loff_t disize; - - disize = i_size_read(inode_out); - if (pos_out >= disize || pos_out + len > disize) - return -EINVAL; - } - - /* If we're linking to EOF, continue to the block boundary. */ - if (pos_in + len == isize) - blen = ALIGN(isize, bs) - pos_in; - else - blen = len; - - /* Only reflink if we're aligned to block boundaries */ - if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) || - !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs)) - return -EINVAL; - - /* Don't allow overlapped reflink within the same file */ - if (same_inode && pos_out + blen > pos_in && pos_out < pos_in + blen) - return -EINVAL; - - /* Wait for the completion of any pending IOs on srcfile */ - ret = xfs_file_wait_for_io(inode_in, pos_in, len); - if (ret) - goto out; - ret = xfs_file_wait_for_io(inode_out, pos_out, len); - if (ret) - goto out; - - if (is_dedupe) - flags |= XFS_REFLINK_DEDUPE; - ret = xfs_reflink_remap_range(XFS_I(inode_in), pos_in, XFS_I(inode_out), - pos_out, len, flags); - if (ret < 0) - goto out; - -out: - return ret; -} - STATIC ssize_t xfs_file_copy_range( struct file *file_in, @@ -1086,7 +920,7 @@ xfs_file_copy_range( { int error; - error = xfs_file_share_range(file_in, pos_in, file_out, pos_out, + error = xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out, len, false); if (error) return error; @@ -1101,7 +935,7 @@ xfs_file_clone_range( loff_t pos_out, u64 len) { - return xfs_file_share_range(file_in, pos_in, file_out, pos_out, + return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out, len, false); } @@ -1124,7 +958,7 @@ xfs_file_dedupe_range( if (len > XFS_MAX_DEDUPE_LEN) len = XFS_MAX_DEDUPE_LEN; - error = xfs_file_share_range(src_file, loff, dst_file, dst_loff, + error = xfs_reflink_remap_range(src_file, loff, dst_file, dst_loff, len, true); if (error) return error; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 14796b744e0a..f295049db681 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1656,9 +1656,9 @@ void xfs_inode_set_cowblocks_tag( xfs_inode_t *ip) { - trace_xfs_inode_set_eofblocks_tag(ip); + trace_xfs_inode_set_cowblocks_tag(ip); return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_cowblocks, - trace_xfs_perag_set_eofblocks, + trace_xfs_perag_set_cowblocks, XFS_ICI_COWBLOCKS_TAG); } @@ -1666,7 +1666,7 @@ void xfs_inode_clear_cowblocks_tag( xfs_inode_t *ip) { - trace_xfs_inode_clear_eofblocks_tag(ip); + trace_xfs_inode_clear_cowblocks_tag(ip); return __xfs_inode_clear_eofblocks_tag(ip, - trace_xfs_perag_clear_eofblocks, XFS_ICI_COWBLOCKS_TAG); + trace_xfs_perag_clear_cowblocks, XFS_ICI_COWBLOCKS_TAG); } diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index d907eb9f8ef3..436e109bb01e 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -566,6 +566,17 @@ xfs_file_iomap_begin_delay( xfs_bmap_search_extents(ip, offset_fsb, XFS_DATA_FORK, &eof, &idx, &got, &prev); if (!eof && got.br_startoff <= offset_fsb) { + if (xfs_is_reflink_inode(ip)) { + bool shared; + + end_fsb = min(XFS_B_TO_FSB(mp, offset + count), + maxbytes_fsb); + xfs_trim_extent(&got, offset_fsb, end_fsb - offset_fsb); + error = xfs_reflink_reserve_cow(ip, &got, &shared); + if (error) + goto out_unlock; + } + trace_xfs_iomap_found(ip, offset, count, 0, &got); goto done; } @@ -961,19 +972,13 @@ xfs_file_iomap_begin( struct xfs_mount *mp = ip->i_mount; struct xfs_bmbt_irec imap; xfs_fileoff_t offset_fsb, end_fsb; - bool shared, trimmed; int nimaps = 1, error = 0; + bool shared = false, trimmed = false; unsigned lockmode; if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; - if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) { - error = xfs_reflink_reserve_cow_range(ip, offset, length); - if (error < 0) - return error; - } - if ((flags & IOMAP_WRITE) && !IS_DAX(inode) && !xfs_get_extsz_hint(ip)) { /* Reserve delalloc blocks for regular writeback. */ @@ -981,7 +986,16 @@ xfs_file_iomap_begin( iomap); } - lockmode = xfs_ilock_data_map_shared(ip); + /* + * COW writes will allocate delalloc space, so we need to make sure + * to take the lock exclusively here. + */ + if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) { + lockmode = XFS_ILOCK_EXCL; + xfs_ilock(ip, XFS_ILOCK_EXCL); + } else { + lockmode = xfs_ilock_data_map_shared(ip); + } ASSERT(offset <= mp->m_super->s_maxbytes); if ((xfs_fsize_t)offset + length > mp->m_super->s_maxbytes) @@ -991,16 +1005,24 @@ xfs_file_iomap_begin( error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap, &nimaps, 0); - if (error) { - xfs_iunlock(ip, lockmode); - return error; + if (error) + goto out_unlock; + + if (flags & IOMAP_REPORT) { + /* Trim the mapping to the nearest shared extent boundary. */ + error = xfs_reflink_trim_around_shared(ip, &imap, &shared, + &trimmed); + if (error) + goto out_unlock; } - /* Trim the mapping to the nearest shared extent boundary. */ - error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed); - if (error) { - xfs_iunlock(ip, lockmode); - return error; + if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) { + error = xfs_reflink_reserve_cow(ip, &imap, &shared); + if (error) + goto out_unlock; + + end_fsb = imap.br_startoff + imap.br_blockcount; + length = XFS_FSB_TO_B(mp, end_fsb) - offset; } if ((flags & IOMAP_WRITE) && imap_needs_alloc(inode, &imap, nimaps)) { @@ -1039,6 +1061,9 @@ xfs_file_iomap_begin( if (shared) iomap->flags |= IOMAP_F_SHARED; return 0; +out_unlock: + xfs_iunlock(ip, lockmode); + return error; } static int diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index fc7873942bea..b341f10cf481 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1009,6 +1009,7 @@ xfs_mountfs( out_quota: xfs_qm_unmount_quotas(mp); out_rtunmount: + mp->m_super->s_flags &= ~MS_ACTIVE; xfs_rtunmount_inodes(mp); out_rele_rip: IRELE(rip); diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 5965e9455d91..a279b4e7f5fe 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -182,7 +182,8 @@ xfs_reflink_trim_around_shared( if (!xfs_is_reflink_inode(ip) || ISUNWRITTEN(irec) || irec->br_startblock == HOLESTARTBLOCK || - irec->br_startblock == DELAYSTARTBLOCK) { + irec->br_startblock == DELAYSTARTBLOCK || + isnullstartblock(irec->br_startblock)) { *shared = false; return 0; } @@ -227,50 +228,54 @@ xfs_reflink_trim_around_shared( } } -/* Create a CoW reservation for a range of blocks within a file. */ -static int -__xfs_reflink_reserve_cow( +/* + * Trim the passed in imap to the next shared/unshared extent boundary, and + * if imap->br_startoff points to a shared extent reserve space for it in the + * COW fork. In this case *shared is set to true, else to false. + * + * Note that imap will always contain the block numbers for the existing blocks + * in the data fork, as the upper layers need them for read-modify-write + * operations. + */ +int +xfs_reflink_reserve_cow( struct xfs_inode *ip, - xfs_fileoff_t *offset_fsb, - xfs_fileoff_t end_fsb, - bool *skipped) + struct xfs_bmbt_irec *imap, + bool *shared) { - struct xfs_bmbt_irec got, prev, imap; - xfs_fileoff_t orig_end_fsb; - int nimaps, eof = 0, error = 0; - bool shared = false, trimmed = false; + struct xfs_bmbt_irec got, prev; + xfs_fileoff_t end_fsb, orig_end_fsb; + int eof = 0, error = 0; + bool trimmed; xfs_extnum_t idx; xfs_extlen_t align; - /* Already reserved? Skip the refcount btree access. */ - xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx, + /* + * Search the COW fork extent list first. This serves two purposes: + * first this implement the speculative preallocation using cowextisze, + * so that we also unshared block adjacent to shared blocks instead + * of just the shared blocks themselves. Second the lookup in the + * extent list is generally faster than going out to the shared extent + * tree. + */ + xfs_bmap_search_extents(ip, imap->br_startoff, XFS_COW_FORK, &eof, &idx, &got, &prev); - if (!eof && got.br_startoff <= *offset_fsb) { - end_fsb = orig_end_fsb = got.br_startoff + got.br_blockcount; - trace_xfs_reflink_cow_found(ip, &got); - goto done; - } + if (!eof && got.br_startoff <= imap->br_startoff) { + trace_xfs_reflink_cow_found(ip, imap); + xfs_trim_extent(imap, got.br_startoff, got.br_blockcount); - /* Read extent from the source file. */ - nimaps = 1; - error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb, - &imap, &nimaps, 0); - if (error) - goto out_unlock; - ASSERT(nimaps == 1); + *shared = true; + return 0; + } /* Trim the mapping to the nearest shared extent boundary. */ - error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed); + error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed); if (error) - goto out_unlock; - - end_fsb = orig_end_fsb = imap.br_startoff + imap.br_blockcount; + return error; /* Not shared? Just report the (potentially capped) extent. */ - if (!shared) { - *skipped = true; - goto done; - } + if (!*shared) + return 0; /* * Fork all the shared blocks from our write offset until the end of @@ -278,72 +283,38 @@ __xfs_reflink_reserve_cow( */ error = xfs_qm_dqattach_locked(ip, 0); if (error) - goto out_unlock; + return error; + + end_fsb = orig_end_fsb = imap->br_startoff + imap->br_blockcount; align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip)); if (align) end_fsb = roundup_64(end_fsb, align); retry: - error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb, - end_fsb - *offset_fsb, &got, - &prev, &idx, eof); + error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff, + end_fsb - imap->br_startoff, &got, &prev, &idx, eof); switch (error) { case 0: break; case -ENOSPC: case -EDQUOT: /* retry without any preallocation */ - trace_xfs_reflink_cow_enospc(ip, &imap); + trace_xfs_reflink_cow_enospc(ip, imap); if (end_fsb != orig_end_fsb) { end_fsb = orig_end_fsb; goto retry; } /*FALLTHRU*/ default: - goto out_unlock; + return error; } if (end_fsb != orig_end_fsb) xfs_inode_set_cowblocks_tag(ip); trace_xfs_reflink_cow_alloc(ip, &got); -done: - *offset_fsb = end_fsb; -out_unlock: - return error; -} - -/* Create a CoW reservation for part of a file. */ -int -xfs_reflink_reserve_cow_range( - struct xfs_inode *ip, - xfs_off_t offset, - xfs_off_t count) -{ - struct xfs_mount *mp = ip->i_mount; - xfs_fileoff_t offset_fsb, end_fsb; - bool skipped = false; - int error; - - trace_xfs_reflink_reserve_cow_range(ip, offset, count); - - offset_fsb = XFS_B_TO_FSBT(mp, offset); - end_fsb = XFS_B_TO_FSB(mp, offset + count); - - xfs_ilock(ip, XFS_ILOCK_EXCL); - while (offset_fsb < end_fsb) { - error = __xfs_reflink_reserve_cow(ip, &offset_fsb, end_fsb, - &skipped); - if (error) { - trace_xfs_reflink_reserve_cow_range_error(ip, error, - _RET_IP_); - break; - } - } - xfs_iunlock(ip, XFS_ILOCK_EXCL); - - return error; + return 0; } /* Allocate all CoW reservations covering a range of blocks in a file. */ @@ -358,9 +329,8 @@ __xfs_reflink_allocate_cow( struct xfs_defer_ops dfops; struct xfs_trans *tp; xfs_fsblock_t first_block; - xfs_fileoff_t next_fsb; int nimaps = 1, error; - bool skipped = false; + bool shared; xfs_defer_init(&dfops, &first_block); @@ -371,33 +341,38 @@ __xfs_reflink_allocate_cow( xfs_ilock(ip, XFS_ILOCK_EXCL); - next_fsb = *offset_fsb; - error = __xfs_reflink_reserve_cow(ip, &next_fsb, end_fsb, &skipped); + /* Read extent from the source file. */ + nimaps = 1; + error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb, + &imap, &nimaps, 0); + if (error) + goto out_unlock; + ASSERT(nimaps == 1); + + error = xfs_reflink_reserve_cow(ip, &imap, &shared); if (error) goto out_trans_cancel; - if (skipped) { - *offset_fsb = next_fsb; + if (!shared) { + *offset_fsb = imap.br_startoff + imap.br_blockcount; goto out_trans_cancel; } xfs_trans_ijoin(tp, ip, 0); - error = xfs_bmapi_write(tp, ip, *offset_fsb, next_fsb - *offset_fsb, + error = xfs_bmapi_write(tp, ip, imap.br_startoff, imap.br_blockcount, XFS_BMAPI_COWFORK, &first_block, XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), &imap, &nimaps, &dfops); if (error) goto out_trans_cancel; - /* We might not have been able to map the whole delalloc extent */ - *offset_fsb = min(*offset_fsb + imap.br_blockcount, next_fsb); - error = xfs_defer_finish(&tp, &dfops, NULL); if (error) goto out_trans_cancel; error = xfs_trans_commit(tp); + *offset_fsb = imap.br_startoff + imap.br_blockcount; out_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; @@ -536,58 +511,49 @@ xfs_reflink_cancel_cow_blocks( xfs_fileoff_t offset_fsb, xfs_fileoff_t end_fsb) { - struct xfs_bmbt_irec irec; - xfs_filblks_t count_fsb; + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_bmbt_irec got, prev, del; + xfs_extnum_t idx; xfs_fsblock_t firstfsb; struct xfs_defer_ops dfops; - int error = 0; - int nimaps; + int error = 0, eof = 0; if (!xfs_is_reflink_inode(ip)) return 0; - /* Go find the old extent in the CoW fork. */ - while (offset_fsb < end_fsb) { - nimaps = 1; - count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb); - error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec, - &nimaps, XFS_BMAPI_COWFORK); - if (error) - break; - ASSERT(nimaps == 1); - - trace_xfs_reflink_cancel_cow(ip, &irec); + xfs_bmap_search_extents(ip, offset_fsb, XFS_COW_FORK, &eof, &idx, + &got, &prev); + if (eof) + return 0; - if (irec.br_startblock == DELAYSTARTBLOCK) { - /* Free a delayed allocation. */ - xfs_mod_fdblocks(ip->i_mount, irec.br_blockcount, - false); - ip->i_delayed_blks -= irec.br_blockcount; + while (got.br_startoff < end_fsb) { + del = got; + xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb); + trace_xfs_reflink_cancel_cow(ip, &del); - /* Remove the mapping from the CoW fork. */ - error = xfs_bunmapi_cow(ip, &irec); + if (isnullstartblock(del.br_startblock)) { + error = xfs_bmap_del_extent_delay(ip, XFS_COW_FORK, + &idx, &got, &del); if (error) break; - } else if (irec.br_startblock == HOLESTARTBLOCK) { - /* empty */ } else { xfs_trans_ijoin(*tpp, ip, 0); xfs_defer_init(&dfops, &firstfsb); /* Free the CoW orphan record. */ error = xfs_refcount_free_cow_extent(ip->i_mount, - &dfops, irec.br_startblock, - irec.br_blockcount); + &dfops, del.br_startblock, + del.br_blockcount); if (error) break; xfs_bmap_add_free(ip->i_mount, &dfops, - irec.br_startblock, irec.br_blockcount, + del.br_startblock, del.br_blockcount, NULL); /* Update quota accounting */ xfs_trans_mod_dquot_byino(*tpp, ip, XFS_TRANS_DQ_BCOUNT, - -(long)irec.br_blockcount); + -(long)del.br_blockcount); /* Roll the transaction */ error = xfs_defer_finish(tpp, &dfops, ip); @@ -597,15 +563,18 @@ xfs_reflink_cancel_cow_blocks( } /* Remove the mapping from the CoW fork. */ - error = xfs_bunmapi_cow(ip, &irec); - if (error) - break; + xfs_bmap_del_extent_cow(ip, &idx, &got, &del); } - /* Roll on... */ - offset_fsb = irec.br_startoff + irec.br_blockcount; + if (++idx >= ifp->if_bytes / sizeof(struct xfs_bmbt_rec)) + break; + xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &got); } + /* clear tag if cow fork is emptied */ + if (!ifp->if_bytes) + xfs_inode_clear_cowblocks_tag(ip); + return error; } @@ -668,25 +637,26 @@ xfs_reflink_end_cow( xfs_off_t offset, xfs_off_t count) { - struct xfs_bmbt_irec irec; - struct xfs_bmbt_irec uirec; + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_bmbt_irec got, prev, del; struct xfs_trans *tp; xfs_fileoff_t offset_fsb; xfs_fileoff_t end_fsb; - xfs_filblks_t count_fsb; xfs_fsblock_t firstfsb; struct xfs_defer_ops dfops; - int error; + int error, eof = 0; unsigned int resblks; - xfs_filblks_t ilen; xfs_filblks_t rlen; - int nimaps; + xfs_extnum_t idx; trace_xfs_reflink_end_cow(ip, offset, count); + /* No COW extents? That's easy! */ + if (ifp->if_bytes == 0) + return 0; + offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset); end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count); - count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb); /* Start a rolling transaction to switch the mappings */ resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK); @@ -698,72 +668,65 @@ xfs_reflink_end_cow( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - /* Go find the old extent in the CoW fork. */ - while (offset_fsb < end_fsb) { - /* Read extent from the source file */ - nimaps = 1; - count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb); - error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec, - &nimaps, XFS_BMAPI_COWFORK); - if (error) - goto out_cancel; - ASSERT(nimaps == 1); + xfs_bmap_search_extents(ip, end_fsb - 1, XFS_COW_FORK, &eof, &idx, + &got, &prev); - ASSERT(irec.br_startblock != DELAYSTARTBLOCK); - trace_xfs_reflink_cow_remap(ip, &irec); + /* If there is a hole at end_fsb - 1 go to the previous extent */ + if (eof || got.br_startoff > end_fsb) { + ASSERT(idx > 0); + xfs_bmbt_get_all(xfs_iext_get_ext(ifp, --idx), &got); + } - /* - * We can have a hole in the CoW fork if part of a directio - * write is CoW but part of it isn't. - */ - rlen = ilen = irec.br_blockcount; - if (irec.br_startblock == HOLESTARTBLOCK) + /* Walk backwards until we're out of the I/O range... */ + while (got.br_startoff + got.br_blockcount > offset_fsb) { + del = got; + xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb); + + /* Extent delete may have bumped idx forward */ + if (!del.br_blockcount) { + idx--; goto next_extent; + } + + ASSERT(!isnullstartblock(got.br_startblock)); /* Unmap the old blocks in the data fork. */ - while (rlen) { - xfs_defer_init(&dfops, &firstfsb); - error = __xfs_bunmapi(tp, ip, irec.br_startoff, - &rlen, 0, 1, &firstfsb, &dfops); - if (error) - goto out_defer; - - /* - * Trim the extent to whatever got unmapped. - * Remember, bunmapi works backwards. - */ - uirec.br_startblock = irec.br_startblock + rlen; - uirec.br_startoff = irec.br_startoff + rlen; - uirec.br_blockcount = irec.br_blockcount - rlen; - irec.br_blockcount = rlen; - trace_xfs_reflink_cow_remap_piece(ip, &uirec); + xfs_defer_init(&dfops, &firstfsb); + rlen = del.br_blockcount; + error = __xfs_bunmapi(tp, ip, del.br_startoff, &rlen, 0, 1, + &firstfsb, &dfops); + if (error) + goto out_defer; - /* Free the CoW orphan record. */ - error = xfs_refcount_free_cow_extent(tp->t_mountp, - &dfops, uirec.br_startblock, - uirec.br_blockcount); - if (error) - goto out_defer; + /* Trim the extent to whatever got unmapped. */ + if (rlen) { + xfs_trim_extent(&del, del.br_startoff + rlen, + del.br_blockcount - rlen); + } + trace_xfs_reflink_cow_remap(ip, &del); - /* Map the new blocks into the data fork. */ - error = xfs_bmap_map_extent(tp->t_mountp, &dfops, - ip, &uirec); - if (error) - goto out_defer; + /* Free the CoW orphan record. */ + error = xfs_refcount_free_cow_extent(tp->t_mountp, &dfops, + del.br_startblock, del.br_blockcount); + if (error) + goto out_defer; - /* Remove the mapping from the CoW fork. */ - error = xfs_bunmapi_cow(ip, &uirec); - if (error) - goto out_defer; + /* Map the new blocks into the data fork. */ + error = xfs_bmap_map_extent(tp->t_mountp, &dfops, ip, &del); + if (error) + goto out_defer; - error = xfs_defer_finish(&tp, &dfops, ip); - if (error) - goto out_defer; - } + /* Remove the mapping from the CoW fork. */ + xfs_bmap_del_extent_cow(ip, &idx, &got, &del); + + error = xfs_defer_finish(&tp, &dfops, ip); + if (error) + goto out_defer; next_extent: - /* Roll on... */ - offset_fsb = irec.br_startoff + ilen; + if (idx < 0) + break; + xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &got); } error = xfs_trans_commit(tp); @@ -774,7 +737,6 @@ next_extent: out_defer: xfs_defer_cancel(&dfops); -out_cancel: xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); out: @@ -1312,19 +1274,26 @@ out_error: */ int xfs_reflink_remap_range( - struct xfs_inode *src, - xfs_off_t srcoff, - struct xfs_inode *dest, - xfs_off_t destoff, - xfs_off_t len, - unsigned int flags) + struct file *file_in, + loff_t pos_in, + struct file *file_out, + loff_t pos_out, + u64 len, + bool is_dedupe) { + struct inode *inode_in = file_inode(file_in); + struct xfs_inode *src = XFS_I(inode_in); + struct inode *inode_out = file_inode(file_out); + struct xfs_inode *dest = XFS_I(inode_out); struct xfs_mount *mp = src->i_mount; + loff_t bs = inode_out->i_sb->s_blocksize; + bool same_inode = (inode_in == inode_out); xfs_fileoff_t sfsbno, dfsbno; xfs_filblks_t fsblen; - int error; xfs_extlen_t cowextsize; - bool is_same; + loff_t isize; + ssize_t ret; + loff_t blen; if (!xfs_sb_version_hasreflink(&mp->m_sb)) return -EOPNOTSUPP; @@ -1332,17 +1301,8 @@ xfs_reflink_remap_range( if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; - /* Don't reflink realtime inodes */ - if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest)) - return -EINVAL; - - if (flags & ~XFS_REFLINK_ALL) - return -EINVAL; - - trace_xfs_reflink_remap_range(src, srcoff, len, dest, destoff); - /* Lock both files against IO */ - if (src->i_ino == dest->i_ino) { + if (same_inode) { xfs_ilock(src, XFS_IOLOCK_EXCL); xfs_ilock(src, XFS_MMAPLOCK_EXCL); } else { @@ -1350,39 +1310,126 @@ xfs_reflink_remap_range( xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL); } + /* Don't touch certain kinds of inodes */ + ret = -EPERM; + if (IS_IMMUTABLE(inode_out)) + goto out_unlock; + + ret = -ETXTBSY; + if (IS_SWAPFILE(inode_in) || IS_SWAPFILE(inode_out)) + goto out_unlock; + + + /* Don't reflink dirs, pipes, sockets... */ + ret = -EISDIR; + if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) + goto out_unlock; + ret = -EINVAL; + if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode)) + goto out_unlock; + if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) + goto out_unlock; + + /* Don't reflink realtime inodes */ + if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest)) + goto out_unlock; + + /* Don't share DAX file data for now. */ + if (IS_DAX(inode_in) || IS_DAX(inode_out)) + goto out_unlock; + + /* Are we going all the way to the end? */ + isize = i_size_read(inode_in); + if (isize == 0) { + ret = 0; + goto out_unlock; + } + + if (len == 0) + len = isize - pos_in; + + /* Ensure offsets don't wrap and the input is inside i_size */ + if (pos_in + len < pos_in || pos_out + len < pos_out || + pos_in + len > isize) + goto out_unlock; + + /* Don't allow dedupe past EOF in the dest file */ + if (is_dedupe) { + loff_t disize; + + disize = i_size_read(inode_out); + if (pos_out >= disize || pos_out + len > disize) + goto out_unlock; + } + + /* If we're linking to EOF, continue to the block boundary. */ + if (pos_in + len == isize) + blen = ALIGN(isize, bs) - pos_in; + else + blen = len; + + /* Only reflink if we're aligned to block boundaries */ + if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) || + !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs)) + goto out_unlock; + + /* Don't allow overlapped reflink within the same file */ + if (same_inode) { + if (pos_out + blen > pos_in && pos_out < pos_in + blen) + goto out_unlock; + } + + /* Wait for the completion of any pending IOs on both files */ + inode_dio_wait(inode_in); + if (!same_inode) + inode_dio_wait(inode_out); + + ret = filemap_write_and_wait_range(inode_in->i_mapping, + pos_in, pos_in + len - 1); + if (ret) + goto out_unlock; + + ret = filemap_write_and_wait_range(inode_out->i_mapping, + pos_out, pos_out + len - 1); + if (ret) + goto out_unlock; + + trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out); + /* * Check that the extents are the same. */ - if (flags & XFS_REFLINK_DEDUPE) { - is_same = false; - error = xfs_compare_extents(VFS_I(src), srcoff, VFS_I(dest), - destoff, len, &is_same); - if (error) - goto out_error; + if (is_dedupe) { + bool is_same = false; + + ret = xfs_compare_extents(inode_in, pos_in, inode_out, pos_out, + len, &is_same); + if (ret) + goto out_unlock; if (!is_same) { - error = -EBADE; - goto out_error; + ret = -EBADE; + goto out_unlock; } } - error = xfs_reflink_set_inode_flag(src, dest); - if (error) - goto out_error; + ret = xfs_reflink_set_inode_flag(src, dest); + if (ret) + goto out_unlock; /* * Invalidate the page cache so that we can clear any CoW mappings * in the destination file. */ - truncate_inode_pages_range(&VFS_I(dest)->i_data, destoff, - PAGE_ALIGN(destoff + len) - 1); + truncate_inode_pages_range(&inode_out->i_data, pos_out, + PAGE_ALIGN(pos_out + len) - 1); - dfsbno = XFS_B_TO_FSBT(mp, destoff); - sfsbno = XFS_B_TO_FSBT(mp, srcoff); + dfsbno = XFS_B_TO_FSBT(mp, pos_out); + sfsbno = XFS_B_TO_FSBT(mp, pos_in); fsblen = XFS_B_TO_FSB(mp, len); - error = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen, - destoff + len); - if (error) - goto out_error; + ret = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen, + pos_out + len); + if (ret) + goto out_unlock; /* * Carry the cowextsize hint from src to dest if we're sharing the @@ -1390,26 +1437,24 @@ xfs_reflink_remap_range( * has a cowextsize hint, and the destination file does not. */ cowextsize = 0; - if (srcoff == 0 && len == i_size_read(VFS_I(src)) && + if (pos_in == 0 && len == i_size_read(inode_in) && (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) && - destoff == 0 && len >= i_size_read(VFS_I(dest)) && + pos_out == 0 && len >= i_size_read(inode_out) && !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)) cowextsize = src->i_d.di_cowextsize; - error = xfs_reflink_update_dest(dest, destoff + len, cowextsize); - if (error) - goto out_error; + ret = xfs_reflink_update_dest(dest, pos_out + len, cowextsize); -out_error: +out_unlock: xfs_iunlock(src, XFS_MMAPLOCK_EXCL); xfs_iunlock(src, XFS_IOLOCK_EXCL); if (src->i_ino != dest->i_ino) { xfs_iunlock(dest, XFS_MMAPLOCK_EXCL); xfs_iunlock(dest, XFS_IOLOCK_EXCL); } - if (error) - trace_xfs_reflink_remap_range_error(dest, error, _RET_IP_); - return error; + if (ret) + trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_); + return ret; } /* diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index 5dc3c8ac12aa..fad11607c9ad 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h @@ -26,8 +26,8 @@ extern int xfs_reflink_find_shared(struct xfs_mount *mp, xfs_agnumber_t agno, extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip, struct xfs_bmbt_irec *irec, bool *shared, bool *trimmed); -extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip, - xfs_off_t offset, xfs_off_t count); +extern int xfs_reflink_reserve_cow(struct xfs_inode *ip, + struct xfs_bmbt_irec *imap, bool *shared); extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t count); extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset, @@ -43,11 +43,8 @@ extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset, extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t count); extern int xfs_reflink_recover_cow(struct xfs_mount *mp); -#define XFS_REFLINK_DEDUPE 1 /* only reflink if contents match */ -#define XFS_REFLINK_ALL (XFS_REFLINK_DEDUPE) -extern int xfs_reflink_remap_range(struct xfs_inode *src, xfs_off_t srcoff, - struct xfs_inode *dest, xfs_off_t destoff, xfs_off_t len, - unsigned int flags); +extern int xfs_reflink_remap_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, u64 len, bool is_dedupe); extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip, struct xfs_trans **tpp); extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset, diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c index 5f8d55d29a11..276d3023d60f 100644 --- a/fs/xfs/xfs_sysfs.c +++ b/fs/xfs/xfs_sysfs.c @@ -512,13 +512,13 @@ static struct attribute *xfs_error_attrs[] = { }; -struct kobj_type xfs_error_cfg_ktype = { +static struct kobj_type xfs_error_cfg_ktype = { .release = xfs_sysfs_release, .sysfs_ops = &xfs_sysfs_ops, .default_attrs = xfs_error_attrs, }; -struct kobj_type xfs_error_ktype = { +static struct kobj_type xfs_error_ktype = { .release = xfs_sysfs_release, .sysfs_ops = &xfs_sysfs_ops, }; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index ad188d3a83f3..0907752be62d 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3346,7 +3346,7 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_alloc); DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_found); DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_enospc); -DEFINE_RW_EVENT(xfs_reflink_reserve_cow_range); +DEFINE_RW_EVENT(xfs_reflink_reserve_cow); DEFINE_RW_EVENT(xfs_reflink_allocate_cow_range); DEFINE_INODE_IREC_EVENT(xfs_reflink_bounce_dio_write); @@ -3356,9 +3356,7 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_irec); DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range); DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow); DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap); -DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap_piece); -DEFINE_INODE_ERROR_EVENT(xfs_reflink_reserve_cow_range_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_allocate_cow_range_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error); |