diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/spl/spl-generic.c | 43 | ||||
-rw-r--r-- | module/spl/spl-kmem.c | 147 | ||||
-rw-r--r-- | module/spl/spl-proc.c | 57 |
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", |