2019-01-25 00:10:46 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2019 Hammerspace Inc
*/
# include <linux/module.h>
# include <linux/kobject.h>
# include <linux/sysfs.h>
# include <linux/fs.h>
# include <linux/slab.h>
# include <linux/netdevice.h>
2019-01-30 05:40:10 +03:00
# include <linux/string.h>
# include <linux/nfs_fs.h>
# include <linux/rcupdate.h>
2023-06-15 21:07:30 +03:00
# include <linux/lockd/lockd.h>
2019-01-25 00:10:46 +03:00
2019-01-30 05:40:10 +03:00
# include "nfs4_fs.h"
# include "netns.h"
2019-01-25 00:10:46 +03:00
# include "sysfs.h"
2023-06-15 21:07:22 +03:00
static struct kset * nfs_kset ;
2019-01-25 00:10:46 +03:00
2023-06-15 21:07:24 +03:00
static void nfs_kset_release ( struct kobject * kobj )
{
struct kset * kset = container_of ( kobj , struct kset , kobj ) ;
kfree ( kset ) ;
}
2019-01-25 00:10:46 +03:00
static const struct kobj_ns_type_operations * nfs_netns_object_child_ns_type (
2022-11-21 12:46:45 +03:00
const struct kobject * kobj )
2019-01-25 00:10:46 +03:00
{
return & net_ns_type_operations ;
}
2023-06-15 21:07:24 +03:00
static struct kobj_type nfs_kset_type = {
. release = nfs_kset_release ,
. sysfs_ops = & kobj_sysfs_ops ,
2019-01-25 00:10:46 +03:00
. child_ns_type = nfs_netns_object_child_ns_type ,
} ;
int nfs_sysfs_init ( void )
{
2023-06-15 21:07:24 +03:00
int ret ;
nfs_kset = kzalloc ( sizeof ( * nfs_kset ) , GFP_KERNEL ) ;
2023-06-15 21:07:22 +03:00
if ( ! nfs_kset )
2019-01-25 00:10:46 +03:00
return - ENOMEM ;
2023-06-15 21:07:24 +03:00
ret = kobject_set_name ( & nfs_kset - > kobj , " nfs " ) ;
if ( ret ) {
kfree ( nfs_kset ) ;
return ret ;
}
nfs_kset - > kobj . parent = fs_kobj ;
nfs_kset - > kobj . ktype = & nfs_kset_type ;
nfs_kset - > kobj . kset = NULL ;
ret = kset_register ( nfs_kset ) ;
if ( ret ) {
kfree ( nfs_kset ) ;
return ret ;
}
2019-01-25 00:10:46 +03:00
return 0 ;
}
void nfs_sysfs_exit ( void )
{
2023-06-15 21:07:22 +03:00
kset_unregister ( nfs_kset ) ;
2019-01-25 00:10:46 +03:00
}
2019-01-30 05:40:10 +03:00
static ssize_t nfs_netns_identifier_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct nfs_netns_client * c = container_of ( kobj ,
struct nfs_netns_client ,
kobject ) ;
2020-10-14 22:22:11 +03:00
ssize_t ret ;
rcu_read_lock ( ) ;
2022-12-05 12:19:16 +03:00
ret = sysfs_emit ( buf , " %s \n " , rcu_dereference ( c - > identifier ) ) ;
2020-10-14 22:22:11 +03:00
rcu_read_unlock ( ) ;
return ret ;
2019-01-30 05:40:10 +03:00
}
/* Strip trailing '\n' */
static size_t nfs_string_strip ( const char * c , size_t len )
{
while ( len > 0 & & c [ len - 1 ] = = ' \n ' )
- - len ;
return len ;
}
static ssize_t nfs_netns_identifier_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct nfs_netns_client * c = container_of ( kobj ,
struct nfs_netns_client ,
kobject ) ;
const char * old ;
char * p ;
size_t len ;
len = nfs_string_strip ( buf , min_t ( size_t , count , CONTAINER_ID_MAXLEN ) ) ;
if ( ! len )
return 0 ;
p = kmemdup_nul ( buf , len , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
2020-10-14 22:22:11 +03:00
old = rcu_dereference_protected ( xchg ( & c - > identifier , ( char __rcu * ) p ) , 1 ) ;
2019-01-30 05:40:10 +03:00
if ( old ) {
synchronize_rcu ( ) ;
kfree ( old ) ;
}
return count ;
}
static void nfs_netns_client_release ( struct kobject * kobj )
{
struct nfs_netns_client * c = container_of ( kobj ,
struct nfs_netns_client ,
kobject ) ;
2020-10-14 22:22:11 +03:00
kfree ( rcu_dereference_raw ( c - > identifier ) ) ;
2019-01-30 05:40:10 +03:00
}
2022-11-21 12:46:45 +03:00
static const void * nfs_netns_client_namespace ( const struct kobject * kobj )
2019-01-30 05:40:10 +03:00
{
return container_of ( kobj , struct nfs_netns_client , kobject ) - > net ;
}
static struct kobj_attribute nfs_netns_client_id = __ATTR ( identifier ,
0644 , nfs_netns_identifier_show , nfs_netns_identifier_store ) ;
static struct attribute * nfs_netns_client_attrs [ ] = {
& nfs_netns_client_id . attr ,
NULL ,
} ;
2021-12-28 17:41:38 +03:00
ATTRIBUTE_GROUPS ( nfs_netns_client ) ;
2019-01-30 05:40:10 +03:00
static struct kobj_type nfs_netns_client_type = {
. release = nfs_netns_client_release ,
2021-12-28 17:41:38 +03:00
. default_groups = nfs_netns_client_groups ,
2019-01-30 05:40:10 +03:00
. sysfs_ops = & kobj_sysfs_ops ,
. namespace = nfs_netns_client_namespace ,
} ;
2023-06-15 21:07:25 +03:00
static void nfs_netns_object_release ( struct kobject * kobj )
{
struct nfs_netns_client * c = container_of ( kobj ,
struct nfs_netns_client ,
nfs_net_kobj ) ;
kfree ( c ) ;
}
static const void * nfs_netns_namespace ( const struct kobject * kobj )
{
return container_of ( kobj , struct nfs_netns_client , nfs_net_kobj ) - > net ;
}
static struct kobj_type nfs_netns_object_type = {
. release = nfs_netns_object_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. namespace = nfs_netns_namespace ,
} ;
2019-01-30 05:40:10 +03:00
static struct nfs_netns_client * nfs_netns_client_alloc ( struct kobject * parent ,
struct net * net )
{
struct nfs_netns_client * p ;
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( p ) {
p - > net = net ;
2023-06-15 21:07:22 +03:00
p - > kobject . kset = nfs_kset ;
2023-06-15 21:07:25 +03:00
p - > nfs_net_kobj . kset = nfs_kset ;
if ( kobject_init_and_add ( & p - > nfs_net_kobj , & nfs_netns_object_type ,
parent , " net " ) ! = 0 ) {
kobject_put ( & p - > nfs_net_kobj ) ;
return NULL ;
}
2019-01-30 05:40:10 +03:00
if ( kobject_init_and_add ( & p - > kobject , & nfs_netns_client_type ,
2023-06-15 21:07:25 +03:00
& p - > nfs_net_kobj , " nfs_client " ) = = 0 )
2019-01-30 05:40:10 +03:00
return p ;
2023-06-15 21:07:25 +03:00
2019-01-30 05:40:10 +03:00
kobject_put ( & p - > kobject ) ;
}
return NULL ;
}
void nfs_netns_sysfs_setup ( struct nfs_net * netns , struct net * net )
{
struct nfs_netns_client * clp ;
2023-06-15 21:07:25 +03:00
clp = nfs_netns_client_alloc ( & nfs_kset - > kobj , net ) ;
2019-01-30 05:40:10 +03:00
if ( clp ) {
netns - > nfs_client = clp ;
kobject_uevent ( & clp - > kobject , KOBJ_ADD ) ;
}
}
void nfs_netns_sysfs_destroy ( struct nfs_net * netns )
{
struct nfs_netns_client * clp = netns - > nfs_client ;
if ( clp ) {
kobject_uevent ( & clp - > kobject , KOBJ_REMOVE ) ;
kobject_del ( & clp - > kobject ) ;
kobject_put ( & clp - > kobject ) ;
2023-06-15 21:07:25 +03:00
kobject_del ( & clp - > nfs_net_kobj ) ;
kobject_put ( & clp - > nfs_net_kobj ) ;
2019-01-30 05:40:10 +03:00
netns - > nfs_client = NULL ;
}
}
2023-06-15 21:07:26 +03:00
2023-06-15 21:07:31 +03:00
static bool shutdown_match_client ( const struct rpc_task * task , const void * data )
{
return true ;
}
static void shutdown_client ( struct rpc_clnt * clnt )
{
clnt - > cl_shutdown = 1 ;
rpc_cancel_tasks ( clnt , - EIO , shutdown_match_client , NULL ) ;
}
2023-06-15 21:07:30 +03:00
static ssize_t
shutdown_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct nfs_server * server = container_of ( kobj , struct nfs_server , kobj ) ;
bool shutdown = server - > flags & NFS_MOUNT_SHUTDOWN ;
return sysfs_emit ( buf , " %d \n " , shutdown ) ;
}
static ssize_t
shutdown_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct nfs_server * server ;
int ret , val ;
server = container_of ( kobj , struct nfs_server , kobj ) ;
ret = kstrtoint ( buf , 0 , & val ) ;
if ( ret )
return ret ;
if ( val ! = 1 )
return - EINVAL ;
/* already shut down? */
if ( server - > flags & NFS_MOUNT_SHUTDOWN )
goto out ;
server - > flags | = NFS_MOUNT_SHUTDOWN ;
2023-06-15 21:07:31 +03:00
shutdown_client ( server - > client ) ;
shutdown_client ( server - > nfs_client - > cl_rpcclient ) ;
2023-06-15 21:07:30 +03:00
if ( ! IS_ERR ( server - > client_acl ) )
2023-06-15 21:07:31 +03:00
shutdown_client ( server - > client_acl ) ;
2023-06-15 21:07:30 +03:00
if ( server - > nlm_host )
2023-06-15 21:07:31 +03:00
shutdown_client ( server - > nlm_host - > h_rpcclnt ) ;
2023-06-15 21:07:30 +03:00
out :
return count ;
}
static struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW ( shutdown ) ;
2023-06-15 21:07:27 +03:00
# define RPC_CLIENT_NAME_SIZE 64
void nfs_sysfs_link_rpc_client ( struct nfs_server * server ,
struct rpc_clnt * clnt , const char * uniq )
{
char name [ RPC_CLIENT_NAME_SIZE ] ;
int ret ;
strcpy ( name , clnt - > cl_program - > name ) ;
strcat ( name , uniq ? uniq : " " ) ;
strcat ( name , " _client " ) ;
ret = sysfs_create_link_nowarn ( & server - > kobj ,
& clnt - > cl_sysfs - > kobject , name ) ;
if ( ret < 0 )
pr_warn ( " NFS: can't create link to %s in sysfs (%d) \n " ,
name , ret ) ;
}
EXPORT_SYMBOL_GPL ( nfs_sysfs_link_rpc_client ) ;
2023-06-15 21:07:26 +03:00
static void nfs_sysfs_sb_release ( struct kobject * kobj )
{
/* no-op: why? see lib/kobject.c kobject_cleanup() */
}
static const void * nfs_netns_server_namespace ( const struct kobject * kobj )
{
return container_of ( kobj , struct nfs_server , kobj ) - > nfs_client - > cl_net ;
}
static struct kobj_type nfs_sb_ktype = {
. release = nfs_sysfs_sb_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. namespace = nfs_netns_server_namespace ,
. child_ns_type = nfs_netns_object_child_ns_type ,
} ;
void nfs_sysfs_add_server ( struct nfs_server * server )
{
int ret ;
ret = kobject_init_and_add ( & server - > kobj , & nfs_sb_ktype ,
& nfs_kset - > kobj , " server-%d " , server - > s_sysfs_id ) ;
2023-06-15 21:07:30 +03:00
if ( ret < 0 ) {
2023-06-15 21:07:26 +03:00
pr_warn ( " NFS: nfs sysfs add server-%d failed (%d) \n " ,
server - > s_sysfs_id , ret ) ;
2023-06-15 21:07:30 +03:00
return ;
}
ret = sysfs_create_file_ns ( & server - > kobj , & nfs_sysfs_attr_shutdown . attr ,
nfs_netns_server_namespace ( & server - > kobj ) ) ;
if ( ret < 0 )
pr_warn ( " NFS: sysfs_create_file_ns for server-%d failed (%d) \n " ,
server - > s_sysfs_id , ret ) ;
2023-06-15 21:07:26 +03:00
}
EXPORT_SYMBOL_GPL ( nfs_sysfs_add_server ) ;
void nfs_sysfs_move_server_to_sb ( struct super_block * s )
{
struct nfs_server * server = s - > s_fs_info ;
int ret ;
ret = kobject_rename ( & server - > kobj , s - > s_id ) ;
if ( ret < 0 )
pr_warn ( " NFS: rename sysfs %s failed (%d) \n " ,
server - > kobj . name , ret ) ;
}
void nfs_sysfs_move_sb_to_server ( struct nfs_server * server )
{
const char * s ;
int ret = - ENOMEM ;
s = kasprintf ( GFP_KERNEL , " server-%d " , server - > s_sysfs_id ) ;
2023-07-10 21:41:58 +03:00
if ( s ) {
2023-06-15 21:07:26 +03:00
ret = kobject_rename ( & server - > kobj , s ) ;
2023-07-10 21:41:58 +03:00
kfree ( s ) ;
}
2023-06-15 21:07:26 +03:00
if ( ret < 0 )
pr_warn ( " NFS: rename sysfs %s failed (%d) \n " ,
server - > kobj . name , ret ) ;
}
/* unlink, not dec-ref */
void nfs_sysfs_remove_server ( struct nfs_server * server )
{
kobject_del ( & server - > kobj ) ;
}