162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Cache data I/O routines 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL OPERATION 862306a36Sopenharmony_ci#include <linux/fscache-cache.h> 962306a36Sopenharmony_ci#include <linux/uio.h> 1062306a36Sopenharmony_ci#include <linux/bvec.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/uio.h> 1362306a36Sopenharmony_ci#include "internal.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * fscache_wait_for_operation - Wait for an object become accessible 1762306a36Sopenharmony_ci * @cres: The cache resources for the operation being performed 1862306a36Sopenharmony_ci * @want_state: The minimum state the object must be at 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * See if the target cache object is at the specified minimum state of 2162306a36Sopenharmony_ci * accessibility yet, and if not, wait for it. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cibool fscache_wait_for_operation(struct netfs_cache_resources *cres, 2462306a36Sopenharmony_ci enum fscache_want_state want_state) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct fscache_cookie *cookie = fscache_cres_cookie(cres); 2762306a36Sopenharmony_ci enum fscache_cookie_state state; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciagain: 3062306a36Sopenharmony_ci if (!fscache_cache_is_live(cookie->volume->cache)) { 3162306a36Sopenharmony_ci _leave(" [broken]"); 3262306a36Sopenharmony_ci return false; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci state = fscache_cookie_state(cookie); 3662306a36Sopenharmony_ci _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci switch (state) { 3962306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_CREATING: 4062306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_INVALIDATING: 4162306a36Sopenharmony_ci if (want_state == FSCACHE_WANT_PARAMS) 4262306a36Sopenharmony_ci goto ready; /* There can be no content */ 4362306a36Sopenharmony_ci fallthrough; 4462306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_LOOKING_UP: 4562306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_LRU_DISCARDING: 4662306a36Sopenharmony_ci wait_var_event(&cookie->state, 4762306a36Sopenharmony_ci fscache_cookie_state(cookie) != state); 4862306a36Sopenharmony_ci goto again; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_ACTIVE: 5162306a36Sopenharmony_ci goto ready; 5262306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_DROPPED: 5362306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_RELINQUISHING: 5462306a36Sopenharmony_ci default: 5562306a36Sopenharmony_ci _leave(" [not live]"); 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciready: 6062306a36Sopenharmony_ci if (!cres->cache_priv2) 6162306a36Sopenharmony_ci return cookie->volume->cache->ops->begin_operation(cres, want_state); 6262306a36Sopenharmony_ci return true; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_wait_for_operation); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Begin an I/O operation on the cache, waiting till we reach the right state. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * Attaches the resources required to the operation resources record. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistatic int fscache_begin_operation(struct netfs_cache_resources *cres, 7262306a36Sopenharmony_ci struct fscache_cookie *cookie, 7362306a36Sopenharmony_ci enum fscache_want_state want_state, 7462306a36Sopenharmony_ci enum fscache_access_trace why) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci enum fscache_cookie_state state; 7762306a36Sopenharmony_ci long timeo; 7862306a36Sopenharmony_ci bool once_only = false; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci cres->ops = NULL; 8162306a36Sopenharmony_ci cres->cache_priv = cookie; 8262306a36Sopenharmony_ci cres->cache_priv2 = NULL; 8362306a36Sopenharmony_ci cres->debug_id = cookie->debug_id; 8462306a36Sopenharmony_ci cres->inval_counter = cookie->inval_counter; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!fscache_begin_cookie_access(cookie, why)) 8762306a36Sopenharmony_ci return -ENOBUFS; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciagain: 9062306a36Sopenharmony_ci spin_lock(&cookie->lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci state = fscache_cookie_state(cookie); 9362306a36Sopenharmony_ci _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci switch (state) { 9662306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_LOOKING_UP: 9762306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_LRU_DISCARDING: 9862306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_INVALIDATING: 9962306a36Sopenharmony_ci goto wait_for_file_wrangling; 10062306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_CREATING: 10162306a36Sopenharmony_ci if (want_state == FSCACHE_WANT_PARAMS) 10262306a36Sopenharmony_ci goto ready; /* There can be no content */ 10362306a36Sopenharmony_ci goto wait_for_file_wrangling; 10462306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_ACTIVE: 10562306a36Sopenharmony_ci goto ready; 10662306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_DROPPED: 10762306a36Sopenharmony_ci case FSCACHE_COOKIE_STATE_RELINQUISHING: 10862306a36Sopenharmony_ci WARN(1, "Can't use cookie in state %u\n", cookie->state); 10962306a36Sopenharmony_ci goto not_live; 11062306a36Sopenharmony_ci default: 11162306a36Sopenharmony_ci goto not_live; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciready: 11562306a36Sopenharmony_ci spin_unlock(&cookie->lock); 11662306a36Sopenharmony_ci if (!cookie->volume->cache->ops->begin_operation(cres, want_state)) 11762306a36Sopenharmony_ci goto failed; 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciwait_for_file_wrangling: 12162306a36Sopenharmony_ci spin_unlock(&cookie->lock); 12262306a36Sopenharmony_ci trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), 12362306a36Sopenharmony_ci atomic_read(&cookie->n_accesses), 12462306a36Sopenharmony_ci fscache_access_io_wait); 12562306a36Sopenharmony_ci timeo = wait_var_event_timeout(&cookie->state, 12662306a36Sopenharmony_ci fscache_cookie_state(cookie) != state, 20 * HZ); 12762306a36Sopenharmony_ci if (timeo <= 1 && !once_only) { 12862306a36Sopenharmony_ci pr_warn("%s: cookie state change wait timed out: cookie->state=%u state=%u", 12962306a36Sopenharmony_ci __func__, fscache_cookie_state(cookie), state); 13062306a36Sopenharmony_ci fscache_print_cookie(cookie, 'O'); 13162306a36Sopenharmony_ci once_only = true; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci goto again; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cinot_live: 13662306a36Sopenharmony_ci spin_unlock(&cookie->lock); 13762306a36Sopenharmony_cifailed: 13862306a36Sopenharmony_ci cres->cache_priv = NULL; 13962306a36Sopenharmony_ci cres->ops = NULL; 14062306a36Sopenharmony_ci fscache_end_cookie_access(cookie, fscache_access_io_not_live); 14162306a36Sopenharmony_ci _leave(" = -ENOBUFS"); 14262306a36Sopenharmony_ci return -ENOBUFS; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciint __fscache_begin_read_operation(struct netfs_cache_resources *cres, 14662306a36Sopenharmony_ci struct fscache_cookie *cookie) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return fscache_begin_operation(cres, cookie, FSCACHE_WANT_PARAMS, 14962306a36Sopenharmony_ci fscache_access_io_read); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_begin_read_operation); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciint __fscache_begin_write_operation(struct netfs_cache_resources *cres, 15462306a36Sopenharmony_ci struct fscache_cookie *cookie) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return fscache_begin_operation(cres, cookie, FSCACHE_WANT_PARAMS, 15762306a36Sopenharmony_ci fscache_access_io_write); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_begin_write_operation); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * fscache_dirty_folio - Mark folio dirty and pin a cache object for writeback 16362306a36Sopenharmony_ci * @mapping: The mapping the folio belongs to. 16462306a36Sopenharmony_ci * @folio: The folio being dirtied. 16562306a36Sopenharmony_ci * @cookie: The cookie referring to the cache object 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Set the dirty flag on a folio and pin an in-use cache object in memory 16862306a36Sopenharmony_ci * so that writeback can later write to it. This is intended 16962306a36Sopenharmony_ci * to be called from the filesystem's ->dirty_folio() method. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * Return: true if the dirty flag was set on the folio, false otherwise. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cibool fscache_dirty_folio(struct address_space *mapping, struct folio *folio, 17462306a36Sopenharmony_ci struct fscache_cookie *cookie) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct inode *inode = mapping->host; 17762306a36Sopenharmony_ci bool need_use = false; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci _enter(""); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!filemap_dirty_folio(mapping, folio)) 18262306a36Sopenharmony_ci return false; 18362306a36Sopenharmony_ci if (!fscache_cookie_valid(cookie)) 18462306a36Sopenharmony_ci return true; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!(inode->i_state & I_PINNING_FSCACHE_WB)) { 18762306a36Sopenharmony_ci spin_lock(&inode->i_lock); 18862306a36Sopenharmony_ci if (!(inode->i_state & I_PINNING_FSCACHE_WB)) { 18962306a36Sopenharmony_ci inode->i_state |= I_PINNING_FSCACHE_WB; 19062306a36Sopenharmony_ci need_use = true; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (need_use) 19562306a36Sopenharmony_ci fscache_use_cookie(cookie, true); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci return true; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_dirty_folio); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct fscache_write_request { 20262306a36Sopenharmony_ci struct netfs_cache_resources cache_resources; 20362306a36Sopenharmony_ci struct address_space *mapping; 20462306a36Sopenharmony_ci loff_t start; 20562306a36Sopenharmony_ci size_t len; 20662306a36Sopenharmony_ci bool set_bits; 20762306a36Sopenharmony_ci netfs_io_terminated_t term_func; 20862306a36Sopenharmony_ci void *term_func_priv; 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_civoid __fscache_clear_page_bits(struct address_space *mapping, 21262306a36Sopenharmony_ci loff_t start, size_t len) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci pgoff_t first = start / PAGE_SIZE; 21562306a36Sopenharmony_ci pgoff_t last = (start + len - 1) / PAGE_SIZE; 21662306a36Sopenharmony_ci struct page *page; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (len) { 21962306a36Sopenharmony_ci XA_STATE(xas, &mapping->i_pages, first); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci rcu_read_lock(); 22262306a36Sopenharmony_ci xas_for_each(&xas, page, last) { 22362306a36Sopenharmony_ci end_page_fscache(page); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci rcu_read_unlock(); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_clear_page_bits); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * Deal with the completion of writing the data to the cache. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic void fscache_wreq_done(void *priv, ssize_t transferred_or_error, 23462306a36Sopenharmony_ci bool was_async) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct fscache_write_request *wreq = priv; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci fscache_clear_page_bits(wreq->mapping, wreq->start, wreq->len, 23962306a36Sopenharmony_ci wreq->set_bits); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (wreq->term_func) 24262306a36Sopenharmony_ci wreq->term_func(wreq->term_func_priv, transferred_or_error, 24362306a36Sopenharmony_ci was_async); 24462306a36Sopenharmony_ci fscache_end_operation(&wreq->cache_resources); 24562306a36Sopenharmony_ci kfree(wreq); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_civoid __fscache_write_to_cache(struct fscache_cookie *cookie, 24962306a36Sopenharmony_ci struct address_space *mapping, 25062306a36Sopenharmony_ci loff_t start, size_t len, loff_t i_size, 25162306a36Sopenharmony_ci netfs_io_terminated_t term_func, 25262306a36Sopenharmony_ci void *term_func_priv, 25362306a36Sopenharmony_ci bool cond) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct fscache_write_request *wreq; 25662306a36Sopenharmony_ci struct netfs_cache_resources *cres; 25762306a36Sopenharmony_ci struct iov_iter iter; 25862306a36Sopenharmony_ci int ret = -ENOBUFS; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (len == 0) 26162306a36Sopenharmony_ci goto abandon; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci _enter("%llx,%zx", start, len); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci wreq = kzalloc(sizeof(struct fscache_write_request), GFP_NOFS); 26662306a36Sopenharmony_ci if (!wreq) 26762306a36Sopenharmony_ci goto abandon; 26862306a36Sopenharmony_ci wreq->mapping = mapping; 26962306a36Sopenharmony_ci wreq->start = start; 27062306a36Sopenharmony_ci wreq->len = len; 27162306a36Sopenharmony_ci wreq->set_bits = cond; 27262306a36Sopenharmony_ci wreq->term_func = term_func; 27362306a36Sopenharmony_ci wreq->term_func_priv = term_func_priv; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci cres = &wreq->cache_resources; 27662306a36Sopenharmony_ci if (fscache_begin_operation(cres, cookie, FSCACHE_WANT_WRITE, 27762306a36Sopenharmony_ci fscache_access_io_write) < 0) 27862306a36Sopenharmony_ci goto abandon_free; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = cres->ops->prepare_write(cres, &start, &len, i_size, false); 28162306a36Sopenharmony_ci if (ret < 0) 28262306a36Sopenharmony_ci goto abandon_end; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* TODO: Consider clearing page bits now for space the write isn't 28562306a36Sopenharmony_ci * covering. This is more complicated than it appears when THPs are 28662306a36Sopenharmony_ci * taken into account. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_SOURCE, &mapping->i_pages, start, len); 29062306a36Sopenharmony_ci fscache_write(cres, start, &iter, fscache_wreq_done, wreq); 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciabandon_end: 29462306a36Sopenharmony_ci return fscache_wreq_done(wreq, ret, false); 29562306a36Sopenharmony_ciabandon_free: 29662306a36Sopenharmony_ci kfree(wreq); 29762306a36Sopenharmony_ciabandon: 29862306a36Sopenharmony_ci fscache_clear_page_bits(mapping, start, len, cond); 29962306a36Sopenharmony_ci if (term_func) 30062306a36Sopenharmony_ci term_func(term_func_priv, ret, false); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_write_to_cache); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* 30562306a36Sopenharmony_ci * Change the size of a backing object. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_civoid __fscache_resize_cookie(struct fscache_cookie *cookie, loff_t new_size) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct netfs_cache_resources cres; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci trace_fscache_resize(cookie, new_size); 31262306a36Sopenharmony_ci if (fscache_begin_operation(&cres, cookie, FSCACHE_WANT_WRITE, 31362306a36Sopenharmony_ci fscache_access_io_resize) == 0) { 31462306a36Sopenharmony_ci fscache_stat(&fscache_n_resizes); 31562306a36Sopenharmony_ci set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* We cannot defer a resize as we need to do it inside the 31862306a36Sopenharmony_ci * netfs's inode lock so that we're serialised with respect to 31962306a36Sopenharmony_ci * writes. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci cookie->volume->cache->ops->resize_cookie(&cres, new_size); 32262306a36Sopenharmony_ci fscache_end_operation(&cres); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci fscache_stat(&fscache_n_resizes_null); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_resize_cookie); 328