/* * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_gk20a.h" #include "ioctl_ctrl.h" #include "ioctl_tsg.h" #include "ioctl_as.h" #include "ioctl_channel.h" #include "ioctl_nvs.h" #include "ioctl.h" #include "os_linux.h" struct tsg_private { struct gk20a *g; struct nvgpu_tsg *tsg; struct nvgpu_cdev *cdev; struct gk20a_ctrl_priv *ctrl_priv; }; extern const struct file_operations gk20a_tsg_ops; struct nvgpu_tsg *nvgpu_tsg_get_from_file(int fd) { struct nvgpu_tsg *tsg; struct tsg_private *priv; struct file *f = fget(fd); if (!f) { return NULL; } if (f->f_op != &gk20a_tsg_ops) { fput(f); return NULL; } priv = (struct tsg_private *)f->private_data; tsg = priv->tsg; fput(f); return tsg; } static int nvgpu_tsg_bind_channel_fd(struct nvgpu_tsg *tsg, int ch_fd) { struct nvgpu_channel *ch; int err; ch = nvgpu_channel_get_from_file(ch_fd); if (!ch) return -EINVAL; err = nvgpu_tsg_create_sync_subcontext_internal(ch->g, tsg, ch); if (err != 0) { nvgpu_err(ch->g, "sync subctx created failed %d", err); return err; } err = nvgpu_tsg_validate_ch_subctx_vm(ch->g, tsg, ch); if (err != 0) { nvgpu_err(ch->g, "channel/subctx VM mismatch"); return err; } err = nvgpu_tsg_bind_channel(tsg, ch); nvgpu_channel_put(ch); return err; } static int gk20a_tsg_ioctl_bind_channel_ex(struct gk20a *g, struct tsg_private *priv, struct nvgpu_tsg_bind_channel_ex_args *arg) { struct nvgpu_tsg *tsg = priv->tsg; struct nvgpu_sched_ctrl *sched = &g->sched_ctrl; struct nvgpu_channel *ch; u32 max_subctx_count; u32 gpu_instance_id; int err = 0; nvgpu_log(g, gpu_dbg_fn | gpu_dbg_sched, "tsgid=%u", tsg->tsgid); nvgpu_mutex_acquire(&sched->control_lock); if (sched->control_locked) { err = -EPERM; goto mutex_release; } err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu"); goto mutex_release; } ch = nvgpu_channel_get_from_file(arg->channel_fd); if (!ch) { err = -EINVAL; goto idle; } gpu_instance_id = nvgpu_get_gpu_instance_id_from_cdev(g, priv->cdev); nvgpu_assert(gpu_instance_id < g->mig.num_gpu_instances); max_subctx_count = nvgpu_grmgr_get_gpu_instance_max_veid_count(g, gpu_instance_id); if (arg->subcontext_id < max_subctx_count) { ch->subctx_id = arg->subcontext_id; } else { err = -EINVAL; goto ch_put; } nvgpu_log(g, gpu_dbg_info, "channel id : %d : subctx: %d", ch->chid, ch->subctx_id); /* Use runqueue selector 1 for all ASYNC ids */ if (ch->subctx_id > CHANNEL_INFO_VEID0) ch->runqueue_sel = 1; err = nvgpu_tsg_create_sync_subcontext_internal(g, tsg, ch); if (err != 0) { nvgpu_err(ch->g, "sync subctx created failed %d", err); goto ch_put; } err = nvgpu_tsg_validate_ch_subctx_vm(g, tsg, ch); if (err != 0) { nvgpu_err(g, "channel/subctx VM mismatch"); goto ch_put; } err = nvgpu_tsg_bind_channel(tsg, ch); ch_put: nvgpu_channel_put(ch); idle: gk20a_idle(g); mutex_release: nvgpu_mutex_release(&sched->control_lock); return err; } static int nvgpu_tsg_unbind_channel_fd(struct nvgpu_tsg *tsg, int ch_fd) { struct nvgpu_channel *ch; int err = 0; ch = nvgpu_channel_get_from_file(ch_fd); if (!ch) { return -EINVAL; } if (tsg != nvgpu_tsg_from_ch(ch)) { err = -EINVAL; goto out; } err = nvgpu_tsg_unbind_channel(tsg, ch, false); if (err == -EAGAIN) { goto out; } /* * Mark the channel unserviceable since channel unbound from TSG * has no context of its own so it can't serve any job */ nvgpu_channel_set_unserviceable(ch); out: nvgpu_channel_put(ch); return err; } #ifdef CONFIG_NVS_PRESENT static int nvgpu_tsg_bind_scheduling_domain(struct nvgpu_tsg *tsg, struct nvgpu_tsg_bind_scheduling_domain_args *args) { struct nvgpu_nvs_domain *domain; int err; if (args->reserved0 != 0) { return -EINVAL; } if (args->reserved[0] != 0) { return -EINVAL; } if (args->reserved[1] != 0) { return -EINVAL; } if (args->reserved[2] != 0) { return -EINVAL; } if (tsg->g->scheduler == NULL) { return -ENOSYS; } domain = nvgpu_nvs_domain_get_from_file(args->domain_fd); if (domain == NULL) { return -ENOENT; } err = nvgpu_tsg_bind_domain(tsg, domain); nvgpu_nvs_domain_put(tsg->g, domain); return err; } #endif #ifdef CONFIG_NVGPU_CHANNEL_TSG_CONTROL /* * Convert common event_id of the form NVGPU_EVENT_ID_* to Linux specific * event_id of the form NVGPU_IOCTL_CHANNEL_EVENT_ID_* which is used in IOCTLs */ static u32 nvgpu_event_id_to_ioctl_channel_event_id( enum nvgpu_event_id_type event_id) { switch (event_id) { case NVGPU_EVENT_ID_BPT_INT: return NVGPU_IOCTL_CHANNEL_EVENT_ID_BPT_INT; case NVGPU_EVENT_ID_BPT_PAUSE: return NVGPU_IOCTL_CHANNEL_EVENT_ID_BPT_PAUSE; case NVGPU_EVENT_ID_BLOCKING_SYNC: return NVGPU_IOCTL_CHANNEL_EVENT_ID_BLOCKING_SYNC; case NVGPU_EVENT_ID_CILP_PREEMPTION_STARTED: return NVGPU_IOCTL_CHANNEL_EVENT_ID_CILP_PREEMPTION_STARTED; case NVGPU_EVENT_ID_CILP_PREEMPTION_COMPLETE: return NVGPU_IOCTL_CHANNEL_EVENT_ID_CILP_PREEMPTION_COMPLETE; case NVGPU_EVENT_ID_GR_SEMAPHORE_WRITE_AWAKEN: return NVGPU_IOCTL_CHANNEL_EVENT_ID_GR_SEMAPHORE_WRITE_AWAKEN; case NVGPU_EVENT_ID_MAX: return NVGPU_IOCTL_CHANNEL_EVENT_ID_MAX; } return NVGPU_IOCTL_CHANNEL_EVENT_ID_MAX; } void nvgpu_tsg_post_event_id(struct nvgpu_tsg *tsg, enum nvgpu_event_id_type event_id) { struct gk20a_event_id_data *channel_event_id_data; u32 channel_event_id; struct gk20a *g = tsg->g; channel_event_id = nvgpu_event_id_to_ioctl_channel_event_id(event_id); if (event_id >= NVGPU_IOCTL_CHANNEL_EVENT_ID_MAX) return; nvgpu_mutex_acquire(&tsg->event_id_list_lock); nvgpu_list_for_each_entry(channel_event_id_data, &tsg->event_id_list, gk20a_event_id_data, event_id_node) { if (channel_event_id_data->event_id == event_id) { nvgpu_mutex_acquire(&channel_event_id_data->lock); nvgpu_log_info(g, "posting event for event_id=%d on tsg=%d\n", channel_event_id, tsg->tsgid); channel_event_id_data->event_posted = true; nvgpu_cond_broadcast_interruptible(&channel_event_id_data->event_id_wq); nvgpu_mutex_release(&channel_event_id_data->lock); } } nvgpu_mutex_release(&tsg->event_id_list_lock); } static unsigned int gk20a_event_id_poll(struct file *filep, poll_table *wait) { unsigned int mask = 0; struct gk20a_event_id_data *event_id_data = filep->private_data; struct gk20a *g = event_id_data->g; u32 event_id = event_id_data->event_id; struct nvgpu_tsg *tsg = g->fifo.tsg + event_id_data->id; nvgpu_log(g, gpu_dbg_fn | gpu_dbg_info, " "); poll_wait(filep, &event_id_data->event_id_wq.wq, wait); nvgpu_mutex_acquire(&event_id_data->lock); if (event_id_data->event_posted) { nvgpu_log_info(g, "found pending event_id=%d on TSG=%d\n", event_id, tsg->tsgid); mask = (POLLPRI | POLLIN); event_id_data->event_posted = false; } nvgpu_mutex_release(&event_id_data->lock); return mask; } static int gk20a_event_id_release(struct inode *inode, struct file *filp) { struct gk20a_event_id_data *event_id_data = filp->private_data; struct gk20a *g; struct nvgpu_tsg *tsg; if (event_id_data == NULL) return -EINVAL; g = event_id_data->g; tsg = g->fifo.tsg + event_id_data->id; nvgpu_mutex_acquire(&tsg->event_id_list_lock); nvgpu_list_del(&event_id_data->event_id_node); nvgpu_mutex_release(&tsg->event_id_list_lock); nvgpu_mutex_destroy(&event_id_data->lock); nvgpu_put(g); nvgpu_kfree(g, event_id_data); filp->private_data = NULL; return 0; } const struct file_operations gk20a_event_id_ops = { .owner = THIS_MODULE, .poll = gk20a_event_id_poll, .release = gk20a_event_id_release, }; static int gk20a_tsg_event_id_enable(struct nvgpu_tsg *tsg, int event_id, int *fd) { int err = 0; int local_fd; struct file *file; char name[64]; struct gk20a_event_id_data *event_id_data; struct gk20a *g; g = nvgpu_get(tsg->g); if (!g) return -ENODEV; err = get_unused_fd_flags(O_RDWR | O_CLOEXEC); if (err < 0) goto free_ref; local_fd = err; (void) snprintf(name, sizeof(name), "nvgpu-event%d-fd%d", event_id, local_fd); event_id_data = nvgpu_kzalloc(tsg->g, sizeof(*event_id_data)); if (!event_id_data) { err = -ENOMEM; goto clean_up; } event_id_data->g = g; event_id_data->id = tsg->tsgid; event_id_data->event_id = event_id; nvgpu_cond_init(&event_id_data->event_id_wq); nvgpu_mutex_init(&event_id_data->lock); nvgpu_init_list_node(&event_id_data->event_id_node); file = anon_inode_getfile(name, &gk20a_event_id_ops, event_id_data, O_RDWR); if (IS_ERR(file)) { err = PTR_ERR(file); goto clean_up_free; } nvgpu_mutex_acquire(&tsg->event_id_list_lock); nvgpu_list_add_tail(&event_id_data->event_id_node, &tsg->event_id_list); nvgpu_mutex_release(&tsg->event_id_list_lock); fd_install(local_fd, file); *fd = local_fd; return 0; clean_up_free: nvgpu_kfree(g, event_id_data); clean_up: put_unused_fd(local_fd); free_ref: nvgpu_put(g); return err; } static int gk20a_tsg_event_id_ctrl(struct gk20a *g, struct nvgpu_tsg *tsg, struct nvgpu_event_id_ctrl_args *args) { int err = 0; int fd = -1; if (args->event_id >= NVGPU_IOCTL_CHANNEL_EVENT_ID_MAX) return -EINVAL; nvgpu_speculation_barrier(); switch (args->cmd) { case NVGPU_IOCTL_CHANNEL_EVENT_ID_CMD_ENABLE: err = gk20a_tsg_event_id_enable(tsg, args->event_id, &fd); if (!err) args->event_fd = fd; break; default: nvgpu_err(tsg->g, "unrecognized tsg event id cmd: 0x%x", args->cmd); err = -EINVAL; break; } return err; } #endif /* CONFIG_NVGPU_CHANNEL_TSG_CONTROL */ #ifdef CONFIG_NVGPU_TSG_SHARING static inline struct nvgpu_tsg_ctrl_dev_node * nvgpu_tsg_ctrl_dev_node_from_tsg_entry(struct nvgpu_list_node *node) { return (struct nvgpu_tsg_ctrl_dev_node *) ((uintptr_t)node - offsetof(struct nvgpu_tsg_ctrl_dev_node, tsg_entry)); }; static void nvgpu_tsg_remove_ctrl_dev_inst_id(struct nvgpu_tsg *tsg, struct gk20a_ctrl_priv *ctrl_priv) { struct nvgpu_tsg_ctrl_dev_node *node, *tmp; struct gk20a *g = tsg->g; nvgpu_mutex_acquire(&tsg->tsg_share_lock); nvgpu_list_for_each_entry_safe(node, tmp, &tsg->ctrl_devices_list, nvgpu_tsg_ctrl_dev_node, tsg_entry) { if (node->device_instance_id == nvgpu_gpu_get_device_instance_id(ctrl_priv)) { nvgpu_log_info(g, "removing ctrl dev id %llx", nvgpu_gpu_get_device_instance_id(ctrl_priv)); nvgpu_list_del(&node->tsg_entry); nvgpu_kfree(g, node); } } nvgpu_mutex_release(&tsg->tsg_share_lock); } static int nvgpu_tsg_add_ctrl_dev_inst_id(struct nvgpu_tsg *tsg, struct gk20a_ctrl_priv *ctrl_priv) { struct nvgpu_tsg_ctrl_dev_node *ctrl_dev_node; struct gk20a *g = tsg->g; ctrl_dev_node = (struct nvgpu_tsg_ctrl_dev_node *) nvgpu_kzalloc(g, sizeof(struct nvgpu_tsg_ctrl_dev_node)); if (ctrl_dev_node == NULL) { nvgpu_err(g, "ctrl_dev_node alloc failed"); return -ENOMEM; } nvgpu_init_list_node(&ctrl_dev_node->tsg_entry); ctrl_dev_node->device_instance_id = nvgpu_gpu_get_device_instance_id(ctrl_priv); nvgpu_mutex_acquire(&tsg->tsg_share_lock); nvgpu_list_add_tail(&ctrl_dev_node->tsg_entry, &tsg->ctrl_devices_list); nvgpu_mutex_release(&tsg->tsg_share_lock); nvgpu_log_info(g, "added ctrl dev id %llx", nvgpu_gpu_get_device_instance_id(ctrl_priv)); return 0; } static bool nvgpu_tsg_is_authorized_ctrl_device_id(struct nvgpu_tsg *tsg, u64 device_instance_id) { struct nvgpu_tsg_ctrl_dev_node *node; bool authorized = false; nvgpu_mutex_acquire(&tsg->tsg_share_lock); nvgpu_list_for_each_entry(node, &tsg->ctrl_devices_list, nvgpu_tsg_ctrl_dev_node, tsg_entry) { if (node->device_instance_id == device_instance_id) { authorized = true; break; } } nvgpu_mutex_release(&tsg->tsg_share_lock); return authorized; } static int nvgpu_tsg_ioctl_get_share_token(struct gk20a *g, struct tsg_private *priv, struct nvgpu_tsg_get_share_token_args *args) { u64 source_device_instance_id = args->source_device_instance_id; u64 target_device_instance_id = args->target_device_instance_id; struct nvgpu_tsg *tsg = priv->tsg; u32 max_subctx_count; u32 gpu_instance_id; u64 share_token = 0; int err; if ((source_device_instance_id == 0UL) || (target_device_instance_id == 0UL)) { nvgpu_err(g, "Invalid source/target device instance id"); return -EINVAL; } if (!nvgpu_tsg_is_authorized_ctrl_device_id(tsg, source_device_instance_id)) { nvgpu_err(g, "Unauthorized source device instance id"); return -EINVAL; } gpu_instance_id = nvgpu_get_gpu_instance_id_from_cdev(g, priv->cdev); nvgpu_assert(gpu_instance_id < g->mig.num_gpu_instances); max_subctx_count = nvgpu_grmgr_get_gpu_instance_max_veid_count(g, gpu_instance_id); nvgpu_mutex_acquire(&tsg->tsg_share_lock); if (tsg->share_token_count == max_subctx_count) { nvgpu_err(g, "Maximum share tokens are in use"); nvgpu_mutex_release(&tsg->tsg_share_lock); return -ENOSPC; } err = nvgpu_gpu_get_share_token(g, source_device_instance_id, target_device_instance_id, tsg, &share_token); if (err != 0) { nvgpu_err(g, "Share token allocation failed %d", err); nvgpu_mutex_release(&tsg->tsg_share_lock); return err; } args->share_token = share_token; tsg->share_token_count++; nvgpu_mutex_release(&tsg->tsg_share_lock); return 0; } static int nvgpu_tsg_ioctl_revoke_share_token(struct gk20a *g, struct nvgpu_tsg *tsg, struct nvgpu_tsg_revoke_share_token_args *args) { u64 source_device_instance_id = args->source_device_instance_id; u64 target_device_instance_id = args->target_device_instance_id; u64 share_token = args->share_token; int err; if ((source_device_instance_id == 0UL) || (target_device_instance_id == 0UL) || (share_token == 0UL)) { nvgpu_err(g, "Invalid source/target device instance id or" " share token"); return -EINVAL; } if (!nvgpu_tsg_is_authorized_ctrl_device_id(tsg, source_device_instance_id)) { nvgpu_err(g, "Unauthorized source device instance id"); return -EINVAL; } nvgpu_mutex_acquire(&tsg->tsg_share_lock); err = nvgpu_gpu_revoke_share_token(g, source_device_instance_id, target_device_instance_id, share_token, tsg); if (err != 0) { nvgpu_err(g, "Share token revocation failed %d", err); nvgpu_mutex_release(&tsg->tsg_share_lock); return err; } tsg->share_token_count--; nvgpu_mutex_release(&tsg->tsg_share_lock); return 0; } #endif int nvgpu_ioctl_tsg_open(struct gk20a *g, struct gk20a_ctrl_priv *ctrl_priv, struct nvgpu_cdev *cdev, struct file *filp, bool open_share, u64 source_device_instance_id, u64 target_device_instance_id, u64 share_token) { struct tsg_private *priv; struct nvgpu_tsg *tsg; struct device *dev; int err; g = nvgpu_get(g); if (!g) return -ENODEV; dev = dev_from_gk20a(g); nvgpu_log(g, gpu_dbg_fn, "tsg: %s", dev_name(dev)); priv = nvgpu_kmalloc(g, sizeof(*priv)); if (!priv) { err = -ENOMEM; goto free_ref; } if (!open_share) { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on, %d", err); goto free_mem; } tsg = nvgpu_tsg_open(g, nvgpu_current_pid(g)); gk20a_idle(g); if (tsg == NULL) { err = -ENOMEM; goto free_mem; } gk20a_sched_ctrl_tsg_added(g, tsg); #ifndef CONFIG_NVGPU_TSG_SHARING } #else } else { tsg = nvgpu_gpu_open_tsg_with_share_token(g, source_device_instance_id, target_device_instance_id, share_token); if (tsg == NULL) { nvgpu_err(g, "TSG open with token failed"); err = -EINVAL; goto free_mem; } } if (ctrl_priv != NULL) { err = nvgpu_tsg_add_ctrl_dev_inst_id(tsg, ctrl_priv); if (err != 0) { nvgpu_err(g, "add ctrl dev failed %d", err); nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); goto free_mem; } } #endif priv->g = g; priv->tsg = tsg; priv->cdev = cdev; priv->ctrl_priv = ctrl_priv; filp->private_data = priv; return 0; free_mem: nvgpu_kfree(g, priv); free_ref: nvgpu_put(g); return err; } int nvgpu_ioctl_tsg_dev_open(struct inode *inode, struct file *filp) { struct gk20a *g; int ret; struct nvgpu_cdev *cdev; cdev = container_of(inode->i_cdev, struct nvgpu_cdev, cdev); g = nvgpu_get_gk20a_from_cdev(cdev); nvgpu_log_fn(g, " "); ret = gk20a_busy(g); if (ret) { nvgpu_err(g, "failed to power on, %d", ret); return ret; } ret = nvgpu_ioctl_tsg_open(g, NULL, cdev, filp, false, 0ULL, 0ULL, 0ULL); gk20a_idle(g); nvgpu_log_fn(g, "done"); return ret; } void nvgpu_ioctl_tsg_release(struct nvgpu_ref *ref) { struct nvgpu_tsg *tsg = container_of(ref, struct nvgpu_tsg, refcount); struct gk20a *g = tsg->g; #ifdef CONFIG_NVGPU_TSG_SHARING nvgpu_assert(tsg->share_token_count <= 1U); #endif gk20a_sched_ctrl_tsg_removed(g, tsg); nvgpu_tsg_release(ref); } int nvgpu_ioctl_tsg_dev_release(struct inode *inode, struct file *filp) { struct tsg_private *priv = filp->private_data; struct nvgpu_tsg *tsg; struct gk20a *g; #ifdef CONFIG_NVGPU_TSG_SHARING u32 count; int err; #endif if (!priv) { /* open failed, never got a tsg for this file */ return 0; } tsg = priv->tsg; g = tsg->g; #ifdef CONFIG_NVGPU_TSG_SHARING nvgpu_mutex_acquire(&tsg->tsg_share_lock); err = nvgpu_gpu_tsg_revoke_share_tokens(g, nvgpu_gpu_get_device_instance_id(priv->ctrl_priv), tsg, &count); if (err != 0) { nvgpu_err(g, "revoke token(%llu) failed %d", nvgpu_gpu_get_device_instance_id(priv->ctrl_priv), err); } tsg->share_token_count -= count; nvgpu_mutex_release(&tsg->tsg_share_lock); nvgpu_tsg_remove_ctrl_dev_inst_id(tsg, priv->ctrl_priv); #endif nvgpu_ref_put(&tsg->refcount, nvgpu_ioctl_tsg_release); nvgpu_kfree(g, priv); nvgpu_put(g); return 0; } static int gk20a_tsg_ioctl_set_runlist_interleave(struct gk20a *g, struct nvgpu_tsg *tsg, struct nvgpu_runlist_interleave_args *arg) { struct nvgpu_sched_ctrl *sched = &g->sched_ctrl; u32 level = arg->level; int err; nvgpu_log(g, gpu_dbg_fn | gpu_dbg_sched, "tsgid=%u", tsg->tsgid); nvgpu_mutex_acquire(&sched->control_lock); if (sched->control_locked) { err = -EPERM; goto done; } err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu"); goto done; } level = nvgpu_get_common_runlist_level(level); err = nvgpu_tsg_set_interleave(tsg, level); gk20a_idle(g); done: nvgpu_mutex_release(&sched->control_lock); return err; } static int gk20a_tsg_ioctl_set_timeslice(struct gk20a *g, struct nvgpu_tsg *tsg, struct nvgpu_timeslice_args *arg) { struct nvgpu_sched_ctrl *sched = &g->sched_ctrl; int err; nvgpu_log(g, gpu_dbg_fn | gpu_dbg_sched, "tsgid=%u", tsg->tsgid); nvgpu_mutex_acquire(&sched->control_lock); if (sched->control_locked) { err = -EPERM; goto done; } err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu"); goto done; } err = g->ops.tsg.set_timeslice(tsg, arg->timeslice_us); gk20a_idle(g); done: nvgpu_mutex_release(&sched->control_lock); return err; } static int gk20a_tsg_ioctl_get_timeslice(struct gk20a *g, struct nvgpu_tsg *tsg, struct nvgpu_timeslice_args *arg) { arg->timeslice_us = nvgpu_tsg_get_timeslice(tsg); return 0; } static int gk20a_tsg_ioctl_read_single_sm_error_state(struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_read_single_sm_error_state_args *args) { const struct nvgpu_tsg_sm_error_state *sm_error_state = NULL; struct nvgpu_tsg_sm_error_state_record sm_error_state_record; u32 sm_id; int err = 0; sm_id = args->sm_id; nvgpu_speculation_barrier(); sm_error_state = nvgpu_tsg_get_sm_error_state(tsg, sm_id); if (sm_error_state == NULL) { return -EINVAL; } sm_error_state_record.global_esr = sm_error_state->hww_global_esr; sm_error_state_record.warp_esr = sm_error_state->hww_warp_esr; sm_error_state_record.warp_esr_pc = sm_error_state->hww_warp_esr_pc; sm_error_state_record.global_esr_report_mask = sm_error_state->hww_global_esr_report_mask; sm_error_state_record.warp_esr_report_mask = sm_error_state->hww_warp_esr_report_mask; if (args->record_size > 0) { size_t write_size = sizeof(*sm_error_state); nvgpu_speculation_barrier(); if (write_size > args->record_size) write_size = args->record_size; nvgpu_mutex_acquire(&g->dbg_sessions_lock); err = copy_to_user((void __user *)(uintptr_t) args->record_mem, &sm_error_state_record, write_size); nvgpu_mutex_release(&g->dbg_sessions_lock); if (err) { nvgpu_err(g, "copy_to_user failed!"); return err; } args->record_size = write_size; } return 0; } static int nvgpu_gpu_ioctl_set_l2_max_ways_evict_last( struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_l2_max_ways_evict_last_args *args) { int err; u32 gr_instance_id = nvgpu_grmgr_get_gr_instance_id(g, gpu_instance_id); nvgpu_mutex_acquire(&g->dbg_sessions_lock); if (g->ops.ltc.set_l2_max_ways_evict_last) { err = nvgpu_gr_exec_with_err_for_instance(g, gr_instance_id, g->ops.ltc.set_l2_max_ways_evict_last(g, tsg, args->max_ways)); } else { err = -ENOSYS; } nvgpu_mutex_release(&g->dbg_sessions_lock); return err; } static int nvgpu_gpu_ioctl_get_l2_max_ways_evict_last( struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_l2_max_ways_evict_last_args *args) { int err; u32 gr_instance_id = nvgpu_grmgr_get_gr_instance_id(g, gpu_instance_id); nvgpu_mutex_acquire(&g->dbg_sessions_lock); if (g->ops.ltc.get_l2_max_ways_evict_last) { err = nvgpu_gr_exec_with_err_for_instance(g, gr_instance_id, g->ops.ltc.get_l2_max_ways_evict_last(g, tsg, &args->max_ways)); } else { err = -ENOSYS; } nvgpu_mutex_release(&g->dbg_sessions_lock); return err; } static u32 nvgpu_translate_l2_sector_promotion_flag(struct gk20a *g, u32 flag) { u32 promotion_flag = NVGPU_L2_SECTOR_PROMOTE_FLAG_INVALID; switch (flag) { case NVGPU_GPU_IOCTL_TSG_L2_SECTOR_PROMOTE_FLAG_NONE: promotion_flag = NVGPU_L2_SECTOR_PROMOTE_FLAG_NONE; break; case NVGPU_GPU_IOCTL_TSG_L2_SECTOR_PROMOTE_FLAG_64B: promotion_flag = NVGPU_L2_SECTOR_PROMOTE_FLAG_64B; break; case NVGPU_GPU_IOCTL_TSG_L2_SECTOR_PROMOTE_FLAG_128B: promotion_flag = NVGPU_L2_SECTOR_PROMOTE_FLAG_128B; break; default: nvgpu_err(g, "invalid sector promotion flag(%d)", flag); break; } return promotion_flag; } static int nvgpu_gpu_ioctl_set_l2_sector_promotion(struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_set_l2_sector_promotion_args *args) { u32 promotion_flag = 0U; int err = 0; u32 gr_instance_id = nvgpu_grmgr_get_gr_instance_id(g, gpu_instance_id); /* * L2 sector promotion is a perf feature so return silently without * error if not supported. */ if (g->ops.ltc.set_l2_sector_promotion == NULL) { return 0; } promotion_flag = nvgpu_translate_l2_sector_promotion_flag(g, args->promotion_flag); if (promotion_flag == NVGPU_L2_SECTOR_PROMOTE_FLAG_INVALID) { return -EINVAL; } err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu"); return err; } err = nvgpu_gr_exec_with_err_for_instance(g, gr_instance_id, g->ops.ltc.set_l2_sector_promotion(g, tsg, promotion_flag)); gk20a_idle(g); return err; } static int nvgpu_tsg_ioctl_create_subcontext(struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_create_subcontext_args *args) { u32 max_subctx_count; struct vm_gk20a *vm; u32 veid; int err; if (!nvgpu_is_enabled(g, NVGPU_SUPPORT_TSG_SUBCONTEXTS)) { return 0; } if (args->type != NVGPU_TSG_SUBCONTEXT_TYPE_SYNC && args->type != NVGPU_TSG_SUBCONTEXT_TYPE_ASYNC) { nvgpu_err(g, "Invalid subcontext type %u", args->type); return -EINVAL; } vm = nvgpu_vm_get_from_file(args->as_fd); if (vm == NULL) { nvgpu_err(g, "Invalid VM (fd = %d) specified for subcontext", args->as_fd); return -EINVAL; } max_subctx_count = nvgpu_grmgr_get_gpu_instance_max_veid_count(g, gpu_instance_id); err = nvgpu_tsg_create_subcontext(g, tsg, args->type, vm, max_subctx_count, &veid); if (err != 0) { nvgpu_err(g, "Create subcontext failed %d", err); nvgpu_vm_put(vm); return err; } nvgpu_vm_put(vm); args->veid = veid; return 0; } static int nvgpu_tsg_ioctl_delete_subcontext(struct gk20a *g, u32 gpu_instance_id, struct nvgpu_tsg *tsg, struct nvgpu_tsg_delete_subcontext_args *args) { u32 max_subctx_count; int err; if (!nvgpu_is_enabled(g, NVGPU_SUPPORT_TSG_SUBCONTEXTS)) { return 0; } max_subctx_count = nvgpu_grmgr_get_gpu_instance_max_veid_count(g, gpu_instance_id); err = nvgpu_tsg_delete_subcontext(g, tsg, max_subctx_count, args->veid); if (err != 0) { nvgpu_err(g, "Delete subcontext failed %d", err); } return err; } long nvgpu_ioctl_tsg_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct tsg_private *priv = filp->private_data; struct nvgpu_tsg *tsg = priv->tsg; struct gk20a *g = tsg->g; u8 __maybe_unused buf[NVGPU_TSG_IOCTL_MAX_ARG_SIZE]; int err = 0; u32 gpu_instance_id, gr_instance_id; nvgpu_log_fn(g, "start %d", _IOC_NR(cmd)); if ((_IOC_TYPE(cmd) != NVGPU_TSG_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) || (_IOC_NR(cmd) > NVGPU_TSG_IOCTL_LAST) || (_IOC_SIZE(cmd) > NVGPU_TSG_IOCTL_MAX_ARG_SIZE)) return -EINVAL; (void) memset(buf, 0, sizeof(buf)); if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) return -EFAULT; } if (!g->sw_ready) { err = gk20a_busy(g); if (err) return err; gk20a_idle(g); } gpu_instance_id = nvgpu_get_gpu_instance_id_from_cdev(g, priv->cdev); nvgpu_assert(gpu_instance_id < g->mig.num_gpu_instances); gr_instance_id = nvgpu_grmgr_get_gr_instance_id(g, gpu_instance_id); nvgpu_assert(gr_instance_id < g->num_gr_instances); switch (cmd) { case NVGPU_TSG_IOCTL_BIND_CHANNEL: { int ch_fd = *(int *)buf; if (ch_fd < 0) { err = -EINVAL; break; } err = nvgpu_tsg_bind_channel_fd(tsg, ch_fd); break; } case NVGPU_TSG_IOCTL_BIND_CHANNEL_EX: { err = gk20a_tsg_ioctl_bind_channel_ex(g, priv, (struct nvgpu_tsg_bind_channel_ex_args *)buf); break; } case NVGPU_TSG_IOCTL_UNBIND_CHANNEL: { int ch_fd = *(int *)buf; if (ch_fd < 0) { err = -EINVAL; break; } err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to host gk20a for ioctl cmd: 0x%x", cmd); break; } err = nvgpu_tsg_unbind_channel_fd(tsg, ch_fd); gk20a_idle(g); break; } #ifdef CONFIG_NVS_PRESENT case NVGPU_TSG_IOCTL_BIND_SCHEDULING_DOMAIN: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to host gk20a for ioctl cmd: 0x%x", cmd); break; } err = nvgpu_tsg_bind_scheduling_domain(tsg, (struct nvgpu_tsg_bind_scheduling_domain_args *)buf); gk20a_idle(g); break; } #endif case NVGPU_IOCTL_TSG_ENABLE: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to host gk20a for ioctl cmd: 0x%x", cmd); return err; } g->ops.tsg.enable(tsg); gk20a_idle(g); break; } case NVGPU_IOCTL_TSG_DISABLE: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to host gk20a for ioctl cmd: 0x%x", cmd); return err; } g->ops.tsg.disable(tsg); gk20a_idle(g); break; } case NVGPU_IOCTL_TSG_PREEMPT: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to host gk20a for ioctl cmd: 0x%x", cmd); return err; } /* preempt TSG */ err = g->ops.fifo.preempt_tsg(g, tsg); gk20a_idle(g); break; } #ifdef CONFIG_NVGPU_CHANNEL_TSG_CONTROL case NVGPU_IOCTL_TSG_EVENT_ID_CTRL: { err = gk20a_tsg_event_id_ctrl(g, tsg, (struct nvgpu_event_id_ctrl_args *)buf); break; } #endif case NVGPU_IOCTL_TSG_SET_RUNLIST_INTERLEAVE: err = gk20a_tsg_ioctl_set_runlist_interleave(g, tsg, (struct nvgpu_runlist_interleave_args *)buf); break; case NVGPU_IOCTL_TSG_SET_TIMESLICE: { err = gk20a_tsg_ioctl_set_timeslice(g, tsg, (struct nvgpu_timeslice_args *)buf); break; } case NVGPU_IOCTL_TSG_GET_TIMESLICE: { err = gk20a_tsg_ioctl_get_timeslice(g, tsg, (struct nvgpu_timeslice_args *)buf); break; } case NVGPU_TSG_IOCTL_READ_SINGLE_SM_ERROR_STATE: { err = gk20a_tsg_ioctl_read_single_sm_error_state(g, gpu_instance_id, tsg, (struct nvgpu_tsg_read_single_sm_error_state_args *)buf); break; } case NVGPU_TSG_IOCTL_SET_L2_MAX_WAYS_EVICT_LAST: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu for ioctl cmd: 0x%x", cmd); break; } err = nvgpu_gpu_ioctl_set_l2_max_ways_evict_last(g, gpu_instance_id, tsg, (struct nvgpu_tsg_l2_max_ways_evict_last_args *)buf); gk20a_idle(g); break; } case NVGPU_TSG_IOCTL_GET_L2_MAX_WAYS_EVICT_LAST: { err = gk20a_busy(g); if (err) { nvgpu_err(g, "failed to power on gpu for ioctl cmd: 0x%x", cmd); break; } err = nvgpu_gpu_ioctl_get_l2_max_ways_evict_last(g, gpu_instance_id, tsg, (struct nvgpu_tsg_l2_max_ways_evict_last_args *)buf); gk20a_idle(g); break; } case NVGPU_TSG_IOCTL_SET_L2_SECTOR_PROMOTION: { err = nvgpu_gpu_ioctl_set_l2_sector_promotion(g, gpu_instance_id, tsg, (struct nvgpu_tsg_set_l2_sector_promotion_args *)buf); break; } case NVGPU_TSG_IOCTL_CREATE_SUBCONTEXT: { err = nvgpu_tsg_ioctl_create_subcontext(g, gpu_instance_id, tsg, (struct nvgpu_tsg_create_subcontext_args *)buf); break; } case NVGPU_TSG_IOCTL_DELETE_SUBCONTEXT: { err = nvgpu_tsg_ioctl_delete_subcontext(g, gpu_instance_id, tsg, (struct nvgpu_tsg_delete_subcontext_args *)buf); break; } #ifdef CONFIG_NVGPU_TSG_SHARING case NVGPU_TSG_IOCTL_GET_SHARE_TOKEN: { err = nvgpu_tsg_ioctl_get_share_token(g, priv, (struct nvgpu_tsg_get_share_token_args *)buf); break; } case NVGPU_TSG_IOCTL_REVOKE_SHARE_TOKEN: { err = nvgpu_tsg_ioctl_revoke_share_token(g, tsg, (struct nvgpu_tsg_revoke_share_token_args *)buf); break; } #endif default: nvgpu_err(g, "unrecognized tsg gpu ioctl cmd: 0x%x", cmd); err = -ENOTTY; break; } if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); return err; }