aboutsummaryrefslogtreecommitdiffstats
path: root/runlist_procfs.c
blob: 183eab69497bd0a816a3eceff7eea9f8f27753d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#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,
};