]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/dev/xen/xenpci/evtchn.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / dev / xen / xenpci / evtchn.c
1 /******************************************************************************
2  * evtchn.c
3  *
4  * A simplified event channel for para-drivers in unmodified linux
5  *
6  * Copyright (c) 2002-2005, K A Fraser
7  * Copyright (c) 2005, Intel Corporation <xiaofeng.ling@intel.com>
8  *
9  * This file may be distributed separately from the Linux kernel, or
10  * incorporated into other software packages, subject to the following license:
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this source file (the "Software"), to deal in the Software without
14  * restriction, including without limitation the rights to use, copy, modify,
15  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
16  * and to permit persons to whom the Software is furnished to do so, subject to
17  * the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28  * IN THE SOFTWARE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/malloc.h>
38 #include <sys/kernel.h>
39 #include <sys/limits.h>
40 #include <sys/lock.h>
41 #include <sys/mutex.h>
42 #include <sys/interrupt.h>
43 #include <sys/pcpu.h>
44
45 #include <machine/xen/xen-os.h>
46 #include <machine/xen/xenvar.h>
47 #include <xen/hypervisor.h>
48 #include <xen/xen_intr.h>
49 #include <xen/evtchn.h>
50 #include <sys/smp.h>
51
52 #include <dev/xen/xenpci/xenpcivar.h>
53
54 static inline unsigned long __ffs(unsigned long word)
55 {
56         __asm__("bsfq %1,%0"
57                 :"=r" (word)
58                 :"rm" (word));
59         return word;
60 }
61
62 #define is_valid_evtchn(x)      ((x) != 0)
63 #define evtchn_from_irq(x)      (irq_evtchn[irq].evtchn)
64
65 static struct {
66         struct mtx lock;
67         driver_intr_t *handler;
68         void *arg;
69         int evtchn;
70         int close:1; /* close on unbind_from_irqhandler()? */
71         int inuse:1;
72         int in_handler:1;
73         int mpsafe:1;
74 } irq_evtchn[256];
75 static int evtchn_to_irq[NR_EVENT_CHANNELS] = {
76         [0 ...  NR_EVENT_CHANNELS-1] = -1 };
77
78 static struct mtx irq_alloc_lock;
79 static device_t xenpci_device;
80
81 #define ARRAY_SIZE(a)   (sizeof(a) / sizeof(a[0]))
82
83 static unsigned int
84 alloc_xen_irq(void)
85 {
86         static int warned;
87         unsigned int irq;
88
89         mtx_lock(&irq_alloc_lock);
90
91         for (irq = 1; irq < ARRAY_SIZE(irq_evtchn); irq++) {
92                 if (irq_evtchn[irq].inuse) 
93                         continue;
94                 irq_evtchn[irq].inuse = 1;
95                 mtx_unlock(&irq_alloc_lock);
96                 return irq;
97         }
98
99         if (!warned) {
100                 warned = 1;
101                 printf("alloc_xen_irq: No available IRQ to bind to: "
102                        "increase irq_evtchn[] size in evtchn.c.\n");
103         }
104
105         mtx_unlock(&irq_alloc_lock);
106
107         return -ENOSPC;
108 }
109
110 static void
111 free_xen_irq(int irq)
112 {
113
114         mtx_lock(&irq_alloc_lock);
115         irq_evtchn[irq].inuse = 0;
116         mtx_unlock(&irq_alloc_lock);
117 }
118
119 int
120 irq_to_evtchn_port(int irq)
121 {
122
123         return irq_evtchn[irq].evtchn;
124 }
125
126 void
127 mask_evtchn(int port)
128 {
129         shared_info_t *s = HYPERVISOR_shared_info;
130
131         synch_set_bit(port, &s->evtchn_mask[0]);
132 }
133
134 void
135 unmask_evtchn(int port)
136 {
137         evtchn_unmask_t op = { .port = port };
138
139         HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &op);
140 }
141
142 int
143 bind_listening_port_to_irqhandler(unsigned int remote_domain,
144     const char *devname, driver_intr_t handler, void *arg,
145     unsigned long irqflags, unsigned int *irqp)
146 {
147         struct evtchn_alloc_unbound alloc_unbound;
148         unsigned int irq;
149         int error;
150
151         irq = alloc_xen_irq();
152         if (irq < 0)
153                 return irq;
154
155         mtx_lock(&irq_evtchn[irq].lock);
156
157         alloc_unbound.dom        = DOMID_SELF;
158         alloc_unbound.remote_dom = remote_domain;
159         error = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
160                                           &alloc_unbound);
161         if (error) {
162                 mtx_unlock(&irq_evtchn[irq].lock);
163                 free_xen_irq(irq);
164                 return (-error);
165         }
166
167         irq_evtchn[irq].handler = handler;
168         irq_evtchn[irq].arg     = arg;
169         irq_evtchn[irq].evtchn  = alloc_unbound.port;
170         irq_evtchn[irq].close   = 1;
171         irq_evtchn[irq].mpsafe  = (irqflags & INTR_MPSAFE) != 0;
172
173         evtchn_to_irq[alloc_unbound.port] = irq;
174
175         unmask_evtchn(alloc_unbound.port);
176
177         mtx_unlock(&irq_evtchn[irq].lock);
178
179         if (irqp)
180                 *irqp = irq;
181         return (0);
182 }
183
184 int 
185 bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
186     unsigned int remote_port, const char *devname, driver_intr_t handler,
187     void *arg, unsigned long irqflags, unsigned int *irqp)
188 {
189         struct evtchn_bind_interdomain bind_interdomain;
190         unsigned int irq;
191         int error;
192
193         irq = alloc_xen_irq();
194         if (irq < 0)
195                 return irq;
196
197         mtx_lock(&irq_evtchn[irq].lock);
198
199         bind_interdomain.remote_dom  = remote_domain;
200         bind_interdomain.remote_port = remote_port;
201         error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
202                                             &bind_interdomain);
203         if (error) {
204                 mtx_unlock(&irq_evtchn[irq].lock);
205                 free_xen_irq(irq);
206                 return (-error);
207         }
208
209         irq_evtchn[irq].handler = handler;
210         irq_evtchn[irq].arg     = arg;
211         irq_evtchn[irq].evtchn  = bind_interdomain.local_port;
212         irq_evtchn[irq].close   = 1;
213         irq_evtchn[irq].mpsafe  = (irqflags & INTR_MPSAFE) != 0;
214
215         evtchn_to_irq[bind_interdomain.local_port] = irq;
216
217         unmask_evtchn(bind_interdomain.local_port);
218
219         mtx_unlock(&irq_evtchn[irq].lock);
220
221         if (irqp)
222                 *irqp = irq;
223         return (0);
224 }
225
226
227 int
228 bind_caller_port_to_irqhandler(unsigned int caller_port,
229     const char *devname, driver_intr_t handler, void *arg,
230     unsigned long irqflags, unsigned int *irqp)
231 {
232         unsigned int irq;
233
234         irq = alloc_xen_irq();
235         if (irq < 0)
236                 return irq;
237
238         mtx_lock(&irq_evtchn[irq].lock);
239
240         irq_evtchn[irq].handler = handler;
241         irq_evtchn[irq].arg     = arg;
242         irq_evtchn[irq].evtchn  = caller_port;
243         irq_evtchn[irq].close   = 0;
244         irq_evtchn[irq].mpsafe  = (irqflags & INTR_MPSAFE) != 0;
245
246         evtchn_to_irq[caller_port] = irq;
247
248         unmask_evtchn(caller_port);
249
250         mtx_unlock(&irq_evtchn[irq].lock);
251
252         if (irqp)
253                 *irqp = irq;
254         return (0);
255 }
256
257 void
258 unbind_from_irqhandler(unsigned int irq)
259 {
260         int evtchn;
261
262         mtx_lock(&irq_evtchn[irq].lock);
263
264         evtchn = evtchn_from_irq(irq);
265
266         if (is_valid_evtchn(evtchn)) {
267                 evtchn_to_irq[evtchn] = -1;
268                 mask_evtchn(evtchn);
269                 if (irq_evtchn[irq].close) {
270                         struct evtchn_close close = { .port = evtchn };
271                         if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close))
272                                 panic("EVTCHNOP_close failed");
273                 }
274         }
275
276         irq_evtchn[irq].handler = NULL;
277         irq_evtchn[irq].evtchn  = 0;
278
279         mtx_unlock(&irq_evtchn[irq].lock);
280
281         while (irq_evtchn[irq].in_handler)
282                 cpu_relax();
283
284         free_xen_irq(irq);
285 }
286
287 void notify_remote_via_irq(int irq)
288 {
289         int evtchn;
290
291         evtchn = evtchn_from_irq(irq);
292         if (is_valid_evtchn(evtchn))
293                 notify_remote_via_evtchn(evtchn);
294 }
295
296 static inline unsigned long active_evtchns(unsigned int cpu, shared_info_t *sh,
297                                                 unsigned int idx)
298 {
299         return (sh->evtchn_pending[idx] & ~sh->evtchn_mask[idx]);
300 }
301
302 static void
303 evtchn_interrupt(void *arg)
304 {
305         unsigned int l1i, l2i, port;
306         unsigned long masked_l1, masked_l2;
307         /* XXX: All events are bound to vcpu0 but irq may be redirected. */
308         int cpu = 0; /*smp_processor_id();*/
309         driver_intr_t *handler;
310         void *handler_arg;
311         int irq, handler_mpsafe;
312         shared_info_t *s = HYPERVISOR_shared_info;
313         vcpu_info_t *v = &s->vcpu_info[cpu];
314         struct pcpu *pc = pcpu_find(cpu);
315         unsigned long l1, l2;
316
317         v->evtchn_upcall_pending = 0;
318
319 #if 0
320 #ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */
321         /* Clear master flag /before/ clearing selector flag. */
322         wmb();
323 #endif
324 #endif
325
326         l1 = atomic_readandclear_long(&v->evtchn_pending_sel);
327
328         l1i = pc->pc_last_processed_l1i;
329         l2i = pc->pc_last_processed_l2i;
330
331         while (l1 != 0) {
332
333                 l1i = (l1i + 1) % LONG_BIT;
334                 masked_l1 = l1 & ((~0UL) << l1i);
335
336                 if (masked_l1 == 0) { /* if we masked out all events, wrap around to the beginning */
337                         l1i = LONG_BIT - 1;
338                         l2i = LONG_BIT - 1;
339                         continue;
340                 }
341                 l1i = __ffs(masked_l1);
342
343                 do {
344                         l2 = active_evtchns(cpu, s, l1i);
345
346                         l2i = (l2i + 1) % LONG_BIT;
347                         masked_l2 = l2 & ((~0UL) << l2i);
348
349                         if (masked_l2 == 0) { /* if we masked out all events, move on */
350                                 l2i = LONG_BIT - 1;
351                                 break;
352                         }
353                         l2i = __ffs(masked_l2);
354
355                         /* process port */
356                         port = (l1i * LONG_BIT) + l2i;
357                         synch_clear_bit(port, &s->evtchn_pending[0]);
358
359                         irq = evtchn_to_irq[port];
360                         if (irq < 0)
361                                 continue;
362
363                         mtx_lock(&irq_evtchn[irq].lock);
364                         handler = irq_evtchn[irq].handler;
365                         handler_arg = irq_evtchn[irq].arg;
366                         handler_mpsafe = irq_evtchn[irq].mpsafe;
367                         if (unlikely(handler == NULL)) {
368                                 printf("Xen IRQ%d (port %d) has no handler!\n",
369                                        irq, port);
370                                 mtx_unlock(&irq_evtchn[irq].lock);
371                                 continue;
372                         }
373                         irq_evtchn[irq].in_handler = 1;
374                         mtx_unlock(&irq_evtchn[irq].lock);
375
376                         //local_irq_enable();
377                         if (!handler_mpsafe)
378                                 mtx_lock(&Giant);
379                         handler(handler_arg);
380                         if (!handler_mpsafe)
381                                 mtx_unlock(&Giant);
382                         //local_irq_disable();
383
384                         mtx_lock(&irq_evtchn[irq].lock);
385                         irq_evtchn[irq].in_handler = 0;
386                         mtx_unlock(&irq_evtchn[irq].lock);
387
388                         /* if this is the final port processed, we'll pick up here+1 next time */
389                         pc->pc_last_processed_l1i = l1i;
390                         pc->pc_last_processed_l2i = l2i;
391
392                 } while (l2i != LONG_BIT - 1);
393
394                 l2 = active_evtchns(cpu, s, l1i);
395                 if (l2 == 0) /* we handled all ports, so we can clear the selector bit */
396                         l1 &= ~(1UL << l1i);
397         }
398 }
399
400 void
401 irq_suspend(void)
402 {
403         struct xenpci_softc *scp = device_get_softc(xenpci_device);
404
405         /*
406          * Take our interrupt handler out of the list of handlers
407          * that can handle this irq.
408          */
409         if (scp->intr_cookie != NULL) {
410                 if (BUS_TEARDOWN_INTR(device_get_parent(xenpci_device),
411                         xenpci_device, scp->res_irq, scp->intr_cookie) != 0)
412                         printf("intr teardown failed.. continuing\n");
413                 scp->intr_cookie = NULL;
414         }
415 }
416
417 void
418 irq_resume(void)
419 {
420         struct xenpci_softc *scp = device_get_softc(xenpci_device);
421         int evtchn, irq;
422
423         for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) {
424                 mask_evtchn(evtchn);
425                 evtchn_to_irq[evtchn] = -1;
426         }
427
428         for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)
429                 irq_evtchn[irq].evtchn = 0;
430
431         BUS_SETUP_INTR(device_get_parent(xenpci_device),
432             xenpci_device, scp->res_irq, INTR_TYPE_MISC,
433             NULL, evtchn_interrupt, NULL, &scp->intr_cookie);
434 }
435
436 int
437 xenpci_irq_init(device_t device, struct xenpci_softc *scp)
438 {
439         int irq, cpu;
440         int error;
441
442         mtx_init(&irq_alloc_lock, "xen-irq-lock", NULL, MTX_DEF);
443
444         for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)
445                 mtx_init(&irq_evtchn[irq].lock, "irq-evtchn", NULL, MTX_DEF);
446
447         for (cpu = 0; cpu < mp_ncpus; cpu++) {
448                 pcpu_find(cpu)->pc_last_processed_l1i = LONG_BIT - 1;
449                 pcpu_find(cpu)->pc_last_processed_l2i = LONG_BIT - 1;
450         }
451
452         error = BUS_SETUP_INTR(device_get_parent(device), device,
453             scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, NULL, evtchn_interrupt,
454             NULL, &scp->intr_cookie);
455         if (error)
456                 return (error);
457
458         xenpci_device = device;
459
460         return (0);
461 }