aboutsummaryrefslogblamecommitdiffstats
path: root/runlist_procfs.c
blob: 411f844d134064a90bbb7ae9f1a1487da087f5a4 (plain) (tree)
1
2
3
4
5
6
                                                            
                                                              



                                     

































                                                                                             


                                                                      






                                                      











                                                                                          






                                                                         
                         





                                                                               
                              
         




                                                                  








                                                                              


                                           
                                                
                                                                                                  






                                                                     
                             

                                 
      
                                                          
                                                                                                                  

                                    






                                                                                                   











                                                                                       
      




















                                                                   






































































































































                                                                                                   
#include <linux/seq_file.h> // For seq_* functions and types
#include <linux/version.h>  // Macros to detect kernel version

#include "nvdebug.h"

#define RUNLIST_PROCFS_NAME "runlist"
#define DETAILED_CHANNEL_INFO

static int runlist_detail_seq_show_chan(struct seq_file *s, struct gk20a *g, uint32_t chid) {
	channel_ctrl_t chan;
	char *loc_txt;
	u64 instance_ptr;
	chan.raw = nvdebug_readq(g, NV_PCCSR_CHANNEL_INST(chid));
	loc_txt = target_to_text(chan.inst_target);
	if (!loc_txt)
		return -EIO;
	instance_ptr = chan.inst_ptr;
	instance_ptr <<= 12;
	seq_printf(s, "  +- Channel Info %-4d -+\n", chid);
	seq_printf(s, "  | Enabled:           %d|\n", chan.enable);
	seq_printf(s, "  | Next:              %d|\n", chan.next);
	seq_printf(s, "  | Force CTX Reload:  %d|\n", chan.force_ctx_reload);
	seq_printf(s, "  | Enable set:        %d|\n", chan.enable_set);
	seq_printf(s, "  | Enable clear:      %d|\n", chan.enable_clear);
	seq_printf(s, "  | PBDMA Faulted:     %d|\n", chan.pbdma_faulted);
	seq_printf(s, "  | ENG Faulted:       %d|\n", chan.eng_faulted);
	seq_printf(s, "  | Status:           %2d|\n", chan.status);
	seq_printf(s, "  | Busy:              %d|\n", chan.busy);
	seq_printf(s, "  | Instance PTR:       |\n");
	seq_printf(s, "  | %#018llx  |\n", instance_ptr);
	seq_printf(s, "  | %-20s|\n", loc_txt);
	seq_printf(s, "  | Instance bound:    %d|\n", chan.inst_bind);
	seq_printf(s, "  +---------------------+\n");
	return 0;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
// Bug workaround. See comment in runlist_file_seq_start()
static loff_t pos_fixup;
#endif

static void *runlist_file_seq_start(struct seq_file *s, loff_t *pos) {
	static struct runlist_iter rl_iter;
	// *pos == 0 for first call after read of file
	if (*pos == 0) {
		int err = get_runlist_iter(&rl_iter);
		if (err)
			return NULL;
		return &rl_iter;
	}
	// If we're resuming an earlier print
	if (*pos < rl_iter.rl_info.len) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
		// There's a nasty bug prior to 4.19-rc1 that if the buffer overflows, the
		// last update to `pos` is not saved. Work around that here by reloading a
		// saved copy of `pos`.
		if (!pos_fixup)
			return NULL;
		*pos = pos_fixup;
#endif
		return &rl_iter;
	}
	// When called with *pos != 0, we already traversed the runlist
	return NULL;
}

static void* runlist_file_seq_next(struct seq_file *s, void *raw_rl_iter,
				   loff_t *pos) {
	struct runlist_iter* rl_iter = raw_rl_iter;
	void *ret = NULL;
	// Advance by one TSG + channels under last TSG
	*pos += 1 + rl_iter->curr_tsg->tsg_length;
	// Verify we haven't reached the end of the runlist
	// rl_info.len is the num of tsg entries + total num of channel entries
	if (*pos < rl_iter->rl_info.len) {
		rl_iter->curr_tsg = next_tsg(rl_iter->curr_tsg);
		ret = rl_iter;
	}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
	// Bug workaround. See comment in runlist_file_seq_start()
	pos_fixup = ret ? *pos : 0;
#endif
	return ret;
}

static void runlist_file_seq_stop(struct seq_file *s, void *raw_rl_iter) {
	// No cleanup needed
}

static int runlist_file_seq_show(struct seq_file *s, void *raw_rl_iter) {
	struct entry_tsg* tsg = ((struct runlist_iter*)raw_rl_iter)->curr_tsg;
	struct runlist_chan* chan;
	struct gk20a *g = get_live_gk20a();
	if (!g)
		return -EIO;
	if (tsg->entry_type != ENTRY_TYPE_TSG) {
                printk(KERN_WARNING "[nvdebug] Attempted to print non-TSG in tsg print logic!\n");
                return -EIO;
        }
        seq_printf(s, "+---- TSG Entry %-2d----+\n", tsg->tsgid);
        seq_printf(s, "| Scale: %-13d|\n", tsg->timeslice_scale);
        seq_printf(s, "| Timeout: %-11d|\n", tsg->timeslice_timeout);
        seq_printf(s, "+---------------------+\n");
	for_chan_in_tsg(chan, tsg) {
#ifndef DETAILED_CHANNEL_INFO
		char* loc_txt;
		u64 instance_ptr;
#endif
		if (chan->entry_type != ENTRY_TYPE_CHAN) {
			printk(KERN_WARNING "[nvdebug] Attempted to print non-channel in channel print logic!\n");
			return -EIO;
		}
#ifdef DETAILED_CHANNEL_INFO
		runlist_detail_seq_show_chan(s, g, chan->chid);
#else
		loc_txt = target_to_text(chan->inst_target);
		if (!loc_txt) {
			printk(KERN_WARNING "[nvdebug] Invalid apature in channel print logic!\n");
			return -EIO;
		}
		// Reconstruct pointer to channel instance block
		instance_ptr = chan->inst_ptr_hi;
		instance_ptr <<= 32;
		instance_ptr |= chan->inst_ptr_lo << 12;

		seq_printf(s, "  +- Channel Entry %-4d-+\n", chan->chid);
		seq_printf(s, "  | Runqueue Selector: %d|\n", chan->runqueue_selector);
		seq_printf(s, "  | Instance PTR:       |\n");
		seq_printf(s, "  | %#018llx  |\n", instance_ptr);
		seq_printf(s, "  | %-20s|\n", loc_txt);
		seq_printf(s, "  +---------------------+\n");
#endif
	}
	return 0;
}

static const struct seq_operations runlist_file_seq_ops = {
        .start = runlist_file_seq_start,
        .next = runlist_file_seq_next,
        .stop = runlist_file_seq_stop,
        .show = runlist_file_seq_show,
};

static int runlist_file_open(struct inode *inode, struct file *f) {
	return seq_open(f, &runlist_file_seq_ops);
}

const struct file_operations runlist_file_ops = {
	.open = runlist_file_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = seq_release,
};

ssize_t preempt_tsg_file_write(struct file *f, const char __user *buffer,
			       size_t count, loff_t *off) {
	uint32_t target_tsgid;
	// Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec
	int err = kstrtou32_from_user(buffer, count, 0, &target_tsgid);
	if (err)
		return err;

	// TSG IDs are a 12-bit field, so make sure the request is in-range
	if (target_tsgid > MAX_TSGID)
		return -ERANGE;

	// Execute preemption
	err = preempt_tsg(target_tsgid);
	if (err)
		return err;

	return count;
}

const struct file_operations preempt_tsg_file_ops = {
	.write = preempt_tsg_file_write,
};

ssize_t disable_channel_file_write(struct file *f, const char __user *buffer,
				   size_t count, loff_t *off) {
	uint32_t target_channel;
	channel_ctrl_t chan;
	int err;
	struct gk20a *g = get_live_gk20a();
	if (!g)
		return -EIO;
	// Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec
	err = kstrtou32_from_user(buffer, count, 0, &target_channel);
	if (err)
		return err;

	if (target_channel > MAX_CHID)
		return -ERANGE;

	// Disable channel
	chan.raw = nvdebug_readq(g, NV_PCCSR_CHANNEL_INST(target_channel));
	chan.enable_clear = true;
	nvdebug_writeq(g, NV_PCCSR_CHANNEL_INST(target_channel), chan.raw);

	return count;
}

const struct file_operations disable_channel_file_ops = {
	.write = disable_channel_file_write,
};

ssize_t enable_channel_file_write(struct file *f, const char __user *buffer,
				   size_t count, loff_t *off) {
	uint32_t target_channel;
	channel_ctrl_t chan;
	int err;
	struct gk20a *g = get_live_gk20a();
	if (!g)
		return -EIO;
	// Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec
	err = kstrtou32_from_user(buffer, count, 0, &target_channel);
	if (err)
		return err;

	if (target_channel > MAX_CHID)
		return -ERANGE;

	// Disable channel
	chan.raw = nvdebug_readq(g, NV_PCCSR_CHANNEL_INST(target_channel));
	chan.enable_set = true;
	nvdebug_writeq(g, NV_PCCSR_CHANNEL_INST(target_channel), chan.raw);

	return count;
}

const struct file_operations enable_channel_file_ops = {
	.write = enable_channel_file_write,
};

ssize_t switch_to_tsg_file_write(struct file *f, const char __user *buffer,
				   size_t count, loff_t *off) {
	uint32_t target_tsgid;
	struct runlist_chan* chan;
	channel_ctrl_t chan_ctl;
	struct runlist_iter rl_iter;
	int err;
	loff_t pos = 0;
	struct gk20a *g = get_live_gk20a();
	if (!g)
		return -EIO;
	// Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec
	err = kstrtou32_from_user(buffer, count, 0, &target_tsgid);
	if (err)
		return err;

	if (target_tsgid > MAX_TSGID)
		return -ERANGE;

	err = get_runlist_iter(&rl_iter);
	if (err)
		return err;

	// Iterate through all TSGs
	while (pos < rl_iter.rl_info.len) {
		if (rl_iter.curr_tsg->tsgid == target_tsgid) {
			// Enable channels of target TSG
			for_chan_in_tsg(chan, rl_iter.curr_tsg) {
				chan_ctl.raw = nvdebug_readq(g, NV_PCCSR_CHANNEL_INST(chan->chid));
				chan_ctl.enable_set = true;
				nvdebug_writeq(g, NV_PCCSR_CHANNEL_INST(chan->chid), chan_ctl.raw);
			}
		} else {
			// Disable all other channels
			for_chan_in_tsg(chan, rl_iter.curr_tsg) {
				chan_ctl.raw = nvdebug_readq(g, NV_PCCSR_CHANNEL_INST(chan->chid));
				chan_ctl.enable_clear = true;
				nvdebug_writeq(g, NV_PCCSR_CHANNEL_INST(chan->chid), chan_ctl.raw);
			}
		}
		pos += 1 + rl_iter.curr_tsg->tsg_length;
		rl_iter.curr_tsg = next_tsg(rl_iter.curr_tsg);
	}
	// Switch to next TSG with active channels (should be our TSG)
	err = preempt_tsg(target_tsgid);
	if (err)
		return err;

	return count;
}

const struct file_operations switch_to_tsg_file_ops = {
	.write = switch_to_tsg_file_write,
};