Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 30 Jul 2016 23:11:29 -0700
From:      Nathan Whitehorn <nwhitehorn@freebsd.org>
To:        Michal Meloun <mmel@FreeBSD.org>
Cc:        "freebsd-arm@freebsd.org" <freebsd-arm@FreeBSD.org>, Svatopluk Kraus <skra@FreeBSD.org>, "freebsd-arch@freebsd.org" <freebsd-arch@FreeBSD.org>
Subject:   Re: INTRNG (Was: svn commit: r301453....)
Message-ID:  <24107713-6d50-c21d-ccf1-7dbdb36cc484@freebsd.org>
In-Reply-To: <579CF7C8.1040302@FreeBSD.org>
References:  <201606051620.u55GKD5S066398@repo.freebsd.org> <5794720F.4050303@FreeBSD.org> <8bfd8668-bc49-e109-e610-b5cd470be3ec@freebsd.org> <57950005.6070403@FreeBSD.org> <f82018ee-51e7-60fa-2682-f0ef307a52b5@freebsd.org> <57961549.4020105@FreeBSD.org> <e2cace17-0924-2084-5fcf-626f87e41cc3@freebsd.org> <CANCZdfr%2BZ4XxXRY0yMiWXwp=8iKq54y3uJ9-OfAOdfxAs1qdtw@mail.gmail.com> <f94bfd25-fabf-efc3-55c9-cfdfd9e4d6e6@freebsd.org> <CANCZdfpz=z3gc3pyb_Qssa3vGJSnPv_r6J-SWDPPpE9zPYB9=w@mail.gmail.com> <ab44ddb1-515b-94ac-6b12-673b7c53d658@freebsd.org> <57976867.6080705@FreeBSD.org> <f2edac8f-2859-cd98-754e-881e2b2d1e63@freebsd.org> <5798E104.5020104@FreeBSD.org> <a5d43044-1733-6cc7-2e99-e85b60b0fcf3@freebsd.org> <579A25BB.8070206@FreeBSD.org> <30790e40-58b4-3371-c0f0-b7545571f389@freebsd.org> <579AFFC5.1040005@FreeBSD.org> <eb603349-eb88-866d-7a26-9e026518fd39@freebsd.org> <579CD355.1050203@FreeBSD.org> <460fa0b3-ddb7-6247-2412-3d75a589d5e7@freebsd.org> <579CF7C8.1040302@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------B749752E283B3662897D27B5
Content-Type: text/plain; charset=windows-1252; format=flowed
Content-Transfer-Encoding: 7bit



On 07/30/16 11:54, Michal Meloun wrote:


[snip]

>
>>>>>> Or: you could skip all of that and use the fully functional mechanism
>>>>>> that already exists.
>>>>> The problem is, at least from my point of view, that we don't have it.
>>>>> The actual MIPS code doesn't work on ARM for numerous reasons.
>>>>> This is
>>>>> first one ->
>>>>>   
>>>>> https://svnweb.freebsd.org/base/head/sys/powerpc/powerpc/nexus.c?revision=297000&view=markup#l186
>>>>> The pre-r301453  ARM INTRNG doesn't support detached interrupts.
>>>>> And r301453  doesn't changed nothing about this.
>>>> I'm happy to write whatever code is missing. The ARM implementation of
>>>> ofw_bus_map_intr() does seem fairly braindead and should be replaced.
>>>>
>>>> So here is where I am right now:
>>>> - The perceived advantage of the new API seems to be that the mapping
>>>> information (interrupt parent, etc.) ends up in a struct resource
>>>> instead of in a centralized mapping table
>>> True. And, in optimal case,  also in RLE. See [1].
>> Your code also has a centralized table (static struct intr_irqsrc
>> *irq_sources[NIRQ];), basically the same one, so I'm actually really
>> confused on this point. The only actual difference seems to be that
>> the firmware interrupt descriptor is stored in the RLE at
>> bus_alloc_resource() time instead of in the table at bus enumeration
>> time and that the new code requires some extra bus methods.
> The irq_sources holds only real interrupts (all interrupts pin from all
> interrupts controllers).  Your effort is move all 'interrupt description
> data' out of INTRNG code -
> to RLE (at bus enumeration time) (see [1])  and to struct resource (at
> bus_alloc_resource() time). The new bus method is only temporary
> shortcut to bypass 11 release.
>
> But, to be fair, I was hoping it might be necessary.
>
> [1] This is last missing step in whole patch series and, in optimal
> case, it need new filed(s) in RLE structure. So we chose to postpone
> discussion about this to time after 11 release.

That's a nice idea. I think it will require a big amount of API 
retooling, though, since we don't use RLEs consistently everywhere in 
the tree (the PCI code, for example).

>
>>>> - Additionally, it gives you a better shot at having the PIC online
>>>> before the PIC's interrupts are parsed (which is not required, but
>>>> nice).
>>> Nope,  we simply fount that detached pics is very dangerous and not
>>> needed. But, if you love it, it can be added as MD extension.
>> Dangerous how? I don't "love it"; it's an unfortunate necessity.
> Imho, some drivers may rely on correct return code from
> bus_alloc_resource() or from bus_setup_intr().
> For example to detect if interrupt exist, or if given interrupt
> configuration is supported.

These can be decoupled. The thing you need for sure is to be able to 
assign resources before the PIC attaches. I think you've convinced me 
that with some care in bus passes, it is possible to postpone the need 
for bus_alloc_resource() and bus_setup_intr() to succeed until after the 
PIC[s] have attached in all cases. That will require work, but it's in 
principle possible.

>>>> - Beyond these aesthetic points, there are no concrete examples of new
>>>> functionality added by this API, aside from some minor implementation
>>>> bugs of the old one on ARM that are easily fixed.
>>> Really? Or you just ignore it?
>> Which ones? I've been asking for a week and a half now and you have
>> responded with some hypothetical examples, all of which either (a) do
>> not seem to occur in the real world and (b) were trivially
>> implementable with the existing code.
>>
>>>> - There are, conversely, a number of concrete cases where this new API
>>>> would not be able to do the right thing. Some of these can be worked
>>>> around or fixed with significant restructuring in the MI parts of the
>>>> kernel.
>>> And i offer you a fix, without "significant restructuring in the MI
>>> parts of the kernel". Unfortunately, you did not want to accept anything
>>> other than the old API.
>> For one of them, partially. For the others, not so much. For example,
>> I have no idea how to implement PCI interrupts (PCIB_ROUTE_INTERRUPT)
>> in this framework. I am perfectly happy to accept a new API. What I am
>> not perfectly happy to accept is an API that makes it impossible for
>> me to support the platforms that I need to support and that,
>> simultaneously, doesn't even provide any new capabilities on other
>> platforms.
>  From one of previous mail:
> <quote>
> But again, all what I want in this area is (for me) simple change of
> your ofw_bus_map_intr() method to something like:
>
> enum intr_map_data_type {
>     INTR_MAP_DATA_OFW,
>     INTR_MAP_DATA_GPIO,
>     ...
> };
>
> int
> bus_map_intr(device_t dev,  enum intr_map_data_type type,  uintptr_t
> pic_id,  void *config_data, int config_size)
> </quote>
>> The current API is certainly a bit of a hack and I would be pleased to
>> see it go; but the replacement needs to be more functional and more
>> robust. As far as I can tell, I literally *cannot* move PowerPC over
>> to this architecture.
> Yes, at this time, I agree. If you can accept enhancement of
> (owf_)bus_map_intr() then we can move to next, significantly less
> hackish, state.

OK, sure. I wrote some code this afternoon (attached) to sketch this. It 
does not compile or run or anything useful, but it should illustrate the 
important parts of what I think is an API that should work for everyone. 
I'm happy to finish it if it looks reasonable -- I may in any case just 
to replace PowerPC's increasingly teetering intr_machdep.c.

The OF part is essentially unchanged from how it currently works: 
there's a method that you pass the interrupt specifier and interrupt 
parent to, and it spits back an IRQ that means that combination of 
things in perpetuity. OFW_BUS_MAP_INTR() in nexus.c, with its current 
API, can be implemented in terms of it in one line. Whenever the 
relevant PIC attaches, it is told to use the given IRQ to refer to that 
opaque bytestring.

I've added a set of equivalent functions for ACPI that take the contents 
of an ACPI interrupt specifier and do the same things. It tries to have 
the IRQ be human-meaningful, if possible, by matching the ACPI interrupt 
number. Having separate functions seemed a little cleaner than exposing 
the enums and unions as part of the KPI, but I'm not wedded to it -- 
this is just an example.

PICs register (and deregister) themselves with either an OF phandle or 
an ACPI resource string or (god help us) both as needed. The PICs have 
corresponding methods for interpreting various kinds of interrupt 
specifiers.

Whether it allows bus_alloc_resource(), bus_setup_intr(), etc. to 
succeed before the PICs are attached is gated on an #ifdef. That can 
probably be off by default on ARM. A PowerPC version of this code would 
have to keep it on until various bus drivers are fixed.

Here's the general flow:
- Parent bus enumerates children, maps IRQs as needed, adds to resource 
list. The struct resources involved aren't special (just a single 
number), so PCIB_ROUTE_INTERRUPT(), MSIs, etc. are trivial to implement 
(and already are implemented, in general, for OF/FDT drivers).
- bus_alloc_resource() does nothing special
- bus_setup_intr() calls into the PIC and does what is needed, as in 
r301453 (to within that #ifdef)

This should keep all the best pieces of everything:
- ACPI and OF are supported together, and easy to extend for other types 
of mapping technologies without breaking either KBI or KPI (just add new 
functions)
- No APIs change for existing Open Firmware code
- The support code can live entirely in machine-dependent directories, 
with no code changes at all required in kern/ or dev/ofw/
- bus_setup_intr() and friends behave as in r301453 and succeed or fail 
for real
- PCI code is not an issue
- No disconnected interrupt state maintained in mapping table (unless 
the transitional #ifdef is on) and the mapping table content is only 
larger than r301453 by having a copy of the original interrupt specifier.

If and when we manage to fix the kernel APIs to allow non-scalar 
interrupt specifiers, this should also be easy to gradually transmogrify 
to support that case since there exists a 1:1 mapping of scalar IRQs to 
rich data and the control flow would be identical: interrupt specifiers 
are read at enumeration time and a resulting handle -- struct resource 
or scalar IRQ -- used afterward to refer to it.

Some more comments below.

>> The ideal API would be one in which the resource enumeration code
>> could assign, instead of numbers, some structured information that
>> contains the full firmware specifier (string parent + interrupt pin
>> for ACPI, interrupt parent phandle + specifier for device tree, bare
>> numbers for simple systems).
> Yes, exactly. I am happy that at least we agree on something. The only
> difference is that we want triplet <OFW, interrupt parent phandle,
> specifier> or <ACPI, string parent, interrupt pin>

Which makes perfect sense and is a good idea.

>> That seemed, and seems, too invasive. I think we agree on this and
>> have chosen to approximate that API in two different ways.
> I still think that we found way how we can realize the above idea in
> non-invasive way.
>  From my point of view the solution is:
> Put/attach the above triplet to RLE and then copy it (within
> bus_resource_attach()  to struct resource.
> But yes, I understand that I am not able to explain clearly enough.

I think we were just talking past eachother somehow.

Anyway, my concern about putting this in struct resource * at 
bus_alloc_resource() time is basically that there are a whole bunch of 
cases in which (a) it's hard to reverse engineer which interrupt from 
the device tree (or whatever) was meant to correspond to a particular 
arbitrary IRQ in an allocation request unless you keep logs and (b) 
there are a bunch of cases, mostly involving PCI, where interrupt 
resource allocation doesn't proceed through struct resource * at all, so 
you pretty much have to keep logs. Once you start keeping logs of which 
IRQ corresponds to which specifier at the device level, you might as 
well just do it once in a centralized place.

>
>> When we designed the interrupt mapping code, we evaluated a large
>> number of possible approaches. Something very much like this was one
>> of them. It was rejected as being too fragile (it requires resource
>> allocation and enumeration to be coupled) and to have too many corner
>> cases (the PCI MSI and LSI APIs, for instance). The interrupt mapping
>> stuff seemed at the time, and so far still seems, like the least-bad
>> approximation to the ideal case: it is essentially that ideal API, in
>> that it happens at bus enumeration and involves just assigning the
>> firmware specifiers, but with lookup keys to that information stuffed
>> into the 32-bit numbers that the rest of the system uses.
> The raw PCI (or MSIX)  case is relatively simple. The PCI domain uses
> raw numbers and it's host-to-pci bridge job to translate raw numbers
> from PCI domain to (for example) OFW based resource.
> At this point, I'm still not sure about MSI...

Right, you can keep a big table in the PCI driver that maps whatever 
IRQs you are handing out to some richer interrupt specifiers and consult 
that when you get a bus_alloc_resource() request. Which is basically the 
approach I'm advocating, except that the table is centralized, which 
reduces code duplication and fixes a number of weird corner cases where 
children assign extra interrupts to themselves, etc. and the bus parent 
has no ability to do something sensible.

Please take a look at the attached interface and see if it (or something 
like it) meets your needs. It meets all of mine, at least, and I think 
fixes all the things you were concerned about.
-Nathan


--------------B749752E283B3662897D27B5
Content-Type: text/plain; charset=UTF-8;
 name="intr.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="intr.h"

#ifndef _ARM_INTR_H
#define _ARM_INTR_H

/* Open Firmware-type mappings */
int map_ofw_irq(uint32_t iparent, uint32_t *ispec, size_t icells);
void register_pic_ofw(device_t dev, uint32_t phandle);
void unregister_pic_ofw(device_t dev);

/* ACPI-type mappings */
int map_acpi_irq(const char *provider, int interrupt, uint8_t polarity,
    uint8_t trigger);
void register_pic_acpi(device_t dev, const char *provider);
void unregister_pic_acpi(device_t dev);

/* PIC interface */
void dispatch_intr(u_int vector, struct trapframe *tf);

#endif


--------------B749752E283B3662897D27B5
Content-Type: text/plain; charset=UTF-8;
 name="intr.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="intr.c"

/*-
 * Copyright 2016 Nathan Whitehorn
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <sys/cpuset.h>
#include <sys/interrupt.h>
#include <sys/ktr.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/pcpu.h>
#include <sys/smp.h>
#include <sys/syslog.h>
#include <sys/vmmeter.h>
#include <sys/proc.h>

static MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data");

struct intr_rec {
	struct intr_event	*event;
	long			*cntp;
	u_int			cntindex;

	device_t		pic;
	cpuset_t		cpu;
	u_int			vector;

	RB_ENTRY(intr_rec)	intr_rec_link;

#ifdef ALLOW_LATE_PIC_ATTACH
	enum intr_trigger	trig;
	enum intr_polarity	pol;
#endif

	union {
		struct {
			void *ofw_spec;
			size_t ofw_spec_len;
			uint32_t ofw_iparent;
		} ofw_spec;
		struct {
			uint8_t acpi_triggering;
			uint8_t acpi_polarity;
			uint32_t acpi_intline;
			const char *acpi_source;
		} acpi_spec;
	} ispec;

	enum {
		IRQ_TYPE_OFW,
		IRQ_TYPE_ACPI,
		IRQ_TYPE_IPI,
	} ispec_type;
};

/* Tree of interrupts: indexes IRQ# (vector) to specifier */

static int intr_rec_compare(struct intr_rec *a, struct intr_rec *b);
static RB_HEAD(intr_rec_tree, intr_rec) intr_list = RB_INITIALIZER(&intr_list);
RB_GENERATE_STATIC(intr_rec_tree, intr_rec, intr_rec_link, intr_rec_compare);
static struct rmlock intr_list_lock;

/* Maps of PICs by ID to device_t */

struct ofw_pic {
	LIST_ENTRY(ofw_pic) ofw_pic_link;

	uint32_t phandle;
	device_t dev;
};
static LIST_HEAD(ofw_pic_head, ofw_pic) ofw_pic_list =
    LIST_HEAD_INITIALIZER(&ofw_pic_list);

struct acpi_pic {
	LIST_ENTRY(acpi_pic) acpi_pic_link;

	const char *acpi_source;
	device_t dev;
};
static LIST_HEAD(acpi_pic_head, acpi_pic) acpi_pic_list =
    LIST_HEAD_INITIALIZER(&acpi_pic_list);

static struct mtx pic_list_lock;

device_t root_pic = NULL;

/*
 * Locking semantics:
 * - intr_list_lock is held for reading whenever an interrupt is being
 *   issued. No non-atomic fields can be updated when held for reading.
 * - intr_list_lock is held for writing whenever non-atomic fields need
 *   to be changed.
 * - When entries are added to or deleted from the table, the intr_list_lock
 *   must be held for writing *and* pic_list_lock must be held.
 * - pic_list_lock is outer to intr_list_lock
 */

static void
intr_init(void *dummy __unused)
{
  
	rm_init(&intr_list_lock, "intr list");
	mtx_init(&pic_list_lock, "PIC list", NULL, MTX_DEF);
}
SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);

/* Utility functions */

static struct intr_rec *
allocate_irq(void)
{
	struct intr_rec *rec = malloc(sizeof(struct intr_rec), M_INTR,
	    M_ZERO | M_WAITOK);

#ifdef SMP
	rec->cpu = all_cpus;
#else
	CPU_SETOF(0, &rec->cpu);
#endif

#ifdef ALLOW_LATE_PIC_ATTACH
	rec->trig = INTR_TRIGGER_CONFORM;
	rec->pol = INTR_POLARITY_CONFORM;
#endif

	return rec;
}

static int
insert_irq(struct intr_rec *rec)
{
	struct intr_rec *existing = NULL;

	rm_assert(&intr_list_lock, RA_WLOCKED);

	/* Allow code to suggest a vector ID. Honor if possible. */
	if (rec->vector != 0) {
		existing = RB_FIND(intr_rec_tree, &intr_list, rec);
		if (existing == NULL)
			goto out;
	}

	/* Otherwise, use min(100, max_irq + 1) */
	existing = RB_MAX(intr_rec_tree, &intr_list);
	if (existing == NULL)
		rec->vector = 100;
	else
		rec->vector = min(existing->vector + 1, 100);

out:
	RB_INSERT(intr_rec_tree, &intr_list, rec);
	return (rec->vector);
}

static struct intr_rec *
find_irq(u_int vector)
{
	struct intr_rec *i, tmp;

	/*
	 * Note on locking: we don't destroy any IRQ mappings once made
	 * (just reuse them if asked for again), so below code is safe
	 */
	tmp.vec = vector;
	rm_rlock(&intr_list_lock);
	i = RB_FIND(intr_rec_tree, &intr_list, &tmp);
	rm_runlock(&intr_list_lock);

	return (i);
}

/* Open Firmware-type mappings */
int
map_ofw_irq(uint32_t iparent, uint32_t *ispec, size_t icells)
{
	struct intr_rec *existing, *newintr;
	struct ofw_pic *pic;
	int irq;

	/*
	 * Allocate early for locking reasons. Allocation is the usual path
	 * anyway.
	 */
	newintr = allocate_irq();

	newintr->ispec_type = IRQ_TYPE_OFW;
	newintr->ispec.ofw_spec.ofw_spec_len = sizeof(ispec[0])*icells;
	newintr->ispec.ofw_spec.ofw_spec = malloc(sizeof(ispec[0])*icells,
	    M_INTR, M_WAITOK);
	memcpy(newintr->ispec.ofw_spec.ofw_spec, ispec,
	    newintr->ispec.ofw_spec.ofw_spec_len);
	newintr->ispec.ofw_iparent = iparent;
	newintr->vector = ispec[0]; /* Usually the interrupt pin. */

	mtx_lock(&pic_list_lock);
	LIST_FOREACH(pic, &ofw_pic_list, ofw_pic_link) {
		if (pic->phandle == iparent)
			break;
	}
	if (pic != NULL)
		newintr->pic = pic->dev;

	if (pic == NULL)
		panic("Open Firmware PIC %#d not registered", iparent);

	rm_wlock(&intr_list_lock);
	existing = NULL;
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->ispec_type != IRQ_TYPE_OFW)
			continue;
		if (existing->ispec.ofw_spec.ofw_iparent != iparent)
			continue;
		if (existing->ispec.ofw_spec.ofw_spec_len !=
		    newintr->ispec.ofw_spec.ofw_spec_len)
			continue;
		if (memcmp(existing->ispec.ofw_spec, ispec,
		    newintr->ispec.ofw_spec_len) == 0)
			break;
	}
	if (existing == NULL)
		irq = insert_irq(newintr);
	else
		irq = existing->vector;
	rm_wunlock(&intr_list_lock);

	if (existing != NULL) { /* Already had one */
		free(newintr->ofw_spec.ofw_spec);
		free(newintr, M_INTR);
	}

	if (pic != NULL && !existing)
		PIC_MAP_OFW(pic->dev, irq, ispec, icells);
	mtx_unlock(&pic_list_lock);

	return (irq);
}

void
register_pic_ofw(device_t dev, uint32_t phandle)
{
	struct intr_rec *existing;
	struct ofw_pic *pic;

	pic = malloc(sizeof(*pic), M_INTR, M_WAITOK);
	pic->dev = dev;
	pic->phandle = phandle;

	mtx_lock(&pic_list_lock);
	LIST_INSERT_HEAD(pic, &ofw_pic_list, ofw_pic_link);

begin_intr_search:
	/*
	 * Associate any previously-mapped interrupts on this PIC with this
	 * PIC.
	 */
	rm_wlock(&intr_list_lock);
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->ispec_type != IRQ_TYPE_OFW)
			continue;
		if (existing->ispec.ofw_spec.ofw_iparent == iparent &&
		    existing->pic == NULL) {
			existing->pic = dev;
			rm_wunlock(&intr_list_lock); /* PIC lock still held */

			PIC_MAP_OFW(dev, existing->vec,
			    existing->ispec.ofw_spec.ofw_ispec,
			    existing->ispec.ofw_spec.ofw_icells);
#ifdef ALLOW_LATE_PIC_ATTACH
			PIC_CONFIG(dev, existing->vec, existing->trig,
			    existing->pol);
#endif
			goto begin_intr_search;
		}
	}
	rm_wunlock(&intr_list_lock);
	mtx_unlock(&pic_list_lock);
}

void
unregister_pic_ofw(device_t dev)
{
	struct intr_rec *existing;
	struct ofw_pic *pic;

	mtx_lock(&pic_list_lock);
	LIST_FOREACH(pic, &ofw_pic_list, ofw_pic_link) {
		if (pic->dev == dev)
			break;
	}
	if (pic != NULL) {
		LIST_REMOVE(pic, ofw_pic_link);
		free(pic, M_INTR);
	}
	mtx_unlock(&pic_list_lock);

	rm_rlock(&intr_list_lock);
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->pic == dev)
			panic("Removing PIC with existing interrupt");
	}
	rm_runlock(&intr_list_lock);
}

/* ACPI-type mappings */

int
map_acpi_irq(const char *provider, int interrupt, uint8_t polarity,
    uint8_t trigger)
{
	struct intr_rec *existing, *newintr;
	char *newprov;
	struct acpi_pic *pic;
	int irq;

	/*
	 * Allocate early for locking reasons. Allocation is the usual path
	 * anyway.
	 */
	newintr = allocate_irq();

	newintr->ispec_type = IRQ_TYPE_ACPI;
	newintr->ispec.acpi_spec.acpi_triggering = trigger;
	newintr->ispec.acpi_spec.acpi_polarity = polarity;
	newintr->ispec.acpi_spec.acpi_intline = interrupt;
	newprov = malloc(strlen(provider) + 1, M_INTR, M_WAITOK);
	strcpy(newprov, provider);
	newintr->ispec.acpi_spec.acpi_source = newprov;
	newintr->vector = interrupt; /* Try for this to make dmesg nice */

	mtx_lock(&pic_list_lock);
	LIST_FOREACH(pic, &acpi_pic_list, acpi_pic_link) {
		if (strcmp(pic->acpi_spec.acpi_source, provider) == 0)
			break;
	}
	if (pic != NULL)
		newintr->pic = pic->dev;

	if (pic == NULL)
		panic("ACPI PIC %s not registered", provider);

	rm_wlock(&intr_list_lock);
	existing = NULL;
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->ispec_type != IRQ_TYPE_ACPI)
			continue;
		if (strcmp(existing->ispec.acpi_spec.acpi_source,
		    provider) != 0)
			continue;
		if (existing->ispec.acpi_spec.acpi_intline == interrupt)
			break;
	}
	if (existing == NULL)
		irq = insert_irq(newintr);
	else
		irq = existing->vector;
	rm_wunlock(&intr_list_lock);

	if (existing != NULL) { /* Already had one */
		free(newintr->acpi_spec.acpi_source);
		free(newintr, M_INTR);
	}

	if (pic != NULL && !existing)
		PIC_MAP_ACPI(pic->dev, irq, interrupt, polarity, trigger);
	mtx_unlock(&pic_list_lock);

	return (irq);
}

void
register_pic_acpi(device_t dev, const char *provider)
{
	struct intr_rec *existing;
	struct acpi_pic *pic;
	char *newprov;

	pic = malloc(sizeof(*pic), M_INTR, M_WAITOK);
	pic->dev = dev;
	newprov = malloc(strlen(provider) + 1, M_INTR, M_WAITOK);
	strcpy(newprov, provider)
	pic->acpi_source = newprov;

	mtx_lock(&pic_list_lock);
	LIST_INSERT_HEAD(pic, &acpi_pic_list, acpi_pic_link);
	mtx_unlock(&pic_list_lock);

begin_intr_search:
	/*
	 * Associate any previously-mapped interrupts on this PIC with this
	 * PIC.
	 */
	rm_wlock(&intr_list_lock);
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->ispec_type != IRQ_TYPE_ACPI)
			continue;
		if (strcmp(existing->ispec.acpi_spec.acpi_source,
		    provider) == 0 && existing->pic == NULL) {
			existing->pic = dev;
			rm_wunlock(&intr_list_lock);

			PIC_MAP_ACPI(dev, existing->vec,
			    existing->ispec.acpi_spec.acpi_intline,
			    existing->ispec.acpi_spec.acpi_polarity,
			    existing->ispec.acpi_spec.acpi_triggering);
#ifdef ALLOW_LATE_PIC_ATTACH
			PIC_CONFIG(dev, existing->vec, existing->trig,
			    existing->pol);
#endif
			goto begin_intr_search;
		}
	}
	rm_wunlock(&intr_list_lock);
}

void
unregister_pic_acpi(device_t dev)
{
	struct intr_rec *existing;
	struct acpi_pic *pic;

	mtx_lock(&pic_list_lock);
	LIST_FOREACH(pic, &acpi_pic_list, acpi_pic_link) {
		if (pic->dev == dev)
			break;
	}
	if (pic != NULL) {
		LIST_REMOVE(pic, acpi_pic_link);
		free(pic->acpi_source, M_INTR);
		free(pic, M_INTR);
	}
	mtx_unlock(&pic_list_lock);

	rm_rlock(&intr_list_lock);
	RB_FOREACH(existing, intr_rec_tree, &intr_list) {
		if (existing->pic == dev)
			panic("Removing PIC with existing interrupt");
	}
	rm_runlock(&intr_list_lock);
}

void
dispatch_intr(u_int vector, struct trapframe *tf)
{
	struct intr_rec *i;
	struct intr_event *ie;

	i = find_irq(vector);
	KASSERT(i != NULL, ("Dispatching unmapped vector %d", vector));

	ie = i->event;
        KASSERT(ie != NULL, ("%s: interrupt without an event", __func__))

	intr_event_handle(ie, tf);
	/* XXX strays? */
}


--------------B749752E283B3662897D27B5--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?24107713-6d50-c21d-ccf1-7dbdb36cc484>