#include #include #include #include #include unsigned long __per_cpu_offset[NR_CPUS]; /* * Force uses of per_cpu() with an invalid area to attempt to access the * middle of the non-canonical address space resulting in a #GP, rather than a * possible #PF at (NULL + a little) which has security implications in the * context of PV guests. */ #define INVALID_PERCPU_AREA (0x8000000000000000L - (long)__per_cpu_start) #define PERCPU_ORDER get_order_from_bytes(__per_cpu_data_end - __per_cpu_start) void __init percpu_init_areas(void) { unsigned int cpu; for ( cpu = 1; cpu < NR_CPUS; cpu++ ) __per_cpu_offset[cpu] = INVALID_PERCPU_AREA; } static int init_percpu_area(unsigned int cpu) { char *p; if ( __per_cpu_offset[cpu] != INVALID_PERCPU_AREA ) return 0; if ( (p = alloc_xenheap_pages(PERCPU_ORDER, 0)) == NULL ) return -ENOMEM; memset(p, 0, __per_cpu_data_end - __per_cpu_start); __per_cpu_offset[cpu] = p - __per_cpu_start; return 0; } struct free_info { unsigned int cpu; struct rcu_head rcu; }; static DEFINE_PER_CPU(struct free_info, free_info); static void cf_check _free_percpu_area(struct rcu_head *head) { struct free_info *info = container_of(head, struct free_info, rcu); unsigned int cpu = info->cpu; char *p = __per_cpu_start + __per_cpu_offset[cpu]; free_xenheap_pages(p, PERCPU_ORDER); __per_cpu_offset[cpu] = INVALID_PERCPU_AREA; } static void free_percpu_area(unsigned int cpu) { struct free_info *info = &per_cpu(free_info, cpu); info->cpu = cpu; call_rcu(&info->rcu, _free_percpu_area); } static int cf_check cpu_percpu_callback( struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; int rc = 0; switch ( action ) { case CPU_UP_PREPARE: rc = init_percpu_area(cpu); break; case CPU_UP_CANCELED: case CPU_DEAD: case CPU_RESUME_FAILED: if ( !park_offline_cpus && system_state != SYS_STATE_suspend ) free_percpu_area(cpu); break; case CPU_REMOVE: if ( park_offline_cpus ) free_percpu_area(cpu); break; } return notifier_from_errno(rc); } static struct notifier_block cpu_percpu_nfb = { .notifier_call = cpu_percpu_callback, .priority = 100 /* highest priority */ }; static int __init cf_check percpu_presmp_init(void) { register_cpu_notifier(&cpu_percpu_nfb); return 0; } presmp_initcall(percpu_presmp_init); /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */