forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
6.2 KiB
253 lines
6.2 KiB
// SPDX-License-Identifier: LGPL-2.1 |
|
/* |
|
* CIFS filesystem cache interface |
|
* |
|
* Copyright (c) 2010 Novell, Inc. |
|
* Author(s): Suresh Jayaraman <[email protected]> |
|
* |
|
*/ |
|
#include "fscache.h" |
|
#include "cifsglob.h" |
|
#include "cifs_debug.h" |
|
#include "cifs_fs_sb.h" |
|
#include "cifsproto.h" |
|
|
|
static void cifs_fscache_fill_volume_coherency( |
|
struct cifs_tcon *tcon, |
|
struct cifs_fscache_volume_coherency_data *cd) |
|
{ |
|
memset(cd, 0, sizeof(*cd)); |
|
cd->resource_id = cpu_to_le64(tcon->resource_id); |
|
cd->vol_create_time = tcon->vol_create_time; |
|
cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); |
|
} |
|
|
|
int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) |
|
{ |
|
struct cifs_fscache_volume_coherency_data cd; |
|
struct TCP_Server_Info *server = tcon->ses->server; |
|
struct fscache_volume *vcookie; |
|
const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; |
|
size_t slen, i; |
|
char *sharename; |
|
char *key; |
|
int ret = -ENOMEM; |
|
|
|
tcon->fscache = NULL; |
|
switch (sa->sa_family) { |
|
case AF_INET: |
|
case AF_INET6: |
|
break; |
|
default: |
|
cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); |
|
return -EINVAL; |
|
} |
|
|
|
memset(&key, 0, sizeof(key)); |
|
|
|
sharename = extract_sharename(tcon->treeName); |
|
if (IS_ERR(sharename)) { |
|
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); |
|
return -EINVAL; |
|
} |
|
|
|
slen = strlen(sharename); |
|
for (i = 0; i < slen; i++) |
|
if (sharename[i] == '/') |
|
sharename[i] = ';'; |
|
|
|
key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); |
|
if (!key) |
|
goto out; |
|
|
|
cifs_fscache_fill_volume_coherency(tcon, &cd); |
|
vcookie = fscache_acquire_volume(key, |
|
NULL, /* preferred_cache */ |
|
&cd, sizeof(cd)); |
|
cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); |
|
if (IS_ERR(vcookie)) { |
|
if (vcookie != ERR_PTR(-EBUSY)) { |
|
ret = PTR_ERR(vcookie); |
|
goto out_2; |
|
} |
|
pr_err("Cache volume key already in use (%s)\n", key); |
|
vcookie = NULL; |
|
} |
|
|
|
tcon->fscache = vcookie; |
|
ret = 0; |
|
out_2: |
|
kfree(key); |
|
out: |
|
kfree(sharename); |
|
return ret; |
|
} |
|
|
|
void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) |
|
{ |
|
struct cifs_fscache_volume_coherency_data cd; |
|
|
|
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); |
|
|
|
cifs_fscache_fill_volume_coherency(tcon, &cd); |
|
fscache_relinquish_volume(tcon->fscache, &cd, false); |
|
tcon->fscache = NULL; |
|
} |
|
|
|
void cifs_fscache_get_inode_cookie(struct inode *inode) |
|
{ |
|
struct cifs_fscache_inode_coherency_data cd; |
|
struct cifsInodeInfo *cifsi = CIFS_I(inode); |
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); |
|
|
|
cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); |
|
|
|
cifsi->netfs.cache = |
|
fscache_acquire_cookie(tcon->fscache, 0, |
|
&cifsi->uniqueid, sizeof(cifsi->uniqueid), |
|
&cd, sizeof(cd), |
|
i_size_read(&cifsi->netfs.inode)); |
|
} |
|
|
|
void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) |
|
{ |
|
if (update) { |
|
struct cifs_fscache_inode_coherency_data cd; |
|
loff_t i_size = i_size_read(inode); |
|
|
|
cifs_fscache_fill_coherency(inode, &cd); |
|
fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); |
|
} else { |
|
fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); |
|
} |
|
} |
|
|
|
void cifs_fscache_release_inode_cookie(struct inode *inode) |
|
{ |
|
struct cifsInodeInfo *cifsi = CIFS_I(inode); |
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode); |
|
|
|
if (cookie) { |
|
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); |
|
fscache_relinquish_cookie(cookie, false); |
|
cifsi->netfs.cache = NULL; |
|
} |
|
} |
|
|
|
/* |
|
* Fallback page reading interface. |
|
*/ |
|
static int fscache_fallback_read_page(struct inode *inode, struct page *page) |
|
{ |
|
struct netfs_cache_resources cres; |
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode); |
|
struct iov_iter iter; |
|
struct bio_vec bvec[1]; |
|
int ret; |
|
|
|
memset(&cres, 0, sizeof(cres)); |
|
bvec[0].bv_page = page; |
|
bvec[0].bv_offset = 0; |
|
bvec[0].bv_len = PAGE_SIZE; |
|
iov_iter_bvec(&iter, READ, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); |
|
|
|
ret = fscache_begin_read_operation(&cres, cookie); |
|
if (ret < 0) |
|
return ret; |
|
|
|
ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, |
|
NULL, NULL); |
|
fscache_end_operation(&cres); |
|
return ret; |
|
} |
|
|
|
/* |
|
* Fallback page writing interface. |
|
*/ |
|
static int fscache_fallback_write_page(struct inode *inode, struct page *page, |
|
bool no_space_allocated_yet) |
|
{ |
|
struct netfs_cache_resources cres; |
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode); |
|
struct iov_iter iter; |
|
struct bio_vec bvec[1]; |
|
loff_t start = page_offset(page); |
|
size_t len = PAGE_SIZE; |
|
int ret; |
|
|
|
memset(&cres, 0, sizeof(cres)); |
|
bvec[0].bv_page = page; |
|
bvec[0].bv_offset = 0; |
|
bvec[0].bv_len = PAGE_SIZE; |
|
iov_iter_bvec(&iter, WRITE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); |
|
|
|
ret = fscache_begin_write_operation(&cres, cookie); |
|
if (ret < 0) |
|
return ret; |
|
|
|
ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), |
|
no_space_allocated_yet); |
|
if (ret == 0) |
|
ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); |
|
fscache_end_operation(&cres); |
|
return ret; |
|
} |
|
|
|
/* |
|
* Retrieve a page from FS-Cache |
|
*/ |
|
int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) |
|
{ |
|
int ret; |
|
|
|
cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", |
|
__func__, cifs_inode_cookie(inode), page, inode); |
|
|
|
ret = fscache_fallback_read_page(inode, page); |
|
if (ret < 0) |
|
return ret; |
|
|
|
/* Read completed synchronously */ |
|
SetPageUptodate(page); |
|
return 0; |
|
} |
|
|
|
void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) |
|
{ |
|
cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", |
|
__func__, cifs_inode_cookie(inode), page, inode); |
|
|
|
fscache_fallback_write_page(inode, page, true); |
|
} |
|
|
|
/* |
|
* Query the cache occupancy. |
|
*/ |
|
int __cifs_fscache_query_occupancy(struct inode *inode, |
|
pgoff_t first, unsigned int nr_pages, |
|
pgoff_t *_data_first, |
|
unsigned int *_data_nr_pages) |
|
{ |
|
struct netfs_cache_resources cres; |
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode); |
|
loff_t start, data_start; |
|
size_t len, data_len; |
|
int ret; |
|
|
|
ret = fscache_begin_read_operation(&cres, cookie); |
|
if (ret < 0) |
|
return ret; |
|
|
|
start = first * PAGE_SIZE; |
|
len = nr_pages * PAGE_SIZE; |
|
ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, |
|
&data_start, &data_len); |
|
if (ret == 0) { |
|
*_data_first = data_start / PAGE_SIZE; |
|
*_data_nr_pages = len / PAGE_SIZE; |
|
} |
|
|
|
fscache_end_operation(&cres); |
|
return ret; |
|
}
|
|
|