1 /* $Id$
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
8 */
9
10 #ident "$Revision: 1.167 $"
11
12 #include <linux/types.h>
13 #include <linux/slab.h>
14 #include <linux/interrupt.h>
15 #include <asm/smp.h>
16 #include <asm/irq.h>
17 #include <asm/hw_irq.h>
18 #include <asm/system.h>
19 #include <asm/sn/sgi.h>
20 #include <asm/uaccess.h>
21 #include <asm/sn/iograph.h>
22 #include <asm/sn/invent.h>
23 #include <asm/sn/hcl.h>
24 #include <asm/sn/labelcl.h>
25 #include <asm/sn/io.h>
26 #include <asm/sn/sn_private.h>
27 #include <asm/sn/klconfig.h>
28 #include <asm/sn/sn_cpuid.h>
29 #include <asm/sn/pci/pciio.h>
30 #include <asm/sn/pci/pcibr.h>
31 #include <asm/sn/xtalk/xtalk.h>
32 #include <asm/sn/pci/pcibr_private.h>
33 #include <asm/sn/intr.h>
34 #include <asm/sn/sn2/shub_mmr_t.h>
35 #include <asm/sal.h>
36 #include <asm/sn/sn_sal.h>
37 #include <asm/sn/sndrv.h>
38
39 #define SHUB_NUM_ECF_REGISTERS 8
40
41 /*
42 * A backport of the 2.5 scheduler is used by many vendors of 2.4-based
43 * distributions.
44 * We can only guess its presence by the lack of the SCHED_YIELD flag.
45 * If the heuristic doesn't work, change this define by hand.
46 */
47 #ifndef SCHED_YIELD
48 #define __HAVE_NEW_SCHEDULER 1
49 #endif
50
51
52 static uint32_t shub_perf_counts[SHUB_NUM_ECF_REGISTERS];
53
54 static shubreg_t shub_perf_counts_regs[SHUB_NUM_ECF_REGISTERS] = {
55 SH_PERFORMANCE_COUNTER0,
56 SH_PERFORMANCE_COUNTER1,
57 SH_PERFORMANCE_COUNTER2,
58 SH_PERFORMANCE_COUNTER3,
59 SH_PERFORMANCE_COUNTER4,
60 SH_PERFORMANCE_COUNTER5,
61 SH_PERFORMANCE_COUNTER6,
62 SH_PERFORMANCE_COUNTER7
63 };
64
65 static inline void
shub_mmr_write(cnodeid_t cnode,shubreg_t reg,uint64_t val)66 shub_mmr_write(cnodeid_t cnode, shubreg_t reg, uint64_t val)
67 {
68 int nasid = cnodeid_to_nasid(cnode);
69 volatile uint64_t *addr = (uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
70
71 *addr = val;
72 __ia64_mf_a();
73 }
74
75 static inline void
shub_mmr_write_iospace(cnodeid_t cnode,shubreg_t reg,uint64_t val)76 shub_mmr_write_iospace(cnodeid_t cnode, shubreg_t reg, uint64_t val)
77 {
78 int nasid = cnodeid_to_nasid(cnode);
79
80 REMOTE_HUB_S(nasid, reg, val);
81 }
82
83 static inline void
shub_mmr_write32(cnodeid_t cnode,shubreg_t reg,uint32_t val)84 shub_mmr_write32(cnodeid_t cnode, shubreg_t reg, uint32_t val)
85 {
86 int nasid = cnodeid_to_nasid(cnode);
87 volatile uint32_t *addr = (uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
88
89 *addr = val;
90 __ia64_mf_a();
91 }
92
93 static inline uint64_t
shub_mmr_read(cnodeid_t cnode,shubreg_t reg)94 shub_mmr_read(cnodeid_t cnode, shubreg_t reg)
95 {
96 int nasid = cnodeid_to_nasid(cnode);
97 volatile uint64_t val;
98
99 val = *(uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
100 __ia64_mf_a();
101
102 return val;
103 }
104
105 static inline uint64_t
shub_mmr_read_iospace(cnodeid_t cnode,shubreg_t reg)106 shub_mmr_read_iospace(cnodeid_t cnode, shubreg_t reg)
107 {
108 int nasid = cnodeid_to_nasid(cnode);
109
110 return REMOTE_HUB_L(nasid, reg);
111 }
112
113 static inline uint32_t
shub_mmr_read32(cnodeid_t cnode,shubreg_t reg)114 shub_mmr_read32(cnodeid_t cnode, shubreg_t reg)
115 {
116 int nasid = cnodeid_to_nasid(cnode);
117 volatile uint32_t val;
118
119 val = *(uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
120 __ia64_mf_a();
121
122 return val;
123 }
124
125 static int
reset_shub_stats(cnodeid_t cnode)126 reset_shub_stats(cnodeid_t cnode)
127 {
128 int i;
129
130 for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
131 shub_perf_counts[i] = 0;
132 shub_mmr_write32(cnode, shub_perf_counts_regs[i], 0);
133 }
134 return 0;
135 }
136
137 static int
configure_shub_stats(cnodeid_t cnode,unsigned long arg)138 configure_shub_stats(cnodeid_t cnode, unsigned long arg)
139 {
140 uint64_t *p = (uint64_t *)arg;
141 uint64_t i;
142 uint64_t regcnt;
143 uint64_t regval[2];
144
145 if (copy_from_user((void *)®cnt, p, sizeof(regcnt)))
146 return -EFAULT;
147
148 for (p++, i=0; i < regcnt; i++, p += 2) {
149 if (copy_from_user((void *)regval, (void *)p, sizeof(regval)))
150 return -EFAULT;
151 if (regval[0] & 0x7) {
152 printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
153 return -EINVAL;
154 }
155 shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
156 }
157 return 0;
158 }
159
160 static int
capture_shub_stats(cnodeid_t cnode,uint32_t * counts)161 capture_shub_stats(cnodeid_t cnode, uint32_t *counts)
162 {
163 int i;
164
165 for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
166 counts[i] = shub_mmr_read32(cnode, shub_perf_counts_regs[i]);
167 }
168 return 0;
169 }
170
171 static int
shubstats_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)172 shubstats_ioctl(struct inode *inode, struct file *file,
173 unsigned int cmd, unsigned long arg)
174 {
175 cnodeid_t cnode;
176 uint64_t longarg;
177 int nasid;
178
179 #ifdef CONFIG_HWGFS_FS
180 cnode = (cnodeid_t)file->f_dentry->d_fsdata;
181 #else
182 cnode = (cnodeid_t)file->private_data;
183 #endif
184 if (cnode < 0 || cnode >= numnodes)
185 return -ENODEV;
186
187 switch (cmd) {
188 case SNDRV_SHUB_CONFIGURE:
189 return configure_shub_stats(cnode, arg);
190 break;
191
192 case SNDRV_SHUB_RESETSTATS:
193 reset_shub_stats(cnode);
194 break;
195
196 case SNDRV_SHUB_INFOSIZE:
197 longarg = sizeof(shub_perf_counts);
198 if (copy_to_user((void *)arg, &longarg, sizeof(longarg))) {
199 return -EFAULT;
200 }
201 break;
202
203 case SNDRV_SHUB_GETSTATS:
204 capture_shub_stats(cnode, shub_perf_counts);
205 if (copy_to_user((void *)arg, shub_perf_counts,
206 sizeof(shub_perf_counts))) {
207 return -EFAULT;
208 }
209 break;
210
211 case SNDRV_SHUB_GETNASID:
212 nasid = cnodeid_to_nasid(cnode);
213 if (copy_to_user((void *)arg, &nasid,
214 sizeof(nasid))) {
215 return -EFAULT;
216 }
217 break;
218
219 default:
220 return -EINVAL;
221 }
222
223 return 0;
224 }
225
226 struct file_operations shub_mon_fops = {
227 ioctl: shubstats_ioctl,
228 };
229
230 /*
231 * "linkstatd" kernel thread to export SGI Numalink
232 * stats via /proc/sgi_sn/linkstats
233 */
234 static struct s_linkstats {
235 uint64_t hs_ni_sn_errors[2];
236 uint64_t hs_ni_cb_errors[2];
237 uint64_t hs_ni_retry_errors[2];
238 int hs_ii_up;
239 uint64_t hs_ii_sn_errors;
240 uint64_t hs_ii_cb_errors;
241 uint64_t hs_ii_retry_errors;
242 } *sn_linkstats;
243
244 static spinlock_t sn_linkstats_lock;
245 static unsigned long sn_linkstats_starttime;
246 static unsigned long sn_linkstats_samples;
247 static unsigned long sn_linkstats_overflows;
248 static unsigned long sn_linkstats_update_msecs;
249
250 void
sn_linkstats_reset(unsigned long msecs)251 sn_linkstats_reset(unsigned long msecs)
252 {
253 int cnode;
254 uint64_t iio_wstat;
255 uint64_t llp_csr_reg;
256
257 spin_lock(&sn_linkstats_lock);
258 memset(sn_linkstats, 0, numnodes * sizeof(struct s_linkstats));
259 for (cnode=0; cnode < numnodes; cnode++) {
260 shub_mmr_write(cnode, SH_NI0_LLP_ERR, 0L);
261 shub_mmr_write(cnode, SH_NI1_LLP_ERR, 0L);
262 shub_mmr_write_iospace(cnode, IIO_LLP_LOG, 0L);
263
264 /* zero the II retry counter */
265 iio_wstat = shub_mmr_read_iospace(cnode, IIO_WSTAT);
266 iio_wstat &= 0xffffffffff00ffff; /* bits 23:16 */
267 shub_mmr_write_iospace(cnode, IIO_WSTAT, iio_wstat);
268
269 /* Check if the II xtalk link is working */
270 llp_csr_reg = shub_mmr_read_iospace(cnode, IIO_LLP_CSR);
271 if (llp_csr_reg & IIO_LLP_CSR_IS_UP)
272 sn_linkstats[cnode].hs_ii_up = 1;
273 }
274
275 sn_linkstats_update_msecs = msecs;
276 sn_linkstats_samples = 0;
277 sn_linkstats_overflows = 0;
278 sn_linkstats_starttime = jiffies;
279 spin_unlock(&sn_linkstats_lock);
280 }
281
282 int
linkstatd_thread(void * unused)283 linkstatd_thread(void *unused)
284 {
285 int cnode;
286 int overflows;
287 uint64_t reg[2];
288 uint64_t iio_wstat = 0L;
289 ii_illr_u_t illr;
290 struct s_linkstats *lsp;
291 struct task_struct *tsk = current;
292
293 daemonize();
294
295 #ifdef __HAVE_NEW_SCHEDULER
296 set_user_nice(tsk, 19);
297 #else
298 tsk->nice = 19;
299 #endif
300 sigfillset(&tsk->blocked);
301 strcpy(tsk->comm, "linkstatd");
302
303 while(1) {
304 set_current_state(TASK_INTERRUPTIBLE);
305 schedule_timeout(sn_linkstats_update_msecs * HZ / 1000);
306
307 spin_lock(&sn_linkstats_lock);
308
309 overflows = 0;
310 for (lsp=sn_linkstats, cnode=0; cnode < numnodes; cnode++, lsp++) {
311 reg[0] = shub_mmr_read(cnode, SH_NI0_LLP_ERR);
312 reg[1] = shub_mmr_read(cnode, SH_NI1_LLP_ERR);
313 if (lsp->hs_ii_up) {
314 illr = (ii_illr_u_t)shub_mmr_read_iospace(cnode, IIO_LLP_LOG);
315 iio_wstat = shub_mmr_read_iospace(cnode, IIO_WSTAT);
316 }
317
318 if (!overflows && (
319 (reg[0] & SH_NI0_LLP_ERR_RX_SN_ERR_COUNT_MASK) ==
320 SH_NI0_LLP_ERR_RX_SN_ERR_COUNT_MASK ||
321 (reg[0] & SH_NI0_LLP_ERR_RX_CB_ERR_COUNT_MASK) ==
322 SH_NI0_LLP_ERR_RX_CB_ERR_COUNT_MASK ||
323 (reg[1] & SH_NI1_LLP_ERR_RX_SN_ERR_COUNT_MASK) ==
324 SH_NI1_LLP_ERR_RX_SN_ERR_COUNT_MASK ||
325 (reg[1] & SH_NI1_LLP_ERR_RX_CB_ERR_COUNT_MASK) ==
326 SH_NI1_LLP_ERR_RX_CB_ERR_COUNT_MASK ||
327 (lsp->hs_ii_up && illr.ii_illr_fld_s.i_sn_cnt == IIO_LLP_SN_MAX) ||
328 (lsp->hs_ii_up && illr.ii_illr_fld_s.i_cb_cnt == IIO_LLP_CB_MAX))) {
329 overflows = 1;
330 }
331
332 #define LINKSTAT_UPDATE(reg, cnt, mask, shift) cnt += (reg & mask) >> shift
333
334 LINKSTAT_UPDATE(reg[0], lsp->hs_ni_sn_errors[0],
335 SH_NI0_LLP_ERR_RX_SN_ERR_COUNT_MASK,
336 SH_NI0_LLP_ERR_RX_SN_ERR_COUNT_SHFT);
337
338 LINKSTAT_UPDATE(reg[1], lsp->hs_ni_sn_errors[1],
339 SH_NI1_LLP_ERR_RX_SN_ERR_COUNT_MASK,
340 SH_NI1_LLP_ERR_RX_SN_ERR_COUNT_SHFT);
341
342 LINKSTAT_UPDATE(reg[0], lsp->hs_ni_cb_errors[0],
343 SH_NI0_LLP_ERR_RX_CB_ERR_COUNT_MASK,
344 SH_NI0_LLP_ERR_RX_CB_ERR_COUNT_SHFT);
345
346 LINKSTAT_UPDATE(reg[1], lsp->hs_ni_cb_errors[1],
347 SH_NI1_LLP_ERR_RX_CB_ERR_COUNT_MASK,
348 SH_NI1_LLP_ERR_RX_CB_ERR_COUNT_SHFT);
349
350 LINKSTAT_UPDATE(reg[0], lsp->hs_ni_retry_errors[0],
351 SH_NI0_LLP_ERR_RETRY_COUNT_MASK,
352 SH_NI0_LLP_ERR_RETRY_COUNT_SHFT);
353
354 LINKSTAT_UPDATE(reg[1], lsp->hs_ni_retry_errors[1],
355 SH_NI1_LLP_ERR_RETRY_COUNT_MASK,
356 SH_NI1_LLP_ERR_RETRY_COUNT_SHFT);
357
358 if (lsp->hs_ii_up) {
359 /* II sn and cb errors */
360 lsp->hs_ii_sn_errors += illr.ii_illr_fld_s.i_sn_cnt;
361 lsp->hs_ii_cb_errors += illr.ii_illr_fld_s.i_cb_cnt;
362 lsp->hs_ii_retry_errors += (iio_wstat & 0x0000000000ff0000) >> 16;
363
364 shub_mmr_write(cnode, SH_NI0_LLP_ERR, 0L);
365 shub_mmr_write(cnode, SH_NI1_LLP_ERR, 0L);
366 shub_mmr_write_iospace(cnode, IIO_LLP_LOG, 0L);
367
368 /* zero the II retry counter */
369 iio_wstat = shub_mmr_read_iospace(cnode, IIO_WSTAT);
370 iio_wstat &= 0xffffffffff00ffff; /* bits 23:16 */
371 shub_mmr_write_iospace(cnode, IIO_WSTAT, iio_wstat);
372 }
373 }
374
375 sn_linkstats_samples++;
376 if (overflows)
377 sn_linkstats_overflows++;
378
379 spin_unlock(&sn_linkstats_lock);
380 }
381 }
382
383 static char *
rate_per_minute(uint64_t val,uint64_t secs)384 rate_per_minute(uint64_t val, uint64_t secs)
385 {
386 static char buf[16];
387 uint64_t a=0, b=0, c=0, d=0;
388
389 if (secs) {
390 a = 60 * val / secs;
391 b = 60 * 10 * val / secs - (10 * a);
392 c = 60 * 100 * val / secs - (100 * a) - (10 * b);
393 d = 60 * 1000 * val / secs - (1000 * a) - (100 * b) - (10 * c);
394 }
395 sprintf(buf, "%4lu.%lu%lu%lu", a, b, c, d);
396
397 return buf;
398 }
399
400 int
sn_linkstats_get(char * page)401 sn_linkstats_get(char *page)
402 {
403 int n = 0;
404 int cnode;
405 int nlport;
406 struct s_linkstats *lsp;
407 nodepda_t *npda;
408 uint64_t snsum = 0;
409 uint64_t cbsum = 0;
410 uint64_t retrysum = 0;
411 uint64_t snsum_ii = 0;
412 uint64_t cbsum_ii = 0;
413 uint64_t retrysum_ii = 0;
414 uint64_t secs;
415
416 spin_lock(&sn_linkstats_lock);
417 secs = (jiffies - sn_linkstats_starttime) / HZ;
418
419 n += sprintf(page, "# SGI Numalink stats v1 : %lu samples, %lu o/flows, update %lu msecs\n",
420 sn_linkstats_samples, sn_linkstats_overflows, sn_linkstats_update_msecs);
421
422 n += sprintf(page+n, "%-37s %8s %8s %8s %8s\n",
423 "# Numalink", "sn errs", "cb errs", "cb/min", "retries");
424
425 for (lsp=sn_linkstats, cnode=0; cnode < numnodes; cnode++, lsp++) {
426 npda = NODEPDA(cnode);
427
428 /* two NL links on each SHub */
429 for (nlport=0; nlport < 2; nlport++) {
430 cbsum += lsp->hs_ni_cb_errors[nlport];
431 snsum += lsp->hs_ni_sn_errors[nlport];
432 retrysum += lsp->hs_ni_retry_errors[nlport];
433
434 /* avoid buffer overrun (should be using seq_read API) */
435 if (numnodes > 64)
436 continue;
437
438 n += sprintf(page + n, "/%s/link/%d %8lu %8lu %8s %8lu\n",
439 npda->hwg_node_name, nlport+1, lsp->hs_ni_sn_errors[nlport],
440 lsp->hs_ni_cb_errors[nlport],
441 rate_per_minute(lsp->hs_ni_cb_errors[nlport], secs),
442 lsp->hs_ni_retry_errors[nlport]);
443 }
444
445 /* one II port on each SHub (may not be connected) */
446 if (lsp->hs_ii_up) {
447 n += sprintf(page + n, "/%s/xtalk %8lu %8lu %8s %8lu\n",
448 npda->hwg_node_name, lsp->hs_ii_sn_errors,
449 lsp->hs_ii_cb_errors, rate_per_minute(lsp->hs_ii_cb_errors, secs),
450 lsp->hs_ii_retry_errors);
451
452 snsum_ii += lsp->hs_ii_sn_errors;
453 cbsum_ii += lsp->hs_ii_cb_errors;
454 retrysum_ii += lsp->hs_ii_retry_errors;
455 }
456 }
457
458 n += sprintf(page + n, "%-37s %8lu %8lu %8s %8lu\n",
459 "System wide NL totals", snsum, cbsum,
460 rate_per_minute(cbsum, secs), retrysum);
461
462 n += sprintf(page + n, "%-37s %8lu %8lu %8s %8lu\n",
463 "System wide II totals", snsum_ii, cbsum_ii,
464 rate_per_minute(cbsum_ii, secs), retrysum_ii);
465
466 spin_unlock(&sn_linkstats_lock);
467
468 return n;
469 }
470
471 static int __init
linkstatd_init(void)472 linkstatd_init(void)
473 {
474 if (!ia64_platform_is("sn2"))
475 return -ENODEV;
476
477 spin_lock_init(&sn_linkstats_lock);
478 sn_linkstats = kmalloc(numnodes * sizeof(struct s_linkstats), GFP_KERNEL);
479 sn_linkstats_reset(60000UL); /* default 60 second update interval */
480 kernel_thread(linkstatd_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
481
482 return 0;
483 }
484
485 __initcall(linkstatd_init);
486