diff options
author | Jack Steiner <steiner@sgi.com> | 2009-06-17 19:28:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-18 16:03:59 -0400 |
commit | 9cc9b056ea51608788609d7e26c7db55ef81bb2e (patch) | |
tree | e6381d7575babaee9658c5a2bc007a8dd2ec49ae /drivers/misc | |
parent | cd1334f03f7b799bc6893b511daf2080e8f73863 (diff) |
gru: dump chiplet state
Add support for dumpping the state of an entire GRU chiplet.
Signed-off-by: Jack Steiner <steiner@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/sgi-gru/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grufile.c | 3 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grukdump.c | 218 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grulib.h | 33 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grutables.h | 12 |
5 files changed, 267 insertions, 1 deletions
diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile index bcd8136d2f98..7c4c306dfa8a 100644 --- a/drivers/misc/sgi-gru/Makefile +++ b/drivers/misc/sgi-gru/Makefile | |||
@@ -3,5 +3,5 @@ ifdef CONFIG_SGI_GRU_DEBUG | |||
3 | endif | 3 | endif |
4 | 4 | ||
5 | obj-$(CONFIG_SGI_GRU) := gru.o | 5 | obj-$(CONFIG_SGI_GRU) := gru.o |
6 | gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o | 6 | gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o |
7 | 7 | ||
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index 3ce2920e2bf3..9e6da46eeb04 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c | |||
@@ -255,6 +255,9 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, | |||
255 | case GRU_GET_CONFIG_INFO: | 255 | case GRU_GET_CONFIG_INFO: |
256 | err = gru_get_config_info(arg); | 256 | err = gru_get_config_info(arg); |
257 | break; | 257 | break; |
258 | case GRU_DUMP_CHIPLET_STATE: | ||
259 | err = gru_dump_chiplet_request(arg); | ||
260 | break; | ||
258 | } | 261 | } |
259 | return err; | 262 | return err; |
260 | } | 263 | } |
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c new file mode 100644 index 000000000000..27e00931a7b8 --- /dev/null +++ b/drivers/misc/sgi-gru/grukdump.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* | ||
2 | * SN Platform GRU Driver | ||
3 | * | ||
4 | * Dump GRU State | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | * | ||
10 | * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/uaccess.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/bitops.h> | ||
19 | #include <asm/uv/uv_hub.h> | ||
20 | #include "gru.h" | ||
21 | #include "grutables.h" | ||
22 | #include "gruhandles.h" | ||
23 | #include "grulib.h" | ||
24 | |||
25 | #define CCH_LOCK_ATTEMPTS 10 | ||
26 | |||
27 | static int gru_user_copy_handle(void __user **dp, void *s) | ||
28 | { | ||
29 | if (copy_to_user(dp, s, GRU_HANDLE_BYTES)) | ||
30 | return -1; | ||
31 | *dp += GRU_HANDLE_BYTES; | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static int gru_dump_context_data(void *grubase, | ||
36 | struct gru_context_configuration_handle *cch, | ||
37 | void __user *ubuf, int ctxnum, int dsrcnt) | ||
38 | { | ||
39 | void *cb, *cbe, *tfh, *gseg; | ||
40 | int i, scr; | ||
41 | |||
42 | gseg = grubase + ctxnum * GRU_GSEG_STRIDE; | ||
43 | cb = gseg + GRU_CB_BASE; | ||
44 | cbe = grubase + GRU_CBE_BASE; | ||
45 | tfh = grubase + GRU_TFH_BASE; | ||
46 | |||
47 | for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { | ||
48 | if (gru_user_copy_handle(&ubuf, cb)) | ||
49 | goto fail; | ||
50 | if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) | ||
51 | goto fail; | ||
52 | if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE)) | ||
53 | goto fail; | ||
54 | cb += GRU_HANDLE_STRIDE; | ||
55 | } | ||
56 | if (dsrcnt) | ||
57 | memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE); | ||
58 | return 0; | ||
59 | |||
60 | fail: | ||
61 | return -EFAULT; | ||
62 | } | ||
63 | |||
64 | static int gru_dump_tfm(struct gru_state *gru, | ||
65 | void __user *ubuf, void __user *ubufend) | ||
66 | { | ||
67 | struct gru_tlb_fault_map *tfm; | ||
68 | int i, ret, bytes; | ||
69 | |||
70 | bytes = GRU_NUM_TFM * GRU_CACHE_LINE_BYTES; | ||
71 | if (bytes > ubufend - ubuf) | ||
72 | ret = -EFBIG; | ||
73 | |||
74 | for (i = 0; i < GRU_NUM_TFM; i++) { | ||
75 | tfm = get_tfm(gru->gs_gru_base_vaddr, i); | ||
76 | if (gru_user_copy_handle(&ubuf, tfm)) | ||
77 | goto fail; | ||
78 | } | ||
79 | return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES; | ||
80 | |||
81 | fail: | ||
82 | return -EFAULT; | ||
83 | } | ||
84 | |||
85 | static int gru_dump_tgh(struct gru_state *gru, | ||
86 | void __user *ubuf, void __user *ubufend) | ||
87 | { | ||
88 | struct gru_tlb_global_handle *tgh; | ||
89 | int i, ret, bytes; | ||
90 | |||
91 | bytes = GRU_NUM_TGH * GRU_CACHE_LINE_BYTES; | ||
92 | if (bytes > ubufend - ubuf) | ||
93 | ret = -EFBIG; | ||
94 | |||
95 | for (i = 0; i < GRU_NUM_TGH; i++) { | ||
96 | tgh = get_tgh(gru->gs_gru_base_vaddr, i); | ||
97 | if (gru_user_copy_handle(&ubuf, tgh)) | ||
98 | goto fail; | ||
99 | } | ||
100 | return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES; | ||
101 | |||
102 | fail: | ||
103 | return -EFAULT; | ||
104 | } | ||
105 | |||
106 | static int gru_dump_context(struct gru_state *gru, int ctxnum, | ||
107 | void __user *ubuf, void __user *ubufend, char data_opt, | ||
108 | char lock_cch) | ||
109 | { | ||
110 | struct gru_dump_context_header hdr; | ||
111 | struct gru_dump_context_header __user *uhdr = ubuf; | ||
112 | struct gru_context_configuration_handle *cch; | ||
113 | struct gru_thread_state *gts; | ||
114 | int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0; | ||
115 | void *grubase; | ||
116 | |||
117 | memset(&hdr, 0, sizeof(hdr)); | ||
118 | grubase = gru->gs_gru_base_vaddr; | ||
119 | cch = get_cch(grubase, ctxnum); | ||
120 | for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) { | ||
121 | cch_locked = trylock_cch_handle(cch); | ||
122 | if (cch_locked) | ||
123 | break; | ||
124 | msleep(1); | ||
125 | } | ||
126 | |||
127 | ubuf += sizeof(hdr); | ||
128 | if (gru_user_copy_handle(&ubuf, cch)) | ||
129 | goto fail; | ||
130 | bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES; | ||
131 | |||
132 | if (cch_locked || !lock_cch) { | ||
133 | gts = gru->gs_gts[ctxnum]; | ||
134 | if (gts) { | ||
135 | hdr.pid = gts->ts_tgid_owner; | ||
136 | hdr.vaddr = gts->ts_vma->vm_start; | ||
137 | } | ||
138 | if (cch->state != CCHSTATE_INACTIVE) { | ||
139 | cbrcnt = hweight64(cch->cbr_allocation_map) * | ||
140 | GRU_CBR_AU_SIZE; | ||
141 | dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) * | ||
142 | GRU_DSR_AU_CL : 0; | ||
143 | } | ||
144 | bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES; | ||
145 | if (bytes > ubufend - ubuf) | ||
146 | ret = -EFBIG; | ||
147 | else | ||
148 | ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, | ||
149 | dsrcnt); | ||
150 | |||
151 | } | ||
152 | if (cch_locked) | ||
153 | unlock_cch_handle(cch); | ||
154 | if (ret) | ||
155 | return ret; | ||
156 | |||
157 | hdr.magic = GRU_DUMP_MAGIC; | ||
158 | hdr.ctxnum = ctxnum; | ||
159 | hdr.cbrcnt = cbrcnt; | ||
160 | hdr.dsrcnt = dsrcnt; | ||
161 | hdr.cch_locked = cch_locked; | ||
162 | if (!ret && copy_to_user((void __user *)uhdr, &hdr, sizeof(hdr))) | ||
163 | ret = -EFAULT; | ||
164 | |||
165 | return ret ? ret : bytes; | ||
166 | |||
167 | fail: | ||
168 | unlock_cch_handle(cch); | ||
169 | return -EFAULT; | ||
170 | } | ||
171 | |||
172 | int gru_dump_chiplet_request(unsigned long arg) | ||
173 | { | ||
174 | struct gru_state *gru; | ||
175 | struct gru_dump_chiplet_state_req req; | ||
176 | void __user *ubuf; | ||
177 | void __user *ubufend; | ||
178 | int ctxnum, ret, cnt = 0; | ||
179 | |||
180 | if (copy_from_user(&req, (void __user *)arg, sizeof(req))) | ||
181 | return -EFAULT; | ||
182 | |||
183 | /* Currently, only dump by gid is implemented */ | ||
184 | if (req.gid >= gru_max_gids || req.gid < 0) | ||
185 | return -EINVAL; | ||
186 | |||
187 | gru = GID_TO_GRU(req.gid); | ||
188 | ubuf = req.buf; | ||
189 | ubufend = req.buf + req.buflen; | ||
190 | |||
191 | ret = gru_dump_tfm(gru, ubuf, ubufend); | ||
192 | if (ret < 0) | ||
193 | goto fail; | ||
194 | ubuf += ret; | ||
195 | |||
196 | ret = gru_dump_tgh(gru, ubuf, ubufend); | ||
197 | if (ret < 0) | ||
198 | goto fail; | ||
199 | ubuf += ret; | ||
200 | |||
201 | for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { | ||
202 | if (req.ctxnum == ctxnum || req.ctxnum < 0) { | ||
203 | ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, | ||
204 | req.data_opt, req.lock_cch); | ||
205 | if (ret < 0) | ||
206 | goto fail; | ||
207 | ubuf += ret; | ||
208 | cnt++; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | if (copy_to_user((void __user *)arg, &req, sizeof(req))) | ||
213 | return -EFAULT; | ||
214 | return cnt; | ||
215 | |||
216 | fail: | ||
217 | return ret; | ||
218 | } | ||
diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h index e56e196a6998..6ab872665e7f 100644 --- a/drivers/misc/sgi-gru/grulib.h +++ b/drivers/misc/sgi-gru/grulib.h | |||
@@ -47,6 +47,9 @@ | |||
47 | /* For fetching GRU chiplet status */ | 47 | /* For fetching GRU chiplet status */ |
48 | #define GRU_GET_CHIPLET_STATUS _IOWR(GRU_IOCTL_NUM, 10, void *) | 48 | #define GRU_GET_CHIPLET_STATUS _IOWR(GRU_IOCTL_NUM, 10, void *) |
49 | 49 | ||
50 | /* For dumpping GRU chiplet state */ | ||
51 | #define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *) | ||
52 | |||
50 | /* For user TLB flushing (primarily for tests) */ | 53 | /* For user TLB flushing (primarily for tests) */ |
51 | #define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *) | 54 | #define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *) |
52 | 55 | ||
@@ -84,6 +87,36 @@ struct gru_flush_tlb_req { | |||
84 | }; | 87 | }; |
85 | 88 | ||
86 | /* | 89 | /* |
90 | * Structure used to pass TLB flush parameters to the driver | ||
91 | */ | ||
92 | enum {dcs_pid, dcs_gid}; | ||
93 | struct gru_dump_chiplet_state_req { | ||
94 | unsigned int op; | ||
95 | int gid; | ||
96 | int ctxnum; | ||
97 | char data_opt; | ||
98 | char lock_cch; | ||
99 | pid_t pid; | ||
100 | void *buf; | ||
101 | size_t buflen; | ||
102 | /* ---- output --- */ | ||
103 | unsigned int num_contexts; | ||
104 | }; | ||
105 | |||
106 | #define GRU_DUMP_MAGIC 0x3474ab6c | ||
107 | struct gru_dump_context_header { | ||
108 | unsigned int magic; | ||
109 | unsigned char gid; | ||
110 | unsigned char ctxnum; | ||
111 | unsigned char cbrcnt; | ||
112 | unsigned char dsrcnt; | ||
113 | pid_t pid; | ||
114 | unsigned long vaddr; | ||
115 | int cch_locked; | ||
116 | unsigned long data[0]; | ||
117 | }; | ||
118 | |||
119 | /* | ||
87 | * GRU configuration info (temp - for testing) | 120 | * GRU configuration info (temp - for testing) |
88 | */ | 121 | */ |
89 | struct gru_config_info { | 122 | struct gru_config_info { |
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index ebf6183c1635..d768f54dc259 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h | |||
@@ -554,6 +554,12 @@ struct gru_blade_state { | |||
554 | 554 | ||
555 | /* Lock hierarchy checking enabled only in emulator */ | 555 | /* Lock hierarchy checking enabled only in emulator */ |
556 | 556 | ||
557 | /* 0 = lock failed, 1 = locked */ | ||
558 | static inline int __trylock_handle(void *h) | ||
559 | { | ||
560 | return !test_and_set_bit(1, h); | ||
561 | } | ||
562 | |||
557 | static inline void __lock_handle(void *h) | 563 | static inline void __lock_handle(void *h) |
558 | { | 564 | { |
559 | while (test_and_set_bit(1, h)) | 565 | while (test_and_set_bit(1, h)) |
@@ -565,6 +571,11 @@ static inline void __unlock_handle(void *h) | |||
565 | clear_bit(1, h); | 571 | clear_bit(1, h); |
566 | } | 572 | } |
567 | 573 | ||
574 | static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch) | ||
575 | { | ||
576 | return __trylock_handle(cch); | ||
577 | } | ||
578 | |||
568 | static inline void lock_cch_handle(struct gru_context_configuration_handle *cch) | 579 | static inline void lock_cch_handle(struct gru_context_configuration_handle *cch) |
569 | { | 580 | { |
570 | __lock_handle(cch); | 581 | __lock_handle(cch); |
@@ -606,6 +617,7 @@ extern void gts_drop(struct gru_thread_state *gts); | |||
606 | extern void gru_tgh_flush_init(struct gru_state *gru); | 617 | extern void gru_tgh_flush_init(struct gru_state *gru); |
607 | extern int gru_kservices_init(struct gru_state *gru); | 618 | extern int gru_kservices_init(struct gru_state *gru); |
608 | extern void gru_kservices_exit(struct gru_state *gru); | 619 | extern void gru_kservices_exit(struct gru_state *gru); |
620 | extern int gru_dump_chiplet_request(unsigned long arg); | ||
609 | extern irqreturn_t gru_intr(int irq, void *dev_id); | 621 | extern irqreturn_t gru_intr(int irq, void *dev_id); |
610 | extern int gru_handle_user_call_os(unsigned long address); | 622 | extern int gru_handle_user_call_os(unsigned long address); |
611 | extern int gru_user_flush_tlb(unsigned long arg); | 623 | extern int gru_user_flush_tlb(unsigned long arg); |