/*
* Copyright 2023 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "kfd_debug.h"
#include "kfd_device_queue_manager.h"
#include "kfd_topology.h"
#include <linux/file.h>
#include <uapi/linux/kfd_ioctl.h>
#include <uapi/linux/kfd_sysfs.h>
#define MAX_WATCH_ADDRESSES 4
int kfd_dbg_ev_query_debug_event(struct kfd_process *process,
unsigned int *queue_id,
unsigned int *gpu_id,
uint64_t exception_clear_mask,
uint64_t *event_status)
{
struct process_queue_manager *pqm;
struct process_queue_node *pqn;
int i;
if (!(process && process->debug_trap_enabled))
return -ENODATA;
mutex_lock(&process->event_mutex);
*event_status = 0;
*queue_id = 0;
*gpu_id = 0;
/* find and report queue events */
pqm = &process->pqm;
list_for_each_entry(pqn, &pqm->queues, process_queue_list) {
uint64_t tmp = process->exception_enable_mask;
if (!pqn->q)
continue;
tmp &= pqn->q->properties.exception_status;
if (!tmp)
continue;
*event_status = pqn->q->properties.exception_status;
*queue_id = pqn->q->properties.queue_id;
*gpu_id = pqn->q->device->id;
pqn->q->properties.exception_status &= ~exception_clear_mask;
goto out;
}
/* find and report device events */
for (i = 0; i < process->n_pdds; i++) {
struct kfd_process_device *pdd = process->pdds[i];
uint64_t tmp = process->exception_enable_mask
& pdd->exception_status;
if (!tmp)
continue;
*event_status = pdd->exception_status;
*gpu_id = pdd->dev->id;
pdd->exception_status &= ~exception_clear_mask;
goto out;
}
/* report process events */
if (process->exception_enable_mask & process->exception_status) {
*event_status = process->exception_status;
process->exception_status &= ~exception_clear_mask;
}
out:
mutex_unlock(&process->event_mutex);
return *event_status ? 0 : -EAGAIN;
}
void debug_event_write_work_handler(struct work_struct *work)
{
struct kfd_process *process;
static const char write_data = '.';
loff_t pos = 0;
process = container_of(work,
struct kfd_process,
debug_event_workarea);
if (process->debug_trap_enabled && process->dbg_ev_file)
kernel_write(process->dbg_ev_file, &write_data, 1, &pos);
}
/* update process/device/queue exception status, write to descriptor
* only if exception_status is enabled.
*/
bool kfd_dbg_ev_raise(uint64_t event_mask,
struct kfd_process *process, struct kfd_node *dev,
unsigned int source_id, bool use_worker,
void *exception_data, size_t exception_data_size)
{
struct process_queue_manager *pqm;
struct process_queue_node *pqn;
int i;
static const char write_data = '.';
loff_t pos = 0;
bool is_subscribed = true;
if (!(process && process->debug_trap_enabled))
return false;
mutex_lock(&process->event_mutex);
if (event_mask & KFD_EC_MASK_DEVICE) {
for (i = 0; i < process->n_pdds; i++) {
struct kfd_process_device *pdd = process->pdds[i];
if (pdd->dev != dev)
continue;
pdd->exception_status |= event_mask & KFD_EC_MASK_DEVICE;
if (event_mask & KFD_EC_MASK(EC_DEVICE_MEMORY_VIOLATION)) {
if (!pdd->vm_fault_exc_data) {
pdd->vm_fault_exc_data = kmemdup(
exception_data,
exception_data_size,
GFP_KERNEL);
if (!pdd->vm_fault_exc_data)
pr_debug("Failed to allocate exception data memory");
} else {
pr_debug(&