1996bc4f4STrond Myklebust // SPDX-License-Identifier: GPL-2.0 2996bc4f4STrond Myklebust /* 3996bc4f4STrond Myklebust * Copyright (c) 2019 Hammerspace Inc 4996bc4f4STrond Myklebust */ 5996bc4f4STrond Myklebust 6996bc4f4STrond Myklebust #include <linux/module.h> 7996bc4f4STrond Myklebust #include <linux/kobject.h> 8996bc4f4STrond Myklebust #include <linux/sysfs.h> 9996bc4f4STrond Myklebust #include <linux/fs.h> 10996bc4f4STrond Myklebust #include <linux/slab.h> 11996bc4f4STrond Myklebust #include <linux/netdevice.h> 12bf11fbdbSTrond Myklebust #include <linux/string.h> 13bf11fbdbSTrond Myklebust #include <linux/nfs_fs.h> 14bf11fbdbSTrond Myklebust #include <linux/rcupdate.h> 15996bc4f4STrond Myklebust 16bf11fbdbSTrond Myklebust #include "nfs4_fs.h" 17bf11fbdbSTrond Myklebust #include "netns.h" 18996bc4f4STrond Myklebust #include "sysfs.h" 19996bc4f4STrond Myklebust 20996bc4f4STrond Myklebust struct kobject *nfs_client_kobj; 21996bc4f4STrond Myklebust static struct kset *nfs_client_kset; 22996bc4f4STrond Myklebust 23996bc4f4STrond Myklebust static void nfs_netns_object_release(struct kobject *kobj) 24996bc4f4STrond Myklebust { 25996bc4f4STrond Myklebust kfree(kobj); 26996bc4f4STrond Myklebust } 27996bc4f4STrond Myklebust 28996bc4f4STrond Myklebust static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type( 29996bc4f4STrond Myklebust struct kobject *kobj) 30996bc4f4STrond Myklebust { 31996bc4f4STrond Myklebust return &net_ns_type_operations; 32996bc4f4STrond Myklebust } 33996bc4f4STrond Myklebust 34996bc4f4STrond Myklebust static struct kobj_type nfs_netns_object_type = { 35996bc4f4STrond Myklebust .release = nfs_netns_object_release, 36996bc4f4STrond Myklebust .sysfs_ops = &kobj_sysfs_ops, 37996bc4f4STrond Myklebust .child_ns_type = nfs_netns_object_child_ns_type, 38996bc4f4STrond Myklebust }; 39996bc4f4STrond Myklebust 40996bc4f4STrond Myklebust static struct kobject *nfs_netns_object_alloc(const char *name, 41996bc4f4STrond Myklebust struct kset *kset, struct kobject *parent) 42996bc4f4STrond Myklebust { 43996bc4f4STrond Myklebust struct kobject *kobj; 44996bc4f4STrond Myklebust 45996bc4f4STrond Myklebust kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); 46996bc4f4STrond Myklebust if (kobj) { 47996bc4f4STrond Myklebust kobj->kset = kset; 48996bc4f4STrond Myklebust if (kobject_init_and_add(kobj, &nfs_netns_object_type, 49996bc4f4STrond Myklebust parent, "%s", name) == 0) 50996bc4f4STrond Myklebust return kobj; 51996bc4f4STrond Myklebust kobject_put(kobj); 52996bc4f4STrond Myklebust } 53996bc4f4STrond Myklebust return NULL; 54996bc4f4STrond Myklebust } 55996bc4f4STrond Myklebust 56996bc4f4STrond Myklebust int nfs_sysfs_init(void) 57996bc4f4STrond Myklebust { 58996bc4f4STrond Myklebust nfs_client_kset = kset_create_and_add("nfs", NULL, fs_kobj); 59996bc4f4STrond Myklebust if (!nfs_client_kset) 60996bc4f4STrond Myklebust return -ENOMEM; 61996bc4f4STrond Myklebust nfs_client_kobj = nfs_netns_object_alloc("net", nfs_client_kset, NULL); 62996bc4f4STrond Myklebust if (!nfs_client_kobj) { 63996bc4f4STrond Myklebust kset_unregister(nfs_client_kset); 64996bc4f4STrond Myklebust nfs_client_kset = NULL; 65996bc4f4STrond Myklebust return -ENOMEM; 66996bc4f4STrond Myklebust } 67996bc4f4STrond Myklebust return 0; 68996bc4f4STrond Myklebust } 69996bc4f4STrond Myklebust 70996bc4f4STrond Myklebust void nfs_sysfs_exit(void) 71996bc4f4STrond Myklebust { 72996bc4f4STrond Myklebust kobject_put(nfs_client_kobj); 73996bc4f4STrond Myklebust kset_unregister(nfs_client_kset); 74996bc4f4STrond Myklebust } 75bf11fbdbSTrond Myklebust 76bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_show(struct kobject *kobj, 77bf11fbdbSTrond Myklebust struct kobj_attribute *attr, char *buf) 78bf11fbdbSTrond Myklebust { 79bf11fbdbSTrond Myklebust struct nfs_netns_client *c = container_of(kobj, 80bf11fbdbSTrond Myklebust struct nfs_netns_client, 81bf11fbdbSTrond Myklebust kobject); 82*094eca37STrond Myklebust ssize_t ret; 83*094eca37STrond Myklebust 84*094eca37STrond Myklebust rcu_read_lock(); 85*094eca37STrond Myklebust ret = scnprintf(buf, PAGE_SIZE, "%s\n", rcu_dereference(c->identifier)); 86*094eca37STrond Myklebust rcu_read_unlock(); 87*094eca37STrond Myklebust return ret; 88bf11fbdbSTrond Myklebust } 89bf11fbdbSTrond Myklebust 90bf11fbdbSTrond Myklebust /* Strip trailing '\n' */ 91bf11fbdbSTrond Myklebust static size_t nfs_string_strip(const char *c, size_t len) 92bf11fbdbSTrond Myklebust { 93bf11fbdbSTrond Myklebust while (len > 0 && c[len-1] == '\n') 94bf11fbdbSTrond Myklebust --len; 95bf11fbdbSTrond Myklebust return len; 96bf11fbdbSTrond Myklebust } 97bf11fbdbSTrond Myklebust 98bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_store(struct kobject *kobj, 99bf11fbdbSTrond Myklebust struct kobj_attribute *attr, 100bf11fbdbSTrond Myklebust const char *buf, size_t count) 101bf11fbdbSTrond Myklebust { 102bf11fbdbSTrond Myklebust struct nfs_netns_client *c = container_of(kobj, 103bf11fbdbSTrond Myklebust struct nfs_netns_client, 104bf11fbdbSTrond Myklebust kobject); 105bf11fbdbSTrond Myklebust const char *old; 106bf11fbdbSTrond Myklebust char *p; 107bf11fbdbSTrond Myklebust size_t len; 108bf11fbdbSTrond Myklebust 109bf11fbdbSTrond Myklebust len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); 110bf11fbdbSTrond Myklebust if (!len) 111bf11fbdbSTrond Myklebust return 0; 112bf11fbdbSTrond Myklebust p = kmemdup_nul(buf, len, GFP_KERNEL); 113bf11fbdbSTrond Myklebust if (!p) 114bf11fbdbSTrond Myklebust return -ENOMEM; 115*094eca37STrond Myklebust old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1); 116bf11fbdbSTrond Myklebust if (old) { 117bf11fbdbSTrond Myklebust synchronize_rcu(); 118bf11fbdbSTrond Myklebust kfree(old); 119bf11fbdbSTrond Myklebust } 120bf11fbdbSTrond Myklebust return count; 121bf11fbdbSTrond Myklebust } 122bf11fbdbSTrond Myklebust 123bf11fbdbSTrond Myklebust static void nfs_netns_client_release(struct kobject *kobj) 124bf11fbdbSTrond Myklebust { 125bf11fbdbSTrond Myklebust struct nfs_netns_client *c = container_of(kobj, 126bf11fbdbSTrond Myklebust struct nfs_netns_client, 127bf11fbdbSTrond Myklebust kobject); 128bf11fbdbSTrond Myklebust 129*094eca37STrond Myklebust kfree(rcu_dereference_raw(c->identifier)); 130bf11fbdbSTrond Myklebust kfree(c); 131bf11fbdbSTrond Myklebust } 132bf11fbdbSTrond Myklebust 133bf11fbdbSTrond Myklebust static const void *nfs_netns_client_namespace(struct kobject *kobj) 134bf11fbdbSTrond Myklebust { 135bf11fbdbSTrond Myklebust return container_of(kobj, struct nfs_netns_client, kobject)->net; 136bf11fbdbSTrond Myklebust } 137bf11fbdbSTrond Myklebust 138bf11fbdbSTrond Myklebust static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, 139bf11fbdbSTrond Myklebust 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); 140bf11fbdbSTrond Myklebust 141bf11fbdbSTrond Myklebust static struct attribute *nfs_netns_client_attrs[] = { 142bf11fbdbSTrond Myklebust &nfs_netns_client_id.attr, 143bf11fbdbSTrond Myklebust NULL, 144bf11fbdbSTrond Myklebust }; 145bf11fbdbSTrond Myklebust 146bf11fbdbSTrond Myklebust static struct kobj_type nfs_netns_client_type = { 147bf11fbdbSTrond Myklebust .release = nfs_netns_client_release, 148bf11fbdbSTrond Myklebust .default_attrs = nfs_netns_client_attrs, 149bf11fbdbSTrond Myklebust .sysfs_ops = &kobj_sysfs_ops, 150bf11fbdbSTrond Myklebust .namespace = nfs_netns_client_namespace, 151bf11fbdbSTrond Myklebust }; 152bf11fbdbSTrond Myklebust 153bf11fbdbSTrond Myklebust static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, 154bf11fbdbSTrond Myklebust struct net *net) 155bf11fbdbSTrond Myklebust { 156bf11fbdbSTrond Myklebust struct nfs_netns_client *p; 157bf11fbdbSTrond Myklebust 158bf11fbdbSTrond Myklebust p = kzalloc(sizeof(*p), GFP_KERNEL); 159bf11fbdbSTrond Myklebust if (p) { 160bf11fbdbSTrond Myklebust p->net = net; 161bf11fbdbSTrond Myklebust p->kobject.kset = nfs_client_kset; 162bf11fbdbSTrond Myklebust if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, 163bf11fbdbSTrond Myklebust parent, "nfs_client") == 0) 164bf11fbdbSTrond Myklebust return p; 165bf11fbdbSTrond Myklebust kobject_put(&p->kobject); 166bf11fbdbSTrond Myklebust } 167bf11fbdbSTrond Myklebust return NULL; 168bf11fbdbSTrond Myklebust } 169bf11fbdbSTrond Myklebust 170bf11fbdbSTrond Myklebust void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) 171bf11fbdbSTrond Myklebust { 172bf11fbdbSTrond Myklebust struct nfs_netns_client *clp; 173bf11fbdbSTrond Myklebust 174bf11fbdbSTrond Myklebust clp = nfs_netns_client_alloc(nfs_client_kobj, net); 175bf11fbdbSTrond Myklebust if (clp) { 176bf11fbdbSTrond Myklebust netns->nfs_client = clp; 177bf11fbdbSTrond Myklebust kobject_uevent(&clp->kobject, KOBJ_ADD); 178bf11fbdbSTrond Myklebust } 179bf11fbdbSTrond Myklebust } 180bf11fbdbSTrond Myklebust 181bf11fbdbSTrond Myklebust void nfs_netns_sysfs_destroy(struct nfs_net *netns) 182bf11fbdbSTrond Myklebust { 183bf11fbdbSTrond Myklebust struct nfs_netns_client *clp = netns->nfs_client; 184bf11fbdbSTrond Myklebust 185bf11fbdbSTrond Myklebust if (clp) { 186bf11fbdbSTrond Myklebust kobject_uevent(&clp->kobject, KOBJ_REMOVE); 187bf11fbdbSTrond Myklebust kobject_del(&clp->kobject); 188bf11fbdbSTrond Myklebust kobject_put(&clp->kobject); 189bf11fbdbSTrond Myklebust netns->nfs_client = NULL; 190bf11fbdbSTrond Myklebust } 191bf11fbdbSTrond Myklebust } 192