]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/nfs4client/nfs4_dev.c
This commit was generated by cvs2svn to compensate for changes in r147078,
[FreeBSD/FreeBSD.git] / sys / nfs4client / nfs4_dev.c
1 /* $FreeBSD$ */
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_MINOR 1
51
52 MALLOC_DEFINE(M_NFS4DEV, "NFS4 dev", "NFS4 device");
53
54 struct nfs4dev_upcall {
55         /* request msg */
56         struct nfs4dev_msg up_reqmsg;
57         size_t up_reqmsglen;
58
59         /* reply (payload only) */
60         caddr_t up_rep;
61         size_t * up_replen;     
62
63         int up_copied;          /* non-zero when reply has been copied to 
64                                    '*up_rep' */
65                                    
66         int up_error;           /* non-zero if an error occured */
67
68         TAILQ_ENTRY(nfs4dev_upcall) up_entry;
69 };
70
71
72 #define nfs4dev_upcall_get(MP) MALLOC((MP), struct nfs4dev_upcall *, sizeof(struct nfs4dev_upcall), M_NFS4DEV, M_WAITOK | M_ZERO)
73
74 #define nfs4dev_upcall_put(MP) FREE((MP), M_NFS4DEV)
75
76 static int nfs4dev_nopen = 0;
77 static struct thread * nfs4dev_reader = NULL;
78 static struct cdev *nfs4device = 0;
79 static struct mtx nfs4dev_daemon_mtx;
80
81 static int nfs4dev_xid = 0;
82 /* queue of pending upcalls */
83 TAILQ_HEAD(, nfs4dev_upcall) nfs4dev_newq;
84 static struct mtx nfs4dev_newq_mtx;
85
86 /* queue of upcalls waiting for replys */
87 TAILQ_HEAD(, nfs4dev_upcall) nfs4dev_waitq;
88 static struct mtx nfs4dev_waitq_mtx;
89
90 /* dev hooks */
91 static d_open_t  nfs4dev_open;
92 static d_close_t nfs4dev_close;
93 static d_ioctl_t nfs4dev_ioctl;
94 static d_poll_t  nfs4dev_poll;
95
96 static struct cdevsw nfs4dev_cdevsw = {
97 #if (__FreeBSD_version > 502102)
98         .d_version =    D_VERSION,
99         .d_flags =      D_NEEDGIANT,
100 #endif
101         .d_open =       nfs4dev_open,
102         .d_close =      nfs4dev_close,
103         .d_ioctl =      nfs4dev_ioctl,
104         .d_poll =       nfs4dev_poll,
105         .d_name =       NFS4DEV_NAME,
106 };
107
108 static int nfs4dev_reply(caddr_t);
109 static int nfs4dev_request(caddr_t);
110
111 /* Userland requests a new operation to service */
112 static int 
113 nfs4dev_request(caddr_t addr)
114 {
115         struct nfs4dev_upcall * u;
116         struct nfs4dev_msg * m = (struct nfs4dev_msg *) addr;
117
118         mtx_lock(&nfs4dev_newq_mtx);
119
120         if (TAILQ_EMPTY(&nfs4dev_newq)) {
121                 mtx_unlock(&nfs4dev_newq_mtx);
122                 return EAGAIN;  
123         }
124
125         u = TAILQ_FIRST(&nfs4dev_newq);
126         TAILQ_REMOVE(&nfs4dev_newq, u, up_entry);
127         mtx_unlock(&nfs4dev_newq_mtx);
128
129         bcopy(&u->up_reqmsg, m, sizeof(struct nfs4dev_msg));
130
131         mtx_lock(&nfs4dev_waitq_mtx);
132         TAILQ_INSERT_TAIL(&nfs4dev_waitq, u, up_entry);
133         mtx_unlock(&nfs4dev_waitq_mtx);
134
135         return 0;
136 }
137
138 static int
139 nfs4dev_reply(caddr_t addr)
140 {
141         struct nfs4dev_upcall * u;
142         struct nfs4dev_msg * m = (struct nfs4dev_msg *) addr;
143         int error;
144
145         if (m->msg_vers != NFS4DEV_VERSION) {
146                 printf("nfs4dev version mismatch\n");
147                 return EINVAL;
148         }
149
150         if (m->msg_type > NFS4DEV_MAX_TYPE) {
151                 NFS4DEV_DEBUG("nfs4dev: unsupported message type\n");
152                 return EINVAL;
153         }
154
155         if (m->msg_len == 0 || m->msg_len > NFS4DEV_MSG_MAX_DATALEN) {
156                 NFS4DEV_DEBUG("bad message length\n");
157                 return EINVAL;
158         }
159                 
160         /* match the reply with a request */
161         mtx_lock(&nfs4dev_waitq_mtx);
162         TAILQ_FOREACH(u, &nfs4dev_waitq, up_entry) {
163                 if (m->msg_xid == u->up_reqmsg.msg_xid) {
164                         if (m->msg_type == u->up_reqmsg.msg_type)
165                                 goto found;
166                         NFS4DEV_DEBUG("nfs4dev: op type mismatch!\n");
167                         break;
168                 }
169         }
170         mtx_unlock(&nfs4dev_waitq_mtx);
171
172         NFS4DEV_DEBUG("nfs4dev msg op: %d xid: %x not found.\n",
173             m->msg_type, m->msg_xid);
174
175         error = EIO;
176         goto bad;
177
178 found:
179         TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
180         mtx_unlock(&nfs4dev_waitq_mtx);
181
182         if (m->msg_error) {
183                 error = m->msg_error;
184                 goto bad;
185         }
186
187         if (m->msg_len > *u->up_replen) {
188                 error = EFAULT;
189                 goto bad;
190         }
191
192         bcopy(m->msg_data, u->up_rep, m->msg_len);
193         *u->up_replen = m->msg_len;
194
195         u->up_copied = m->msg_len;
196         wakeup(u);
197
198         return 0;
199 bad:
200         u->up_error = error;
201         wakeup(u);
202         return error;
203 }
204
205 void
206 nfs4dev_init(void)
207 {
208         nfs4dev_xid = arc4random();
209         TAILQ_INIT(&nfs4dev_newq);      
210         TAILQ_INIT(&nfs4dev_waitq);     
211         mtx_init(&nfs4dev_newq_mtx, "nfs4dev newq", NULL, MTX_DEF);
212         mtx_init(&nfs4dev_waitq_mtx, "nfs4dev waitq", NULL, MTX_DEF);
213
214         mtx_init(&nfs4dev_daemon_mtx, "nfs4dev state", NULL, MTX_DEF);
215
216         nfs4device = make_dev(&nfs4dev_cdevsw, CDEV_MINOR, (uid_t)0, (gid_t)0,
217             S_IRUSR | S_IWUSR, "nfs4");
218 }
219
220 void
221 nfs4dev_uninit(void)
222 {
223         struct proc * dead = NULL;
224
225         mtx_lock(&nfs4dev_daemon_mtx);
226         if (nfs4dev_nopen) {
227                 if (nfs4dev_reader == NULL) {
228                         NFS4DEV_DEBUG("nfs4dev uninit(): unregistered reader\n");
229                 } else {
230                         dead = nfs4dev_reader->td_proc;
231                 }
232         }
233         mtx_unlock(&nfs4dev_daemon_mtx);
234
235         if (dead != NULL) {
236                 NFS4DEV_DEBUG("nfs4dev_uninit(): you forgot to kill attached daemon (pid: %u)\n",
237                     dead->p_pid);
238                 PROC_LOCK(dead);
239                 psignal(dead, SIGTERM);
240                 PROC_UNLOCK(dead);
241         }
242
243         /* XXX moot? */
244         nfs4dev_purge();
245
246         mtx_destroy(&nfs4dev_newq_mtx);
247         mtx_destroy(&nfs4dev_waitq_mtx);
248         mtx_destroy(&nfs4dev_daemon_mtx);
249
250         destroy_dev(nfs4device);
251 }
252
253 /* device interface functions */
254 static int
255 nfs4dev_open(struct cdev *dev, int flags, int fmt, d_thread_t *td)
256 {
257         if (dev != nfs4device) 
258                 return ENODEV;
259
260         mtx_lock(&nfs4dev_daemon_mtx);
261         if (nfs4dev_nopen) {
262                 mtx_unlock(&nfs4dev_daemon_mtx);
263                 return EBUSY;
264         }
265
266         nfs4dev_nopen++;
267         nfs4dev_reader = curthread;
268         mtx_unlock(&nfs4dev_daemon_mtx);
269
270         return (0);
271 }
272
273 static int
274 nfs4dev_close(struct cdev *dev, int flags, int fmt, d_thread_t *td)
275 {
276         struct nfs4dev_upcall * u;
277
278         if (dev != nfs4device) 
279                 return ENODEV;
280
281         mtx_lock(&nfs4dev_daemon_mtx);
282         if (!nfs4dev_nopen) {
283                 mtx_unlock(&nfs4dev_daemon_mtx);
284                 return ENOENT;
285         }
286
287         nfs4dev_nopen--; 
288         nfs4dev_reader = NULL;
289         mtx_unlock(&nfs4dev_daemon_mtx);
290
291         mtx_lock(&nfs4dev_waitq_mtx);
292
293         while (!TAILQ_EMPTY(&nfs4dev_waitq)) {
294                 u = TAILQ_FIRST(&nfs4dev_waitq);
295                 TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
296                 u->up_error = EINTR;
297                 wakeup(u);
298         }
299
300         mtx_unlock(&nfs4dev_waitq_mtx);
301
302         return 0;
303 }
304
305 static int 
306 nfs4dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
307 {
308         int error;
309
310         if (dev != nfs4device)
311                 return ENODEV; 
312
313         if (data == NULL) 
314                 return EFAULT;
315
316         if (nfs4dev_reader != curthread)
317                 nfs4dev_reader = curthread;
318
319         switch (cmd) {
320         case NFS4DEVIOCGET:
321                 error = nfs4dev_request(data);
322         break;
323         case NFS4DEVIOCPUT:
324                 error = nfs4dev_reply(data);
325         break;
326         default:
327                 NFS4DEV_DEBUG("nfs4dev_ioctl: unkown ioctl cmd %d\n", (int)cmd);
328                 error = EOPNOTSUPP;
329         break;
330         }
331
332         return error;
333 }
334
335 static int 
336 nfs4dev_poll(struct cdev *dev, int events, struct thread *td)
337 {
338         int revents;
339
340         if (dev != nfs4device) 
341           return EINVAL;
342
343         mtx_lock(&nfs4dev_daemon_mtx);
344         if (nfs4dev_nopen == 0) {
345                 mtx_unlock(&nfs4dev_daemon_mtx);
346                 return 0;
347         }
348         mtx_unlock(&nfs4dev_daemon_mtx);
349
350         revents = 0;
351
352         /* check readable data */
353         mtx_lock(&nfs4dev_newq_mtx);
354           if (!TAILQ_EMPTY(&nfs4dev_newq))
355             revents |= POLLIN;
356         mtx_unlock(&nfs4dev_newq_mtx);
357
358         mtx_lock(&nfs4dev_waitq_mtx);
359           if (!TAILQ_EMPTY(&nfs4dev_waitq))
360             revents |= POLLOUT;
361         mtx_unlock(&nfs4dev_waitq_mtx);
362
363         return revents;
364 }
365
366 int 
367 nfs4dev_call(uint32_t type, caddr_t req_data, size_t req_len, caddr_t rep_data, size_t * rep_lenp)
368 {
369         struct nfs4dev_upcall * u;
370         int error = 0; 
371         unsigned int xtmp;
372
373         mtx_lock(&nfs4dev_daemon_mtx);
374         if (nfs4dev_nopen == 0) {
375                 mtx_unlock(&nfs4dev_daemon_mtx);
376                 return EINVAL;
377         }
378         mtx_unlock(&nfs4dev_daemon_mtx);
379
380         if (type > NFS4DEV_MAX_TYPE)
381           return EOPNOTSUPP;
382
383         NFS4DEV_DEBUG("upcall %d/%d:%d\n", type, req_len, *rep_lenp);
384
385         nfs4dev_upcall_get(u);
386
387         u->up_error = 0;
388         u->up_rep = rep_data;
389         u->up_replen = rep_lenp;
390         u->up_copied = 0;
391
392         u->up_reqmsg.msg_vers = NFS4DEV_VERSION;
393         /* XXX efficient copying */
394         bcopy(req_data, u->up_reqmsg.msg_data, req_len);
395         u->up_reqmsg.msg_len  = req_len;
396
397         mtx_lock(&nfs4dev_newq_mtx);
398
399         /* get new XID */
400         while ((xtmp = arc4random() % 256) == 0);
401         nfs4dev_xid += xtmp;
402         u->up_reqmsg.msg_xid = nfs4dev_xid;
403
404         TAILQ_INSERT_TAIL(&nfs4dev_newq, u, up_entry);
405         mtx_unlock(&nfs4dev_newq_mtx);
406
407
408         NFS4DEV_DEBUG("nfs4dev op: %d xid: %x sleeping\n", u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid);
409
410         do {
411                 tsleep(u, PLOCK, "nfs4dev", 0);
412         } while (u->up_copied == 0 && u->up_error == 0);
413
414         /* upcall now removed from the queue */
415
416         NFS4DEV_DEBUG("nfs4dev prog: %d xid: %x continues...\n", 
417             u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid);
418
419         if (u->up_error) {
420                 error = u->up_error; 
421                 NFS4DEV_DEBUG("nfs4dev prog: %d xid: %x error: %d\n", 
422                     u->up_reqmsg.msg_type, u->up_reqmsg.msg_xid, u->up_error);
423                 goto out;
424         }
425
426 out:
427         nfs4dev_upcall_put(u);
428         return error;
429 }
430
431 void
432 nfs4dev_purge(void)
433 {
434         struct nfs4dev_upcall * u;
435
436         mtx_lock(&nfs4dev_newq_mtx);
437         while (!TAILQ_EMPTY(&nfs4dev_newq)) {
438                 u = TAILQ_FIRST(&nfs4dev_newq);
439                 TAILQ_REMOVE(&nfs4dev_newq, u, up_entry);
440                 u->up_error = EINTR;
441                 wakeup(u);
442         }
443         mtx_unlock(&nfs4dev_newq_mtx);
444
445         mtx_lock(&nfs4dev_waitq_mtx);
446         while (!TAILQ_EMPTY(&nfs4dev_waitq)) {
447                 u = TAILQ_FIRST(&nfs4dev_waitq);
448                 TAILQ_REMOVE(&nfs4dev_waitq, u, up_entry);
449                 u->up_error = EINTR;
450                 wakeup(u);
451         }
452         mtx_unlock(&nfs4dev_waitq_mtx);
453 }