2019-05-28 19:57:16 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-08-22 01:29:54 +04:00
/*
* Ceph cache definitions .
*
* Copyright ( C ) 2013 by Adfin Solutions , Inc . All Rights Reserved .
* Written by Milosz Tanski ( milosz @ adfin . com )
*/
# include "super.h"
# include "cache.h"
struct ceph_aux_inode {
2018-07-13 23:18:36 +03:00
u64 version ;
u64 mtime_sec ;
u64 mtime_nsec ;
2013-08-22 01:29:54 +04:00
} ;
struct fscache_netfs ceph_cache_netfs = {
. name = " ceph " ,
. version = 0 ,
} ;
2017-06-27 06:57:56 +03:00
static DEFINE_MUTEX ( ceph_fscache_lock ) ;
static LIST_HEAD ( ceph_fscache_list ) ;
struct ceph_fscache_entry {
struct list_head list ;
struct fscache_cookie * fscache ;
size_t uniq_len ;
2018-04-04 15:41:28 +03:00
/* The following members must be last */
struct ceph_fsid fsid ;
2017-06-27 06:57:56 +03:00
char uniquifier [ 0 ] ;
} ;
2013-08-22 01:29:54 +04:00
static const struct fscache_cookie_def ceph_fscache_fsid_object_def = {
. name = " CEPH.fsid " ,
. type = FSCACHE_COOKIE_TYPE_INDEX ,
} ;
2018-03-10 15:32:05 +03:00
int __init ceph_fscache_register ( void )
2013-08-22 01:29:54 +04:00
{
return fscache_register_netfs ( & ceph_cache_netfs ) ;
}
2013-09-06 19:13:18 +04:00
void ceph_fscache_unregister ( void )
2013-08-22 01:29:54 +04:00
{
fscache_unregister_netfs ( & ceph_cache_netfs ) ;
}
int ceph_fscache_register_fs ( struct ceph_fs_client * fsc )
{
2017-06-27 06:57:56 +03:00
const struct ceph_fsid * fsid = & fsc - > client - > fsid ;
const char * fscache_uniq = fsc - > mount_options - > fscache_uniq ;
size_t uniq_len = fscache_uniq ? strlen ( fscache_uniq ) : 0 ;
struct ceph_fscache_entry * ent ;
int err = 0 ;
mutex_lock ( & ceph_fscache_lock ) ;
list_for_each_entry ( ent , & ceph_fscache_list , list ) {
if ( memcmp ( & ent - > fsid , fsid , sizeof ( * fsid ) ) )
continue ;
if ( ent - > uniq_len ! = uniq_len )
continue ;
if ( uniq_len & & memcmp ( ent - > uniquifier , fscache_uniq , uniq_len ) )
continue ;
pr_err ( " fscache cookie already registered for fsid %pU \n " , fsid ) ;
pr_err ( " use fsc=%%s mount option to specify a uniquifier \n " ) ;
err = - EBUSY ;
goto out_unlock ;
}
ent = kzalloc ( sizeof ( * ent ) + uniq_len , GFP_KERNEL ) ;
if ( ! ent ) {
err = - ENOMEM ;
goto out_unlock ;
}
2018-04-04 15:41:28 +03:00
memcpy ( & ent - > fsid , fsid , sizeof ( * fsid ) ) ;
if ( uniq_len > 0 ) {
memcpy ( & ent - > uniquifier , fscache_uniq , uniq_len ) ;
ent - > uniq_len = uniq_len ;
}
2013-08-22 01:29:54 +04:00
fsc - > fscache = fscache_acquire_cookie ( ceph_cache_netfs . primary_index ,
& ceph_fscache_fsid_object_def ,
2018-04-04 15:41:28 +03:00
& ent - > fsid , sizeof ( ent - > fsid ) + uniq_len ,
NULL , 0 ,
2018-04-04 15:41:28 +03:00
fsc , 0 , true ) ;
2013-08-22 01:29:54 +04:00
2017-06-27 06:57:56 +03:00
if ( fsc - > fscache ) {
ent - > fscache = fsc - > fscache ;
list_add_tail ( & ent - > list , & ceph_fscache_list ) ;
} else {
kfree ( ent ) ;
pr_err ( " unable to register fscache cookie for fsid %pU \n " ,
fsid ) ;
/* all other fs ignore this error */
}
out_unlock :
mutex_unlock ( & ceph_fscache_lock ) ;
return err ;
2013-08-22 01:29:54 +04:00
}
static enum fscache_checkaux ceph_fscache_inode_check_aux (
2018-04-04 15:41:28 +03:00
void * cookie_netfs_data , const void * data , uint16_t dlen ,
loff_t object_size )
2013-08-22 01:29:54 +04:00
{
struct ceph_aux_inode aux ;
struct ceph_inode_info * ci = cookie_netfs_data ;
struct inode * inode = & ci - > vfs_inode ;
2018-04-04 15:41:28 +03:00
if ( dlen ! = sizeof ( aux ) | |
i_size_read ( inode ) ! = object_size )
2013-08-22 01:29:54 +04:00
return FSCACHE_CHECKAUX_OBSOLETE ;
memset ( & aux , 0 , sizeof ( aux ) ) ;
2016-05-20 11:57:29 +03:00
aux . version = ci - > i_version ;
2018-07-13 23:18:36 +03:00
aux . mtime_sec = inode - > i_mtime . tv_sec ;
aux . mtime_nsec = inode - > i_mtime . tv_nsec ;
2013-08-22 01:29:54 +04:00
if ( memcmp ( data , & aux , sizeof ( aux ) ) ! = 0 )
return FSCACHE_CHECKAUX_OBSOLETE ;
2018-01-30 11:29:17 +03:00
dout ( " ceph inode 0x%p cached okay \n " , ci ) ;
2013-08-22 01:29:54 +04:00
return FSCACHE_CHECKAUX_OKAY ;
}
static const struct fscache_cookie_def ceph_fscache_inode_object_def = {
. name = " CEPH.inode " ,
. type = FSCACHE_COOKIE_TYPE_DATAFILE ,
. check_aux = ceph_fscache_inode_check_aux ,
} ;
2016-05-18 10:25:03 +03:00
void ceph_fscache_register_inode_cookie ( struct inode * inode )
2013-08-22 01:29:54 +04:00
{
2016-05-18 10:25:03 +03:00
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
struct ceph_fs_client * fsc = ceph_inode_to_client ( inode ) ;
2018-04-04 15:41:28 +03:00
struct ceph_aux_inode aux ;
2013-08-22 01:29:54 +04:00
/* No caching for filesystem */
2017-08-20 21:22:02 +03:00
if ( ! fsc - > fscache )
2013-08-22 01:29:54 +04:00
return ;
/* Only cache for regular files that are read only */
2016-05-18 10:25:03 +03:00
if ( ! S_ISREG ( inode - > i_mode ) )
2013-08-22 01:29:54 +04:00
return ;
2016-05-18 10:25:03 +03:00
inode_lock_nested ( inode , I_MUTEX_CHILD ) ;
if ( ! ci - > fscache ) {
2018-04-04 15:41:28 +03:00
memset ( & aux , 0 , sizeof ( aux ) ) ;
aux . version = ci - > i_version ;
2018-07-13 23:18:36 +03:00
aux . mtime_sec = inode - > i_mtime . tv_sec ;
aux . mtime_nsec = inode - > i_mtime . tv_nsec ;
2016-05-18 10:25:03 +03:00
ci - > fscache = fscache_acquire_cookie ( fsc - > fscache ,
2018-04-04 15:41:28 +03:00
& ceph_fscache_inode_object_def ,
& ci - > i_vino , sizeof ( ci - > i_vino ) ,
& aux , sizeof ( aux ) ,
2018-04-04 15:41:28 +03:00
ci , i_size_read ( inode ) , false ) ;
2016-05-18 10:25:03 +03:00
}
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2013-08-22 01:29:54 +04:00
}
void ceph_fscache_unregister_inode_cookie ( struct ceph_inode_info * ci )
{
struct fscache_cookie * cookie ;
if ( ( cookie = ci - > fscache ) = = NULL )
return ;
ci - > fscache = NULL ;
fscache_uncache_all_inode_pages ( cookie , & ci - > vfs_inode ) ;
2018-04-04 15:41:28 +03:00
fscache_relinquish_cookie ( cookie , & ci - > i_vino , false ) ;
2013-08-22 01:29:54 +04:00
}
2016-05-18 10:25:03 +03:00
static bool ceph_fscache_can_enable ( void * data )
{
struct inode * inode = data ;
return ! inode_is_open_for_write ( inode ) ;
}
void ceph_fscache_file_set_cookie ( struct inode * inode , struct file * filp )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
if ( ! fscache_cookie_valid ( ci - > fscache ) )
return ;
if ( inode_is_open_for_write ( inode ) ) {
dout ( " fscache_file_set_cookie %p %p disabling cache \n " ,
inode , filp ) ;
2018-04-04 15:41:28 +03:00
fscache_disable_cookie ( ci - > fscache , & ci - > i_vino , false ) ;
2016-05-18 10:25:03 +03:00
fscache_uncache_all_inode_pages ( ci - > fscache , inode ) ;
} else {
2018-04-04 15:41:28 +03:00
fscache_enable_cookie ( ci - > fscache , & ci - > i_vino , i_size_read ( inode ) ,
2018-04-04 15:41:28 +03:00
ceph_fscache_can_enable , inode ) ;
2016-05-18 10:25:03 +03:00
if ( fscache_cookie_enabled ( ci - > fscache ) ) {
2016-12-29 23:19:32 +03:00
dout ( " fscache_file_set_cookie %p %p enabling cache \n " ,
2016-05-18 10:25:03 +03:00
inode , filp ) ;
}
}
}
2017-08-04 06:22:31 +03:00
static void ceph_readpage_from_fscache_complete ( struct page * page , void * data , int error )
2013-08-22 01:29:54 +04:00
{
if ( ! error )
SetPageUptodate ( page ) ;
unlock_page ( page ) ;
}
2016-03-25 12:18:39 +03:00
static inline bool cache_valid ( struct ceph_inode_info * ci )
2013-08-22 01:29:54 +04:00
{
2016-05-18 15:31:55 +03:00
return ci - > i_fscache_gen = = ci - > i_rdcache_gen ;
2013-08-22 01:29:54 +04:00
}
/* Atempt to read from the fscache,
*
* This function is called from the readpage_nounlock context . DO NOT attempt to
* unlock the page here ( or in the callback ) .
*/
int ceph_readpage_from_fscache ( struct inode * inode , struct page * page )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
int ret ;
if ( ! cache_valid ( ci ) )
return - ENOBUFS ;
ret = fscache_read_or_alloc_page ( ci - > fscache , page ,
2017-08-04 06:22:31 +03:00
ceph_readpage_from_fscache_complete , NULL ,
2013-08-22 01:29:54 +04:00
GFP_KERNEL ) ;
switch ( ret ) {
case 0 : /* Page found */
dout ( " page read submitted \n " ) ;
return 0 ;
case - ENOBUFS : /* Pages were not found, and can't be */
case - ENODATA : /* Pages were not found */
dout ( " page/inode not in cache \n " ) ;
return ret ;
default :
dout ( " %s: unknown error ret = %i \n " , __func__ , ret ) ;
return ret ;
}
}
int ceph_readpages_from_fscache ( struct inode * inode ,
struct address_space * mapping ,
struct list_head * pages ,
unsigned * nr_pages )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
int ret ;
if ( ! cache_valid ( ci ) )
return - ENOBUFS ;
ret = fscache_read_or_alloc_pages ( ci - > fscache , mapping , pages , nr_pages ,
2017-08-04 06:22:31 +03:00
ceph_readpage_from_fscache_complete ,
2013-08-22 01:29:54 +04:00
NULL , mapping_gfp_mask ( mapping ) ) ;
switch ( ret ) {
case 0 : /* All pages found */
dout ( " all-page read submitted \n " ) ;
return 0 ;
case - ENOBUFS : /* Some pages were not found, and can't be */
case - ENODATA : /* some pages were not found */
dout ( " page/inode not in cache \n " ) ;
return ret ;
default :
dout ( " %s: unknown error ret = %i \n " , __func__ , ret ) ;
return ret ;
}
}
void ceph_readpage_to_fscache ( struct inode * inode , struct page * page )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
int ret ;
2013-09-04 03:11:01 +04:00
if ( ! PageFsCache ( page ) )
return ;
2013-08-22 01:29:54 +04:00
if ( ! cache_valid ( ci ) )
return ;
2018-04-04 15:41:28 +03:00
ret = fscache_write_page ( ci - > fscache , page , i_size_read ( inode ) ,
GFP_KERNEL ) ;
2013-08-22 01:29:54 +04:00
if ( ret )
fscache_uncache_page ( ci - > fscache , page ) ;
}
void ceph_invalidate_fscache_page ( struct inode * inode , struct page * page )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
2013-09-25 19:18:14 +04:00
if ( ! PageFsCache ( page ) )
return ;
2013-08-22 01:29:54 +04:00
fscache_wait_on_page_write ( ci - > fscache , page ) ;
fscache_uncache_page ( ci - > fscache , page ) ;
}
void ceph_fscache_unregister_fs ( struct ceph_fs_client * fsc )
{
2017-06-27 06:57:56 +03:00
if ( fscache_cookie_valid ( fsc - > fscache ) ) {
struct ceph_fscache_entry * ent ;
bool found = false ;
mutex_lock ( & ceph_fscache_lock ) ;
list_for_each_entry ( ent , & ceph_fscache_list , list ) {
if ( ent - > fscache = = fsc - > fscache ) {
list_del ( & ent - > list ) ;
kfree ( ent ) ;
found = true ;
break ;
}
}
WARN_ON_ONCE ( ! found ) ;
mutex_unlock ( & ceph_fscache_lock ) ;
2018-04-04 15:41:28 +03:00
__fscache_relinquish_cookie ( fsc - > fscache , NULL , false ) ;
2017-06-27 06:57:56 +03:00
}
2013-08-22 01:29:54 +04:00
fsc - > fscache = NULL ;
}
2016-05-18 15:31:55 +03:00
/*
* caller should hold CEPH_CAP_FILE_ { RD , CACHE }
*/
void ceph_fscache_revalidate_cookie ( struct ceph_inode_info * ci )
2013-08-22 01:29:54 +04:00
{
2016-05-18 15:31:55 +03:00
if ( cache_valid ( ci ) )
2013-09-05 22:29:03 +04:00
return ;
2016-05-18 15:31:55 +03:00
/* resue i_truncate_mutex. There should be no pending
* truncate while the caller holds CEPH_CAP_FILE_RD */
mutex_lock ( & ci - > i_truncate_mutex ) ;
if ( ! cache_valid ( ci ) ) {
2018-04-04 15:41:28 +03:00
if ( fscache_check_consistency ( ci - > fscache , & ci - > i_vino ) )
2016-05-18 15:31:55 +03:00
fscache_invalidate ( ci - > fscache ) ;
spin_lock ( & ci - > i_ceph_lock ) ;
ci - > i_fscache_gen = ci - > i_rdcache_gen ;
spin_unlock ( & ci - > i_ceph_lock ) ;
2013-08-22 01:29:54 +04:00
}
2016-05-18 15:31:55 +03:00
mutex_unlock ( & ci - > i_truncate_mutex ) ;
2013-08-22 01:29:54 +04:00
}