162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2019 Hammerspace Inc 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/kobject.h> 862306a36Sopenharmony_ci#include <linux/sysfs.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1462306a36Sopenharmony_ci#include <linux/rcupdate.h> 1562306a36Sopenharmony_ci#include <linux/lockd/lockd.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "nfs4_fs.h" 1862306a36Sopenharmony_ci#include "netns.h" 1962306a36Sopenharmony_ci#include "sysfs.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct kset *nfs_kset; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void nfs_kset_release(struct kobject *kobj) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct kset *kset = container_of(kobj, struct kset, kobj); 2662306a36Sopenharmony_ci kfree(kset); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type( 3062306a36Sopenharmony_ci const struct kobject *kobj) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return &net_ns_type_operations; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct kobj_type nfs_kset_type = { 3662306a36Sopenharmony_ci .release = nfs_kset_release, 3762306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 3862306a36Sopenharmony_ci .child_ns_type = nfs_netns_object_child_ns_type, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint nfs_sysfs_init(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci nfs_kset = kzalloc(sizeof(*nfs_kset), GFP_KERNEL); 4662306a36Sopenharmony_ci if (!nfs_kset) 4762306a36Sopenharmony_ci return -ENOMEM; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = kobject_set_name(&nfs_kset->kobj, "nfs"); 5062306a36Sopenharmony_ci if (ret) { 5162306a36Sopenharmony_ci kfree(nfs_kset); 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci nfs_kset->kobj.parent = fs_kobj; 5662306a36Sopenharmony_ci nfs_kset->kobj.ktype = &nfs_kset_type; 5762306a36Sopenharmony_ci nfs_kset->kobj.kset = NULL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = kset_register(nfs_kset); 6062306a36Sopenharmony_ci if (ret) { 6162306a36Sopenharmony_ci kfree(nfs_kset); 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_civoid nfs_sysfs_exit(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci kset_unregister(nfs_kset); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic ssize_t nfs_netns_identifier_show(struct kobject *kobj, 7462306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct nfs_netns_client *c = container_of(kobj, 7762306a36Sopenharmony_ci struct nfs_netns_client, 7862306a36Sopenharmony_ci kobject); 7962306a36Sopenharmony_ci ssize_t ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci rcu_read_lock(); 8262306a36Sopenharmony_ci ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier)); 8362306a36Sopenharmony_ci rcu_read_unlock(); 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Strip trailing '\n' */ 8862306a36Sopenharmony_cistatic size_t nfs_string_strip(const char *c, size_t len) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci while (len > 0 && c[len-1] == '\n') 9162306a36Sopenharmony_ci --len; 9262306a36Sopenharmony_ci return len; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic ssize_t nfs_netns_identifier_store(struct kobject *kobj, 9662306a36Sopenharmony_ci struct kobj_attribute *attr, 9762306a36Sopenharmony_ci const char *buf, size_t count) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct nfs_netns_client *c = container_of(kobj, 10062306a36Sopenharmony_ci struct nfs_netns_client, 10162306a36Sopenharmony_ci kobject); 10262306a36Sopenharmony_ci const char *old; 10362306a36Sopenharmony_ci char *p; 10462306a36Sopenharmony_ci size_t len; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); 10762306a36Sopenharmony_ci if (!len) 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci p = kmemdup_nul(buf, len, GFP_KERNEL); 11062306a36Sopenharmony_ci if (!p) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1); 11362306a36Sopenharmony_ci if (old) { 11462306a36Sopenharmony_ci synchronize_rcu(); 11562306a36Sopenharmony_ci kfree(old); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci return count; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void nfs_netns_client_release(struct kobject *kobj) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct nfs_netns_client *c = container_of(kobj, 12362306a36Sopenharmony_ci struct nfs_netns_client, 12462306a36Sopenharmony_ci kobject); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci kfree(rcu_dereference_raw(c->identifier)); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const void *nfs_netns_client_namespace(const struct kobject *kobj) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return container_of(kobj, struct nfs_netns_client, kobject)->net; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, 13562306a36Sopenharmony_ci 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct attribute *nfs_netns_client_attrs[] = { 13862306a36Sopenharmony_ci &nfs_netns_client_id.attr, 13962306a36Sopenharmony_ci NULL, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ciATTRIBUTE_GROUPS(nfs_netns_client); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct kobj_type nfs_netns_client_type = { 14462306a36Sopenharmony_ci .release = nfs_netns_client_release, 14562306a36Sopenharmony_ci .default_groups = nfs_netns_client_groups, 14662306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 14762306a36Sopenharmony_ci .namespace = nfs_netns_client_namespace, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void nfs_netns_object_release(struct kobject *kobj) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct nfs_netns_client *c = container_of(kobj, 15362306a36Sopenharmony_ci struct nfs_netns_client, 15462306a36Sopenharmony_ci nfs_net_kobj); 15562306a36Sopenharmony_ci kfree(c); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const void *nfs_netns_namespace(const struct kobject *kobj) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return container_of(kobj, struct nfs_netns_client, nfs_net_kobj)->net; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic struct kobj_type nfs_netns_object_type = { 16462306a36Sopenharmony_ci .release = nfs_netns_object_release, 16562306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 16662306a36Sopenharmony_ci .namespace = nfs_netns_namespace, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, 17062306a36Sopenharmony_ci struct net *net) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct nfs_netns_client *p; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 17562306a36Sopenharmony_ci if (p) { 17662306a36Sopenharmony_ci p->net = net; 17762306a36Sopenharmony_ci p->kobject.kset = nfs_kset; 17862306a36Sopenharmony_ci p->nfs_net_kobj.kset = nfs_kset; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (kobject_init_and_add(&p->nfs_net_kobj, &nfs_netns_object_type, 18162306a36Sopenharmony_ci parent, "net") != 0) { 18262306a36Sopenharmony_ci kobject_put(&p->nfs_net_kobj); 18362306a36Sopenharmony_ci return NULL; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, 18762306a36Sopenharmony_ci &p->nfs_net_kobj, "nfs_client") == 0) 18862306a36Sopenharmony_ci return p; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci kobject_put(&p->kobject); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci return NULL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_civoid nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct nfs_netns_client *clp; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci clp = nfs_netns_client_alloc(&nfs_kset->kobj, net); 20062306a36Sopenharmony_ci if (clp) { 20162306a36Sopenharmony_ci netns->nfs_client = clp; 20262306a36Sopenharmony_ci kobject_uevent(&clp->kobject, KOBJ_ADD); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_civoid nfs_netns_sysfs_destroy(struct nfs_net *netns) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct nfs_netns_client *clp = netns->nfs_client; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (clp) { 21162306a36Sopenharmony_ci kobject_uevent(&clp->kobject, KOBJ_REMOVE); 21262306a36Sopenharmony_ci kobject_del(&clp->kobject); 21362306a36Sopenharmony_ci kobject_put(&clp->kobject); 21462306a36Sopenharmony_ci kobject_del(&clp->nfs_net_kobj); 21562306a36Sopenharmony_ci kobject_put(&clp->nfs_net_kobj); 21662306a36Sopenharmony_ci netns->nfs_client = NULL; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic bool shutdown_match_client(const struct rpc_task *task, const void *data) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci return true; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void shutdown_client(struct rpc_clnt *clnt) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci clnt->cl_shutdown = 1; 22862306a36Sopenharmony_ci rpc_cancel_tasks(clnt, -EIO, shutdown_match_client, NULL); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic ssize_t 23262306a36Sopenharmony_cishutdown_show(struct kobject *kobj, struct kobj_attribute *attr, 23362306a36Sopenharmony_ci char *buf) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct nfs_server *server = container_of(kobj, struct nfs_server, kobj); 23662306a36Sopenharmony_ci bool shutdown = server->flags & NFS_MOUNT_SHUTDOWN; 23762306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", shutdown); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic ssize_t 24162306a36Sopenharmony_cishutdown_store(struct kobject *kobj, struct kobj_attribute *attr, 24262306a36Sopenharmony_ci const char *buf, size_t count) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct nfs_server *server; 24562306a36Sopenharmony_ci int ret, val; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci server = container_of(kobj, struct nfs_server, kobj); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &val); 25062306a36Sopenharmony_ci if (ret) 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (val != 1) 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* already shut down? */ 25762306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_SHUTDOWN) 25862306a36Sopenharmony_ci goto out; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci server->flags |= NFS_MOUNT_SHUTDOWN; 26162306a36Sopenharmony_ci shutdown_client(server->client); 26262306a36Sopenharmony_ci shutdown_client(server->nfs_client->cl_rpcclient); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!IS_ERR(server->client_acl)) 26562306a36Sopenharmony_ci shutdown_client(server->client_acl); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (server->nlm_host) 26862306a36Sopenharmony_ci shutdown_client(server->nlm_host->h_rpcclnt); 26962306a36Sopenharmony_ciout: 27062306a36Sopenharmony_ci return count; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW(shutdown); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci#define RPC_CLIENT_NAME_SIZE 64 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_civoid nfs_sysfs_link_rpc_client(struct nfs_server *server, 27862306a36Sopenharmony_ci struct rpc_clnt *clnt, const char *uniq) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci char name[RPC_CLIENT_NAME_SIZE]; 28162306a36Sopenharmony_ci int ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci strcpy(name, clnt->cl_program->name); 28462306a36Sopenharmony_ci strcat(name, uniq ? uniq : ""); 28562306a36Sopenharmony_ci strcat(name, "_client"); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = sysfs_create_link_nowarn(&server->kobj, 28862306a36Sopenharmony_ci &clnt->cl_sysfs->kobject, name); 28962306a36Sopenharmony_ci if (ret < 0) 29062306a36Sopenharmony_ci pr_warn("NFS: can't create link to %s in sysfs (%d)\n", 29162306a36Sopenharmony_ci name, ret); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_sysfs_link_rpc_client); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void nfs_sysfs_sb_release(struct kobject *kobj) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci /* no-op: why? see lib/kobject.c kobject_cleanup() */ 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic const void *nfs_netns_server_namespace(const struct kobject *kobj) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return container_of(kobj, struct nfs_server, kobj)->nfs_client->cl_net; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic struct kobj_type nfs_sb_ktype = { 30662306a36Sopenharmony_ci .release = nfs_sysfs_sb_release, 30762306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 30862306a36Sopenharmony_ci .namespace = nfs_netns_server_namespace, 30962306a36Sopenharmony_ci .child_ns_type = nfs_netns_object_child_ns_type, 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_civoid nfs_sysfs_add_server(struct nfs_server *server) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = kobject_init_and_add(&server->kobj, &nfs_sb_ktype, 31762306a36Sopenharmony_ci &nfs_kset->kobj, "server-%d", server->s_sysfs_id); 31862306a36Sopenharmony_ci if (ret < 0) { 31962306a36Sopenharmony_ci pr_warn("NFS: nfs sysfs add server-%d failed (%d)\n", 32062306a36Sopenharmony_ci server->s_sysfs_id, ret); 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_shutdown.attr, 32462306a36Sopenharmony_ci nfs_netns_server_namespace(&server->kobj)); 32562306a36Sopenharmony_ci if (ret < 0) 32662306a36Sopenharmony_ci pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n", 32762306a36Sopenharmony_ci server->s_sysfs_id, ret); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_sysfs_add_server); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_civoid nfs_sysfs_move_server_to_sb(struct super_block *s) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct nfs_server *server = s->s_fs_info; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = kobject_rename(&server->kobj, s->s_id); 33762306a36Sopenharmony_ci if (ret < 0) 33862306a36Sopenharmony_ci pr_warn("NFS: rename sysfs %s failed (%d)\n", 33962306a36Sopenharmony_ci server->kobj.name, ret); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_civoid nfs_sysfs_move_sb_to_server(struct nfs_server *server) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci const char *s; 34562306a36Sopenharmony_ci int ret = -ENOMEM; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci s = kasprintf(GFP_KERNEL, "server-%d", server->s_sysfs_id); 34862306a36Sopenharmony_ci if (s) { 34962306a36Sopenharmony_ci ret = kobject_rename(&server->kobj, s); 35062306a36Sopenharmony_ci kfree(s); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci if (ret < 0) 35362306a36Sopenharmony_ci pr_warn("NFS: rename sysfs %s failed (%d)\n", 35462306a36Sopenharmony_ci server->kobj.name, ret); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* unlink, not dec-ref */ 35862306a36Sopenharmony_civoid nfs_sysfs_remove_server(struct nfs_server *server) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci kobject_del(&server->kobj); 36162306a36Sopenharmony_ci} 362