2010-07-05 16:42:15 +04:00
/*
* fs / cifs / fscache . c - CIFS filesystem cache interface
*
* Copyright ( c ) 2010 Novell , Inc .
2010-11-24 15:19:06 +03:00
* Author ( s ) : Suresh Jayaraman < sjayaraman @ suse . de >
2010-07-05 16:42:15 +04:00
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "fscache.h"
# include "cifsglob.h"
# include "cifs_debug.h"
2010-07-05 16:42:45 +04:00
# include "cifs_fs_sb.h"
2010-07-05 16:42:15 +04:00
2018-04-04 15:41:28 +03:00
/*
* Key layout of CIFS server cache index object
*/
struct cifs_server_key {
struct {
uint16_t family ; /* address family */
__be16 port ; /* IP port */
} hdr ;
union {
struct in_addr ipv4_addr ;
struct in6_addr ipv6_addr ;
} ;
} __packed ;
/*
* Get a cookie for a server object keyed by { IPaddress , port , family } tuple
*/
2010-07-05 16:42:15 +04:00
void cifs_fscache_get_client_cookie ( struct TCP_Server_Info * server )
{
2018-04-04 15:41:28 +03:00
const struct sockaddr * sa = ( struct sockaddr * ) & server - > dstaddr ;
const struct sockaddr_in * addr = ( struct sockaddr_in * ) sa ;
const struct sockaddr_in6 * addr6 = ( struct sockaddr_in6 * ) sa ;
struct cifs_server_key key ;
uint16_t key_len = sizeof ( key . hdr ) ;
memset ( & key , 0 , sizeof ( key ) ) ;
/*
* Should not be a problem as sin_family / sin6_family overlays
* sa_family field
*/
key . hdr . family = sa - > sa_family ;
switch ( sa - > sa_family ) {
case AF_INET :
key . hdr . port = addr - > sin_port ;
key . ipv4_addr = addr - > sin_addr ;
key_len + = sizeof ( key . ipv4_addr ) ;
break ;
case AF_INET6 :
key . hdr . port = addr6 - > sin6_port ;
key . ipv6_addr = addr6 - > sin6_addr ;
key_len + = sizeof ( key . ipv6_addr ) ;
break ;
default :
cifs_dbg ( VFS , " Unknown network family '%d' \n " , sa - > sa_family ) ;
server - > fscache = NULL ;
return ;
}
2010-07-05 16:42:15 +04:00
server - > fscache =
fscache_acquire_cookie ( cifs_fscache_netfs . primary_index ,
2018-04-04 15:41:28 +03:00
& cifs_fscache_server_index_def ,
& key , key_len ,
NULL , 0 ,
2018-04-04 15:41:28 +03:00
server , 0 , true ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/0x%p) \n " ,
__func__ , server , server - > fscache ) ;
2010-07-05 16:42:15 +04:00
}
void cifs_fscache_release_client_cookie ( struct TCP_Server_Info * server )
{
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/0x%p) \n " ,
__func__ , server , server - > fscache ) ;
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( server - > fscache , NULL , false ) ;
2010-07-05 16:42:15 +04:00
server - > fscache = NULL ;
}
2011-05-27 08:34:02 +04:00
void cifs_fscache_get_super_cookie ( struct cifs_tcon * tcon )
2010-07-05 16:42:27 +04:00
{
struct TCP_Server_Info * server = tcon - > ses - > server ;
2018-04-04 15:41:28 +03:00
char * sharename ;
sharename = extract_sharename ( tcon - > treeName ) ;
if ( IS_ERR ( sharename ) ) {
cifs_dbg ( FYI , " %s: couldn't extract sharename \n " , __func__ ) ;
tcon - > fscache = NULL ;
return ;
}
2010-07-05 16:42:27 +04:00
tcon - > fscache =
fscache_acquire_cookie ( server - > fscache ,
2018-04-04 15:41:28 +03:00
& cifs_fscache_super_index_def ,
sharename , strlen ( sharename ) ,
& tcon - > resource_id , sizeof ( tcon - > resource_id ) ,
2018-04-04 15:41:28 +03:00
tcon , 0 , true ) ;
2018-04-04 15:41:28 +03:00
kfree ( sharename ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/0x%p) \n " ,
__func__ , server - > fscache , tcon - > fscache ) ;
2010-07-05 16:42:27 +04:00
}
2011-05-27 08:34:02 +04:00
void cifs_fscache_release_super_cookie ( struct cifs_tcon * tcon )
2010-07-05 16:42:27 +04:00
{
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p) \n " , __func__ , tcon - > fscache ) ;
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( tcon - > fscache , & tcon - > resource_id , false ) ;
2010-07-05 16:42:27 +04:00
tcon - > fscache = NULL ;
}
2010-07-05 16:42:45 +04:00
2018-04-04 15:41:28 +03:00
static void cifs_fscache_acquire_inode_cookie ( struct cifsInodeInfo * cifsi ,
struct cifs_tcon * tcon )
{
struct cifs_fscache_inode_auxdata auxdata ;
memset ( & auxdata , 0 , sizeof ( auxdata ) ) ;
auxdata . eof = cifsi - > server_eof ;
2018-06-19 18:27:59 +03:00
auxdata . last_write_time_sec = cifsi - > vfs_inode . i_mtime . tv_sec ;
auxdata . last_change_time_sec = cifsi - > vfs_inode . i_ctime . tv_sec ;
auxdata . last_write_time_nsec = cifsi - > vfs_inode . i_mtime . tv_nsec ;
auxdata . last_change_time_nsec = cifsi - > vfs_inode . i_ctime . tv_nsec ;
2018-04-04 15:41:28 +03:00
cifsi - > fscache =
fscache_acquire_cookie ( tcon - > fscache ,
& cifs_fscache_inode_object_def ,
& cifsi - > uniqueid , sizeof ( cifsi - > uniqueid ) ,
& auxdata , sizeof ( auxdata ) ,
2018-04-04 15:41:28 +03:00
cifsi , cifsi - > vfs_inode . i_size , true ) ;
2018-04-04 15:41:28 +03:00
}
2010-07-05 16:42:45 +04:00
static void cifs_fscache_enable_inode_cookie ( struct inode * inode )
{
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
2011-05-27 08:34:02 +04:00
struct cifs_tcon * tcon = cifs_sb_master_tcon ( cifs_sb ) ;
2010-07-05 16:42:45 +04:00
if ( cifsi - > fscache )
return ;
2018-04-04 15:41:28 +03:00
if ( ! ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_FSCACHE ) )
return ;
cifs_fscache_acquire_inode_cookie ( cifsi , tcon ) ;
cifs_dbg ( FYI , " %s: got FH cookie (0x%p/0x%p) \n " ,
__func__ , tcon - > fscache , cifsi - > fscache ) ;
2010-07-05 16:42:45 +04:00
}
void cifs_fscache_release_inode_cookie ( struct inode * inode )
{
2018-04-04 15:41:28 +03:00
struct cifs_fscache_inode_auxdata auxdata ;
2010-07-05 16:42:45 +04:00
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
if ( cifsi - > fscache ) {
2018-04-04 15:41:28 +03:00
memset ( & auxdata , 0 , sizeof ( auxdata ) ) ;
auxdata . eof = cifsi - > server_eof ;
2018-06-19 18:27:59 +03:00
auxdata . last_write_time_sec = cifsi - > vfs_inode . i_mtime . tv_sec ;
auxdata . last_change_time_sec = cifsi - > vfs_inode . i_ctime . tv_sec ;
auxdata . last_write_time_nsec = cifsi - > vfs_inode . i_mtime . tv_nsec ;
auxdata . last_change_time_nsec = cifsi - > vfs_inode . i_ctime . tv_nsec ;
2018-04-04 15:41:28 +03:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p) \n " , __func__ , cifsi - > fscache ) ;
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( cifsi - > fscache , & auxdata , false ) ;
2010-07-05 16:42:45 +04:00
cifsi - > fscache = NULL ;
}
}
static void cifs_fscache_disable_inode_cookie ( struct inode * inode )
{
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
if ( cifsi - > fscache ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p) \n " , __func__ , cifsi - > fscache ) ;
2011-07-07 15:19:48 +04:00
fscache_uncache_all_inode_pages ( cifsi - > fscache , inode ) ;
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( cifsi - > fscache , NULL , true ) ;
2010-07-05 16:42:45 +04:00
cifsi - > fscache = NULL ;
}
}
void cifs_fscache_set_inode_cookie ( struct inode * inode , struct file * filp )
{
if ( ( filp - > f_flags & O_ACCMODE ) ! = O_RDONLY )
cifs_fscache_disable_inode_cookie ( inode ) ;
2010-11-24 15:19:06 +03:00
else
2010-07-05 16:42:45 +04:00
cifs_fscache_enable_inode_cookie ( inode ) ;
}
void cifs_fscache_reset_inode_cookie ( struct inode * inode )
{
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
2018-04-04 15:41:28 +03:00
struct cifs_tcon * tcon = cifs_sb_master_tcon ( cifs_sb ) ;
2010-07-05 16:42:45 +04:00
struct fscache_cookie * old = cifsi - > fscache ;
if ( cifsi - > fscache ) {
/* retire the current fscache cache and get a new one */
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( cifsi - > fscache , NULL , true ) ;
2010-07-05 16:42:45 +04:00
2018-04-04 15:41:28 +03:00
cifs_fscache_acquire_inode_cookie ( cifsi , tcon ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: new cookie 0x%p oldcookie 0x%p \n " ,
__func__ , cifsi - > fscache , old ) ;
2010-07-05 16:42:45 +04:00
}
}
2010-07-05 16:43:00 +04:00
int cifs_fscache_release_page ( struct page * page , gfp_t gfp )
{
if ( PageFsCache ( page ) ) {
struct inode * inode = page - > mapping - > host ;
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/0x%p) \n " ,
__func__ , page , cifsi - > fscache ) ;
2010-07-05 16:43:00 +04:00
if ( ! fscache_maybe_release_page ( cifsi - > fscache , page , gfp ) )
return 0 ;
}
return 1 ;
}
2010-07-05 16:43:25 +04:00
static void cifs_readpage_from_fscache_complete ( struct page * page , void * ctx ,
int error )
{
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/%d) \n " , __func__ , page , error ) ;
2010-07-05 16:43:25 +04:00
if ( ! error )
SetPageUptodate ( page ) ;
unlock_page ( page ) ;
}
/*
* Retrieve a page from FS - Cache
*/
int __cifs_readpage_from_fscache ( struct inode * inode , struct page * page )
{
int ret ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (fsc:%p, p:%p, i:0x%p \n " ,
__func__ , CIFS_I ( inode ) - > fscache , page , inode ) ;
2010-07-05 16:43:25 +04:00
ret = fscache_read_or_alloc_page ( CIFS_I ( inode ) - > fscache , page ,
cifs_readpage_from_fscache_complete ,
NULL ,
GFP_KERNEL ) ;
switch ( ret ) {
case 0 : /* page found in fscache, read submitted */
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: submitted \n " , __func__ ) ;
2010-07-05 16:43:25 +04:00
return ret ;
case - ENOBUFS : /* page won't be cached */
case - ENODATA : /* page not in cache */
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: %d \n " , __func__ , ret ) ;
2010-07-05 16:43:25 +04:00
return 1 ;
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " unknown error ret = %d \n " , ret ) ;
2010-07-05 16:43:25 +04:00
}
return ret ;
}
/*
* Retrieve a set of pages from FS - Cache
*/
int __cifs_readpages_from_fscache ( struct inode * inode ,
struct address_space * mapping ,
struct list_head * pages ,
unsigned * nr_pages )
{
int ret ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/%u/0x%p) \n " ,
__func__ , CIFS_I ( inode ) - > fscache , * nr_pages , inode ) ;
2010-07-05 16:43:25 +04:00
ret = fscache_read_or_alloc_pages ( CIFS_I ( inode ) - > fscache , mapping ,
pages , nr_pages ,
cifs_readpage_from_fscache_complete ,
NULL ,
mapping_gfp_mask ( mapping ) ) ;
switch ( ret ) {
case 0 : /* read submitted to the cache for all pages */
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: submitted \n " , __func__ ) ;
2010-07-05 16:43:25 +04:00
return ret ;
case - ENOBUFS : /* some pages are not cached and can't be */
case - ENODATA : /* some pages are not cached */
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: no page \n " , __func__ ) ;
2010-07-05 16:43:25 +04:00
return 1 ;
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " unknown error ret = %d \n " , ret ) ;
2010-07-05 16:43:25 +04:00
}
return ret ;
}
2010-07-05 16:43:11 +04:00
void __cifs_readpage_to_fscache ( struct inode * inode , struct page * page )
{
2018-04-04 15:41:28 +03:00
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
2010-07-05 16:43:11 +04:00
int ret ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (fsc: %p, p: %p, i: %p) \n " ,
2018-04-04 15:41:28 +03:00
__func__ , cifsi - > fscache , page , inode ) ;
ret = fscache_write_page ( cifsi - > fscache , page ,
cifsi - > vfs_inode . i_size , GFP_KERNEL ) ;
2010-07-05 16:43:11 +04:00
if ( ret ! = 0 )
2018-04-04 15:41:28 +03:00
fscache_uncache_page ( cifsi - > fscache , page ) ;
2010-07-05 16:43:11 +04:00
}
2013-09-04 21:10:39 +04:00
void __cifs_fscache_readpages_cancel ( struct inode * inode , struct list_head * pages )
{
cifs_dbg ( FYI , " %s: (fsc: %p, i: %p) \n " ,
__func__ , CIFS_I ( inode ) - > fscache , inode ) ;
fscache_readpages_cancel ( CIFS_I ( inode ) - > fscache , pages ) ;
}
2010-07-05 16:43:00 +04:00
void __cifs_fscache_invalidate_page ( struct page * page , struct inode * inode )
{
struct cifsInodeInfo * cifsi = CIFS_I ( inode ) ;
struct fscache_cookie * cookie = cifsi - > fscache ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: (0x%p/0x%p) \n " , __func__ , page , cookie ) ;
2010-07-05 16:43:00 +04:00
fscache_wait_on_page_write ( cookie , page ) ;
fscache_uncache_page ( cookie , page ) ;
}