162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   CIFS filesystem cache interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *   Copyright (c) 2010 Novell, Inc.
662306a36Sopenharmony_ci *   Author(s): Suresh Jayaraman <sjayaraman@suse.de>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include "fscache.h"
1062306a36Sopenharmony_ci#include "cifsglob.h"
1162306a36Sopenharmony_ci#include "cifs_debug.h"
1262306a36Sopenharmony_ci#include "cifs_fs_sb.h"
1362306a36Sopenharmony_ci#include "cifsproto.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void cifs_fscache_fill_volume_coherency(
1662306a36Sopenharmony_ci	struct cifs_tcon *tcon,
1762306a36Sopenharmony_ci	struct cifs_fscache_volume_coherency_data *cd)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	memset(cd, 0, sizeof(*cd));
2062306a36Sopenharmony_ci	cd->resource_id		= cpu_to_le64(tcon->resource_id);
2162306a36Sopenharmony_ci	cd->vol_create_time	= tcon->vol_create_time;
2262306a36Sopenharmony_ci	cd->vol_serial_number	= cpu_to_le32(tcon->vol_serial_number);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct cifs_fscache_volume_coherency_data cd;
2862306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
2962306a36Sopenharmony_ci	struct fscache_volume *vcookie;
3062306a36Sopenharmony_ci	const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
3162306a36Sopenharmony_ci	size_t slen, i;
3262306a36Sopenharmony_ci	char *sharename;
3362306a36Sopenharmony_ci	char *key;
3462306a36Sopenharmony_ci	int ret = -ENOMEM;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	tcon->fscache = NULL;
3762306a36Sopenharmony_ci	switch (sa->sa_family) {
3862306a36Sopenharmony_ci	case AF_INET:
3962306a36Sopenharmony_ci	case AF_INET6:
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	default:
4262306a36Sopenharmony_ci		cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
4362306a36Sopenharmony_ci		return -EINVAL;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	memset(&key, 0, sizeof(key));
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	sharename = extract_sharename(tcon->tree_name);
4962306a36Sopenharmony_ci	if (IS_ERR(sharename)) {
5062306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
5162306a36Sopenharmony_ci		return PTR_ERR(sharename);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	slen = strlen(sharename);
5562306a36Sopenharmony_ci	for (i = 0; i < slen; i++)
5662306a36Sopenharmony_ci		if (sharename[i] == '/')
5762306a36Sopenharmony_ci			sharename[i] = ';';
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename);
6062306a36Sopenharmony_ci	if (!key)
6162306a36Sopenharmony_ci		goto out;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	cifs_fscache_fill_volume_coherency(tcon, &cd);
6462306a36Sopenharmony_ci	vcookie = fscache_acquire_volume(key,
6562306a36Sopenharmony_ci					 NULL, /* preferred_cache */
6662306a36Sopenharmony_ci					 &cd, sizeof(cd));
6762306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie);
6862306a36Sopenharmony_ci	if (IS_ERR(vcookie)) {
6962306a36Sopenharmony_ci		if (vcookie != ERR_PTR(-EBUSY)) {
7062306a36Sopenharmony_ci			ret = PTR_ERR(vcookie);
7162306a36Sopenharmony_ci			goto out_2;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci		pr_err("Cache volume key already in use (%s)\n", key);
7462306a36Sopenharmony_ci		vcookie = NULL;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	tcon->fscache = vcookie;
7862306a36Sopenharmony_ci	ret = 0;
7962306a36Sopenharmony_ciout_2:
8062306a36Sopenharmony_ci	kfree(key);
8162306a36Sopenharmony_ciout:
8262306a36Sopenharmony_ci	kfree(sharename);
8362306a36Sopenharmony_ci	return ret;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_civoid cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct cifs_fscache_volume_coherency_data cd;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	cifs_fscache_fill_volume_coherency(tcon, &cd);
9362306a36Sopenharmony_ci	fscache_relinquish_volume(tcon->fscache, &cd, false);
9462306a36Sopenharmony_ci	tcon->fscache = NULL;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid cifs_fscache_get_inode_cookie(struct inode *inode)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct cifs_fscache_inode_coherency_data cd;
10062306a36Sopenharmony_ci	struct cifsInodeInfo *cifsi = CIFS_I(inode);
10162306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
10262306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	cifsi->netfs.cache =
10762306a36Sopenharmony_ci		fscache_acquire_cookie(tcon->fscache, 0,
10862306a36Sopenharmony_ci				       &cifsi->uniqueid, sizeof(cifsi->uniqueid),
10962306a36Sopenharmony_ci				       &cd, sizeof(cd),
11062306a36Sopenharmony_ci				       i_size_read(&cifsi->netfs.inode));
11162306a36Sopenharmony_ci	if (cifsi->netfs.cache)
11262306a36Sopenharmony_ci		mapping_set_release_always(inode->i_mapping);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	if (update) {
11862306a36Sopenharmony_ci		struct cifs_fscache_inode_coherency_data cd;
11962306a36Sopenharmony_ci		loff_t i_size = i_size_read(inode);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		cifs_fscache_fill_coherency(inode, &cd);
12262306a36Sopenharmony_ci		fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size);
12362306a36Sopenharmony_ci	} else {
12462306a36Sopenharmony_ci		fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL);
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_civoid cifs_fscache_release_inode_cookie(struct inode *inode)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct cifsInodeInfo *cifsi = CIFS_I(inode);
13162306a36Sopenharmony_ci	struct fscache_cookie *cookie = cifs_inode_cookie(inode);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (cookie) {
13462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie);
13562306a36Sopenharmony_ci		fscache_relinquish_cookie(cookie, false);
13662306a36Sopenharmony_ci		cifsi->netfs.cache = NULL;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * Fallback page reading interface.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistatic int fscache_fallback_read_page(struct inode *inode, struct page *page)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct netfs_cache_resources cres;
14662306a36Sopenharmony_ci	struct fscache_cookie *cookie = cifs_inode_cookie(inode);
14762306a36Sopenharmony_ci	struct iov_iter iter;
14862306a36Sopenharmony_ci	struct bio_vec bvec;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	memset(&cres, 0, sizeof(cres));
15262306a36Sopenharmony_ci	bvec_set_page(&bvec, page, PAGE_SIZE, 0);
15362306a36Sopenharmony_ci	iov_iter_bvec(&iter, ITER_DEST, &bvec, 1, PAGE_SIZE);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = fscache_begin_read_operation(&cres, cookie);
15662306a36Sopenharmony_ci	if (ret < 0)
15762306a36Sopenharmony_ci		return ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL,
16062306a36Sopenharmony_ci			   NULL, NULL);
16162306a36Sopenharmony_ci	fscache_end_operation(&cres);
16262306a36Sopenharmony_ci	return ret;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci * Fallback page writing interface.
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_cistatic int fscache_fallback_write_pages(struct inode *inode, loff_t start, size_t len,
16962306a36Sopenharmony_ci					bool no_space_allocated_yet)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct netfs_cache_resources cres;
17262306a36Sopenharmony_ci	struct fscache_cookie *cookie = cifs_inode_cookie(inode);
17362306a36Sopenharmony_ci	struct iov_iter iter;
17462306a36Sopenharmony_ci	int ret;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	memset(&cres, 0, sizeof(cres));
17762306a36Sopenharmony_ci	iov_iter_xarray(&iter, ITER_SOURCE, &inode->i_mapping->i_pages, start, len);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = fscache_begin_write_operation(&cres, cookie);
18062306a36Sopenharmony_ci	if (ret < 0)
18162306a36Sopenharmony_ci		return ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode),
18462306a36Sopenharmony_ci				      no_space_allocated_yet);
18562306a36Sopenharmony_ci	if (ret == 0)
18662306a36Sopenharmony_ci		ret = fscache_write(&cres, start, &iter, NULL, NULL);
18762306a36Sopenharmony_ci	fscache_end_operation(&cres);
18862306a36Sopenharmony_ci	return ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/*
19262306a36Sopenharmony_ci * Retrieve a page from FS-Cache
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_ciint __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	int ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n",
19962306a36Sopenharmony_ci		 __func__, cifs_inode_cookie(inode), page, inode);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ret = fscache_fallback_read_page(inode, page);
20262306a36Sopenharmony_ci	if (ret < 0)
20362306a36Sopenharmony_ci		return ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Read completed synchronously */
20662306a36Sopenharmony_ci	SetPageUptodate(page);
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_civoid __cifs_readahead_to_fscache(struct inode *inode, loff_t pos, size_t len)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: (fsc: %p, p: %llx, l: %zx, i: %p)\n",
21362306a36Sopenharmony_ci		 __func__, cifs_inode_cookie(inode), pos, len, inode);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	fscache_fallback_write_pages(inode, pos, len, true);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/*
21962306a36Sopenharmony_ci * Query the cache occupancy.
22062306a36Sopenharmony_ci */
22162306a36Sopenharmony_ciint __cifs_fscache_query_occupancy(struct inode *inode,
22262306a36Sopenharmony_ci				   pgoff_t first, unsigned int nr_pages,
22362306a36Sopenharmony_ci				   pgoff_t *_data_first,
22462306a36Sopenharmony_ci				   unsigned int *_data_nr_pages)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct netfs_cache_resources cres;
22762306a36Sopenharmony_ci	struct fscache_cookie *cookie = cifs_inode_cookie(inode);
22862306a36Sopenharmony_ci	loff_t start, data_start;
22962306a36Sopenharmony_ci	size_t len, data_len;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = fscache_begin_read_operation(&cres, cookie);
23362306a36Sopenharmony_ci	if (ret < 0)
23462306a36Sopenharmony_ci		return ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	start = first * PAGE_SIZE;
23762306a36Sopenharmony_ci	len = nr_pages * PAGE_SIZE;
23862306a36Sopenharmony_ci	ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE,
23962306a36Sopenharmony_ci					&data_start, &data_len);
24062306a36Sopenharmony_ci	if (ret == 0) {
24162306a36Sopenharmony_ci		*_data_first = data_start / PAGE_SIZE;
24262306a36Sopenharmony_ci		*_data_nr_pages = len / PAGE_SIZE;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	fscache_end_operation(&cres);
24662306a36Sopenharmony_ci	return ret;
24762306a36Sopenharmony_ci}
248