Commit 32028c70 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] kmalloc_percpu: interface change

From: Rusty Russell <rusty@rustcorp.com.au>

Several tweaks to the kmalloc_percpu()/kfree_percpu() interface, to
allow future implementations to be more flexible, and make easier to
use now we can see how it's actually being used.

1) No flags argument: GFP_ATOMIC doesn't make much sense,

2) Explicit alignment argument, so we don't have to give SMP_CACHE_BYTES
   alignment always,

3) Zeros memory, since most callers want that and it's not entirely
   trivial,

4) Convenient type-safe wrapper which takes a typename, and

5) Rename to alloc_percpu/__alloc_percpu, since usage no longer matches
   kmalloc.
parent 6b206194
...@@ -158,16 +158,15 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { ...@@ -158,16 +158,15 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static inline int init_disk_stats(struct gendisk *disk) static inline int init_disk_stats(struct gendisk *disk)
{ {
disk->dkstats = kmalloc_percpu(sizeof (struct disk_stats), GFP_KERNEL); disk->dkstats = alloc_percpu(struct disk_stats);
if (!disk->dkstats) if (!disk->dkstats)
return 0; return 0;
disk_stat_set_all(disk, 0);
return 1; return 1;
} }
static inline void free_disk_stats(struct gendisk *disk) static inline void free_disk_stats(struct gendisk *disk)
{ {
kfree_percpu(disk->dkstats); free_percpu(disk->dkstats);
} }
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
static inline int init_disk_stats(struct gendisk *disk) static inline int init_disk_stats(struct gendisk *disk)
......
#ifndef __LINUX_PERCPU_H #ifndef __LINUX_PERCPU_H
#define __LINUX_PERCPU_H #define __LINUX_PERCPU_H
#include <linux/spinlock.h> /* For preempt_disable() */ #include <linux/spinlock.h> /* For preempt_disable() */
#include <linux/slab.h> /* For kmalloc_percpu() */ #include <linux/slab.h> /* For kmalloc() */
#include <linux/string.h> /* For memset() */
#include <asm/percpu.h> #include <asm/percpu.h>
/* Must be an lvalue. */ /* Must be an lvalue. */
...@@ -17,7 +18,7 @@ struct percpu_data { ...@@ -17,7 +18,7 @@ struct percpu_data {
/* /*
* Use this to get to a cpu's version of the per-cpu object allocated using * Use this to get to a cpu's version of the per-cpu object allocated using
* kmalloc_percpu. If you want to get "this cpu's version", maybe you want * alloc_percpu. If you want to get "this cpu's version", maybe you want
* to use get_cpu_ptr... * to use get_cpu_ptr...
*/ */
#define per_cpu_ptr(ptr, cpu) \ #define per_cpu_ptr(ptr, cpu) \
...@@ -26,19 +27,22 @@ struct percpu_data { ...@@ -26,19 +27,22 @@ struct percpu_data {
(__typeof__(ptr))__p->ptrs[(cpu)]; \ (__typeof__(ptr))__p->ptrs[(cpu)]; \
}) })
extern void *kmalloc_percpu(size_t size, int flags); extern void *__alloc_percpu(size_t size, size_t align);
extern void kfree_percpu(const void *); extern void free_percpu(const void *);
extern void kmalloc_percpu_init(void); extern void kmalloc_percpu_init(void);
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
#define per_cpu_ptr(ptr, cpu) (ptr) #define per_cpu_ptr(ptr, cpu) (ptr)
static inline void *kmalloc_percpu(size_t size, int flags) static inline void *__alloc_percpu(size_t size, size_t align)
{ {
return(kmalloc(size, flags)); void *ret = kmalloc(size, GFP_KERNEL);
if (ret)
memset(ret, 0, size);
return ret;
} }
static inline void kfree_percpu(const void *ptr) static inline void free_percpu(const void *ptr)
{ {
kfree(ptr); kfree(ptr);
} }
...@@ -46,9 +50,13 @@ static inline void kmalloc_percpu_init(void) { } ...@@ -46,9 +50,13 @@ static inline void kmalloc_percpu_init(void) { }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
/* Simple wrapper for the common case: zeros memory. */
#define alloc_percpu(type) \
((type *)(__alloc_percpu(sizeof(type), __alignof__(type))))
/* /*
* Use these with kmalloc_percpu. If * Use these with alloc_percpu. If
* 1. You want to operate on memory allocated by kmalloc_percpu (dereference * 1. You want to operate on memory allocated by alloc_percpu (dereference
* and read/modify/write) AND * and read/modify/write) AND
* 2. You want "this cpu's version" of the object AND * 2. You want "this cpu's version" of the object AND
* 3. You want to do this safely since: * 3. You want to do this safely since:
......
...@@ -145,7 +145,7 @@ extern atomic_t inet6_sock_nr; ...@@ -145,7 +145,7 @@ extern atomic_t inet6_sock_nr;
int snmp6_register_dev(struct inet6_dev *idev); int snmp6_register_dev(struct inet6_dev *idev);
int snmp6_unregister_dev(struct inet6_dev *idev); int snmp6_unregister_dev(struct inet6_dev *idev);
int snmp6_mib_init(void *ptr[2], size_t mibsize); int snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign);
void snmp6_mib_free(void *ptr[2]); void snmp6_mib_free(void *ptr[2]);
struct ip6_ra_chain struct ip6_ra_chain
......
...@@ -98,8 +98,8 @@ EXPORT_SYMBOL(remove_shrinker); ...@@ -98,8 +98,8 @@ EXPORT_SYMBOL(remove_shrinker);
EXPORT_SYMBOL(kmalloc); EXPORT_SYMBOL(kmalloc);
EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kfree);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
EXPORT_SYMBOL(kmalloc_percpu); EXPORT_SYMBOL(__alloc_percpu);
EXPORT_SYMBOL(kfree_percpu); EXPORT_SYMBOL(free_percpu);
EXPORT_SYMBOL(percpu_counter_mod); EXPORT_SYMBOL(percpu_counter_mod);
#endif #endif
EXPORT_SYMBOL(vfree); EXPORT_SYMBOL(vfree);
......
...@@ -1984,26 +1984,18 @@ void * kmalloc (size_t size, int flags) ...@@ -1984,26 +1984,18 @@ void * kmalloc (size_t size, int flags)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/** /**
* kmalloc_percpu - allocate one copy of the object for every present * __alloc_percpu - allocate one copy of the object for every present
* cpu in the system. * cpu in the system, zeroing them.
* Objects should be dereferenced using per_cpu_ptr/get_cpu_ptr * Objects should be dereferenced using per_cpu_ptr/get_cpu_ptr
* macros only. * macros only.
* *
* @size: how many bytes of memory are required. * @size: how many bytes of memory are required.
* @flags: the type of memory to allocate. * @align: the alignment, which can't be greater than SMP_CACHE_BYTES.
* The @flags argument may be one of:
*
* %GFP_USER - Allocate memory on behalf of user. May sleep.
*
* %GFP_KERNEL - Allocate normal kernel ram. May sleep.
*
* %GFP_ATOMIC - Allocation will not sleep. Use inside interrupt handlers.
*/ */
void * void *__alloc_percpu(size_t size, size_t align)
kmalloc_percpu(size_t size, int flags)
{ {
int i; int i;
struct percpu_data *pdata = kmalloc(sizeof (*pdata), flags); struct percpu_data *pdata = kmalloc(sizeof (*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return NULL; return NULL;
...@@ -2011,9 +2003,10 @@ kmalloc_percpu(size_t size, int flags) ...@@ -2011,9 +2003,10 @@ kmalloc_percpu(size_t size, int flags)
for (i = 0; i < NR_CPUS; i++) { for (i = 0; i < NR_CPUS; i++) {
if (!cpu_possible(i)) if (!cpu_possible(i))
continue; continue;
pdata->ptrs[i] = kmalloc(size, flags); pdata->ptrs[i] = kmalloc(size, GFP_KERNEL);
if (!pdata->ptrs[i]) if (!pdata->ptrs[i])
goto unwind_oom; goto unwind_oom;
memset(pdata->ptrs[i], 0, size);
} }
/* Catch derefs w/o wrappers */ /* Catch derefs w/o wrappers */
...@@ -2070,14 +2063,14 @@ void kfree (const void *objp) ...@@ -2070,14 +2063,14 @@ void kfree (const void *objp)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/** /**
* kfree_percpu - free previously allocated percpu memory * free_percpu - free previously allocated percpu memory
* @objp: pointer returned by kmalloc_percpu. * @objp: pointer returned by alloc_percpu.
* *
* Don't free memory not originally allocated by kmalloc_percpu() * Don't free memory not originally allocated by alloc_percpu()
* The complemented objp is to check for that. * The complemented objp is to check for that.
*/ */
void void
kfree_percpu(const void *objp) free_percpu(const void *objp)
{ {
int i; int i;
struct percpu_data *p = (struct percpu_data *) (~(unsigned long) objp); struct percpu_data *p = (struct percpu_data *) (~(unsigned long) objp);
......
...@@ -1068,54 +1068,22 @@ static struct inet_protocol icmp_protocol = { ...@@ -1068,54 +1068,22 @@ static struct inet_protocol icmp_protocol = {
static int __init init_ipv4_mibs(void) static int __init init_ipv4_mibs(void)
{ {
int i; net_statistics[0] = alloc_percpu(struct linux_mib);
net_statistics[1] = alloc_percpu(struct linux_mib);
net_statistics[0] = ip_statistics[0] = alloc_percpu(struct ip_mib);
kmalloc_percpu(sizeof (struct linux_mib), GFP_KERNEL); ip_statistics[1] = alloc_percpu(struct ip_mib);
net_statistics[1] = icmp_statistics[0] = alloc_percpu(struct icmp_mib);
kmalloc_percpu(sizeof (struct linux_mib), GFP_KERNEL); icmp_statistics[1] = alloc_percpu(struct icmp_mib);
ip_statistics[0] = kmalloc_percpu(sizeof (struct ip_mib), GFP_KERNEL); tcp_statistics[0] = alloc_percpu(struct tcp_mib);
ip_statistics[1] = kmalloc_percpu(sizeof (struct ip_mib), GFP_KERNEL); tcp_statistics[1] = alloc_percpu(struct tcp_mib);
icmp_statistics[0] = udp_statistics[0] = alloc_percpu(struct udp_mib);
kmalloc_percpu(sizeof (struct icmp_mib), GFP_KERNEL); udp_statistics[1] = alloc_percpu(struct udp_mib);
icmp_statistics[1] =
kmalloc_percpu(sizeof (struct icmp_mib), GFP_KERNEL);
tcp_statistics[0] = kmalloc_percpu(sizeof (struct tcp_mib), GFP_KERNEL);
tcp_statistics[1] = kmalloc_percpu(sizeof (struct tcp_mib), GFP_KERNEL);
udp_statistics[0] = kmalloc_percpu(sizeof (struct udp_mib), GFP_KERNEL);
udp_statistics[1] = kmalloc_percpu(sizeof (struct udp_mib), GFP_KERNEL);
if (! if (!
(net_statistics[0] && net_statistics[1] && ip_statistics[0] (net_statistics[0] && net_statistics[1] && ip_statistics[0]
&& ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1] && ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1]
&& udp_statistics[0] && udp_statistics[1])) && udp_statistics[0] && udp_statistics[1]))
return -ENOMEM; return -ENOMEM;
/* Set all the per cpu copies of the mibs to zero */
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(net_statistics[0], i), 0,
sizeof (struct linux_mib));
memset(per_cpu_ptr(net_statistics[1], i), 0,
sizeof (struct linux_mib));
memset(per_cpu_ptr(ip_statistics[0], i), 0,
sizeof (struct ip_mib));
memset(per_cpu_ptr(ip_statistics[1], i), 0,
sizeof (struct ip_mib));
memset(per_cpu_ptr(icmp_statistics[0], i), 0,
sizeof (struct icmp_mib));
memset(per_cpu_ptr(icmp_statistics[1], i), 0,
sizeof (struct icmp_mib));
memset(per_cpu_ptr(tcp_statistics[0], i), 0,
sizeof (struct tcp_mib));
memset(per_cpu_ptr(tcp_statistics[1], i), 0,
sizeof (struct tcp_mib));
memset(per_cpu_ptr(udp_statistics[0], i), 0,
sizeof (struct udp_mib));
memset(per_cpu_ptr(udp_statistics[1], i), 0,
sizeof (struct udp_mib));
}
}
(void) tcp_mib_init(); (void) tcp_mib_init();
return 0; return 0;
......
...@@ -2694,16 +2694,9 @@ int __init ip_rt_init(void) ...@@ -2694,16 +2694,9 @@ int __init ip_rt_init(void)
ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1); ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1);
ip_rt_max_size = (rt_hash_mask + 1) * 16; ip_rt_max_size = (rt_hash_mask + 1) * 16;
rt_cache_stat = kmalloc_percpu(sizeof (struct rt_cache_stat), rt_cache_stat = alloc_percpu(struct rt_cache_stat);
GFP_KERNEL);
if (!rt_cache_stat) if (!rt_cache_stat)
goto out_enomem1; goto out_enomem1;
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(rt_cache_stat, i), 0,
sizeof (struct rt_cache_stat));
}
}
devinet_init(); devinet_init();
ip_fib_init(); ip_fib_init();
...@@ -2739,7 +2732,7 @@ int __init ip_rt_init(void) ...@@ -2739,7 +2732,7 @@ int __init ip_rt_init(void)
out: out:
return rc; return rc;
out_enomem: out_enomem:
kfree_percpu(rt_cache_stat); free_percpu(rt_cache_stat);
out_enomem1: out_enomem1:
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
......
...@@ -640,32 +640,23 @@ inet6_unregister_protosw(struct inet_protosw *p) ...@@ -640,32 +640,23 @@ inet6_unregister_protosw(struct inet_protosw *p)
} }
int int
snmp6_mib_init(void *ptr[2], size_t mibsize) snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
{ {
int i;
if (ptr == NULL) if (ptr == NULL)
return -EINVAL; return -EINVAL;
ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL); ptr[0] = __alloc_percpu(mibsize, mibalign);
if (!ptr[0]) if (!ptr[0])
goto err0; goto err0;
ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL); ptr[1] = __alloc_percpu(mibsize, mibalign);
if (!ptr[1]) if (!ptr[1])
goto err1; goto err1;
/* Zero percpu version of the mibs */
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(ptr[0], i), 0, mibsize);
memset(per_cpu_ptr(ptr[1], i), 0, mibsize);
}
}
return 0; return 0;
err1: err1:
kfree_percpu(ptr[0]); free_percpu(ptr[0]);
ptr[0] = NULL; ptr[0] = NULL;
err0: err0:
return -ENOMEM; return -ENOMEM;
...@@ -676,18 +667,21 @@ snmp6_mib_free(void *ptr[2]) ...@@ -676,18 +667,21 @@ snmp6_mib_free(void *ptr[2])
{ {
if (ptr == NULL) if (ptr == NULL)
return; return;
kfree_percpu(ptr[0]); free_percpu(ptr[0]);
kfree_percpu(ptr[1]); free_percpu(ptr[1]);
ptr[0] = ptr[1] = NULL; ptr[0] = ptr[1] = NULL;
} }
static int __init init_ipv6_mibs(void) static int __init init_ipv6_mibs(void)
{ {
if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipv6_mib)) < 0) if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipv6_mib),
__alignof__(struct ipv6_mib)) < 0)
goto err_ip_mib; goto err_ip_mib;
if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib)) < 0) if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib),
__alignof__(struct ipv6_mib)) < 0)
goto err_icmp_mib; goto err_icmp_mib;
if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib)) < 0) if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib),
__alignof__(struct ipv6_mib)) < 0)
goto err_udp_mib; goto err_udp_mib;
return 0; return 0;
......
...@@ -228,7 +228,8 @@ int snmp6_register_dev(struct inet6_dev *idev) ...@@ -228,7 +228,8 @@ int snmp6_register_dev(struct inet6_dev *idev)
if (!idev || !idev->dev) if (!idev || !idev->dev)
return -EINVAL; return -EINVAL;
if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib)) < 0) if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib),
__alignof__(struct ipv6_mib)) < 0)
goto err_icmp; goto err_icmp;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
......
...@@ -880,34 +880,22 @@ static int __init init_sctp_mibs(void) ...@@ -880,34 +880,22 @@ static int __init init_sctp_mibs(void)
{ {
int i; int i;
sctp_statistics[0] = kmalloc_percpu(sizeof (struct sctp_mib), sctp_statistics[0] = alloc_percpu(struct sctp_mib);
GFP_KERNEL);
if (!sctp_statistics[0]) if (!sctp_statistics[0])
return -ENOMEM; return -ENOMEM;
sctp_statistics[1] = kmalloc_percpu(sizeof (struct sctp_mib), sctp_statistics[1] = alloc_percpu(struct sctp_mib);
GFP_KERNEL);
if (!sctp_statistics[1]) { if (!sctp_statistics[1]) {
kfree_percpu(sctp_statistics[0]); free_percpu(sctp_statistics[0]);
return -ENOMEM; return -ENOMEM;
} }
/* Zero all percpu versions of the mibs */
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(sctp_statistics[0], i), 0,
sizeof (struct sctp_mib));
memset(per_cpu_ptr(sctp_statistics[1], i), 0,
sizeof (struct sctp_mib));
}
}
return 0; return 0;
} }
static void cleanup_sctp_mibs(void) static void cleanup_sctp_mibs(void)
{ {
kfree_percpu(sctp_statistics[0]); free_percpu(sctp_statistics[0]);
kfree_percpu(sctp_statistics[1]); free_percpu(sctp_statistics[1]);
} }
/* Initialize the universe into something sensible. */ /* Initialize the universe into something sensible. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment