diff mbox series

[v4,06/18] x86/apic: Add update_vector callback for Secure AVIC

Message ID 20250417091708.215826-7-Neeraj.Upadhyay@amd.com (mailing list archive)
State New
Headers show
Series AMD: Add Secure AVIC Guest Support | expand

Commit Message

Neeraj Upadhyay April 17, 2025, 9:16 a.m. UTC
Add update_vector callback to set/clear ALLOWED_IRR field in
a vCPU's APIC backing page for external vectors. The ALLOWED_IRR
field indicates the interrupt vectors which the guest allows the
hypervisor to send (typically for emulated devices). Interrupt
vectors used exclusively by the guest itself and the vectors which
are not emulated by the hypervisor, such as IPI vectors, are part
of system vectors and are not set in the ALLOWED_IRR.

Co-developed-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
---
Changes since v3:

 - Moved apic_update_vector() to apic.h
 - Restructured update_vector() in x2apic_savic.c.

 arch/x86/include/asm/apic.h         |  9 +++++
 arch/x86/kernel/apic/vector.c       | 53 ++++++++++++++++++++++-------
 arch/x86/kernel/apic/x2apic_savic.c | 35 +++++++++++++++++++
 3 files changed, 85 insertions(+), 12 deletions(-)

Comments

Thomas Gleixner April 17, 2025, 10:50 a.m. UTC | #1
On Thu, Apr 17 2025 at 14:46, Neeraj Upadhyay wrote:
> Add update_vector callback to set/clear ALLOWED_IRR field in
> a vCPU's APIC backing page for external vectors. The ALLOWED_IRR
> field indicates the interrupt vectors which the guest allows the
> hypervisor to send (typically for emulated devices). Interrupt
> vectors used exclusively by the guest itself and the vectors which
> are not emulated by the hypervisor, such as IPI vectors, are part
> of system vectors and are not set in the ALLOWED_IRR.

Please structure changelogs properly in paragraphs:

  https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#changelog

>  arch/x86/include/asm/apic.h         |  9 +++++
>  arch/x86/kernel/apic/vector.c       | 53 ++++++++++++++++++++++-------
>  arch/x86/kernel/apic/x2apic_savic.c | 35 +++++++++++++++++++

And split this patch up into two:

  1) Do the modifications in vector.c which is what the $Subject line
     says

  2) Add the SAVIC specific bits

> @@ -471,6 +473,12 @@ static __always_inline bool apic_id_valid(u32 apic_id)
>  	return apic_id <= apic->max_apic_id;
>  }
>  
> +static __always_inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set)
> +{
> +	if (apic->update_vector)
> +		apic->update_vector(cpu, vector, set);
> +}

This is in the public header because it can?
  
> -static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
> -			       unsigned int newcpu)
> +static int irq_alloc_vector(const struct cpumask *dest, bool resvd, unsigned int *cpu)
> +{
> +	int vector;
> +
> +	vector = irq_matrix_alloc(vector_matrix, dest, resvd, cpu);

        int vector = irq_matrix_alloc(...);

> +
> +	if (vector >= 0)
> +		apic_update_vector(*cpu, vector, true);
> +
> +	return vector;
> +}
> +
> +static int irq_alloc_managed_vector(unsigned int *cpu)
> +{
> +	int vector;
> +
> +	vector = irq_matrix_alloc_managed(vector_matrix, vector_searchmask, cpu);
> +
> +	if (vector >= 0)
> +		apic_update_vector(*cpu, vector, true);
> +
> +	return vector;
> +}

I completely fail to see the value of these two functions. Each of them
has exactly _ONE_ call site and both sites invoke apic_update_vector()
when the allocation succeeded. Why can't you just do the obvious and
leave the existing code alone and add

      if (apic->update_vector)
      		apic->update_vector();

into apic_update_vector()? But then you have another place where you
need the update, which does not invoke apic_update_vector().

Now if you look deeper, then you notice that all places which invoke
apic_update_vector() invoke apic_update_irq_cfg(), which is also called
at this other place, no?

> +static void irq_free_vector(unsigned int cpu, unsigned int vector, bool managed)
> +{
> +	apic_update_vector(cpu, vector, false);
> +	irq_matrix_free(vector_matrix, cpu, vector, managed);
> +}

This one makes sense, but please name it: apic_free_vector()

Something like the uncompiled below, no?

Thanks,

        tglx
---
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -134,9 +134,19 @@ static void apic_update_irq_cfg(struct i
 
 	apicd->hw_irq_cfg.vector = vector;
 	apicd->hw_irq_cfg.dest_apicid = apic->calc_dest_apicid(cpu);
+
+	if (apic->update_vector)
+		apic->update_vector(cpu, vector, true);
+
 	irq_data_update_effective_affinity(irqd, cpumask_of(cpu));
-	trace_vector_config(irqd->irq, vector, cpu,
-			    apicd->hw_irq_cfg.dest_apicid);
+	trace_vector_config(irqd->irq, vector, cpu, apicd->hw_irq_cfg.dest_apicid);
+}
+
+static void apic_free_vector(unsigned int cpu, unsigned int vector, bool managed)
+{
+	if (apic->update_vector)
+		apic->update_vector(cpu, vector, false);
+	irq_matrix_free(vector_matrix, cpu, vector, managed);
 }
 
 static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
@@ -174,8 +184,7 @@ static void apic_update_vector(struct ir
 		apicd->prev_cpu = apicd->cpu;
 		WARN_ON_ONCE(apicd->cpu == newcpu);
 	} else {
-		irq_matrix_free(vector_matrix, apicd->cpu, apicd->vector,
-				managed);
+		apic_free_vector(apicd->cpu, apicd->vector, managed);
 	}
 
 setnew:
@@ -183,6 +192,7 @@ static void apic_update_vector(struct ir
 	apicd->cpu = newcpu;
 	BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec]));
 	per_cpu(vector_irq, newcpu)[newvec] = desc;
+	apic_update_irq_cfg(irqd, newvec, newcpu);
 }
 
 static void vector_assign_managed_shutdown(struct irq_data *irqd)
@@ -261,8 +271,6 @@ assign_vector_locked(struct irq_data *ir
 	if (vector < 0)
 		return vector;
 	apic_update_vector(irqd, vector, cpu);
-	apic_update_irq_cfg(irqd, vector, cpu);
-
 	return 0;
 }
 
@@ -338,7 +346,6 @@ assign_managed_vector(struct irq_data *i
 	if (vector < 0)
 		return vector;
 	apic_update_vector(irqd, vector, cpu);
-	apic_update_irq_cfg(irqd, vector, cpu);
 	return 0;
 }
 
@@ -357,7 +364,7 @@ static void clear_irq_vector(struct irq_
 			   apicd->prev_cpu);
 
 	per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_SHUTDOWN;
-	irq_matrix_free(vector_matrix, apicd->cpu, vector, managed);
+	apic_free_vector(apicd->cpu, vector, managed);
 	apicd->vector = 0;
 
 	/* Clean up move in progress */
@@ -366,7 +373,7 @@ static void clear_irq_vector(struct irq_
 		return;
 
 	per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_SHUTDOWN;
-	irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, managed);
+	apic_free_vector(apicd->prev_cpu, vector, managed);
 	apicd->prev_vector = 0;
 	apicd->move_in_progress = 0;
 	hlist_del_init(&apicd->clist);
@@ -905,7 +912,7 @@ static void free_moved_vector(struct api
 	 *    affinity mask comes online.
 	 */
 	trace_vector_free_moved(apicd->irq, cpu, vector, managed);
-	irq_matrix_free(vector_matrix, cpu, vector, managed);
+	apic_free_vector(cpu, vector, managed);
 	per_cpu(vector_irq, cpu)[vector] = VECTOR_UNUSED;
 	hlist_del_init(&apicd->clist);
 	apicd->prev_vector = 0;
Thomas Gleixner April 17, 2025, 10:52 a.m. UTC | #2
On Thu, Apr 17 2025 at 14:46, Neeraj Upadhyay wrote:
> +
> +static inline void update_vector(unsigned int cpu, unsigned int offset,
> +				 unsigned int vector, bool set)
> +{
> +	unsigned long *reg = get_reg_bitmap(cpu, offset);
> +	unsigned int bit = get_vec_bit(vector);
> +
> +	if (set)
> +		set_bit(bit, reg);
> +	else
> +		clear_bit(bit, reg);
> +}
  
> +static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
> +{
> +	update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);

This indirection is required because otherwise the code is too simple to
follow?
Neeraj Upadhyay April 17, 2025, noon UTC | #3
On 4/17/2025 4:20 PM, Thomas Gleixner wrote:
> On Thu, Apr 17 2025 at 14:46, Neeraj Upadhyay wrote:
>> Add update_vector callback to set/clear ALLOWED_IRR field in
>> a vCPU's APIC backing page for external vectors. The ALLOWED_IRR
>> field indicates the interrupt vectors which the guest allows the
>> hypervisor to send (typically for emulated devices). Interrupt
>> vectors used exclusively by the guest itself and the vectors which
>> are not emulated by the hypervisor, such as IPI vectors, are part
>> of system vectors and are not set in the ALLOWED_IRR.
> 
> Please structure changelogs properly in paragraphs:
> 
>   https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#changelog
> 

Ok

>>  arch/x86/include/asm/apic.h         |  9 +++++
>>  arch/x86/kernel/apic/vector.c       | 53 ++++++++++++++++++++++-------
>>  arch/x86/kernel/apic/x2apic_savic.c | 35 +++++++++++++++++++
> 
> And split this patch up into two:
> 
>   1) Do the modifications in vector.c which is what the $Subject line
>      says
> 
>   2) Add the SAVIC specific bits
> 

Ok

>> @@ -471,6 +473,12 @@ static __always_inline bool apic_id_valid(u32 apic_id)
>>  	return apic_id <= apic->max_apic_id;
>>  }
>>  
>> +static __always_inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set)
>> +{
>> +	if (apic->update_vector)
>> +		apic->update_vector(cpu, vector, set);
>> +}
> 
> This is in the public header because it can?
>

apic_update_vector() is needed for some system vectors which are emulated/injected
by Hypervisor. Patch 8 calls it for lapic timer. HYPERVISOR_CALLBACK_VECTOR would need
it for hyperv. This patch series does not call apic_update_vector() for
HYPERVISOR_CALLBACK_VECTOR though.

Given that currently this callback is not used outside of apic code,
do I need to add it to arch/x86/kernel/apic/local.h or just remove it and use
conditional call in current callsites?
 
   
>> -static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
>> -			       unsigned int newcpu)
>> +static int irq_alloc_vector(const struct cpumask *dest, bool resvd, unsigned int *cpu)
>> +{
>> +	int vector;
>> +
>> +	vector = irq_matrix_alloc(vector_matrix, dest, resvd, cpu);
> 
>         int vector = irq_matrix_alloc(...);
> 

Ok

>> +
>> +	if (vector >= 0)
>> +		apic_update_vector(*cpu, vector, true);
>> +
>> +	return vector;
>> +}
>> +
>> +static int irq_alloc_managed_vector(unsigned int *cpu)
>> +{
>> +	int vector;
>> +
>> +	vector = irq_matrix_alloc_managed(vector_matrix, vector_searchmask, cpu);
>> +
>> +	if (vector >= 0)
>> +		apic_update_vector(*cpu, vector, true);
>> +
>> +	return vector;
>> +}
> 
> I completely fail to see the value of these two functions. Each of them
> has exactly _ONE_ call site and both sites invoke apic_update_vector()

Ok, this was done to associate apic->update_vector() calls with the
setup (irq_matrix_alloc()) and teardown (irq_matrix_free()) of vector,
which was my understanding from your suggestion here: 
https://lore.kernel.org/lkml/87jz8i31dv.ffs@tglx/ . Given that there
is single callsite for each and vector_configure_legacy() calls
apic->update_vector() outside of alloc path, adding it to apic_update_irq_cfg(),
as you suggest handles all callers.


> when the allocation succeeded. Why can't you just do the obvious and
> leave the existing code alone and add
> 
>       if (apic->update_vector)
>       		apic->update_vector();
> 
> into apic_update_vector()? But then you have another place where you
> need the update, which does not invoke apic_update_vector().
> 
> Now if you look deeper, then you notice that all places which invoke
> apic_update_vector() invoke apic_update_irq_cfg(), which is also called
> at this other place, no?
> 

Yes, makes sense. apic_update_irq_cfg() is called for MANAGED_IRQ_SHUTDOWN_VECTOR
also. I am not aware of the use case of that vector. Whethere it is an interrupt
which is injected by Hypervisor.


static void vector_assign_managed_shutdown(struct irq_data *irqd)
{
        unsigned int cpu = cpumask_first(cpu_online_mask);

        apic_update_irq_cfg(irqd, MANAGED_IRQ_SHUTDOWN_VECTOR, cpu);
}


>> +static void irq_free_vector(unsigned int cpu, unsigned int vector, bool managed)
>> +{
>> +	apic_update_vector(cpu, vector, false);
>> +	irq_matrix_free(vector_matrix, cpu, vector, managed);
>> +}
> 
> This one makes sense, but please name it: apic_free_vector()
> 

Ok

> Something like the uncompiled below, no?
> 

Ok, makes sense. Looks good.


- Neeraj

> Thanks,
> 
>         tglx
> ---
> --- a/arch/x86/kernel/apic/vector.c
> +++ b/arch/x86/kernel/apic/vector.c
> @@ -134,9 +134,19 @@ static void apic_update_irq_cfg(struct i
>  
>  	apicd->hw_irq_cfg.vector = vector;
>  	apicd->hw_irq_cfg.dest_apicid = apic->calc_dest_apicid(cpu);
> +
> +	if (apic->update_vector)
> +		apic->update_vector(cpu, vector, true);
> +
>  	irq_data_update_effective_affinity(irqd, cpumask_of(cpu));
> -	trace_vector_config(irqd->irq, vector, cpu,
> -			    apicd->hw_irq_cfg.dest_apicid);
> +	trace_vector_config(irqd->irq, vector, cpu, apicd->hw_irq_cfg.dest_apicid);
> +}
> +
> +static void apic_free_vector(unsigned int cpu, unsigned int vector, bool managed)
> +{
> +	if (apic->update_vector)
> +		apic->update_vector(cpu, vector, false);
> +	irq_matrix_free(vector_matrix, cpu, vector, managed);
>  }
>  
>  static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
> @@ -174,8 +184,7 @@ static void apic_update_vector(struct ir
>  		apicd->prev_cpu = apicd->cpu;
>  		WARN_ON_ONCE(apicd->cpu == newcpu);
>  	} else {
> -		irq_matrix_free(vector_matrix, apicd->cpu, apicd->vector,
> -				managed);
> +		apic_free_vector(apicd->cpu, apicd->vector, managed);
>  	}
>  
>  setnew:
> @@ -183,6 +192,7 @@ static void apic_update_vector(struct ir
>  	apicd->cpu = newcpu;
>  	BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec]));
>  	per_cpu(vector_irq, newcpu)[newvec] = desc;
> +	apic_update_irq_cfg(irqd, newvec, newcpu);
>  }
>  
>  static void vector_assign_managed_shutdown(struct irq_data *irqd)
> @@ -261,8 +271,6 @@ assign_vector_locked(struct irq_data *ir
>  	if (vector < 0)
>  		return vector;
>  	apic_update_vector(irqd, vector, cpu);
> -	apic_update_irq_cfg(irqd, vector, cpu);
> -
>  	return 0;
>  }
>  
> @@ -338,7 +346,6 @@ assign_managed_vector(struct irq_data *i
>  	if (vector < 0)
>  		return vector;
>  	apic_update_vector(irqd, vector, cpu);
> -	apic_update_irq_cfg(irqd, vector, cpu);
>  	return 0;
>  }
>  
> @@ -357,7 +364,7 @@ static void clear_irq_vector(struct irq_
>  			   apicd->prev_cpu);
>  
>  	per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_SHUTDOWN;
> -	irq_matrix_free(vector_matrix, apicd->cpu, vector, managed);
> +	apic_free_vector(apicd->cpu, vector, managed);
>  	apicd->vector = 0;
>  
>  	/* Clean up move in progress */
> @@ -366,7 +373,7 @@ static void clear_irq_vector(struct irq_
>  		return;
>  
>  	per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_SHUTDOWN;
> -	irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, managed);
> +	apic_free_vector(apicd->prev_cpu, vector, managed);
>  	apicd->prev_vector = 0;
>  	apicd->move_in_progress = 0;
>  	hlist_del_init(&apicd->clist);
> @@ -905,7 +912,7 @@ static void free_moved_vector(struct api
>  	 *    affinity mask comes online.
>  	 */
>  	trace_vector_free_moved(apicd->irq, cpu, vector, managed);
> -	irq_matrix_free(vector_matrix, cpu, vector, managed);
> +	apic_free_vector(cpu, vector, managed);
>  	per_cpu(vector_irq, cpu)[vector] = VECTOR_UNUSED;
>  	hlist_del_init(&apicd->clist);
>  	apicd->prev_vector = 0;
Neeraj Upadhyay April 17, 2025, 12:12 p.m. UTC | #4
On 4/17/2025 4:22 PM, Thomas Gleixner wrote:
> On Thu, Apr 17 2025 at 14:46, Neeraj Upadhyay wrote:
>> +
>> +static inline void update_vector(unsigned int cpu, unsigned int offset,
>> +				 unsigned int vector, bool set)
>> +{
>> +	unsigned long *reg = get_reg_bitmap(cpu, offset);
>> +	unsigned int bit = get_vec_bit(vector);
>> +
>> +	if (set)
>> +		set_bit(bit, reg);
>> +	else
>> +		clear_bit(bit, reg);
>> +}
>   
>> +static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
>> +{
>> +	update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
> 
> This indirection is required because otherwise the code is too simple to
> follow?
> 

update_vector() is used by send_ipi_dest() in Patch 7. From your comment
on v3 https://lore.kernel.org/lkml/87y0whv57k.ffs@tglx/ , what I understood
was that you wanted update_vector() to be defined in the patch where that code
is added (i.e. this patch) and not at a later patch. Is that not correct
understanding?


- Neeraj
Thomas Gleixner April 17, 2025, 4:51 p.m. UTC | #5
On Thu, Apr 17 2025 at 17:30, Neeraj Upadhyay wrote:
> On 4/17/2025 4:20 PM, Thomas Gleixner wrote:
>>> +static __always_inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set)
>>> +{
>>> +	if (apic->update_vector)
>>> +		apic->update_vector(cpu, vector, set);
>>> +}
>> 
>> This is in the public header because it can?
>>
>
> apic_update_vector() is needed for some system vectors which are emulated/injected
> by Hypervisor. Patch 8 calls it for lapic timer. HYPERVISOR_CALLBACK_VECTOR would need
> it for hyperv. This patch series does not call apic_update_vector() for
> HYPERVISOR_CALLBACK_VECTOR though.

Then explain so in the change log instead of letting reviewers
wonder. I'm patently bad at reading your thoughts.

>> Now if you look deeper, then you notice that all places which invoke
>> apic_update_vector() invoke apic_update_irq_cfg(), which is also called
>> at this other place, no?
>> 
>
> Yes, makes sense. apic_update_irq_cfg() is called for MANAGED_IRQ_SHUTDOWN_VECTOR
> also. I am not aware of the use case of that vector. Whethere it is an interrupt
> which is injected by Hypervisor.

It can. The managed shutdown vector is used when a queue of a managed
multiqueue device is shut down. The device MSI-X entry is redirected to
this special vector in case that the device/hardware raises an
unexpected spurious interrupt post shut down.

Thanks,

        tglx
Thomas Gleixner April 17, 2025, 4:52 p.m. UTC | #6
On Thu, Apr 17 2025 at 17:42, Neeraj Upadhyay wrote:
> On 4/17/2025 4:22 PM, Thomas Gleixner wrote:
>>> +static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
>>> +{
>>> +	update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
>> 
>> This indirection is required because otherwise the code is too simple to
>> follow?
>> 
> update_vector() is used by send_ipi_dest() in Patch 7. From your comment
> on v3 https://lore.kernel.org/lkml/87y0whv57k.ffs@tglx/ , what I understood
> was that you wanted update_vector() to be defined in the patch where that code
> is added (i.e. this patch) and not at a later patch. Is that not correct
> understanding?

Fair enough. I missed the later usage sites. Again, a short note in the
change log which explains the rationale would avoid this.

Thanks,

        tglx
diff mbox series

Patch

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 562115100038..c359cce60b22 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -318,6 +318,8 @@  struct apic {
 	/* wakeup secondary CPU using 64-bit wakeup point */
 	int	(*wakeup_secondary_cpu_64)(u32 apicid, unsigned long start_eip);
 
+	void	(*update_vector)(unsigned int cpu, unsigned int vector, bool set);
+
 	char	*name;
 };
 
@@ -471,6 +473,12 @@  static __always_inline bool apic_id_valid(u32 apic_id)
 	return apic_id <= apic->max_apic_id;
 }
 
+static __always_inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set)
+{
+	if (apic->update_vector)
+		apic->update_vector(cpu, vector, set);
+}
+
 #else /* CONFIG_X86_LOCAL_APIC */
 
 static inline u32 apic_read(u32 reg) { return 0; }
@@ -482,6 +490,7 @@  static inline void apic_wait_icr_idle(void) { }
 static inline u32 safe_apic_wait_icr_idle(void) { return 0; }
 static inline void apic_native_eoi(void) { WARN_ON_ONCE(1); }
 static inline void apic_setup_apic_calls(void) { }
+static inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set) { }
 
 #define apic_update_callback(_callback, _fn) do { } while (0)
 
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index fee42a73d64a..351db49b9975 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -139,8 +139,38 @@  static void apic_update_irq_cfg(struct irq_data *irqd, unsigned int vector,
 			    apicd->hw_irq_cfg.dest_apicid);
 }
 
-static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
-			       unsigned int newcpu)
+static int irq_alloc_vector(const struct cpumask *dest, bool resvd, unsigned int *cpu)
+{
+	int vector;
+
+	vector = irq_matrix_alloc(vector_matrix, dest, resvd, cpu);
+
+	if (vector >= 0)
+		apic_update_vector(*cpu, vector, true);
+
+	return vector;
+}
+
+static int irq_alloc_managed_vector(unsigned int *cpu)
+{
+	int vector;
+
+	vector = irq_matrix_alloc_managed(vector_matrix, vector_searchmask, cpu);
+
+	if (vector >= 0)
+		apic_update_vector(*cpu, vector, true);
+
+	return vector;
+}
+
+static void irq_free_vector(unsigned int cpu, unsigned int vector, bool managed)
+{
+	apic_update_vector(cpu, vector, false);
+	irq_matrix_free(vector_matrix, cpu, vector, managed);
+}
+
+static void apic_chipd_update_vector(struct irq_data *irqd, unsigned int newvec,
+				     unsigned int newcpu)
 {
 	struct apic_chip_data *apicd = apic_chip_data(irqd);
 	struct irq_desc *desc = irq_data_to_desc(irqd);
@@ -174,8 +204,7 @@  static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
 		apicd->prev_cpu = apicd->cpu;
 		WARN_ON_ONCE(apicd->cpu == newcpu);
 	} else {
-		irq_matrix_free(vector_matrix, apicd->cpu, apicd->vector,
-				managed);
+		irq_free_vector(apicd->cpu, apicd->vector, managed);
 	}
 
 setnew:
@@ -256,11 +285,11 @@  assign_vector_locked(struct irq_data *irqd, const struct cpumask *dest)
 	if (apicd->move_in_progress || !hlist_unhashed(&apicd->clist))
 		return -EBUSY;
 
-	vector = irq_matrix_alloc(vector_matrix, dest, resvd, &cpu);
+	vector = irq_alloc_vector(dest, resvd, &cpu);
 	trace_vector_alloc(irqd->irq, vector, resvd, vector);
 	if (vector < 0)
 		return vector;
-	apic_update_vector(irqd, vector, cpu);
+	apic_chipd_update_vector(irqd, vector, cpu);
 	apic_update_irq_cfg(irqd, vector, cpu);
 
 	return 0;
@@ -332,12 +361,11 @@  assign_managed_vector(struct irq_data *irqd, const struct cpumask *dest)
 	/* set_affinity might call here for nothing */
 	if (apicd->vector && cpumask_test_cpu(apicd->cpu, vector_searchmask))
 		return 0;
-	vector = irq_matrix_alloc_managed(vector_matrix, vector_searchmask,
-					  &cpu);
+	vector = irq_alloc_managed_vector(&cpu);
 	trace_vector_alloc_managed(irqd->irq, vector, vector);
 	if (vector < 0)
 		return vector;
-	apic_update_vector(irqd, vector, cpu);
+	apic_chipd_update_vector(irqd, vector, cpu);
 	apic_update_irq_cfg(irqd, vector, cpu);
 	return 0;
 }
@@ -357,7 +385,7 @@  static void clear_irq_vector(struct irq_data *irqd)
 			   apicd->prev_cpu);
 
 	per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_SHUTDOWN;
-	irq_matrix_free(vector_matrix, apicd->cpu, vector, managed);
+	irq_free_vector(apicd->cpu, vector, managed);
 	apicd->vector = 0;
 
 	/* Clean up move in progress */
@@ -366,7 +394,7 @@  static void clear_irq_vector(struct irq_data *irqd)
 		return;
 
 	per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_SHUTDOWN;
-	irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, managed);
+	irq_free_vector(apicd->prev_cpu, vector, managed);
 	apicd->prev_vector = 0;
 	apicd->move_in_progress = 0;
 	hlist_del_init(&apicd->clist);
@@ -528,6 +556,7 @@  static bool vector_configure_legacy(unsigned int virq, struct irq_data *irqd,
 	if (irqd_is_activated(irqd)) {
 		trace_vector_setup(virq, true, 0);
 		apic_update_irq_cfg(irqd, apicd->vector, apicd->cpu);
+		apic_update_vector(apicd->cpu, apicd->vector, true);
 	} else {
 		/* Release the vector */
 		apicd->can_reserve = true;
@@ -905,7 +934,7 @@  static void free_moved_vector(struct apic_chip_data *apicd)
 	 *    affinity mask comes online.
 	 */
 	trace_vector_free_moved(apicd->irq, cpu, vector, managed);
-	irq_matrix_free(vector_matrix, cpu, vector, managed);
+	irq_free_vector(cpu, vector, managed);
 	per_cpu(vector_irq, cpu)[vector] = VECTOR_UNUSED;
 	hlist_del_init(&apicd->clist);
 	apicd->prev_vector = 0;
diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c
index 81d932061b7b..9d2e93656037 100644
--- a/arch/x86/kernel/apic/x2apic_savic.c
+++ b/arch/x86/kernel/apic/x2apic_savic.c
@@ -43,6 +43,34 @@  static __always_inline void set_reg(unsigned int offset, u32 val)
 	WRITE_ONCE(this_cpu_ptr(apic_page)->regs[offset >> 2], val);
 }
 
+static inline unsigned long *get_reg_bitmap(unsigned int cpu, unsigned int offset)
+{
+	struct apic_page *ap = per_cpu_ptr(apic_page, cpu);
+
+	return (unsigned long *) &ap->bytes[offset];
+}
+
+static inline unsigned int get_vec_bit(unsigned int vector)
+{
+	/*
+	 * The registers are 32-bit wide and 16-byte aligned.
+	 * Compensate for the resulting bit number spacing.
+	 */
+	return vector + 96 * (vector / 32);
+}
+
+static inline void update_vector(unsigned int cpu, unsigned int offset,
+				 unsigned int vector, bool set)
+{
+	unsigned long *reg = get_reg_bitmap(cpu, offset);
+	unsigned int bit = get_vec_bit(vector);
+
+	if (set)
+		set_bit(bit, reg);
+	else
+		clear_bit(bit, reg);
+}
+
 #define SAVIC_ALLOWED_IRR	0x204
 
 static u32 savic_read(u32 reg)
@@ -144,6 +172,11 @@  static void savic_write(u32 reg, u32 data)
 	}
 }
 
+static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
+{
+	update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
+}
+
 static void init_apic_page(void)
 {
 	u32 apic_id;
@@ -225,6 +258,8 @@  static struct apic apic_x2apic_savic __ro_after_init = {
 	.eoi				= native_apic_msr_eoi,
 	.icr_read			= native_x2apic_icr_read,
 	.icr_write			= native_x2apic_icr_write,
+
+	.update_vector			= savic_update_vector,
 };
 
 apic_driver(apic_x2apic_savic);