import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / ia64 / sn / io / sn2 / shub.c
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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);