sysfs.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2019 Hammerspace Inc
  4. */
  5. #include <linux/module.h>
  6. #include <linux/kobject.h>
  7. #include <linux/sysfs.h>
  8. #include <linux/fs.h>
  9. #include <linux/slab.h>
  10. #include <linux/netdevice.h>
  11. #include <linux/string.h>
  12. #include <linux/nfs_fs.h>
  13. #include <linux/rcupdate.h>
  14. #include "nfs4_fs.h"
  15. #include "netns.h"
  16. #include "sysfs.h"
  17. struct kobject *nfs_client_kobj;
  18. static struct kset *nfs_client_kset;
  19. static void nfs_netns_object_release(struct kobject *kobj)
  20. {
  21. kfree(kobj);
  22. }
  23. static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type(
  24. struct kobject *kobj)
  25. {
  26. return &net_ns_type_operations;
  27. }
  28. static struct kobj_type nfs_netns_object_type = {
  29. .release = nfs_netns_object_release,
  30. .sysfs_ops = &kobj_sysfs_ops,
  31. .child_ns_type = nfs_netns_object_child_ns_type,
  32. };
  33. static struct kobject *nfs_netns_object_alloc(const char *name,
  34. struct kset *kset, struct kobject *parent)
  35. {
  36. struct kobject *kobj;
  37. kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
  38. if (kobj) {
  39. kobj->kset = kset;
  40. if (kobject_init_and_add(kobj, &nfs_netns_object_type,
  41. parent, "%s", name) == 0)
  42. return kobj;
  43. kobject_put(kobj);
  44. }
  45. return NULL;
  46. }
  47. int nfs_sysfs_init(void)
  48. {
  49. nfs_client_kset = kset_create_and_add("nfs", NULL, fs_kobj);
  50. if (!nfs_client_kset)
  51. return -ENOMEM;
  52. nfs_client_kobj = nfs_netns_object_alloc("net", nfs_client_kset, NULL);
  53. if (!nfs_client_kobj) {
  54. kset_unregister(nfs_client_kset);
  55. nfs_client_kset = NULL;
  56. return -ENOMEM;
  57. }
  58. return 0;
  59. }
  60. void nfs_sysfs_exit(void)
  61. {
  62. kobject_put(nfs_client_kobj);
  63. kset_unregister(nfs_client_kset);
  64. }
  65. static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
  66. struct kobj_attribute *attr, char *buf)
  67. {
  68. struct nfs_netns_client *c = container_of(kobj,
  69. struct nfs_netns_client,
  70. kobject);
  71. ssize_t ret;
  72. rcu_read_lock();
  73. ret = scnprintf(buf, PAGE_SIZE, "%s\n", rcu_dereference(c->identifier));
  74. rcu_read_unlock();
  75. return ret;
  76. }
  77. /* Strip trailing '\n' */
  78. static size_t nfs_string_strip(const char *c, size_t len)
  79. {
  80. while (len > 0 && c[len-1] == '\n')
  81. --len;
  82. return len;
  83. }
  84. static ssize_t nfs_netns_identifier_store(struct kobject *kobj,
  85. struct kobj_attribute *attr,
  86. const char *buf, size_t count)
  87. {
  88. struct nfs_netns_client *c = container_of(kobj,
  89. struct nfs_netns_client,
  90. kobject);
  91. const char *old;
  92. char *p;
  93. size_t len;
  94. len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN));
  95. if (!len)
  96. return 0;
  97. p = kmemdup_nul(buf, len, GFP_KERNEL);
  98. if (!p)
  99. return -ENOMEM;
  100. old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1);
  101. if (old) {
  102. synchronize_rcu();
  103. kfree(old);
  104. }
  105. return count;
  106. }
  107. static void nfs_netns_client_release(struct kobject *kobj)
  108. {
  109. struct nfs_netns_client *c = container_of(kobj,
  110. struct nfs_netns_client,
  111. kobject);
  112. kfree(rcu_dereference_raw(c->identifier));
  113. kfree(c);
  114. }
  115. static const void *nfs_netns_client_namespace(struct kobject *kobj)
  116. {
  117. return container_of(kobj, struct nfs_netns_client, kobject)->net;
  118. }
  119. static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier,
  120. 0644, nfs_netns_identifier_show, nfs_netns_identifier_store);
  121. static struct attribute *nfs_netns_client_attrs[] = {
  122. &nfs_netns_client_id.attr,
  123. NULL,
  124. };
  125. static struct kobj_type nfs_netns_client_type = {
  126. .release = nfs_netns_client_release,
  127. .default_attrs = nfs_netns_client_attrs,
  128. .sysfs_ops = &kobj_sysfs_ops,
  129. .namespace = nfs_netns_client_namespace,
  130. };
  131. static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
  132. struct net *net)
  133. {
  134. struct nfs_netns_client *p;
  135. p = kzalloc(sizeof(*p), GFP_KERNEL);
  136. if (p) {
  137. p->net = net;
  138. p->kobject.kset = nfs_client_kset;
  139. if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type,
  140. parent, "nfs_client") == 0)
  141. return p;
  142. kobject_put(&p->kobject);
  143. }
  144. return NULL;
  145. }
  146. void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net)
  147. {
  148. struct nfs_netns_client *clp;
  149. clp = nfs_netns_client_alloc(nfs_client_kobj, net);
  150. if (clp) {
  151. netns->nfs_client = clp;
  152. kobject_uevent(&clp->kobject, KOBJ_ADD);
  153. }
  154. }
  155. void nfs_netns_sysfs_destroy(struct nfs_net *netns)
  156. {
  157. struct nfs_netns_client *clp = netns->nfs_client;
  158. if (clp) {
  159. kobject_uevent(&clp->kobject, KOBJ_REMOVE);
  160. kobject_del(&clp->kobject);
  161. kobject_put(&clp->kobject);
  162. netns->nfs_client = NULL;
  163. }
  164. }