diff mbox

[v2,RESEND,1/5] apic: Implement LAPIC low priority arbitration functions

Message ID 1428363937-19003-2-git-send-email-sullivan.james.f@gmail.com
State New
Headers show

Commit Message

James Sullivan April 6, 2015, 11:45 p.m. UTC
Currently, apic_get_arb_pri() is unimplemented and returns 0.

Implemented apic_get_arb_pri() and added two helper functions
apic_compare_prio() and apic_lowest_prio() to be used for LAPIC
arbitration.

Signed-off-by: James Sullivan <sullivan.james.f@gmail.com>
---
 hw/intc/apic.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)

Comments

Radim Krčmář April 23, 2015, 1:49 p.m. UTC | #1
2015-04-06 17:45-0600, James Sullivan:
> Currently, apic_get_arb_pri() is unimplemented and returns 0.
> 
> Implemented apic_get_arb_pri() and added two helper functions
> apic_compare_prio() and apic_lowest_prio() to be used for LAPIC
> arbitration.
> 
> Signed-off-by: James Sullivan <sullivan.james.f@gmail.com>
> ---
> +static int apic_compare_prio(struct APICCommonState *cpu1,
> +                             struct APICCommonState *cpu2)
> +{
> +    return apic_get_arb_pri(cpu1) - apic_get_arb_pri(cpu2);
> +}
> +
> +static struct APICCommonState *apic_lowest_prio(const uint32_t
> +                                                *deliver_bitmask)
> +{
> +    APICCommonState *lowest = NULL;
> +    int i, d;
> +
> +    for (i = 0; i < MAX_APIC_WORDS; i++) {
> +        if (deliver_bitmask[i]) {
> +            d = i * 32 + apic_ffs_bit(deliver_bitmask[i]);
> +            if (!lowest || apic_compare_prio(local_apics[d], lowest) < 0) {
> +                lowest = local_apics[d];

deliver_bitmask is 8 times u32 to express all 255 possible APICs.
apic_ffs_bit() takes the last set bit, so this loop incorrectly
considers only up to 8 candidates for lowest priority delivery.
foreach_apic() could be used when fixing it, which would also avoid a
'local_apic[d] == NULL' crash.

> +            }
> +        }
> +    }
> +    return lowest;

(For consideration:
 APM 2-16.2.2 Lowest Priority Messages and Arbitration
   If there is a tie for lowest priority, the local APIC with the highest
   APIC ID is selected.

 Intel is undefined in spec and picks the lowest APIC ID in practice.)

> @@ -336,8 +360,27 @@ static int apic_get_ppr(APICCommonState *s)
>  
>  static int apic_get_arb_pri(APICCommonState *s)

(I'd call it apic_get_apr() -- we return the state of that register.)

>  {
> -    /* XXX: arbitration */
> -    return 0;
> +    int tpr, isrv, irrv, apr;
> +
> +    tpr = apic_get_tpr(s);
> +    isrv = get_highest_priority_int(s->isr);
> +    if (isrv < 0) {
> +        isrv = 0;
> +    }
> +    isrv >>= 4;
> +    irrv = get_highest_priority_int(s->irr);
> +    if (irrv < 0) {
> +        irrv = 0;
> +    }
> +    irrv >>= 4;
> +
> +    if ((tpr >= irrv) && (tpr > isrv)) {
> +        apr = s->tpr & 0xff;
> +    } else {
> +        apr = ((tpr & isrv) > irrv) ? (tpr & isrv) : irrv;
> +        apr <<= 4;
> +    }
> +    return apr;
>  }

(This function is called too much IMO.
 The trivial optimization is to memorize apic_get_arb_pri() of lowest
 priority LAPIC.  And you can instantly return if it is 0.
 The more complicated one is to use ARB as a real LAPIC register and
 update it on TPR/ISR/IRR change, so apic_get_arb_pri() would be just
 'return s->apr;'.)
James Sullivan April 23, 2015, 6:34 p.m. UTC | #2
On 04/23/2015 07:49 AM, Radim Krčmář wrote:
> 2015-04-06 17:45-0600, James Sullivan:
>> Currently, apic_get_arb_pri() is unimplemented and returns 0.
>>
>> Implemented apic_get_arb_pri() and added two helper functions
>> apic_compare_prio() and apic_lowest_prio() to be used for LAPIC
>> arbitration.
>>
>> Signed-off-by: James Sullivan <sullivan.james.f@gmail.com>
>> ---
>> +static int apic_compare_prio(struct APICCommonState *cpu1,
>> +                             struct APICCommonState *cpu2)
>> +{
>> +    return apic_get_arb_pri(cpu1) - apic_get_arb_pri(cpu2);
>> +}
>> +
>> +static struct APICCommonState *apic_lowest_prio(const uint32_t
>> +                                                *deliver_bitmask)
>> +{
>> +    APICCommonState *lowest = NULL;
>> +    int i, d;
>> +
>> +    for (i = 0; i < MAX_APIC_WORDS; i++) {
>> +        if (deliver_bitmask[i]) {
>> +            d = i * 32 + apic_ffs_bit(deliver_bitmask[i]);
>> +            if (!lowest || apic_compare_prio(local_apics[d], lowest) < 0) {
>> +                lowest = local_apics[d];
> 
> deliver_bitmask is 8 times u32 to express all 255 possible APICs.
> apic_ffs_bit() takes the last set bit, so this loop incorrectly
> considers only up to 8 candidates for lowest priority delivery.
> foreach_apic() could be used when fixing it, which would also avoid a
> 'local_apic[d] == NULL' crash.
> 

Dumb mistake, I'll fix the former point. I shied away from
foreach_apic() because I think it's a bit messy to embed a block or an
`if` statement into the macro like so:

foreach_apic(apic,bmask,
	if (cond)
	    foo();
);

But if people are okay with that sort of thing we can switch to it.

>> +            }
>> +        }
>> +    }
>> +    return lowest;
> 
> (For consideration:
>  APM 2-16.2.2 Lowest Priority Messages and Arbitration
>    If there is a tie for lowest priority, the local APIC with the highest
>    APIC ID is selected.
> 
>  Intel is undefined in spec and picks the lowest APIC ID in practice.)
> 

Pretty quick change there, set lowest = local_apics[d] when
apic_compare_prio(local_apics[d], lowest) <= 0 rather than < 0

>> @@ -336,8 +360,27 @@ static int apic_get_ppr(APICCommonState *s)
>>  
>>  static int apic_get_arb_pri(APICCommonState *s)
> 
> (I'd call it apic_get_apr() -- we return the state of that register.)
>

Good point, more consistent with other functions too.

>>  {
>> -    /* XXX: arbitration */
>> -    return 0;
>> +    int tpr, isrv, irrv, apr;
>> +
>> +    tpr = apic_get_tpr(s);
>> +    isrv = get_highest_priority_int(s->isr);
>> +    if (isrv < 0) {
>> +        isrv = 0;
>> +    }
>> +    isrv >>= 4;
>> +    irrv = get_highest_priority_int(s->irr);
>> +    if (irrv < 0) {
>> +        irrv = 0;
>> +    }
>> +    irrv >>= 4;
>> +
>> +    if ((tpr >= irrv) && (tpr > isrv)) {
>> +        apr = s->tpr & 0xff;
>> +    } else {
>> +        apr = ((tpr & isrv) > irrv) ? (tpr & isrv) : irrv;
>> +        apr <<= 4;
>> +    }
>> +    return apr;
>>  }
> 
> (This function is called too much IMO.
>  The trivial optimization is to memorize apic_get_arb_pri() of lowest
>  priority LAPIC.  And you can instantly return if it is 0.
>  The more complicated one is to use ARB as a real LAPIC register and
>  update it on TPR/ISR/IRR change, so apic_get_arb_pri() would be just
>  'return s->apr;'.)
> 

The latter approach would be smart, I'll look into it.
Radim Krčmář April 24, 2015, 12:27 p.m. UTC | #3
2015-04-23 12:34-0600, James Sullivan:
> On 04/23/2015 07:49 AM, Radim Krčmář wrote:
>> 2015-04-06 17:45-0600, James Sullivan:
>>> Currently, apic_get_arb_pri() is unimplemented and returns 0.
>>>
>>> Implemented apic_get_arb_pri() and added two helper functions
>>> apic_compare_prio() and apic_lowest_prio() to be used for LAPIC
>>> arbitration.
>>>
>>> Signed-off-by: James Sullivan <sullivan.james.f@gmail.com>
>>> ---
>>> +    for (i = 0; i < MAX_APIC_WORDS; i++) {
>>> +        if (deliver_bitmask[i]) {
>>> +            d = i * 32 + apic_ffs_bit(deliver_bitmask[i]);
>>> +            if (!lowest || apic_compare_prio(local_apics[d], lowest) < 0) {
>>> +                lowest = local_apics[d];
>> 
>> deliver_bitmask is 8 times u32 to express all 255 possible APICs.
>> apic_ffs_bit() takes the last set bit, so this loop incorrectly
>> considers only up to 8 candidates for lowest priority delivery.
>> foreach_apic() could be used when fixing it, which would also avoid a
>> 'local_apic[d] == NULL' crash.
>> 
> 
> Dumb mistake, I'll fix the former point. I shied away from
> foreach_apic() because I think it's a bit messy to embed a block or an
> `if` statement into the macro like so:
> 
> foreach_apic(apic,bmask,
> 	if (cond)
> 	    foo();
> );
> 
> But if people are okay with that sort of thing we can switch to it.

Yeah, I wouldn't use it either :)
It could use the canonical form (and optimizations for sparse bitmasks)
  foreach(...)
    code;

Moving this logic to the loop in [4/4] would be an elegant solution.

> > (For consideration:
> >  APM 2-16.2.2 Lowest Priority Messages and Arbitration
> >    If there is a tie for lowest priority, the local APIC with the highest
> >    APIC ID is selected.
> > 
> >  Intel is undefined in spec and picks the lowest APIC ID in practice.)
> > 
> 
> Pretty quick change there, set lowest = local_apics[d] when
> apic_compare_prio(local_apics[d], lowest) <= 0 rather than < 0

local_apics[] aren't indexed by APIC ID, which brings two complications
- QEMU allows writing to APIC ID.
  Luckily, the spec allows us to make it read only, but even then
- local_apics[] might not be ordered like their APIC IDs; (I don't know)
  and future development can change that, so we should also encode the
  expecation somewhere.  (Comments don't work very well.)

Taking a look at the real APIC ID would be safest.
(Silently ignoring it is also a viable option.)
diff mbox

Patch

diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 0f97b47..b372513 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -38,6 +38,7 @@  static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
 static void apic_update_irq(APICCommonState *s);
 static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
                                       uint8_t dest, uint8_t dest_mode);
+static int apic_get_arb_pri(APICCommonState *s);
 
 /* Find first bit starting from msb */
 static int apic_fls_bit(uint32_t value)
@@ -199,6 +200,29 @@  static void apic_external_nmi(APICCommonState *s)
     apic_local_deliver(s, APIC_LVT_LINT1);
 }
 
+static int apic_compare_prio(struct APICCommonState *cpu1,
+                             struct APICCommonState *cpu2)
+{
+    return apic_get_arb_pri(cpu1) - apic_get_arb_pri(cpu2);
+}
+
+static struct APICCommonState *apic_lowest_prio(const uint32_t
+                                                *deliver_bitmask)
+{
+    APICCommonState *lowest = NULL;
+    int i, d;
+
+    for (i = 0; i < MAX_APIC_WORDS; i++) {
+        if (deliver_bitmask[i]) {
+            d = i * 32 + apic_ffs_bit(deliver_bitmask[i]);
+            if (!lowest || apic_compare_prio(local_apics[d], lowest) < 0) {
+                lowest = local_apics[d];
+            }
+        }
+    }
+    return lowest;
+}
+
 #define foreach_apic(apic, deliver_bitmask, code) \
 {\
     int __i, __j;\
@@ -336,8 +360,27 @@  static int apic_get_ppr(APICCommonState *s)
 
 static int apic_get_arb_pri(APICCommonState *s)
 {
-    /* XXX: arbitration */
-    return 0;
+    int tpr, isrv, irrv, apr;
+
+    tpr = apic_get_tpr(s);
+    isrv = get_highest_priority_int(s->isr);
+    if (isrv < 0) {
+        isrv = 0;
+    }
+    isrv >>= 4;
+    irrv = get_highest_priority_int(s->irr);
+    if (irrv < 0) {
+        irrv = 0;
+    }
+    irrv >>= 4;
+
+    if ((tpr >= irrv) && (tpr > isrv)) {
+        apr = s->tpr & 0xff;
+    } else {
+        apr = ((tpr & isrv) > irrv) ? (tpr & isrv) : irrv;
+        apr <<= 4;
+    }
+    return apr;
 }