/* *Wrapperaroundwait_queue_entry_t
*/ struct kfd_event_waiter {
wait_queue_entry_t wait; struct kfd_event *event; /* Event to wait for */ bool activated; /* Becomes true when event is signaled */ bool event_age_enabled; /* set to true when last_event_age is non-zero */
};
if (!p->signal_page || id >= KFD_SIGNAL_EVENT_LIMIT) return NULL;
/* Fast path for the common case that @id is not a partial ID *andweonlyneedasinglelookup.
*/ if (bits > 31 || (1U << bits) >= KFD_SIGNAL_EVENT_LIMIT) { if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) return NULL;
return idr_find(&p->event_idr, id);
}
/* General case for partial IDs: Iterate over all matching IDs *andfindthefirstonethathassignaled.
*/ for (ev = NULL; id < KFD_SIGNAL_EVENT_LIMIT && !ev; id += 1U << bits) { if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) continue;
if (p->signal_mapped_size &&
p->signal_event_count == p->signal_mapped_size / 8) { if (!p->signal_event_limit_reached) {
pr_debug("Signal event wasn't created because limit was reached\n");
p->signal_event_limit_reached = true;
} return -ENOSPC;
}
ret = allocate_event_notification_slot(p, ev, restore_id); if (ret) {
pr_warn("Signal event wasn't created because out of kernel memory\n"); return ret;
}
p->signal_event_count++;
ev->user_signal_address = &p->signal_page->user_address[ev->event_id];
pr_debug("Signal event number %zu created with id %d, address %p\n",
p->signal_event_count, ev->event_id,
ev->user_signal_address);
if (restore_id)
id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1,
GFP_KERNEL); else /* Cast KFD_LAST_NONSIGNAL_EVENT to uint32_t. This allows an *intentionalintegeroverflowto-1withoutacompiler *warning.idr_alloctreatsanegativevalueas"maximum *signedinteger".
*/
id = idr_alloc(&p->event_idr, ev, KFD_FIRST_NONSIGNAL_EVENT_ID,
(uint32_t)KFD_LAST_NONSIGNAL_EVENT_ID + 1,
GFP_KERNEL);
if (id < 0) return id;
ev->event_id = id;
return0;
}
int kfd_event_init_process(struct kfd_process *p)
{ int id;
mutex_init(&p->event_mutex);
idr_init(&p->event_idr);
p->signal_page = NULL;
p->signal_event_count = 1; /* Allocate event ID 0. It is used for a fast path to ignore bogus events *thataresentbytheCPwithoutacontextID
*/
id = idr_alloc(&p->event_idr, NULL, 0, 1, GFP_KERNEL); if (id < 0) {
idr_destroy(&p->event_idr);
mutex_destroy(&p->event_mutex); return id;
} return0;
}
/* Wake up pending waiters. They will return failure */
spin_lock(&ev->lock);
list_for_each_entry(waiter, &ev->wq.head, wait.entry)
WRITE_ONCE(waiter->event, NULL);
wake_up_all(&ev->wq);
spin_unlock(&ev->lock);
if (ev->type == KFD_EVENT_TYPE_SIGNAL ||
ev->type == KFD_EVENT_TYPE_DEBUG)
p->signal_event_count--;
pr_debug("Checkpointed event[%d] id = 0x%08x auto_reset = %x type = %x signaled = %x\n",
i,
ev_priv->event_id,
ev_priv->auto_reset,
ev_priv->type,
ev_priv->signaled);
i++;
}
ret = copy_to_user(user_priv_data + *priv_data_offset,
ev_privs, num_events * sizeof(*ev_privs)); if (ret) {
pr_err("Failed to copy events priv to user\n");
ret = -EFAULT;
}
/* Auto reset if the list is non-empty and we're waking *someone.waitqueue_activeissafeherebecausewe're *protectedbytheev->lock,whichisalsoheldwhen *updatingthewaitqueuesinkfd_wait_on_events.
*/
ev->signaled = !ev->auto_reset || !waitqueue_active(&ev->wq); if (!(++ev->event_age)) { /* Never wrap back to reserved/default event age 0/1 */
ev->event_age = 2;
WARN_ONCE(1, "event_age wrap back!");
}
if (valid_id_bits)
pr_debug_ratelimited("Partial ID invalid: %u (%u valid bits)\n",
partial_id, valid_id_bits);
if (p->signal_event_count < KFD_SIGNAL_EVENT_LIMIT / 64) { /* With relatively few events, it's faster to *iterateovertheeventIDR
*/
idr_for_each_entry(&p->event_idr, ev, id) { if (id >= KFD_SIGNAL_EVENT_LIMIT) break;
if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT)
set_event_from_interrupt(p, ev);
}
} else { /* With relatively many events, it's faster to *iterateoverthesignalslotsandlookup *onlysignaledeventsfromtheIDR.
*/ for (id = 1; id < KFD_SIGNAL_EVENT_LIMIT; id++) if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) {
ev = lookup_event_by_id(p, id);
set_event_from_interrupt(p, ev);
}
}
}
for (i = 0; i < num_events; i++) if (waiters[i].event) {
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);
}
kfree(waiters);
}
int kfd_wait_on_events(struct kfd_process *p,
uint32_t num_events, void __user *data, bool all, uint32_t *user_timeout_ms,
uint32_t *wait_result)
{ struct kfd_event_data __user *events =
(struct kfd_event_data __user *) data;
uint32_t i; int ret = 0;
struct kfd_event_waiter *event_waiters = NULL; long timeout = user_timeout_to_jiffies(*user_timeout_ms);
event_waiters = alloc_event_waiters(num_events); if (!event_waiters) {
ret = -ENOMEM; goto out;
}
/* Use p->event_mutex here to protect against concurrent creation and *destructionofeventswhileweinitializeevent_waiters.
*/
mutex_lock(&p->event_mutex);
for (i = 0; i < num_events; i++) { struct kfd_event_data event_data;
if (copy_from_user(&event_data, &events[i], sizeof(struct kfd_event_data))) {
ret = -EFAULT; goto out_unlock;
}
ret = init_event_waiter(p, &event_waiters[i], &event_data); if (ret) goto out_unlock;
}
/* Check condition once. */
*wait_result = test_event_condition(all, num_events, event_waiters); if (*wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) {
ret = copy_signaled_event_data(num_events,
event_waiters, events); goto out_unlock;
} elseif (WARN_ON(*wait_result == KFD_IOC_WAIT_RESULT_FAIL)) { /* This should not happen. Events shouldn't be *destroyedwhilewe'reholdingtheevent_mutex
*/ goto out_unlock;
}
mutex_unlock(&p->event_mutex);
while (true) { if (fatal_signal_pending(current)) {
ret = -EINTR; break;
}
if (signal_pending(current)) {
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;
}
/* Set task state to interruptible sleep before *checkingwake-upconditions.Aconcurrentwake-up *willputthetaskbackintorunnablestate.Inthat *caseschedule_timeoutwillnotputthetaskto *sleepandwe'llgetachancetore-checkthe *updatedconditionsalmostimmediately.Otherwise, *thisraceconditionwouldleadtoasofthangora *verylongsleep.
*/
set_current_state(TASK_INTERRUPTIBLE);
*wait_result = test_event_condition(all, num_events,
event_waiters); if (*wait_result != KFD_IOC_WAIT_RESULT_TIMEOUT) break;
mutex_lock(&p->event_mutex); /* copy_signaled_event_data may sleep. So this has to happen *afterthetaskstateissetbacktoRUNNING. * *Theeventmayalsohavebeendestroyedaftersignaling.So *copy_signaled_event_dataalsomustconfirmthattheevent *stillexists.Thereforethismustbeunderthep->event_mutex *whichisalsoheldwheneventsaredestroyed.
*/ if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE)
ret = copy_signaled_event_data(num_events,
event_waiters, events);
out_unlock:
free_waiters(num_events, event_waiters, ret == -ERESTARTSYS);
mutex_unlock(&p->event_mutex);
out: if (ret)
*wait_result = KFD_IOC_WAIT_RESULT_FAIL; elseif (*wait_result == KFD_IOC_WAIT_RESULT_FAIL)
ret = -EIO;
return ret;
}
int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma)
{ unsignedlong pfn; struct kfd_signal_page *page; int ret;
page = p->signal_page; if (!page) { /* Probably KFD bug, but mmap is user-accessible. */
pr_debug("Signal page could not be found\n"); return -EINVAL;
}
/* mapping the page to user process */
ret = remap_pfn_range(vma, vma->vm_start, pfn,
vma->vm_end - vma->vm_start, vma->vm_page_prot); if (!ret)
p->signal_mapped_size = vma->vm_end - vma->vm_start;
id = KFD_FIRST_NONSIGNAL_EVENT_ID;
idr_for_each_entry_continue(&p->event_idr, ev, id) if (ev->type == type) {
send_signal = false;
dev_dbg(kfd_device, "Event found: id %X type %d",
ev->event_id, ev->type);
spin_lock(&ev->lock);
set_event(ev); if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data)
ev->memory_exception_data = *ev_data;
spin_unlock(&ev->lock);
}
if (type == KFD_EVENT_TYPE_MEMORY) {
dev_warn(kfd_device, "Sending SIGSEGV to process pid %d",
p->lead_thread->pid);
send_sig(SIGSEGV, p->lead_thread, 0);
}
/* Send SIGTERM no event of type "type" has been found*/ if (send_signal) { if (send_sigterm) {
dev_warn(kfd_device, "Sending SIGTERM to process pid %d",
p->lead_thread->pid);
send_sig(SIGTERM, p->lead_thread, 0);
} else {
dev_err(kfd_device, "Process pid %d got unhandled exception",
p->lead_thread->pid);
}
}
// Send VM seg fault to all kfd process device for (i = 0; i < p->n_pdds; i++) {
pdd = p->pdds[i];
exception_data.gpu_id = pdd->user_gpu_id;
kfd_evict_process_device(pdd);
kfd_signal_vm_fault_event(pdd, NULL, &exception_data);
}
}
user_gpu_id = kfd_process_get_user_gpu_id(p, pdd->dev->id); if (unlikely(user_gpu_id == -EINVAL)) {
WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n",
pdd->dev->id); return;
}
/* SoC15 chips and onwards will pass in data from now on. */ if (!data) {
memset(&memory_exception_data, 0, sizeof(memory_exception_data));
memory_exception_data.gpu_id = user_gpu_id;
memory_exception_data.failure.imprecise = true;
/* Whole gpu reset caused by GPU hang and memory is lost */
memset(&hw_exception_data, 0, sizeof(hw_exception_data));
hw_exception_data.memory_lost = 1;
hw_exception_data.reset_cause = reset_cause;
if (unlikely(user_gpu_id == -EINVAL)) {
WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); continue;
}
if (unlikely(!pdd)) {
WARN_ONCE(1, "Could not get device data from process pid:%d\n",
p->lead_thread->pid); continue;
}
if (dev->dqm->detect_hang_count && !pdd->has_reset_queue) continue;
if (dev->dqm->detect_hang_count) { struct amdgpu_task_info *ti; struct amdgpu_fpriv *drv_priv;
if (unlikely(amdgpu_file_to_fpriv(pdd->drm_file, &drv_priv))) {
WARN_ONCE(1, "Could not get vm for device %x from pid:%d\n",
dev->id, p->lead_thread->pid); continue;
}
ti = amdgpu_vm_get_task_info_vm(&drv_priv->vm); if (ti) {
dev_err(dev->adev->dev, "Queues reset on process %s tid %d thread %s pid %d\n",
ti->process_name, ti->tgid, ti->task.comm, ti->task.pid);
amdgpu_vm_put_task_info(ti);
}
}
if (!p) {
dev_warn(dev->adev->dev, "Not find process with pasid:%d\n", pasid); return; /* Presumably process exited. */
}
user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); if (unlikely(user_gpu_id == -EINVAL)) {
WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id);
kfd_unref_process(p); return;
}
dev_warn(dev->adev->dev, "Send SIGBUS to process %s(pasid:%d)\n",
p->lead_thread->comm, pasid);
rcu_read_unlock();
/* user application will handle SIGBUS signal */
send_sig(SIGBUS, p->lead_thread, 0);
kfd_unref_process(p);
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.22Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.