2022-10-31 09:53:26 -04:00
// SPDX-License-Identifier: GPL-2.0
2019-08-18 14:18:48 -04:00
/*
2022-10-31 09:53:26 -04:00
* The NFSD open file cache .
2019-08-18 14:18:48 -04:00
*
* ( c ) 2015 - Jeff Layton < jeff . layton @ primarydata . com >
2022-11-01 13:30:46 -04:00
*
* An nfsd_file object is a per - file collection of open state that binds
* together :
* - a struct file *
* - a user credential
* - a network namespace
* - a read - ahead context
* - monitoring for writeback errors
*
* nfsd_file objects are reference - counted . Consumers acquire a new
* object via the nfsd_file_acquire API . They manage their interest in
* the acquired object , and hence the object ' s reference count , via
* nfsd_file_get and nfsd_file_put . There are two varieties of nfsd_file
* object :
*
* * non - garbage - collected : When a consumer wants to precisely control
* the lifetime of a file ' s open state , it acquires a non - garbage -
* collected nfsd_file . The final nfsd_file_put releases the open
* state immediately .
*
* * garbage - collected : When a consumer does not control the lifetime
* of open state , it acquires a garbage - collected nfsd_file . The
* final nfsd_file_put allows the open state to linger for a period
* during which it may be re - used .
2019-08-18 14:18:48 -04:00
*/
# include <linux/hash.h>
# include <linux/slab.h>
# include <linux/file.h>
2022-02-13 17:23:58 -05:00
# include <linux/pagemap.h>
2019-08-18 14:18:48 -04:00
# include <linux/sched.h>
# include <linux/list_lru.h>
# include <linux/fsnotify_backend.h>
# include <linux/fsnotify.h>
# include <linux/seq_file.h>
2022-07-08 14:26:23 -04:00
# include <linux/rhashtable.h>
2019-08-18 14:18:48 -04:00
# include "vfs.h"
# include "nfsd.h"
# include "nfsfh.h"
2019-09-02 13:02:55 -04:00
# include "netns.h"
2019-08-18 14:18:48 -04:00
# include "filecache.h"
# include "trace.h"
# define NFSD_LAUNDRETTE_DELAY (2 * HZ)
2022-07-08 14:26:16 -04:00
# define NFSD_FILE_CACHE_UP (0)
2019-08-18 14:18:48 -04:00
/* We only care about NFSD_MAY_READ/WRITE for this cache */
# define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE)
static DEFINE_PER_CPU ( unsigned long , nfsd_file_cache_hits ) ;
2022-07-08 14:23:59 -04:00
static DEFINE_PER_CPU ( unsigned long , nfsd_file_acquisitions ) ;
2022-07-08 14:24:05 -04:00
static DEFINE_PER_CPU ( unsigned long , nfsd_file_releases ) ;
2022-07-08 14:24:12 -04:00
static DEFINE_PER_CPU ( unsigned long , nfsd_file_total_age ) ;
2022-07-08 14:24:38 -04:00
static DEFINE_PER_CPU ( unsigned long , nfsd_file_evictions ) ;
2019-08-18 14:18:48 -04:00
2020-01-06 13:18:05 -05:00
struct nfsd_fcache_disposal {
struct work_struct work ;
spinlock_t lock ;
struct list_head freeme ;
} ;
2020-02-03 09:43:57 +08:00
static struct workqueue_struct * nfsd_filecache_wq __read_mostly ;
2020-01-06 13:18:05 -05:00
2019-08-18 14:18:48 -04:00
static struct kmem_cache * nfsd_file_slab ;
static struct kmem_cache * nfsd_file_mark_slab ;
static struct list_lru nfsd_file_lru ;
2022-07-08 14:26:16 -04:00
static unsigned long nfsd_file_flags ;
2019-08-18 14:18:48 -04:00
static struct fsnotify_group * nfsd_file_fsnotify_group ;
static struct delayed_work nfsd_filecache_laundrette ;
2022-11-24 15:09:04 -05:00
static struct rhltable nfsd_file_rhltable
2022-07-08 14:26:23 -04:00
____cacheline_aligned_in_smp ;
static bool
nfsd_match_cred ( const struct cred * c1 , const struct cred * c2 )
{
int i ;
if ( ! uid_eq ( c1 - > fsuid , c2 - > fsuid ) )
return false ;
if ( ! gid_eq ( c1 - > fsgid , c2 - > fsgid ) )
return false ;
if ( c1 - > group_info = = NULL | | c2 - > group_info = = NULL )
return c1 - > group_info = = c2 - > group_info ;
if ( c1 - > group_info - > ngroups ! = c2 - > group_info - > ngroups )
return false ;
for ( i = 0 ; i < c1 - > group_info - > ngroups ; i + + ) {
if ( ! gid_eq ( c1 - > group_info - > gid [ i ] , c2 - > group_info - > gid [ i ] ) )
return false ;
}
return true ;
}
static const struct rhashtable_params nfsd_file_rhash_params = {
. key_len = sizeof_field ( struct nfsd_file , nf_inode ) ,
. key_offset = offsetof ( struct nfsd_file , nf_inode ) ,
2022-11-24 15:09:04 -05:00
. head_offset = offsetof ( struct nfsd_file , nf_rlist ) ,
/*
* Start with a single page hash table to reduce resizing churn
* on light workloads .
*/
. min_size = 256 ,
2022-07-08 14:26:23 -04:00
. automatic_shrinking = true ,
} ;
2019-08-18 14:18:48 -04:00
static void
2020-01-06 13:18:05 -05:00
nfsd_file_schedule_laundrette ( void )
2019-08-18 14:18:48 -04:00
{
2022-11-02 14:44:50 -04:00
if ( test_bit ( NFSD_FILE_CACHE_UP , & nfsd_file_flags ) )
queue_delayed_work ( system_wq , & nfsd_filecache_laundrette ,
NFSD_LAUNDRETTE_DELAY ) ;
2019-08-18 14:18:48 -04:00
}
static void
nfsd_file_slab_free ( struct rcu_head * rcu )
{
struct nfsd_file * nf = container_of ( rcu , struct nfsd_file , nf_rcu ) ;
put_cred ( nf - > nf_cred ) ;
kmem_cache_free ( nfsd_file_slab , nf ) ;
}
static void
nfsd_file_mark_free ( struct fsnotify_mark * mark )
{
struct nfsd_file_mark * nfm = container_of ( mark , struct nfsd_file_mark ,
nfm_mark ) ;
kmem_cache_free ( nfsd_file_mark_slab , nfm ) ;
}
static struct nfsd_file_mark *
nfsd_file_mark_get ( struct nfsd_file_mark * nfm )
{
2020-01-14 12:02:44 -05:00
if ( ! refcount_inc_not_zero ( & nfm - > nfm_ref ) )
2019-08-18 14:18:48 -04:00
return NULL ;
return nfm ;
}
static void
nfsd_file_mark_put ( struct nfsd_file_mark * nfm )
{
2020-01-14 12:02:44 -05:00
if ( refcount_dec_and_test ( & nfm - > nfm_ref ) ) {
2019-08-18 14:18:48 -04:00
fsnotify_destroy_mark ( & nfm - > nfm_mark , nfsd_file_fsnotify_group ) ;
fsnotify_put_mark ( & nfm - > nfm_mark ) ;
}
}
static struct nfsd_file_mark *
2022-07-08 14:27:09 -04:00
nfsd_file_mark_find_or_create ( struct nfsd_file * nf , struct inode * inode )
2019-08-18 14:18:48 -04:00
{
int err ;
struct fsnotify_mark * mark ;
struct nfsd_file_mark * nfm = NULL , * new ;
do {
2022-04-22 15:03:20 +03:00
fsnotify_group_lock ( nfsd_file_fsnotify_group ) ;
2019-08-18 14:18:48 -04:00
mark = fsnotify_find_mark ( & inode - > i_fsnotify_marks ,
2022-04-22 15:03:20 +03:00
nfsd_file_fsnotify_group ) ;
2019-08-18 14:18:48 -04:00
if ( mark ) {
nfm = nfsd_file_mark_get ( container_of ( mark ,
struct nfsd_file_mark ,
nfm_mark ) ) ;
2022-04-22 15:03:20 +03:00
fsnotify_group_unlock ( nfsd_file_fsnotify_group ) ;
2020-01-06 13:20:47 -05:00
if ( nfm ) {
fsnotify_put_mark ( mark ) ;
2019-08-18 14:18:48 -04:00
break ;
2020-01-06 13:20:47 -05:00
}
/* Avoid soft lockup race with nfsd_file_mark_put() */
fsnotify_destroy_mark ( mark , nfsd_file_fsnotify_group ) ;
fsnotify_put_mark ( mark ) ;
2022-04-22 15:03:20 +03:00
} else {
fsnotify_group_unlock ( nfsd_file_fsnotify_group ) ;
}
2019-08-18 14:18:48 -04:00
/* allocate a new nfm */
new = kmem_cache_alloc ( nfsd_file_mark_slab , GFP_KERNEL ) ;
if ( ! new )
return NULL ;
fsnotify_init_mark ( & new - > nfm_mark , nfsd_file_fsnotify_group ) ;
new - > nfm_mark . mask = FS_ATTRIB | FS_DELETE_SELF ;
2020-01-14 12:02:44 -05:00
refcount_set ( & new - > nfm_ref , 1 ) ;
2019-08-18 14:18:48 -04:00
err = fsnotify_add_inode_mark ( & new - > nfm_mark , inode , 0 ) ;
/*
* If the add was successful , then return the object .
* Otherwise , we need to put the reference we hold on the
* nfm_mark . The fsnotify code will take a reference and put
* it on failure , so we can ' t just free it directly . It ' s also
* not safe to call fsnotify_destroy_mark on it as the
* mark - > group will be NULL . Thus , we can ' t let the nfm_ref
* counter drive the destruction at this point .
*/
if ( likely ( ! err ) )
nfm = new ;
else
fsnotify_put_mark ( & new - > nfm_mark ) ;
} while ( unlikely ( err = = - EEXIST ) ) ;
return nfm ;
}
static struct nfsd_file *
2022-11-24 15:09:04 -05:00
nfsd_file_alloc ( struct net * net , struct inode * inode , unsigned char need ,
bool want_gc )
2019-08-18 14:18:48 -04:00
{
struct nfsd_file * nf ;
nf = kmem_cache_alloc ( nfsd_file_slab , GFP_KERNEL ) ;
2022-11-24 15:09:04 -05:00
if ( unlikely ( ! nf ) )
return NULL ;
INIT_LIST_HEAD ( & nf - > nf_lru ) ;
nf - > nf_birthtime = ktime_get ( ) ;
nf - > nf_file = NULL ;
nf - > nf_cred = get_current_cred ( ) ;
nf - > nf_net = net ;
nf - > nf_flags = want_gc ?
BIT ( NFSD_FILE_HASHED ) | BIT ( NFSD_FILE_PENDING ) | BIT ( NFSD_FILE_GC ) :
BIT ( NFSD_FILE_HASHED ) | BIT ( NFSD_FILE_PENDING ) ;
nf - > nf_inode = inode ;
refcount_set ( & nf - > nf_ref , 1 ) ;
nf - > nf_may = need ;
nf - > nf_mark = NULL ;
2019-08-18 14:18:48 -04:00
return nf ;
}
2023-02-07 12:02:46 -05:00
/**
* nfsd_file_check_write_error - check for writeback errors on a file
* @ nf : nfsd_file to check for writeback errors
*
* Check whether a nfsd_file has an unseen error . Reset the write
* verifier if so .
*/
2022-11-02 14:44:48 -04:00
static void
nfsd_file_check_write_error ( struct nfsd_file * nf )
{
struct file * file = nf - > nf_file ;
2023-02-07 12:02:46 -05:00
if ( ( file - > f_mode & FMODE_WRITE ) & &
filemap_check_wb_err ( file - > f_mapping , READ_ONCE ( file - > f_wb_err ) ) )
nfsd_reset_write_verifier ( net_generic ( nf - > nf_net , nfsd_net_id ) ) ;
2022-11-02 14:44:48 -04:00
}
static void
nfsd_file_hash_remove ( struct nfsd_file * nf )
{
trace_nfsd_file_unhash ( nf ) ;
2022-11-24 15:09:04 -05:00
rhltable_remove ( & nfsd_file_rhltable , & nf - > nf_rlist ,
nfsd_file_rhash_params ) ;
2022-11-02 14:44:48 -04:00
}
static bool
nfsd_file_unhash ( struct nfsd_file * nf )
{
if ( test_and_clear_bit ( NFSD_FILE_HASHED , & nf - > nf_flags ) ) {
nfsd_file_hash_remove ( nf ) ;
return true ;
}
return false ;
}
2022-12-11 06:19:33 -05:00
static void
2019-08-18 14:18:48 -04:00
nfsd_file_free ( struct nfsd_file * nf )
{
2022-07-08 14:24:12 -04:00
s64 age = ktime_to_ms ( ktime_sub ( ktime_get ( ) , nf - > nf_birthtime ) ) ;
2019-08-18 14:18:48 -04:00
2022-11-02 14:44:48 -04:00
trace_nfsd_file_free ( nf ) ;
2022-07-08 14:24:05 -04:00
this_cpu_inc ( nfsd_file_releases ) ;
2022-07-08 14:24:12 -04:00
this_cpu_add ( nfsd_file_total_age , age ) ;
2022-07-08 14:24:05 -04:00
2022-12-11 06:19:33 -05:00
nfsd_file_unhash ( nf ) ;
2019-08-18 14:18:48 -04:00
if ( nf - > nf_mark )
nfsd_file_mark_put ( nf - > nf_mark ) ;
if ( nf - > nf_file ) {
2023-02-07 12:02:46 -05:00
nfsd_file_check_write_error ( nf ) ;
2023-01-18 12:31:37 -05:00
filp_close ( nf - > nf_file , NULL ) ;
2019-08-18 14:18:48 -04:00
}
2022-07-08 14:25:04 -04:00
/*
* If this item is still linked via nf_lru , that ' s a bug .
* WARN and leak it to preserve system stability .
*/
if ( WARN_ON_ONCE ( ! list_empty ( & nf - > nf_lru ) ) )
2022-12-11 06:19:33 -05:00
return ;
2022-07-08 14:25:04 -04:00
2019-08-18 14:18:48 -04:00
call_rcu ( & nf - > nf_rcu , nfsd_file_slab_free ) ;
}
2019-09-02 13:02:57 -04:00
static bool
nfsd_file_check_writeback ( struct nfsd_file * nf )
{
struct file * file = nf - > nf_file ;
struct address_space * mapping ;
2023-02-15 06:53:54 -05:00
/* File not open for write? */
if ( ! ( file - > f_mode & FMODE_WRITE ) )
2019-09-02 13:02:57 -04:00
return false ;
2023-02-15 06:53:54 -05:00
/*
* Some filesystems ( e . g . NFS ) flush all dirty data on close .
* On others , there is no need to wait for writeback .
*/
if ( ! ( file_inode ( file ) - > i_sb - > s_export_op - > flags & EXPORT_OP_FLUSH_ON_CLOSE ) )
return false ;
2019-09-02 13:02:57 -04:00
mapping = file - > f_mapping ;
return mapping_tagged ( mapping , PAGECACHE_TAG_DIRTY ) | |
mapping_tagged ( mapping , PAGECACHE_TAG_WRITEBACK ) ;
}
2023-02-15 06:53:54 -05:00
2022-12-11 06:19:33 -05:00
static bool nfsd_file_lru_add ( struct nfsd_file * nf )
2019-08-18 14:18:48 -04:00
{
2022-07-08 14:25:17 -04:00
set_bit ( NFSD_FILE_REFERENCED , & nf - > nf_flags ) ;
2022-12-11 06:19:33 -05:00
if ( list_lru_add ( & nfsd_file_lru , & nf - > nf_lru ) ) {
2022-07-08 14:25:11 -04:00
trace_nfsd_file_lru_add ( nf ) ;
2022-12-11 06:19:33 -05:00
return true ;
}
return false ;
2022-07-08 14:25:11 -04:00
}
2019-08-18 14:18:48 -04:00
2022-12-11 06:19:33 -05:00
static bool nfsd_file_lru_remove ( struct nfsd_file * nf )
2022-07-08 14:25:11 -04:00
{
2022-12-11 06:19:33 -05:00
if ( list_lru_del ( & nfsd_file_lru , & nf - > nf_lru ) ) {
2022-07-08 14:25:11 -04:00
trace_nfsd_file_lru_del ( nf ) ;
2022-12-11 06:19:33 -05:00
return true ;
}
return false ;
2022-07-08 14:25:11 -04:00
}
2022-11-02 14:44:48 -04:00
struct nfsd_file *
nfsd_file_get ( struct nfsd_file * nf )
2019-08-18 14:18:48 -04:00
{
2023-01-06 10:33:47 -05:00
if ( nf & & refcount_inc_not_zero ( & nf - > nf_ref ) )
2022-11-02 14:44:48 -04:00
return nf ;
return NULL ;
2019-08-18 14:18:48 -04:00
}
2022-12-11 06:19:33 -05:00
/**
* nfsd_file_put - put the reference to a nfsd_file
* @ nf : nfsd_file of which to put the reference
*
* Put a reference to a nfsd_file . In the non - GC case , we just put the
* reference immediately . In the GC case , if the reference would be
* the last one , the put it on the LRU instead to be cleaned up later .
*/
2019-08-18 14:18:48 -04:00
void
nfsd_file_put ( struct nfsd_file * nf )
{
2022-05-11 13:02:21 -04:00
might_sleep ( ) ;
2022-12-11 06:19:33 -05:00
trace_nfsd_file_put ( nf ) ;
2022-05-11 13:02:21 -04:00
2022-12-11 06:19:33 -05:00
if ( test_bit ( NFSD_FILE_GC , & nf - > nf_flags ) & &
test_bit ( NFSD_FILE_HASHED , & nf - > nf_flags ) ) {
/*
* If this is the last reference ( nf_ref = = 1 ) , then try to
* transfer it to the LRU .
*/
if ( refcount_dec_not_one ( & nf - > nf_ref ) )
return ;
/* Try to add it to the LRU. If that fails, decrement. */
if ( nfsd_file_lru_add ( nf ) ) {
/* If it's still hashed, we're done */
if ( test_bit ( NFSD_FILE_HASHED , & nf - > nf_flags ) ) {
nfsd_file_schedule_laundrette ( ) ;
return ;
}
2019-08-18 14:18:48 -04:00
2022-12-11 06:19:33 -05:00
/*
* We ' re racing with unhashing , so try to remove it from
* the LRU . If removal fails , then someone else already
* has our reference .
*/
if ( ! nfsd_file_lru_remove ( nf ) )
return ;
}
2019-08-18 14:18:48 -04:00
}
2022-12-11 06:19:33 -05:00
if ( refcount_dec_and_test ( & nf - > nf_ref ) )
nfsd_file_free ( nf ) ;
2019-08-18 14:18:48 -04:00
}
static void
2022-12-11 06:19:33 -05:00
nfsd_file_dispose_list ( struct list_head * dispose )
2019-08-18 14:18:48 -04:00
{
struct nfsd_file * nf ;
2022-12-11 06:19:33 -05:00
while ( ! list_empty ( dispose ) ) {
2019-08-18 14:18:48 -04:00
nf = list_first_entry ( dispose , struct nfsd_file , nf_lru ) ;
2022-07-08 14:25:04 -04:00
list_del_init ( & nf - > nf_lru ) ;
2022-12-11 06:19:33 -05:00
nfsd_file_free ( nf ) ;
2019-08-18 14:18:48 -04:00
}
}
2023-04-14 17:31:44 -04:00
/**
* nfsd_file_dispose_list_delayed - move list of dead files to net ' s freeme list
* @ dispose : list of nfsd_files to be disposed
*
* Transfers each file to the " freeme " list for its nfsd_net , to eventually
* be disposed of by the per - net garbage collector .
*/
2020-01-06 13:18:05 -05:00
static void
nfsd_file_dispose_list_delayed ( struct list_head * dispose )
{
while ( ! list_empty ( dispose ) ) {
2023-04-14 17:31:44 -04:00
struct nfsd_file * nf = list_first_entry ( dispose ,
struct nfsd_file , nf_lru ) ;
struct nfsd_net * nn = net_generic ( nf - > nf_net , nfsd_net_id ) ;
struct nfsd_fcache_disposal * l = nn - > fcache_disposal ;
spin_lock ( & l - > lock ) ;
list_move_tail ( & nf - > nf_lru , & l - > freeme ) ;
spin_unlock ( & l - > lock ) ;
queue_work ( nfsd_filecache_wq , & l - > work ) ;
2020-01-06 13:18:05 -05:00
}
}
2022-07-08 14:25:17 -04:00
/**
* nfsd_file_lru_cb - Examine an entry on the LRU list
* @ item : LRU entry to examine
* @ lru : controlling LRU
* @ lock : LRU list lock ( unused )
* @ arg : dispose list
*
* Return values :
* % LRU_REMOVED : @ item was removed from the LRU
2022-07-08 14:25:24 -04:00
* % LRU_ROTATE : @ item is to be moved to the LRU tail
2022-07-08 14:25:17 -04:00
* % LRU_SKIP : @ item cannot be evicted
2019-08-18 14:18:48 -04:00
*/
static enum lru_status
nfsd_file_lru_cb ( struct list_head * item , struct list_lru_one * lru ,
spinlock_t * lock , void * arg )
__releases ( lock )
__acquires ( lock )
{
struct list_head * head = arg ;
struct nfsd_file * nf = list_entry ( item , struct nfsd_file , nf_lru ) ;
2022-12-11 06:19:33 -05:00
/* We should only be dealing with GC entries here */
WARN_ON_ONCE ( ! test_bit ( NFSD_FILE_GC , & nf - > nf_flags ) ) ;
2019-09-02 13:02:57 -04:00
/*
* Don ' t throw out files that are still undergoing I / O or
* that have uncleared errors pending .
*/
2022-07-08 14:25:11 -04:00
if ( nfsd_file_check_writeback ( nf ) ) {
trace_nfsd_file_gc_writeback ( nf ) ;
return LRU_SKIP ;
}
2019-09-02 13:02:57 -04:00
2022-12-11 06:19:33 -05:00
/* If it was recently added to the list, skip it */
2022-07-08 14:25:11 -04:00
if ( test_and_clear_bit ( NFSD_FILE_REFERENCED , & nf - > nf_flags ) ) {
trace_nfsd_file_gc_referenced ( nf ) ;
2022-07-08 14:25:24 -04:00
return LRU_ROTATE ;
2022-07-08 14:25:11 -04:00
}
2019-08-18 14:18:48 -04:00
2022-12-11 06:19:33 -05:00
/*
* Put the reference held on behalf of the LRU . If it wasn ' t the last
* one , then just remove it from the LRU and ignore it .
*/
if ( ! refcount_dec_and_test ( & nf - > nf_ref ) ) {
trace_nfsd_file_gc_in_use ( nf ) ;
list_lru_isolate ( lru , & nf - > nf_lru ) ;
return LRU_REMOVED ;
2022-07-08 14:25:11 -04:00
}
2019-08-18 14:18:48 -04:00
2022-12-11 06:19:33 -05:00
/* Refcount went to zero. Unhash it and queue it to the dispose list */
nfsd_file_unhash ( nf ) ;
2019-08-18 14:18:48 -04:00
list_lru_isolate_move ( lru , & nf - > nf_lru , head ) ;
2022-07-08 14:24:38 -04:00
this_cpu_inc ( nfsd_file_evictions ) ;
2022-07-08 14:25:11 -04:00
trace_nfsd_file_gc_disposed ( nf ) ;
2019-08-18 14:18:48 -04:00
return LRU_REMOVED ;
}
2020-01-06 13:18:05 -05:00
static void
nfsd_file_gc ( void )
{
2022-07-08 14:24:25 -04:00
LIST_HEAD ( dispose ) ;
2022-07-08 14:24:38 -04:00
unsigned long ret ;
2022-07-08 14:24:25 -04:00
2022-07-08 14:24:38 -04:00
ret = list_lru_walk ( & nfsd_file_lru , nfsd_file_lru_cb ,
2022-07-08 14:25:24 -04:00
& dispose , list_lru_count ( & nfsd_file_lru ) ) ;
2022-07-08 14:24:38 -04:00
trace_nfsd_file_gc_removed ( ret , list_lru_count ( & nfsd_file_lru ) ) ;
2022-12-11 06:19:33 -05:00
nfsd_file_dispose_list_delayed ( & dispose ) ;
2020-01-06 13:18:05 -05:00
}
static void
nfsd_file_gc_worker ( struct work_struct * work )
{
nfsd_file_gc ( ) ;
2022-11-02 14:44:50 -04:00
if ( list_lru_count ( & nfsd_file_lru ) )
nfsd_file_schedule_laundrette ( ) ;
2019-08-18 14:18:48 -04:00
}
static unsigned long
nfsd_file_lru_count ( struct shrinker * s , struct shrink_control * sc )
{
return list_lru_count ( & nfsd_file_lru ) ;
}
static unsigned long
nfsd_file_lru_scan ( struct shrinker * s , struct shrink_control * sc )
{
2022-07-08 14:24:31 -04:00
LIST_HEAD ( dispose ) ;
unsigned long ret ;
ret = list_lru_shrink_walk ( & nfsd_file_lru , sc ,
nfsd_file_lru_cb , & dispose ) ;
2022-07-08 14:24:38 -04:00
trace_nfsd_file_shrinker_removed ( ret , list_lru_count ( & nfsd_file_lru ) ) ;
2022-12-11 06:19:33 -05:00
nfsd_file_dispose_list_delayed ( & dispose ) ;
2022-07-08 14:24:31 -04:00
return ret ;
2019-08-18 14:18:48 -04:00
}
static struct shrinker nfsd_file_shrinker = {
. scan_objects = nfsd_file_lru_scan ,
. count_objects = nfsd_file_lru_count ,
. seeks = 1 ,
} ;
2023-01-20 14:52:14 -05:00
/**
* nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
* @ nf : nfsd_file to attempt to queue
* @ dispose : private list to queue successfully - put objects
*
* Unhash an nfsd_file , try to get a reference to it , and then put that
* reference . If it ' s the last reference , queue it to the dispose list .
*/
static void
nfsd_file_cond_queue ( struct nfsd_file * nf , struct list_head * dispose )
__must_hold ( RCU )
{
int decrement = 1 ;
/* If we raced with someone else unhashing, ignore it */
if ( ! nfsd_file_unhash ( nf ) )
return ;
/* If we can't get a reference, ignore it */
if ( ! nfsd_file_get ( nf ) )
return ;
/* Extra decrement if we remove from the LRU */
if ( nfsd_file_lru_remove ( nf ) )
+ + decrement ;
/* If refcount goes to 0, then put on the dispose list */
if ( refcount_sub_and_test ( decrement , & nf - > nf_ref ) ) {
list_add ( & nf - > nf_lru , dispose ) ;
trace_nfsd_file_closing ( nf ) ;
}
}
2022-12-11 06:19:33 -05:00
/**
* nfsd_file_queue_for_close : try to close out any open nfsd_files for an inode
* @ inode : inode on which to close out nfsd_files
* @ dispose : list on which to gather nfsd_files to close out
*
2022-11-24 15:09:04 -05:00
* An nfsd_file represents a struct file being held open on behalf of nfsd .
* An open file however can block other activity ( such as leases ) , or cause
2022-12-11 06:19:33 -05:00
* undesirable behavior ( e . g . spurious silly - renames when reexporting NFS ) .
*
* This function is intended to find open nfsd_files when this sort of
* conflicting access occurs and then attempt to close those files out .
*
* Populates the dispose list with entries that have already had their
* refcounts go to zero . The actual free of an nfsd_file can be expensive ,
* so we leave it up to the caller whether it wants to wait or not .
2022-07-08 14:25:57 -04:00
*/
2022-12-11 06:19:33 -05:00
static void
nfsd_file_queue_for_close ( struct inode * inode , struct list_head * dispose )
2019-08-18 14:18:48 -04:00
{
2022-11-24 15:09:04 -05:00
struct rhlist_head * tmp , * list ;
2022-07-08 14:26:30 -04:00
struct nfsd_file * nf ;
2019-08-18 14:18:48 -04:00
2022-07-08 14:26:30 -04:00
rcu_read_lock ( ) ;
2022-11-24 15:09:04 -05:00
list = rhltable_lookup ( & nfsd_file_rhltable , & inode ,
nfsd_file_rhash_params ) ;
rhl_for_each_entry_rcu ( nf , tmp , list , nf_rlist ) {
if ( ! test_bit ( NFSD_FILE_GC , & nf - > nf_flags ) )
continue ;
2023-01-20 14:52:14 -05:00
nfsd_file_cond_queue ( nf , dispose ) ;
2022-11-24 15:09:04 -05:00
}
2022-07-08 14:26:30 -04:00
rcu_read_unlock ( ) ;
2019-08-18 14:18:48 -04:00
}
/**
2022-12-11 06:19:33 -05:00
* nfsd_file_close_inode - attempt a delayed close of a nfsd_file
2019-08-18 14:18:48 -04:00
* @ inode : inode of the file to attempt to remove
*
2022-12-11 06:19:33 -05:00
* Close out any open nfsd_files that can be reaped for @ inode . The
* actual freeing is deferred to the dispose_list_delayed infrastructure .
*
* This is used by the fsnotify callbacks and setlease notifier .
2019-08-18 14:18:48 -04:00
*/
2022-12-11 06:19:33 -05:00
static void
nfsd_file_close_inode ( struct inode * inode )
2019-08-18 14:18:48 -04:00
{
LIST_HEAD ( dispose ) ;
2022-12-11 06:19:33 -05:00
nfsd_file_queue_for_close ( inode , & dispose ) ;
nfsd_file_dispose_list_delayed ( & dispose ) ;
2019-08-18 14:18:48 -04:00
}
/**
2022-12-11 06:19:33 -05:00
* nfsd_file_close_inode_sync - attempt to forcibly close a nfsd_file
2019-08-18 14:18:48 -04:00
* @ inode : inode of the file to attempt to remove
*
2022-12-11 06:19:33 -05:00
* Close out any open nfsd_files that can be reaped for @ inode . The
* nfsd_files are closed out synchronously .
*
* This is called from nfsd_rename and nfsd_unlink to avoid silly - renames
* when reexporting NFS .
2019-08-18 14:18:48 -04:00
*/
2022-12-11 06:19:33 -05:00
void
nfsd_file_close_inode_sync ( struct inode * inode )
2019-08-18 14:18:48 -04:00
{
2022-12-11 06:19:33 -05:00
struct nfsd_file * nf ;
2019-08-18 14:18:48 -04:00
LIST_HEAD ( dispose ) ;
2022-12-11 06:19:33 -05:00
trace_nfsd_file_close ( inode ) ;
nfsd_file_queue_for_close ( inode , & dispose ) ;
while ( ! list_empty ( & dispose ) ) {
nf = list_first_entry ( & dispose , struct nfsd_file , nf_lru ) ;
list_del_init ( & nf - > nf_lru ) ;
nfsd_file_free ( nf ) ;
}
flush_delayed_fput ( ) ;
2019-08-18 14:18:48 -04:00
}
/**
* nfsd_file_delayed_close - close unused nfsd_files
* @ work : dummy
*
2023-04-14 17:31:44 -04:00
* Scrape the freeme list for this nfsd_net , and then dispose of them
* all .
2019-08-18 14:18:48 -04:00
*/
static void
nfsd_file_delayed_close ( struct work_struct * work )
{
LIST_HEAD ( head ) ;
2020-01-06 13:18:05 -05:00
struct nfsd_fcache_disposal * l = container_of ( work ,
struct nfsd_fcache_disposal , work ) ;
2019-08-18 14:18:48 -04:00
2023-04-14 17:31:44 -04:00
spin_lock ( & l - > lock ) ;
list_splice_init ( & l - > freeme , & head ) ;
spin_unlock ( & l - > lock ) ;
2020-01-06 13:18:05 -05:00
nfsd_file_dispose_list ( & head ) ;
2019-08-18 14:18:48 -04:00
}
static int
nfsd_file_lease_notifier_call ( struct notifier_block * nb , unsigned long arg ,
void * data )
{
struct file_lock * fl = data ;
/* Only close files for F_SETLEASE leases */
if ( fl - > fl_flags & FL_LEASE )
2022-12-11 06:19:33 -05:00
nfsd_file_close_inode ( file_inode ( fl - > fl_file ) ) ;
2019-08-18 14:18:48 -04:00
return 0 ;
}
static struct notifier_block nfsd_file_lease_notifier = {
. notifier_call = nfsd_file_lease_notifier_call ,
} ;
static int
2020-07-22 15:58:48 +03:00
nfsd_file_fsnotify_handle_event ( struct fsnotify_mark * mark , u32 mask ,
struct inode * inode , struct inode * dir ,
2020-12-02 14:07:07 +02:00
const struct qstr * name , u32 cookie )
2019-08-18 14:18:48 -04:00
{
2021-10-25 16:27:26 -03:00
if ( WARN_ON_ONCE ( ! inode ) )
return 0 ;
2019-08-18 14:18:48 -04:00
trace_nfsd_file_fsnotify_handle_event ( inode , mask ) ;
/* Should be no marks on non-regular files */
if ( ! S_ISREG ( inode - > i_mode ) ) {
WARN_ON_ONCE ( 1 ) ;
return 0 ;
}
/* don't close files if this was not the last link */
if ( mask & FS_ATTRIB ) {
if ( inode - > i_nlink )
return 0 ;
}
nfsd_file_close_inode ( inode ) ;
return 0 ;
}
static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
2020-07-22 15:58:48 +03:00
. handle_inode_event = nfsd_file_fsnotify_handle_event ,
2019-08-18 14:18:48 -04:00
. free_mark = nfsd_file_mark_free ,
} ;
int
nfsd_file_cache_init ( void )
{
2022-07-08 14:26:36 -04:00
int ret ;
2019-08-18 14:18:48 -04:00
2022-07-08 14:26:16 -04:00
lockdep_assert_held ( & nfsd_mutex ) ;
if ( test_and_set_bit ( NFSD_FILE_CACHE_UP , & nfsd_file_flags ) = = 1 )
2019-08-18 14:18:48 -04:00
return 0 ;
2022-11-24 15:09:04 -05:00
ret = rhltable_init ( & nfsd_file_rhltable , & nfsd_file_rhash_params ) ;
2022-07-08 14:26:23 -04:00
if ( ret )
return ret ;
ret = - ENOMEM ;
2020-01-06 13:18:05 -05:00
nfsd_filecache_wq = alloc_workqueue ( " nfsd_filecache " , 0 , 0 ) ;
if ( ! nfsd_filecache_wq )
goto out ;
2019-08-18 14:18:48 -04:00
nfsd_file_slab = kmem_cache_create ( " nfsd_file " ,
sizeof ( struct nfsd_file ) , 0 , 0 , NULL ) ;
if ( ! nfsd_file_slab ) {
pr_err ( " nfsd: unable to create nfsd_file_slab \n " ) ;
goto out_err ;
}
nfsd_file_mark_slab = kmem_cache_create ( " nfsd_file_mark " ,
sizeof ( struct nfsd_file_mark ) , 0 , 0 , NULL ) ;
if ( ! nfsd_file_mark_slab ) {
pr_err ( " nfsd: unable to create nfsd_file_mark_slab \n " ) ;
goto out_err ;
}
ret = list_lru_init ( & nfsd_file_lru ) ;
if ( ret ) {
pr_err ( " nfsd: failed to init nfsd_file_lru: %d \n " , ret ) ;
goto out_err ;
}
2022-05-31 20:22:24 -07:00
ret = register_shrinker ( & nfsd_file_shrinker , " nfsd-filecache " ) ;
2019-08-18 14:18:48 -04:00
if ( ret ) {
pr_err ( " nfsd: failed to register nfsd_file_shrinker: %d \n " , ret ) ;
goto out_lru ;
}
ret = lease_register_notifier ( & nfsd_file_lease_notifier ) ;
if ( ret ) {
pr_err ( " nfsd: unable to register lease notifier: %d \n " , ret ) ;
goto out_shrinker ;
}
2022-04-22 15:03:15 +03:00
nfsd_file_fsnotify_group = fsnotify_alloc_group ( & nfsd_file_fsnotify_ops ,
2022-04-22 15:03:20 +03:00
FSNOTIFY_GROUP_NOFS ) ;
2019-08-18 14:18:48 -04:00
if ( IS_ERR ( nfsd_file_fsnotify_group ) ) {
pr_err ( " nfsd: unable to create fsnotify group: %ld \n " ,
PTR_ERR ( nfsd_file_fsnotify_group ) ) ;
2020-11-25 03:39:33 -05:00
ret = PTR_ERR ( nfsd_file_fsnotify_group ) ;
2019-08-18 14:18:48 -04:00
nfsd_file_fsnotify_group = NULL ;
goto out_notifier ;
}
2020-01-06 13:18:05 -05:00
INIT_DELAYED_WORK ( & nfsd_filecache_laundrette , nfsd_file_gc_worker ) ;
2019-08-18 14:18:48 -04:00
out :
return ret ;
out_notifier :
lease_unregister_notifier ( & nfsd_file_lease_notifier ) ;
out_shrinker :
unregister_shrinker ( & nfsd_file_shrinker ) ;
out_lru :
list_lru_destroy ( & nfsd_file_lru ) ;
out_err :
kmem_cache_destroy ( nfsd_file_slab ) ;
nfsd_file_slab = NULL ;
kmem_cache_destroy ( nfsd_file_mark_slab ) ;
nfsd_file_mark_slab = NULL ;
2020-01-06 13:18:05 -05:00
destroy_workqueue ( nfsd_filecache_wq ) ;
nfsd_filecache_wq = NULL ;
2022-11-24 15:09:04 -05:00
rhltable_destroy ( & nfsd_file_rhltable ) ;
2019-08-18 14:18:48 -04:00
goto out ;
}
2022-12-11 06:19:33 -05:00
/**
* __nfsd_file_cache_purge : clean out the cache for shutdown
* @ net : net - namespace to shut down the cache ( may be NULL )
*
* Walk the nfsd_file cache and close out any that match @ net . If @ net is NULL ,
2023-01-26 12:21:16 -05:00
* then close out everything . Called when an nfsd instance is being shut down ,
* and when the exports table is flushed .
2022-12-11 06:19:33 -05:00
*/
2022-07-08 14:26:16 -04:00
static void
__nfsd_file_cache_purge ( struct net * net )
2019-08-18 14:18:48 -04:00
{
2022-07-08 14:26:30 -04:00
struct rhashtable_iter iter ;
struct nfsd_file * nf ;
2019-08-18 14:18:48 -04:00
LIST_HEAD ( dispose ) ;
2022-11-24 15:09:04 -05:00
rhltable_walk_enter ( & nfsd_file_rhltable , & iter ) ;
2022-07-08 14:26:30 -04:00
do {
rhashtable_walk_start ( & iter ) ;
2019-09-02 13:02:55 -04:00
2022-07-08 14:26:30 -04:00
nf = rhashtable_walk_next ( & iter ) ;
while ( ! IS_ERR_OR_NULL ( nf ) ) {
2023-01-20 14:52:14 -05:00
if ( ! net | | nf - > nf_net = = net )
nfsd_file_cond_queue ( nf , & dispose ) ;
2022-07-08 14:26:30 -04:00
nf = rhashtable_walk_next ( & iter ) ;
2019-08-18 14:18:48 -04:00
}
2022-07-08 14:26:30 -04:00
rhashtable_walk_stop ( & iter ) ;
} while ( nf = = ERR_PTR ( - EAGAIN ) ) ;
rhashtable_walk_exit ( & iter ) ;
nfsd_file_dispose_list ( & dispose ) ;
2019-08-18 14:18:48 -04:00
}
2020-01-06 13:18:05 -05:00
static struct nfsd_fcache_disposal *
2021-12-01 10:58:14 +11:00
nfsd_alloc_fcache_disposal ( void )
2020-01-06 13:18:05 -05:00
{
struct nfsd_fcache_disposal * l ;
l = kmalloc ( sizeof ( * l ) , GFP_KERNEL ) ;
if ( ! l )
return NULL ;
INIT_WORK ( & l - > work , nfsd_file_delayed_close ) ;
spin_lock_init ( & l - > lock ) ;
INIT_LIST_HEAD ( & l - > freeme ) ;
return l ;
}
static void
nfsd_free_fcache_disposal ( struct nfsd_fcache_disposal * l )
{
cancel_work_sync ( & l - > work ) ;
nfsd_file_dispose_list ( & l - > freeme ) ;
2021-12-01 10:58:14 +11:00
kfree ( l ) ;
2020-01-06 13:18:05 -05:00
}
static void
nfsd_free_fcache_disposal_net ( struct net * net )
{
2021-12-01 10:58:14 +11:00
struct nfsd_net * nn = net_generic ( net , nfsd_net_id ) ;
struct nfsd_fcache_disposal * l = nn - > fcache_disposal ;
2020-01-06 13:18:05 -05:00
2021-12-01 10:58:14 +11:00
nfsd_free_fcache_disposal ( l ) ;
2020-01-06 13:18:05 -05:00
}
int
nfsd_file_cache_start_net ( struct net * net )
{
2021-12-01 10:58:14 +11:00
struct nfsd_net * nn = net_generic ( net , nfsd_net_id ) ;
nn - > fcache_disposal = nfsd_alloc_fcache_disposal ( ) ;
return nn - > fcache_disposal ? 0 : - ENOMEM ;
2020-01-06 13:18:05 -05:00
}
2022-07-08 14:26:16 -04:00
/**
* nfsd_file_cache_purge - Remove all cache items associated with @ net
* @ net : target net namespace
*
*/
void
nfsd_file_cache_purge ( struct net * net )
{
lockdep_assert_held ( & nfsd_mutex ) ;
if ( test_bit ( NFSD_FILE_CACHE_UP , & nfsd_file_flags ) = = 1 )
__nfsd_file_cache_purge ( net ) ;
}
2020-01-06 13:18:05 -05:00
void
nfsd_file_cache_shutdown_net ( struct net * net )
{
nfsd_file_cache_purge ( net ) ;
nfsd_free_fcache_disposal_net ( net ) ;
}
2019-08-18 14:18:48 -04:00
void
nfsd_file_cache_shutdown ( void )
{
2022-07-08 14:24:51 -04:00
int i ;
2022-07-08 14:26:16 -04:00
lockdep_assert_held ( & nfsd_mutex ) ;
if ( test_and_clear_bit ( NFSD_FILE_CACHE_UP , & nfsd_file_flags ) = = 0 )
return ;
2019-08-18 14:18:48 -04:00
lease_unregister_notifier ( & nfsd_file_lease_notifier ) ;
unregister_shrinker ( & nfsd_file_shrinker ) ;
/*
* make sure all callers of nfsd_file_lru_cb are done before
* calling nfsd_file_cache_purge
*/
cancel_delayed_work_sync ( & nfsd_filecache_laundrette ) ;
2022-07-08 14:26:16 -04:00
__nfsd_file_cache_purge ( NULL ) ;
2019-08-18 14:18:48 -04:00
list_lru_destroy ( & nfsd_file_lru ) ;
rcu_barrier ( ) ;
fsnotify_put_group ( nfsd_file_fsnotify_group ) ;
nfsd_file_fsnotify_group = NULL ;
kmem_cache_destroy ( nfsd_file_slab ) ;
nfsd_file_slab = NULL ;
fsnotify_wait_marks_destroyed ( ) ;
kmem_cache_destroy ( nfsd_file_mark_slab ) ;
nfsd_file_mark_slab = NULL ;
2020-01-06 13:18:05 -05:00
destroy_workqueue ( nfsd_filecache_wq ) ;
nfsd_filecache_wq = NULL ;
2022-11-24 15:09:04 -05:00
rhltable_destroy ( & nfsd_file_rhltable ) ;
2022-07-08 14:24:51 -04:00
for_each_possible_cpu ( i ) {
per_cpu ( nfsd_file_cache_hits , i ) = 0 ;
per_cpu ( nfsd_file_acquisitions , i ) = 0 ;
per_cpu ( nfsd_file_releases , i ) = 0 ;
per_cpu ( nfsd_file_total_age , i ) = 0 ;
per_cpu ( nfsd_file_evictions , i ) = 0 ;
2019-08-18 14:18:48 -04:00
}
}
2022-11-24 15:09:04 -05:00
static struct nfsd_file *
nfsd_file_lookup_locked ( const struct net * net , const struct cred * cred ,
struct inode * inode , unsigned char need ,
bool want_gc )
{
struct rhlist_head * tmp , * list ;
struct nfsd_file * nf ;
list = rhltable_lookup ( & nfsd_file_rhltable , & inode ,
nfsd_file_rhash_params ) ;
rhl_for_each_entry_rcu ( nf , tmp , list , nf_rlist ) {
if ( nf - > nf_may ! = need )
continue ;
if ( nf - > nf_net ! = net )
continue ;
if ( ! nfsd_match_cred ( nf - > nf_cred , cred ) )
continue ;
if ( test_bit ( NFSD_FILE_GC , & nf - > nf_flags ) ! = want_gc )
continue ;
if ( test_bit ( NFSD_FILE_HASHED , & nf - > nf_flags ) = = 0 )
continue ;
if ( ! nfsd_file_get ( nf ) )
continue ;
return nf ;
}
return NULL ;
}
2019-08-18 14:18:48 -04:00
/**
2022-07-08 14:26:30 -04:00
* nfsd_file_is_cached - are there any cached open files for this inode ?
* @ inode : inode to check
*
* The lookup matches inodes in all net namespaces and is atomic wrt
* nfsd_file_acquire ( ) .
2019-08-18 14:18:48 -04:00
*
2022-07-08 14:26:30 -04:00
* Return values :
* % true : filecache contains at least one file matching this inode
* % false : filecache contains no files matching this inode
2019-08-18 14:18:48 -04:00
*/
bool
nfsd_file_is_cached ( struct inode * inode )
{
2022-11-24 15:09:04 -05:00
struct rhlist_head * tmp , * list ;
struct nfsd_file * nf ;
2022-07-08 14:26:30 -04:00
bool ret = false ;
2022-11-24 15:09:04 -05:00
rcu_read_lock ( ) ;
list = rhltable_lookup ( & nfsd_file_rhltable , & inode ,
nfsd_file_rhash_params ) ;
rhl_for_each_entry_rcu ( nf , tmp , list , nf_rlist )
if ( test_bit ( NFSD_FILE_GC , & nf - > nf_flags ) ) {
ret = true ;
break ;
}
rcu_read_unlock ( ) ;
2022-07-08 14:25:37 -04:00
trace_nfsd_file_is_cached ( inode , ( int ) ret ) ;
2019-08-18 14:18:48 -04:00
return ret ;
}
2022-03-30 10:30:54 -04:00
static __be32
2022-07-08 14:26:43 -04:00
nfsd_file_do_acquire ( struct svc_rqst * rqstp , struct svc_fh * fhp ,
2023-01-05 14:55:56 -05:00
unsigned int may_flags , struct file * file ,
struct nfsd_file * * pnf , bool want_gc )
2019-08-18 14:18:48 -04:00
{
2022-11-24 15:09:04 -05:00
unsigned char need = may_flags & NFSD_FILE_MAY_MASK ;
struct net * net = SVC_NET ( rqstp ) ;
struct nfsd_file * new , * nf ;
const struct cred * cred ;
2022-10-04 15:41:10 -04:00
bool open_retry = true ;
2022-11-24 15:09:04 -05:00
struct inode * inode ;
2022-07-08 14:26:30 -04:00
__be32 status ;
2022-10-04 15:41:10 -04:00
int ret ;
2019-08-18 14:18:48 -04:00
status = fh_verify ( rqstp , fhp , S_IFREG ,
may_flags | NFSD_MAY_OWNER_OVERRIDE ) ;
if ( status ! = nfs_ok )
return status ;
2022-11-24 15:09:04 -05:00
inode = d_inode ( fhp - > fh_dentry ) ;
cred = get_current_cred ( ) ;
2019-08-18 14:18:48 -04:00
retry :
2022-10-04 15:41:10 -04:00
rcu_read_lock ( ) ;
2022-11-24 15:09:04 -05:00
nf = nfsd_file_lookup_locked ( net , cred , inode , need , want_gc ) ;
2022-10-04 15:41:10 -04:00
rcu_read_unlock ( ) ;
2022-12-11 06:19:33 -05:00
if ( nf ) {
2023-01-05 07:15:12 -05:00
/*
* If the nf is on the LRU then it holds an extra reference
* that must be put if it ' s removed . It had better not be
* the last one however , since we should hold another .
*/
2022-12-11 06:19:33 -05:00
if ( nfsd_file_lru_remove ( nf ) )
WARN_ON_ONCE ( refcount_dec_and_test ( & nf - > nf_ref ) ) ;
2019-08-18 14:18:48 -04:00
goto wait_for_construction ;
2022-12-11 06:19:33 -05:00
}
2019-08-18 14:18:48 -04:00
2022-11-24 15:09:04 -05:00
new = nfsd_file_alloc ( net , inode , need , want_gc ) ;
if ( ! new ) {
2022-07-08 14:25:37 -04:00
status = nfserr_jukebox ;
2023-01-05 07:15:11 -05:00
goto out ;
2019-08-18 14:18:48 -04:00
}
2022-11-24 15:09:04 -05:00
rcu_read_lock ( ) ;
spin_lock ( & inode - > i_lock ) ;
nf = nfsd_file_lookup_locked ( net , cred , inode , need , want_gc ) ;
if ( unlikely ( nf ) ) {
spin_unlock ( & inode - > i_lock ) ;
rcu_read_unlock ( ) ;
nfsd_file_slab_free ( & new - > nf_rcu ) ;
goto wait_for_construction ;
}
nf = new ;
ret = rhltable_insert ( & nfsd_file_rhltable , & nf - > nf_rlist ,
nfsd_file_rhash_params ) ;
spin_unlock ( & inode - > i_lock ) ;
rcu_read_unlock ( ) ;
2022-10-04 15:41:10 -04:00
if ( likely ( ret = = 0 ) )
2019-08-18 14:18:48 -04:00
goto open_file ;
2022-10-04 15:41:10 -04:00
if ( ret = = - EEXIST )
goto retry ;
2022-11-24 15:09:04 -05:00
trace_nfsd_file_insert_err ( rqstp , inode , may_flags , ret ) ;
2022-10-04 15:41:10 -04:00
status = nfserr_jukebox ;
2023-01-05 07:15:11 -05:00
goto construction_err ;
2019-08-18 14:18:48 -04:00
wait_for_construction :
wait_on_bit ( & nf - > nf_flags , NFSD_FILE_PENDING , TASK_UNINTERRUPTIBLE ) ;
/* Did construction of this file fail? */
if ( ! test_bit ( NFSD_FILE_HASHED , & nf - > nf_flags ) ) {
2022-11-24 15:09:04 -05:00
trace_nfsd_file_cons_err ( rqstp , inode , may_flags , nf ) ;
2022-10-04 15:41:10 -04:00
if ( ! open_retry ) {
2020-01-06 13:18:03 -05:00
status = nfserr_jukebox ;
2023-01-05 07:15:11 -05:00
goto construction_err ;
2020-01-06 13:18:03 -05:00
}
2022-10-04 15:41:10 -04:00
open_retry = false ;
2019-08-18 14:18:48 -04:00
goto retry ;
}
this_cpu_inc ( nfsd_file_cache_hits ) ;
2022-07-29 17:01:07 -04:00
status = nfserrno ( nfsd_open_break_lease ( file_inode ( nf - > nf_file ) , may_flags ) ) ;
2023-01-05 07:15:11 -05:00
if ( status ! = nfs_ok ) {
nfsd_file_put ( nf ) ;
nf = NULL ;
}
2019-08-18 14:18:48 -04:00
out :
if ( status = = nfs_ok ) {
2023-01-05 14:55:56 -05:00
this_cpu_inc ( nfsd_file_acquisitions ) ;
2023-02-07 12:02:46 -05:00
nfsd_file_check_write_error ( nf ) ;
2019-08-18 14:18:48 -04:00
* pnf = nf ;
}
2022-11-24 15:09:04 -05:00
put_cred ( cred ) ;
trace_nfsd_file_acquire ( rqstp , inode , may_flags , nf , status ) ;
2019-08-18 14:18:48 -04:00
return status ;
2022-07-08 14:25:37 -04:00
2019-08-18 14:18:48 -04:00
open_file :
2022-07-08 14:26:49 -04:00
trace_nfsd_file_alloc ( nf ) ;
2022-11-24 15:09:04 -05:00
nf - > nf_mark = nfsd_file_mark_find_or_create ( nf , inode ) ;
2022-03-30 10:30:54 -04:00
if ( nf - > nf_mark ) {
2023-01-05 14:55:56 -05:00
if ( file ) {
get_file ( file ) ;
nf - > nf_file = file ;
status = nfs_ok ;
trace_nfsd_file_opened ( nf , status ) ;
} else {
2022-03-30 10:30:54 -04:00
status = nfsd_open_verified ( rqstp , fhp , may_flags ,
& nf - > nf_file ) ;
2022-03-27 16:42:20 -04:00
trace_nfsd_file_open ( nf , status ) ;
2023-01-05 14:55:56 -05:00
}
2022-03-30 10:30:54 -04:00
} else
2019-08-18 14:18:48 -04:00
status = nfserr_jukebox ;
/*
* If construction failed , or we raced with a call to unlink ( )
* then unhash .
*/
2022-11-24 15:09:04 -05:00
if ( status ! = nfs_ok | | inode - > i_nlink = = 0 )
2022-12-11 06:19:33 -05:00
nfsd_file_unhash ( nf ) ;
2023-01-05 07:15:09 -05:00
clear_and_wake_up_bit ( NFSD_FILE_PENDING , & nf - > nf_flags ) ;
2023-01-05 07:15:11 -05:00
if ( status = = nfs_ok )
goto out ;
construction_err :
if ( refcount_dec_and_test ( & nf - > nf_ref ) )
nfsd_file_free ( nf ) ;
nf = NULL ;
2019-08-18 14:18:48 -04:00
goto out ;
}
2022-10-28 10:46:51 -04:00
/**
* nfsd_file_acquire_gc - Get a struct nfsd_file with an open file
* @ rqstp : the RPC transaction being executed
* @ fhp : the NFS filehandle of the file to be opened
* @ may_flags : NFSD_MAY_ settings for the file
* @ pnf : OUT : new or found " struct nfsd_file " object
*
* The nfsd_file object returned by this API is reference - counted
* and garbage - collected . The object is retained for a few
* seconds after the final nfsd_file_put ( ) in case the caller
* wants to re - use it .
*
2022-11-24 15:09:04 -05:00
* Return values :
* % nfs_ok - @ pnf points to an nfsd_file with its reference
* count boosted .
*
* On error , an nfsstat value in network byte order is returned .
2022-10-28 10:46:51 -04:00
*/
__be32
nfsd_file_acquire_gc ( struct svc_rqst * rqstp , struct svc_fh * fhp ,
unsigned int may_flags , struct nfsd_file * * pnf )
{
2023-01-05 14:55:56 -05:00
return nfsd_file_do_acquire ( rqstp , fhp , may_flags , NULL , pnf , true ) ;
2022-10-28 10:46:51 -04:00
}
2022-03-30 10:30:54 -04:00
/**
* nfsd_file_acquire - Get a struct nfsd_file with an open file
* @ rqstp : the RPC transaction being executed
* @ fhp : the NFS filehandle of the file to be opened
* @ may_flags : NFSD_MAY_ settings for the file
* @ pnf : OUT : new or found " struct nfsd_file " object
*
2022-10-28 10:46:51 -04:00
* The nfsd_file_object returned by this API is reference - counted
* but not garbage - collected . The object is unhashed after the
* final nfsd_file_put ( ) .
*
2022-11-24 15:09:04 -05:00
* Return values :
* % nfs_ok - @ pnf points to an nfsd_file with its reference
* count boosted .
*
* On error , an nfsstat value in network byte order is returned .
2022-03-30 10:30:54 -04:00
*/
__be32
nfsd_file_acquire ( struct svc_rqst * rqstp , struct svc_fh * fhp ,
unsigned int may_flags , struct nfsd_file * * pnf )
{
2023-01-05 14:55:56 -05:00
return nfsd_file_do_acquire ( rqstp , fhp , may_flags , NULL , pnf , false ) ;
2022-03-30 10:30:54 -04:00
}
/**
2023-01-05 14:55:56 -05:00
* nfsd_file_acquire_opened - Get a struct nfsd_file using existing open file
2022-03-30 10:30:54 -04:00
* @ rqstp : the RPC transaction being executed
* @ fhp : the NFS filehandle of the file just created
* @ may_flags : NFSD_MAY_ settings for the file
2023-01-05 14:55:56 -05:00
* @ file : cached , already - open file ( may be NULL )
2022-03-30 10:30:54 -04:00
* @ pnf : OUT : new or found " struct nfsd_file " object
*
2023-01-05 14:55:56 -05:00
* Acquire a nfsd_file object that is not GC ' ed . If one doesn ' t already exist ,
* and @ file is non - NULL , use it to instantiate a new nfsd_file instead of
* opening a new one .
2022-10-28 10:46:51 -04:00
*
2022-11-24 15:09:04 -05:00
* Return values :
* % nfs_ok - @ pnf points to an nfsd_file with its reference
* count boosted .
*
* On error , an nfsstat value in network byte order is returned .
2022-03-30 10:30:54 -04:00
*/
__be32
2023-01-05 14:55:56 -05:00
nfsd_file_acquire_opened ( struct svc_rqst * rqstp , struct svc_fh * fhp ,
unsigned int may_flags , struct file * file ,
struct nfsd_file * * pnf )
2022-03-30 10:30:54 -04:00
{
2023-01-05 14:55:56 -05:00
return nfsd_file_do_acquire ( rqstp , fhp , may_flags , file , pnf , false ) ;
2022-03-30 10:30:54 -04:00
}
2019-08-18 14:18:48 -04:00
/*
* Note that fields may be added , removed or reordered in the future . Programs
* scraping this file for info should test the labels to ensure they ' re
* getting the correct field .
*/
2022-09-23 00:31:56 +08:00
int nfsd_file_cache_stats_show ( struct seq_file * m , void * v )
2019-08-18 14:18:48 -04:00
{
2022-11-02 14:44:47 -04:00
unsigned long releases = 0 , evictions = 0 ;
2022-07-08 14:24:45 -04:00
unsigned long hits = 0 , acquisitions = 0 ;
2022-07-08 14:26:30 -04:00
unsigned int i , count = 0 , buckets = 0 ;
2022-07-08 14:24:12 -04:00
unsigned long lru = 0 , total_age = 0 ;
2019-08-18 14:18:48 -04:00
2022-07-08 14:26:30 -04:00
/* Serialize with server shutdown */
2019-08-18 14:18:48 -04:00
mutex_lock ( & nfsd_mutex ) ;
2022-07-08 14:26:16 -04:00
if ( test_bit ( NFSD_FILE_CACHE_UP , & nfsd_file_flags ) = = 1 ) {
2022-07-08 14:26:30 -04:00
struct bucket_table * tbl ;
struct rhashtable * ht ;
2022-07-08 14:23:52 -04:00
lru = list_lru_count ( & nfsd_file_lru ) ;
2022-07-08 14:26:30 -04:00
rcu_read_lock ( ) ;
2022-11-24 15:09:04 -05:00
ht = & nfsd_file_rhltable . ht ;
2022-07-08 14:26:30 -04:00
count = atomic_read ( & ht - > nelems ) ;
tbl = rht_dereference_rcu ( ht - > tbl , ht ) ;
buckets = tbl - > size ;
rcu_read_unlock ( ) ;
2019-08-18 14:18:48 -04:00
}
mutex_unlock ( & nfsd_mutex ) ;
2022-07-08 14:23:59 -04:00
for_each_possible_cpu ( i ) {
2019-08-18 14:18:48 -04:00
hits + = per_cpu ( nfsd_file_cache_hits , i ) ;
2022-07-08 14:23:59 -04:00
acquisitions + = per_cpu ( nfsd_file_acquisitions , i ) ;
2022-07-08 14:24:05 -04:00
releases + = per_cpu ( nfsd_file_releases , i ) ;
2022-07-08 14:24:12 -04:00
total_age + = per_cpu ( nfsd_file_total_age , i ) ;
2022-07-08 14:24:38 -04:00
evictions + = per_cpu ( nfsd_file_evictions , i ) ;
2022-07-08 14:23:59 -04:00
}
2019-08-18 14:18:48 -04:00
2022-11-24 15:09:04 -05:00
seq_printf ( m , " total inodes: %u \n " , count ) ;
2022-07-08 14:26:30 -04:00
seq_printf ( m , " hash buckets: %u \n " , buckets ) ;
2022-07-08 14:23:52 -04:00
seq_printf ( m , " lru entries: %lu \n " , lru ) ;
2019-08-18 14:18:48 -04:00
seq_printf ( m , " cache hits: %lu \n " , hits ) ;
2022-07-08 14:23:59 -04:00
seq_printf ( m , " acquisitions: %lu \n " , acquisitions ) ;
2022-07-08 14:24:05 -04:00
seq_printf ( m , " releases: %lu \n " , releases ) ;
2022-07-08 14:24:38 -04:00
seq_printf ( m , " evictions: %lu \n " , evictions ) ;
2022-07-08 14:24:12 -04:00
if ( releases )
seq_printf ( m , " mean age (ms): %ld \n " , total_age / releases ) ;
else
seq_printf ( m , " mean age (ms): - \n " ) ;
2019-08-18 14:18:48 -04:00
return 0 ;
}