summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/spl/spl-generic.c43
-rw-r--r--module/spl/spl-kmem.c147
-rw-r--r--module/spl/spl-proc.c57
3 files changed, 175 insertions, 72 deletions
diff --git a/module/spl/spl-generic.c b/module/spl/spl-generic.c
index 96a14c62e..c23abf6ff 100644
--- a/module/spl/spl-generic.c
+++ b/module/spl/spl-generic.c
@@ -54,6 +54,10 @@ EXPORT_SYMBOL(hw_serial);
int p0 = 0;
EXPORT_SYMBOL(p0);
+#ifndef HAVE_KALLSYMS_LOOKUP_NAME
+kallsyms_lookup_name_t spl_kallsyms_lookup_name_fn = NULL;
+#endif
+
int
highbit(unsigned long i)
{
@@ -269,6 +273,42 @@ zone_get_hostid(void *zone)
}
EXPORT_SYMBOL(zone_get_hostid);
+#ifdef HAVE_KALLSYMS_LOOKUP_NAME
+#define set_kallsyms_lookup_name() (0)
+#else
+/*
+ * Because kallsyms_lookup_name() is no longer exported in the
+ * mainline kernel we are forced to resort to somewhat drastic
+ * measures. This function replaces the functionality by performing
+ * an upcall to user space where /proc/kallsyms is consulted for
+ * the requested address.
+ */
+#define GET_KALLSYMS_ADDR_CMD \
+ "awk '{ if ( $3 == \"kallsyms_lookup_name\") { print $1 } }' " \
+ "/proc/kallsyms >/proc/sys/kernel/spl/kallsyms_lookup_name"
+
+static int
+set_kallsyms_lookup_name(void)
+{
+ char sh_path[] = "/bin/sh";
+ char *argv[] = { sh_path,
+ "-c",
+ GET_KALLSYMS_ADDR_CMD,
+ NULL };
+ char *envp[] = { "HOME=/",
+ "TERM=linux",
+ "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+ NULL };
+ int rc;
+
+ rc = call_usermodehelper(sh_path, argv, envp, 1);
+ if (rc)
+ return rc;
+
+ return spl_kmem_init_kallsyms_lookup();
+}
+#endif
+
static int __init spl_init(void)
{
int rc = 0;
@@ -297,6 +337,9 @@ static int __init spl_init(void)
if ((rc = set_hostid()))
GOTO(out7, rc = -EADDRNOTAVAIL);
+ if ((rc = set_kallsyms_lookup_name()))
+ GOTO(out7, rc = -EADDRNOTAVAIL);
+
printk("SPL: Loaded Solaris Porting Layer v%s\n", VERSION);
RETURN(rc);
out7:
diff --git a/module/spl/spl-kmem.c b/module/spl/spl-kmem.c
index 18613e799..b8b006610 100644
--- a/module/spl/spl-kmem.c
+++ b/module/spl/spl-kmem.c
@@ -79,85 +79,29 @@ EXPORT_SYMBOL(zio_alloc_arena);
vmem_t *zio_arena = NULL;
EXPORT_SYMBOL(zio_arena);
+#ifndef HAVE_GET_VMALLOC_INFO
+get_vmalloc_info_t get_vmalloc_info_fn = NULL;
+EXPORT_SYMBOL(get_vmalloc_info_fn);
+#endif /* HAVE_GET_VMALLOC_INFO */
+
#ifndef HAVE_FIRST_ONLINE_PGDAT
-struct pglist_data *
-first_online_pgdat(void)
-{
- return NODE_DATA(first_online_node);
-}
-EXPORT_SYMBOL(first_online_pgdat);
+first_online_pgdat_t first_online_pgdat_fn = NULL;
+EXPORT_SYMBOL(first_online_pgdat_fn);
#endif /* HAVE_FIRST_ONLINE_PGDAT */
#ifndef HAVE_NEXT_ONLINE_PGDAT
-struct pglist_data *
-next_online_pgdat(struct pglist_data *pgdat)
-{
- int nid = next_online_node(pgdat->node_id);
-
- if (nid == MAX_NUMNODES)
- return NULL;
-
- return NODE_DATA(nid);
-}
-EXPORT_SYMBOL(next_online_pgdat);
+next_online_pgdat_t next_online_pgdat_fn = NULL;
+EXPORT_SYMBOL(next_online_pgdat_fn);
#endif /* HAVE_NEXT_ONLINE_PGDAT */
#ifndef HAVE_NEXT_ZONE
-struct zone *
-next_zone(struct zone *zone)
-{
- pg_data_t *pgdat = zone->zone_pgdat;
-
- if (zone < pgdat->node_zones + MAX_NR_ZONES - 1)
- zone++;
- else {
- pgdat = next_online_pgdat(pgdat);
- if (pgdat)
- zone = pgdat->node_zones;
- else
- zone = NULL;
- }
- return zone;
-}
-EXPORT_SYMBOL(next_zone);
+next_zone_t next_zone_fn = NULL;
+EXPORT_SYMBOL(next_zone_fn);
#endif /* HAVE_NEXT_ZONE */
#ifndef HAVE_GET_ZONE_COUNTS
-void
-__get_zone_counts(unsigned long *active, unsigned long *inactive,
- unsigned long *free, struct pglist_data *pgdat)
-{
- struct zone *zones = pgdat->node_zones;
- int i;
-
- *active = 0;
- *inactive = 0;
- *free = 0;
- for (i = 0; i < MAX_NR_ZONES; i++) {
- *active += zones[i].nr_active;
- *inactive += zones[i].nr_inactive;
- *free += zones[i].free_pages;
- }
-}
-
-void
-get_zone_counts(unsigned long *active, unsigned long *inactive,
- unsigned long *free)
-{
- struct pglist_data *pgdat;
-
- *active = 0;
- *inactive = 0;
- *free = 0;
- for_each_online_pgdat(pgdat) {
- unsigned long l, m, n;
- __get_zone_counts(&l, &m, &n, pgdat);
- *active += l;
- *inactive += m;
- *free += n;
- }
-}
-EXPORT_SYMBOL(get_zone_counts);
+get_zone_counts_t get_zone_counts_fn = NULL;
+EXPORT_SYMBOL(get_zone_counts_fn);
#endif /* HAVE_GET_ZONE_COUNTS */
pgcnt_t
@@ -177,11 +121,20 @@ EXPORT_SYMBOL(spl_kmem_availrmem);
size_t
vmem_size(vmem_t *vmp, int typemask)
{
- /* Arena's unsupported */
+ struct vmalloc_info vmi;
+ size_t size = 0;
+
ASSERT(vmp == NULL);
ASSERT(typemask & (VMEM_ALLOC | VMEM_FREE));
- return 0;
+ get_vmalloc_info(&vmi);
+ if (typemask & VMEM_ALLOC)
+ size += (size_t)vmi.used;
+
+ if (typemask & VMEM_FREE)
+ size += (size_t)(VMALLOC_TOTAL - vmi.used);
+
+ return size;
}
EXPORT_SYMBOL(vmem_size);
@@ -1812,6 +1765,57 @@ spl_kmem_init_globals(void)
swapfs_reserve = MIN(4*1024*1024 / PAGE_SIZE, physmem / 16);
}
+/*
+ * Called at module init when it is safe to use spl_kallsyms_lookup_name()
+ */
+int
+spl_kmem_init_kallsyms_lookup(void)
+{
+#ifndef HAVE_GET_VMALLOC_INFO
+ get_vmalloc_info_fn = (get_vmalloc_info_t)
+ spl_kallsyms_lookup_name("get_vmalloc_info");
+ if (!get_vmalloc_info_fn)
+ return -EFAULT;
+#endif /* HAVE_GET_VMALLOC_INFO */
+
+#ifndef HAVE_FIRST_ONLINE_PGDAT
+ first_online_pgdat_fn = (first_online_pgdat_t)
+ spl_kallsyms_lookup_name("first_online_pgdat");
+ if (!first_online_pgdat_fn)
+ return -EFAULT;
+#endif /* HAVE_FIRST_ONLINE_PGDAT */
+
+#ifndef HAVE_NEXT_ONLINE_PGDAT
+ next_online_pgdat_fn = (next_online_pgdat_t)
+ spl_kallsyms_lookup_name("next_online_pgdat");
+ if (!next_online_pgdat_fn)
+ return -EFAULT;
+#endif /* HAVE_NEXT_ONLINE_PGDAT */
+
+#ifndef HAVE_NEXT_ZONE
+ next_zone_fn = (next_zone_t)
+ spl_kallsyms_lookup_name("next_zone");
+ if (!next_zone_fn)
+ return -EFAULT;
+#endif /* HAVE_NEXT_ZONE */
+
+#ifndef HAVE_GET_ZONE_COUNTS
+ get_zone_counts_fn = (get_zone_counts_t)
+ spl_kallsyms_lookup_name("get_zone_counts");
+ if (!get_zone_counts_fn)
+ return -EFAULT;
+#endif /* HAVE_GET_ZONE_COUNTS */
+
+ /*
+ * It is now safe to initialize the global tunings which rely on
+ * the use of the for_each_zone() macro. This macro in turns
+ * depends on the *_pgdat symbols which are now available.
+ */
+ spl_kmem_init_globals();
+
+ return 0;
+}
+
int
spl_kmem_init(void)
{
@@ -1820,7 +1824,6 @@ spl_kmem_init(void)
init_rwsem(&spl_kmem_cache_sem);
INIT_LIST_HEAD(&spl_kmem_cache_list);
- spl_kmem_init_globals();
#ifdef HAVE_SET_SHRINKER
spl_kmem_cache_shrinker = set_shrinker(KMC_DEFAULT_SEEKS,
diff --git a/module/spl/spl-proc.c b/module/spl/spl-proc.c
index 90c89ce31..d0ce23194 100644
--- a/module/spl/spl-proc.c
+++ b/module/spl/spl-proc.c
@@ -68,6 +68,7 @@ struct proc_dir_entry *proc_spl_kstat = NULL;
#define CTL_VERSION CTL_UNNUMBERED /* Version */
#define CTL_HOSTID CTL_UNNUMBERED /* Host id by /usr/bin/hostid */
#define CTL_HW_SERIAL CTL_UNNUMBERED /* HW serial number by hostid */
+#define CTL_KALLSYMS CTL_UNNUMBERED /* kallsyms_lookup_name addr */
#define CTL_DEBUG_SUBSYS CTL_UNNUMBERED /* Debug subsystem */
#define CTL_DEBUG_MASK CTL_UNNUMBERED /* Debug mask */
@@ -123,6 +124,7 @@ enum {
CTL_VERSION = 1, /* Version */
CTL_HOSTID, /* Host id reported by /usr/bin/hostid */
CTL_HW_SERIAL, /* Hardware serial number from hostid */
+ CTL_KALLSYMS, /* Address of kallsyms_lookup_name */
CTL_DEBUG_SUBSYS, /* Debug subsystem */
CTL_DEBUG_MASK, /* Debug mask */
@@ -489,6 +491,51 @@ proc_dohostid(struct ctl_table *table, int write, struct file *filp,
RETURN(rc);
}
+#ifndef HAVE_KALLSYMS_LOOKUP_NAME
+static int
+proc_dokallsyms_lookup_name(struct ctl_table *table, int write,
+ struct file *filp, void __user *buffer,
+ size_t *lenp, loff_t *ppos) {
+ int len, rc = 0;
+ char *end, str[32];
+ ENTRY;
+
+ if (write) {
+ /* This may only be set once at module load time */
+ if (spl_kallsyms_lookup_name_fn)
+ RETURN(-EEXIST);
+
+ /* We can't use proc_doulongvec_minmax() in the write
+ * case hear because the address while a hex value has no
+ * leading 0x which confuses the helper function. */
+ rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
+ if (rc < 0)
+ RETURN(rc);
+
+ spl_kallsyms_lookup_name_fn =
+ (kallsyms_lookup_name_t)simple_strtoul(str, &end, 16);
+ if (str == end)
+ RETURN(-EINVAL);
+
+ *ppos += *lenp;
+ } else {
+ len = snprintf(str, sizeof(str), "%lx",
+ (unsigned long)spl_kallsyms_lookup_name_fn);
+ if (*ppos >= len)
+ rc = 0;
+ else
+ rc = proc_copyout_string(buffer,*lenp,str+*ppos,"\n");
+
+ if (rc >= 0) {
+ *lenp = rc;
+ *ppos += rc;
+ }
+ }
+
+ RETURN(rc);
+}
+#endif /* HAVE_KALLSYMS_LOOKUP_NAME */
+
static int
proc_doavailrmem(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -1018,6 +1065,16 @@ static struct ctl_table spl_table[] = {
.mode = 0444,
.proc_handler = &proc_dostring,
},
+#ifndef HAVE_KALLSYMS_LOOKUP_NAME
+ {
+ .ctl_name = CTL_KALLSYMS,
+ .procname = "kallsyms_lookup_name",
+ .data = &spl_kallsyms_lookup_name_fn,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644,
+ .proc_handler = &proc_dokallsyms_lookup_name,
+ },
+#endif
{
.ctl_name = CTL_SPL_DEBUG,
.procname = "debug",