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>
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"
struct kobject * nfs_client_kobj ;
static struct kset * nfs_client_kset ;
static void nfs_netns_object_release ( struct kobject * kobj )
{
kfree ( kobj ) ;
}
static const struct kobj_ns_type_operations * nfs_netns_object_child_ns_type (
struct kobject * kobj )
{
return & net_ns_type_operations ;
}
static struct kobj_type nfs_netns_object_type = {
. release = nfs_netns_object_release ,
. sysfs_ops = & kobj_sysfs_ops ,
. child_ns_type = nfs_netns_object_child_ns_type ,
} ;
static struct kobject * nfs_netns_object_alloc ( const char * name ,
struct kset * kset , struct kobject * parent )
{
struct kobject * kobj ;
kobj = kzalloc ( sizeof ( * kobj ) , GFP_KERNEL ) ;
if ( kobj ) {
kobj - > kset = kset ;
if ( kobject_init_and_add ( kobj , & nfs_netns_object_type ,
parent , " %s " , name ) = = 0 )
return kobj ;
kobject_put ( kobj ) ;
}
return NULL ;
}
int nfs_sysfs_init ( void )
{
nfs_client_kset = kset_create_and_add ( " nfs " , NULL , fs_kobj ) ;
if ( ! nfs_client_kset )
return - ENOMEM ;
nfs_client_kobj = nfs_netns_object_alloc ( " net " , nfs_client_kset , NULL ) ;
if ( ! nfs_client_kobj ) {
kset_unregister ( nfs_client_kset ) ;
nfs_client_kset = NULL ;
return - ENOMEM ;
}
return 0 ;
}
void nfs_sysfs_exit ( void )
{
kobject_put ( nfs_client_kobj ) ;
kset_unregister ( nfs_client_kset ) ;
}
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 ( ) ;
ret = scnprintf ( buf , PAGE_SIZE , " %s \n " , rcu_dereference ( c - > identifier ) ) ;
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
kfree ( c ) ;
}
static const void * nfs_netns_client_namespace ( struct kobject * kobj )
{
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 ,
} ;
static struct kobj_type nfs_netns_client_type = {
. release = nfs_netns_client_release ,
. default_attrs = nfs_netns_client_attrs ,
. sysfs_ops = & kobj_sysfs_ops ,
. namespace = nfs_netns_client_namespace ,
} ;
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 ;
p - > kobject . kset = nfs_client_kset ;
if ( kobject_init_and_add ( & p - > kobject , & nfs_netns_client_type ,
parent , " nfs_client " ) = = 0 )
return p ;
kobject_put ( & p - > kobject ) ;
}
return NULL ;
}
void nfs_netns_sysfs_setup ( struct nfs_net * netns , struct net * net )
{
struct nfs_netns_client * clp ;
clp = nfs_netns_client_alloc ( nfs_client_kobj , net ) ;
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 ) ;
netns - > nfs_client = NULL ;
}
}