lkml.org 
[lkml]   [2021]   [Jan]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 10/25] netfs: Add write_begin helper
    From
    Date
    Add a helper to do the pre-reading work for the netfs write_begin address
    space op.

    Signed-off-by: David Howells <dhowells@redhat.com>
    ---

    fs/netfs/internal.h | 2 +
    fs/netfs/read_helper.c | 165 ++++++++++++++++++++++++++++++++++++++++++
    fs/netfs/stats.c | 10 ++-
    include/linux/netfs.h | 8 ++
    include/trace/events/netfs.h | 4 +
    5 files changed, 185 insertions(+), 4 deletions(-)

    diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
    index d83317b1eb9d..3d7ab3ab5743 100644
    --- a/fs/netfs/internal.h
    +++ b/fs/netfs/internal.h
    @@ -34,8 +34,10 @@ extern atomic_t netfs_n_rh_read_failed;
    extern atomic_t netfs_n_rh_zero;
    extern atomic_t netfs_n_rh_short_read;
    extern atomic_t netfs_n_rh_write;
    +extern atomic_t netfs_n_rh_write_begin;
    extern atomic_t netfs_n_rh_write_done;
    extern atomic_t netfs_n_rh_write_failed;
    +extern atomic_t netfs_n_rh_write_zskip;


    static inline void netfs_stat(atomic_t *stat)
    diff --git a/fs/netfs/read_helper.c b/fs/netfs/read_helper.c
    index 275ac37e2834..eb34c368617d 100644
    --- a/fs/netfs/read_helper.c
    +++ b/fs/netfs/read_helper.c
    @@ -760,3 +760,168 @@ int netfs_readpage(struct file *file,
    return ret;
    }
    EXPORT_SYMBOL(netfs_readpage);
    +
    +static void netfs_clear_thp(struct page *page)
    +{
    + unsigned int i;
    +
    + for (i = 0; i < thp_nr_pages(page); i++)
    + clear_highpage(page + i);
    +}
    +
    +/**
    + * netfs_write_begin - Helper to prepare for writing
    + * @file: The file to read from
    + * @mapping: The mapping to read from
    + * @pos: File position at which the write will begin
    + * @len: The length of the write in this page
    + * @flags: AOP_* flags
    + * @_page: Where to put the resultant page
    + * @_fsdata: Place for the netfs to store a cookie
    + * @ops: The network filesystem's operations for the helper to use
    + * @netfs_priv: Private netfs data to be retained in the request
    + *
    + * Pre-read data for a write-begin request by drawing data from the cache if
    + * possible, or the netfs if not. Space beyond the EOF is zero-filled.
    + * Multiple I/O requests from different sources will get munged together. If
    + * necessary, the readahead window can be expanded in either direction to a
    + * more convenient alighment for RPC efficiency or to make storage in the cache
    + * feasible.
    + *
    + * The calling netfs must provide a table of operations, only one of which,
    + * issue_op, is mandatory.
    + *
    + * The check_write_begin() operation can be provided to check for and flush
    + * conflicting writes once the page is grabbed and locked. It is passed a
    + * pointer to the fsdata cookie that gets returned to the VM to be passed to
    + * write_end. It is permitted to sleep. It should return 0 if the request
    + * should go ahead; unlock the page and return -EAGAIN to cause the page to be
    + * regot; or return an error.
    + *
    + * This is usable whether or not caching is enabled.
    + */
    +int netfs_write_begin(struct file *file, struct address_space *mapping,
    + loff_t pos, unsigned int len, unsigned int flags,
    + struct page **_page, void **_fsdata,
    + const struct netfs_read_request_ops *ops,
    + void *netfs_priv)
    +{
    + struct netfs_read_request *rreq;
    + struct page *page, *xpage;
    + struct inode *inode = file_inode(file);
    + unsigned int debug_index = 0;
    + pgoff_t index = pos >> PAGE_SHIFT;
    + int pos_in_page = pos & ~PAGE_MASK;
    + loff_t size;
    + int ret;
    +
    + struct readahead_control ractl = {
    + .file = file,
    + .mapping = mapping,
    + ._index = index,
    + ._nr_pages = 0,
    + };
    +
    +retry:
    + page = grab_cache_page_write_begin(mapping, index, 0);
    + if (!page)
    + return -ENOMEM;
    +
    + if (ops->check_write_begin) {
    + /* Allow the netfs (eg. ceph) to flush conflicts. */
    + ret = ops->check_write_begin(file, pos, len, page, _fsdata);
    + if (ret < 0) {
    + if (ret == -EAGAIN)
    + goto retry;
    + goto error;
    + }
    + }
    +
    + if (PageUptodate(page))
    + goto have_page;
    +
    + /* If the page is beyond the EOF, we want to clear it - unless it's
    + * within the cache granule containing the EOF, in which case we need
    + * to preload the granule.
    + */
    + size = i_size_read(inode);
    + if (!ops->is_cache_enabled(inode) &&
    + ((pos_in_page == 0 && len == thp_size(page)) ||
    + (pos >= size) ||
    + (pos_in_page == 0 && (pos + len) >= size))) {
    + netfs_clear_thp(page);
    + SetPageUptodate(page);
    + netfs_stat(&netfs_n_rh_write_zskip);
    + goto have_page_no_wait;
    + }
    +
    + ret = -ENOMEM;
    + rreq = netfs_alloc_read_request(ops, netfs_priv, file);
    + if (!rreq)
    + goto error;
    + rreq->mapping = page->mapping;
    + rreq->start = page->index * PAGE_SIZE;
    + rreq->len = thp_size(page);
    + rreq->no_unlock_page = page->index;
    + __set_bit(NETFS_RREQ_NO_UNLOCK_PAGE, &rreq->flags);
    + netfs_priv = NULL;
    +
    + netfs_stat(&netfs_n_rh_write_begin);
    + trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
    +
    + /* Expand the request to meet caching requirements and download
    + * preferences.
    + */
    + ractl._nr_pages = thp_nr_pages(page);
    + netfs_rreq_expand(rreq, &ractl);
    + netfs_get_read_request(rreq);
    +
    + /* We hold the page locks, so we can drop the references */
    + while ((xpage = readahead_page(&ractl)))
    + if (xpage != page)
    + put_page(xpage);
    +
    + atomic_set(&rreq->nr_rd_ops, 1);
    + do {
    + if (!netfs_rreq_submit_slice(rreq, &debug_index))
    + break;
    +
    + } while (rreq->submitted < rreq->len);
    +
    + // TODO: If we didn't submit enough readage, we need to clean up
    +
    + /* Keep nr_rd_ops incremented so that the ref always belongs to us, and
    + * the service code isn't punted off to a random thread pool to
    + * process.
    + */
    + for (;;) {
    + wait_var_event(&rreq->nr_rd_ops, atomic_read(&rreq->nr_rd_ops) == 1);
    + netfs_rreq_assess(rreq);
    + if (!test_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags))
    + break;
    + cond_resched();
    + }
    +
    + ret = rreq->error;
    + netfs_put_read_request(rreq);
    + if (ret < 0)
    + goto error;
    +
    +have_page:
    + wait_on_page_fscache(page);
    +have_page_no_wait:
    + if (netfs_priv)
    + ops->cleanup(netfs_priv, mapping);
    + *_page = page;
    + _leave(" = 0");
    + return 0;
    +
    +error:
    + unlock_page(page);
    + put_page(page);
    + if (netfs_priv)
    + ops->cleanup(netfs_priv, mapping);
    + _leave(" = %d", ret);
    + return ret;
    +}
    +EXPORT_SYMBOL(netfs_write_begin);
    diff --git a/fs/netfs/stats.c b/fs/netfs/stats.c
    index 3a7a3c10e1cd..cdd09e30ce75 100644
    --- a/fs/netfs/stats.c
    +++ b/fs/netfs/stats.c
    @@ -23,19 +23,23 @@ atomic_t netfs_n_rh_read_failed;
    atomic_t netfs_n_rh_zero;
    atomic_t netfs_n_rh_short_read;
    atomic_t netfs_n_rh_write;
    +atomic_t netfs_n_rh_write_begin;
    atomic_t netfs_n_rh_write_done;
    atomic_t netfs_n_rh_write_failed;
    +atomic_t netfs_n_rh_write_zskip;

    void netfs_stats_show(struct seq_file *m)
    {
    - seq_printf(m, "RdHelp : RA=%u RP=%u rr=%u sr=%u\n",
    + seq_printf(m, "RdHelp : RA=%u RP=%u WB=%u rr=%u sr=%u\n",
    atomic_read(&netfs_n_rh_readahead),
    atomic_read(&netfs_n_rh_readpage),
    + atomic_read(&netfs_n_rh_write_begin),
    atomic_read(&netfs_n_rh_rreq),
    atomic_read(&netfs_n_rh_sreq));
    - seq_printf(m, "RdHelp : ZR=%u sh=%u\n",
    + seq_printf(m, "RdHelp : ZR=%u sh=%u sk=%u\n",
    atomic_read(&netfs_n_rh_zero),
    - atomic_read(&netfs_n_rh_short_read));
    + atomic_read(&netfs_n_rh_short_read),
    + atomic_read(&netfs_n_rh_write_zskip));
    seq_printf(m, "RdHelp : DL=%u ds=%u df=%u di=%u\n",
    atomic_read(&netfs_n_rh_download),
    atomic_read(&netfs_n_rh_download_done),
    diff --git a/include/linux/netfs.h b/include/linux/netfs.h
    index 9a262eb36b0f..1a81baecb182 100644
    --- a/include/linux/netfs.h
    +++ b/include/linux/netfs.h
    @@ -87,11 +87,14 @@ struct netfs_read_request {
    * Operations the network filesystem can/must provide to the helpers.
    */
    struct netfs_read_request_ops {
    + bool (*is_cache_enabled)(struct inode *inode);
    void (*init_rreq)(struct netfs_read_request *rreq, struct file *file);
    void (*expand_readahead)(struct netfs_read_request *rreq);
    bool (*clamp_length)(struct netfs_read_subrequest *subreq);
    void (*issue_op)(struct netfs_read_subrequest *subreq);
    bool (*is_still_valid)(struct netfs_read_request *rreq);
    + int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
    + struct page *page, void **_fsdata);
    void (*done)(struct netfs_read_request *rreq);
    void (*cleanup)(struct address_space *mapping, void *netfs_priv);
    };
    @@ -104,6 +107,11 @@ extern int netfs_readpage(struct file *,
    struct page *,
    const struct netfs_read_request_ops *,
    void *);
    +extern int netfs_write_begin(struct file *, struct address_space *,
    + loff_t, unsigned int, unsigned int, struct page **,
    + void **,
    + const struct netfs_read_request_ops *,
    + void *);

    extern void netfs_subreq_terminated(struct netfs_read_subrequest *, ssize_t);
    extern void netfs_stats_show(struct seq_file *);
    diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h
    index 7044c981349d..ed54de2ed14d 100644
    --- a/include/trace/events/netfs.h
    +++ b/include/trace/events/netfs.h
    @@ -22,6 +22,7 @@ enum netfs_read_trace {
    netfs_read_trace_expanded,
    netfs_read_trace_readahead,
    netfs_read_trace_readpage,
    + netfs_read_trace_write_begin,
    };

    enum netfs_rreq_trace {
    @@ -50,7 +51,8 @@ enum netfs_sreq_trace {
    #define netfs_read_traces \
    EM(netfs_read_trace_expanded, "EXPANDED ") \
    EM(netfs_read_trace_readahead, "READAHEAD") \
    - E_(netfs_read_trace_readpage, "READPAGE ")
    + EM(netfs_read_trace_readpage, "READPAGE ") \
    + E_(netfs_read_trace_write_begin, "WRITEBEGN")

    #define netfs_rreq_traces \
    EM(netfs_rreq_trace_assess, "ASSESS") \

    \
     
     \ /
      Last update: 2021-01-21 01:11    [W:5.131 / U:0.076 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site