Commit bea9a56a authored by Felix Kuehling's avatar Felix Kuehling Committed by Alex Deucher
Browse files

drm/amdkfd: Handle restart of kfd_ioctl_wait_events



When kfd_ioctl_wait_events needs to restart due to a signal, we need to
update the timeout to account for the time already elapsed. We also need
to undo auto_reset of events that have signaled already, so that the
restarted ioctl will be able to count those signals again.

This fixes infinite hangs when kfd_ioctl_wait_events is interrupted by a
signal.

Signed-off-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Reviewed-and-tested-by: default avatarXiaogang Chen <Xiaogang.Chen@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c4c10a68
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -874,7 +874,7 @@ static int kfd_ioctl_wait_events(struct file *filp, struct kfd_process *p,
	err = kfd_wait_on_events(p, args->num_events,
			(void __user *)args->events_ptr,
			(args->wait_for_all != 0),
			args->timeout, &args->wait_result);
			&args->timeout, &args->wait_result);

	return err;
}
+12 −12
Original line number Diff line number Diff line
@@ -894,7 +894,8 @@ static long user_timeout_to_jiffies(uint32_t user_timeout_ms)
	return msecs_to_jiffies(user_timeout_ms) + 1;
}

static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters,
			 bool undo_auto_reset)
{
	uint32_t i;

@@ -903,6 +904,9 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
			spin_lock(&waiters[i].event->lock);
			remove_wait_queue(&waiters[i].event->wq,
					  &waiters[i].wait);
			if (undo_auto_reset && waiters[i].activated &&
			    waiters[i].event && waiters[i].event->auto_reset)
				set_event(waiters[i].event);
			spin_unlock(&waiters[i].event->lock);
		}

@@ -911,7 +915,7 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)

int kfd_wait_on_events(struct kfd_process *p,
		       uint32_t num_events, void __user *data,
		       bool all, uint32_t user_timeout_ms,
		       bool all, uint32_t *user_timeout_ms,
		       uint32_t *wait_result)
{
	struct kfd_event_data __user *events =
@@ -920,7 +924,7 @@ int kfd_wait_on_events(struct kfd_process *p,
	int ret = 0;

	struct kfd_event_waiter *event_waiters = NULL;
	long timeout = user_timeout_to_jiffies(user_timeout_ms);
	long timeout = user_timeout_to_jiffies(*user_timeout_ms);

	event_waiters = alloc_event_waiters(num_events);
	if (!event_waiters) {
@@ -970,15 +974,11 @@ int kfd_wait_on_events(struct kfd_process *p,
		}

		if (signal_pending(current)) {
			/*
			 * This is wrong when a nonzero, non-infinite timeout
			 * is specified. We need to use
			 * ERESTARTSYS_RESTARTBLOCK, but struct restart_block
			 * contains a union with data for each user and it's
			 * in generic kernel code that I don't want to
			 * touch yet.
			 */
			ret = -ERESTARTSYS;
			if (*user_timeout_ms != KFD_EVENT_TIMEOUT_IMMEDIATE &&
			    *user_timeout_ms != KFD_EVENT_TIMEOUT_INFINITE)
				*user_timeout_ms = jiffies_to_msecs(
					max(0l, timeout-1));
			break;
		}

@@ -1019,7 +1019,7 @@ int kfd_wait_on_events(struct kfd_process *p,
					       event_waiters, events);

out_unlock:
	free_waiters(num_events, event_waiters);
	free_waiters(num_events, event_waiters, ret == -ERESTARTSYS);
	mutex_unlock(&p->event_mutex);
out:
	if (ret)
+1 −1
Original line number Diff line number Diff line
@@ -1317,7 +1317,7 @@ void kfd_event_free_process(struct kfd_process *p);
int kfd_event_mmap(struct kfd_process *process, struct vm_area_struct *vma);
int kfd_wait_on_events(struct kfd_process *p,
		       uint32_t num_events, void __user *data,
		       bool all, uint32_t user_timeout_ms,
		       bool all, uint32_t *user_timeout_ms,
		       uint32_t *wait_result);
void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
				uint32_t valid_id_bits);