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
7 * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
10 #ident "$Revision: 1.167 $"
12 #include <linux/types.h>
13 #include <linux/slab.h>
14 #include <linux/interrupt.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>
36 #include <asm/sn/sn_sal.h>
37 #include <asm/sn/sndrv.h>
39 #define SHUB_NUM_ECF_REGISTERS 8
42 * A backport of the 2.5 scheduler is used by many vendors of 2.4-based
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.
48 #define __HAVE_NEW_SCHEDULER 1
52 static uint32_t shub_perf_counts[SHUB_NUM_ECF_REGISTERS];
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
66 shub_mmr_write(cnodeid_t cnode, shubreg_t reg, uint64_t val)
68 int nasid = cnodeid_to_nasid(cnode);
69 volatile uint64_t *addr = (uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
76 shub_mmr_write_iospace(cnodeid_t cnode, shubreg_t reg, uint64_t val)
78 int nasid = cnodeid_to_nasid(cnode);
80 REMOTE_HUB_S(nasid, reg, val);
84 shub_mmr_write32(cnodeid_t cnode, shubreg_t reg, uint32_t val)
86 int nasid = cnodeid_to_nasid(cnode);
87 volatile uint32_t *addr = (uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
93 static inline uint64_t
94 shub_mmr_read(cnodeid_t cnode, shubreg_t reg)
96 int nasid = cnodeid_to_nasid(cnode);
97 volatile uint64_t val;
99 val = *(uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
105 static inline uint64_t
106 shub_mmr_read_iospace(cnodeid_t cnode, shubreg_t reg)
108 int nasid = cnodeid_to_nasid(cnode);
110 return REMOTE_HUB_L(nasid, reg);
113 static inline uint32_t
114 shub_mmr_read32(cnodeid_t cnode, shubreg_t reg)
116 int nasid = cnodeid_to_nasid(cnode);
117 volatile uint32_t val;
119 val = *(uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
126 reset_shub_stats(cnodeid_t cnode)
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);
138 configure_shub_stats(cnodeid_t cnode, unsigned long arg)
140 uint64_t *p = (uint64_t *)arg;
145 if (copy_from_user((void *)®cnt, p, sizeof(regcnt)))
148 for (p++, i=0; i < regcnt; i++, p += 2) {
149 if (copy_from_user((void *)regval, (void *)p, sizeof(regval)))
151 if (regval[0] & 0x7) {
152 printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
155 shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
161 capture_shub_stats(cnodeid_t cnode, uint32_t *counts)
165 for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
166 counts[i] = shub_mmr_read32(cnode, shub_perf_counts_regs[i]);
172 shubstats_ioctl(struct inode *inode, struct file *file,
173 unsigned int cmd, unsigned long arg)
179 #ifdef CONFIG_HWGFS_FS
180 cnode = (cnodeid_t)file->f_dentry->d_fsdata;
182 cnode = (cnodeid_t)file->private_data;
184 if (cnode < 0 || cnode >= numnodes)
188 case SNDRV_SHUB_CONFIGURE:
189 return configure_shub_stats(cnode, arg);
192 case SNDRV_SHUB_RESETSTATS:
193 reset_shub_stats(cnode);
196 case SNDRV_SHUB_INFOSIZE:
197 longarg = sizeof(shub_perf_counts);
198 if (copy_to_user((void *)arg, &longarg, sizeof(longarg))) {
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))) {
211 case SNDRV_SHUB_GETNASID:
212 nasid = cnodeid_to_nasid(cnode);
213 if (copy_to_user((void *)arg, &nasid,
226 struct file_operations shub_mon_fops = {
227 ioctl: shubstats_ioctl,
231 * "linkstatd" kernel thread to export SGI Numalink
232 * stats via /proc/sgi_sn/linkstats
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];
239 uint64_t hs_ii_sn_errors;
240 uint64_t hs_ii_cb_errors;
241 uint64_t hs_ii_retry_errors;
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;
251 sn_linkstats_reset(unsigned long msecs)
255 uint64_t llp_csr_reg;
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);
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);
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;
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);
283 linkstatd_thread(void *unused)
288 uint64_t iio_wstat = 0L;
290 struct s_linkstats *lsp;
291 struct task_struct *tsk = current;
295 #ifdef __HAVE_NEW_SCHEDULER
296 set_user_nice(tsk, 19);
300 sigfillset(&tsk->blocked);
301 strcpy(tsk->comm, "linkstatd");
304 set_current_state(TASK_INTERRUPTIBLE);
305 schedule_timeout(sn_linkstats_update_msecs * HZ / 1000);
307 spin_lock(&sn_linkstats_lock);
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);
314 illr = (ii_illr_u_t)shub_mmr_read_iospace(cnode, IIO_LLP_LOG);
315 iio_wstat = shub_mmr_read_iospace(cnode, IIO_WSTAT);
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))) {
332 #define LINKSTAT_UPDATE(reg, cnt, mask, shift) cnt += (reg & mask) >> shift
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);
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);
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);
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);
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);
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);
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;
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);
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);
375 sn_linkstats_samples++;
377 sn_linkstats_overflows++;
379 spin_unlock(&sn_linkstats_lock);
384 rate_per_minute(uint64_t val, uint64_t secs)
387 uint64_t a=0, b=0, c=0, d=0;
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);
395 sprintf(buf, "%4lu.%lu%lu%lu", a, b, c, d);
401 sn_linkstats_get(char *page)
406 struct s_linkstats *lsp;
410 uint64_t retrysum = 0;
411 uint64_t snsum_ii = 0;
412 uint64_t cbsum_ii = 0;
413 uint64_t retrysum_ii = 0;
416 spin_lock(&sn_linkstats_lock);
417 secs = (jiffies - sn_linkstats_starttime) / HZ;
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);
422 n += sprintf(page+n, "%-37s %8s %8s %8s %8s\n",
423 "# Numalink", "sn errs", "cb errs", "cb/min", "retries");
425 for (lsp=sn_linkstats, cnode=0; cnode < numnodes; cnode++, lsp++) {
426 npda = NODEPDA(cnode);
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];
434 /* avoid buffer overrun (should be using seq_read API) */
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]);
445 /* one II port on each SHub (may not be connected) */
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);
452 snsum_ii += lsp->hs_ii_sn_errors;
453 cbsum_ii += lsp->hs_ii_cb_errors;
454 retrysum_ii += lsp->hs_ii_retry_errors;
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);
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);
466 spin_unlock(&sn_linkstats_lock);
474 if (!ia64_platform_is("sn2"))
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);
485 __initcall(linkstatd_init);