From 2a2c16af5f9f1ccfc93a13e820d5381e5c881e92 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Wed, 18 Apr 2018 12:59:00 -0700 Subject: gpu: nvgpu: Move Linux files away from common Move all Linux source code files to drivers/gpu/nvgpu/os/linux from drivers/gpu/nvgpu/common/linux. This changes the meaning of common to be OS independent. JIRA NVGPU-598 JIRA NVGPU-601 Change-Id: Ib7f2a43d3688bb0d0b7dcc48469a6783fd988ce9 Signed-off-by: Terje Bergstrom Reviewed-on: https://git-master.nvidia.com/r/1747714 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c | 562 +++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c (limited to 'drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c') diff --git a/drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c b/drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c new file mode 100644 index 00000000..501b5f93 --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/ioctl_clk_arb.c @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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 +#ifdef CONFIG_DEBUG_FS +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gk20a/gk20a.h" +#include "clk/clk.h" +#include "pstate/pstate.h" +#include "lpwr/lpwr.h" +#include "volt/volt.h" + +#ifdef CONFIG_DEBUG_FS +#include "os_linux.h" +#endif + +static int nvgpu_clk_arb_release_completion_dev(struct inode *inode, + struct file *filp) +{ + struct nvgpu_clk_dev *dev = filp->private_data; + struct nvgpu_clk_session *session = dev->session; + + + clk_arb_dbg(session->g, " "); + + nvgpu_ref_put(&session->refcount, nvgpu_clk_arb_free_session); + nvgpu_ref_put(&dev->refcount, nvgpu_clk_arb_free_fd); + return 0; +} + +static inline unsigned int nvgpu_convert_poll_mask(unsigned int nvgpu_poll_mask) +{ + unsigned int poll_mask = 0; + + if (nvgpu_poll_mask & NVGPU_POLLIN) + poll_mask |= POLLIN; + if (nvgpu_poll_mask & NVGPU_POLLPRI) + poll_mask |= POLLPRI; + if (nvgpu_poll_mask & NVGPU_POLLOUT) + poll_mask |= POLLOUT; + if (nvgpu_poll_mask & NVGPU_POLLRDNORM) + poll_mask |= POLLRDNORM; + if (nvgpu_poll_mask & NVGPU_POLLHUP) + poll_mask |= POLLHUP; + + return poll_mask; +} + +static unsigned int nvgpu_clk_arb_poll_dev(struct file *filp, poll_table *wait) +{ + struct nvgpu_clk_dev *dev = filp->private_data; + + clk_arb_dbg(dev->session->g, " "); + + poll_wait(filp, &dev->readout_wq.wq, wait); + return nvgpu_convert_poll_mask(nvgpu_atomic_xchg(&dev->poll_mask, 0)); +} + +void nvgpu_clk_arb_event_post_event(struct nvgpu_clk_dev *dev) +{ + nvgpu_cond_broadcast_interruptible(&dev->readout_wq); +} + +static int nvgpu_clk_arb_release_event_dev(struct inode *inode, + struct file *filp) +{ + struct nvgpu_clk_dev *dev = filp->private_data; + struct nvgpu_clk_session *session = dev->session; + struct nvgpu_clk_arb *arb; + + arb = session->g->clk_arb; + + clk_arb_dbg(session->g, " "); + + if (arb) { + nvgpu_spinlock_acquire(&arb->users_lock); + nvgpu_list_del(&dev->link); + nvgpu_spinlock_release(&arb->users_lock); + nvgpu_clk_notification_queue_free(arb->g, &dev->queue); + } + + nvgpu_ref_put(&session->refcount, nvgpu_clk_arb_free_session); + nvgpu_ref_put(&dev->refcount, nvgpu_clk_arb_free_fd); + + return 0; +} + +static inline u32 nvgpu_convert_gpu_event(u32 nvgpu_event) +{ + u32 nvgpu_gpu_event; + + switch (nvgpu_event) { + case NVGPU_EVENT_VF_UPDATE: + nvgpu_gpu_event = NVGPU_GPU_EVENT_VF_UPDATE; + break; + case NVGPU_EVENT_ALARM_TARGET_VF_NOT_POSSIBLE: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_TARGET_VF_NOT_POSSIBLE; + break; + case NVGPU_EVENT_ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE; + break; + case NVGPU_EVENT_ALARM_CLOCK_ARBITER_FAILED: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_CLOCK_ARBITER_FAILED; + break; + case NVGPU_EVENT_ALARM_VF_TABLE_UPDATE_FAILED: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_VF_TABLE_UPDATE_FAILED; + break; + case NVGPU_EVENT_ALARM_THERMAL_ABOVE_THRESHOLD: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_THERMAL_ABOVE_THRESHOLD; + break; + case NVGPU_EVENT_ALARM_POWER_ABOVE_THRESHOLD: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_POWER_ABOVE_THRESHOLD; + break; + case NVGPU_EVENT_ALARM_GPU_LOST: + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_GPU_LOST; + break; + default: + /* Control shouldn't come here */ + nvgpu_gpu_event = NVGPU_GPU_EVENT_ALARM_GPU_LOST + 1; + break; + } + return nvgpu_gpu_event; +} + +static inline u32 __pending_event(struct nvgpu_clk_dev *dev, + struct nvgpu_gpu_event_info *info) { + + u32 tail, head; + u32 events = 0; + struct nvgpu_clk_notification *p_notif; + + tail = nvgpu_atomic_read(&dev->queue.tail); + head = nvgpu_atomic_read(&dev->queue.head); + + head = (tail - head) < dev->queue.size ? head : tail - dev->queue.size; + + if (_WRAPGTEQ(tail, head) && info) { + head++; + p_notif = &dev->queue.notifications[head % dev->queue.size]; + events |= nvgpu_convert_gpu_event(p_notif->notification); + info->event_id = ffs(events) - 1; + info->timestamp = p_notif->timestamp; + nvgpu_atomic_set(&dev->queue.head, head); + } + + return events; +} + +static ssize_t nvgpu_clk_arb_read_event_dev(struct file *filp, char __user *buf, + size_t size, loff_t *off) +{ + struct nvgpu_clk_dev *dev = filp->private_data; + struct nvgpu_gpu_event_info info; + ssize_t err; + + clk_arb_dbg(dev->session->g, + "filp=%p, buf=%p, size=%zu", filp, buf, size); + + if ((size - *off) < sizeof(info)) + return 0; + + memset(&info, 0, sizeof(info)); + /* Get the oldest event from the queue */ + while (!__pending_event(dev, &info)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + err = NVGPU_COND_WAIT_INTERRUPTIBLE(&dev->readout_wq, + __pending_event(dev, &info), 0); + if (err) + return err; + if (info.timestamp) + break; + } + + if (copy_to_user(buf + *off, &info, sizeof(info))) + return -EFAULT; + + return sizeof(info); +} + +static int nvgpu_clk_arb_set_event_filter(struct nvgpu_clk_dev *dev, + struct nvgpu_gpu_set_event_filter_args *args) +{ + struct gk20a *g = dev->session->g; + u32 mask; + + nvgpu_log(g, gpu_dbg_fn, " "); + + if (args->flags) + return -EINVAL; + + if (args->size != 1) + return -EINVAL; + + if (copy_from_user(&mask, (void __user *) args->buffer, + args->size * sizeof(u32))) + return -EFAULT; + + /* update alarm mask */ + nvgpu_atomic_set(&dev->enabled_mask, mask); + + return 0; +} + +static long nvgpu_clk_arb_ioctl_event_dev(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct nvgpu_clk_dev *dev = filp->private_data; + struct gk20a *g = dev->session->g; + u8 buf[NVGPU_EVENT_IOCTL_MAX_ARG_SIZE]; + int err = 0; + + nvgpu_log(g, gpu_dbg_fn, "nr=%d", _IOC_NR(cmd)); + + if ((_IOC_TYPE(cmd) != NVGPU_EVENT_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) + || (_IOC_NR(cmd) > NVGPU_EVENT_IOCTL_LAST)) + return -EINVAL; + + BUG_ON(_IOC_SIZE(cmd) > NVGPU_EVENT_IOCTL_MAX_ARG_SIZE); + + memset(buf, 0, sizeof(buf)); + if (_IOC_DIR(cmd) & _IOC_WRITE) { + if (copy_from_user(buf, (void __user *) arg, _IOC_SIZE(cmd))) + return -EFAULT; + } + + switch (cmd) { + case NVGPU_EVENT_IOCTL_SET_FILTER: + err = nvgpu_clk_arb_set_event_filter(dev, + (struct nvgpu_gpu_set_event_filter_args *)buf); + break; + default: + nvgpu_warn(g, "unrecognized event ioctl cmd: 0x%x", cmd); + err = -ENOTTY; + } + + if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) + err = copy_to_user((void __user *) arg, buf, _IOC_SIZE(cmd)); + + return err; +} + +static const struct file_operations completion_dev_ops = { + .owner = THIS_MODULE, + .release = nvgpu_clk_arb_release_completion_dev, + .poll = nvgpu_clk_arb_poll_dev, +}; + +static const struct file_operations event_dev_ops = { + .owner = THIS_MODULE, + .release = nvgpu_clk_arb_release_event_dev, + .poll = nvgpu_clk_arb_poll_dev, + .read = nvgpu_clk_arb_read_event_dev, +#ifdef CONFIG_COMPAT + .compat_ioctl = nvgpu_clk_arb_ioctl_event_dev, +#endif + .unlocked_ioctl = nvgpu_clk_arb_ioctl_event_dev, +}; + +static int nvgpu_clk_arb_install_fd(struct gk20a *g, + struct nvgpu_clk_session *session, + const struct file_operations *fops, + struct nvgpu_clk_dev **_dev) +{ + struct file *file; + int fd; + int err; + int status; + char name[64]; + struct nvgpu_clk_dev *dev; + + clk_arb_dbg(g, " "); + + dev = nvgpu_kzalloc(g, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + status = nvgpu_clk_notification_queue_alloc(g, &dev->queue, + DEFAULT_EVENT_NUMBER); + if (status < 0) { + err = status; + goto fail; + } + + fd = get_unused_fd_flags(O_RDWR); + if (fd < 0) { + err = fd; + goto fail; + } + + snprintf(name, sizeof(name), "%s-clk-fd%d", g->name, fd); + file = anon_inode_getfile(name, fops, dev, O_RDWR); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto fail_fd; + } + + fd_install(fd, file); + + nvgpu_cond_init(&dev->readout_wq); + + nvgpu_atomic_set(&dev->poll_mask, 0); + + dev->session = session; + nvgpu_ref_init(&dev->refcount); + + nvgpu_ref_get(&session->refcount); + + *_dev = dev; + + return fd; + +fail_fd: + put_unused_fd(fd); +fail: + nvgpu_kfree(g, dev); + + return err; +} + +int nvgpu_clk_arb_install_event_fd(struct gk20a *g, + struct nvgpu_clk_session *session, int *event_fd, u32 alarm_mask) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + struct nvgpu_clk_dev *dev; + int fd; + + clk_arb_dbg(g, " "); + + fd = nvgpu_clk_arb_install_fd(g, session, &event_dev_ops, &dev); + if (fd < 0) + return fd; + + /* TODO: alarm mask needs to be set to default value to prevent + * failures of legacy tests. This will be removed when sanity is + * updated + */ + if (alarm_mask) + nvgpu_atomic_set(&dev->enabled_mask, alarm_mask); + else + nvgpu_atomic_set(&dev->enabled_mask, EVENT(VF_UPDATE)); + + dev->arb_queue_head = nvgpu_atomic_read(&arb->notification_queue.head); + + nvgpu_spinlock_acquire(&arb->users_lock); + nvgpu_list_add_tail(&dev->link, &arb->users); + nvgpu_spinlock_release(&arb->users_lock); + + *event_fd = fd; + + return 0; +} + +int nvgpu_clk_arb_install_request_fd(struct gk20a *g, + struct nvgpu_clk_session *session, int *request_fd) +{ + struct nvgpu_clk_dev *dev; + int fd; + + clk_arb_dbg(g, " "); + + fd = nvgpu_clk_arb_install_fd(g, session, &completion_dev_ops, &dev); + if (fd < 0) + return fd; + + *request_fd = fd; + + return 0; +} + +int nvgpu_clk_arb_commit_request_fd(struct gk20a *g, + struct nvgpu_clk_session *session, int request_fd) +{ + struct nvgpu_clk_arb *arb = g->clk_arb; + struct nvgpu_clk_dev *dev; + struct fd fd; + int err = 0; + + clk_arb_dbg(g, " "); + + fd = fdget(request_fd); + if (!fd.file) + return -EINVAL; + + if (fd.file->f_op != &completion_dev_ops) { + err = -EINVAL; + goto fdput_fd; + } + + dev = (struct nvgpu_clk_dev *) fd.file->private_data; + + if (!dev || dev->session != session) { + err = -EINVAL; + goto fdput_fd; + } + nvgpu_ref_get(&dev->refcount); + nvgpu_spinlock_acquire(&session->session_lock); + nvgpu_list_add(&dev->node, &session->targets); + nvgpu_spinlock_release(&session->session_lock); + nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); + +fdput_fd: + fdput(fd); + return err; +} + +int nvgpu_clk_arb_set_session_target_mhz(struct nvgpu_clk_session *session, + int request_fd, u32 api_domain, u16 target_mhz) +{ + struct nvgpu_clk_dev *dev; + struct fd fd; + int err = 0; + + clk_arb_dbg(session->g, + "domain=0x%08x target_mhz=%u", api_domain, target_mhz); + + fd = fdget(request_fd); + if (!fd.file) + return -EINVAL; + + if (fd.file->f_op != &completion_dev_ops) { + err = -EINVAL; + goto fdput_fd; + } + + dev = fd.file->private_data; + if (!dev || dev->session != session) { + err = -EINVAL; + goto fdput_fd; + } + + switch (api_domain) { + case NVGPU_CLK_DOMAIN_MCLK: + dev->mclk_target_mhz = target_mhz; + break; + + case NVGPU_CLK_DOMAIN_GPCCLK: + dev->gpc2clk_target_mhz = target_mhz * 2ULL; + break; + + default: + err = -EINVAL; + } + +fdput_fd: + fdput(fd); + return err; +} + +u32 nvgpu_clk_arb_get_arbiter_clk_domains(struct gk20a *g) +{ + u32 clk_domains = g->ops.clk_arb.get_arbiter_clk_domains(g); + u32 api_domains = 0; + + if (clk_domains & CTRL_CLK_DOMAIN_GPC2CLK) + api_domains |= BIT(NVGPU_GPU_CLK_DOMAIN_GPCCLK); + + if (clk_domains & CTRL_CLK_DOMAIN_MCLK) + api_domains |= BIT(NVGPU_GPU_CLK_DOMAIN_MCLK); + + return api_domains; +} + +#ifdef CONFIG_DEBUG_FS +static int nvgpu_clk_arb_stats_show(struct seq_file *s, void *unused) +{ + struct gk20a *g = s->private; + struct nvgpu_clk_arb *arb = g->clk_arb; + struct nvgpu_clk_arb_debug *debug; + + u64 num; + s64 tmp, avg, std, max, min; + + debug = NV_ACCESS_ONCE(arb->debug); + /* Make copy of structure and ensure no reordering */ + nvgpu_smp_rmb(); + if (!debug) + return -EINVAL; + + std = debug->switch_std; + avg = debug->switch_avg; + max = debug->switch_max; + min = debug->switch_min; + num = debug->switch_num; + + tmp = std; + do_div(tmp, num); + seq_printf(s, "Number of transitions: %lld\n", + num); + seq_printf(s, "max / min : %lld / %lld usec\n", + max, min); + seq_printf(s, "avg / std : %lld / %ld usec\n", + avg, int_sqrt(tmp)); + + return 0; +} + +static int nvgpu_clk_arb_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, nvgpu_clk_arb_stats_show, inode->i_private); +} + +static const struct file_operations nvgpu_clk_arb_stats_fops = { + .open = nvgpu_clk_arb_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +int nvgpu_clk_arb_debugfs_init(struct gk20a *g) +{ + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct dentry *gpu_root = l->debugfs; + struct dentry *d; + + nvgpu_log(g, gpu_dbg_info, "g=%p", g); + + d = debugfs_create_file( + "arb_stats", + S_IRUGO, + gpu_root, + g, + &nvgpu_clk_arb_stats_fops); + if (!d) + return -ENOMEM; + + return 0; +} +#endif -- cgit v1.2.2