diff options
Diffstat (limited to '0012-Linux-5.18-replace-readpages-with-readahead.patch')
-rw-r--r-- | 0012-Linux-5.18-replace-readpages-with-readahead.patch | 304 |
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 + |