Commit | Line | Data |
---|---|---|
996bc4f4 TM |
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> | |
bf11fbdb TM |
12 | #include <linux/string.h> |
13 | #include <linux/nfs_fs.h> | |
14 | #include <linux/rcupdate.h> | |
996bc4f4 | 15 | |
bf11fbdb TM |
16 | #include "nfs4_fs.h" |
17 | #include "netns.h" | |
996bc4f4 TM |
18 | #include "sysfs.h" |
19 | ||
20 | struct kobject *nfs_client_kobj; | |
21 | static struct kset *nfs_client_kset; | |
22 | ||
23 | static void nfs_netns_object_release(struct kobject *kobj) | |
24 | { | |
25 | kfree(kobj); | |
26 | } | |
27 | ||
28 | static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type( | |
02a476d9 | 29 | const struct kobject *kobj) |
996bc4f4 TM |
30 | { |
31 | return &net_ns_type_operations; | |
32 | } | |
33 | ||
34 | static struct kobj_type nfs_netns_object_type = { | |
35 | .release = nfs_netns_object_release, | |
36 | .sysfs_ops = &kobj_sysfs_ops, | |
37 | .child_ns_type = nfs_netns_object_child_ns_type, | |
38 | }; | |
39 | ||
40 | static struct kobject *nfs_netns_object_alloc(const char *name, | |
41 | struct kset *kset, struct kobject *parent) | |
42 | { | |
43 | struct kobject *kobj; | |
44 | ||
45 | kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); | |
46 | if (kobj) { | |
47 | kobj->kset = kset; | |
48 | if (kobject_init_and_add(kobj, &nfs_netns_object_type, | |
49 | parent, "%s", name) == 0) | |
50 | return kobj; | |
51 | kobject_put(kobj); | |
52 | } | |
53 | return NULL; | |
54 | } | |
55 | ||
56 | int nfs_sysfs_init(void) | |
57 | { | |
58 | nfs_client_kset = kset_create_and_add("nfs", NULL, fs_kobj); | |
59 | if (!nfs_client_kset) | |
60 | return -ENOMEM; | |
61 | nfs_client_kobj = nfs_netns_object_alloc("net", nfs_client_kset, NULL); | |
62 | if (!nfs_client_kobj) { | |
63 | kset_unregister(nfs_client_kset); | |
64 | nfs_client_kset = NULL; | |
65 | return -ENOMEM; | |
66 | } | |
67 | return 0; | |
68 | } | |
69 | ||
70 | void nfs_sysfs_exit(void) | |
71 | { | |
72 | kobject_put(nfs_client_kobj); | |
73 | kset_unregister(nfs_client_kset); | |
74 | } | |
bf11fbdb TM |
75 | |
76 | static ssize_t nfs_netns_identifier_show(struct kobject *kobj, | |
77 | struct kobj_attribute *attr, char *buf) | |
78 | { | |
79 | struct nfs_netns_client *c = container_of(kobj, | |
80 | struct nfs_netns_client, | |
81 | kobject); | |
094eca37 TM |
82 | ssize_t ret; |
83 | ||
84 | rcu_read_lock(); | |
19cdc8fa | 85 | ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier)); |
094eca37 TM |
86 | rcu_read_unlock(); |
87 | return ret; | |
bf11fbdb TM |
88 | } |
89 | ||
90 | /* Strip trailing '\n' */ | |
91 | static size_t nfs_string_strip(const char *c, size_t len) | |
92 | { | |
93 | while (len > 0 && c[len-1] == '\n') | |
94 | --len; | |
95 | return len; | |
96 | } | |
97 | ||
98 | static ssize_t nfs_netns_identifier_store(struct kobject *kobj, | |
99 | struct kobj_attribute *attr, | |
100 | const char *buf, size_t count) | |
101 | { | |
102 | struct nfs_netns_client *c = container_of(kobj, | |
103 | struct nfs_netns_client, | |
104 | kobject); | |
105 | const char *old; | |
106 | char *p; | |
107 | size_t len; | |
108 | ||
109 | len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); | |
110 | if (!len) | |
111 | return 0; | |
112 | p = kmemdup_nul(buf, len, GFP_KERNEL); | |
113 | if (!p) | |
114 | return -ENOMEM; | |
094eca37 | 115 | old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1); |
bf11fbdb TM |
116 | if (old) { |
117 | synchronize_rcu(); | |
118 | kfree(old); | |
119 | } | |
120 | return count; | |
121 | } | |
122 | ||
123 | static void nfs_netns_client_release(struct kobject *kobj) | |
124 | { | |
125 | struct nfs_netns_client *c = container_of(kobj, | |
126 | struct nfs_netns_client, | |
127 | kobject); | |
128 | ||
094eca37 | 129 | kfree(rcu_dereference_raw(c->identifier)); |
bf11fbdb TM |
130 | kfree(c); |
131 | } | |
132 | ||
02a476d9 | 133 | static const void *nfs_netns_client_namespace(const struct kobject *kobj) |
bf11fbdb TM |
134 | { |
135 | return container_of(kobj, struct nfs_netns_client, kobject)->net; | |
136 | } | |
137 | ||
138 | static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, | |
139 | 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); | |
140 | ||
141 | static struct attribute *nfs_netns_client_attrs[] = { | |
142 | &nfs_netns_client_id.attr, | |
143 | NULL, | |
144 | }; | |
01f34245 | 145 | ATTRIBUTE_GROUPS(nfs_netns_client); |
bf11fbdb TM |
146 | |
147 | static struct kobj_type nfs_netns_client_type = { | |
148 | .release = nfs_netns_client_release, | |
01f34245 | 149 | .default_groups = nfs_netns_client_groups, |
bf11fbdb TM |
150 | .sysfs_ops = &kobj_sysfs_ops, |
151 | .namespace = nfs_netns_client_namespace, | |
152 | }; | |
153 | ||
154 | static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, | |
155 | struct net *net) | |
156 | { | |
157 | struct nfs_netns_client *p; | |
158 | ||
159 | p = kzalloc(sizeof(*p), GFP_KERNEL); | |
160 | if (p) { | |
161 | p->net = net; | |
162 | p->kobject.kset = nfs_client_kset; | |
163 | if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, | |
164 | parent, "nfs_client") == 0) | |
165 | return p; | |
166 | kobject_put(&p->kobject); | |
167 | } | |
168 | return NULL; | |
169 | } | |
170 | ||
171 | void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) | |
172 | { | |
173 | struct nfs_netns_client *clp; | |
174 | ||
175 | clp = nfs_netns_client_alloc(nfs_client_kobj, net); | |
176 | if (clp) { | |
177 | netns->nfs_client = clp; | |
178 | kobject_uevent(&clp->kobject, KOBJ_ADD); | |
179 | } | |
180 | } | |
181 | ||
182 | void nfs_netns_sysfs_destroy(struct nfs_net *netns) | |
183 | { | |
184 | struct nfs_netns_client *clp = netns->nfs_client; | |
185 | ||
186 | if (clp) { | |
187 | kobject_uevent(&clp->kobject, KOBJ_REMOVE); | |
188 | kobject_del(&clp->kobject); | |
189 | kobject_put(&clp->kobject); | |
190 | netns->nfs_client = NULL; | |
191 | } | |
192 | } |