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( | |
29 | struct kobject *kobj) | |
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); | |
82 | return scnprintf(buf, PAGE_SIZE, "%s\n", c->identifier); | |
83 | } | |
84 | ||
85 | /* Strip trailing '\n' */ | |
86 | static size_t nfs_string_strip(const char *c, size_t len) | |
87 | { | |
88 | while (len > 0 && c[len-1] == '\n') | |
89 | --len; | |
90 | return len; | |
91 | } | |
92 | ||
93 | static ssize_t nfs_netns_identifier_store(struct kobject *kobj, | |
94 | struct kobj_attribute *attr, | |
95 | const char *buf, size_t count) | |
96 | { | |
97 | struct nfs_netns_client *c = container_of(kobj, | |
98 | struct nfs_netns_client, | |
99 | kobject); | |
100 | const char *old; | |
101 | char *p; | |
102 | size_t len; | |
103 | ||
104 | len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); | |
105 | if (!len) | |
106 | return 0; | |
107 | p = kmemdup_nul(buf, len, GFP_KERNEL); | |
108 | if (!p) | |
109 | return -ENOMEM; | |
110 | old = xchg(&c->identifier, p); | |
111 | if (old) { | |
112 | synchronize_rcu(); | |
113 | kfree(old); | |
114 | } | |
115 | return count; | |
116 | } | |
117 | ||
118 | static void nfs_netns_client_release(struct kobject *kobj) | |
119 | { | |
120 | struct nfs_netns_client *c = container_of(kobj, | |
121 | struct nfs_netns_client, | |
122 | kobject); | |
123 | ||
124 | if (c->identifier) | |
125 | kfree(c->identifier); | |
126 | kfree(c); | |
127 | } | |
128 | ||
129 | static const void *nfs_netns_client_namespace(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 | ||
142 | static struct kobj_type nfs_netns_client_type = { | |
143 | .release = nfs_netns_client_release, | |
144 | .default_attrs = nfs_netns_client_attrs, | |
145 | .sysfs_ops = &kobj_sysfs_ops, | |
146 | .namespace = nfs_netns_client_namespace, | |
147 | }; | |
148 | ||
149 | static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, | |
150 | struct net *net) | |
151 | { | |
152 | struct nfs_netns_client *p; | |
153 | ||
154 | p = kzalloc(sizeof(*p), GFP_KERNEL); | |
155 | if (p) { | |
156 | p->net = net; | |
157 | p->kobject.kset = nfs_client_kset; | |
158 | if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, | |
159 | parent, "nfs_client") == 0) | |
160 | return p; | |
161 | kobject_put(&p->kobject); | |
162 | } | |
163 | return NULL; | |
164 | } | |
165 | ||
166 | void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) | |
167 | { | |
168 | struct nfs_netns_client *clp; | |
169 | ||
170 | clp = nfs_netns_client_alloc(nfs_client_kobj, net); | |
171 | if (clp) { | |
172 | netns->nfs_client = clp; | |
173 | kobject_uevent(&clp->kobject, KOBJ_ADD); | |
174 | } | |
175 | } | |
176 | ||
177 | void nfs_netns_sysfs_destroy(struct nfs_net *netns) | |
178 | { | |
179 | struct nfs_netns_client *clp = netns->nfs_client; | |
180 | ||
181 | if (clp) { | |
182 | kobject_uevent(&clp->kobject, KOBJ_REMOVE); | |
183 | kobject_del(&clp->kobject); | |
184 | kobject_put(&clp->kobject); | |
185 | netns->nfs_client = NULL; | |
186 | } | |
187 | } |