summarylogtreecommitdiffstats
path: root/0012-Linux-5.18-replace-readpages-with-readahead.patch
diff options
context:
space:
mode:
Diffstat (limited to '0012-Linux-5.18-replace-readpages-with-readahead.patch')
-rw-r--r--0012-Linux-5.18-replace-readpages-with-readahead.patch304
1 files changed, 304 insertions, 0 deletions
diff --git a/0012-Linux-5.18-replace-readpages-with-readahead.patch b/0012-Linux-5.18-replace-readpages-with-readahead.patch
new file mode 100644
index 000000000000..b7c474c6f2da
--- /dev/null
+++ b/0012-Linux-5.18-replace-readpages-with-readahead.patch
@@ -0,0 +1,304 @@
+From 7daef2e2bd350a1bee8bdbe6fdce3923c7867299 Mon Sep 17 00:00:00 2001
+From: Cheyenne Wills <cwills@sinenomine.net>
+Date: Tue, 31 May 2022 14:43:33 -0600
+Subject: [PATCH 12/12] Linux-5.18: replace readpages with readahead
+
+The linux 5.18 the commit 'fs: Remove ->readpages address space
+operation' (704528d8) removes the address_space_operations operation
+"readpages" which is replaced with the "readahead" operation
+that was introduced with the 5.8 commit 'mm: add readahead address
+space operation' (8151b4c8).
+
+The address_space_operation function, readahead is called by the VM
+to read pages. A filesystem provides an implementation to handle this
+operation.
+
+When readahead is called, the list of pages have already been added to
+the lru caches and are locked. The implementation of the readahead
+function needs to handle decrementing the reference count, unlocking the
+page and setting PageUptoDate when the IO has completed successfully. IO
+errors are ignored by the vfs during readahead (errors will be detected
+later in the vfs processing). We must simply unlock the page if an error
+occurs.
+ (See Linux Documentation/filesystems/vfs.rst)
+
+Add an autoconf test to detect the presence of 'readahead' in the
+address_space_operations structure.
+
+For the implementation of readahead (which is contained in Linux's
+osi_vnodeops.c):
+
+Add new functions 'afs_linux_bypass_readahead' and 'afs_linux_readahead'
+as replacements for 'afs_bypass_readpages' and 'afs_linux_readpages'
+when the linux kernel supports the readahead operation.
+
+Don't manage the LRU for pages for the readahead case (e.g. don't
+call the afs_lru_cache_* functions).
+
+Notes:
+ In afs_linux_bypass_readahead, the pages are already locked and are
+ already in the page cache, we just need to place the page into the
+ iovecp. The page's refcount will be decremented and will be unlocked
+ when processing the read request.
+
+ In afs_linux_readahead, the lrupages is needed in case a page is added
+ to the cachefp's mapping in afs_linux_read_cache (which also handles
+ unlocking the page). Failure to unlock the page if there was no tdc
+ results in the read process waiting on that page.
+
+Change-Id: I6960a2fc14df85869c373f3e3afbf3ee5eb7228f
+---
+ src/afs/LINUX/osi_vnodeops.c | 190 +++++++++++++++++++++++++++++++++-
+ src/cf/linux-kernel-struct.m4 | 2 +
+ 2 files changed, 190 insertions(+), 2 deletions(-)
+
+diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c
+index 893f8afff..1cc7cf8d6 100644
+--- a/src/afs/LINUX/osi_vnodeops.c
++++ b/src/afs/LINUX/osi_vnodeops.c
+@@ -2536,6 +2536,92 @@ afs_linux_prefetch(struct file *fp, struct page *pp)
+
+ }
+
++#if defined(STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD)
++static void
++afs_linux_bypass_readahead(struct readahead_control *rac)
++{
++ struct file *fp = rac->file;
++ unsigned num_pages = readahead_count(rac);
++ afs_int32 page_ix;
++ afs_offs_t offset;
++ struct iovec* iovecp;
++ struct nocache_read_request *ancr;
++ struct page *pp;
++
++ afs_int32 code = 0;
++
++ cred_t *credp;
++ struct inode *ip = FILE_INODE(fp);
++ struct vcache *avc = VTOAFS(ip);
++ afs_int32 base_index = 0;
++ afs_int32 page_count = 0;
++ afs_int32 isize;
++
++ /* background thread must free: iovecp, auio, ancr */
++ ancr = afs_alloc_ncr(num_pages);
++ if (!ancr)
++ goto error;
++
++ iovecp = ancr->auio->uio_iov;
++
++ for (page_ix = 0; page_ix < num_pages; ++page_ix) {
++ pp = readahead_page(rac);
++ if (pp == NULL)
++ break;
++
++ isize = (i_size_read(fp->f_mapping->host) - 1) >> PAGE_SHIFT;
++ if (pp->index > isize) {
++ if(PageLocked(pp))
++ unlock_page(pp);
++ continue;
++ }
++
++ if (page_ix == 0) {
++ offset = page_offset(pp);
++ ancr->offset = ancr->auio->uio_offset = offset;
++ base_index = pp->index;
++ }
++ iovecp[page_ix].iov_len = PAGE_SIZE;
++ if (base_index != pp->index) {
++ if (PageLocked(pp))
++ unlock_page(pp);
++ put_page(pp);
++ iovecp[page_ix].iov_base = NULL;
++ base_index++;
++ ancr->length -= PAGE_SIZE;
++ continue;
++ }
++ base_index++;
++ page_count++;
++ /* save the page for background map */
++ iovecp[page_ix].iov_base = pp;
++ }
++
++ /* If there were useful pages in the page list, schedule
++ * the read */
++ if (page_count) {
++ credp = crref();
++ code = afs_ReadNoCache(avc, ancr, credp);
++ crfree(credp);
++ } else {
++ /* If there is nothing for the background thread to handle,
++ * it won't be freeing the things that we never gave it */
++ afs_free_ncr(&ancr);
++ }
++ /* we do not flush, release, or unmap pages--that will be
++ * done for us by the background thread as each page comes in
++ * from the fileserver */
++ return;
++
++ error:
++ while ((pp = readahead_page(rac)) != NULL) {
++ if (PageLocked(pp)) {
++ unlock_page(pp);
++ }
++ }
++ return;
++}
++#else /* STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD */
+ static int
+ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
+ struct list_head *page_list, unsigned num_pages)
+@@ -2632,7 +2718,7 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
+ * from the fileserver */
+ return afs_convert_code(code);
+ }
+-
++#endif /* STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD */
+
+ static int
+ afs_linux_bypass_readpage(struct file *fp, struct page *pp)
+@@ -2806,11 +2892,106 @@ get_dcache_readahead(struct dcache **adc, struct file **acacheFp,
+ return code;
+ }
+
+-/* Readpages reads a number of pages for a particular file. We use
++#if defined(STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD)
++/*
++ * Readahead reads a number of pages for a particular file. We use
+ * this to optimise the reading, by limiting the number of times upon which
+ * we have to lookup, lock and open vcaches and dcaches
+ */
++static void
++afs_linux_readahead(struct readahead_control *rac)
++{
++ struct page *page;
++ struct address_space *mapping = rac->mapping;
++ struct inode *inode = mapping->host;
++ struct vcache *avc = VTOAFS(inode);
++ struct dcache *tdc;
++ struct file *cacheFp = NULL;
++ int code = 0;
++ loff_t offset;
++ struct afs_lru_pages lrupages;
++ struct afs_pagecopy_task *task;
++
++ if (afs_linux_bypass_check(inode)) {
++ afs_linux_bypass_readahead(rac);
++ return;
++ }
++ if (cacheDiskType == AFS_FCACHE_TYPE_MEM)
++ return;
++
++ /* No readpage (ex: tmpfs) , skip */
++ if (cachefs_noreadpage)
++ return;
++
++ AFS_GLOCK();
++ if ((code = afs_linux_VerifyVCache(avc, NULL))) {
++ AFS_GUNLOCK();
++ return;
++ }
++
++ ObtainWriteLock(&avc->lock, 912);
++ AFS_GUNLOCK();
++
++ task = afs_pagecopy_init_task();
+
++ tdc = NULL;
++
++ afs_lru_cache_init(&lrupages);
++
++ while ((page = readahead_page(rac)) != NULL) {
++ offset = page_offset(page);
++
++ code = get_dcache_readahead(&tdc, &cacheFp, avc, offset);
++ if (code)
++ goto error;
++
++ if (tdc != NULL) {
++ /* Note that add_to_page_cache() locked 'page'.
++ * afs_linux_read_cache() is guaranteed to handle unlocking it. */
++ afs_linux_read_cache(cacheFp, page, tdc->f.chunk, &lrupages, task);
++ } else if (PageLocked(page)) {
++ unlock_page(page);
++ }
++ put_page(page);
++ }
++ afs_lru_cache_finalize(&lrupages);
++
++ done:
++ if (cacheFp)
++ filp_close(cacheFp, NULL);
++
++ afs_pagecopy_put_task(task);
++
++ AFS_GLOCK();
++ if (tdc != NULL) {
++ ReleaseReadLock(&tdc->lock);
++ afs_PutDCache(tdc);
++ }
++
++ ReleaseWriteLock(&avc->lock);
++ AFS_GUNLOCK();
++ return;
++
++ error:
++ /*
++ * Any error detected during readahead are ignored by the vfs.
++ * Simply unlock the page(s).
++ */
++ if (PageLocked(page)) {
++ unlock_page(page);
++ }
++ while ((page = readahead_page(rac)) != NULL) {
++ if (PageLocked(page)) {
++ unlock_page(page);
++ }
++ }
++ goto done;
++}
++#else /* STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD */
++/* Readpages reads a number of pages for a particular file. We use
++ * this to optimise the reading, by limiting the number of times upon which
++ * we have to lookup, lock and open vcaches and dcaches
++ */
+ static int
+ afs_linux_readpages(struct file *fp, struct address_space *mapping,
+ struct list_head *page_list, unsigned int num_pages)
+@@ -2887,6 +3068,7 @@ out:
+ AFS_GUNLOCK();
+ return 0;
+ }
++#endif /* STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD */
+
+ /* Prepare an AFS vcache for writeback. Should be called with the vcache
+ * locked */
+@@ -3325,7 +3507,11 @@ static struct inode_operations afs_file_iops = {
+
+ static struct address_space_operations afs_file_aops = {
+ .readpage = afs_linux_readpage,
++#if defined(STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_READAHEAD)
++ .readahead = afs_linux_readahead,
++#else
+ .readpages = afs_linux_readpages,
++#endif
+ .writepage = afs_linux_writepage,
+ #if defined(STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_DIRTY_FOLIO)
+ .dirty_folio = block_dirty_folio,
+diff --git a/src/cf/linux-kernel-struct.m4 b/src/cf/linux-kernel-struct.m4
+index 2d8cee655..597289bc8 100644
+--- a/src/cf/linux-kernel-struct.m4
++++ b/src/cf/linux-kernel-struct.m4
+@@ -5,6 +5,8 @@ AC_CHECK_LINUX_STRUCT([address_space_operations],
+ [write_begin], [fs.h])
+ dnl linux 5.18 replaced set_page_dirty with dirty_folio
+ AC_CHECK_LINUX_STRUCT([address_space_operations], [dirty_folio], [fs.h])
++dnl linux 5.18 replaced readpages with readahead (introduced in 5.8)
++AC_CHECK_LINUX_STRUCT([address_space_operations], [readahead], [fs.h])
+ AC_CHECK_LINUX_STRUCT([backing_dev_info], [name],
+ [backing-dev.h])
+ AC_CHECK_LINUX_STRUCT([cred], [session_keyring], [cred.h])
+--
+2.36.1
+