mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	 3e684903a8
			
		
	
	
		3e684903a8
		
	
	
	
	
		
			
			A livepatch transition may stall indefinitely when a kvm vCPU is heavily loaded. To the host, the vCPU task is a user thread which is spending a very long time in the ioctl(KVM_RUN) syscall. During livepatch transition, set_notify_signal() will be called on such tasks to interrupt the syscall so that the task can be transitioned. This interrupts guest execution, but when xfer_to_guest_mode_work() sees that TIF_NOTIFY_SIGNAL is set but not TIF_SIGPENDING it concludes that an exit to user mode is unnecessary, and guest execution is resumed without transitioning the task for the livepatch. This handling of TIF_NOTIFY_SIGNAL is incorrect, as set_notify_signal() is expected to break tasks out of interruptible kernel loops and cause them to return to userspace. Change xfer_to_guest_mode_work() to handle TIF_NOTIFY_SIGNAL the same as TIF_SIGPENDING, signaling to the vCPU run loop that an exit to userpsace is needed. Any pending task_work will be run when get_signal() is called from exit_to_user_mode_loop(), so there is no longer any need to run task work from xfer_to_guest_mode_work(). Suggested-by: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Petr Mladek <pmladek@suse.com> Signed-off-by: Seth Forshee <sforshee@digitalocean.com> Message-Id: <20220504180840.2907296-1-sforshee@digitalocean.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			49 lines
		
	
	
	
		
			1.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			49 lines
		
	
	
	
		
			1.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| 
 | |
| #include <linux/entry-kvm.h>
 | |
| #include <linux/kvm_host.h>
 | |
| 
 | |
| static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work)
 | |
| {
 | |
| 	do {
 | |
| 		int ret;
 | |
| 
 | |
| 		if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) {
 | |
| 			kvm_handle_signal_exit(vcpu);
 | |
| 			return -EINTR;
 | |
| 		}
 | |
| 
 | |
| 		if (ti_work & _TIF_NEED_RESCHED)
 | |
| 			schedule();
 | |
| 
 | |
| 		if (ti_work & _TIF_NOTIFY_RESUME)
 | |
| 			resume_user_mode_work(NULL);
 | |
| 
 | |
| 		ret = arch_xfer_to_guest_mode_handle_work(vcpu, ti_work);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		ti_work = read_thread_flags();
 | |
| 	} while (ti_work & XFER_TO_GUEST_MODE_WORK || need_resched());
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu)
 | |
| {
 | |
| 	unsigned long ti_work;
 | |
| 
 | |
| 	/*
 | |
| 	 * This is invoked from the outer guest loop with interrupts and
 | |
| 	 * preemption enabled.
 | |
| 	 *
 | |
| 	 * KVM invokes xfer_to_guest_mode_work_pending() with interrupts
 | |
| 	 * disabled in the inner loop before going into guest mode. No need
 | |
| 	 * to disable interrupts here.
 | |
| 	 */
 | |
| 	ti_work = read_thread_flags();
 | |
| 	if (!(ti_work & XFER_TO_GUEST_MODE_WORK))
 | |
| 		return 0;
 | |
| 
 | |
| 	return xfer_to_guest_mode_work(vcpu, ti_work);
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(xfer_to_guest_mode_handle_work);
 |