1 /* $FreeBSD: releng/5.2/sys/nfs4client/nfs4_dev.c 122698 2003-11-14 20:54:10Z alfred $ */
2 /* $Id: nfs4_dev.c,v 1.10 2003/11/05 14:58:59 rees Exp $ */
3
4 /*
5 * copyright (c) 2003
6 * the regents of the university of michigan
7 * all rights reserved
8 *
9 * permission is granted to use, copy, create derivative works and redistribute
10 * this software and such derivative works for any purpose, so long as the name
11 * of the university of michigan is not used in any advertising or publicity
12 * pertaining to the use or distribution of this software without specific,
13 * written prior authorization. if the above copyright notice or any other
14 * identification of the university of michigan is included in any copy of any
15 * portion of this software, then the disclaimer below must also be included.
16 *
17 * this software is provided as is, without representation from the university
18 * of michigan as to its fitness for any purpose, and without warranty by the
19 * university of michigan of any kind, either express or implied, including
20 * without limitation the implied warranties of merchantability and fitness for
21 * a particular purpose. the regents of the university of michigan shall not be
22 * liable for any damages, including special, indirect, incidental, or
23 * consequential damages, with respect to any claim arising out of or in
24 * connection with the use of the software, even if it has been or is hereafter
25 * advised of the possibility of such damages.
26 */
27
28 #include <sys/param.h>
29 #include <sys/conf.h>
30 #include <sys/queue.h>
31 #include <sys/malloc.h>
32 #include <sys/kernel.h>
33 #include <sys/poll.h>
34 #include <sys/mutex.h>
35 #include <sys/stat.h>
36 #include <sys/systm.h>
37 #include <sys/proc.h>
38 #include <sys/wait.h>
39 #include <sys/signalvar.h>
40
41 #include <nfs4client/nfs4_dev.h>
42
43 #ifdef NFS4DEVVERBOSE
44 #define NFS4DEV_DEBUG(X...) printf(X)
45 #else
46 #define NFS4DEV_DEBUG(X...)
47 #endif
48
49 #define NFS4DEV_NAME "nfs4"
50 #define CDEV_MAJOR 29 /* XXX where are these numbers assigned!?!? */
51 #define CDEV_MINOR 1
52
53 MALLOC_DEFINE(M_NFS4DEV, "NFS4 dev", "NFS4 device");
54
55 struct nfs4dev_upcall {
56 /* request msg */
57 struct nfs4dev_msg up_reqmsg;
58 size_t up_reqmsglen;
59
60 /* reply (payload only) */
61 caddr_t up_rep;
62 size_t * up_replen;
63
64 int up_copied; /* non-zero when reply has been copied to
65 '*up_rep' */
66
67 int up_error; /* non-zero if an error occured */
68
69 TAILQ_ENTRY(nfs4dev_upcall) up_entry;
70 };
71
72
73 #define nfs4dev_upcall_get(MP) MALLOC((MP), struct nfs4dev_upcall *, sizeof(struct nfs4dev_upcall), M_NFS4DEV, M_WAITOK | M_ZERO)
74
75 #define nfs4dev_upcall_put(MP) FREE((MP), M_NFS4DEV)
76
77 static int nfs4dev_nopen = 0;
78 static struct thread * nfs4dev_reader = NULL;
79 static dev_t nfs4device = 0;
80 static struct mtx nfs4dev_daemon_mtx;
81
82 static int nfs4dev_xid = 0;
83 /* queue of pending upcalls */
84 TAILQ_HEAD(, nfs4dev_upcall) nfs4dev_newq;
85 static struct mtx nfs4dev_newq_mtx;
86
87 /* queue of upcalls waiting for replys */
88 TAILQ_HEAD(, nfs4dev_upcall) nfs4dev_waitq;
89 static struct mtx nfs4dev_waitq_mtx;
90
91 /* dev hooks */
92 static d_open_t nfs4dev_open;
93 static d_close_t nfs4dev_close;
94 static d_ioctl_t nfs4dev_ioctl;
95 static d_poll_t nfs4dev_poll;
96
97 static struct cdevsw nfs4dev_cdevsw = {
98 .d_open = nfs4dev_open,
99 .d_close = nfs4dev_close,
100 .d_ioctl = nfs4dev_ioctl,
101 .d_poll = nfs4dev_poll,
102 .d_name = NFS4DEV_NAME,
103 .d_maj = CDEV_MAJOR
104 };
105
106 static int nfs4dev_reply(caddr_t);
107 static int nfs4dev_request(caddr_t);
108
109 /* Userland requests a new operation to service */
110 static int
111 nfs4dev_request(caddr_t addr)
112 {
113 struct nfs4dev_upcall * u;
114 struct nfs4dev_msg * m = (struct nfs4dev_msg *) addr;
115
116 mtx_lock(&nfs4dev_newq_mtx);
117
118 if (TAILQ_EMPTY(&nfs4dev_newq)) {
119 mtx_unlock(&nfs4dev_newq_mtx);
120 return EAGAIN;
121 }
122
123 u = TAILQ_FIRST(&nfs4dev_newq);
124 TAILQ_REMOVE(&nfs4dev_newq, u, up_entry);
125 mtx_unlock(&nfs4dev_newq_mtx);
126
127 bcopy(&u->up_reqmsg, m, sizeof(struct nfs4dev_msg));
128
129 mtx_lock(&nfs4dev_waitq_mtx);
130 TAILQ_INSERT_TAIL(&nfs4dev_waitq, u, up_entry);
131 mtx_unlock(&nfs4dev_waitq_mtx);
132
133 return 0;
134 }
135
136 static int
137 nfs4dev_reply(caddr_t addr)
138 {
139 struct nfs4dev_upcall * u;
140 struct nfs4dev_msg * m = (struct nfs4dev_msg *) addr;
141 int error;
142
143 if (m->msg_vers != NFS4DEV_VERSION) {
144 printf("nfs4dev version mismatch\n");
145 return EINVAL;
146 }
147
148 if (m->msg_type > NFS4DEV_MAX_TYPE) {
149 NFS4DEV_DEBUG("nfs4dev: unsupported message type\n");
150 return EINVAL;
151 }
152
153 if (m->msg_len == 0 || m->msg_len > NFS4DEV_MSG_MAX_DATALEN) {
154 NFS4DEV_DEBUG("bad message length\n");
155 return EINVAL;
156 }
157
158 /* match the reply with a request */
159 mtx_lock(&nfs4dev_waitq_mtx);
160 TAILQ_FOREACH(u, &nfs4dev_waitq, up_entry) {
161 if (m->msg_xid == u->up_reqmsg.msg_xid) {
162 if (m->msg_type == u->up_reqmsg.msg_type)
163 goto found;
164 NFS4DEV_DEBUG("nfs4dev: op type mismatch!\n");
165 break;
166 }
167 }
168 mtx_unlock(&nfs4dev_waitq_mtx);
169
170 NFS4DEV_DEBUG("nfs4dev msg op: %d xid: %x not found.\n",
171 m->msg_type, m->msg_xid);
172
173 error = EIO;
174 goto bad;
175
176 found:
177 TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
178 mtx_unlock(&nfs4dev_waitq_mtx);
179
180 if (m->msg_error) {
181 error = m->msg_error;
182 goto bad;
183 }
184
185 if (m->msg_len > *u->up_replen) {
186 error = EFAULT;
187 goto bad;
188 }
189
190 bcopy(m->msg_data, u->up_rep, m->msg_len);
191 *u->up_replen = m->msg_len;
192
193 u->up_copied = m->msg_len;
194 wakeup(u);
195
196 return 0;
197 bad:
198 u->up_error = error;
199 wakeup(u);
200 return error;
201 }
202
203 void
204 nfs4dev_init(void)
205 {
206 nfs4dev_xid = arc4random();
207 TAILQ_INIT(&nfs4dev_newq);
208 TAILQ_INIT(&nfs4dev_waitq);
209 mtx_init(&nfs4dev_newq_mtx, "nfs4dev newq", NULL, MTX_DEF);
210 mtx_init(&nfs4dev_waitq_mtx, "nfs4dev waitq", NULL, MTX_DEF);
211
212 mtx_init(&nfs4dev_daemon_mtx, "nfs4dev state", NULL, MTX_DEF);
213
214 nfs4device = make_dev(&nfs4dev_cdevsw, CDEV_MINOR, (uid_t)0, (gid_t)0,
215 S_IRUSR | S_IWUSR, "nfs4");
216 }
217
218 void
219 nfs4dev_uninit(void)
220 {
221 struct proc * dead = NULL;
222
223 mtx_lock(&nfs4dev_daemon_mtx);
224 if (nfs4dev_nopen) {
225 if (nfs4dev_reader == NULL) {
226 NFS4DEV_DEBUG("nfs4dev uninit(): unregistered reader\n");
227 } else {
228 dead = nfs4dev_reader->td_proc;
229 }
230 }
231 mtx_unlock(&nfs4dev_daemon_mtx);
232
233 if (dead != NULL) {
234 NFS4DEV_DEBUG("nfs4dev_uninit(): you forgot to kill attached daemon (pid: %u)\n",
235 dead->p_pid);
236 PROC_LOCK(dead);
237 psignal(dead, SIGTERM);
238 PROC_UNLOCK(dead);
239 }
240
241 /* XXX moot? */
242 nfs4dev_purge();
243
244 mtx_destroy(&nfs4dev_newq_mtx);
245 mtx_destroy(&nfs4dev_waitq_mtx);
246 mtx_destroy(&nfs4dev_daemon_mtx);
247
248 destroy_dev(nfs4device);
249 }
250
251 /* device interface functions */
252 static int
253 nfs4dev_open(dev_t dev, int flags, int fmt, d_thread_t *td)
254 {
255 if (dev != nfs4device)
256 return ENODEV;
257
258 mtx_lock(&nfs4dev_daemon_mtx);
259 if (nfs4dev_nopen) {
260 mtx_unlock(&nfs4dev_daemon_mtx);
261 return EBUSY;
262 }
263
264 nfs4dev_nopen++;
265 nfs4dev_reader = curthread;
266 mtx_unlock(&nfs4dev_daemon_mtx);
267
268 return (0);
269 }
270
271 static int
272 nfs4dev_close(dev_t dev, int flags, int fmt, d_thread_t *td)
273 {
274 struct nfs4dev_upcall * u;
275
276 if (dev != nfs4device)
277 return ENODEV;
278
279 mtx_lock(&nfs4dev_daemon_mtx);
280 if (!nfs4dev_nopen) {
281 mtx_unlock(&nfs4dev_daemon_mtx);
282 return ENOENT;
283 }
284
285 nfs4dev_nopen--;
286 nfs4dev_reader = NULL;
287 mtx_unlock(&nfs4dev_daemon_mtx);
288
289 mtx_lock(&nfs4dev_waitq_mtx);
290
291 while (!TAILQ_EMPTY(&nfs4dev_waitq)) {
292 u = TAILQ_FIRST(&nfs4dev_waitq);
293 TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
294 u->up_error = EINTR;
295 wakeup(u);
296 }
297
298 mtx_unlock(&nfs4dev_waitq_mtx);
299
300 return 0;
301 }
302
303 static int
304 nfs4dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
305 {
306 int error;
307
308 if (dev != nfs4device)
309 return ENODEV;
310
311 if (data == NULL)
312 return EFAULT;
313
314 if (nfs4dev_reader != curthread)
315 nfs4dev_reader = curthread;
316
317 switch (cmd) {
318 case NFS4DEVIOCGET:
319 error = nfs4dev_request(data);
320 break;
321 case NFS4DEVIOCPUT:
322 error = nfs4dev_reply(data);
323 break;
324 default:
325 NFS4DEV_DEBUG("nfs4dev_ioctl: unkown ioctl cmd %d\n", (int)cmd);
326 error = EOPNOTSUPP;
327 break;
328 }
329
330 return error;
331 }
332
333 static int
334 nfs4dev_poll(dev_t dev, int events, struct thread *td)
335 {
336 int revents;
337
338 if (dev != nfs4device)
339 return EINVAL;
340
341 mtx_lock(&nfs4dev_daemon_mtx);
342 if (nfs4dev_nopen == 0) {
343 mtx_unlock(&nfs4dev_daemon_mtx);
344 return 0;
345 }
346 mtx_unlock(&nfs4dev_daemon_mtx);
347
348 revents = 0;
349
350 /* check readable data */
351 mtx_lock(&nfs4dev_newq_mtx);
352 if (!TAILQ_EMPTY(&nfs4dev_newq))
353 revents |= POLLIN;
354 mtx_unlock(&nfs4dev_newq_mtx);
355
356 mtx_lock(&nfs4dev_waitq_mtx);
357 if (!TAILQ_EMPTY(&nfs4dev_waitq))
358 revents |= POLLOUT;
359 mtx_unlock(&nfs4dev_waitq_mtx);
360
361 return revents;
362 }
363
364 int
365 nfs4dev_call(uint32_t type, caddr_t req_data, size_t req_len, caddr_t rep_data, size_t * rep_lenp)
366 {
367 struct nfs4dev_upcall * u;
368 int error = 0;
369 unsigned int xtmp;
370
371 mtx_lock(&nfs4dev_daemon_mtx);
372 if (nfs4dev_nopen == 0) {
373 mtx_unlock(&nfs4dev_daemon_mtx);
374 return EINVAL;
375 }
376 mtx_unlock(&nfs4dev_daemon_mtx);
377
378 if (type > NFS4DEV_MAX_TYPE)
379 return EOPNOTSUPP;
380
381 NFS4DEV_DEBUG("upcall %d/%d:%d\n", type, req_len, *rep_lenp);
382
383 nfs4dev_upcall_get(u);
384
385 u->up_error = 0;
386 u->up_rep = rep_data;
387 u->up_replen = rep_lenp;
388 u->up_copied = 0;
389
390 u->up_reqmsg.msg_vers = NFS4DEV_VERSION;
391 /* XXX efficient copying */
392 bcopy(req_data, u->up_reqmsg.msg_data, req_len);
393 u->up_reqmsg.msg_len = req_len;
394
395 mtx_lock(&nfs4dev_newq_mtx);
396
397 /* get new XID */
398 while ((xtmp = arc4random() % 256) == 0);
399 nfs4dev_xid += xtmp;
400 u->up_reqmsg.msg_xid = nfs4dev_xid;
401
402 TAILQ_INSERT_TAIL(&nfs4dev_newq, u, up_entry);
403 mtx_unlock(&nfs4dev_newq_mtx);
404
405
406 NFS4DEV_DEBUG("nfs4dev op: %d xid: %x sleeping\n", u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid);
407
408 do {
409 tsleep(u, PLOCK, "nfs4dev", 0);
410 } while (u->up_copied == 0 && u->up_error == 0);
411
412 /* upcall now removed from the queue */
413
414 NFS4DEV_DEBUG("nfs4dev prog: %d xid: %x continues...\n",
415 u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid);
416
417 if (u->up_error) {
418 error = u->up_error;
419 NFS4DEV_DEBUG("nfs4dev prog: %d xid: %x error: %d\n",
420 u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid, u->up_error);
421 goto out;
422 }
423
424 out:
425 nfs4dev_upcall_put(u);
426 return error;
427 }
428
429 void
430 nfs4dev_purge(void)
431 {
432 struct nfs4dev_upcall * u;
433
434 mtx_lock(&nfs4dev_newq_mtx);
435 while (!TAILQ_EMPTY(&nfs4dev_newq)) {
436 u = TAILQ_FIRST(&nfs4dev_newq);
437 TAILQ_REMOVE(&nfs4dev_newq, u, up_entry);
438 u->up_error = EINTR;
439 wakeup(u);
440 }
441 mtx_unlock(&nfs4dev_newq_mtx);
442
443 mtx_lock(&nfs4dev_waitq_mtx);
444 while (!TAILQ_EMPTY(&nfs4dev_waitq)) {
445 u = TAILQ_FIRST(&nfs4dev_waitq);
446 TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
447 u->up_error = EINTR;
448 wakeup(u);
449 }
450 mtx_unlock(&nfs4dev_waitq_mtx);
451 }
Cache object: 6ae579120138d671b3fc1fa73220709e
|