aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig4
-rw-r--r--drivers/misc/eeprom/at24.c67
-rw-r--r--drivers/misc/eeprom/at25.c58
-rw-r--r--drivers/misc/sgi-gru/Makefile2
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h22
-rw-r--r--drivers/misc/sgi-gru/grufault.c130
-rw-r--r--drivers/misc/sgi-gru/grufile.c36
-rw-r--r--drivers/misc/sgi-gru/gruhandles.c183
-rw-r--r--drivers/misc/sgi-gru/gruhandles.h178
-rw-r--r--drivers/misc/sgi-gru/grukservices.c131
-rw-r--r--drivers/misc/sgi-gru/grukservices.h33
-rw-r--r--drivers/misc/sgi-gru/grumain.c84
-rw-r--r--drivers/misc/sgi-gru/gruprocfs.c45
-rw-r--r--drivers/misc/sgi-gru/grutables.h41
-rw-r--r--drivers/misc/sgi-gru/grutlbpurge.c7
-rw-r--r--drivers/misc/sgi-xp/xpc.h33
-rw-r--r--drivers/misc/sgi-xp/xpc_channel.c8
-rw-r--r--drivers/misc/sgi-xp/xpc_main.c6
-rw-r--r--drivers/misc/sgi-xp/xpc_sn2.c20
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c229
20 files changed, 899 insertions, 418 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5f3bff43462..0b92b2f6ea6 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -165,7 +165,7 @@ config SGI_XP
depends on (IA64_GENERIC || IA64_SGI_SN2 || IA64_SGI_UV || X86_UV) && SMP
select IA64_UNCACHED_ALLOCATOR if IA64_GENERIC || IA64_SGI_SN2
select GENERIC_ALLOCATOR if IA64_GENERIC || IA64_SGI_SN2
- select SGI_GRU if (IA64_GENERIC || IA64_SGI_UV || X86_64) && SMP
+ select SGI_GRU if X86_64 && SMP
---help---
An SGI machine can be divided into multiple Single System
Images which act independently of each other and have
@@ -189,7 +189,7 @@ config HP_ILO
config SGI_GRU
tristate "SGI GRU driver"
- depends on (X86_UV || IA64_SGI_UV || IA64_GENERIC) && SMP
+ depends on X86_UV && SMP
default n
select MMU_NOTIFIER
---help---
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index d4775528abc..d184dfab963 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -53,6 +53,7 @@
struct at24_data {
struct at24_platform_data chip;
+ struct memory_accessor macc;
bool use_smbus;
/*
@@ -225,14 +226,11 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
return status;
}
-static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
+static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
- struct at24_data *at24;
ssize_t retval = 0;
- at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
-
if (unlikely(!count))
return count;
@@ -262,12 +260,14 @@ static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
return retval;
}
+static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct at24_data *at24;
-/*
- * REVISIT: export at24_bin{read,write}() to let other kernel code use
- * eeprom data. For example, it might hold a board's Ethernet address, or
- * board-specific calibration data generated on the manufacturing floor.
- */
+ at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ return at24_read(at24, buf, off, count);
+}
/*
@@ -347,14 +347,11 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, char *buf,
return -ETIMEDOUT;
}
-static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
+static ssize_t at24_write(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
- struct at24_data *at24;
ssize_t retval = 0;
- at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
-
if (unlikely(!count))
return count;
@@ -384,6 +381,39 @@ static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
return retval;
}
+static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct at24_data *at24;
+
+ at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ return at24_write(at24, buf, off, count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This lets other kernel code access the eeprom data. For example, it
+ * might hold a board's Ethernet address, or board-specific calibration
+ * data generated on the manufacturing floor.
+ */
+
+static ssize_t at24_macc_read(struct memory_accessor *macc, char *buf,
+ off_t offset, size_t count)
+{
+ struct at24_data *at24 = container_of(macc, struct at24_data, macc);
+
+ return at24_read(at24, buf, offset, count);
+}
+
+static ssize_t at24_macc_write(struct memory_accessor *macc, char *buf,
+ off_t offset, size_t count)
+{
+ struct at24_data *at24 = container_of(macc, struct at24_data, macc);
+
+ return at24_write(at24, buf, offset, count);
+}
+
/*-------------------------------------------------------------------------*/
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -413,6 +443,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
* is recommended anyhow.
*/
chip.page_size = 1;
+
+ chip.setup = NULL;
+ chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
@@ -463,6 +496,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
+ at24->macc.read = at24_macc_read;
+
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter,
@@ -470,6 +505,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
unsigned write_max = chip.page_size;
+ at24->macc.write = at24_macc_write;
+
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
@@ -520,6 +557,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->write_max,
use_smbus ? ", use_smbus" : "");
+ /* export data to kernel code */
+ if (chip.setup)
+ chip.setup(&at24->macc, chip.context);
+
return 0;
err_clients:
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 290dbe99647..6bc0dac5c1e 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -30,6 +30,7 @@
struct at25_data {
struct spi_device *spi;
+ struct memory_accessor mem;
struct mutex lock;
struct spi_eeprom chip;
struct bin_attribute bin;
@@ -75,6 +76,13 @@ at25_ee_read(
struct spi_transfer t[2];
struct spi_message m;
+ if (unlikely(offset >= at25->bin.size))
+ return 0;
+ if ((offset + count) > at25->bin.size)
+ count = at25->bin.size - offset;
+ if (unlikely(!count))
+ return count;
+
cp = command;
*cp++ = AT25_READ;
@@ -127,13 +135,6 @@ at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr,
dev = container_of(kobj, struct device, kobj);
at25 = dev_get_drvdata(dev);
- if (unlikely(off >= at25->bin.size))
- return 0;
- if ((off + count) > at25->bin.size)
- count = at25->bin.size - off;
- if (unlikely(!count))
- return count;
-
return at25_ee_read(at25, buf, off, count);
}
@@ -146,6 +147,13 @@ at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count)
unsigned buf_size;
u8 *bounce;
+ if (unlikely(off >= at25->bin.size))
+ return -EFBIG;
+ if ((off + count) > at25->bin.size)
+ count = at25->bin.size - off;
+ if (unlikely(!count))
+ return count;
+
/* Temp buffer starts with command and address */
buf_size = at25->chip.page_size;
if (buf_size > io_limit)
@@ -253,18 +261,31 @@ at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr,
dev = container_of(kobj, struct device, kobj);
at25 = dev_get_drvdata(dev);
- if (unlikely(off >= at25->bin.size))
- return -EFBIG;
- if ((off + count) > at25->bin.size)
- count = at25->bin.size - off;
- if (unlikely(!count))
- return count;
-
return at25_ee_write(at25, buf, off, count);
}
/*-------------------------------------------------------------------------*/
+/* Let in-kernel code access the eeprom data. */
+
+static ssize_t at25_mem_read(struct memory_accessor *mem, char *buf,
+ off_t offset, size_t count)
+{
+ struct at25_data *at25 = container_of(mem, struct at25_data, mem);
+
+ return at25_ee_read(at25, buf, offset, count);
+}
+
+static ssize_t at25_mem_write(struct memory_accessor *mem, char *buf,
+ off_t offset, size_t count)
+{
+ struct at25_data *at25 = container_of(mem, struct at25_data, mem);
+
+ return at25_ee_write(at25, buf, offset, count);
+}
+
+/*-------------------------------------------------------------------------*/
+
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
@@ -317,6 +338,10 @@ static int at25_probe(struct spi_device *spi)
at25->addrlen = addrlen;
/* Export the EEPROM bytes through sysfs, since that's convenient.
+ * And maybe to other kernel code; it might hold a board's Ethernet
+ * address, or board-specific calibration data generated on the
+ * manufacturing floor.
+ *
* Default to root-only access to the data; EEPROMs often hold data
* that's sensitive for read and/or write, like ethernet addresses,
* security codes, board-specific manufacturing calibrations, etc.
@@ -324,17 +349,22 @@ static int at25_probe(struct spi_device *spi)
at25->bin.attr.name = "eeprom";
at25->bin.attr.mode = S_IRUSR;
at25->bin.read = at25_bin_read;
+ at25->mem.read = at25_mem_read;
at25->bin.size = at25->chip.byte_len;
if (!(chip->flags & EE_READONLY)) {
at25->bin.write = at25_bin_write;
at25->bin.attr.mode |= S_IWUSR;
+ at25->mem.write = at25_mem_write;
}
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
if (err)
goto fail;
+ if (chip->setup)
+ chip->setup(&at25->mem, chip->context);
+
dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
(at25->bin.size < 1024)
? at25->bin.size
diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile
index 9e9170b3599..bcd8136d2f9 100644
--- a/drivers/misc/sgi-gru/Makefile
+++ b/drivers/misc/sgi-gru/Makefile
@@ -3,5 +3,5 @@ ifdef CONFIG_SGI_GRU_DEBUG
endif
obj-$(CONFIG_SGI_GRU) := gru.o
-gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o
+gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
index 48762e7b98b..3fde33c1e8f 100644
--- a/drivers/misc/sgi-gru/gru_instructions.h
+++ b/drivers/misc/sgi-gru/gru_instructions.h
@@ -19,8 +19,11 @@
#ifndef __GRU_INSTRUCTIONS_H__
#define __GRU_INSTRUCTIONS_H__
-#define gru_flush_cache_hook(p)
-#define gru_emulator_wait_hook(p, w)
+extern int gru_check_status_proc(void *cb);
+extern int gru_wait_proc(void *cb);
+extern void gru_wait_abort_proc(void *cb);
+
+
/*
* Architecture dependent functions
@@ -29,16 +32,16 @@
#if defined(CONFIG_IA64)
#include <linux/compiler.h>
#include <asm/intrinsics.h>
-#define __flush_cache(p) ia64_fc(p)
+#define __flush_cache(p) ia64_fc((unsigned long)p)
/* Use volatile on IA64 to ensure ordering via st4.rel */
-#define gru_ordered_store_int(p,v) \
+#define gru_ordered_store_int(p, v) \
do { \
barrier(); \
*((volatile int *)(p)) = v; /* force st.rel */ \
} while (0)
#elif defined(CONFIG_X86_64)
#define __flush_cache(p) clflush(p)
-#define gru_ordered_store_int(p,v) \
+#define gru_ordered_store_int(p, v) \
do { \
barrier(); \
*(int *)p = v; \
@@ -558,20 +561,19 @@ extern int gru_get_cb_exception_detail(void *cb,
#define GRU_EXC_STR_SIZE 256
-extern int gru_check_status_proc(void *cb);
-extern int gru_wait_proc(void *cb);
-extern void gru_wait_abort_proc(void *cb);
/*
* Control block definition for checking status
*/
struct gru_control_block_status {
unsigned int icmd :1;
- unsigned int unused1 :31;
+ unsigned int ima :3;
+ unsigned int reserved0 :4;
+ unsigned int unused1 :24;
unsigned int unused2 :24;
unsigned int istatus :2;
unsigned int isubstatus :4;
- unsigned int inused3 :2;
+ unsigned int unused3 :2;
};
/* Get CB status */
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 3ee698ad859..ab118558552 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -32,6 +32,7 @@
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/security.h>
#include <asm/pgtable.h>
#include "gru.h"
#include "grutables.h"
@@ -266,6 +267,44 @@ err:
return 1;
}
+static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
+ int write, int atomic, unsigned long *gpa, int *pageshift)
+{
+ struct mm_struct *mm = gts->ts_mm;
+ struct vm_area_struct *vma;
+ unsigned long paddr;
+ int ret, ps;
+
+ vma = find_vma(mm, vaddr);
+ if (!vma)
+ goto inval;
+
+ /*
+ * Atomic lookup is faster & usually works even if called in non-atomic
+ * context.
+ */
+ rmb(); /* Must/check ms_range_active before loading PTEs */
+ ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps);
+ if (ret) {
+ if (atomic)
+ goto upm;
+ if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
+ goto inval;
+ }
+ if (is_gru_paddr(paddr))
+ goto inval;
+ paddr = paddr & ~((1UL << ps) - 1);
+ *gpa = uv_soc_phys_ram_to_gpa(paddr);
+ *pageshift = ps;
+ return 0;
+
+inval:
+ return -1;
+upm:
+ return -2;
+}
+
+
/*
* Drop a TLB entry into the GRU. The fault is described by info in an TFH.
* Input:
@@ -280,10 +319,8 @@ static int gru_try_dropin(struct gru_thread_state *gts,
struct gru_tlb_fault_handle *tfh,
unsigned long __user *cb)
{
- struct mm_struct *mm = gts->ts_mm;
- struct vm_area_struct *vma;
- int pageshift, asid, write, ret;
- unsigned long paddr, gpa, vaddr;
+ int pageshift = 0, asid, write, ret, atomic = !cb;
+ unsigned long gpa = 0, vaddr = 0;
/*
* NOTE: The GRU contains magic hardware that eliminates races between
@@ -317,28 +354,19 @@ static int gru_try_dropin(struct gru_thread_state *gts,
if (atomic_read(&gts->ts_gms->ms_range_active))
goto failactive;
- vma = find_vma(mm, vaddr);
- if (!vma)
+ ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
+ if (ret == -1)
goto failinval;
+ if (ret == -2)
+ goto failupm;
- /*
- * Atomic lookup is faster & usually works even if called in non-atomic
- * context.
- */
- rmb(); /* Must/check ms_range_active before loading PTEs */
- ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &pageshift);
- if (ret) {
- if (!cb)
+ if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
+ gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
+ if (atomic || !gru_update_cch(gts, 0)) {
+ gts->ts_force_cch_reload = 1;
goto failupm;
- if (non_atomic_pte_lookup(vma, vaddr, write, &paddr,
- &pageshift))
- goto failinval;
+ }
}
- if (is_gru_paddr(paddr))
- goto failinval;
-
- paddr = paddr & ~((1UL << pageshift) - 1);
- gpa = uv_soc_phys_ram_to_gpa(paddr);
gru_cb_set_istatus_active(cb);
tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
GRU_PAGESIZE(pageshift));
@@ -368,6 +396,7 @@ failupm:
failfmm:
/* FMM state on UPM call */
+ gru_flush_cache(tfh);
STAT(tlb_dropin_fail_fmm);
gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state);
return 0;
@@ -448,6 +477,7 @@ irqreturn_t gru_intr(int irq, void *dev_id)
up_read(&gts->ts_mm->mmap_sem);
} else {
tfh_user_polling_mode(tfh);
+ STAT(intr_mm_lock_failed);
}
}
return IRQ_HANDLED;
@@ -497,10 +527,8 @@ int gru_handle_user_call_os(unsigned long cb)
if (!gts)
return -EINVAL;
- if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
- ret = -EINVAL;
+ if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE)
goto exit;
- }
/*
* If force_unload is set, the UPM TLB fault is phony. The task
@@ -508,6 +536,20 @@ int gru_handle_user_call_os(unsigned long cb)
* unload the context. The task will page fault and assign a new
* context.
*/
+ if (gts->ts_tgid_owner == current->tgid && gts->ts_blade >= 0 &&
+ gts->ts_blade != uv_numa_blade_id()) {
+ STAT(call_os_offnode_reference);
+ gts->ts_force_unload = 1;
+ }
+
+ /*
+ * CCH may contain stale data if ts_force_cch_reload is set.
+ */
+ if (gts->ts_gru && gts->ts_force_cch_reload) {
+ gru_update_cch(gts, 0);
+ gts->ts_force_cch_reload = 0;
+ }
+
ret = -EAGAIN;
cbrnum = thread_cbr_number(gts, ucbnum);
if (gts->ts_force_unload) {
@@ -541,11 +583,13 @@ int gru_get_exception_detail(unsigned long arg)
if (!gts)
return -EINVAL;
- if (gts->ts_gru) {
- ucbnum = get_cb_number((void *)excdet.cb);
+ ucbnum = get_cb_number((void *)excdet.cb);
+ if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
+ ret = -EINVAL;
+ } else if (gts->ts_gru) {
cbrnum = thread_cbr_number(gts, ucbnum);
cbe = get_cbe_by_index(gts->ts_gru, cbrnum);
- prefetchw(cbe); /* Harmless on hardware, required for emulator */
+ prefetchw(cbe);/* Harmless on hardware, required for emulator */
excdet.opc = cbe->opccpy;
excdet.exopc = cbe->exopccpy;
excdet.ecause = cbe->ecause;
@@ -567,6 +611,31 @@ int gru_get_exception_detail(unsigned long arg)
/*
* User request to unload a context. Content is saved for possible reload.
*/
+static int gru_unload_all_contexts(void)
+{
+ struct gru_thread_state *gts;
+ struct gru_state *gru;
+ int gid, ctxnum;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ foreach_gid(gid) {
+ gru = GID_TO_GRU(gid);
+ spin_lock(&gru->gs_lock);
+ for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
+ gts = gru->gs_gts[ctxnum];
+ if (gts && mutex_trylock(&gts->ts_ctxlock)) {
+ spin_unlock(&gru->gs_lock);
+ gru_unload_context(gts, 1);
+ gru_unlock_gts(gts);
+ spin_lock(&gru->gs_lock);
+ }
+ }
+ spin_unlock(&gru->gs_lock);
+ }
+ return 0;
+}
+
int gru_user_unload_context(unsigned long arg)
{
struct gru_thread_state *gts;
@@ -578,6 +647,9 @@ int gru_user_unload_context(unsigned long arg)
gru_dbg(grudev, "gseg 0x%lx\n", req.gseg);
+ if (!req.gseg)
+ return gru_unload_all_contexts();
+
gts = gru_find_lock_gts(req.gseg);
if (!gts)
return -EINVAL;
@@ -609,7 +681,7 @@ int gru_user_flush_tlb(unsigned long arg)
if (!gts)
return -EINVAL;
- gru_flush_tlb_range(gts->ts_gms, req.vaddr, req.vaddr + req.len);
+ gru_flush_tlb_range(gts->ts_gms, req.vaddr, req.len);
gru_unlock_gts(gts);
return 0;
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
index c67e4e8bd62..3e6e42d2f01 100644
--- a/drivers/misc/sgi-gru/grufile.c
+++ b/drivers/misc/sgi-gru/grufile.c
@@ -45,7 +45,9 @@
#include <asm/uv/uv_mmrs.h>
struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly;
-unsigned long gru_start_paddr, gru_end_paddr __read_mostly;
+unsigned long gru_start_paddr __read_mostly;
+unsigned long gru_end_paddr __read_mostly;
+unsigned int gru_max_gids __read_mostly;
struct gru_stats_s gru_stats;
/* Guaranteed user available resources on each node */
@@ -101,7 +103,7 @@ static int gru_file_mmap(struct file *file, struct vm_area_struct *vma)
return -EPERM;
if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) ||
- vma->vm_end & (GRU_GSEG_PAGESIZE - 1))
+ vma->vm_end & (GRU_GSEG_PAGESIZE - 1))
return -EINVAL;
vma->vm_flags |=
@@ -273,8 +275,11 @@ static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr,
gru->gs_blade_id = bid;
gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1;
gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1;
+ gru->gs_asid_limit = MAX_ASID;
gru_tgh_flush_init(gru);
- gru_dbg(grudev, "bid %d, nid %d, gru %x, vaddr %p (0x%lx)\n",
+ if (gru->gs_gid >= gru_max_gids)
+ gru_max_gids = gru->gs_gid + 1;
+ gru_dbg(grudev, "bid %d, nid %d, gid %d, vaddr %p (0x%lx)\n",
bid, nid, gru->gs_gid, gru->gs_gru_base_vaddr,
gru->gs_gru_base_paddr);
gru_kservices_init(gru);
@@ -295,7 +300,7 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
for_each_online_node(nid) {
bid = uv_node_to_blade_id(nid);
pnode = uv_node_to_pnode(nid);
- if (gru_base[bid])
+ if (bid < 0 || gru_base[bid])
continue;
page = alloc_pages_node(nid, GFP_KERNEL, order);
if (!page)
@@ -308,11 +313,11 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
dsrbytes = 0;
cbrs = 0;
for (gru = gru_base[bid]->bs_grus, chip = 0;
- chip < GRU_CHIPLETS_PER_BLADE;
+ chip < GRU_CHIPLETS_PER_BLADE;
chip++, gru++) {
paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip);
vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip);
- gru_init_chiplet(gru, paddr, vaddr, bid, nid, chip);
+ gru_init_chiplet(gru, paddr, vaddr, nid, bid, chip);
n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
cbrs = max(cbrs, n);
n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
@@ -370,26 +375,26 @@ static int __init gru_init(void)
void *gru_start_vaddr;
if (!is_uv_system())
- return 0;
+ return -ENODEV;
#if defined CONFIG_IA64
gru_start_paddr = 0xd000000000UL; /* ZZZZZZZZZZZZZZZZZZZ fixme */
#else
gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR) &
0x7fffffffffffUL;
-
#endif
gru_start_vaddr = __va(gru_start_paddr);
- gru_end_paddr = gru_start_paddr + MAX_NUMNODES * GRU_SIZE;
+ gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE;
printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n",
gru_start_paddr, gru_end_paddr);
irq = get_base_irq();
for (chip = 0; chip < GRU_CHIPLETS_PER_BLADE; chip++) {
ret = request_irq(irq + chip, gru_intr, 0, id, NULL);
- /* TODO: fix irq handling on x86. For now ignore failures because
+ /* TODO: fix irq handling on x86. For now ignore failure because
* interrupts are not required & not yet fully supported */
if (ret) {
- printk("!!!WARNING: GRU ignoring request failure!!!\n");
+ printk(KERN_WARNING
+ "!!!WARNING: GRU ignoring request failure!!!\n");
ret = 0;
}
if (ret) {
@@ -435,7 +440,7 @@ exit1:
static void __exit gru_exit(void)
{
- int i, bid;
+ int i, bid, gid;
int order = get_order(sizeof(struct gru_state) *
GRU_CHIPLETS_PER_BLADE);
@@ -445,6 +450,9 @@ static void __exit gru_exit(void)
for (i = 0; i < GRU_CHIPLETS_PER_BLADE; i++)
free_irq(IRQ_GRU + i, NULL);
+ foreach_gid(gid)
+ gru_kservices_exit(GID_TO_GRU(gid));
+
for (bid = 0; bid < GRU_MAX_BLADES; bid++)
free_pages((unsigned long)gru_base[bid], order);
@@ -469,7 +477,11 @@ struct vm_operations_struct gru_vm_ops = {
.fault = gru_fault,
};
+#ifndef MODULE
fs_initcall(gru_init);
+#else
+module_init(gru_init);
+#endif
module_exit(gru_exit);
module_param(gru_options, ulong, 0644);
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c
new file mode 100644
index 00000000000..9b7ccb32869
--- /dev/null
+++ b/drivers/misc/sgi-gru/gruhandles.c
@@ -0,0 +1,183 @@
+/*
+ * GRU KERNEL MCS INSTRUCTIONS
+ *
+ * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include "gru.h"
+#include "grulib.h"
+#include "grutables.h"
+
+/* 10 sec */
+#ifdef CONFIG_IA64
+#include <asm/processor.h>
+#define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10)
+#else
+#include <asm/tsc.h>
+#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
+#endif
+
+/* Extract the status field from a kernel handle */
+#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3)
+
+struct mcs_op_statistic mcs_op_statistics[mcsop_last];
+
+static void update_mcs_stats(enum mcs_op op, unsigned long clks)
+{
+ atomic_long_inc(&mcs_op_statistics[op].count);
+ atomic_long_add(clks, &mcs_op_statistics[op].total);
+ if (mcs_op_statistics[op].max < clks)
+ mcs_op_statistics[op].max = clks;
+}
+
+static void start_instruction(void *h)
+{
+ unsigned long *w0 = h;
+
+ wmb(); /* setting CMD bit must be last */
+ *w0 = *w0 | 1;
+ gru_flush_cache(h);
+}
+
+static int wait_instruction_complete(void *h, enum mcs_op opc)
+{
+ int status;
+ cycles_t start_time = get_cycles();
+
+ while (1) {
+ cpu_relax();
+ status = GET_MSEG_HANDLE_STATUS(h);
+ if (status != CCHSTATUS_ACTIVE)
+ break;
+ if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time))
+ panic("GRU %p is malfunctioning\n", h);
+ }
+ if (gru_options & OPT_STATS)
+ update_mcs_stats(opc, get_cycles() - start_time);
+ return status;
+}
+
+int cch_allocate(struct gru_context_configuration_handle *cch,
+ int asidval, int sizeavail, unsigned long cbrmap,
+ unsigned long dsrmap)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ cch->asid[i] = (asidval++);
+ cch->sizeavail[i] = sizeavail;
+ }
+ cch->dsr_allocation_map = dsrmap;
+ cch->cbr_allocation_map = cbrmap;
+ cch->opc = CCHOP_ALLOCATE;
+ start_instruction(cch);
+ return wait_instruction_complete(cch, cchop_allocate);
+}
+
+int cch_start(struct gru_context_configuration_handle *cch)
+{
+ cch->opc = CCHOP_START;
+ start_instruction(cch);
+ return wait_instruction_complete(cch, cchop_start);
+}
+
+int cch_interrupt(struct gru_context_configuration_handle *cch)
+{
+ cch->opc = CCHOP_INTERRUPT;
+ start_instruction(cch);
+ return wait_instruction_complete(cch, cchop_interrupt);
+}
+
+int cch_deallocate(struct gru_context_configuration_handle *cch)
+{
+ cch->opc = CCHOP_DEALLOCATE;
+ start_instruction(cch);
+ return wait_instruction_complete(cch, cchop_deallocate);
+}
+
+int cch_interrupt_sync(struct gru_context_configuration_handle
+ *cch)
+{
+ cch->opc = CCHOP_INTERRUPT_SYNC;
+ start_instruction(cch);
+ return wait_instruction_complete(cch, cchop_interrupt_sync);
+}
+
+int tgh_invalidate(struct gru_tlb_global_handle *tgh,
+ unsigned long vaddr, unsigned long vaddrmask,
+ int asid, int pagesize, int global, int n,
+ unsigned short ctxbitmap)
+{
+ tgh->vaddr = vaddr;
+ tgh->asid = asid;
+ tgh->pagesize = pagesize;
+ tgh->n = n;
+ tgh->global = global;
+ tgh->vaddrmask = vaddrmask;
+ tgh->ctxbitmap = ctxbitmap;
+ tgh->opc = TGHOP_TLBINV;
+ start_instruction(tgh);
+ return wait_instruction_complete(tgh, tghop_invalidate);
+}
+
+void tfh_write_only(struct gru_tlb_fault_handle *tfh,
+ unsigned long pfn, unsigned long vaddr,
+ int asid, int dirty, int pagesize)
+{
+ tfh->fillasid = asid;
+ tfh->fillvaddr = vaddr;
+ tfh->pfn = pfn;
+ tfh->dirty = dirty;
+ tfh->pagesize = pagesize;
+ tfh->opc = TFHOP_WRITE_ONLY;
+ start_instruction(tfh);
+}
+
+void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
+ unsigned long paddr, int gaa,
+ unsigned long vaddr, int asid, int dirty,
+ int pagesize)
+{
+ tfh->fillasid = asid;
+ tfh->fillvaddr = vaddr;
+ tfh->pfn = paddr >> GRU_PADDR_SHIFT;
+ tfh->gaa = gaa;
+ tfh->dirty = dirty;
+ tfh->pagesize = pagesize;
+ tfh->opc = TFHOP_WRITE_RESTART;
+ start_instruction(tfh);
+}
+
+void tfh_restart(struct gru_tlb_fault_handle *tfh)
+{
+ tfh->opc = TFHOP_RESTART;
+ start_instruction(tfh);
+}
+
+void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh)
+{
+ tfh->opc = TFHOP_USER_POLLING_MODE;
+ start_instruction(tfh);
+}
+
+void tfh_exception(struct gru_tlb_fault_handle *tfh)
+{
+ tfh->opc = TFHOP_EXCEPTION;
+ start_instruction(tfh);
+}
+
diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h
index b63018d60fe..1ed74d7508c 100644
--- a/drivers/misc/sgi-gru/gruhandles.h
+++ b/drivers/misc/sgi-gru/gruhandles.h
@@ -489,170 +489,28 @@ enum gru_cbr_state {
* 64m 26 8
* ...
*/
-#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2: (sh)) >> 1) - 6)
+#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6)
#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh))
/* minimum TLB purge count to ensure a full purge */
#define GRUMAXINVAL 1024UL
-
-/* Extract the status field from a kernel handle */
-#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3)
-
-static inline void start_instruction(void *h)
-{
- unsigned long *w0 = h;
-
- wmb(); /* setting CMD bit must be last */
- *w0 = *w0 | 1;
- gru_flush_cache(h);
-}
-
-static inline int wait_instruction_complete(void *h)
-{
- int status;
-
- do {
- cpu_relax();
- barrier();
- status = GET_MSEG_HANDLE_STATUS(h);
- } while (status == CCHSTATUS_ACTIVE);
- return status;
-}
-
-#if defined CONFIG_IA64
-static inline void cch_allocate_set_asids(
- struct gru_context_configuration_handle *cch, int asidval)
-{
- int i;
-
- for (i = 0; i <= RGN_HPAGE; i++) { /* assume HPAGE is last region */
- cch->asid[i] = (asidval++);
-#if 0
- /* ZZZ hugepages not supported yet */
- if (i == RGN_HPAGE)
- cch->sizeavail[i] = GRU_SIZEAVAIL(hpage_shift);
- else
-#endif
- cch->sizeavail[i] = GRU_SIZEAVAIL(PAGE_SHIFT);
- }
-}
-#elif defined CONFIG_X86_64
-static inline void cch_allocate_set_asids(
- struct gru_context_configuration_handle *cch, int asidval)
-{
- int i;
-
- for (i = 0; i