Commit | Line | Data |
---|---|---|
d7e09d03 PT |
1 | /* |
2 | * GPL HEADER START | |
3 | * | |
4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 only, | |
8 | * as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License version 2 for more details (a copy is included | |
14 | * in the LICENSE file that accompanied this code). | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * version 2 along with this program; If not, see | |
18 | * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf | |
19 | * | |
20 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
21 | * CA 95054 USA or visit www.sun.com if you need additional information or | |
22 | * have any questions. | |
23 | * | |
24 | * GPL HEADER END | |
25 | */ | |
26 | /* | |
27 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. | |
28 | * Use is subject to license terms. | |
29 | * | |
30 | * Copyright (c) 2012, Intel Corporation. | |
31 | */ | |
32 | /* | |
33 | * This file is part of Lustre, http://www.lustre.org/ | |
34 | * Lustre is a trademark of Sun Microsystems, Inc. | |
35 | * | |
36 | * Author: Nathan Rutman <nathan.rutman@sun.com> | |
37 | * | |
38 | * Kernel <-> userspace communication routines. | |
39 | * Using pipes for all arches. | |
40 | */ | |
41 | ||
42 | #define DEBUG_SUBSYSTEM S_CLASS | |
43 | #define D_KUC D_OTHER | |
44 | ||
45 | #include <linux/libcfs/libcfs.h> | |
46 | ||
47 | #ifdef LUSTRE_UTILS | |
48 | /* This is the userspace side. */ | |
49 | ||
50 | /** Start the userspace side of a KUC pipe. | |
51 | * @param link Private descriptor for pipe/socket. | |
52 | * @param groups KUC broadcast group to listen to | |
53 | * (can be null for unicast to this pid) | |
54 | */ | |
55 | int libcfs_ukuc_start(lustre_kernelcomm *link, int group) | |
56 | { | |
57 | int pfd[2]; | |
58 | ||
59 | if (pipe(pfd) < 0) | |
60 | return -errno; | |
61 | ||
62 | memset(link, 0, sizeof(*link)); | |
63 | link->lk_rfd = pfd[0]; | |
64 | link->lk_wfd = pfd[1]; | |
65 | link->lk_group = group; | |
66 | link->lk_uid = getpid(); | |
67 | return 0; | |
68 | } | |
69 | ||
70 | int libcfs_ukuc_stop(lustre_kernelcomm *link) | |
71 | { | |
72 | if (link->lk_wfd > 0) | |
73 | close(link->lk_wfd); | |
74 | return close(link->lk_rfd); | |
75 | } | |
76 | ||
77 | #define lhsz sizeof(*kuch) | |
78 | ||
79 | /** Read a message from the link. | |
80 | * Allocates memory, returns handle | |
81 | * | |
82 | * @param link Private descriptor for pipe/socket. | |
83 | * @param buf Buffer to read into, must include size for kuc_hdr | |
84 | * @param maxsize Maximum message size allowed | |
85 | * @param transport Only listen to messages on this transport | |
86 | * (and the generic transport) | |
87 | */ | |
88 | int libcfs_ukuc_msg_get(lustre_kernelcomm *link, char *buf, int maxsize, | |
89 | int transport) | |
90 | { | |
91 | struct kuc_hdr *kuch; | |
92 | int rc = 0; | |
93 | ||
94 | memset(buf, 0, maxsize); | |
95 | ||
96 | CDEBUG(D_KUC, "Waiting for message from kernel on fd %d\n", | |
97 | link->lk_rfd); | |
98 | ||
99 | while (1) { | |
100 | /* Read header first to get message size */ | |
101 | rc = read(link->lk_rfd, buf, lhsz); | |
102 | if (rc <= 0) { | |
103 | rc = -errno; | |
104 | break; | |
105 | } | |
106 | kuch = (struct kuc_hdr *)buf; | |
107 | ||
108 | CDEBUG(D_KUC, "Received message mg=%x t=%d m=%d l=%d\n", | |
109 | kuch->kuc_magic, kuch->kuc_transport, kuch->kuc_msgtype, | |
110 | kuch->kuc_msglen); | |
111 | ||
112 | if (kuch->kuc_magic != KUC_MAGIC) { | |
113 | CERROR("bad message magic %x != %x\n", | |
114 | kuch->kuc_magic, KUC_MAGIC); | |
115 | rc = -EPROTO; | |
116 | break; | |
117 | } | |
118 | ||
119 | if (kuch->kuc_msglen > maxsize) { | |
120 | rc = -EMSGSIZE; | |
121 | break; | |
122 | } | |
123 | ||
124 | /* Read payload */ | |
125 | rc = read(link->lk_rfd, buf + lhsz, kuch->kuc_msglen - lhsz); | |
126 | if (rc < 0) { | |
127 | rc = -errno; | |
128 | break; | |
129 | } | |
130 | if (rc < (kuch->kuc_msglen - lhsz)) { | |
131 | CERROR("short read: got %d of %d bytes\n", | |
132 | rc, kuch->kuc_msglen); | |
133 | rc = -EPROTO; | |
134 | break; | |
135 | } | |
136 | ||
137 | if (kuch->kuc_transport == transport || | |
138 | kuch->kuc_transport == KUC_TRANSPORT_GENERIC) { | |
139 | return 0; | |
140 | } | |
141 | /* Drop messages for other transports */ | |
142 | } | |
143 | return rc; | |
144 | } | |
145 | ||
146 | #else /* LUSTRE_UTILS */ | |
147 | /* This is the kernel side (liblustre as well). */ | |
148 | ||
149 | /** | |
150 | * libcfs_kkuc_msg_put - send an message from kernel to userspace | |
151 | * @param fp to send the message to | |
152 | * @param payload Payload data. First field of payload is always | |
153 | * struct kuc_hdr | |
154 | */ | |
155 | int libcfs_kkuc_msg_put(struct file *filp, void *payload) | |
156 | { | |
157 | struct kuc_hdr *kuch = (struct kuc_hdr *)payload; | |
7d5ed06b PT |
158 | ssize_t count = kuch->kuc_msglen; |
159 | loff_t offset = 0; | |
160 | mm_segment_t fs; | |
d7e09d03 PT |
161 | int rc = -ENOSYS; |
162 | ||
163 | if (filp == NULL || IS_ERR(filp)) | |
164 | return -EBADF; | |
165 | ||
166 | if (kuch->kuc_magic != KUC_MAGIC) { | |
167 | CERROR("KernelComm: bad magic %x\n", kuch->kuc_magic); | |
168 | return -ENOSYS; | |
169 | } | |
170 | ||
7d5ed06b PT |
171 | fs = get_fs(); |
172 | set_fs(KERNEL_DS); | |
e543d2ea PT |
173 | while (count > 0) { |
174 | rc = vfs_write(filp, (void __force __user *)payload, | |
7d5ed06b PT |
175 | count, &offset); |
176 | if (rc < 0) | |
177 | break; | |
178 | count -= rc; | |
179 | payload += rc; | |
180 | rc = 0; | |
d7e09d03 | 181 | } |
7d5ed06b | 182 | set_fs(fs); |
d7e09d03 PT |
183 | |
184 | if (rc < 0) | |
185 | CWARN("message send failed (%d)\n", rc); | |
186 | else | |
187 | CDEBUG(D_KUC, "Sent message rc=%d, fp=%p\n", rc, filp); | |
188 | ||
189 | return rc; | |
190 | } | |
191 | EXPORT_SYMBOL(libcfs_kkuc_msg_put); | |
192 | ||
193 | /* Broadcast groups are global across all mounted filesystems; | |
194 | * i.e. registering for a group on 1 fs will get messages for that | |
195 | * group from any fs */ | |
17afd17b | 196 | /** A single group registration has a uid and a file pointer */ |
d7e09d03 PT |
197 | struct kkuc_reg { |
198 | struct list_head kr_chain; | |
199 | int kr_uid; | |
200 | struct file *kr_fp; | |
201 | __u32 kr_data; | |
202 | }; | |
203 | static struct list_head kkuc_groups[KUC_GRP_MAX+1] = {}; | |
204 | /* Protect message sending against remove and adds */ | |
205 | static DECLARE_RWSEM(kg_sem); | |
206 | ||
207 | /** Add a receiver to a broadcast group | |
208 | * @param filp pipe to write into | |
17afd17b | 209 | * @param uid identifier for this receiver |
d7e09d03 PT |
210 | * @param group group number |
211 | */ | |
212 | int libcfs_kkuc_group_add(struct file *filp, int uid, int group, __u32 data) | |
213 | { | |
214 | struct kkuc_reg *reg; | |
215 | ||
216 | if (group > KUC_GRP_MAX) { | |
217 | CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group); | |
218 | return -EINVAL; | |
219 | } | |
220 | ||
221 | /* fput in group_rem */ | |
222 | if (filp == NULL) | |
223 | return -EBADF; | |
224 | ||
225 | /* freed in group_rem */ | |
226 | reg = kmalloc(sizeof(*reg), 0); | |
227 | if (reg == NULL) | |
228 | return -ENOMEM; | |
229 | ||
230 | reg->kr_fp = filp; | |
231 | reg->kr_uid = uid; | |
232 | reg->kr_data = data; | |
233 | ||
234 | down_write(&kg_sem); | |
235 | if (kkuc_groups[group].next == NULL) | |
236 | INIT_LIST_HEAD(&kkuc_groups[group]); | |
237 | list_add(®->kr_chain, &kkuc_groups[group]); | |
238 | up_write(&kg_sem); | |
239 | ||
240 | CDEBUG(D_KUC, "Added uid=%d fp=%p to group %d\n", uid, filp, group); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | EXPORT_SYMBOL(libcfs_kkuc_group_add); | |
245 | ||
246 | int libcfs_kkuc_group_rem(int uid, int group) | |
247 | { | |
248 | struct kkuc_reg *reg, *next; | |
d7e09d03 PT |
249 | |
250 | if (kkuc_groups[group].next == NULL) | |
0a3bdb00 | 251 | return 0; |
d7e09d03 PT |
252 | |
253 | if (uid == 0) { | |
254 | /* Broadcast a shutdown message */ | |
255 | struct kuc_hdr lh; | |
256 | ||
257 | lh.kuc_magic = KUC_MAGIC; | |
258 | lh.kuc_transport = KUC_TRANSPORT_GENERIC; | |
259 | lh.kuc_msgtype = KUC_MSG_SHUTDOWN; | |
260 | lh.kuc_msglen = sizeof(lh); | |
261 | libcfs_kkuc_group_put(group, &lh); | |
262 | } | |
263 | ||
264 | down_write(&kg_sem); | |
265 | list_for_each_entry_safe(reg, next, &kkuc_groups[group], kr_chain) { | |
266 | if ((uid == 0) || (uid == reg->kr_uid)) { | |
267 | list_del(®->kr_chain); | |
268 | CDEBUG(D_KUC, "Removed uid=%d fp=%p from group %d\n", | |
269 | reg->kr_uid, reg->kr_fp, group); | |
270 | if (reg->kr_fp != NULL) | |
271 | fput(reg->kr_fp); | |
272 | kfree(reg); | |
273 | } | |
274 | } | |
275 | up_write(&kg_sem); | |
276 | ||
0a3bdb00 | 277 | return 0; |
d7e09d03 PT |
278 | } |
279 | EXPORT_SYMBOL(libcfs_kkuc_group_rem); | |
280 | ||
281 | int libcfs_kkuc_group_put(int group, void *payload) | |
282 | { | |
283 | struct kkuc_reg *reg; | |
284 | int rc = 0; | |
285 | int one_success = 0; | |
d7e09d03 PT |
286 | |
287 | down_read(&kg_sem); | |
288 | list_for_each_entry(reg, &kkuc_groups[group], kr_chain) { | |
289 | if (reg->kr_fp != NULL) { | |
290 | rc = libcfs_kkuc_msg_put(reg->kr_fp, payload); | |
291 | if (rc == 0) | |
292 | one_success = 1; | |
293 | else if (rc == -EPIPE) { | |
294 | fput(reg->kr_fp); | |
295 | reg->kr_fp = NULL; | |
296 | } | |
297 | } | |
298 | } | |
299 | up_read(&kg_sem); | |
300 | ||
301 | /* don't return an error if the message has been delivered | |
302 | * at least to one agent */ | |
303 | if (one_success) | |
304 | rc = 0; | |
305 | ||
0a3bdb00 | 306 | return rc; |
d7e09d03 PT |
307 | } |
308 | EXPORT_SYMBOL(libcfs_kkuc_group_put); | |
309 | ||
310 | /** | |
311 | * Calls a callback function for each link of the given kuc group. | |
312 | * @param group the group to call the function on. | |
313 | * @param cb_func the function to be called. | |
314 | * @param cb_arg iextra argument to be passed to the callback function. | |
315 | */ | |
316 | int libcfs_kkuc_group_foreach(int group, libcfs_kkuc_cb_t cb_func, | |
317 | void *cb_arg) | |
318 | { | |
319 | struct kkuc_reg *reg; | |
320 | int rc = 0; | |
d7e09d03 PT |
321 | |
322 | if (group > KUC_GRP_MAX) { | |
323 | CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group); | |
0a3bdb00 | 324 | return -EINVAL; |
d7e09d03 PT |
325 | } |
326 | ||
327 | /* no link for this group */ | |
328 | if (kkuc_groups[group].next == NULL) | |
0a3bdb00 | 329 | return 0; |
d7e09d03 PT |
330 | |
331 | down_read(&kg_sem); | |
332 | list_for_each_entry(reg, &kkuc_groups[group], kr_chain) { | |
83bffc81 | 333 | if (reg->kr_fp != NULL) |
d7e09d03 | 334 | rc = cb_func(reg->kr_data, cb_arg); |
d7e09d03 PT |
335 | } |
336 | up_read(&kg_sem); | |
337 | ||
0a3bdb00 | 338 | return rc; |
d7e09d03 PT |
339 | } |
340 | EXPORT_SYMBOL(libcfs_kkuc_group_foreach); | |
341 | ||
342 | #endif /* LUSTRE_UTILS */ |