gpu: nvgpu: Add support for alarms in arbiter

Add a queue system to collect alarm notifications
and deliver them to user space

Add interface for driver to set global alarms

JIRA: DNVGPU-189

Change-Id: I24a340283c02d8ea95ad6ea148acdb37157ef69c
Signed-off-by: David Nieto <dmartineznie@nvidia.com>
Reviewed-on: http://git-master/r/1252475
(cherry picked from commit 5b79c7541066148ce0580d25daad54a8fa82f8be)
Reviewed-on: http://git-master/r/1280887
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
David Nieto
2016-11-13 20:44:13 -08:00
committed by mobile promotions
parent 1526670f4a
commit 177c874ff2
2 changed files with 406 additions and 109 deletions

View File

@@ -25,6 +25,11 @@
#define MAX_F_POINTS 127
#define DEFAULT_EVENT_NUMBER 32
struct nvgpu_clk_dev;
struct nvgpu_clk_arb_target;
struct nvgpu_clk_notification_queue;
#ifdef CONFIG_DEBUG_FS
static int nvgpu_clk_arb_debugfs_init(struct gk20a *g);
@@ -34,12 +39,12 @@ static int nvgpu_clk_arb_release_event_dev(struct inode *inode,
struct file *filp);
static int nvgpu_clk_arb_release_completion_dev(struct inode *inode,
struct file *filp);
static unsigned int nvgpu_clk_arb_poll_completion_dev(struct file *filp, poll_table *wait);
static unsigned int nvgpu_clk_arb_poll_event_dev(struct file *filp, poll_table *wait);
static ssize_t nvgpu_clk_arb_read_event_dev(struct file *filp,
char __user *buf, size_t size, loff_t *off);
long nvgpu_clk_arb_ioctl_event_dev(struct file *filp, unsigned int cmd,
unsigned long arg);
static unsigned int nvgpu_clk_arb_poll_dev(struct file *filp, poll_table *wait);
static ssize_t nvgpu_clk_arb_read_event_dev(struct file *filp, char __user *buf,
size_t size, loff_t *off);
static long nvgpu_clk_arb_ioctl_event_dev(struct file *filp, unsigned int cmd,
unsigned long arg);
static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work);
static void nvgpu_clk_arb_run_vf_table_cb(struct work_struct *work);
@@ -52,6 +57,21 @@ static int nvgpu_clk_arb_change_vf_point(struct gk20a *g, u16 gpc2clk_target,
static u8 nvgpu_clk_arb_find_vf_point(struct nvgpu_clk_arb *arb,
u16 *gpc2clk, u16 *sys2clk, u16 *xbar2clk, u16 *mclk,
u32 *voltuv, u32 *voltuv_sram, u32 *nuvmin, u32 *nuvmin_sram);
static u32 nvgpu_clk_arb_notify(struct nvgpu_clk_dev *dev,
struct nvgpu_clk_arb_target *target,
u32 alarm_mask);
static void nvgpu_clk_arb_set_global_alarm(struct gk20a *g, u32 alarm);
static void nvgpu_clk_arb_clear_global_alarm(struct gk20a *g, u32 alarm);
static void nvgpu_clk_arb_queue_notification(struct gk20a *g,
struct nvgpu_clk_notification_queue *queue,
u32 alarm_mask);
static int nvgpu_clk_notification_queue_alloc(
struct nvgpu_clk_notification_queue *queue,
size_t events_number);
static void nvgpu_clk_notification_queue_free(
struct nvgpu_clk_notification_queue *queue);
#define VF_POINT_INVALID_PSTATE ~0U
#define VF_POINT_SET_PSTATE_SUPPORTED(a, b) ((a)->pstates |= (1UL << (b)))
@@ -62,6 +82,26 @@ static u8 nvgpu_clk_arb_find_vf_point(struct nvgpu_clk_arb *arb,
__fls((a)->pstates & (b)->pstates) :\
VF_POINT_INVALID_PSTATE)
/* Local Alarms */
#define EVENT(alarm) (0x1UL << NVGPU_GPU_EVENT_##alarm)
#define LOCAL_ALARM_MASK (EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE) | \
EVENT(VF_UPDATE))
#define _WRAPGTEQ(a, b) ((a-b) > 0)
struct nvgpu_clk_notification {
u32 notification;
u64 timestamp;
};
struct nvgpu_clk_notification_queue {
u32 size;
atomic_t head;
atomic_t tail;
struct nvgpu_clk_notification *notifications;
};
struct nvgpu_clk_vf_point {
u16 pstates;
union {
@@ -135,6 +175,9 @@ struct nvgpu_clk_arb {
u16 *gpc2clk_f_points;
u32 gpc2clk_f_numpoints;
atomic64_t alarm_mask;
struct nvgpu_clk_notification_queue notification_queue;
#ifdef CONFIG_DEBUG_FS
struct nvgpu_clk_arb_debug debug_pool[2];
struct nvgpu_clk_arb_debug *debug;
@@ -152,9 +195,10 @@ struct nvgpu_clk_dev {
atomic_t poll_mask;
u16 gpc2clk_target_mhz;
u16 mclk_target_mhz;
spinlock_t event_lock;
u32 event_status;
u32 event_mask;
u32 alarms_reported;
atomic_t enabled_mask;
struct nvgpu_clk_notification_queue queue;
u32 arb_queue_head;
struct kref refcount;
};
@@ -172,13 +216,13 @@ struct nvgpu_clk_session {
static const struct file_operations completion_dev_ops = {
.owner = THIS_MODULE,
.release = nvgpu_clk_arb_release_completion_dev,
.poll = nvgpu_clk_arb_poll_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_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,
@@ -186,6 +230,29 @@ static const struct file_operations event_dev_ops = {
.unlocked_ioctl = nvgpu_clk_arb_ioctl_event_dev,
};
static int nvgpu_clk_notification_queue_alloc(
struct nvgpu_clk_notification_queue *queue,
size_t events_number) {
queue->notifications = kcalloc(events_number,
sizeof(struct nvgpu_clk_notification), GFP_KERNEL);
if (!queue->notifications)
return -ENOMEM;
queue->size = events_number;
atomic_set(&queue->head, 0);
atomic_set(&queue->tail, 0);
return 0;
}
static void nvgpu_clk_notification_queue_free(
struct nvgpu_clk_notification_queue *queue) {
kfree(queue->notifications);
queue->size = 0;
atomic_set(&queue->head, 0);
atomic_set(&queue->tail, 0);
}
int nvgpu_clk_arb_init_arbiter(struct gk20a *g)
{
struct nvgpu_clk_arb *arb;
@@ -247,7 +314,7 @@ int nvgpu_clk_arb_init_arbiter(struct gk20a *g)
err = g->ops.clk_arb.get_arbiter_clk_default(g,
NVGPU_GPU_CLK_DOMAIN_MCLK, &default_mhz);
if (err) {
if (err < 0) {
err = -EINVAL;
goto init_fail;
}
@@ -256,7 +323,7 @@ int nvgpu_clk_arb_init_arbiter(struct gk20a *g)
err = g->ops.clk_arb.get_arbiter_clk_default(g,
NVGPU_GPU_CLK_DOMAIN_GPC2CLK, &default_mhz);
if (err) {
if (err < 0) {
err = -EINVAL;
goto init_fail;
}
@@ -267,6 +334,12 @@ int nvgpu_clk_arb_init_arbiter(struct gk20a *g)
atomic_set(&arb->req_nr, 0);
atomic64_set(&arb->alarm_mask, 0);
err = nvgpu_clk_notification_queue_alloc(&arb->notification_queue,
DEFAULT_EVENT_NUMBER);
if (err < 0)
goto init_fail;
INIT_LIST_HEAD_RCU(&arb->users);
INIT_LIST_HEAD_RCU(&arb->sessions);
init_llist_head(&arb->requests);
@@ -324,6 +397,61 @@ init_fail:
return err;
}
void nvgpu_clk_arb_schedule_alarm(struct gk20a *g, u32 alarm)
{
struct nvgpu_clk_arb *arb = g->clk_arb;
nvgpu_clk_arb_set_global_alarm(g, alarm);
queue_work(arb->update_work_queue, &arb->update_fn_work);
}
static void nvgpu_clk_arb_clear_global_alarm(struct gk20a *g, u32 alarm)
{
struct nvgpu_clk_arb *arb = g->clk_arb;
u64 current_mask;
u32 refcnt;
u32 alarm_mask;
u64 new_mask;
do {
current_mask = atomic64_read(&arb->alarm_mask);
/* atomic operations are strong so they do not need masks */
refcnt = ((u32) (current_mask >> 32)) + 1;
alarm_mask = (u32) (current_mask & ~alarm);
new_mask = ((u64) refcnt << 32) | alarm_mask;
} while (unlikely(current_mask !=
(u64)atomic64_cmpxchg(&arb->alarm_mask,
current_mask, new_mask)));
}
static void nvgpu_clk_arb_set_global_alarm(struct gk20a *g, u32 alarm)
{
struct nvgpu_clk_arb *arb = g->clk_arb;
u64 current_mask;
u32 refcnt;
u32 alarm_mask;
u64 new_mask;
do {
current_mask = atomic64_read(&arb->alarm_mask);
/* atomic operations are strong so they do not need masks */
refcnt = ((u32) (current_mask >> 32)) + 1;
alarm_mask = (u32) (current_mask & ~0) | alarm;
new_mask = ((u64) refcnt << 32) | alarm_mask;
} while (unlikely(current_mask !=
(u64)atomic64_cmpxchg(&arb->alarm_mask,
current_mask, new_mask)));
nvgpu_clk_arb_queue_notification(g, &arb->notification_queue, alarm);
}
void nvgpu_clk_arb_cleanup_arbiter(struct gk20a *g)
{
kfree(g->clk_arb);
@@ -339,6 +467,7 @@ static int nvgpu_clk_arb_install_fd(struct gk20a *g,
int fd;
int err;
struct nvgpu_clk_dev *dev;
int status;
gk20a_dbg_fn("");
@@ -346,6 +475,12 @@ static int nvgpu_clk_arb_install_fd(struct gk20a *g,
if (!dev)
return -ENOMEM;
status = nvgpu_clk_notification_queue_alloc(&dev->queue,
DEFAULT_EVENT_NUMBER);
if (status < 0)
return status;
fd = get_unused_fd_flags(O_RDWR);
if (fd < 0) {
err = fd;
@@ -364,9 +499,7 @@ static int nvgpu_clk_arb_install_fd(struct gk20a *g,
init_waitqueue_head(&dev->readout_wq);
spin_lock_init(&dev->event_lock);
dev->event_status = 0;
dev->event_mask = ~0;
atomic_set(&dev->poll_mask, 0);
dev->session = session;
kref_init(&dev->refcount);
@@ -465,7 +598,7 @@ void nvgpu_clk_arb_release_session(struct gk20a *g,
}
int nvgpu_clk_arb_install_event_fd(struct gk20a *g,
struct nvgpu_clk_session *session, int *event_fd)
struct nvgpu_clk_session *session, int *event_fd, u32 alarm_mask)
{
struct nvgpu_clk_arb *arb = g->clk_arb;
struct nvgpu_clk_dev *dev;
@@ -477,6 +610,17 @@ int nvgpu_clk_arb_install_event_fd(struct gk20a *g,
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)
atomic_set(&dev->enabled_mask, alarm_mask);
else
atomic_set(&dev->enabled_mask, EVENT(VF_UPDATE));
dev->arb_queue_head = atomic_read(&arb->notification_queue.head);
spin_lock(&arb->users_lock);
list_add_tail_rcu(&dev->link, &arb->users);
spin_unlock(&arb->users_lock);
@@ -813,9 +957,14 @@ static int nvgpu_clk_arb_update_vf_table(struct nvgpu_clk_arb *arb)
smp_wmb();
xchg(&arb->current_vf_table, table);
queue_work(arb->update_work_queue, &arb->update_fn_work);
exit_vf_table:
if (status < 0)
nvgpu_clk_arb_set_global_alarm(g,
EVENT(ALARM_VF_TABLE_UPDATE_FAILED));
queue_work(arb->update_work_queue, &arb->update_fn_work);
return status;
}
@@ -838,6 +987,11 @@ static void nvgpu_clk_arb_run_vf_table_cb(struct work_struct *work)
if (err) {
gk20a_err(dev_from_gk20a(g),
"failed to cache VF table");
nvgpu_clk_arb_set_global_alarm(g,
EVENT(ALARM_VF_TABLE_UPDATE_FAILED));
queue_work(arb->update_work_queue, &arb->update_fn_work);
return;
}
nvgpu_clk_arb_update_vf_table(arb);
@@ -859,10 +1013,13 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
bool mclk_set, gpc2clk_set;
u32 nuvmin, nuvmin_sram;
u32 alarms_notified = 0;
u32 current_alarm;
int status = 0;
/* Temporary variables for checking target frequency */
u16 gpc2clk_target, sys2clk_target, xbar2clk_target, mclk_target;
u16 gpc2clk_session_target, mclk_session_target;
#ifdef CONFIG_DEBUG_FS
u64 t0, t1;
@@ -872,6 +1029,10 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
gk20a_dbg_fn("");
/* bail out if gpu is down */
if (atomic_read(&arb->alarm_mask) & EVENT(ALARM_GPU_LOST))
goto exit_arb;
#ifdef CONFIG_DEBUG_FS
g->ops.read_ptimer(g, &t0);
#endif
@@ -937,6 +1098,10 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
sys2clk_target = 0;
xbar2clk_target = 0;
gpc2clk_session_target = gpc2clk_target;
mclk_session_target = mclk_target;
/* Query the table for the closest vf point to program */
pstate = nvgpu_clk_arb_find_vf_point(arb, &gpc2clk_target,
&sys2clk_target, &xbar2clk_target, &mclk_target, &voltuv,
@@ -949,6 +1114,11 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
goto exit_arb;
}
if ((gpc2clk_target < gpc2clk_session_target) ||
(mclk_target < mclk_session_target))
nvgpu_clk_arb_set_global_alarm(g,
EVENT(ALARM_TARGET_VF_NOT_POSSIBLE));
if ((arb->actual->gpc2clk == gpc2clk_target) &&
(arb->actual->mclk == mclk_target) &&
(arb->voltuv_actual == voltuv)) {
@@ -1044,6 +1214,9 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
/* Unlock pstate change for PG */
mutex_unlock(&arb->pstate_lock);
/* VF Update complete */
nvgpu_clk_arb_set_global_alarm(g, EVENT(VF_UPDATE));
wake_up_interruptible(&arb->request_wq);
#ifdef CONFIG_DEBUG_FS
@@ -1081,10 +1254,14 @@ static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
#endif
exit_arb:
if (status < 0)
if (status < 0) {
gk20a_err(dev_from_gk20a(g),
"Error in arbiter update");
nvgpu_clk_arb_set_global_alarm(g,
EVENT(ALARM_CLOCK_ARBITER_FAILED));
}
current_alarm = (u32) atomic64_read(&arb->alarm_mask);
/* notify completion for all requests */
head = llist_del_all(&arb->requests);
llist_for_each_entry_safe(dev, tmp, head, node) {
@@ -1093,118 +1270,133 @@ exit_arb:
kref_put(&dev->refcount, nvgpu_clk_arb_free_fd);
}
atomic_set(&arb->notification_queue.head,
atomic_read(&arb->notification_queue.tail));
/* notify event for all users */
rcu_read_lock();
list_for_each_entry_rcu(dev, &arb->users, link) {
spin_lock(&dev->event_lock);
dev->event_status |= (1UL << NVGPU_GPU_EVENT_VF_UPDATE);
spin_unlock(&dev->event_lock);
wake_up_interruptible(&dev->readout_wq);
alarms_notified |=
nvgpu_clk_arb_notify(dev, arb->actual, current_alarm);
}
rcu_read_unlock();
/* clear alarms */
nvgpu_clk_arb_clear_global_alarm(g, alarms_notified &
~EVENT(ALARM_GPU_LOST));
}
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;
static void nvgpu_clk_arb_queue_notification(struct gk20a *g,
struct nvgpu_clk_notification_queue *queue,
u32 alarm_mask) {
gk20a_dbg_fn("");
u32 queue_index;
u64 timestamp;
fd = fdget(request_fd);
queue_index = (atomic_inc_return(&queue->tail)) % queue->size;
/* get current timestamp */
timestamp = (u64) get_cycles();
if (!fd.file)
return -EINVAL;
queue->notifications[queue_index].timestamp = timestamp;
queue->notifications[queue_index].notification = alarm_mask;
dev = (struct nvgpu_clk_dev *) fd.file->private_data;
if (!dev || dev->session != session) {
err = -EINVAL;
goto fdput_fd;
}
kref_get(&dev->refcount);
llist_add(&dev->node, &session->targets);
queue_work(arb->update_work_queue, &arb->update_fn_work);
fdput_fd:
fdput(fd);
return err;
}
static inline int __pending_event(struct nvgpu_clk_dev *dev,
struct nvgpu_gpu_event_info *info)
{
struct gk20a *g = dev->session->g;
u32 status;
static u32 nvgpu_clk_arb_notify(struct nvgpu_clk_dev *dev,
struct nvgpu_clk_arb_target *target,
u32 alarm) {
spin_lock(&dev->event_lock);
status = dev->event_status & dev->event_mask;
if (status && info)
{
/* TODO: retrieve oldest event_id based on timestamp */
info->event_id = ffs(status) - 1;
g->ops.read_ptimer(g, &info->timestamp);
struct nvgpu_clk_session *session = dev->session;
struct nvgpu_clk_arb *arb = session->g->clk_arb;
struct nvgpu_clk_notification *notification;
dev->event_status &= ~(1UL << info->event_id);
}
spin_unlock(&dev->event_lock);
return status;
}
u32 queue_alarm_mask = 0;
u32 enabled_mask = 0;
u32 new_alarms_reported = 0;
u32 poll_mask = 0;
u32 tail, head;
u32 queue_index;
size_t size;
int index;
static unsigned int nvgpu_clk_arb_poll_completion_dev(struct file *filp,
poll_table *wait)
{
struct nvgpu_clk_dev *dev = filp->private_data;
enabled_mask = atomic_read(&dev->enabled_mask);
size = arb->notification_queue.size;
gk20a_dbg_fn("");
/* queue global arbiter notifications in buffer */
do {
tail = atomic_read(&arb->notification_queue.tail);
/* copy items to the queue */
queue_index = atomic_read(&dev->queue.tail);
head = dev->arb_queue_head;
head = (tail - head) < arb->notification_queue.size ?
head : tail - arb->notification_queue.size;
poll_wait(filp, &dev->readout_wq, wait);
return atomic_xchg(&dev->poll_mask, 0);
}
for (index = head; _WRAPGTEQ(tail, index); index++) {
u32 alarm_detected;
static unsigned int nvgpu_clk_arb_poll_event_dev(struct file *filp,
poll_table *wait)
{
struct nvgpu_clk_dev *dev = filp->private_data;
notification = &arb->notification_queue.
notifications[(index+1) % size];
alarm_detected =
ACCESS_ONCE(notification->notification);
gk20a_dbg_fn("");
if (!(enabled_mask & alarm_detected))
continue;
poll_wait(filp, &dev->readout_wq, wait);
return __pending_event(dev, NULL);
}
queue_index++;
dev->queue.notifications[
queue_index % dev->queue.size].timestamp =
ACCESS_ONCE(notification->timestamp);
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;
int err;
dev->queue.notifications[
queue_index % dev->queue.size].notification =
alarm_detected;
gk20a_dbg(gpu_dbg_fn, "filp=%p buf=%p size=%zu", filp, buf, size);
queue_alarm_mask |= alarm_detected;
}
} while (unlikely(atomic_read(&arb->notification_queue.tail) !=
(int)tail));
if (size < sizeof(info))
return 0;
atomic_set(&dev->queue.tail, queue_index);
/* update the last notification we processed from global queue */
memset(&info, 0, sizeof(info));
while (!__pending_event(dev, &info)) {
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
err = wait_event_interruptible(dev->readout_wq,
__pending_event(dev, &info));
if (err)
return err;
dev->arb_queue_head = tail;
/* Check if current session targets are met */
if (enabled_mask & EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE)) {
if ((target->gpc2clk < session->target->gpc2clk)
|| (target->mclk < session->target->mclk)) {
poll_mask |= (POLLIN | POLLPRI);
nvgpu_clk_arb_queue_notification(arb->g, &dev->queue,
EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE));
}
}
if (copy_to_user(buf, &info, sizeof(info)))
return -EFAULT;
/* Check if there is a new VF update */
if (queue_alarm_mask & EVENT(VF_UPDATE))
poll_mask |= (POLLIN | POLLRDNORM);
*off += sizeof(info);
/* Notify sticky alarms that were not reported on previous run*/
new_alarms_reported = (queue_alarm_mask |
(alarm & ~dev->alarms_reported & queue_alarm_mask));
return sizeof(info);
if (new_alarms_reported & ~LOCAL_ALARM_MASK) {
/* check that we are not re-reporting */
if (new_alarms_reported & EVENT(ALARM_GPU_LOST))
poll_mask |= POLLHUP;
poll_mask |= (POLLIN | POLLPRI);
/* On next run do not report global alarms that were already
* reported, but report SHUTDOWN always */
dev->alarms_reported = new_alarms_reported & ~LOCAL_ALARM_MASK &
~EVENT(ALARM_GPU_LOST);
}
if (poll_mask) {
atomic_set(&dev->poll_mask, poll_mask);
wake_up_interruptible_all(&dev->readout_wq);
}
return new_alarms_reported;
}
static int nvgpu_clk_arb_set_event_filter(struct nvgpu_clk_dev *dev,
@@ -1224,10 +1416,8 @@ static int nvgpu_clk_arb_set_event_filter(struct nvgpu_clk_dev *dev,
args->size * sizeof(u32)))
return -EFAULT;
spin_lock(&dev->event_lock);
/* update event mask */
dev->event_mask = mask;
spin_unlock(&dev->event_lock);
/* update alarm mask */
atomic_set(&dev->enabled_mask, mask);
return 0;
}
@@ -1271,6 +1461,106 @@ long nvgpu_clk_arb_ioctl_event_dev(struct file *filp, unsigned int cmd,
return err;
}
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;
gk20a_dbg_fn("");
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;
}
kref_get(&dev->refcount);
llist_add(&dev->node, &session->targets);
queue_work(arb->update_work_queue, &arb->update_fn_work);
fdput_fd:
fdput(fd);
return err;
}
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 = atomic_read(&dev->queue.tail);
head = 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 |= p_notif->notification;
info->event_id = ffs(events) - 1;
info->timestamp = p_notif->timestamp;
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;
gk20a_dbg_fn("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 = wait_event_interruptible(dev->readout_wq,
__pending_event(dev, &info));
if (err)
return err;
if (info.timestamp)
break;
}
if (copy_to_user(buf + *off, &info, sizeof(info)))
return -EFAULT;
return sizeof(info);
}
static unsigned int nvgpu_clk_arb_poll_dev(struct file *filp, poll_table *wait)
{
struct nvgpu_clk_dev *dev = filp->private_data;
gk20a_dbg_fn("");
poll_wait(filp, &dev->readout_wq, wait);
return atomic_xchg(&dev->poll_mask, 0);
}
static int nvgpu_clk_arb_release_completion_dev(struct inode *inode,
struct file *filp)
{
@@ -1305,6 +1595,8 @@ static int nvgpu_clk_arb_release_event_dev(struct inode *inode,
synchronize_rcu();
kref_put(&session->refcount, nvgpu_clk_arb_free_session);
nvgpu_clk_notification_queue_free(&dev->queue);
kfree(dev);
return 0;
@@ -1320,10 +1612,14 @@ int nvgpu_clk_arb_set_session_target_mhz(struct nvgpu_clk_session *session,
gk20a_dbg_fn("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;
@@ -1599,7 +1895,7 @@ find_exit:
/* noise unaware vmin */
*nuvmin = mclk_voltuv;
*nuvmin_sram = mclk_voltuv_sram;
*gpc2clk = gpc2clk_target;
*gpc2clk = gpc2clk_target < *gpc2clk ? gpc2clk_target : *gpc2clk;
*mclk = mclk_target;
return pstate;
}

View File

@@ -56,7 +56,7 @@ int nvgpu_clk_arb_get_session_target_mhz(struct nvgpu_clk_session *session,
u32 api_domain, u16 *target_mhz);
int nvgpu_clk_arb_install_event_fd(struct gk20a *g,
struct nvgpu_clk_session *session, int *event_fd);
struct nvgpu_clk_session *session, int *event_fd, u32 alarm_mask);
int nvgpu_clk_arb_install_request_fd(struct gk20a *g,
struct nvgpu_clk_session *session, int *event_fd);
@@ -67,5 +67,6 @@ int nvgpu_clk_arb_get_current_pstate(struct gk20a *g);
void nvgpu_clk_arb_pstate_change_lock(struct gk20a *g, bool lock);
void nvgpu_clk_arb_schedule_alarm(struct gk20a *g, u32 alarm);
#endif /* _CLK_ARB_H_ */