evtchn.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 357 行

C
357
字号
/****************************************************************************** * evtchn.c * * A simplified event channel for para-drivers in unmodified linux * * Copyright (c) 2002-2005, K A Fraser * Copyright (c) 2005, Intel Corporation <xiaofeng.ling@intel.com> * * This file may be distributed separately from the Linux kernel, or * incorporated into other software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/spinlock.h>#include <xen/evtchn.h>#include <xen/interface/hvm/ioreq.h>#include <xen/features.h>#include "platform-pci.h"#ifdef HAVE_XEN_PLATFORM_COMPAT_H#include <xen/platform-compat.h>#endifvoid *shared_info_area;#define is_valid_evtchn(x)	((x) != 0)#define evtchn_from_irq(x)	(irq_evtchn[irq].evtchn)static struct {	spinlock_t lock;	irq_handler_t handler;	void *dev_id;	int evtchn;	int close:1; /* close on unbind_from_irqhandler()? */	int inuse:1;	int in_handler:1;} irq_evtchn[256];static int evtchn_to_irq[NR_EVENT_CHANNELS] = {	[0 ...  NR_EVENT_CHANNELS-1] = -1 };static DEFINE_SPINLOCK(irq_alloc_lock);static int alloc_xen_irq(void){	static int warned;	int irq;	spin_lock(&irq_alloc_lock);	for (irq = 1; irq < ARRAY_SIZE(irq_evtchn); irq++) {		if (irq_evtchn[irq].inuse) 			continue;		irq_evtchn[irq].inuse = 1;		spin_unlock(&irq_alloc_lock);		return irq;	}	if (!warned) {		warned = 1;		printk(KERN_WARNING "No available IRQ to bind to: "		       "increase irq_evtchn[] size in evtchn.c.\n");	}	spin_unlock(&irq_alloc_lock);	return -ENOSPC;}static void free_xen_irq(int irq){	spin_lock(&irq_alloc_lock);	irq_evtchn[irq].inuse = 0;	spin_unlock(&irq_alloc_lock);}int irq_to_evtchn_port(int irq){	return irq_evtchn[irq].evtchn;}EXPORT_SYMBOL(irq_to_evtchn_port);void mask_evtchn(int port){	shared_info_t *s = shared_info_area;	synch_set_bit(port, &s->evtchn_mask[0]);}EXPORT_SYMBOL(mask_evtchn);void unmask_evtchn(int port){	evtchn_unmask_t op = { .port = port };	VOID(HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &op));}EXPORT_SYMBOL(unmask_evtchn);int bind_listening_port_to_irqhandler(	unsigned int remote_domain,	irq_handler_t handler,	unsigned long irqflags,	const char *devname,	void *dev_id){	struct evtchn_alloc_unbound alloc_unbound;	int err, irq;	irq = alloc_xen_irq();	if (irq < 0)		return irq;	spin_lock_irq(&irq_evtchn[irq].lock);	alloc_unbound.dom        = DOMID_SELF;	alloc_unbound.remote_dom = remote_domain;	err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,					  &alloc_unbound);	if (err) {		spin_unlock_irq(&irq_evtchn[irq].lock);		free_xen_irq(irq);		return err;	}	irq_evtchn[irq].handler = handler;	irq_evtchn[irq].dev_id  = dev_id;	irq_evtchn[irq].evtchn  = alloc_unbound.port;	irq_evtchn[irq].close   = 1;	evtchn_to_irq[alloc_unbound.port] = irq;	unmask_evtchn(alloc_unbound.port);	spin_unlock_irq(&irq_evtchn[irq].lock);	return irq;}EXPORT_SYMBOL(bind_listening_port_to_irqhandler);int bind_caller_port_to_irqhandler(	unsigned int caller_port,	irq_handler_t handler,	unsigned long irqflags,	const char *devname,	void *dev_id){	int irq;	irq = alloc_xen_irq();	if (irq < 0)		return irq;	spin_lock_irq(&irq_evtchn[irq].lock);	irq_evtchn[irq].handler = handler;	irq_evtchn[irq].dev_id  = dev_id;	irq_evtchn[irq].evtchn  = caller_port;	irq_evtchn[irq].close   = 0;	evtchn_to_irq[caller_port] = irq;	unmask_evtchn(caller_port);	spin_unlock_irq(&irq_evtchn[irq].lock);	return irq;}EXPORT_SYMBOL(bind_caller_port_to_irqhandler);void unbind_from_irqhandler(unsigned int irq, void *dev_id){	int evtchn;	spin_lock_irq(&irq_evtchn[irq].lock);	evtchn = evtchn_from_irq(irq);	if (is_valid_evtchn(evtchn)) {		evtchn_to_irq[evtchn] = -1;		mask_evtchn(evtchn);		if (irq_evtchn[irq].close) {			struct evtchn_close close = { .port = evtchn };			if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close))				BUG();		}	}	irq_evtchn[irq].handler = NULL;	irq_evtchn[irq].evtchn  = 0;	spin_unlock_irq(&irq_evtchn[irq].lock);	while (irq_evtchn[irq].in_handler)		cpu_relax();	free_xen_irq(irq);}EXPORT_SYMBOL(unbind_from_irqhandler);void notify_remote_via_irq(int irq){	int evtchn;	evtchn = evtchn_from_irq(irq);	if (is_valid_evtchn(evtchn))		notify_remote_via_evtchn(evtchn);}EXPORT_SYMBOL(notify_remote_via_irq);static DEFINE_PER_CPU(unsigned int, last_processed_l1i) = { BITS_PER_LONG - 1 };static DEFINE_PER_CPU(unsigned int, last_processed_l2i) = { BITS_PER_LONG - 1 };static inline unsigned long active_evtchns(unsigned int cpu, shared_info_t *sh,						unsigned int idx){	return (sh->evtchn_pending[idx] & ~sh->evtchn_mask[idx]);}static irqreturn_t evtchn_interrupt(int irq, void *dev_id#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)				    , struct pt_regs *regs#else# define handler(irq, dev_id, regs) handler(irq, dev_id)#endif				    ){	unsigned int l1i, l2i, port;	unsigned long masked_l1, masked_l2;	/* XXX: All events are bound to vcpu0 but irq may be redirected. */	int cpu = 0; /*smp_processor_id();*/	irq_handler_t handler;	shared_info_t *s = shared_info_area;	vcpu_info_t *v = &s->vcpu_info[cpu];	unsigned long l1, l2;	v->evtchn_upcall_pending = 0;#ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */	/* Clear master flag /before/ clearing selector flag. */	wmb();#endif	l1 = xchg(&v->evtchn_pending_sel, 0);	l1i = per_cpu(last_processed_l1i, cpu);	l2i = per_cpu(last_processed_l2i, cpu);	while (l1 != 0) {		l1i = (l1i + 1) % BITS_PER_LONG;		masked_l1 = l1 & ((~0UL) << l1i);		if (masked_l1 == 0) { /* if we masked out all events, wrap around to the beginning */			l1i = BITS_PER_LONG - 1;			l2i = BITS_PER_LONG - 1;			continue;		}		l1i = __ffs(masked_l1);		do {			l2 = active_evtchns(cpu, s, l1i);			l2i = (l2i + 1) % BITS_PER_LONG;			masked_l2 = l2 & ((~0UL) << l2i);			if (masked_l2 == 0) { /* if we masked out all events, move on */				l2i = BITS_PER_LONG - 1;				break;			}			l2i = __ffs(masked_l2);			/* process port */			port = (l1i * BITS_PER_LONG) + l2i;			synch_clear_bit(port, &s->evtchn_pending[0]);			irq = evtchn_to_irq[port];			if (irq < 0)				continue;			spin_lock(&irq_evtchn[irq].lock);			handler = irq_evtchn[irq].handler;			dev_id  = irq_evtchn[irq].dev_id;			if (unlikely(handler == NULL)) {				printk("Xen IRQ%d (port %d) has no handler!\n",				       irq, port);				spin_unlock(&irq_evtchn[irq].lock);				continue;			}			irq_evtchn[irq].in_handler = 1;			spin_unlock(&irq_evtchn[irq].lock);			local_irq_enable();			handler(irq, irq_evtchn[irq].dev_id, regs);			local_irq_disable();			spin_lock(&irq_evtchn[irq].lock);			irq_evtchn[irq].in_handler = 0;			spin_unlock(&irq_evtchn[irq].lock);			/* if this is the final port processed, we'll pick up here+1 next time */			per_cpu(last_processed_l1i, cpu) = l1i;			per_cpu(last_processed_l2i, cpu) = l2i;		} while (l2i != BITS_PER_LONG - 1);		l2 = active_evtchns(cpu, s, l1i);		if (l2 == 0) /* we handled all ports, so we can clear the selector bit */			l1 &= ~(1UL << l1i);	}	return IRQ_HANDLED;}void irq_resume(void){	int evtchn, irq;	for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) {		mask_evtchn(evtchn);		evtchn_to_irq[evtchn] = -1;	}	for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)		irq_evtchn[irq].evtchn = 0;}int xen_irq_init(struct pci_dev *pdev){	int irq;	for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)		spin_lock_init(&irq_evtchn[irq].lock);	return request_irq(pdev->irq, evtchn_interrupt,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)			   SA_SHIRQ | SA_SAMPLE_RANDOM | SA_INTERRUPT,#else			   IRQF_SHARED | IRQF_SAMPLE_RANDOM | IRQF_DISABLED,#endif			   "xen-platform-pci", pdev);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?