patch-2.4.4 linux/arch/ia64/sn/sn1/synergy.c

Next file: linux/arch/ia64/tools/print_offsets.awk
Previous file: linux/arch/ia64/sn/sn1/sv.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/arch/ia64/sn/sn1/synergy.c linux/arch/ia64/sn/sn1/synergy.c
@@ -8,9 +8,12 @@
 
 
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
 
 #include <asm/ptrace.h>
 #include <linux/devfs_fs_kernel.h>
@@ -26,8 +29,6 @@
 void setclear_mask_a(int irq, int cpuid, int set);
 void * kmalloc(size_t size, int flags);
 
-extern struct sn1_cnode_action_list *sn1_node_actions[];
-
 
 void
 synergy_intr_alloc(int bit, int cpuid) {
@@ -40,9 +41,7 @@
 {
 	int irq;
 	unsigned is_b;
-int nasid;
 
-nasid = cpuid_to_nasid(cpuid);
 	irq = bit_pos_to_irq(bit);
 
 	is_b = (cpuid_to_slice(cpuid)) & 1;
@@ -202,3 +201,229 @@
 		REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
 	}
 }
+
+#if defined(CONFIG_IA64_SGI_SYNERGY_PERF)
+
+/*
+ * Synergy perf registers. Multiplexed via timer_interrupt
+ */
+static struct proc_dir_entry *synergy_perf_proc = NULL;
+
+/*
+ * read handler for /proc/synergy
+ */
+static int
+synergy_perf_read_proc (char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+	cnodeid_t       cnode;
+	nodepda_t       *npdap;
+	synergy_perf_t	*p;
+	int		len = 0;
+
+	len += sprintf(page+len, "# cnode module slot event synergy-A synergy-B\n");
+
+	/* walk the event list for each node */
+	for (cnode=0; cnode < numnodes; cnode++) {
+		npdap = NODEPDA(cnode);
+		if (npdap->synergy_perf_enabled == 0) {
+			len += sprintf(page+len, "# DISABLED\n");
+			break;
+		}
+
+		spin_lock_irq(&npdap->synergy_perf_lock);
+		for (p = npdap->synergy_perf_first; p;) {
+			uint64_t cnt_a=0, cnt_b=0;
+
+			if (p->intervals > 0) {
+				cnt_a = p->counts[0] * npdap->synergy_active_intervals / p->intervals;
+				cnt_b = p->counts[1] * npdap->synergy_active_intervals / p->intervals;
+			}
+
+			len += sprintf(page+len, "%d %d %d %12lx %lu %lu\n",
+				(int)cnode, (int)npdap->module_id, (int)npdap->slotdesc,
+				p->modesel, cnt_a, cnt_b);
+
+			p = p->next;
+			if (p == npdap->synergy_perf_first)
+				break;
+		}
+		spin_unlock_irq(&npdap->synergy_perf_lock);
+	}
+
+	if (len <= off+count) *eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len>count) len = count;
+	if (len<0) len = 0;
+
+	return len;
+}
+
+static int
+synergy_perf_append(uint64_t modesel)
+{
+	int		cnode;
+	nodepda_t       *npdap;
+	synergy_perf_t	*p;
+	int		err = 0;
+
+	/* bit 45 is enable */
+	modesel |= (1UL << 45);
+
+	for (cnode=0; cnode < numnodes; cnode++) {
+		/* for each node, insert a new synergy_perf entry */
+		if ((npdap = NODEPDA(cnode)) == NULL) {
+			printk("synergy_perf_append: cnode=%d NODEPDA(cnode)==NULL, nodepda=%p\n", cnode, nodepda);
+			continue;
+		}
+
+		/* XX use kmem_alloc_node() when it is implemented */
+		p = (synergy_perf_t *)kmalloc(sizeof(synergy_perf_t), GFP_KERNEL);
+		if (p == NULL)
+			err = -ENOMEM;
+		else {
+			memset(p, 0, sizeof(synergy_perf_t));
+			p->modesel = modesel;
+			if (npdap->synergy_perf_data == NULL) {
+				/* circular list */
+				p->next = p;
+				npdap->synergy_perf_data = p;
+				npdap->synergy_perf_first = p;
+			}
+			else {
+				/*
+				 * Jumble up the insertion order so we get better sampling.
+				 * Once the list is complete, "first" stays the same so the
+				 * reporting order is consistent.
+				 */
+				p->next = npdap->synergy_perf_first->next;
+				npdap->synergy_perf_first->next = p;
+				npdap->synergy_perf_first = p->next;
+			}
+		}
+	}
+
+	return err;
+}
+
+static int
+synergy_perf_write_proc (struct file *file, const char *buffer,
+                                unsigned long count, void *data)
+{
+	int		cnode;
+	nodepda_t       *npdap;
+	uint64_t	modesel;
+	char		cmd[64];
+	extern long	atoi(char *);
+    
+	if (count == sizeof(uint64_t)) {
+	    if (copy_from_user(&modesel, buffer, sizeof(uint64_t)))
+		    return -EFAULT;
+	    synergy_perf_append(modesel);
+	}
+	else {
+	    if (copy_from_user(cmd, buffer, count < sizeof(cmd) ? count : sizeof(cmd)))
+		    return -EFAULT;
+	    if (strncmp(cmd, "enable", 6) == 0) {
+		/* enable counting */
+		for (cnode=0; cnode < numnodes; cnode++) {
+			npdap = NODEPDA(cnode);
+			npdap->synergy_perf_enabled = 1;
+		}
+		printk("NOTICE: synergy perf counting enabled\n");
+	    }
+	    else
+	    if (strncmp(cmd, "disable", 7) == 0) {
+		/* disable counting */
+		for (cnode=0; cnode < numnodes; cnode++) {
+			npdap = NODEPDA(cnode);
+			npdap->synergy_perf_enabled = 0;
+		}
+		printk("NOTICE: synergy perf counting disabled\n");
+	    }
+	    else
+	    if (strncmp(cmd, "frequency", 9) == 0) {
+		/* set the update frequency (timer-interrupts per update) */
+		int freq;
+
+		if (count < 12)
+			return -EINVAL;
+		freq = atoi(cmd + 10);
+		if (freq <= 0 || freq > 100)
+			return -EINVAL;
+		for (cnode=0; cnode < numnodes; cnode++) {
+			npdap = NODEPDA(cnode);
+			npdap->synergy_perf_freq = (uint64_t)freq;
+		}
+		printk("NOTICE: synergy perf freq set to %d\n", freq);
+	    }
+	    else
+		return -EINVAL;
+	}
+	
+	return count;
+}
+
+void
+synergy_perf_update(int cpu)
+{
+	nasid_t		nasid;
+	cnodeid_t       cnode = cpuid_to_cnodeid(cpu);
+	struct nodepda_s *npdap;
+	extern struct nodepda_s *nodepda;
+
+	if (nodepda == NULL || (npdap=NODEPDA(cnode)) == NULL || npdap->synergy_perf_enabled == 0 ||
+		npdap->synergy_perf_data == NULL) {
+		/* I/O not initialized, or not enabled, or no events to monitor */
+		return;
+	}
+
+	if (npdap->synergy_inactive_intervals++ % npdap->synergy_perf_freq != 0) {
+		/* don't multiplex on every timer interrupt */
+		return;
+	}
+
+	/*
+	 * Read registers for last interval and increment counters.
+	 * Hold the per-node synergy_perf_lock so concurrent readers get
+	 * consistent values.
+	 */
+	spin_lock_irq(&npdap->synergy_perf_lock);
+
+	nasid = cpuid_to_nasid(cpu);
+	npdap->synergy_active_intervals++;
+	npdap->synergy_perf_data->intervals++;
+
+	npdap->synergy_perf_data->counts[0] += 0xffffffffffUL &
+		REMOTE_SYNERGY_LOAD(nasid, 0, PERF_CNTR0_A);
+
+	npdap->synergy_perf_data->counts[1] += 0xffffffffffUL &
+		REMOTE_SYNERGY_LOAD(nasid, 1, PERF_CNTR0_B);
+
+	/* skip to next in circular list */
+	npdap->synergy_perf_data = npdap->synergy_perf_data->next;
+
+	spin_unlock_irq(&npdap->synergy_perf_lock);
+
+	/* set the counter 0 selection modes for both A and B */
+	REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTL0_A, npdap->synergy_perf_data->modesel);
+	REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTL0_B, npdap->synergy_perf_data->modesel);
+
+	/* and reset the counter registers to zero */
+	REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTR0_A, 0UL);
+	REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTR0_B, 0UL);
+}
+
+void
+synergy_perf_init(void)
+{
+        if ((synergy_perf_proc = create_proc_entry("synergy", 0644, NULL)) != NULL) {
+                synergy_perf_proc->read_proc = synergy_perf_read_proc;
+                synergy_perf_proc->write_proc = synergy_perf_write_proc;
+                printk("markgw: synergy_perf_init()\n");
+        }
+}
+
+#endif /* CONFIG_IA64_SGI_SYNERGY_PERF */
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)