// SPDX-License-Identifier: GPL-2.0-or-later /* Single, monolithic object support (e.g. AFS directory). * * Copyright (C) 2024 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #include #include #include #include #include #include #include #include #include #include "internal.h" /** * netfs_single_mark_inode_dirty - Mark a single, monolithic object inode dirty * @inode: The inode to mark * * Mark an inode that contains a single, monolithic object as dirty so that its * writepages op will get called. If set, the SINGLE_NO_UPLOAD flag indicates * that the object will only be written to the cache and not uploaded (e.g. AFS * directory contents). */ void netfs_single_mark_inode_dirty(struct inode *inode) { struct netfs_inode *ictx = netfs_inode(inode); bool cache_only = test_bit(NETFS_ICTX_SINGLE_NO_UPLOAD, &ictx->flags); bool caching = fscache_cookie_enabled(netfs_i_cookie(netfs_inode(inode))); if (cache_only && !caching) return; mark_inode_dirty(inode); if (caching && !(inode->i_state & I_PINNING_NETFS_WB)) { bool need_use = false; spin_lock(&inode->i_lock); if (!(inode->i_state & I_PINNING_NETFS_WB)) { inode->i_state |= I_PINNING_NETFS_WB; need_use = true; } spin_unlock(&inode->i_lock); if (need_use) fscache_use_cookie(netfs_i_cookie(ictx), true); } } EXPORT_SYMBOL(netfs_single_mark_inode_dirty); static int netfs_single_begin_cache_read(struct netfs_io_request *rreq, struct netfs_inode *ctx) { return fscache_begin_read_operation(&rreq->cache_resources, netfs_i_cookie(ctx)); } static void netfs_single_cache_prepare_read(struct netfs_io_request *rreq, struct netfs_io_subrequest *subreq) { struct netfs_cache_resources *cres = &rreq->cache_resources; if (!cres->ops) { subreq->source = NETFS_DOWNLOAD_FROM_SERVER; return; } subreq->source = cres->ops->prepare_read(subreq, rreq->i_size); trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); } static void netfs_single_read_cache(struct netfs_io_request *rreq, struct netfs_io_subrequest *subreq) { struct netfs_cache_resources *cres = &rreq->cache_resources; _enter("R=%08x[%x]", rreq->debug_id, subreq->debug_index); netfs_stat(&netfs_n_rh_read); cres->ops->read(cres, subreq->start, &subreq->io_iter, NETFS_READ_HOLE_FAIL, netfs_cache_read_terminated, subreq); } /* * Perform a read to a buffer from the cache or the server. Only a single * subreq is permitted as the object must be fetched in a single transaction. */ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) { struct netfs_io_stream *stream = &rreq->io_streams[0]; struct netfs_io_subrequest *subreq; int ret = 0; subreq = netfs_alloc_subrequest(rreq); if (!subreq) return -ENOMEM; subreq->source = NETFS_SOURCE_UNKNOWN; subreq->start = 0; subreq->len = rreq->len; subreq->io_iter = rreq->buffer.iter; __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); spin_lock(&rreq->lock); list_add_tail(&subreq->rreq_link, &stream->subrequests); trace_netfs_sreq(subreq, netfs_sreq_trace_added); stream->front = subreq; /* Store list pointers before active flag */ smp_store_release(&stream->active, true); spin_unlock(&rreq->lock); netfs_single_cache_prepare_read(rreq, subreq); switch (subreq->source) { case NETFS_DOWNLOAD_FROM_SERVER: netfs_stat(&netfs_n_rh_download); if (rreq->netfs_ops->prepare_read) { ret = rreq->netfs_ops->prepare_read(subreq); if (ret < 0) goto cancel; } rreq->netfs_ops->issue_read(subreq); rreq->submitted += subreq->len; break; case NETFS_READ_FROM_CACHE: trace_netfs_sreq(subreq, netfs_sreq_trace_submit); netfs_single_read_cache(rreq, subreq); rreq->submitted += subreq->len; ret = 0; break; default: pr_warn("Unexpected single-read source %u\n", subreq->source); WARN_ON_ONCE(true); ret = -EIO; break; } smp_wmb(); /* Write lists before ALL_QUEUED. */ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); return ret; cancel: netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_cancel); return ret; } /** * netfs_read_single - Synchronously read a single blob of pages. * @inode: The inode to read from. * @file: The file we're using to read or NULL. * @iter: The buffer we're reading into. * * Fulfil a read request for a single monolithic object by drawing data from * the cache if possible, or the netfs if not. The buffer may be larger than * the file content; unused beyond the EOF will be zero-filled. The content * will be read with a single I/O request (though this may be retried). * * The calling netfs must initialise a netfs context contiguous to the vfs * inode before calling this. * * This is usable whether or not caching is enabled. If caching is enabled, * the data will be stored as a single object into the cache. */ ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_iter *iter) { struct netfs_io_request *rreq; struct netfs_inode *ictx = netfs_inode(inode); ssize_t ret; rreq = netfs_alloc_request(inode->i_mapping, file, 0, iov_iter_count(iter), NETFS_READ_SINGLE); if (IS_ERR(rreq)) return PTR_ERR(rreq); ret = netfs_single_begin_cache_read(rreq, ictx); if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) goto cleanup_free; netfs_stat(&netfs_n_rh_read_single); trace_netfs_read(rreq, 0, rreq->len, netfs_read_trace_read_single); rreq->buffer.iter = *iter; netfs_single_dispatch_read(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, true, netfs_rreq_trace_put_return); return ret; cleanup_free: netfs_put_request(rreq, false, netfs_rreq_trace_put_failed); return ret; } EXPORT_SYMBOL(netfs_read_single);