netfs, fscache: Add cache write preparation
diff --git a/fs/afs/file.c b/fs/afs/file.c index 58363d1..6e7f8a6 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c
@@ -367,7 +367,13 @@ static void afs_free_dirty_region(struct netfs_dirty_region *region) static void afs_init_wreq(struct netfs_write_request *wreq) { + struct afs_vnode *vnode = AFS_FS_I(wreq->inode); + //wreq->netfs_priv = key_get(afs_file_key(file)); + if (test_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags) && + fscache_prepare_write_operation(wreq, afs_vnode_cache(vnode)) < 0) + __clear_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags); + } static void afs_update_i_size(struct file *file, loff_t new_i_size)
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index da3948fd..fb609ef 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c
@@ -569,4 +569,5 @@ const struct fscache_cache_ops cachefiles_cache_ops = { .dissociate_pages = cachefiles_dissociate_pages, .check_consistency = cachefiles_check_consistency, .begin_read_operation = cachefiles_begin_read_operation, + .prepare_write_operation = cachefiles_prepare_write_operation, };
diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 4ed83aa..9350282 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h
@@ -154,6 +154,14 @@ void cachefiles_put_object(struct fscache_object *_object, enum fscache_obj_ref_trace why); /* + * io.c + */ +extern int cachefiles_begin_read_operation(struct netfs_read_request *, + struct fscache_retrieval *); +extern int cachefiles_prepare_write_operation(struct netfs_write_request *wreq, + struct fscache_operation *op); + +/* * key.c */ extern char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type); @@ -221,12 +229,6 @@ extern int cachefiles_write_page(struct fscache_storage *, struct page *); extern void cachefiles_uncache_page(struct fscache_object *, struct page *); /* - * rdwr2.c - */ -extern int cachefiles_begin_read_operation(struct netfs_read_request *, - struct fscache_retrieval *); - -/* * security.c */ extern int cachefiles_get_security_ID(struct cachefiles_cache *cache);
diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c index ca68bb9..e350660 100644 --- a/fs/cachefiles/io.c +++ b/fs/cachefiles/io.c
@@ -351,7 +351,7 @@ static int cachefiles_prepare_write(struct netfs_cache_resources *cres, */ static void cachefiles_end_operation(struct netfs_cache_resources *cres) { - struct fscache_retrieval *op = cres->cache_priv; + struct fscache_operation *op = cres->cache_priv; struct file *file = cres->cache_priv2; _enter(""); @@ -359,8 +359,8 @@ static void cachefiles_end_operation(struct netfs_cache_resources *cres) if (file) fput(file); if (op) { - fscache_op_complete(&op->op, false); - fscache_put_retrieval(op); + fscache_op_complete(op, false); + fscache_put_operation(op); } _leave(""); @@ -407,7 +407,7 @@ int cachefiles_begin_read_operation(struct netfs_read_request *rreq, } fscache_get_retrieval(op); - rreq->cache_resources.cache_priv = op; + rreq->cache_resources.cache_priv = &op->op; rreq->cache_resources.cache_priv2 = file; rreq->cache_resources.ops = &cachefiles_netfs_cache_ops; rreq->cache_resources.debug_id = object->fscache.debug_id; @@ -418,3 +418,47 @@ int cachefiles_begin_read_operation(struct netfs_read_request *rreq, fput(file); return -EIO; } + +/* + * Open the cache file when preparing a cache write operation. + */ +int cachefiles_prepare_write_operation(struct netfs_write_request *wreq, + struct fscache_operation *op) +{ + struct cachefiles_object *object; + struct cachefiles_cache *cache; + struct path path; + struct file *file; + + _enter(""); + + object = container_of(op->object, struct cachefiles_object, fscache); + cache = container_of(object->fscache.cache, + struct cachefiles_cache, cache); + + path.mnt = cache->mnt; + path.dentry = object->backer; + file = open_with_fake_path(&path, O_RDWR | O_LARGEFILE | O_DIRECT, + d_inode(object->backer), cache->cache_cred); + if (IS_ERR(file)) + return PTR_ERR(file); + if (!S_ISREG(file_inode(file)->i_mode)) + goto error_file; + if (unlikely(!file->f_op->read_iter) || + unlikely(!file->f_op->write_iter)) { + pr_notice("Cache does not support read_iter and write_iter\n"); + goto error_file; + } + + atomic_inc(&op->usage); + wreq->cache_resources.cache_priv = op; + wreq->cache_resources.cache_priv2 = file; + wreq->cache_resources.ops = &cachefiles_netfs_cache_ops; + wreq->cache_resources.debug_id = object->fscache.debug_id; + _leave(""); + return 0; + +error_file: + fput(file); + return -EIO; +}
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index c483863..269f733 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h
@@ -206,6 +206,8 @@ extern atomic_t fscache_n_stores_ok; extern atomic_t fscache_n_stores_again; extern atomic_t fscache_n_stores_nobufs; extern atomic_t fscache_n_stores_oom; +extern atomic_t fscache_n_stores_object_dead; +extern atomic_t fscache_n_stores_op_waits; extern atomic_t fscache_n_store_ops; extern atomic_t fscache_n_store_calls; extern atomic_t fscache_n_store_pages;
diff --git a/fs/fscache/io.c b/fs/fscache/io.c index 8ecc114..6b019ad 100644 --- a/fs/fscache/io.c +++ b/fs/fscache/io.c
@@ -114,3 +114,102 @@ int __fscache_begin_read_operation(struct netfs_read_request *rreq, return -ENOBUFS; } EXPORT_SYMBOL(__fscache_begin_read_operation); + +/* + * Prepare for a cache write operation. + * - we return: + * -ENOMEM - Out of memory + * -ERESTARTSYS - Interrupted + * -ENOBUFS - No backing object or space available in which to cache any + * pages not being written + * -ENODATA - No data available in the backing object for some or all of + * the pages + * 0 - Prepared a write on all pages + */ +int __fscache_prepare_write_operation(struct netfs_write_request *rreq, + struct fscache_cookie *cookie) +{ + struct fscache_operation *op; + struct fscache_object *object; + bool wake_cookie = false; + int ret; + + _enter("rr=%08x", rreq->debug_id); + + fscache_stat(&fscache_n_stores); + + if (hlist_empty(&cookie->backing_objects)) + goto nobufs; + + if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { + _leave(" = -ENOBUFS [invalidating]"); + return -ENOBUFS; + } + + ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX); + + if (fscache_wait_for_deferred_lookup(cookie) < 0) + return -ERESTARTSYS; + + op = kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY); + if (!op) + return -ENOMEM; + + fscache_operation_init(cookie, op, NULL, NULL, NULL); + op->flags = FSCACHE_OP_ASYNC | + (1 << FSCACHE_OP_WAITING) | + (1 << FSCACHE_OP_UNUSE_COOKIE); + + spin_lock(&cookie->lock); + + if (!fscache_cookie_enabled(cookie) || + hlist_empty(&cookie->backing_objects)) + goto nobufs_unlock; + object = hlist_entry(cookie->backing_objects.first, + struct fscache_object, cookie_link); + + __fscache_use_cookie(cookie); + + if (fscache_submit_op(object, op) < 0) + goto nobufs_unlock_dec; + spin_unlock(&cookie->lock); + + fscache_stat(&fscache_n_retrieval_ops); + + /* we wait for the operation to become active, and then process it + * *here*, in this thwrite, and not in the thwrite pool */ + ret = fscache_wait_for_operation_activation( + object, op, + __fscache_stat(&fscache_n_stores_op_waits), + __fscache_stat(&fscache_n_stores_object_dead)); + if (ret < 0) + goto error; + + /* ask the cache to honour the operation */ + ret = object->cache->ops->prepare_write_operation(rreq, op); + +error: + if (ret == -ENOMEM) + fscache_stat(&fscache_n_stores_oom); + else if (ret < 0) + fscache_stat(&fscache_n_stores_nobufs); + else + fscache_stat(&fscache_n_stores_ok); + + fscache_put_operation(op); + _leave(" = %d", ret); + return ret; + +nobufs_unlock_dec: + wake_cookie = __fscache_unuse_cookie(cookie); +nobufs_unlock: + spin_unlock(&cookie->lock); + fscache_put_operation(op); + if (wake_cookie) + __fscache_wake_unused_cookie(cookie); +nobufs: + fscache_stat(&fscache_n_stores_nobufs); + _leave(" = -ENOBUFS"); + return -ENOBUFS; +} +EXPORT_SYMBOL(__fscache_prepare_write_operation);
diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index a7c3ed8..264826d 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c
@@ -55,6 +55,8 @@ atomic_t fscache_n_stores_ok; atomic_t fscache_n_stores_again; atomic_t fscache_n_stores_nobufs; atomic_t fscache_n_stores_oom; +atomic_t fscache_n_stores_object_dead; +atomic_t fscache_n_stores_op_waits; atomic_t fscache_n_store_ops; atomic_t fscache_n_store_calls; atomic_t fscache_n_store_pages; @@ -233,6 +235,9 @@ int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_store_pages), atomic_read(&fscache_n_store_radix_deletes), atomic_read(&fscache_n_store_pages_over_limit)); + seq_printf(m, "Stores : owt=%u abt=%u\n", + atomic_read(&fscache_n_stores_op_waits), + atomic_read(&fscache_n_stores_object_dead)); seq_printf(m, "VmScan : nos=%u gon=%u bsy=%u can=%u wt=%u\n", atomic_read(&fscache_n_store_vmscan_not_storing),
diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index 6f44a67..1407bb0 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c
@@ -177,8 +177,10 @@ struct netfs_write_request *netfs_alloc_write_request(struct address_space *mapp wreq->inode = inode; wreq->netfs_ops = ctx->ops; wreq->debug_id = atomic_inc_return(&debug_ids); - if (cached) + if (cached) { + kdebug("cached W=%08x", wreq->debug_id); __set_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags); + } xa_init(&wreq->buffer); INIT_WORK(&wreq->work, netfs_writeback_worker); INIT_LIST_HEAD(&wreq->proc_link);
diff --git a/fs/netfs/write_back.c b/fs/netfs/write_back.c index 9f1c759..0ebe9ae 100644 --- a/fs/netfs/write_back.c +++ b/fs/netfs/write_back.c
@@ -218,6 +218,11 @@ static void netfs_set_up_write_to_cache(struct netfs_write_request *wreq) { struct netfs_write_operation *op; + if (!wreq->cache_resources.ops) { + clear_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags); + return; + } + op = netfs_create_write_operation(wreq, NETFS_WRITE_TO_CACHE, wreq->coverage.start, wreq->coverage.end - wreq->coverage.start, @@ -246,10 +251,9 @@ static void netfs_writeback(struct netfs_write_request *wreq) !netfs_wreq_encrypt(wreq)) goto out; - if (wreq->cache_resources.ops && - test_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags)) + if (test_bit(NETFS_WREQ_WRITE_TO_CACHE, &wreq->flags)) netfs_set_up_write_to_cache(wreq); - //if (wreq->region->type != NETFS_REGION_CACHE_COPY) + if (test_bit(NETFS_WREQ_WRITE_TO_SERVER, &wreq->flags)) ctx->ops->create_write_operations(wreq); out: @@ -577,6 +581,8 @@ static long netfs_flush_dirty(struct netfs_i_context *ctx, netfs_get_dirty_region(ctx, r, netfs_region_trace_get_wreq); netfs_deduct_write_credit(r, r->dirty.end - r->dirty.start); list_move_tail(&r->flush_link, &wreq->regions); + if (r->type != NETFS_REGION_CACHE_COPY) + __set_bit(NETFS_WREQ_WRITE_TO_SERVER, &wreq->flags); if (r == tail) break; }
diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 3235ddb..d2273eb 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h
@@ -308,6 +308,10 @@ struct fscache_cache_ops { /* Begin a read operation for the netfs lib */ int (*begin_read_operation)(struct netfs_read_request *rreq, struct fscache_retrieval *op); + + /* Prepare a write operation for the netfs lib */ + int (*prepare_write_operation)(struct netfs_write_request *wreq, + struct fscache_operation *op); }; extern struct fscache_cookie fscache_fsdef_index;
diff --git a/include/linux/fscache.h b/include/linux/fscache.h index ba58c42..0085dd3 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h
@@ -196,6 +196,8 @@ extern void __fscache_wait_on_invalidate(struct fscache_cookie *); #ifdef FSCACHE_USE_NEW_IO_API extern int __fscache_begin_read_operation(struct netfs_read_request *, struct fscache_cookie *); +extern int __fscache_prepare_write_operation(struct netfs_write_request *, + struct fscache_cookie *); #else extern int __fscache_read_or_alloc_page(struct fscache_cookie *, struct page *, @@ -534,6 +536,32 @@ int fscache_begin_read_operation(struct netfs_read_request *rreq, return -ENOBUFS; } +/** + * fscache_prepare_write_operation - Prepare a write operation for the netfs lib + * @wreq: The write request being undertaken + * @cookie: The cookie representing the cache object + * + * Prepare a write operation on behalf of the netfs helper library. @wreq + * indicates the write request to which the operation state should be attached; + * @cookie indicates the cache object that will be accessed. + * + * This is intended to be called from the ->init_wreq() netfs lib operation as + * implemented by the network filesystem. + * + * Returns: + * * 0 - Success + * * -ENOBUFS - No caching available + * * Other error code from the cache, such as -ENOMEM. + */ +static inline +int fscache_prepare_write_operation(struct netfs_write_request *wreq, + struct fscache_cookie *cookie) +{ + if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) + return __fscache_prepare_write_operation(wreq, cookie); + return -ENOBUFS; +} + #else /* FSCACHE_USE_NEW_IO_API */ /**
diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 33dd886..b551ed3 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h
@@ -352,6 +352,7 @@ struct netfs_write_request { unsigned long flags; #define NETFS_WREQ_WRITE_TO_CACHE 0 /* Need to write to the cache */ #define NETFS_WREQ_BUFFERED 1 /* Data is held in ->buffer */ +#define NETFS_WREQ_WRITE_TO_SERVER 2 /* Need to write to the server */ const struct netfs_request_ops *netfs_ops; };