aboutsummaryrefslogtreecommitdiff
path: root/arch/alpha/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel')
-rw-r--r--arch/alpha/kernel/Makefile1
-rw-r--r--arch/alpha/kernel/core_t2.c11
-rw-r--r--arch/alpha/kernel/entry.S81
-rw-r--r--arch/alpha/kernel/err_ev6.c12
-rw-r--r--arch/alpha/kernel/err_marvel.c39
-rw-r--r--arch/alpha/kernel/err_titan.c35
-rw-r--r--arch/alpha/kernel/irq.c7
-rw-r--r--arch/alpha/kernel/irq_alpha.c3
-rw-r--r--arch/alpha/kernel/machvec_impl.h3
-rw-r--r--arch/alpha/kernel/osf_sys.c23
-rw-r--r--arch/alpha/kernel/pci-sysfs.c2
-rw-r--r--arch/alpha/kernel/pci_iommu.c4
-rw-r--r--arch/alpha/kernel/perf_event.c886
-rw-r--r--arch/alpha/kernel/process.c7
-rw-r--r--arch/alpha/kernel/proto.h3
-rw-r--r--arch/alpha/kernel/ptrace.c7
-rw-r--r--arch/alpha/kernel/signal.c97
-rw-r--r--arch/alpha/kernel/srm_env.c2
-rw-r--r--arch/alpha/kernel/sys_cabriolet.c19
-rw-r--r--arch/alpha/kernel/sys_takara.c11
-rw-r--r--arch/alpha/kernel/systbls.S5
-rw-r--r--arch/alpha/kernel/time.c26
-rw-r--r--arch/alpha/kernel/traps.c3
23 files changed, 1076 insertions, 211 deletions
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 5a62fb46ef2..1ee9b5b629b 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
obj-$(CONFIG_SRM_ENV) += srm_env.o
obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o
ifdef CONFIG_ALPHA_GENERIC
diff --git a/arch/alpha/kernel/core_t2.c b/arch/alpha/kernel/core_t2.c
index e6d90568b65..2f770e99428 100644
--- a/arch/alpha/kernel/core_t2.c
+++ b/arch/alpha/kernel/core_t2.c
@@ -74,8 +74,6 @@
# define DBG(args)
#endif
-DEFINE_RAW_SPINLOCK(t2_hae_lock);
-
static volatile unsigned int t2_mcheck_any_expected;
static volatile unsigned int t2_mcheck_last_taken;
@@ -406,6 +404,7 @@ void __init
t2_init_arch(void)
{
struct pci_controller *hose;
+ struct resource *hae_mem;
unsigned long temp;
unsigned int i;
@@ -433,7 +432,13 @@ t2_init_arch(void)
*/
pci_isa_hose = hose = alloc_pci_controller();
hose->io_space = &ioport_resource;
- hose->mem_space = &iomem_resource;
+ hae_mem = alloc_resource();
+ hae_mem->start = 0;
+ hae_mem->end = T2_MEM_R1_MASK;
+ hae_mem->name = pci_hae0_name;
+ if (request_resource(&iomem_resource, hae_mem) < 0)
+ printk(KERN_ERR "Failed to request HAE_MEM\n");
+ hose->mem_space = hae_mem;
hose->index = 0;
hose->sparse_mem_base = T2_SPARSE_MEM - IDENT_ADDR;
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index b45d913a51c..6d159cee5f2 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -73,8 +73,6 @@
ldq $20, HAE_REG($19); \
stq $21, HAE_CACHE($19); \
stq $21, 0($20); \
- ldq $0, 0($sp); \
- ldq $1, 8($sp); \
99:; \
ldq $19, 72($sp); \
ldq $20, 80($sp); \
@@ -316,19 +314,24 @@ ret_from_sys_call:
cmovne $26, 0, $19 /* $19 = 0 => non-restartable */
ldq $0, SP_OFF($sp)
and $0, 8, $0
- beq $0, restore_all
-ret_from_reschedule:
+ beq $0, ret_to_kernel
+ret_to_user:
/* Make sure need_resched and sigpending don't change between
sampling and the rti. */
lda $16, 7
call_pal PAL_swpipl
ldl $5, TI_FLAGS($8)
and $5, _TIF_WORK_MASK, $2
- bne $5, work_pending
+ bne $2, work_pending
restore_all:
RESTORE_ALL
call_pal PAL_rti
+ret_to_kernel:
+ lda $16, 7
+ call_pal PAL_swpipl
+ br restore_all
+
.align 3
$syscall_error:
/*
@@ -363,7 +366,7 @@ $ret_success:
* $8: current.
* $19: The old syscall number, or zero if this is not a return
* from a syscall that errored and is possibly restartable.
- * $20: Error indication.
+ * $20: The old a3 value
*/
.align 4
@@ -392,12 +395,18 @@ $work_resched:
$work_notifysig:
mov $sp, $16
- br $1, do_switch_stack
+ bsr $1, do_switch_stack
mov $sp, $17
mov $5, $18
+ mov $19, $9 /* save old syscall number */
+ mov $20, $10 /* save old a3 */
+ and $5, _TIF_SIGPENDING, $2
+ cmovne $2, 0, $9 /* we don't want double syscall restarts */
jsr $26, do_notify_resume
+ mov $9, $19
+ mov $10, $20
bsr $1, undo_switch_stack
- br restore_all
+ br ret_to_user
.end work_pending
/*
@@ -430,6 +439,7 @@ strace:
beq $1, 1f
ldq $27, 0($2)
1: jsr $26, ($27), sys_gettimeofday
+ret_from_straced:
ldgp $gp, 0($26)
/* check return.. */
@@ -650,7 +660,7 @@ kernel_thread:
/* We don't actually care for a3 success widgetry in the kernel.
Not for positive errno values. */
stq $0, 0($sp) /* $0 */
- br restore_all
+ br ret_to_kernel
.end kernel_thread
/*
@@ -757,11 +767,15 @@ sys_vfork:
.ent sys_sigreturn
sys_sigreturn:
.prologue 0
+ lda $9, ret_from_straced
+ cmpult $26, $9, $9
mov $sp, $17
lda $18, -SWITCH_STACK_SIZE($sp)
lda $sp, -SWITCH_STACK_SIZE($sp)
jsr $26, do_sigreturn
- br $1, undo_switch_stack
+ bne $9, 1f
+ jsr $26, syscall_trace
+1: br $1, undo_switch_stack
br ret_from_sys_call
.end sys_sigreturn
@@ -770,47 +784,19 @@ sys_sigreturn:
.ent sys_rt_sigreturn
sys_rt_sigreturn:
.prologue 0
+ lda $9, ret_from_straced
+ cmpult $26, $9, $9
mov $sp, $17
lda $18, -SWITCH_STACK_SIZE($sp)
lda $sp, -SWITCH_STACK_SIZE($sp)
jsr $26, do_rt_sigreturn
- br $1, undo_switch_stack
+ bne $9, 1f
+ jsr $26, syscall_trace
+1: br $1, undo_switch_stack
br ret_from_sys_call
.end sys_rt_sigreturn
.align 4
- .globl sys_sigsuspend
- .ent sys_sigsuspend
-sys_sigsuspend:
- .prologue 0
- mov $sp, $17
- br $1, do_switch_stack
- mov $sp, $18
- subq $sp, 16, $sp
- stq $26, 0($sp)
- jsr $26, do_sigsuspend
- ldq $26, 0($sp)
- lda $sp, SWITCH_STACK_SIZE+16($sp)
- ret
-.end sys_sigsuspend
-
- .align 4
- .globl sys_rt_sigsuspend
- .ent sys_rt_sigsuspend
-sys_rt_sigsuspend:
- .prologue 0
- mov $sp, $18
- br $1, do_switch_stack
- mov $sp, $19
- subq $sp, 16, $sp
- stq $26, 0($sp)
- jsr $26, do_rt_sigsuspend
- ldq $26, 0($sp)
- lda $sp, SWITCH_STACK_SIZE+16($sp)
- ret
-.end sys_rt_sigsuspend
-
- .align 4
.globl sys_sethae
.ent sys_sethae
sys_sethae:
@@ -929,15 +915,6 @@ sys_execve:
.end sys_execve
.align 4
- .globl osf_sigprocmask
- .ent osf_sigprocmask
-osf_sigprocmask:
- .prologue 0
- mov $sp, $18
- jmp $31, sys_osf_sigprocmask
-.end osf_sigprocmask
-
- .align 4
.globl alpha_ni_syscall
.ent alpha_ni_syscall
alpha_ni_syscall:
diff --git a/arch/alpha/kernel/err_ev6.c b/arch/alpha/kernel/err_ev6.c
index 8ca6345bf13..253cf1a8748 100644
--- a/arch/alpha/kernel/err_ev6.c
+++ b/arch/alpha/kernel/err_ev6.c
@@ -90,11 +90,13 @@ static int
ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn,
u64 c_stat, u64 c_sts, int print)
{
- char *sourcename[] = { "UNKNOWN", "UNKNOWN", "UNKNOWN",
- "MEMORY", "BCACHE", "DCACHE",
- "BCACHE PROBE", "BCACHE PROBE" };
- char *streamname[] = { "D", "I" };
- char *bitsname[] = { "SINGLE", "DOUBLE" };
+ static const char * const sourcename[] = {
+ "UNKNOWN", "UNKNOWN", "UNKNOWN",
+ "MEMORY", "BCACHE", "DCACHE",
+ "BCACHE PROBE", "BCACHE PROBE"
+ };
+ static const char * const streamname[] = { "D", "I" };
+ static const char * const bitsname[] = { "SINGLE", "DOUBLE" };
int status = MCHK_DISPOSITION_REPORT;
int source = -1, stream = -1, bits = -1;
diff --git a/arch/alpha/kernel/err_marvel.c b/arch/alpha/kernel/err_marvel.c
index 52a79dfc13c..648ae88aeb8 100644
--- a/arch/alpha/kernel/err_marvel.c
+++ b/arch/alpha/kernel/err_marvel.c
@@ -109,7 +109,7 @@ marvel_print_err_cyc(u64 err_cyc)
#define IO7__ERR_CYC__CYCLE__M (0x7)
printk("%s Packet In Error: %s\n"
- "%s Error in %s, cycle %ld%s%s\n",
+ "%s Error in %s, cycle %lld%s%s\n",
err_print_prefix,
packet_desc[EXTRACT(err_cyc, IO7__ERR_CYC__PACKET)],
err_print_prefix,
@@ -313,7 +313,7 @@ marvel_print_po7_ugbge_sym(u64 ugbge_sym)
}
printk("%s Up Hose Garbage Symptom:\n"
- "%s Source Port: %ld - Dest PID: %ld - OpCode: %s\n",
+ "%s Source Port: %lld - Dest PID: %lld - OpCode: %s\n",
err_print_prefix,
err_print_prefix,
EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_SRC_PORT),
@@ -552,7 +552,7 @@ marvel_print_pox_spl_cmplt(u64 spl_cmplt)
#define IO7__POX_SPLCMPLT__REM_BYTE_COUNT__M (0xfff)
printk("%s Split Completion Error:\n"
- "%s Source (Bus:Dev:Func): %ld:%ld:%ld\n",
+ "%s Source (Bus:Dev:Func): %lld:%lld:%lld\n",
err_print_prefix,
err_print_prefix,
EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_BUS),
@@ -589,22 +589,23 @@ marvel_print_pox_spl_cmplt(u64 spl_cmplt)
static void
marvel_print_pox_trans_sum(u64 trans_sum)
{
- char *pcix_cmd[] = { "Interrupt Acknowledge",
- "Special Cycle",
- "I/O Read",
- "I/O Write",
- "Reserved",
- "Reserved / Device ID Message",
- "Memory Read",
- "Memory Write",
- "Reserved / Alias to Memory Read Block",
- "Reserved / Alias to Memory Write Block",
- "Configuration Read",
- "Configuration Write",
- "Memory Read Multiple / Split Completion",
- "Dual Address Cycle",
- "Memory Read Line / Memory Read Block",
- "Memory Write and Invalidate / Memory Write Block"
+ static const char * const pcix_cmd[] = {
+ "Interrupt Acknowledge",
+ "Special Cycle",
+ "I/O Read",
+ "I/O Write",
+ "Reserved",
+ "Reserved / Device ID Message",
+ "Memory Read",
+ "Memory Write",
+ "Reserved / Alias to Memory Read Block",
+ "Reserved / Alias to Memory Write Block",
+ "Configuration Read",
+ "Configuration Write",
+ "Memory Read Multiple / Split Completion",
+ "Dual Address Cycle",
+ "Memory Read Line / Memory Read Block",
+ "Memory Write and Invalidate / Memory Write Block"
};
#define IO7__POX_TRANSUM__PCI_ADDR__S (0)
diff --git a/arch/alpha/kernel/err_titan.c b/arch/alpha/kernel/err_titan.c
index f7ed97ce0df..c3b3781a03d 100644
--- a/arch/alpha/kernel/err_titan.c
+++ b/arch/alpha/kernel/err_titan.c
@@ -75,8 +75,12 @@ titan_parse_p_serror(int which, u64 serror, int print)
int status = MCHK_DISPOSITION_REPORT;
#ifdef CONFIG_VERBOSE_MCHECK
- char *serror_src[] = {"GPCI", "APCI", "AGP HP", "AGP LP"};
- char *serror_cmd[] = {"DMA Read", "DMA RMW", "SGTE Read", "Reserved"};
+ static const char * const serror_src[] = {
+ "GPCI", "APCI", "AGP HP", "AGP LP"
+ };
+ static const char * const serror_cmd[] = {
+ "DMA Read", "DMA RMW", "SGTE Read", "Reserved"
+ };
#endif /* CONFIG_VERBOSE_MCHECK */
#define TITAN__PCHIP_SERROR__LOST_UECC (1UL << 0)
@@ -140,14 +144,15 @@ titan_parse_p_perror(int which, int port, u64 perror, int print)
int status = MCHK_DISPOSITION_REPORT;
#ifdef CONFIG_VERBOSE_MCHECK
- char *perror_cmd[] = { "Interrupt Acknowledge", "Special Cycle",
- "I/O Read", "I/O Write",
- "Reserved", "Reserved",
- "Memory Read", "Memory Write",
- "Reserved", "Reserved",
- "Configuration Read", "Configuration Write",
- "Memory Read Multiple", "Dual Address Cycle",
- "Memory Read Line","Memory Write and Invalidate"
+ static const char * const perror_cmd[] = {
+ "Interrupt Acknowledge", "Special Cycle",
+ "I/O Read", "I/O Write",
+ "Reserved", "Reserved",
+ "Memory Read", "Memory Write",
+ "Reserved", "Reserved",
+ "Configuration Read", "Configuration Write",
+ "Memory Read Multiple", "Dual Address Cycle",
+ "Memory Read Line", "Memory Write and Invalidate"
};
#endif /* CONFIG_VERBOSE_MCHECK */
@@ -273,11 +278,11 @@ titan_parse_p_agperror(int which, u64 agperror, int print)
int cmd, len;
unsigned long addr;
- char *agperror_cmd[] = { "Read (low-priority)", "Read (high-priority)",
- "Write (low-priority)",
- "Write (high-priority)",
- "Reserved", "Reserved",
- "Flush", "Fence"
+ static const char * const agperror_cmd[] = {
+ "Read (low-priority)", "Read (high-priority)",
+ "Write (low-priority)", "Write (high-priority)",
+ "Reserved", "Reserved",
+ "Flush", "Fence"
};
#endif /* CONFIG_VERBOSE_MCHECK */
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index 7f912ba3d9a..fe912984d9b 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -31,6 +31,7 @@
#include <asm/uaccess.h>
volatile unsigned long irq_err_count;
+DEFINE_PER_CPU(unsigned long, irq_pmi_count);
void ack_bad_irq(unsigned int irq)
{
@@ -63,9 +64,7 @@ int irq_select_affinity(unsigned int irq)
int
show_interrupts(struct seq_file *p, void *v)
{
-#ifdef CONFIG_SMP
int j;
-#endif
int irq = *(loff_t *) v;
struct irqaction * action;
unsigned long flags;
@@ -112,6 +111,10 @@ unlock:
seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
seq_putc(p, '\n');
#endif
+ seq_puts(p, "PMI: ");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10lu ", per_cpu(irq_pmi_count, j));
+ seq_puts(p, " Performance Monitoring\n");
seq_printf(p, "ERR: %10lu\n", irq_err_count);
}
return 0;
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index cfde865b78e..5f77afb88e8 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -10,6 +10,7 @@
#include <asm/machvec.h>
#include <asm/dma.h>
+#include <asm/perf_event.h>
#include "proto.h"
#include "irq_impl.h"
@@ -111,6 +112,8 @@ init_IRQ(void)
wrent(entInt, 0);
alpha_mv.init_irq();
+
+ init_hw_perf_events();
}
/*
diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h
index 512685f7809..7fa62488bd1 100644
--- a/arch/alpha/kernel/machvec_impl.h
+++ b/arch/alpha/kernel/machvec_impl.h
@@ -25,6 +25,9 @@
#ifdef MCPCIA_ONE_HAE_WINDOW
#define MCPCIA_HAE_ADDRESS (&alpha_mv.hae_cache)
#endif
+#ifdef T2_ONE_HAE_WINDOW
+#define T2_HAE_ADDRESS (&alpha_mv.hae_cache)
+#endif
/* Only a few systems don't define IACK_SC, handling all interrupts through
the SRM console. But splitting out that one case from IO() below
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index de9d3971780..547e8b84b2f 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>
@@ -69,7 +68,6 @@ SYSCALL_DEFINE4(osf_set_program_attributes, unsigned long, text_start,
{
struct mm_struct *mm;
- lock_kernel();
mm = current->mm;
mm->end_code = bss_start + bss_len;
mm->start_brk = bss_start + bss_len;
@@ -78,7 +76,6 @@ SYSCALL_DEFINE4(osf_set_program_attributes, unsigned long, text_start,
printk("set_program_attributes(%lx %lx %lx %lx)\n",
text_start, text_len, bss_start, bss_len);
#endif
- unlock_kernel();
return 0;
}
@@ -234,17 +231,17 @@ linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_st
}
static int
-do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer,
+do_osf_statfs(struct path *path, struct osf_statfs __user *buffer,
unsigned long bufsiz)
{
struct kstatfs linux_stat;
- int error = vfs_statfs(dentry, &linux_stat);
+ int error = vfs_statfs(path, &linux_stat);
if (!error)
error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
return error;
}
-SYSCALL_DEFINE3(osf_statfs, char __user *, pathname,
+SYSCALL_DEFINE3(osf_statfs, const char __user *, pathname,
struct osf_statfs __user *, buffer, unsigned long, bufsiz)
{
struct path path;
@@ -252,7 +249,7 @@ SYSCALL_DEFINE3(osf_statfs, char __user *, pathname,
retval = user_path(pathname, &path);
if (!retval) {
- retval = do_osf_statfs(path.dentry, buffer, bufsiz);
+ retval = do_osf_statfs(&path, buffer, bufsiz);
path_put(&path);
}
return retval;
@@ -267,7 +264,7 @@ SYSCALL_DEFINE3(osf_fstatfs, unsigned long, fd,
retval = -EBADF;
file = fget(fd);
if (file) {
- retval = do_osf_statfs(file->f_path.dentry, buffer, bufsiz);
+ retval = do_osf_statfs(&file->f_path, buffer, bufsiz);
fput(file);
}
return retval;
@@ -358,7 +355,7 @@ osf_procfs_mount(char *dirname, struct procfs_args __user *args, int flags)
return do_mount("", dirname, "proc", flags, NULL);
}
-SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, char __user *, path,
+SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
int, flag, void __user *, data)
{
int retval;
@@ -517,7 +514,6 @@ SYSCALL_DEFINE2(osf_proplist_syscall, enum pl_code, code,
long error;
int __user *min_buf_size_ptr;
- lock_kernel();
switch (code) {
case PL_SET:
if (get_user(error, &args->set.nbytes))
@@ -547,7 +543,6 @@ SYSCALL_DEFINE2(osf_proplist_syscall, enum pl_code, code,
error = -EOPNOTSUPP;
break;
};
- unlock_kernel();
return error;
}
@@ -594,7 +589,7 @@ SYSCALL_DEFINE2(osf_sigstack, struct sigstack __user *, uss,
SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
{
- char *sysinfo_table[] = {
+ const char *sysinfo_table[] = {
utsname()->sysname,
utsname()->nodename,
utsname()->release,
@@ -606,7 +601,7 @@ SYSCALL_DEFINE3(osf_sysinfo, int, command, char __user *, buf, long, count)
"dummy", /* secure RPC domain */
};
unsigned long offset;
- char *res;
+ const char *res;
long len, err = -EINVAL;
offset = command-1;
@@ -932,7 +927,7 @@ SYSCALL_DEFINE3(osf_setitimer, int, which, struct itimerval32 __user *, in,
}
-SYSCALL_DEFINE2(osf_utimes, char __user *, filename,
+SYSCALL_DEFINE2(osf_utimes, const char __user *, filename,
struct timeval32 __user *, tvs)
{
struct timespec tv[2];
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 738fc824e2e..b899e95f79f 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -66,7 +66,7 @@ static int pci_mmap_resource(struct kobject *kobj,
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
- struct resource *res = (struct resource *)attr->private;
+ struct resource *res = attr->private;
enum pci_mmap_state mmap_type;
struct pci_bus_region bar;
int i;
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c
index d1dbd9acd1d..022c2748fa4 100644
--- a/arch/alpha/kernel/pci_iommu.c
+++ b/arch/alpha/kernel/pci_iommu.c
@@ -223,7 +223,7 @@ iommu_arena_free(struct pci_iommu_arena *arena, long ofs, long n)
*/
static int pci_dac_dma_supported(struct pci_dev *dev, u64 mask)
{
- dma64_addr_t dac_offset = alpha_mv.pci_dac_offset;
+ dma_addr_t dac_offset = alpha_mv.pci_dac_offset;
int ok = 1;
/* If this is not set, the machine doesn't support DAC at all. */
@@ -756,7 +756,7 @@ static void alpha_pci_unmap_sg(struct device *dev, struct scatterlist *sg,
spin_lock_irqsave(&arena->lock, flags);
for (end = sg + nents; sg < end; ++sg) {
- dma64_addr_t addr;
+ dma_addr_t addr;
size_t size;
long npages, ofs;
dma_addr_t tend;
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
new file mode 100644
index 00000000000..1cc49683fb6
--- /dev/null
+++ b/arch/alpha/kernel/perf_event.c
@@ -0,0 +1,886 @@
+/*
+ * Hardware performance events for the Alpha.
+ *
+ * We implement HW counts on the EV67 and subsequent CPUs only.
+ *
+ * (C) 2010 Michael J. Cree
+ *
+ * Somewhat based on the Sparc code, and to a lesser extent the PowerPC and
+ * ARM code, which are copyright by their respective authors.
+ */
+
+#include <linux/perf_event.h>
+#include <linux/kprobes.h>
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/mutex.h>
+
+#include <asm/hwrpb.h>
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/irq_regs.h>
+#include <asm/pal.h>
+#include <asm/wrperfmon.h>
+#include <asm/hw_irq.h>
+
+
+/* The maximum number of PMCs on any Alpha CPU whatsoever. */
+#define MAX_HWEVENTS 3
+#define PMC_NO_INDEX -1
+
+/* For tracking PMCs and the hw events they monitor on each CPU. */
+struct cpu_hw_events {
+ int enabled;
+ /* Number of events scheduled; also number entries valid in arrays below. */
+ int n_events;
+ /* Number events added since last hw_perf_disable(). */
+ int n_added;
+ /* Events currently scheduled. */
+ struct perf_event *event[MAX_HWEVENTS];
+ /* Event type of each scheduled event. */
+ unsigned long evtype[MAX_HWEVENTS];
+ /* Current index of each scheduled event; if not yet determined
+ * contains PMC_NO_INDEX.
+ */
+ int current_idx[MAX_HWEVENTS];
+ /* The active PMCs' config for easy use with wrperfmon(). */
+ unsigned long config;
+ /* The active counters' indices for easy use with wrperfmon(). */
+ unsigned long idx_mask;
+};
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+
+
+
+/*
+ * A structure to hold the description of the PMCs available on a particular
+ * type of Alpha CPU.
+ */
+struct alpha_pmu_t {
+ /* Mapping of the perf system hw event types to indigenous event types */
+ const int *event_map;
+ /* The number of entries in the event_map */
+ int max_events;
+ /* The number of PMCs on this Alpha */
+ int num_pmcs;
+ /*
+ * All PMC counters reside in the IBOX register PCTR. This is the
+ * LSB of the counter.
+ */
+ int pmc_count_shift[MAX_HWEVENTS];
+ /*
+ * The mask that isolates the PMC bits when the LSB of the counter
+ * is shifted to bit 0.
+ */
+ unsigned long pmc_count_mask[MAX_HWEVENTS];
+ /* The maximum period the PMC can count. */
+ unsigned long pmc_max_period[MAX_HWEVENTS];
+ /*
+ * The maximum value that may be written to the counter due to
+ * hardware restrictions is pmc_max_period - pmc_left.
+ */
+ long pmc_left[3];
+ /* Subroutine for allocation of PMCs. Enforces constraints. */
+ int (*check_constraints)(struct perf_event **, unsigned long *, int);
+};
+
+/*
+ * The Alpha CPU PMU description currently in operation. This is set during
+ * the boot process to the specific CPU of the machine.
+ */
+static const struct alpha_pmu_t *alpha_pmu;
+
+
+#define HW_OP_UNSUPPORTED -1
+
+/*
+ * The hardware description of the EV67, EV68, EV69, EV7 and EV79 PMUs
+ * follow. Since they are identical we refer to them collectively as the
+ * EV67 henceforth.
+ */
+
+/*
+ * EV67 PMC event types
+ *
+ * There is no one-to-one mapping of the possible hw event types to the
+ * actual codes that are used to program the PMCs hence we introduce our
+ * own hw event type identifiers.
+ */
+enum ev67_pmc_event_type {
+ EV67_CYCLES = 1,
+ EV67_INSTRUCTIONS,
+ EV67_BCACHEMISS,
+ EV67_MBOXREPLAY,
+ EV67_LAST_ET
+};
+#define EV67_NUM_EVENT_TYPES (EV67_LAST_ET-EV67_CYCLES)
+
+
+/* Mapping of the hw event types to the perf tool interface */
+static const int ev67_perfmon_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = EV67_CYCLES,
+ [PERF_COUNT_HW_INSTRUCTIONS] = EV67_INSTRUCTIONS,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_CACHE_MISSES] = EV67_BCACHEMISS,
+};
+
+struct ev67_mapping_t {
+ int config;
+ int idx;
+};
+
+/*
+ * The mapping used for one event only - these must be in same order as enum
+ * ev67_pmc_event_type definition.
+ */
+static const struct ev67_mapping_t ev67_mapping[] = {
+ {EV67_PCTR_INSTR_CYCLES, 1}, /* EV67_CYCLES, */
+ {EV67_PCTR_INSTR_CYCLES, 0}, /* EV67_INSTRUCTIONS */
+ {EV67_PCTR_INSTR_BCACHEMISS, 1}, /* EV67_BCACHEMISS */
+ {EV67_PCTR_CYCLES_MBOX, 1} /* EV67_MBOXREPLAY */
+};
+
+
+/*
+ * Check that a group of events can be simultaneously scheduled on to the
+ * EV67 PMU. Also allocate counter indices and config.
+ */
+static int ev67_check_constraints(struct perf_event **event,
+ unsigned long *evtype, int n_ev)
+{
+ int idx0;
+ unsigned long config;
+
+ idx0 = ev67_mapping[evtype[0]-1].idx;
+ config = ev67_mapping[evtype[0]-1].config;
+ if (n_ev == 1)
+ goto success;
+
+ BUG_ON(n_ev != 2);
+
+ if (evtype[0] == EV67_MBOXREPLAY || evtype[1] == EV67_MBOXREPLAY) {
+ /* MBOX replay traps must be on PMC 1 */
+ idx0 = (evtype[0] == EV67_MBOXREPLAY) ? 1 : 0;
+ /* Only cycles can accompany MBOX replay traps */
+ if (evtype[idx0] == EV67_CYCLES) {
+ config = EV67_PCTR_CYCLES_MBOX;
+ goto success;
+ }
+ }
+
+ if (evtype[0] == EV67_BCACHEMISS || evtype[1] == EV67_BCACHEMISS) {
+ /* Bcache misses must be on PMC 1 */
+ idx0 = (evtype[0] == EV67_BCACHEMISS) ? 1 : 0;
+ /* Only instructions can accompany Bcache misses */
+ if (evtype[idx0] == EV67_INSTRUCTIONS) {
+ config = EV67_PCTR_INSTR_BCACHEMISS;
+ goto success;
+ }
+ }
+
+ if (evtype[0] == EV67_INSTRUCTIONS || evtype[1] == EV67_INSTRUCTIONS) {
+ /* Instructions must be on PMC 0 */
+ idx0 = (evtype[0] == EV67_INSTRUCTIONS) ? 0 : 1;
+ /* By this point only cycles can accompany instructions */
+ if (evtype[idx0^1] == EV67_CYCLES) {
+ config = EV67_PCTR_INSTR_CYCLES;
+ goto success;
+ }
+ }
+
+ /* Otherwise, darn it, there is a conflict. */
+ return -1;
+
+success:
+ event[0]->hw.idx = idx0;
+ event[0]->hw.config_base = config;
+ if (n_ev == 2) {
+ event[1]->hw.idx = idx0 ^ 1;
+ event[1]->hw.config_base = config;
+ }
+ return 0;
+}
+
+
+static const struct alpha_pmu_t ev67_pmu = {
+ .event_map = ev67_perfmon_event_map,
+ .max_events = ARRAY_SIZE(ev67_perfmon_event_map),
+ .num_pmcs = 2,
+ .pmc_count_shift = {EV67_PCTR_0_COUNT_SHIFT, EV67_PCTR_1_COUNT_SHIFT, 0},
+ .pmc_count_mask = {EV67_PCTR_0_COUNT_MASK, EV67_PCTR_1_COUNT_MASK, 0},
+ .pmc_max_period = {(1UL<<20) - 1, (1UL<<20) - 1, 0},
+ .pmc_left = {16, 4, 0},
+ .check_constraints = ev67_check_constraints
+};
+
+
+
+/*
+ * Helper routines to ensure that we read/write only the correct PMC bits
+ * when calling the wrperfmon PALcall.
+ */
+static inline void alpha_write_pmc(int idx, unsigned long val)
+{
+ val &= alpha_pmu->pmc_count_mask[idx];
+ val <<= alpha_pmu->pmc_count_shift[idx];
+ val |= (1<<idx);
+ wrperfmon(PERFMON_CMD_WRITE, val);
+}
+
+static inline unsigned long alpha_read_pmc(int idx)
+{
+ unsigned long val;
+
+ val = wrperfmon(PERFMON_CMD_READ, 0);
+ val >>= alpha_pmu->pmc_count_shift[idx];
+ val &= alpha_pmu->pmc_count_mask[idx];
+ return val;
+}
+
+/* Set a new period to sample over */
+static int alpha_perf_event_set_period(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ long left = local64_read(&hwc->period_left);
+ long period = hwc->sample_period;
+ int ret = 0;
+
+ if (unlikely(left <= -period)) {
+ left = period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+
+ if (unlikely(left <= 0)) {
+ left += period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+
+ /*
+ * Hardware restrictions require that the counters must not be
+ * written with values that are too close to the maximum period.
+ */
+ if (unlikely(left < alpha_pmu->pmc_left[idx]))
+ left = alpha_pmu->pmc_left[idx];
+
+ if (left > (long)alpha_pmu->pmc_max_period[idx])
+ left = alpha_pmu->pmc_max_period[idx];
+
+ local64_set(&hwc->prev_count, (unsigned long)(-left));
+
+ alpha_write_pmc(idx, (unsigned long)(-left));
+
+ perf_event_update_userpage(event);
+
+ return ret;
+}
+
+
+/*
+ * Calculates the count (the 'delta') since the last time the PMC was read.
+ *
+ * As the PMCs' full period can easily be exceeded within the perf system
+ * sampling period we cannot use any high order bits as a guard bit in the
+ * PMCs to detect overflow as is done by other architectures. The code here
+ * calculates the delta on the basis that there is no overflow when ovf is
+ * zero. The value passed via ovf by the interrupt handler corrects for
+ * overflow.
+ *
+ * This can be racey on rare occasions -- a call to this routine can occur
+ * with an overflowed counter just before the PMI service routine is called.
+ * The check for delta negative hopefully always rectifies this situation.
+ */
+static unsigned long alpha_perf_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx, long ovf)
+{
+ long prev_raw_count, new_raw_count;
+ long delta;
+
+again:
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = alpha_read_pmc(idx);
+
+ if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count) != prev_raw_count)
+ goto again;
+
+ delta = (new_raw_count - (prev_raw_count & alpha_pmu->pmc_count_mask[idx])) + ovf;
+
+ /* It is possible on very rare occasions that the PMC has overflowed
+ * but the interrupt is yet to come. Detect and fix this situation.
+ */
+ if (unlikely(delta < 0)) {
+ delta += alpha_pmu->pmc_max_period[idx] + 1;
+ }
+
+ local64_add(delta, &event->count);
+ local64_sub(delta, &hwc->period_left);
+
+ return new_raw_count;
+}
+
+
+/*
+ * Collect all HW events into the array event[].
+ */
+static int collect_events(struct perf_event *group, int max_count,
+ struct perf_event *event[], unsigned long *evtype,
+ int *current_idx)
+{
+ struct perf_event *pe;
+ int n = 0;
+
+ if (!is_software_event(group)) {
+ if (n >= max_count)
+ return -1;
+ event[n] = group;
+ evtype[n] = group->hw.event_base;
+ current_idx[n++] = PMC_NO_INDEX;
+ }
+ list_for_each_entry(pe, &group->sibling_list, group_entry) {
+ if (!is_software_event(pe) && pe->state != PERF_EVENT_STATE_OFF) {
+ if (n >= max_count)
+ return -1;
+ event[n] = pe;
+ evtype[n] = pe->hw.event_base;
+ current_idx[n++] = PMC_NO_INDEX;
+ }
+ }
+ return n;
+}
+
+
+
+/*
+ * Check that a group of events can be simultaneously scheduled on to the PMU.
+ */
+static int alpha_check_constraints(struct perf_event **events,
+ unsigned long *evtypes, int n_ev)
+{
+
+ /* No HW events is possible from hw_perf_group_sched_in(). */
+ if (n_ev == 0)
+ return 0;
+
+ if (n_ev > alpha_pmu->num_pmcs)
+ return -1;
+
+ return alpha_pmu->check_constraints(events, evtypes, n_ev);
+}
+
+
+/*
+ * If new events have been scheduled then update cpuc with the new
+ * configuration. This may involve shifting cycle counts from one PMC to
+ * another.
+ */
+static void maybe_change_configuration(struct cpu_hw_events *cpuc)
+{
+ int j;
+
+ if (cpuc->n_added == 0)
+ return;
+
+ /* Find counters that are moving to another PMC and update */
+ for (j = 0; j < cpuc->n_events; j++) {
+ struct perf_event *pe = cpuc->event[j];
+
+ if (cpuc->current_idx[j] != PMC_NO_INDEX &&
+ cpuc->current_idx[j] != pe->hw.idx) {
+ alpha_perf_event_update(pe, &pe->hw, cpuc->current_idx[j], 0);
+ cpuc->current_idx[j] = PMC_NO_INDEX;
+ }
+ }
+
+ /* Assign to counters all unassigned events. */
+ cpuc->idx_mask = 0;
+ for (j = 0; j < cpuc->n_events; j++) {
+ struct perf_event *pe = cpuc->event[j];
+ struct hw_perf_event *hwc = &pe->hw;
+ int idx = hwc->idx;
+
+ if (cpuc->current_idx[j] == PMC_NO_INDEX) {
+ alpha_perf_event_set_period(pe, hwc, idx);
+ cpuc->current_idx[j] = idx;
+ }
+
+ if (!(hwc->state & PERF_HES_STOPPED))
+ cpuc->idx_mask |= (1<<cpuc->current_idx[j]);
+ }
+ cpuc->config = cpuc->event[0]->hw.config_base;
+}
+
+
+
+/* Schedule perf HW event on to PMU.
+ * - this function is called from outside this module via the pmu struct
+ * returned from perf event initialisation.
+ */
+static int alpha_pmu_add(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ int n0;
+ int ret;
+ unsigned long irq_flags;
+
+ /*
+ * The Sparc code has the IRQ disable first followed by the perf
+ * disable, however this can lead to an overflowed counter with the
+ * PMI disabled on rare occasions. The alpha_perf_event_update()
+ * routine should detect this situation by noting a negative delta,
+ * nevertheless we disable the PMCs first to enable a potential
+ * final PMI to occur before we disable interrupts.
+ */
+ perf_pmu_disable(event->pmu);
+ local_irq_save(irq_flags);
+
+ /* Default to error to be returned */
+ ret = -EAGAIN;
+
+ /* Insert event on to PMU and if successful modify ret to valid return */
+ n0 = cpuc->n_events;
+ if (n0 < alpha_pmu->num_pmcs) {
+ cpuc->event[n0] = event;
+ cpuc->evtype[n0] = event->hw.event_base;
+ cpuc->current_idx[n0] = PMC_NO_INDEX;
+
+ if (!alpha_check_constraints(cpuc->event, cpuc->evtype, n0+1)) {
+ cpuc->n_events++;
+ cpuc->n_added++;
+ ret = 0;
+ }
+ }
+
+ hwc->state = PERF_HES_UPTODATE;
+ if (!(flags & PERF_EF_START))
+ hwc->state |= PERF_HES_STOPPED;
+
+ local_irq_restore(irq_flags);
+ perf_pmu_enable(event->pmu);
+
+ return ret;
+}
+
+
+
+/* Disable performance monitoring unit
+ * - this function is called from outside this module via the pmu struct
+ * returned from perf event initialisation.
+ */
+static void alpha_pmu_del(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned long irq_flags;
+ int j;
+
+ perf_pmu_disable(event->pmu);
+ local_irq_save(irq_flags);
+
+ for (j = 0; j < cpuc->n_events; j++) {
+ if (event == cpuc->event[j]) {
+ int idx = cpuc->current_idx[j];
+
+ /* Shift remaining entries down into the existing
+ * slot.
+ */
+ while (++j < cpuc->n_events) {
+ cpuc->event[j - 1] = cpuc->event[j];
+ cpuc->evtype[j - 1] = cpuc->evtype[j];
+ cpuc->current_idx[j - 1] =
+ cpuc->current_idx[j];
+ }
+
+ /* Absorb the final count and turn off the event. */
+ alpha_perf_event_update(event, hwc, idx, 0);
+ perf_event_update_userpage(event);
+
+ cpuc->idx_mask &= ~(1UL<<idx);
+ cpuc->n_events--;
+ break;
+ }
+ }
+
+ local_irq_restore(irq_flags);
+ perf_pmu_enable(event->pmu);
+}
+
+
+static void alpha_pmu_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ alpha_perf_event_update(event, hwc, hwc->idx, 0);
+}
+
+
+static void alpha_pmu_stop(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ cpuc->idx_mask &= ~(1UL<<hwc->idx);
+ hwc->state |= PERF_HES_STOPPED;
+ }
+
+ if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+ alpha_perf_event_update(event, hwc, hwc->idx, 0);
+ hwc->state |= PERF_HES_UPTODATE;
+ }
+
+ if (cpuc->enabled)
+ wrperfmon(PERFMON_CMD_DISABLE, (1UL<<hwc->idx));
+}
+
+
+static void alpha_pmu_start(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+ return;
+
+ if (flags & PERF_EF_RELOAD) {
+ WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+ alpha_perf_event_set_period(event, hwc, hwc->idx);
+ }
+
+ hwc->state = 0;
+
+ cpuc->idx_mask |= 1UL<<hwc->idx;
+ if (cpuc->enabled)
+ wrperfmon(PERFMON_CMD_ENABLE, (1UL<<hwc->idx));
+}
+
+
+/*
+ * Check that CPU performance counters are supported.
+ * - currently support EV67 and later CPUs.
+ * - actually some later revisions of the EV6 have the same PMC model as the
+ * EV67 but we don't do suffiently deep CPU detection to detect them.
+ * Bad luck to the very few people who might have one, I guess.
+ */
+static int supported_cpu(void)
+{
+ struct percpu_struct *cpu;
+ unsigned long cputype;
+
+ /* Get cpu type from HW */
+ cpu = (struct percpu_struct *)((char *)hwrpb + hwrpb->processor_offset);
+ cputype = cpu->type & 0xffffffff;
+ /* Include all of EV67, EV68, EV7, EV79 and EV69 as supported. */
+ return (cputype >= EV67_CPU) && (cputype <= EV69_CPU);
+}
+
+
+
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+ /* Nothing to be done! */
+ return;
+}
+
+
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ struct hw_perf_event *hwc = &event->hw;
+ struct perf_event *evts[MAX_HWEVENTS];
+ unsigned long evtypes[MAX_HWEVENTS];
+ int idx_rubbish_bin[MAX_HWEVENTS];
+ int ev;
+ int n;
+
+ /* We only support a limited range of HARDWARE event types with one
+ * only programmable via a RAW event type.
+ */
+ if (attr->type == PERF_TYPE_HARDWARE) {
+ if (attr->config >= alpha_pmu->max_events)
+ return -EINVAL;
+ ev = alpha_pmu->event_map[attr->config];
+ } else if (attr->type == PERF_TYPE_HW_CACHE) {
+ return -EOPNOTSUPP;
+ } else if (attr->type == PERF_TYPE_RAW) {
+ ev = attr->config & 0xff;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ if (ev < 0) {
+ return ev;
+ }
+
+ /* The EV67 does not support mode exclusion */
+ if (attr->exclude_kernel || attr->exclude_user
+ || attr->exclude_hv || attr->exclude_idle) {
+ return -EPERM;
+ }
+
+ /*
+ * We place the event type in event_base here and leave calculation
+ * of the codes to programme the PMU for alpha_pmu_enable() because
+ * it is only then we will know what HW events are actually
+ * scheduled on to the PMU. At that point the code to programme the
+ * PMU is put into config_base and the PMC to use is placed into
+ * idx. We initialise idx (below) to PMC_NO_INDEX to indicate that
+ * it is yet to be determined.
+ */
+ hwc->event_base = ev;
+
+ /* Collect events in a group together suitable for calling
+ * alpha_check_constraints() to verify that the group as a whole can
+ * be scheduled on to the PMU.
+ */
+ n = 0;
+ if (event->group_leader != event) {
+ n = collect_events(event->group_leader,
+ alpha_pmu->num_pmcs - 1,
+ evts, evtypes, idx_rubbish_bin);
+ if (n < 0)
+ return -EINVAL;
+ }
+ evtypes[n] = hwc->event_base;
+ evts[n] = event;
+
+ if (alpha_check_constraints(evts, evtypes, n + 1))
+ return -EINVAL;
+
+ /* Indicate that PMU config and idx are yet to be determined. */
+ hwc->config_base = 0;
+ hwc->idx = PMC_NO_INDEX;
+
+ event->destroy = hw_perf_event_destroy;
+
+ /*
+ * Most architectures reserve the PMU for their use at this point.
+ * As there is no existing mechanism to arbitrate usage and there
+ * appears to be no other user of the Alpha PMU we just assume
+ * that we can just use it, hence a NO-OP here.
+ *
+ * Maybe an alpha_reserve_pmu() routine should be implemented but is
+ * anything else ever going to use it?
+ */
+
+ if (!hwc->sample_period) {
+ hwc->sample_period = alpha_pmu->pmc_max_period[0];
+ hwc->last_period = hwc->sample_period;
+ local64_set(&hwc->period_left, hwc->sample_period);
+ }
+
+ return 0;
+}
+
+/*
+ * Main entry point to initialise a HW performance event.
+ */
+static int alpha_pmu_event_init(struct perf_event *event)
+{
+ int err;
+
+ switch (event->attr.type) {
+ case PERF_TYPE_RAW:
+ case PERF_TYPE_HARDWARE:
+ case PERF_TYPE_HW_CACHE:
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (!alpha_pmu)
+ return -ENODEV;
+
+ /* Do the real initialisation work. */
+ err = __hw_perf_event_init(event);
+
+ return err;
+}
+
+/*
+ * Main entry point - enable HW performance counters.
+ */
+static void alpha_pmu_enable(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ if (cpuc->enabled)
+ return;
+
+ cpuc->enabled = 1;
+ barrier();
+
+ if (cpuc->n_events > 0) {
+ /* Update cpuc with information from any new scheduled events. */
+ maybe_change_configuration(cpuc);
+
+ /* Start counting the desired events. */
+ wrperfmon(PERFMON_CMD_LOGGING_OPTIONS, EV67_PCTR_MODE_AGGREGATE);
+ wrperfmon(PERFMON_CMD_DESIRED_EVENTS, cpuc->config);
+ wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+ }
+}
+
+
+/*
+ * Main entry point - disable HW performance counters.
+ */
+
+static void alpha_pmu_disable(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ if (!cpuc->enabled)
+ return;
+
+ cpuc->enabled = 0;
+ cpuc->n_added = 0;
+
+ wrperfmon(PERFMON_CMD_DISABLE, cpuc->idx_mask);
+}
+
+static struct pmu pmu = {
+ .pmu_enable = alpha_pmu_enable,
+ .pmu_disable = alpha_pmu_disable,
+ .event_init = alpha_pmu_event_init,
+ .add = alpha_pmu_add,
+ .del = alpha_pmu_del,
+ .start = alpha_pmu_start,
+ .stop = alpha_pmu_stop,
+ .read = alpha_pmu_read,
+};
+
+
+/*
+ * Main entry point - don't know when this is called but it
+ * obviously dumps debug info.
+ */
+void perf_event_print_debug(void)
+{
+ unsigned long flags;
+ unsigned long pcr;
+ int pcr0, pcr1;
+ int cpu;
+
+ if (!supported_cpu())
+ return;
+
+ local_irq_save(flags);
+
+ cpu = smp_processor_id();
+
+ pcr = wrperfmon(PERFMON_CMD_READ, 0);
+ pcr0 = (pcr >> alpha_pmu->pmc_count_shift[0]) & alpha_pmu->pmc_count_mask[0];
+ pcr1 = (pcr >> alpha_pmu->pmc_count_shift[1]) & alpha_pmu->pmc_count_mask[1];
+
+ pr_info("CPU#%d: PCTR0[%06x] PCTR1[%06x]\n", cpu, pcr0, pcr1);
+
+ local_irq_restore(flags);
+}
+
+
+/*
+ * Performance Monitoring Interrupt Service Routine called when a PMC
+ * overflows. The PMC that overflowed is passed in la_ptr.
+ */
+static void alpha_perf_event_irq_handler(unsigned long la_ptr,
+ struct pt_regs *regs)
+{
+ struct cpu_hw_events *cpuc;
+ struct perf_sample_data data;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ int idx, j;
+
+ __get_cpu_var(irq_pmi_count)++;
+ cpuc = &__get_cpu_var(cpu_hw_events);
+
+ /* Completely counting through the PMC's period to trigger a new PMC
+ * overflow interrupt while in this interrupt routine is utterly
+ * disastrous! The EV6 and EV67 counters are sufficiently large to
+ * prevent this but to be really sure disable the PMCs.
+ */
+ wrperfmon(PERFMON_CMD_DISABLE, cpuc->idx_mask);
+
+ /* la_ptr is the counter that overflowed. */
+ if (unlikely(la_ptr >= alpha_pmu->num_pmcs)) {
+ /* This should never occur! */
+ irq_err_count++;
+ pr_warning("PMI: silly index %ld\n", la_ptr);
+ wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+ return;
+ }
+
+ idx = la_ptr;
+
+ perf_sample_data_init(&data, 0);
+ for (j = 0; j < cpuc->n_events; j++) {
+ if (cpuc->current_idx[j] == idx)
+ break;
+ }
+
+ if (unlikely(j == cpuc->n_events)) {
+ /* This can occur if the event is disabled right on a PMC overflow. */
+ wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+ return;
+ }
+
+ event = cpuc->event[j];
+
+ if (unlikely(!event)) {
+ /* This should never occur! */
+ irq_err_count++;
+ pr_warning("PMI: No event at index %d!\n", idx);
+ wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+ return;
+ }
+
+ hwc = &event->hw;
+ alpha_perf_event_update(event, hwc, idx, alpha_pmu->pmc_max_period[idx]+1);
+ data.period = event->hw.last_period;
+
+ if (alpha_perf_event_set_period(event, hwc, idx)) {
+ if (perf_event_overflow(event, 1, &data, regs)) {
+ /* Interrupts coming too quickly; "throttle" the
+ * counter, i.e., disable it for a little while.
+ */
+ alpha_pmu_stop(event, 0);
+ }
+ }
+ wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+
+ return;
+}
+
+
+
+/*
+ * Init call to initialise performance events at kernel startup.
+ */
+void __init init_hw_perf_events(void)
+{
+ pr_info("Performance events: ");
+
+ if (!supported_cpu()) {
+ pr_cont("No support for your CPU.\n");
+ return;
+ }
+
+ pr_cont("Supported CPU type!\n");
+
+ /* Override performance counter IRQ vector */
+
+ perf_irq = alpha_perf_event_irq_handler;
+
+ /* And set up PMU specification */
+ alpha_pmu = &ev67_pmu;
+
+ perf_pmu_register(&pmu);
+}
+
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index 395a464353b..3ec35066f1d 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -356,7 +356,7 @@ dump_elf_thread(elf_greg_t *dest, struct pt_regs *pt, struct thread_info *ti)
dest[27] = pt->r27;
dest[28] = pt->r28;
dest[29] = pt->gp;
- dest[30] = rdusp();
+ dest[30] = ti == current_thread_info() ? rdusp() : ti->pcb.usp;
dest[31] = pt->pc;
/* Once upon a time this was the PS value. Which is stupid
@@ -387,8 +387,9 @@ EXPORT_SYMBOL(dump_elf_task_fp);
* sys_execve() executes a new program.
*/
asmlinkage int
-do_sys_execve(char __user *ufilename, char __user * __user *argv,
- char __user * __user *envp, struct pt_regs *regs)
+do_sys_execve(const char __user *ufilename,
+ const char __user *const __user *argv,
+ const char __user *const __user *envp, struct pt_regs *regs)
{
int error;
char *filename;
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index 3d2627ec986..d3e52d3fd59 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -156,9 +156,6 @@ extern void SMC669_Init(int);
/* es1888.c */
extern void es1888_init(void);
-/* ns87312.c */
-extern void ns87312_enable_ide(long ide_base);
-
/* ../lib/fpreg.c */
extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
extern unsigned long alpha_read_fp_reg (unsigned long reg);
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index baa903602f6..e2af5eb59bb 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -269,7 +269,8 @@ void ptrace_disable(struct task_struct *child)
user_disable_single_step(child);
}
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
{
unsigned long tmp;
size_t copied;
@@ -292,7 +293,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
case PTRACE_PEEKUSR:
force_successful_syscall_return();
ret = get_reg(child, addr);
- DBG(DBG_MEM, ("peek $%ld->%#lx\n", addr, ret));
+ DBG(DBG_MEM, ("peek $%lu->%#lx\n", addr, ret));
break;
/* When I and D space are separate, this will have to be fixed. */
@@ -302,7 +303,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
case PTRACE_POKEUSR: /* write the specified register */
- DBG(DBG_MEM, ("poke $%ld<-%#lx\n", addr, data));
+ DBG(DBG_MEM, ("poke $%lu<-%#lx\n", addr, data));
ret = put_reg(child, addr, data);
break;
default:
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
index 0932dbb1ef8..6f7feb5db27 100644
--- a/arch/alpha/kernel/signal.c
+++ b/arch/alpha/kernel/signal.c
@@ -41,46 +41,20 @@ static void do_signal(struct pt_regs *, struct switch_stack *,
/*
* The OSF/1 sigprocmask calling sequence is different from the
* C sigprocmask() sequence..
- *
- * how:
- * 1 - SIG_BLOCK
- * 2 - SIG_UNBLOCK
- * 3 - SIG_SETMASK
- *
- * We change the range to -1 .. 1 in order to let gcc easily
- * use the conditional move instructions.
- *
- * Note that we don't need to acquire the kernel lock for SMP
- * operation, as all of this is local to this thread.
*/
-SYSCALL_DEFINE3(osf_sigprocmask, int, how, unsigned long, newmask,
- struct pt_regs *, regs)
+SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask)
{
- unsigned long oldmask = -EINVAL;
-
- if ((unsigned long)how-1 <= 2) {
- long sign = how-2; /* -1 .. 1 */
- unsigned long block, unblock;
-
- newmask &= _BLOCKABLE;
- spin_lock_irq(&current->sighand->siglock);
- oldmask = current->blocked.sig[0];
-
- unblock = oldmask & ~newmask;
- block = oldmask | newmask;
- if (!sign)
- block = unblock;
- if (sign <= 0)
- newmask = block;
- if (_NSIG_WORDS > 1 && sign > 0)
- sigemptyset(&current->blocked);
- current->blocked.sig[0] = newmask;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- regs->r0 = 0; /* special no error return */
+ sigset_t oldmask;
+ sigset_t mask;
+ unsigned long res;
+
+ siginitset(&mask, newmask & _BLOCKABLE);
+ res = sigprocmask(how, &mask, &oldmask);
+ if (!res) {
+ force_successful_syscall_return();
+ res = oldmask.sig[0];
}
- return oldmask;
+ return res;
}
SYSCALL_DEFINE3(osf_sigaction, int, sig,
@@ -94,9 +68,9 @@ SYSCALL_DEFINE3(osf_sigaction, int, sig,
old_sigset_t mask;
if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
- __get_user(new_ka.sa.sa_flags, &act->sa_flags))
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+ __get_user(mask, &act->sa_mask))
return -EFAULT;
- __get_user(mask, &act->sa_mask);
siginitset(&new_ka.sa.sa_mask, mask);
new_ka.ka_restorer = NULL;
}
@@ -106,9 +80,9 @@ SYSCALL_DEFINE3(osf_sigaction, int, sig,
if (!ret && oact) {
if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
- __put_user(old_ka.sa.sa_flags, &oact->sa_flags))
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
return -EFAULT;
- __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
@@ -144,8 +118,7 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act,
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
-asmlinkage int
-do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw)
+SYSCALL_DEFINE1(sigsuspend, old_sigset_t, mask)
{
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
@@ -154,41 +127,6 @@ do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- /* Indicate EINTR on return from any possible signal handler,
- which will not come back through here, but via sigreturn. */
- regs->r0 = EINTR;
- regs->r19 = 1;
-
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- set_thread_flag(TIF_RESTORE_SIGMASK);
- return -ERESTARTNOHAND;
-}
-
-asmlinkage int
-do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
- struct pt_regs *regs, struct switch_stack *sw)
-{
- sigset_t set;
-
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (sigsetsize != sizeof(sigset_t))
- return -EINVAL;
- if (copy_from_user(&set, uset, sizeof(set)))
- return -EFAULT;
-
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(&current->sighand->siglock);
- current->saved_sigmask = current->blocked;
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- /* Indicate EINTR on return from any possible signal handler,
- which will not come back through here, but via sigreturn. */
- regs->r0 = EINTR;
- regs->r19 = 1;
-
current->state = TASK_INTERRUPTIBLE;
schedule();
set_thread_flag(TIF_RESTORE_SIGMASK);
@@ -239,6 +177,8 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
unsigned long usp;
long i, err = __get_user(regs->pc, &sc->sc_pc);
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
sw->r26 = (unsigned long) ret_from_sys_call;
err |= __get_user(regs->r0, sc->sc_regs+0);
@@ -591,7 +531,6 @@ syscall_restart(unsigned long r0, unsigned long r19,
regs->pc -= 4;
break;
case ERESTART_RESTARTBLOCK:
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
regs->r0 = EINTR;
break;
}
diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c
index 4afc1a1e2e5..f0df3fbd840 100644
--- a/arch/alpha/kernel/srm_env.c
+++ b/arch/alpha/kernel/srm_env.c
@@ -87,7 +87,7 @@ static int srm_env_proc_show(struct seq_file *m, void *v)
srm_env_t *entry;
char *page;
- entry = (srm_env_t *)m->private;
+ entry = m->private;
page = (char *)__get_free_page(GFP_USER);
if (!page)
return -ENOMEM;
diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c
index affd0f3f25d..14c8898d19e 100644
--- a/arch/alpha/kernel/sys_cabriolet.c
+++ b/arch/alpha/kernel/sys_cabriolet.c
@@ -33,7 +33,7 @@
#include "irq_impl.h"
#include "pci_impl.h"
#include "machvec_impl.h"
-
+#include "pc873xx.h"
/* Note mask bit is true for DISABLED irqs. */
static unsigned long cached_irq_mask = ~0UL;
@@ -236,17 +236,30 @@ cabriolet_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
}
static inline void __init
+cabriolet_enable_ide(void)
+{
+ if (pc873xx_probe() == -1) {
+ printk(KERN_ERR "Probing for PC873xx Super IO chip failed.\n");
+ } else {
+ printk(KERN_INFO "Found %s Super IO chip at 0x%x\n",
+ pc873xx_get_model(), pc873xx_get_base());
+
+ pc873xx_enable_ide();
+ }
+}
+
+static inline void __init
cabriolet_init_pci(void)
{
common_init_pci();
- ns87312_enable_ide(0x398);
+ cabriolet_enable_ide();
}
static inline void __init
cia_cab_init_pci(void)
{
cia_init_pci();
- ns87312_enable_ide(0x398);
+ cabriolet_enable_ide();
}
/*
diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c
index 230464885b5..4da596b6adb 100644
--- a/arch/alpha/kernel/sys_takara.c
+++ b/arch/alpha/kernel/sys_takara.c
@@ -29,7 +29,7 @@
#include "irq_impl.h"
#include "pci_impl.h"
#include "machvec_impl.h"
-
+#include "pc873xx.h"
/* Note mask bit is true for DISABLED irqs. */
static unsigned long cached_irq_mask[2] = { -1, -1 };
@@ -264,7 +264,14 @@ takara_init_pci(void)
alpha_mv.pci_map_irq = takara_map_irq_srm;
cia_init_pci();
- ns87312_enable_ide(0x26e);
+
+ if (pc873xx_probe() == -1) {
+ printk(KERN_ERR "Probing for PC873xx Super IO chip failed.\n");
+ } else {
+ printk(KERN_INFO "Found %s Super IO chip at 0x%x\n",
+ pc873xx_get_model(), pc873xx_get_base());
+ pc873xx_enable_ide();
+ }
}
diff --git a/arch/alpha/kernel/systbls.S b/arch/alpha/kernel/systbls.S
index 09acb786e72..a6a1de9db16 100644
--- a/arch/alpha/kernel/systbls.S
+++ b/arch/alpha/kernel/systbls.S
@@ -58,7 +58,7 @@ sys_call_table:
.quad sys_open /* 45 */
.quad alpha_ni_syscall
.quad sys_getxgid
- .quad osf_sigprocmask
+ .quad sys_osf_sigprocmask
.quad alpha_ni_syscall
.quad alpha_ni_syscall /* 50 */
.quad sys_acct
@@ -512,6 +512,9 @@ sys_call_table:
.quad sys_pwritev
.quad sys_rt_tgsigqueueinfo
.quad sys_perf_event_open
+ .quad sys_fanotify_init
+ .quad sys_fanotify_mark /* 495 */
+ .quad sys_prlimit64
.size sys_call_table, . - sys_call_table
.type sys_call_table, @object
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index 1efbed82c0f..0f1d8493cfc 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -41,6 +41,7 @@
#include <linux/init.h>
#include <linux/bcd.h>
#include <linux/profile.h>
+#include <linux/irq_work.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -82,6 +83,26 @@ static struct {
unsigned long est_cycle_freq;
+#ifdef CONFIG_IRQ_WORK
+
+DEFINE_PER_CPU(u8, irq_work_pending);
+
+#define set_irq_work_pending_flag() __get_cpu_var(irq_work_pending) = 1
+#define test_irq_work_pending() __get_cpu_var(irq_work_pending)
+#define clear_irq_work_pending() __get_cpu_var(irq_work_pending) = 0
+
+void set_irq_work_pending(void)
+{
+ set_irq_work_pending_flag();
+}
+
+#else /* CONFIG_IRQ_WORK */
+
+#define test_irq_work_pending() 0
+#define clear_irq_work_pending()
+
+#endif /* CONFIG_IRQ_WORK */
+
static inline __u32 rpcc(void)
{
@@ -170,6 +191,11 @@ irqreturn_t timer_interrupt(int irq, void *dev)
write_sequnlock(&xtime_lock);
+ if (test_irq_work_pending()) {
+ clear_irq_work_pending();
+ irq_work_run();
+ }
+
#ifndef CONFIG_SMP
while (nticks--)
update_process_times(user_mode(get_irq_regs()));
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index b14f015008a..0414e021a91 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -13,7 +13,6 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/delay.h>
-#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
@@ -623,7 +622,6 @@ do_entUna(void * va, unsigned long opcode, unsigned long reg,
return;
}
- lock_kernel();
printk("Bad unaligned kernel access at %016lx: %p %lx %lu\n",
pc, va, opcode, reg);
do_exit(SIGSEGV);
@@ -646,7 +644,6 @@ got_exception:
* Yikes! No one to forward the exception to.
* Since the registers are in a weird format, dump them ourselves.
*/
- lock_kernel();
printk("%s(%d): unhandled unaligned exception\n",
current->comm, task_pid_nr(current));