The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/i386/msi.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2006 Yahoo!, Inc.
    3  * All rights reserved.
    4  * Written by: John Baldwin <jhb@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. Neither the name of the author nor the names of any co-contributors
   15  *    may be used to endorse or promote products derived from this software
   16  *    without specific prior written permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 /*
   32  * Support for PCI Message Signalled Interrupts (MSI).  MSI interrupts on
   33  * x86 are basically APIC messages that the northbridge delivers directly
   34  * to the local APICs as if they had come from an I/O APIC.
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __FBSDID("$FreeBSD: releng/8.0/sys/i386/i386/msi.c 198991 2009-11-06 17:09:04Z attilio $");
   39 
   40 #include <sys/param.h>
   41 #include <sys/bus.h>
   42 #include <sys/kernel.h>
   43 #include <sys/lock.h>
   44 #include <sys/malloc.h>
   45 #include <sys/mutex.h>
   46 #include <sys/sx.h>
   47 #include <sys/systm.h>
   48 #include <machine/apicreg.h>
   49 #include <machine/cputypes.h>
   50 #include <machine/md_var.h>
   51 #include <machine/frame.h>
   52 #include <machine/intr_machdep.h>
   53 #include <machine/apicvar.h>
   54 #include <machine/specialreg.h>
   55 #include <dev/pci/pcivar.h>
   56 
   57 /* Fields in address for Intel MSI messages. */
   58 #define MSI_INTEL_ADDR_DEST             0x000ff000
   59 #define MSI_INTEL_ADDR_RH               0x00000008
   60 # define MSI_INTEL_ADDR_RH_ON           0x00000008
   61 # define MSI_INTEL_ADDR_RH_OFF          0x00000000
   62 #define MSI_INTEL_ADDR_DM               0x00000004
   63 # define MSI_INTEL_ADDR_DM_PHYSICAL     0x00000000
   64 # define MSI_INTEL_ADDR_DM_LOGICAL      0x00000004
   65 
   66 /* Fields in data for Intel MSI messages. */
   67 #define MSI_INTEL_DATA_TRGRMOD          IOART_TRGRMOD   /* Trigger mode. */
   68 # define MSI_INTEL_DATA_TRGREDG         IOART_TRGREDG
   69 # define MSI_INTEL_DATA_TRGRLVL         IOART_TRGRLVL
   70 #define MSI_INTEL_DATA_LEVEL            0x00004000      /* Polarity. */
   71 # define MSI_INTEL_DATA_DEASSERT        0x00000000
   72 # define MSI_INTEL_DATA_ASSERT          0x00004000
   73 #define MSI_INTEL_DATA_DELMOD           IOART_DELMOD    /* Delivery mode. */
   74 # define MSI_INTEL_DATA_DELFIXED        IOART_DELFIXED
   75 # define MSI_INTEL_DATA_DELLOPRI        IOART_DELLOPRI
   76 # define MSI_INTEL_DATA_DELSMI          IOART_DELSMI
   77 # define MSI_INTEL_DATA_DELNMI          IOART_DELNMI
   78 # define MSI_INTEL_DATA_DELINIT         IOART_DELINIT
   79 # define MSI_INTEL_DATA_DELEXINT        IOART_DELEXINT
   80 #define MSI_INTEL_DATA_INTVEC           IOART_INTVEC    /* Interrupt vector. */
   81 
   82 /*
   83  * Build Intel MSI message and data values from a source.  AMD64 systems
   84  * seem to be compatible, so we use the same function for both.
   85  */
   86 #define INTEL_ADDR(msi)                                                 \
   87         (MSI_INTEL_ADDR_BASE | (msi)->msi_cpu << 12 |                   \
   88             MSI_INTEL_ADDR_RH_OFF | MSI_INTEL_ADDR_DM_PHYSICAL)
   89 #define INTEL_DATA(msi)                                                 \
   90         (MSI_INTEL_DATA_TRGREDG | MSI_INTEL_DATA_DELFIXED | (msi)->msi_vector)
   91 
   92 static MALLOC_DEFINE(M_MSI, "msi", "PCI MSI");
   93 
   94 /*
   95  * MSI sources are bunched into groups.  This is because MSI forces
   96  * all of the messages to share the address and data registers and
   97  * thus certain properties (such as the local APIC ID target on x86).
   98  * Each group has a 'first' source that contains information global to
   99  * the group.  These fields are marked with (g) below.
  100  *
  101  * Note that local APIC ID is kind of special.  Each message will be
  102  * assigned an ID by the system; however, a group will use the ID from
  103  * the first message.
  104  *
  105  * For MSI-X, each message is isolated.
  106  */
  107 struct msi_intsrc {
  108         struct intsrc msi_intsrc;
  109         device_t msi_dev;               /* Owning device. (g) */
  110         struct msi_intsrc *msi_first;   /* First source in group. */
  111         u_int msi_irq;                  /* IRQ cookie. */
  112         u_int msi_msix;                 /* MSI-X message. */
  113         u_int msi_vector:8;             /* IDT vector. */
  114         u_int msi_cpu:8;                /* Local APIC ID. (g) */
  115         u_int msi_count:8;              /* Messages in this group. (g) */
  116         u_int msi_maxcount:8;           /* Alignment for this group. (g) */
  117         int *msi_irqs;                  /* Group's IRQ list. (g) */
  118 };
  119 
  120 static void     msi_create_source(void);
  121 static void     msi_enable_source(struct intsrc *isrc);
  122 static void     msi_disable_source(struct intsrc *isrc, int eoi);
  123 static void     msi_eoi_source(struct intsrc *isrc);
  124 static void     msi_enable_intr(struct intsrc *isrc);
  125 static void     msi_disable_intr(struct intsrc *isrc);
  126 static int      msi_vector(struct intsrc *isrc);
  127 static int      msi_source_pending(struct intsrc *isrc);
  128 static int      msi_config_intr(struct intsrc *isrc, enum intr_trigger trig,
  129                     enum intr_polarity pol);
  130 static int      msi_assign_cpu(struct intsrc *isrc, u_int apic_id);
  131 
  132 struct pic msi_pic = { msi_enable_source, msi_disable_source, msi_eoi_source,
  133                        msi_enable_intr, msi_disable_intr, msi_vector,
  134                        msi_source_pending, NULL, NULL, msi_config_intr,
  135                        msi_assign_cpu };
  136 
  137 static int msi_enabled;
  138 static int msi_last_irq;
  139 static struct mtx msi_lock;
  140 
  141 static void
  142 msi_enable_source(struct intsrc *isrc)
  143 {
  144 }
  145 
  146 static void
  147 msi_disable_source(struct intsrc *isrc, int eoi)
  148 {
  149 
  150         if (eoi == PIC_EOI)
  151                 lapic_eoi();
  152 }
  153 
  154 static void
  155 msi_eoi_source(struct intsrc *isrc)
  156 {
  157 
  158         lapic_eoi();
  159 }
  160 
  161 static void
  162 msi_enable_intr(struct intsrc *isrc)
  163 {
  164         struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
  165 
  166         apic_enable_vector(msi->msi_cpu, msi->msi_vector);
  167 }
  168 
  169 static void
  170 msi_disable_intr(struct intsrc *isrc)
  171 {
  172         struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
  173 
  174         apic_disable_vector(msi->msi_cpu, msi->msi_vector);
  175 }
  176 
  177 static int
  178 msi_vector(struct intsrc *isrc)
  179 {
  180         struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
  181 
  182         return (msi->msi_irq);
  183 }
  184 
  185 static int
  186 msi_source_pending(struct intsrc *isrc)
  187 {
  188 
  189         return (0);
  190 }
  191 
  192 static int
  193 msi_config_intr(struct intsrc *isrc, enum intr_trigger trig,
  194     enum intr_polarity pol)
  195 {
  196 
  197         return (ENODEV);
  198 }
  199 
  200 static int
  201 msi_assign_cpu(struct intsrc *isrc, u_int apic_id)
  202 {
  203         struct msi_intsrc *sib, *msi = (struct msi_intsrc *)isrc;
  204         int old_vector;
  205         u_int old_id;
  206         int i, vector;
  207 
  208         /*
  209          * Only allow CPUs to be assigned to the first message for an
  210          * MSI group.
  211          */
  212         if (msi->msi_first != msi)
  213                 return (EINVAL);
  214 
  215         /* Store information to free existing irq. */
  216         old_vector = msi->msi_vector;
  217         old_id = msi->msi_cpu;
  218         if (old_id == apic_id)
  219                 return (0);
  220 
  221         /* Allocate IDT vectors on this cpu. */
  222         if (msi->msi_count > 1) {
  223                 KASSERT(msi->msi_msix == 0, ("MSI-X message group"));
  224                 vector = apic_alloc_vectors(apic_id, msi->msi_irqs,
  225                     msi->msi_count, msi->msi_maxcount);
  226         } else
  227                 vector = apic_alloc_vector(apic_id, msi->msi_irq);
  228         if (vector == 0)
  229                 return (ENOSPC);
  230 
  231         msi->msi_cpu = apic_id;
  232         msi->msi_vector = vector;
  233         if (msi->msi_intsrc.is_handlers > 0)
  234                 apic_enable_vector(msi->msi_cpu, msi->msi_vector);
  235         if (bootverbose)
  236                 printf("msi: Assigning %s IRQ %d to local APIC %u vector %u\n",
  237                     msi->msi_msix ? "MSI-X" : "MSI", msi->msi_irq,
  238                     msi->msi_cpu, msi->msi_vector);
  239         for (i = 1; i < msi->msi_count; i++) {
  240                 sib = (struct msi_intsrc *)intr_lookup_source(msi->msi_irqs[i]);
  241                 sib->msi_cpu = apic_id;
  242                 sib->msi_vector = vector + i;
  243                 if (sib->msi_intsrc.is_handlers > 0)
  244                         apic_enable_vector(sib->msi_cpu, sib->msi_vector);
  245                 if (bootverbose)
  246                         printf(
  247                     "msi: Assigning MSI IRQ %d to local APIC %u vector %u\n",
  248                             sib->msi_irq, sib->msi_cpu, sib->msi_vector);
  249         }
  250         pci_remap_msi_irq(msi->msi_dev, msi->msi_irq);
  251 
  252         /*
  253          * Free the old vector after the new one is established.  This is done
  254          * to prevent races where we could miss an interrupt.
  255          */
  256         if (msi->msi_intsrc.is_handlers > 0)
  257                 apic_disable_vector(old_id, old_vector);
  258         apic_free_vector(old_id, old_vector, msi->msi_irq);
  259         for (i = 1; i < msi->msi_count; i++) {
  260                 sib = (struct msi_intsrc *)intr_lookup_source(msi->msi_irqs[i]);
  261                 if (sib->msi_intsrc.is_handlers > 0)
  262                         apic_disable_vector(old_id, old_vector + i);
  263                 apic_free_vector(old_id, old_vector + i, msi->msi_irqs[i]);
  264         }
  265         return (0);
  266 }
  267 
  268 void
  269 msi_init(void)
  270 {
  271 
  272         /* Check if we have a supported CPU. */
  273         switch (cpu_vendor_id) {
  274         case CPU_VENDOR_INTEL:
  275         case CPU_VENDOR_AMD:
  276                 break;
  277         case CPU_VENDOR_CENTAUR:
  278                 if (CPUID_TO_FAMILY(cpu_id) == 0x6 &&
  279                     CPUID_TO_MODEL(cpu_id) >= 0xf)
  280                         break;
  281                 /* FALLTHROUGH */
  282         default:
  283                 return;
  284         }
  285 
  286         msi_enabled = 1;
  287         intr_register_pic(&msi_pic);
  288         mtx_init(&msi_lock, "msi", NULL, MTX_DEF);
  289 }
  290 
  291 void
  292 msi_create_source(void)
  293 {
  294         struct msi_intsrc *msi;
  295         u_int irq;
  296 
  297         mtx_lock(&msi_lock);
  298         if (msi_last_irq >= NUM_MSI_INTS) {
  299                 mtx_unlock(&msi_lock);
  300                 return;
  301         }
  302         irq = msi_last_irq + FIRST_MSI_INT;
  303         msi_last_irq++;
  304         mtx_unlock(&msi_lock);
  305 
  306         msi = malloc(sizeof(struct msi_intsrc), M_MSI, M_WAITOK | M_ZERO);
  307         msi->msi_intsrc.is_pic = &msi_pic;
  308         msi->msi_irq = irq;
  309         intr_register_source(&msi->msi_intsrc);
  310         nexus_add_irq(irq);
  311 }
  312 
  313 /*
  314  * Try to allocate 'count' interrupt sources with contiguous IDT values.
  315  */
  316 int
  317 msi_alloc(device_t dev, int count, int maxcount, int *irqs)
  318 {
  319         struct msi_intsrc *msi, *fsrc;
  320         u_int cpu;
  321         int cnt, i, *mirqs, vector;
  322 
  323         if (!msi_enabled)
  324                 return (ENXIO);
  325 
  326         if (count > 1)
  327                 mirqs = malloc(count * sizeof(*mirqs), M_MSI, M_WAITOK);
  328         else
  329                 mirqs = NULL;
  330 again:
  331         mtx_lock(&msi_lock);
  332 
  333         /* Try to find 'count' free IRQs. */
  334         cnt = 0;
  335         for (i = FIRST_MSI_INT; i < FIRST_MSI_INT + NUM_MSI_INTS; i++) {
  336                 msi = (struct msi_intsrc *)intr_lookup_source(i);
  337 
  338                 /* End of allocated sources, so break. */
  339                 if (msi == NULL)
  340                         break;
  341 
  342                 /* If this is a free one, save its IRQ in the array. */
  343                 if (msi->msi_dev == NULL) {
  344                         irqs[cnt] = i;
  345                         cnt++;
  346                         if (cnt == count)
  347                                 break;
  348                 }
  349         }
  350 
  351         /* Do we need to create some new sources? */
  352         if (cnt < count) {
  353                 /* If we would exceed the max, give up. */
  354                 if (i + (count - cnt) > FIRST_MSI_INT + NUM_MSI_INTS) {
  355                         mtx_unlock(&msi_lock);
  356                         free(mirqs, M_MSI);
  357                         return (ENXIO);
  358                 }
  359                 mtx_unlock(&msi_lock);
  360 
  361                 /* We need count - cnt more sources. */
  362                 while (cnt < count) {
  363                         msi_create_source();
  364                         cnt++;
  365                 }
  366                 goto again;
  367         }
  368 
  369         /* Ok, we now have the IRQs allocated. */
  370         KASSERT(cnt == count, ("count mismatch"));
  371 
  372         /* Allocate 'count' IDT vectors. */
  373         cpu = intr_next_cpu();
  374         vector = apic_alloc_vectors(cpu, irqs, count, maxcount);
  375         if (vector == 0) {
  376                 mtx_unlock(&msi_lock);
  377                 free(mirqs, M_MSI);
  378                 return (ENOSPC);
  379         }
  380 
  381         /* Assign IDT vectors and make these messages owned by 'dev'. */
  382         fsrc = (struct msi_intsrc *)intr_lookup_source(irqs[0]);
  383         for (i = 0; i < count; i++) {
  384                 msi = (struct msi_intsrc *)intr_lookup_source(irqs[i]);
  385                 msi->msi_cpu = cpu;
  386                 msi->msi_dev = dev;
  387                 msi->msi_vector = vector + i;
  388                 if (bootverbose)
  389                         printf(
  390                     "msi: routing MSI IRQ %d to local APIC %u vector %u\n",
  391                             msi->msi_irq, msi->msi_cpu, msi->msi_vector);
  392                 msi->msi_first = fsrc;
  393                 KASSERT(msi->msi_intsrc.is_handlers == 0,
  394                     ("dead MSI has handlers"));
  395         }
  396         fsrc->msi_count = count;
  397         fsrc->msi_maxcount = maxcount;
  398         if (count > 1)
  399                 bcopy(irqs, mirqs, count * sizeof(*mirqs));
  400         fsrc->msi_irqs = mirqs;
  401         mtx_unlock(&msi_lock);
  402 
  403         return (0);
  404 }
  405 
  406 int
  407 msi_release(int *irqs, int count)
  408 {
  409         struct msi_intsrc *msi, *first;
  410         int i;
  411 
  412         mtx_lock(&msi_lock);
  413         first = (struct msi_intsrc *)intr_lookup_source(irqs[0]);
  414         if (first == NULL) {
  415                 mtx_unlock(&msi_lock);
  416                 return (ENOENT);
  417         }
  418 
  419         /* Make sure this isn't an MSI-X message. */
  420         if (first->msi_msix) {
  421                 mtx_unlock(&msi_lock);
  422                 return (EINVAL);
  423         }
  424 
  425         /* Make sure this message is allocated to a group. */
  426         if (first->msi_first == NULL) {
  427                 mtx_unlock(&msi_lock);
  428                 return (ENXIO);
  429         }
  430 
  431         /*
  432          * Make sure this is the start of a group and that we are releasing
  433          * the entire group.
  434          */
  435         if (first->msi_first != first || first->msi_count != count) {
  436                 mtx_unlock(&msi_lock);
  437                 return (EINVAL);
  438         }
  439         KASSERT(first->msi_dev != NULL, ("unowned group"));
  440 
  441         /* Clear all the extra messages in the group. */
  442         for (i = 1; i < count; i++) {
  443                 msi = (struct msi_intsrc *)intr_lookup_source(irqs[i]);
  444                 KASSERT(msi->msi_first == first, ("message not in group"));
  445                 KASSERT(msi->msi_dev == first->msi_dev, ("owner mismatch"));
  446                 msi->msi_first = NULL;
  447                 msi->msi_dev = NULL;
  448                 apic_free_vector(msi->msi_cpu, msi->msi_vector, msi->msi_irq);
  449                 msi->msi_vector = 0;
  450         }
  451 
  452         /* Clear out the first message. */
  453         first->msi_first = NULL;
  454         first->msi_dev = NULL;
  455         apic_free_vector(first->msi_cpu, first->msi_vector, first->msi_irq);
  456         first->msi_vector = 0;
  457         first->msi_count = 0;
  458         first->msi_maxcount = 0;
  459         free(first->msi_irqs, M_MSI);
  460         first->msi_irqs = NULL;
  461 
  462         mtx_unlock(&msi_lock);
  463         return (0);
  464 }
  465 
  466 int
  467 msi_map(int irq, uint64_t *addr, uint32_t *data)
  468 {
  469         struct msi_intsrc *msi;
  470 
  471         mtx_lock(&msi_lock);
  472         msi = (struct msi_intsrc *)intr_lookup_source(irq);
  473         if (msi == NULL) {
  474                 mtx_unlock(&msi_lock);
  475                 return (ENOENT);
  476         }
  477 
  478         /* Make sure this message is allocated to a device. */
  479         if (msi->msi_dev == NULL) {
  480                 mtx_unlock(&msi_lock);
  481                 return (ENXIO);
  482         }
  483 
  484         /*
  485          * If this message isn't an MSI-X message, make sure it's part
  486          * of a group, and switch to the first message in the
  487          * group.
  488          */
  489         if (!msi->msi_msix) {
  490                 if (msi->msi_first == NULL) {
  491                         mtx_unlock(&msi_lock);
  492                         return (ENXIO);
  493                 }
  494                 msi = msi->msi_first;
  495         }
  496 
  497         *addr = INTEL_ADDR(msi);
  498         *data = INTEL_DATA(msi);
  499         mtx_unlock(&msi_lock);
  500         return (0);
  501 }
  502 
  503 int
  504 msix_alloc(device_t dev, int *irq)
  505 {
  506         struct msi_intsrc *msi;
  507         u_int cpu;
  508         int i, vector;
  509 
  510         if (!msi_enabled)
  511                 return (ENXIO);
  512 
  513 again:
  514         mtx_lock(&msi_lock);
  515 
  516         /* Find a free IRQ. */
  517         for (i = FIRST_MSI_INT; i < FIRST_MSI_INT + NUM_MSI_INTS; i++) {
  518                 msi = (struct msi_intsrc *)intr_lookup_source(i);
  519 
  520                 /* End of allocated sources, so break. */
  521                 if (msi == NULL)
  522                         break;
  523 
  524                 /* Stop at the first free source. */
  525                 if (msi->msi_dev == NULL)
  526                         break;
  527         }
  528 
  529         /* Do we need to create a new source? */
  530         if (msi == NULL) {
  531                 /* If we would exceed the max, give up. */
  532                 if (i + 1 > FIRST_MSI_INT + NUM_MSI_INTS) {
  533                         mtx_unlock(&msi_lock);
  534                         return (ENXIO);
  535                 }
  536                 mtx_unlock(&msi_lock);
  537 
  538                 /* Create a new source. */
  539                 msi_create_source();
  540                 goto again;
  541         }
  542 
  543         /* Allocate an IDT vector. */
  544         cpu = intr_next_cpu();
  545         vector = apic_alloc_vector(cpu, i);
  546         if (vector == 0) {
  547                 mtx_unlock(&msi_lock);
  548                 return (ENOSPC);
  549         }
  550         if (bootverbose)
  551                 printf("msi: routing MSI-X IRQ %d to local APIC %u vector %u\n",
  552                     msi->msi_irq, cpu, vector);
  553 
  554         /* Setup source. */
  555         msi->msi_cpu = cpu;
  556         msi->msi_dev = dev;
  557         msi->msi_first = msi;
  558         msi->msi_vector = vector;
  559         msi->msi_msix = 1;
  560         msi->msi_count = 1;
  561         msi->msi_maxcount = 1;
  562         msi->msi_irqs = NULL;
  563 
  564         KASSERT(msi->msi_intsrc.is_handlers == 0, ("dead MSI-X has handlers"));
  565         mtx_unlock(&msi_lock);
  566 
  567         *irq = i;
  568         return (0);
  569 }
  570 
  571 int
  572 msix_release(int irq)
  573 {
  574         struct msi_intsrc *msi;
  575 
  576         mtx_lock(&msi_lock);
  577         msi = (struct msi_intsrc *)intr_lookup_source(irq);
  578         if (msi == NULL) {
  579                 mtx_unlock(&msi_lock);
  580                 return (ENOENT);
  581         }
  582 
  583         /* Make sure this is an MSI-X message. */
  584         if (!msi->msi_msix) {
  585                 mtx_unlock(&msi_lock);
  586                 return (EINVAL);
  587         }
  588 
  589         KASSERT(msi->msi_dev != NULL, ("unowned message"));
  590 
  591         /* Clear out the message. */
  592         msi->msi_first = NULL;
  593         msi->msi_dev = NULL;
  594         apic_free_vector(msi->msi_cpu, msi->msi_vector, msi->msi_irq);
  595         msi->msi_vector = 0;
  596         msi->msi_msix = 0;
  597         msi->msi_count = 0;
  598         msi->msi_maxcount = 0;
  599 
  600         mtx_unlock(&msi_lock);
  601         return (0);
  602 }

Cache object: 14339489e18cd4d079094b7a25e89808


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.