Add additional functionality to the upcall support to allow us to wait for
[dragonfly.git] / sys / kern / kern_upcall.c
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/kern/kern_upcall.c,v 1.4 2003/12/07 04:20:40 dillon Exp $
27  */
28
29 /*
30  * Implement upcall registration and dispatch.
31  */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/upcall.h>
38 #include <sys/thread2.h>
39 #include <sys/upcall.h>
40 #include <sys/malloc.h>
41 #include <sys/sysproto.h>
42 #include <sys/lock.h>
43 #include <sys/signalvar.h>
44
45 #include <vm/vm.h>
46 #include <vm/vm_param.h>
47 #include <vm/vm_kern.h>
48 #include <vm/pmap.h>
49 #include <vm/vm_map.h>
50
51 #include <machine/cpu.h>
52
53 MALLOC_DEFINE(M_UPCALL, "upcalls", "upcall registration structures");
54
55 #ifdef SMP
56
57 static void
58 sigupcall_remote(void *arg)
59 {
60         struct proc *p = arg;
61         if (p == lwkt_preempted_proc())
62                 sigupcall();
63 }
64
65 #endif
66
67 /*
68  * upc_register:
69  *
70  *      Register an upcall context wrapper and procedure.  Note that the
71  *      upcall context is set globally for the process, not for each upcall.
72  *
73  * ARGS(struct upcall *upc, upcall_func_t ctx, upcall_func_t func, void *data)
74  */
75 int
76 upc_register(struct upc_register_args *uap)
77 {
78     struct proc *p = curproc;
79     struct vmspace *vm = p->p_vmspace;
80     struct vmupcall *vu;
81
82     if (vm->vm_upccount >= UPCALL_MAXCOUNT)
83         return(EFBIG);
84
85     vu = malloc(sizeof(struct vmupcall), M_UPCALL, M_WAITOK|M_ZERO);
86     vu->vu_ctx = uap->ctxfunc;
87     vu->vu_func = uap->func;
88     vu->vu_data = uap->data;
89     vu->vu_proc = p;
90     p->p_upcall = uap->upc;
91
92     if (vm->vm_upcalls != NULL)
93         vu->vu_id = vm->vm_upcalls->vu_id + 1;
94     else
95         vu->vu_id = 1;
96     vu->vu_next = vm->vm_upcalls;
97     vm->vm_upcalls = vu;
98     ++vm->vm_upccount;
99     uap->sysmsg_result = vu->vu_id;
100     return(0);
101 }
102
103 /*
104  * upc_control:
105  *
106  * ARGS(int cmd, int upcid, void *data)
107  */
108 int
109 upc_control(struct upc_control_args *uap)
110 {
111     struct proc *p = curproc;
112     struct proc *targp;
113     struct vmspace *vms = p->p_vmspace;
114     struct vmupcall *vu;
115     struct vmupcall *vu_send;
116     struct vmupcall **vupp;
117     int error;
118
119     switch(uap->cmd) {
120     case UPC_CONTROL_DISPATCH:
121         /*
122          * Dispatch the specified upcall id or the next pending id if -1.
123          * the upcall will be marked pending but an actual upcall will only
124          * occur if userland is not in a critical section and the userland
125          * pending bit is not set.
126          *
127          * You can dispatch an upcall associated with your process or another
128          * process sharing the same VM space.
129          */
130         error = (uap->upcid == -1) ? 0 : ENOENT;
131         for (vu = vms->vm_upcalls; vu; vu = vu->vu_next) {
132             if (vu->vu_id == uap->upcid || 
133                 (uap->upcid == -1 && vu->vu_pending >= (int)uap->data && vu->vu_proc == p)
134             ) {
135                 if (vu->vu_pending < (int)uap->data)
136                     vu->vu_pending = (int)uap->data;
137                 error = 0;
138                 targp = vu->vu_proc;
139                 targp->p_flag |= P_UPCALLPEND;
140                 if (targp->p_flag & P_UPCALLWAIT)
141                     wakeup(&targp->p_upcall);
142 #ifdef SMP
143                 if (targp->p_thread->td_gd != mycpu)
144                     lwkt_send_ipiq(targp->p_thread->td_gd->gd_cpuid, sigupcall_remote, targp);
145                 else
146                     sigupcall();
147 #else
148                 sigupcall();
149 #endif
150                 break;
151             }
152         }
153         break;
154     case UPC_CONTROL_NEXT:
155         /*
156          * This is used by the context code to fetch the next pending upcall.
157          * The context code has two choices:  (A) it can drop
158          * upcall->crit_count and set upcall->pending then make this call
159          * unconditionally or * (B) it can drop upcall->crit_count and then
160          * test upcall->pending and only make this call if upcall->pending
161          * is set.  If upcall->pending is clear the context code can pop
162          * the upcall stack itself and return without entering into the kernel
163          * again.  (B) is more efficient but leaves a small window of
164          * opportunity where multiple upcalls can pushdown the stack.
165          *
166          * If another upcall is pending the crit_count will be bumped and
167          * the function, data, and context pointers will be returned in
168          * registers (C cannot call this routine).  If no more upcalls are
169          * pending the pending bit will be cleared and the 'data' argument
170          * is expected to be pointing at the upcall context which we will
171          * then pop, returning to the original code that was interrupted
172          * (NOT the context code).
173          */
174         vu_send = NULL;
175         for (vu = vms->vm_upcalls; vu; vu = vu->vu_next) {
176             if (vu->vu_proc == p && vu->vu_pending) {
177                 if (vu_send)
178                     break;
179                 vu_send = vu;
180             }
181         }
182         /*
183          * vu_send may be NULL, indicating that no more upcalls are pending
184          * for this cpu.  We set the userland pending bit based on whether
185          * additional upcalls are pending or not.
186          */
187         error = fetchupcall(vu_send, vu != NULL, uap->data);
188         break;
189     case UPC_CONTROL_DELETE:
190         /*
191          * Delete the specified upcall id.  If the upcall id is -1, delete
192          * all upcall id's associated with the current process.
193          */
194         error = (uap->upcid == -1) ? 0 : ENOENT;
195         vupp = &vms->vm_upcalls;
196         while ((vu = *vupp) != NULL) {
197             if (vu->vu_id == uap->upcid || 
198                 (uap->upcid == -1 && vu->vu_proc == p)
199             ) {
200                 *vupp = vu->vu_next;
201                 error = 0;
202                 free(vu, M_UPCALL);
203             } else {
204                 vupp = &vu->vu_next;
205             }
206         }
207         break;
208     case UPC_CONTROL_POLL:
209     case UPC_CONTROL_POLLANDCLEAR:
210     case UPC_CONTROL_WAIT:
211         /*
212          * If upcid is -1 poll for the first pending upcall and return the
213          * id or 0 if no upcalls are pending.
214          *
215          * If upcid is a particular upcall then poll that upcall and return
216          * its pending status (0 or 1).  For POLLANDCLEAR, also clear the
217          * pending status.  The userland pending bit is not modified by
218          * this call (maybe we should modify it for poll-and-clear).
219          */
220         error = (uap->upcid == -1) ? 0 : ENOENT;
221         for (vu = vms->vm_upcalls; vu; vu = vu->vu_next) {
222             if (vu->vu_id == uap->upcid || 
223                 (uap->upcid == -1 && vu->vu_pending >= (int)uap->data && vu->vu_proc == p)
224             ) {
225                 error = 0;
226                 if (uap->upcid == -1)
227                     uap->sysmsg_result = vu->vu_id;
228                 else
229                     uap->sysmsg_result = vu->vu_pending;
230                 if (uap->cmd == UPC_CONTROL_POLLANDCLEAR)
231                     vu->vu_pending = 0;
232                 break;
233             }
234         }
235         if (uap->cmd == UPC_CONTROL_WAIT && vu == NULL) {
236             p->p_flag |= P_UPCALLWAIT;
237             tsleep(&p->p_upcall, PCATCH, "wupcall", 0);
238             p->p_flag &= ~P_UPCALLWAIT;
239         }
240         break;
241     default:
242         error = EINVAL;
243         break;
244     }
245     return(error);
246 }
247
248 void
249 upc_release(struct vmspace *vm, struct proc *p)
250 {
251     struct vmupcall **vupp;
252     struct vmupcall *vu;
253
254     vupp = &vm->vm_upcalls;
255     while ((vu = *vupp) != NULL) {
256         if (vu->vu_proc == p) {
257             *vupp = vu->vu_next;
258             free(vu, M_UPCALL);
259             --vm->vm_upccount;
260         } else {
261             vupp = &vu->vu_next;
262         }
263     }
264 }
265
266 /*
267  * XXX eventually we should sort by vu_pending priority and dispatch
268  * the highest priority upcall first.
269  */
270 void
271 postupcall(struct proc *p)
272 {
273     struct vmspace *vm = p->p_vmspace;
274     struct vmupcall *vu;
275     struct vmupcall *vu_send = NULL;
276
277     for (vu = vm->vm_upcalls; vu; vu = vu->vu_next) {
278         if (vu->vu_proc == p && vu->vu_pending) {
279             if (vu_send) {
280                 sendupcall(vu, 1);
281                 return;
282             }
283             vu_send = vu;
284         }
285     }
286     if (vu_send)
287         sendupcall(vu_send, 0);
288 }
289