18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/9p/vfs_addr.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file contians vfs address (mmap) ops for 9P2000. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci#include <linux/file.h> 158c2ecf20Sopenharmony_ci#include <linux/stat.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/inet.h> 188c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 198c2ecf20Sopenharmony_ci#include <linux/idr.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/uio.h> 228c2ecf20Sopenharmony_ci#include <linux/bvec.h> 238c2ecf20Sopenharmony_ci#include <net/9p/9p.h> 248c2ecf20Sopenharmony_ci#include <net/9p/client.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "v9fs.h" 278c2ecf20Sopenharmony_ci#include "v9fs_vfs.h" 288c2ecf20Sopenharmony_ci#include "cache.h" 298c2ecf20Sopenharmony_ci#include "fid.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * v9fs_fid_readpage - read an entire page in from 9P 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * @fid: fid being read 358c2ecf20Sopenharmony_ci * @page: structure to page 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic int v9fs_fid_readpage(void *data, struct page *page) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct p9_fid *fid = data; 418c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 428c2ecf20Sopenharmony_ci struct bio_vec bvec = {.bv_page = page, .bv_len = PAGE_SIZE}; 438c2ecf20Sopenharmony_ci struct iov_iter to; 448c2ecf20Sopenharmony_ci int retval, err; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "\n"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci BUG_ON(!PageLocked(page)); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci retval = v9fs_readpage_from_fscache(inode, page); 518c2ecf20Sopenharmony_ci if (retval == 0) 528c2ecf20Sopenharmony_ci return retval; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci iov_iter_bvec(&to, READ, &bvec, 1, PAGE_SIZE); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci retval = p9_client_read(fid, page_offset(page), &to, &err); 578c2ecf20Sopenharmony_ci if (err) { 588c2ecf20Sopenharmony_ci v9fs_uncache_page(inode, page); 598c2ecf20Sopenharmony_ci retval = err; 608c2ecf20Sopenharmony_ci goto done; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci zero_user(page, retval, PAGE_SIZE - retval); 648c2ecf20Sopenharmony_ci flush_dcache_page(page); 658c2ecf20Sopenharmony_ci SetPageUptodate(page); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci v9fs_readpage_to_fscache(inode, page); 688c2ecf20Sopenharmony_ci retval = 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cidone: 718c2ecf20Sopenharmony_ci unlock_page(page); 728c2ecf20Sopenharmony_ci return retval; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * v9fs_vfs_readpage - read an entire page in from 9P 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * @filp: file being read 798c2ecf20Sopenharmony_ci * @page: structure to page 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int v9fs_vfs_readpage(struct file *filp, struct page *page) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return v9fs_fid_readpage(filp->private_data, page); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * v9fs_vfs_readpages - read a set of pages from 9P 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * @filp: file being read 928c2ecf20Sopenharmony_ci * @mapping: the address space 938c2ecf20Sopenharmony_ci * @pages: list of pages to read 948c2ecf20Sopenharmony_ci * @nr_pages: count of pages to read 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping, 998c2ecf20Sopenharmony_ci struct list_head *pages, unsigned nr_pages) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int ret = 0; 1028c2ecf20Sopenharmony_ci struct inode *inode; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci inode = mapping->host; 1058c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages); 1088c2ecf20Sopenharmony_ci if (ret == 0) 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ret = read_cache_pages(mapping, pages, v9fs_fid_readpage, 1128c2ecf20Sopenharmony_ci filp->private_data); 1138c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, " = %d\n", ret); 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/** 1188c2ecf20Sopenharmony_ci * v9fs_release_page - release the private state associated with a page 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Returns 1 if the page can be released, false otherwise. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int v9fs_release_page(struct page *page, gfp_t gfp) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci if (PagePrivate(page)) 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci return v9fs_fscache_release_page(page, gfp); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * v9fs_invalidate_page - Invalidate a page completely or partially 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * @page: structure to page 1348c2ecf20Sopenharmony_ci * @offset: offset in the page 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void v9fs_invalidate_page(struct page *page, unsigned int offset, 1388c2ecf20Sopenharmony_ci unsigned int length) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * If called with zero offset, we should release 1428c2ecf20Sopenharmony_ci * the private state assocated with the page 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci if (offset == 0 && length == PAGE_SIZE) 1458c2ecf20Sopenharmony_ci v9fs_fscache_invalidate_page(page); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int v9fs_vfs_writepage_locked(struct page *page) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 1518c2ecf20Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 1528c2ecf20Sopenharmony_ci loff_t size = i_size_read(inode); 1538c2ecf20Sopenharmony_ci struct iov_iter from; 1548c2ecf20Sopenharmony_ci struct bio_vec bvec; 1558c2ecf20Sopenharmony_ci int err, len; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (page->index == size >> PAGE_SHIFT) 1588c2ecf20Sopenharmony_ci len = size & ~PAGE_MASK; 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci len = PAGE_SIZE; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci bvec.bv_page = page; 1638c2ecf20Sopenharmony_ci bvec.bv_offset = 0; 1648c2ecf20Sopenharmony_ci bvec.bv_len = len; 1658c2ecf20Sopenharmony_ci iov_iter_bvec(&from, WRITE, &bvec, 1, len); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* We should have writeback_fid always set */ 1688c2ecf20Sopenharmony_ci BUG_ON(!v9inode->writeback_fid); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci set_page_writeback(page); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci p9_client_write(v9inode->writeback_fid, page_offset(page), &from, &err); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci end_page_writeback(page); 1758c2ecf20Sopenharmony_ci return err; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci int retval; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "page %p\n", page); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci retval = v9fs_vfs_writepage_locked(page); 1858c2ecf20Sopenharmony_ci if (retval < 0) { 1868c2ecf20Sopenharmony_ci if (retval == -EAGAIN) { 1878c2ecf20Sopenharmony_ci redirty_page_for_writepage(wbc, page); 1888c2ecf20Sopenharmony_ci retval = 0; 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci SetPageError(page); 1918c2ecf20Sopenharmony_ci mapping_set_error(page->mapping, retval); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } else 1948c2ecf20Sopenharmony_ci retval = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci unlock_page(page); 1978c2ecf20Sopenharmony_ci return retval; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/** 2018c2ecf20Sopenharmony_ci * v9fs_launder_page - Writeback a dirty page 2028c2ecf20Sopenharmony_ci * Returns 0 on success. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int v9fs_launder_page(struct page *page) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int retval; 2088c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci v9fs_fscache_wait_on_page_write(inode, page); 2118c2ecf20Sopenharmony_ci if (clear_page_dirty_for_io(page)) { 2128c2ecf20Sopenharmony_ci retval = v9fs_vfs_writepage_locked(page); 2138c2ecf20Sopenharmony_ci if (retval) 2148c2ecf20Sopenharmony_ci return retval; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/** 2208c2ecf20Sopenharmony_ci * v9fs_direct_IO - 9P address space operation for direct I/O 2218c2ecf20Sopenharmony_ci * @iocb: target I/O control block 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * The presence of v9fs_direct_IO() in the address space ops vector 2248c2ecf20Sopenharmony_ci * allowes open() O_DIRECT flags which would have failed otherwise. 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * In the non-cached mode, we shunt off direct read and write requests before 2278c2ecf20Sopenharmony_ci * the VFS gets them, so this method should never be called. 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * Direct IO is not 'yet' supported in the cached mode. Hence when 2308c2ecf20Sopenharmony_ci * this routine is called through generic_file_aio_read(), the read/write fails 2318c2ecf20Sopenharmony_ci * with an error. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic ssize_t 2358c2ecf20Sopenharmony_civ9fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct file *file = iocb->ki_filp; 2388c2ecf20Sopenharmony_ci loff_t pos = iocb->ki_pos; 2398c2ecf20Sopenharmony_ci ssize_t n; 2408c2ecf20Sopenharmony_ci int err = 0; 2418c2ecf20Sopenharmony_ci if (iov_iter_rw(iter) == WRITE) { 2428c2ecf20Sopenharmony_ci n = p9_client_write(file->private_data, pos, iter, &err); 2438c2ecf20Sopenharmony_ci if (n) { 2448c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 2458c2ecf20Sopenharmony_ci loff_t i_size = i_size_read(inode); 2468c2ecf20Sopenharmony_ci if (pos + n > i_size) 2478c2ecf20Sopenharmony_ci inode_add_bytes(inode, pos + n - i_size); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci n = p9_client_read(file->private_data, pos, iter, &err); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci return n ? n : err; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int v9fs_write_begin(struct file *filp, struct address_space *mapping, 2568c2ecf20Sopenharmony_ci loff_t pos, unsigned len, unsigned flags, 2578c2ecf20Sopenharmony_ci struct page **pagep, void **fsdata) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci int retval = 0; 2608c2ecf20Sopenharmony_ci struct page *page; 2618c2ecf20Sopenharmony_ci struct v9fs_inode *v9inode; 2628c2ecf20Sopenharmony_ci pgoff_t index = pos >> PAGE_SHIFT; 2638c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci v9inode = V9FS_I(inode); 2698c2ecf20Sopenharmony_cistart: 2708c2ecf20Sopenharmony_ci page = grab_cache_page_write_begin(mapping, index, flags); 2718c2ecf20Sopenharmony_ci if (!page) { 2728c2ecf20Sopenharmony_ci retval = -ENOMEM; 2738c2ecf20Sopenharmony_ci goto out; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci BUG_ON(!v9inode->writeback_fid); 2768c2ecf20Sopenharmony_ci if (PageUptodate(page)) 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (len == PAGE_SIZE) 2808c2ecf20Sopenharmony_ci goto out; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci retval = v9fs_fid_readpage(v9inode->writeback_fid, page); 2838c2ecf20Sopenharmony_ci put_page(page); 2848c2ecf20Sopenharmony_ci if (!retval) 2858c2ecf20Sopenharmony_ci goto start; 2868c2ecf20Sopenharmony_ciout: 2878c2ecf20Sopenharmony_ci *pagep = page; 2888c2ecf20Sopenharmony_ci return retval; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int v9fs_write_end(struct file *filp, struct address_space *mapping, 2928c2ecf20Sopenharmony_ci loff_t pos, unsigned len, unsigned copied, 2938c2ecf20Sopenharmony_ci struct page *page, void *fsdata) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci loff_t last_pos = pos + copied; 2968c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!PageUptodate(page)) { 3018c2ecf20Sopenharmony_ci if (unlikely(copied < len)) { 3028c2ecf20Sopenharmony_ci copied = 0; 3038c2ecf20Sopenharmony_ci goto out; 3048c2ecf20Sopenharmony_ci } else if (len == PAGE_SIZE) { 3058c2ecf20Sopenharmony_ci SetPageUptodate(page); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * No need to use i_size_read() here, the i_size 3108c2ecf20Sopenharmony_ci * cannot change under us because we hold the i_mutex. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (last_pos > inode->i_size) { 3138c2ecf20Sopenharmony_ci inode_add_bytes(inode, last_pos - inode->i_size); 3148c2ecf20Sopenharmony_ci i_size_write(inode, last_pos); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci set_page_dirty(page); 3178c2ecf20Sopenharmony_ciout: 3188c2ecf20Sopenharmony_ci unlock_page(page); 3198c2ecf20Sopenharmony_ci put_page(page); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return copied; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciconst struct address_space_operations v9fs_addr_operations = { 3268c2ecf20Sopenharmony_ci .readpage = v9fs_vfs_readpage, 3278c2ecf20Sopenharmony_ci .readpages = v9fs_vfs_readpages, 3288c2ecf20Sopenharmony_ci .set_page_dirty = __set_page_dirty_nobuffers, 3298c2ecf20Sopenharmony_ci .writepage = v9fs_vfs_writepage, 3308c2ecf20Sopenharmony_ci .write_begin = v9fs_write_begin, 3318c2ecf20Sopenharmony_ci .write_end = v9fs_write_end, 3328c2ecf20Sopenharmony_ci .releasepage = v9fs_release_page, 3338c2ecf20Sopenharmony_ci .invalidatepage = v9fs_invalidate_page, 3348c2ecf20Sopenharmony_ci .launder_page = v9fs_launder_page, 3358c2ecf20Sopenharmony_ci .direct_IO = v9fs_direct_IO, 3368c2ecf20Sopenharmony_ci}; 337