1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2019 Hammerspace Inc 4 */ 5 6 #include <linux/module.h> 7 #include <linux/kobject.h> 8 #include <linux/sysfs.h> 9 #include <linux/fs.h> 10 #include <linux/slab.h> 11 #include <linux/netdevice.h> 12 #include <linux/string.h> 13 #include <linux/nfs_fs.h> 14 #include <linux/rcupdate.h> 15 #include <linux/lockd/lockd.h> 16 17 #include "nfs4_fs.h" 18 #include "netns.h" 19 #include "sysfs.h" 20 21 static struct kset *nfs_kset; 22 23 static void nfs_kset_release(struct kobject *kobj) 24 { 25 struct kset *kset = container_of(kobj, struct kset, kobj); 26 kfree(kset); 27 } 28 29 static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type( 30 const struct kobject *kobj) 31 { 32 return &net_ns_type_operations; 33 } 34 35 static struct kobj_type nfs_kset_type = { 36 .release = nfs_kset_release, 37 .sysfs_ops = &kobj_sysfs_ops, 38 .child_ns_type = nfs_netns_object_child_ns_type, 39 }; 40 41 int nfs_sysfs_init(void) 42 { 43 int ret; 44 45 nfs_kset = kzalloc(sizeof(*nfs_kset), GFP_KERNEL); 46 if (!nfs_kset) 47 return -ENOMEM; 48 49 ret = kobject_set_name(&nfs_kset->kobj, "nfs"); 50 if (ret) { 51 kfree(nfs_kset); 52 return ret; 53 } 54 55 nfs_kset->kobj.parent = fs_kobj; 56 nfs_kset->kobj.ktype = &nfs_kset_type; 57 nfs_kset->kobj.kset = NULL; 58 59 ret = kset_register(nfs_kset); 60 if (ret) { 61 kfree(nfs_kset); 62 return ret; 63 } 64 65 return 0; 66 } 67 68 void nfs_sysfs_exit(void) 69 { 70 kset_unregister(nfs_kset); 71 } 72 73 static ssize_t nfs_netns_identifier_show(struct kobject *kobj, 74 struct kobj_attribute *attr, char *buf) 75 { 76 struct nfs_netns_client *c = container_of(kobj, 77 struct nfs_netns_client, 78 kobject); 79 ssize_t ret; 80 81 rcu_read_lock(); 82 ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier)); 83 rcu_read_unlock(); 84 return ret; 85 } 86 87 /* Strip trailing '\n' */ 88 static size_t nfs_string_strip(const char *c, size_t len) 89 { 90 while (len > 0 && c[len-1] == '\n') 91 --len; 92 return len; 93 } 94 95 static ssize_t nfs_netns_identifier_store(struct kobject *kobj, 96 struct kobj_attribute *attr, 97 const char *buf, size_t count) 98 { 99 struct nfs_netns_client *c = container_of(kobj, 100 struct nfs_netns_client, 101 kobject); 102 const char *old; 103 char *p; 104 size_t len; 105 106 len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); 107 if (!len) 108 return 0; 109 p = kmemdup_nul(buf, len, GFP_KERNEL); 110 if (!p) 111 return -ENOMEM; 112 old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1); 113 if (old) { 114 synchronize_rcu(); 115 kfree(old); 116 } 117 return count; 118 } 119 120 static void nfs_netns_client_release(struct kobject *kobj) 121 { 122 struct nfs_netns_client *c = container_of(kobj, 123 struct nfs_netns_client, 124 kobject); 125 126 kfree(rcu_dereference_raw(c->identifier)); 127 } 128 129 static const void *nfs_netns_client_namespace(const struct kobject *kobj) 130 { 131 return container_of(kobj, struct nfs_netns_client, kobject)->net; 132 } 133 134 static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, 135 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); 136 137 static struct attribute *nfs_netns_client_attrs[] = { 138 &nfs_netns_client_id.attr, 139 NULL, 140 }; 141 ATTRIBUTE_GROUPS(nfs_netns_client); 142 143 static struct kobj_type nfs_netns_client_type = { 144 .release = nfs_netns_client_release, 145 .default_groups = nfs_netns_client_groups, 146 .sysfs_ops = &kobj_sysfs_ops, 147 .namespace = nfs_netns_client_namespace, 148 }; 149 150 static void nfs_netns_object_release(struct kobject *kobj) 151 { 152 struct nfs_netns_client *c = container_of(kobj, 153 struct nfs_netns_client, 154 nfs_net_kobj); 155 kfree(c); 156 } 157 158 static const void *nfs_netns_namespace(const struct kobject *kobj) 159 { 160 return container_of(kobj, struct nfs_netns_client, nfs_net_kobj)->net; 161 } 162 163 static struct kobj_type nfs_netns_object_type = { 164 .release = nfs_netns_object_release, 165 .sysfs_ops = &kobj_sysfs_ops, 166 .namespace = nfs_netns_namespace, 167 }; 168 169 static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, 170 struct net *net) 171 { 172 struct nfs_netns_client *p; 173 174 p = kzalloc(sizeof(*p), GFP_KERNEL); 175 if (p) { 176 p->net = net; 177 p->kobject.kset = nfs_kset; 178 p->nfs_net_kobj.kset = nfs_kset; 179 180 if (kobject_init_and_add(&p->nfs_net_kobj, &nfs_netns_object_type, 181 parent, "net") != 0) { 182 kobject_put(&p->nfs_net_kobj); 183 return NULL; 184 } 185 186 if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, 187 &p->nfs_net_kobj, "nfs_client") == 0) 188 return p; 189 190 kobject_put(&p->kobject); 191 } 192 return NULL; 193 } 194 195 void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) 196 { 197 struct nfs_netns_client *clp; 198 199 clp = nfs_netns_client_alloc(&nfs_kset->kobj, net); 200 if (clp) { 201 netns->nfs_client = clp; 202 kobject_uevent(&clp->kobject, KOBJ_ADD); 203 } 204 } 205 206 void nfs_netns_sysfs_destroy(struct nfs_net *netns) 207 { 208 struct nfs_netns_client *clp = netns->nfs_client; 209 210 if (clp) { 211 kobject_uevent(&clp->kobject, KOBJ_REMOVE); 212 kobject_del(&clp->kobject); 213 kobject_put(&clp->kobject); 214 kobject_del(&clp->nfs_net_kobj); 215 kobject_put(&clp->nfs_net_kobj); 216 netns->nfs_client = NULL; 217 } 218 } 219 220 static bool shutdown_match_client(const struct rpc_task *task, const void *data) 221 { 222 return true; 223 } 224 225 static void shutdown_client(struct rpc_clnt *clnt) 226 { 227 clnt->cl_shutdown = 1; 228 rpc_cancel_tasks(clnt, -EIO, shutdown_match_client, NULL); 229 } 230 231 static ssize_t 232 shutdown_show(struct kobject *kobj, struct kobj_attribute *attr, 233 char *buf) 234 { 235 struct nfs_server *server = container_of(kobj, struct nfs_server, kobj); 236 bool shutdown = server->flags & NFS_MOUNT_SHUTDOWN; 237 return sysfs_emit(buf, "%d\n", shutdown); 238 } 239 240 static ssize_t 241 shutdown_store(struct kobject *kobj, struct kobj_attribute *attr, 242 const char *buf, size_t count) 243 { 244 struct nfs_server *server; 245 int ret, val; 246 247 server = container_of(kobj, struct nfs_server, kobj); 248 249 ret = kstrtoint(buf, 0, &val); 250 if (ret) 251 return ret; 252 253 if (val != 1) 254 return -EINVAL; 255 256 /* already shut down? */ 257 if (server->flags & NFS_MOUNT_SHUTDOWN) 258 goto out; 259 260 server->flags |= NFS_MOUNT_SHUTDOWN; 261 shutdown_client(server->client); 262 shutdown_client(server->nfs_client->cl_rpcclient); 263 264 if (!IS_ERR(server->client_acl)) 265 shutdown_client(server->client_acl); 266 267 if (server->nlm_host) 268 shutdown_client(server->nlm_host->h_rpcclnt); 269 out: 270 return count; 271 } 272 273 static struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW(shutdown); 274 275 #define RPC_CLIENT_NAME_SIZE 64 276 277 void nfs_sysfs_link_rpc_client(struct nfs_server *server, 278 struct rpc_clnt *clnt, const char *uniq) 279 { 280 char name[RPC_CLIENT_NAME_SIZE]; 281 int ret; 282 283 strcpy(name, clnt->cl_program->name); 284 strcat(name, uniq ? uniq : ""); 285 strcat(name, "_client"); 286 287 ret = sysfs_create_link_nowarn(&server->kobj, 288 &clnt->cl_sysfs->kobject, name); 289 if (ret < 0) 290 pr_warn("NFS: can't create link to %s in sysfs (%d)\n", 291 name, ret); 292 } 293 EXPORT_SYMBOL_GPL(nfs_sysfs_link_rpc_client); 294 295 static void nfs_sysfs_sb_release(struct kobject *kobj) 296 { 297 /* no-op: why? see lib/kobject.c kobject_cleanup() */ 298 } 299 300 static const void *nfs_netns_server_namespace(const struct kobject *kobj) 301 { 302 return container_of(kobj, struct nfs_server, kobj)->nfs_client->cl_net; 303 } 304 305 static struct kobj_type nfs_sb_ktype = { 306 .release = nfs_sysfs_sb_release, 307 .sysfs_ops = &kobj_sysfs_ops, 308 .namespace = nfs_netns_server_namespace, 309 .child_ns_type = nfs_netns_object_child_ns_type, 310 }; 311 312 void nfs_sysfs_add_server(struct nfs_server *server) 313 { 314 int ret; 315 316 ret = kobject_init_and_add(&server->kobj, &nfs_sb_ktype, 317 &nfs_kset->kobj, "server-%d", server->s_sysfs_id); 318 if (ret < 0) { 319 pr_warn("NFS: nfs sysfs add server-%d failed (%d)\n", 320 server->s_sysfs_id, ret); 321 return; 322 } 323 ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_shutdown.attr, 324 nfs_netns_server_namespace(&server->kobj)); 325 if (ret < 0) 326 pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n", 327 server->s_sysfs_id, ret); 328 } 329 EXPORT_SYMBOL_GPL(nfs_sysfs_add_server); 330 331 void nfs_sysfs_move_server_to_sb(struct super_block *s) 332 { 333 struct nfs_server *server = s->s_fs_info; 334 int ret; 335 336 ret = kobject_rename(&server->kobj, s->s_id); 337 if (ret < 0) 338 pr_warn("NFS: rename sysfs %s failed (%d)\n", 339 server->kobj.name, ret); 340 } 341 342 void nfs_sysfs_move_sb_to_server(struct nfs_server *server) 343 { 344 const char *s; 345 int ret = -ENOMEM; 346 347 s = kasprintf(GFP_KERNEL, "server-%d", server->s_sysfs_id); 348 if (s) { 349 ret = kobject_rename(&server->kobj, s); 350 kfree(s); 351 } 352 if (ret < 0) 353 pr_warn("NFS: rename sysfs %s failed (%d)\n", 354 server->kobj.name, ret); 355 } 356 357 /* unlink, not dec-ref */ 358 void nfs_sysfs_remove_server(struct nfs_server *server) 359 { 360 kobject_del(&server->kobj); 361 } 362
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.