From owner-freebsd-arm@freebsd.org Sun Jul 31 06:11:39 2016 Return-Path: Delivered-To: freebsd-arm@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id D8997BA9C0E; Sun, 31 Jul 2016 06:11:39 +0000 (UTC) (envelope-from nwhitehorn@freebsd.org) Received: from c.mail.sonic.net (c.mail.sonic.net [64.142.111.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id BA55011EB; Sun, 31 Jul 2016 06:11:39 +0000 (UTC) (envelope-from nwhitehorn@freebsd.org) Received: from zeppelin.tachypleus.net (75-101-50-44.static.sonic.net [75.101.50.44]) (authenticated bits=0) by c.mail.sonic.net (8.15.1/8.15.1) with ESMTPSA id u6V6BTAl015001 (version=TLSv1.2 cipher=DHE-RSA-AES128-SHA bits=128 verify=NOT); Sat, 30 Jul 2016 23:11:30 -0700 Subject: Re: INTRNG (Was: svn commit: r301453....) To: Michal Meloun References: <201606051620.u55GKD5S066398@repo.freebsd.org> <5794720F.4050303@FreeBSD.org> <8bfd8668-bc49-e109-e610-b5cd470be3ec@freebsd.org> <57950005.6070403@FreeBSD.org> <57961549.4020105@FreeBSD.org> <57976867.6080705@FreeBSD.org> <5798E104.5020104@FreeBSD.org> <579A25BB.8070206@FreeBSD.org> <30790e40-58b4-3371-c0f0-b7545571f389@freebsd.org> <579AFFC5.1040005@FreeBSD.org> <579CD355.1050203@FreeBSD.org> <460fa0b3-ddb7-6247-2412-3d75a589d5e7@freebsd.org> <579CF7C8.1040302@FreeBSD.org> Cc: "freebsd-arm@freebsd.org" , Svatopluk Kraus , "freebsd-arch@freebsd.org" From: Nathan Whitehorn Message-ID: <24107713-6d50-c21d-ccf1-7dbdb36cc484@freebsd.org> Date: Sat, 30 Jul 2016 23:11:29 -0700 User-Agent: Mozilla/5.0 (X11; FreeBSD amd64; rv:45.0) Gecko/20100101 Thunderbird/45.1.0 MIME-Version: 1.0 In-Reply-To: <579CF7C8.1040302@FreeBSD.org> Content-Type: multipart/mixed; boundary="------------B749752E283B3662897D27B5" X-Sonic-CAuth: UmFuZG9tSVY7JnB2hp4xwcXi2+33i82Up9sMkb7R78poh7TNRC/sop5gnkeK+TCcQg5Jo5M5noycmkDqPWgzI7KwZOyRBCsx2IhbssMPEeY= X-Sonic-ID: C;wl/enuVW5hGIlaDx2xNB0g== M;oJ45n+VW5hGIlaDx2xNB0g== X-Spam-Flag: No X-Sonic-Spam-Details: 0.0/5.0 by cerberusd X-BeenThere: freebsd-arm@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "Porting FreeBSD to ARM processors." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 31 Jul 2016 06:11:39 -0000 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: > > 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) > >> 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 specifier> or 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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--