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 *)&regcnt, 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