diff options
-rw-r--r-- | arch/x86/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/x86/kernel/ldt.c | 9 | ||||
-rw-r--r-- | arch/x86/kernel/paravirt-spinlocks.c | 31 | ||||
-rw-r--r-- | arch/x86/kernel/paravirt.c | 27 | ||||
-rw-r--r-- | arch/x86/xen/Makefile | 11 | ||||
-rw-r--r-- | arch/x86/xen/enlighten.c | 173 | ||||
-rw-r--r-- | arch/x86/xen/irq.c | 143 | ||||
-rw-r--r-- | arch/x86/xen/smp.c | 167 | ||||
-rw-r--r-- | arch/x86/xen/spinlock.c | 183 | ||||
-rw-r--r-- | arch/x86/xen/xen-asm_32.S | 2 | ||||
-rw-r--r-- | arch/x86/xen/xen-asm_64.S | 2 | ||||
-rw-r--r-- | arch/x86/xen/xen-ops.h | 4 | ||||
-rw-r--r-- | drivers/xen/balloon.c | 174 | ||||
-rw-r--r-- | drivers/xen/events.c | 11 | ||||
-rw-r--r-- | include/asm-x86/desc.h | 15 | ||||
-rw-r--r-- | include/asm-x86/paravirt.h | 13 | ||||
-rw-r--r-- | include/linux/kernel.h | 2 | ||||
-rw-r--r-- | lib/cmdline.c | 2 |
18 files changed, 491 insertions, 482 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 3db651fc8ec5..d679cb2c79b4 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -10,7 +10,7 @@ ifdef CONFIG_FTRACE | |||
10 | # Do not profile debug and lowlevel utilities | 10 | # Do not profile debug and lowlevel utilities |
11 | CFLAGS_REMOVE_tsc.o = -pg | 11 | CFLAGS_REMOVE_tsc.o = -pg |
12 | CFLAGS_REMOVE_rtc.o = -pg | 12 | CFLAGS_REMOVE_rtc.o = -pg |
13 | CFLAGS_REMOVE_paravirt.o = -pg | 13 | CFLAGS_REMOVE_paravirt-spinlocks.o = -pg |
14 | endif | 14 | endif |
15 | 15 | ||
16 | # | 16 | # |
@@ -89,7 +89,7 @@ obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o | |||
89 | obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o | 89 | obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o |
90 | obj-$(CONFIG_KVM_GUEST) += kvm.o | 90 | obj-$(CONFIG_KVM_GUEST) += kvm.o |
91 | obj-$(CONFIG_KVM_CLOCK) += kvmclock.o | 91 | obj-$(CONFIG_KVM_CLOCK) += kvmclock.o |
92 | obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o | 92 | obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o paravirt-spinlocks.o |
93 | obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o | 93 | obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o |
94 | 94 | ||
95 | obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o | 95 | obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o |
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c index b68e21f06f4f..6e388412a854 100644 --- a/arch/x86/kernel/ldt.c +++ b/arch/x86/kernel/ldt.c | |||
@@ -51,6 +51,8 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) | |||
51 | memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, | 51 | memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, |
52 | (mincount - oldsize) * LDT_ENTRY_SIZE); | 52 | (mincount - oldsize) * LDT_ENTRY_SIZE); |
53 | 53 | ||
54 | paravirt_alloc_ldt(newldt, mincount); | ||
55 | |||
54 | #ifdef CONFIG_X86_64 | 56 | #ifdef CONFIG_X86_64 |
55 | /* CHECKME: Do we really need this ? */ | 57 | /* CHECKME: Do we really need this ? */ |
56 | wmb(); | 58 | wmb(); |
@@ -73,6 +75,7 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) | |||
73 | #endif | 75 | #endif |
74 | } | 76 | } |
75 | if (oldsize) { | 77 | if (oldsize) { |
78 | paravirt_free_ldt(oldldt, oldsize); | ||
76 | if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) | 79 | if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) |
77 | vfree(oldldt); | 80 | vfree(oldldt); |
78 | else | 81 | else |
@@ -84,10 +87,13 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) | |||
84 | static inline int copy_ldt(mm_context_t *new, mm_context_t *old) | 87 | static inline int copy_ldt(mm_context_t *new, mm_context_t *old) |
85 | { | 88 | { |
86 | int err = alloc_ldt(new, old->size, 0); | 89 | int err = alloc_ldt(new, old->size, 0); |
90 | int i; | ||
87 | 91 | ||
88 | if (err < 0) | 92 | if (err < 0) |
89 | return err; | 93 | return err; |
90 | memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE); | 94 | |
95 | for(i = 0; i < old->size; i++) | ||
96 | write_ldt_entry(new->ldt, i, old->ldt + i * LDT_ENTRY_SIZE); | ||
91 | return 0; | 97 | return 0; |
92 | } | 98 | } |
93 | 99 | ||
@@ -124,6 +130,7 @@ void destroy_context(struct mm_struct *mm) | |||
124 | if (mm == current->active_mm) | 130 | if (mm == current->active_mm) |
125 | clear_LDT(); | 131 | clear_LDT(); |
126 | #endif | 132 | #endif |
133 | paravirt_free_ldt(mm->context.ldt, mm->context.size); | ||
127 | if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE) | 134 | if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE) |
128 | vfree(mm->context.ldt); | 135 | vfree(mm->context.ldt); |
129 | else | 136 | else |
diff --git a/arch/x86/kernel/paravirt-spinlocks.c b/arch/x86/kernel/paravirt-spinlocks.c new file mode 100644 index 000000000000..38d7f7f1dbc9 --- /dev/null +++ b/arch/x86/kernel/paravirt-spinlocks.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Split spinlock implementation out into its own file, so it can be | ||
3 | * compiled in a FTRACE-compatible way. | ||
4 | */ | ||
5 | #include <linux/spinlock.h> | ||
6 | #include <linux/module.h> | ||
7 | |||
8 | #include <asm/paravirt.h> | ||
9 | |||
10 | struct pv_lock_ops pv_lock_ops = { | ||
11 | #ifdef CONFIG_SMP | ||
12 | .spin_is_locked = __ticket_spin_is_locked, | ||
13 | .spin_is_contended = __ticket_spin_is_contended, | ||
14 | |||
15 | .spin_lock = __ticket_spin_lock, | ||
16 | .spin_trylock = __ticket_spin_trylock, | ||
17 | .spin_unlock = __ticket_spin_unlock, | ||
18 | #endif | ||
19 | }; | ||
20 | EXPORT_SYMBOL_GPL(pv_lock_ops); | ||
21 | |||
22 | void __init paravirt_use_bytelocks(void) | ||
23 | { | ||
24 | #ifdef CONFIG_SMP | ||
25 | pv_lock_ops.spin_is_locked = __byte_spin_is_locked; | ||
26 | pv_lock_ops.spin_is_contended = __byte_spin_is_contended; | ||
27 | pv_lock_ops.spin_lock = __byte_spin_lock; | ||
28 | pv_lock_ops.spin_trylock = __byte_spin_trylock; | ||
29 | pv_lock_ops.spin_unlock = __byte_spin_unlock; | ||
30 | #endif | ||
31 | } | ||
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 94da4d52d798..7faea1817d05 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c | |||
@@ -268,17 +268,6 @@ enum paravirt_lazy_mode paravirt_get_lazy_mode(void) | |||
268 | return __get_cpu_var(paravirt_lazy_mode); | 268 | return __get_cpu_var(paravirt_lazy_mode); |
269 | } | 269 | } |
270 | 270 | ||
271 | void __init paravirt_use_bytelocks(void) | ||
272 | { | ||
273 | #ifdef CONFIG_SMP | ||
274 | pv_lock_ops.spin_is_locked = __byte_spin_is_locked; | ||
275 | pv_lock_ops.spin_is_contended = __byte_spin_is_contended; | ||
276 | pv_lock_ops.spin_lock = __byte_spin_lock; | ||
277 | pv_lock_ops.spin_trylock = __byte_spin_trylock; | ||
278 | pv_lock_ops.spin_unlock = __byte_spin_unlock; | ||
279 | #endif | ||
280 | } | ||
281 | |||
282 | struct pv_info pv_info = { | 271 | struct pv_info pv_info = { |
283 | .name = "bare hardware", | 272 | .name = "bare hardware", |
284 | .paravirt_enabled = 0, | 273 | .paravirt_enabled = 0, |
@@ -348,6 +337,10 @@ struct pv_cpu_ops pv_cpu_ops = { | |||
348 | .write_ldt_entry = native_write_ldt_entry, | 337 | .write_ldt_entry = native_write_ldt_entry, |
349 | .write_gdt_entry = native_write_gdt_entry, | 338 | .write_gdt_entry = native_write_gdt_entry, |
350 | .write_idt_entry = native_write_idt_entry, | 339 | .write_idt_entry = native_write_idt_entry, |
340 | |||
341 | .alloc_ldt = paravirt_nop, | ||
342 | .free_ldt = paravirt_nop, | ||
343 | |||
351 | .load_sp0 = native_load_sp0, | 344 | .load_sp0 = native_load_sp0, |
352 | 345 | ||
353 | #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) | 346 | #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) |
@@ -461,18 +454,6 @@ struct pv_mmu_ops pv_mmu_ops = { | |||
461 | .set_fixmap = native_set_fixmap, | 454 | .set_fixmap = native_set_fixmap, |
462 | }; | 455 | }; |
463 | 456 | ||
464 | struct pv_lock_ops pv_lock_ops = { | ||
465 | #ifdef CONFIG_SMP | ||
466 | .spin_is_locked = __ticket_spin_is_locked, | ||
467 | .spin_is_contended = __ticket_spin_is_contended, | ||
468 | |||
469 | .spin_lock = __ticket_spin_lock, | ||
470 | .spin_trylock = __ticket_spin_trylock, | ||
471 | .spin_unlock = __ticket_spin_unlock, | ||
472 | #endif | ||
473 | }; | ||
474 | EXPORT_SYMBOL_GPL(pv_lock_ops); | ||
475 | |||
476 | EXPORT_SYMBOL_GPL(pv_time_ops); | 457 | EXPORT_SYMBOL_GPL(pv_time_ops); |
477 | EXPORT_SYMBOL (pv_cpu_ops); | 458 | EXPORT_SYMBOL (pv_cpu_ops); |
478 | EXPORT_SYMBOL (pv_mmu_ops); | 459 | EXPORT_SYMBOL (pv_mmu_ops); |
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 59c1e539aed2..9ee745fa5527 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile | |||
@@ -1,4 +1,11 @@ | |||
1 | obj-y := enlighten.o setup.o multicalls.o mmu.o \ | 1 | ifdef CONFIG_FTRACE |
2 | # Do not profile debug and lowlevel utilities | ||
3 | CFLAGS_REMOVE_spinlock.o = -pg | ||
4 | CFLAGS_REMOVE_time.o = -pg | ||
5 | CFLAGS_REMOVE_irq.o = -pg | ||
6 | endif | ||
7 | |||
8 | obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \ | ||
2 | time.o xen-asm_$(BITS).o grant-table.o suspend.o | 9 | time.o xen-asm_$(BITS).o grant-table.o suspend.o |
3 | 10 | ||
4 | obj-$(CONFIG_SMP) += smp.o | 11 | obj-$(CONFIG_SMP) += smp.o spinlock.o |
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 9ff6e3cbf08f..cf8b3a93122b 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <xen/interface/xen.h> | 30 | #include <xen/interface/xen.h> |
31 | #include <xen/interface/physdev.h> | 31 | #include <xen/interface/physdev.h> |
32 | #include <xen/interface/vcpu.h> | 32 | #include <xen/interface/vcpu.h> |
33 | #include <xen/interface/sched.h> | ||
34 | #include <xen/features.h> | 33 | #include <xen/features.h> |
35 | #include <xen/page.h> | 34 | #include <xen/page.h> |
36 | #include <xen/hvc-console.h> | 35 | #include <xen/hvc-console.h> |
@@ -226,103 +225,66 @@ static unsigned long xen_get_debugreg(int reg) | |||
226 | return HYPERVISOR_get_debugreg(reg); | 225 | return HYPERVISOR_get_debugreg(reg); |
227 | } | 226 | } |
228 | 227 | ||
229 | static unsigned long xen_save_fl(void) | 228 | static void xen_leave_lazy(void) |
230 | { | 229 | { |
231 | struct vcpu_info *vcpu; | 230 | paravirt_leave_lazy(paravirt_get_lazy_mode()); |
232 | unsigned long flags; | 231 | xen_mc_flush(); |
233 | |||
234 | vcpu = x86_read_percpu(xen_vcpu); | ||
235 | |||
236 | /* flag has opposite sense of mask */ | ||
237 | flags = !vcpu->evtchn_upcall_mask; | ||
238 | |||
239 | /* convert to IF type flag | ||
240 | -0 -> 0x00000000 | ||
241 | -1 -> 0xffffffff | ||
242 | */ | ||
243 | return (-flags) & X86_EFLAGS_IF; | ||
244 | } | 232 | } |
245 | 233 | ||
246 | static void xen_restore_fl(unsigned long flags) | 234 | static unsigned long xen_store_tr(void) |
247 | { | 235 | { |
248 | struct vcpu_info *vcpu; | 236 | return 0; |
249 | |||
250 | /* convert from IF type flag */ | ||
251 | flags = !(flags & X86_EFLAGS_IF); | ||
252 | |||
253 | /* There's a one instruction preempt window here. We need to | ||
254 | make sure we're don't switch CPUs between getting the vcpu | ||
255 | pointer and updating the mask. */ | ||
256 | preempt_disable(); | ||
257 | vcpu = x86_read_percpu(xen_vcpu); | ||
258 | vcpu->evtchn_upcall_mask = flags; | ||
259 | preempt_enable_no_resched(); | ||
260 | |||
261 | /* Doesn't matter if we get preempted here, because any | ||
262 | pending event will get dealt with anyway. */ | ||
263 | |||
264 | if (flags == 0) { | ||
265 | preempt_check_resched(); | ||
266 | barrier(); /* unmask then check (avoid races) */ | ||
267 | if (unlikely(vcpu->evtchn_upcall_pending)) | ||
268 | force_evtchn_callback(); | ||
269 | } | ||
270 | } | 237 | } |
271 | 238 | ||
272 | static void xen_irq_disable(void) | 239 | /* |
240 | * If 'v' is a vmalloc mapping, then find the linear mapping of the | ||
241 | * page (if any) and also set its protections to match: | ||
242 | */ | ||
243 | static void set_aliased_prot(void *v, pgprot_t prot) | ||
273 | { | 244 | { |
274 | /* There's a one instruction preempt window here. We need to | 245 | int level; |
275 | make sure we're don't switch CPUs between getting the vcpu | 246 | pte_t *ptep; |
276 | pointer and updating the mask. */ | 247 | pte_t pte; |
277 | preempt_disable(); | 248 | unsigned long pfn; |
278 | x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; | 249 | struct page *page; |
279 | preempt_enable_no_resched(); | ||
280 | } | ||
281 | 250 | ||
282 | static void xen_irq_enable(void) | 251 | ptep = lookup_address((unsigned long)v, &level); |
283 | { | 252 | BUG_ON(ptep == NULL); |
284 | struct vcpu_info *vcpu; | ||
285 | 253 | ||
286 | /* We don't need to worry about being preempted here, since | 254 | pfn = pte_pfn(*ptep); |
287 | either a) interrupts are disabled, so no preemption, or b) | 255 | page = pfn_to_page(pfn); |
288 | the caller is confused and is trying to re-enable interrupts | ||
289 | on an indeterminate processor. */ | ||
290 | 256 | ||
291 | vcpu = x86_read_percpu(xen_vcpu); | 257 | pte = pfn_pte(pfn, prot); |
292 | vcpu->evtchn_upcall_mask = 0; | ||
293 | 258 | ||
294 | /* Doesn't matter if we get preempted here, because any | 259 | if (HYPERVISOR_update_va_mapping((unsigned long)v, pte, 0)) |
295 | pending event will get dealt with anyway. */ | 260 | BUG(); |
296 | 261 | ||
297 | barrier(); /* unmask then check (avoid races) */ | 262 | if (!PageHighMem(page)) { |
298 | if (unlikely(vcpu->evtchn_upcall_pending)) | 263 | void *av = __va(PFN_PHYS(pfn)); |
299 | force_evtchn_callback(); | ||
300 | } | ||
301 | 264 | ||
302 | static void xen_safe_halt(void) | 265 | if (av != v) |
303 | { | 266 | if (HYPERVISOR_update_va_mapping((unsigned long)av, pte, 0)) |
304 | /* Blocking includes an implicit local_irq_enable(). */ | 267 | BUG(); |
305 | if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) | 268 | } else |
306 | BUG(); | 269 | kmap_flush_unused(); |
307 | } | 270 | } |
308 | 271 | ||
309 | static void xen_halt(void) | 272 | static void xen_alloc_ldt(struct desc_struct *ldt, unsigned entries) |
310 | { | 273 | { |
311 | if (irqs_disabled()) | 274 | const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; |
312 | HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); | 275 | int i; |
313 | else | ||
314 | xen_safe_halt(); | ||
315 | } | ||
316 | 276 | ||
317 | static void xen_leave_lazy(void) | 277 | for(i = 0; i < entries; i += entries_per_page) |
318 | { | 278 | set_aliased_prot(ldt + i, PAGE_KERNEL_RO); |
319 | paravirt_leave_lazy(paravirt_get_lazy_mode()); | ||
320 | xen_mc_flush(); | ||
321 | } | 279 | } |
322 | 280 | ||
323 | static unsigned long xen_store_tr(void) | 281 | static void xen_free_ldt(struct desc_struct *ldt, unsigned entries) |
324 | { | 282 | { |
325 | return 0; | 283 | const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; |
284 | int i; | ||
285 | |||
286 | for(i = 0; i < entries; i += entries_per_page) | ||
287 | set_aliased_prot(ldt + i, PAGE_KERNEL); | ||
326 | } | 288 | } |
327 | 289 | ||
328 | static void xen_set_ldt(const void *addr, unsigned entries) | 290 | static void xen_set_ldt(const void *addr, unsigned entries) |
@@ -426,7 +388,7 @@ static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum, | |||
426 | const void *ptr) | 388 | const void *ptr) |
427 | { | 389 | { |
428 | unsigned long lp = (unsigned long)&dt[entrynum]; | 390 | unsigned long lp = (unsigned long)&dt[entrynum]; |
429 | xmaddr_t mach_lp = virt_to_machine(lp); | 391 | xmaddr_t mach_lp = arbitrary_virt_to_machine(lp); |
430 | u64 entry = *(u64 *)ptr; | 392 | u64 entry = *(u64 *)ptr; |
431 | 393 | ||
432 | preempt_disable(); | 394 | preempt_disable(); |
@@ -559,7 +521,7 @@ static void xen_write_gdt_entry(struct desc_struct *dt, int entry, | |||
559 | } | 521 | } |
560 | 522 | ||
561 | static void xen_load_sp0(struct tss_struct *tss, | 523 | static void xen_load_sp0(struct tss_struct *tss, |
562 | struct thread_struct *thread) | 524 | struct thread_struct *thread) |
563 | { | 525 | { |
564 | struct multicall_space mcs = xen_mc_entry(0); | 526 | struct multicall_space mcs = xen_mc_entry(0); |
565 | MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->sp0); | 527 | MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->sp0); |
@@ -803,6 +765,19 @@ static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high) | |||
803 | ret = -EFAULT; | 765 | ret = -EFAULT; |
804 | break; | 766 | break; |
805 | #endif | 767 | #endif |
768 | |||
769 | case MSR_STAR: | ||
770 | case MSR_CSTAR: | ||
771 | case MSR_LSTAR: | ||
772 | case MSR_SYSCALL_MASK: | ||
773 | case MSR_IA32_SYSENTER_CS: | ||
774 | case MSR_IA32_SYSENTER_ESP: | ||
775 | case MSR_IA32_SYSENTER_EIP: | ||
776 | /* Fast syscall setup is all done in hypercalls, so | ||
777 | these are all ignored. Stub them out here to stop | ||
778 | Xen console noise. */ | ||
779 | break; | ||
780 | |||
806 | default: | 781 | default: |
807 | ret = native_write_msr_safe(msr, low, high); | 782 | ret = native_write_msr_safe(msr, low, high); |
808 | } | 783 | } |
@@ -1220,6 +1195,9 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { | |||
1220 | .load_gs_index = xen_load_gs_index, | 1195 | .load_gs_index = xen_load_gs_index, |
1221 | #endif | 1196 | #endif |
1222 | 1197 | ||
1198 | .alloc_ldt = xen_alloc_ldt, | ||
1199 | .free_ldt = xen_free_ldt, | ||
1200 | |||
1223 | .store_gdt = native_store_gdt, | 1201 | .store_gdt = native_store_gdt, |
1224 | .store_idt = native_store_idt, | 1202 | .store_idt = native_store_idt, |
1225 | .store_tr = xen_store_tr, | 1203 | .store_tr = xen_store_tr, |
@@ -1241,36 +1219,6 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { | |||
1241 | }, | 1219 | }, |
1242 | }; | 1220 | }; |
1243 | 1221 | ||
1244 | static void __init __xen_init_IRQ(void) | ||
1245 | { | ||
1246 | #ifdef CONFIG_X86_64 | ||
1247 | int i; | ||
1248 | |||
1249 | /* Create identity vector->irq map */ | ||
1250 | for(i = 0; i < NR_VECTORS; i++) { | ||
1251 | int cpu; | ||
1252 | |||
1253 | for_each_possible_cpu(cpu) | ||
1254 | per_cpu(vector_irq, cpu)[i] = i; | ||
1255 | } | ||
1256 | #endif /* CONFIG_X86_64 */ | ||
1257 | |||
1258 | xen_init_IRQ(); | ||
1259 | } | ||
1260 | |||
1261 | static const struct pv_irq_ops xen_irq_ops __initdata = { | ||
1262 | .init_IRQ = __xen_init_IRQ, | ||
1263 | .save_fl = xen_save_fl, | ||
1264 | .restore_fl = xen_restore_fl, | ||
1265 | .irq_disable = xen_irq_disable, | ||
1266 | .irq_enable = xen_irq_enable, | ||
1267 | .safe_halt = xen_safe_halt, | ||
1268 | .halt = xen_halt, | ||
1269 | #ifdef CONFIG_X86_64 | ||
1270 | .adjust_exception_frame = xen_adjust_exception_frame, | ||
1271 | #endif | ||
1272 | }; | ||
1273 | |||
1274 | static const struct pv_apic_ops xen_apic_ops __initdata = { | 1222 | static const struct pv_apic_ops xen_apic_ops __initdata = { |
1275 | #ifdef CONFIG_X86_LOCAL_APIC | 1223 | #ifdef CONFIG_X86_LOCAL_APIC |
1276 | .apic_write = xen_apic_write, | 1224 | .apic_write = xen_apic_write, |
@@ -1324,7 +1272,7 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = { | |||
1324 | .ptep_modify_prot_commit = __ptep_modify_prot_commit, | 1272 | .ptep_modify_prot_commit = __ptep_modify_prot_commit, |
1325 | 1273 | ||
1326 | .pte_val = xen_pte_val, | 1274 | .pte_val = xen_pte_val, |
1327 | .pte_flags = native_pte_val, | 1275 | .pte_flags = native_pte_flags, |
1328 | .pgd_val = xen_pgd_val, | 1276 | .pgd_val = xen_pgd_val, |
1329 | 1277 | ||
1330 | .make_pte = xen_make_pte, | 1278 | .make_pte = xen_make_pte, |
@@ -1673,10 +1621,11 @@ asmlinkage void __init xen_start_kernel(void) | |||
1673 | pv_init_ops = xen_init_ops; | 1621 | pv_init_ops = xen_init_ops; |
1674 | pv_time_ops = xen_time_ops; | 1622 | pv_time_ops = xen_time_ops; |
1675 | pv_cpu_ops = xen_cpu_ops; | 1623 | pv_cpu_ops = xen_cpu_ops; |
1676 | pv_irq_ops = xen_irq_ops; | ||
1677 | pv_apic_ops = xen_apic_ops; | 1624 | pv_apic_ops = xen_apic_ops; |
1678 | pv_mmu_ops = xen_mmu_ops; | 1625 | pv_mmu_ops = xen_mmu_ops; |
1679 | 1626 | ||
1627 | xen_init_irq_ops(); | ||
1628 | |||
1680 | if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) { | 1629 | if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) { |
1681 | pv_mmu_ops.ptep_modify_prot_start = xen_ptep_modify_prot_start; | 1630 | pv_mmu_ops.ptep_modify_prot_start = xen_ptep_modify_prot_start; |
1682 | pv_mmu_ops.ptep_modify_prot_commit = xen_ptep_modify_prot_commit; | 1631 | pv_mmu_ops.ptep_modify_prot_commit = xen_ptep_modify_prot_commit; |
diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c new file mode 100644 index 000000000000..28b85ab8422e --- /dev/null +++ b/arch/x86/xen/irq.c | |||
@@ -0,0 +1,143 @@ | |||
1 | #include <linux/hardirq.h> | ||
2 | |||
3 | #include <xen/interface/xen.h> | ||
4 | #include <xen/interface/sched.h> | ||
5 | #include <xen/interface/vcpu.h> | ||
6 | |||
7 | #include <asm/xen/hypercall.h> | ||
8 | #include <asm/xen/hypervisor.h> | ||
9 | |||
10 | #include "xen-ops.h" | ||
11 | |||
12 | /* | ||
13 | * Force a proper event-channel callback from Xen after clearing the | ||
14 | * callback mask. We do this in a very simple manner, by making a call | ||
15 | * down into Xen. The pending flag will be checked by Xen on return. | ||
16 | */ | ||
17 | void xen_force_evtchn_callback(void) | ||
18 | { | ||
19 | (void)HYPERVISOR_xen_version(0, NULL); | ||
20 | } | ||
21 | |||
22 | static void __init __xen_init_IRQ(void) | ||
23 | { | ||
24 | #ifdef CONFIG_X86_64 | ||
25 | int i; | ||
26 | |||
27 | /* Create identity vector->irq map */ | ||
28 | for(i = 0; i < NR_VECTORS; i++) { | ||
29 | int cpu; | ||
30 | |||
31 | for_each_possible_cpu(cpu) | ||
32 | per_cpu(vector_irq, cpu)[i] = i; | ||
33 | } | ||
34 | #endif /* CONFIG_X86_64 */ | ||
35 | |||
36 | xen_init_IRQ(); | ||
37 | } | ||
38 | |||
39 | static unsigned long xen_save_fl(void) | ||
40 | { | ||
41 | struct vcpu_info *vcpu; | ||
42 | unsigned long flags; | ||
43 | |||
44 | vcpu = x86_read_percpu(xen_vcpu); | ||
45 | |||
46 | /* flag has opposite sense of mask */ | ||
47 | flags = !vcpu->evtchn_upcall_mask; | ||
48 | |||
49 | /* convert to IF type flag | ||
50 | -0 -> 0x00000000 | ||
51 | -1 -> 0xffffffff | ||
52 | */ | ||
53 | return (-flags) & X86_EFLAGS_IF; | ||
54 | } | ||
55 | |||
56 | static void xen_restore_fl(unsigned long flags) | ||
57 | { | ||
58 | struct vcpu_info *vcpu; | ||
59 | |||
60 | /* convert from IF type flag */ | ||
61 | flags = !(flags & X86_EFLAGS_IF); | ||
62 | |||
63 | /* There's a one instruction preempt window here. We need to | ||
64 | make sure we're don't switch CPUs between getting the vcpu | ||
65 | pointer and updating the mask. */ | ||
66 | preempt_disable(); | ||
67 | vcpu = x86_read_percpu(xen_vcpu); | ||
68 | vcpu->evtchn_upcall_mask = flags; | ||
69 | preempt_enable_no_resched(); | ||
70 | |||
71 | /* Doesn't matter if we get preempted here, because any | ||
72 | pending event will get dealt with anyway. */ | ||
73 | |||
74 | if (flags == 0) { | ||
75 | preempt_check_resched(); | ||
76 | barrier(); /* unmask then check (avoid races) */ | ||
77 | if (unlikely(vcpu->evtchn_upcall_pending)) | ||
78 | xen_force_evtchn_callback(); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void xen_irq_disable(void) | ||
83 | { | ||
84 | /* There's a one instruction preempt window here. We need to | ||
85 | make sure we're don't switch CPUs between getting the vcpu | ||
86 | pointer and updating the mask. */ | ||
87 | preempt_disable(); | ||
88 | x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; | ||
89 | preempt_enable_no_resched(); | ||
90 | } | ||
91 | |||
92 | static void xen_irq_enable(void) | ||
93 | { | ||
94 | struct vcpu_info *vcpu; | ||
95 | |||
96 | /* We don't need to worry about being preempted here, since | ||
97 | either a) interrupts are disabled, so no preemption, or b) | ||
98 | the caller is confused and is trying to re-enable interrupts | ||
99 | on an indeterminate processor. */ | ||
100 | |||
101 | vcpu = x86_read_percpu(xen_vcpu); | ||
102 | vcpu->evtchn_upcall_mask = 0; | ||
103 | |||
104 | /* Doesn't matter if we get preempted here, because any | ||
105 | pending event will get dealt with anyway. */ | ||
106 | |||
107 | barrier(); /* unmask then check (avoid races) */ | ||
108 | if (unlikely(vcpu->evtchn_upcall_pending)) | ||
109 | xen_force_evtchn_callback(); | ||
110 | } | ||
111 | |||
112 | static void xen_safe_halt(void) | ||
113 | { | ||
114 | /* Blocking includes an implicit local_irq_enable(). */ | ||
115 | if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) | ||
116 | BUG(); | ||
117 | } | ||
118 | |||
119 | static void xen_halt(void) | ||
120 | { | ||
121 | if (irqs_disabled()) | ||
122 | HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); | ||
123 | else | ||
124 | xen_safe_halt(); | ||
125 | } | ||
126 | |||
127 | static const struct pv_irq_ops xen_irq_ops __initdata = { | ||
128 | .init_IRQ = __xen_init_IRQ, | ||
129 | .save_fl = xen_save_fl, | ||
130 | .restore_fl = xen_restore_fl, | ||
131 | .irq_disable = xen_irq_disable, | ||
132 | .irq_enable = xen_irq_enable, | ||
133 | .safe_halt = xen_safe_halt, | ||
134 | .halt = xen_halt, | ||
135 | #ifdef CONFIG_X86_64 | ||
136 | .adjust_exception_frame = xen_adjust_exception_frame, | ||
137 | #endif | ||
138 | }; | ||
139 | |||
140 | void __init xen_init_irq_ops() | ||
141 | { | ||
142 | pv_irq_ops = xen_irq_ops; | ||
143 | } | ||
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index d8faf79a0a1d..baca7f2fbd8a 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c | |||
@@ -15,7 +15,6 @@ | |||
15 | * This does not handle HOTPLUG_CPU yet. | 15 | * This does not handle HOTPLUG_CPU yet. |
16 | */ | 16 | */ |
17 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
18 | #include <linux/kernel_stat.h> | ||
19 | #include <linux/err.h> | 18 | #include <linux/err.h> |
20 | #include <linux/smp.h> | 19 | #include <linux/smp.h> |
21 | 20 | ||
@@ -36,8 +35,6 @@ | |||
36 | #include "xen-ops.h" | 35 | #include "xen-ops.h" |
37 | #include "mmu.h" | 36 | #include "mmu.h" |
38 | 37 | ||
39 | static void __cpuinit xen_init_lock_cpu(int cpu); | ||
40 | |||
41 | cpumask_t xen_cpu_initialized_map; | 38 | cpumask_t xen_cpu_initialized_map; |
42 | 39 | ||
43 | static DEFINE_PER_CPU(int, resched_irq); | 40 | static DEFINE_PER_CPU(int, resched_irq); |
@@ -419,170 +416,6 @@ static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id) | |||
419 | return IRQ_HANDLED; | 416 | return IRQ_HANDLED; |
420 | } | 417 | } |
421 | 418 | ||
422 | struct xen_spinlock { | ||
423 | unsigned char lock; /* 0 -> free; 1 -> locked */ | ||
424 | unsigned short spinners; /* count of waiting cpus */ | ||
425 | }; | ||
426 | |||
427 | static int xen_spin_is_locked(struct raw_spinlock *lock) | ||
428 | { | ||
429 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
430 | |||
431 | return xl->lock != 0; | ||
432 | } | ||
433 | |||
434 | static int xen_spin_is_contended(struct raw_spinlock *lock) | ||
435 | { | ||
436 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
437 | |||
438 | /* Not strictly true; this is only the count of contended | ||
439 | lock-takers entering the slow path. */ | ||
440 | return xl->spinners != 0; | ||
441 | } | ||
442 | |||
443 | static int xen_spin_trylock(struct raw_spinlock *lock) | ||
444 | { | ||
445 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
446 | u8 old = 1; | ||
447 | |||
448 | asm("xchgb %b0,%1" | ||
449 | : "+q" (old), "+m" (xl->lock) : : "memory"); | ||
450 | |||
451 | return old == 0; | ||
452 | } | ||
453 | |||
454 | static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; | ||
455 | static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); | ||
456 | |||
457 | static inline void spinning_lock(struct xen_spinlock *xl) | ||
458 | { | ||
459 | __get_cpu_var(lock_spinners) = xl; | ||
460 | wmb(); /* set lock of interest before count */ | ||
461 | asm(LOCK_PREFIX " incw %0" | ||
462 | : "+m" (xl->spinners) : : "memory"); | ||
463 | } | ||
464 | |||
465 | static inline void unspinning_lock(struct xen_spinlock *xl) | ||
466 | { | ||
467 | asm(LOCK_PREFIX " decw %0" | ||
468 | : "+m" (xl->spinners) : : "memory"); | ||
469 | wmb(); /* decrement count before clearing lock */ | ||
470 | __get_cpu_var(lock_spinners) = NULL; | ||
471 | } | ||
472 | |||
473 | static noinline int xen_spin_lock_slow(struct raw_spinlock *lock) | ||
474 | { | ||
475 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
476 | int irq = __get_cpu_var(lock_kicker_irq); | ||
477 | int ret; | ||
478 | |||
479 | /* If kicker interrupts not initialized yet, just spin */ | ||
480 | if (irq == -1) | ||
481 | return 0; | ||
482 | |||
483 | /* announce we're spinning */ | ||
484 | spinning_lock(xl); | ||
485 | |||
486 | /* clear pending */ | ||
487 | xen_clear_irq_pending(irq); | ||
488 | |||
489 | /* check again make sure it didn't become free while | ||
490 | we weren't looking */ | ||
491 | ret = xen_spin_trylock(lock); | ||
492 | if (ret) | ||
493 | goto out; | ||
494 | |||
495 | /* block until irq becomes pending */ | ||
496 | xen_poll_irq(irq); | ||
497 | kstat_this_cpu.irqs[irq]++; | ||
498 | |||
499 | out: | ||
500 | unspinning_lock(xl); | ||
501 | return ret; | ||
502 | } | ||
503 | |||
504 | static void xen_spin_lock(struct raw_spinlock *lock) | ||
505 | { | ||
506 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
507 | int timeout; | ||
508 | u8 oldval; | ||
509 | |||
510 | do { | ||
511 | timeout = 1 << 10; | ||
512 | |||
513 | asm("1: xchgb %1,%0\n" | ||
514 | " testb %1,%1\n" | ||
515 | " jz 3f\n" | ||
516 | "2: rep;nop\n" | ||
517 | " cmpb $0,%0\n" | ||
518 | " je 1b\n" | ||
519 | " dec %2\n" | ||
520 | " jnz 2b\n" | ||
521 | "3:\n" | ||
522 | : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) | ||
523 | : "1" (1) | ||
524 | : "memory"); | ||
525 | |||
526 | } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock))); | ||
527 | } | ||
528 | |||
529 | static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) | ||
530 | { | ||
531 | int cpu; | ||
532 | |||
533 | for_each_online_cpu(cpu) { | ||
534 | /* XXX should mix up next cpu selection */ | ||
535 | if (per_cpu(lock_spinners, cpu) == xl) { | ||
536 | xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); | ||
537 | break; | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | |||
542 | static void xen_spin_unlock(struct raw_spinlock *lock) | ||
543 | { | ||
544 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
545 | |||
546 | smp_wmb(); /* make sure no writes get moved after unlock */ | ||
547 | xl->lock = 0; /* release lock */ | ||
548 | |||
549 | /* make sure unlock happens before kick */ | ||
550 | barrier(); | ||
551 | |||
552 | if (unlikely(xl->spinners)) | ||
553 | xen_spin_unlock_slow(xl); | ||
554 | } | ||
555 | |||
556 | static __cpuinit void xen_init_lock_cpu(int cpu) | ||
557 | { | ||
558 | int irq; | ||
559 | const char *name; | ||
560 | |||
561 | name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); | ||
562 | irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, | ||
563 | cpu, | ||
564 | xen_reschedule_interrupt, | ||
565 | IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, | ||
566 | name, | ||
567 | NULL); | ||
568 | |||
569 | if (irq >= 0) { | ||
570 | disable_irq(irq); /* make sure it's never delivered */ | ||
571 | per_cpu(lock_kicker_irq, cpu) = irq; | ||
572 | } | ||
573 | |||
574 | printk("cpu %d spinlock event irq %d\n", cpu, irq); | ||
575 | } | ||
576 | |||
577 | static void __init xen_init_spinlocks(void) | ||
578 | { | ||
579 | pv_lock_ops.spin_is_locked = xen_spin_is_locked; | ||
580 | pv_lock_ops.spin_is_contended = xen_spin_is_contended; | ||
581 | pv_lock_ops.spin_lock = xen_spin_lock; | ||
582 | pv_lock_ops.spin_trylock = xen_spin_trylock; | ||
583 | pv_lock_ops.spin_unlock = xen_spin_unlock; | ||
584 | } | ||
585 | |||
586 | static const struct smp_ops xen_smp_ops __initdata = { | 419 | static const struct smp_ops xen_smp_ops __initdata = { |
587 | .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, | 420 | .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, |
588 | .smp_prepare_cpus = xen_smp_prepare_cpus, | 421 | .smp_prepare_cpus = xen_smp_prepare_cpus, |
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c new file mode 100644 index 000000000000..8dc4d31da67f --- /dev/null +++ b/arch/x86/xen/spinlock.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Split spinlock implementation out into its own file, so it can be | ||
3 | * compiled in a FTRACE-compatible way. | ||
4 | */ | ||
5 | #include <linux/kernel_stat.h> | ||
6 | #include <linux/spinlock.h> | ||
7 | |||
8 | #include <asm/paravirt.h> | ||
9 | |||
10 | #include <xen/interface/xen.h> | ||
11 | #include <xen/events.h> | ||
12 | |||
13 | #include "xen-ops.h" | ||
14 | |||
15 | struct xen_spinlock { | ||
16 | unsigned char lock; /* 0 -> free; 1 -> locked */ | ||
17 | unsigned short spinners; /* count of waiting cpus */ | ||
18 | }; | ||
19 | |||
20 | static int xen_spin_is_locked(struct raw_spinlock *lock) | ||
21 | { | ||
22 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
23 | |||
24 | return xl->lock != 0; | ||
25 | } | ||
26 | |||
27 | static int xen_spin_is_contended(struct raw_spinlock *lock) | ||
28 | { | ||
29 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
30 | |||
31 | /* Not strictly true; this is only the count of contended | ||
32 | lock-takers entering the slow path. */ | ||
33 | return xl->spinners != 0; | ||
34 | } | ||
35 | |||
36 | static int xen_spin_trylock(struct raw_spinlock *lock) | ||
37 | { | ||
38 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
39 | u8 old = 1; | ||
40 | |||
41 | asm("xchgb %b0,%1" | ||
42 | : "+q" (old), "+m" (xl->lock) : : "memory"); | ||
43 | |||
44 | return old == 0; | ||
45 | } | ||
46 | |||
47 | static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; | ||
48 | static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); | ||
49 | |||
50 | static inline void spinning_lock(struct xen_spinlock *xl) | ||
51 | { | ||
52 | __get_cpu_var(lock_spinners) = xl; | ||
53 | wmb(); /* set lock of interest before count */ | ||
54 | asm(LOCK_PREFIX " incw %0" | ||
55 | : "+m" (xl->spinners) : : "memory"); | ||
56 | } | ||
57 | |||
58 | static inline void unspinning_lock(struct xen_spinlock *xl) | ||
59 | { | ||
60 | asm(LOCK_PREFIX " decw %0" | ||
61 | : "+m" (xl->spinners) : : "memory"); | ||
62 | wmb(); /* decrement count before clearing lock */ | ||
63 | __get_cpu_var(lock_spinners) = NULL; | ||
64 | } | ||
65 | |||
66 | static noinline int xen_spin_lock_slow(struct raw_spinlock *lock) | ||
67 | { | ||
68 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
69 | int irq = __get_cpu_var(lock_kicker_irq); | ||
70 | int ret; | ||
71 | |||
72 | /* If kicker interrupts not initialized yet, just spin */ | ||
73 | if (irq == -1) | ||
74 | return 0; | ||
75 | |||
76 | /* announce we're spinning */ | ||
77 | spinning_lock(xl); | ||
78 | |||
79 | /* clear pending */ | ||
80 | xen_clear_irq_pending(irq); | ||
81 | |||
82 | /* check again make sure it didn't become free while | ||
83 | we weren't looking */ | ||
84 | ret = xen_spin_trylock(lock); | ||
85 | if (ret) | ||
86 | goto out; | ||
87 | |||
88 | /* block until irq becomes pending */ | ||
89 | xen_poll_irq(irq); | ||
90 | kstat_this_cpu.irqs[irq]++; | ||
91 | |||
92 | out: | ||
93 | unspinning_lock(xl); | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | static void xen_spin_lock(struct raw_spinlock *lock) | ||
98 | { | ||
99 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
100 | int timeout; | ||
101 | u8 oldval; | ||
102 | |||
103 | do { | ||
104 | timeout = 1 << 10; | ||
105 | |||
106 | asm("1: xchgb %1,%0\n" | ||
107 | " testb %1,%1\n" | ||
108 | " jz 3f\n" | ||
109 | "2: rep;nop\n" | ||
110 | " cmpb $0,%0\n" | ||
111 | " je 1b\n" | ||
112 | " dec %2\n" | ||
113 | " jnz 2b\n" | ||
114 | "3:\n" | ||
115 | : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) | ||
116 | : "1" (1) | ||
117 | : "memory"); | ||
118 | |||
119 | } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock))); | ||
120 | } | ||
121 | |||
122 | static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) | ||
123 | { | ||
124 | int cpu; | ||
125 | |||
126 | for_each_online_cpu(cpu) { | ||
127 | /* XXX should mix up next cpu selection */ | ||
128 | if (per_cpu(lock_spinners, cpu) == xl) { | ||
129 | xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); | ||
130 | break; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | static void xen_spin_unlock(struct raw_spinlock *lock) | ||
136 | { | ||
137 | struct xen_spinlock *xl = (struct xen_spinlock *)lock; | ||
138 | |||
139 | smp_wmb(); /* make sure no writes get moved after unlock */ | ||
140 | xl->lock = 0; /* release lock */ | ||
141 | |||
142 | /* make sure unlock happens before kick */ | ||
143 | barrier(); | ||
144 | |||
145 | if (unlikely(xl->spinners)) | ||
146 | xen_spin_unlock_slow(xl); | ||
147 | } | ||
148 | |||
149 | static irqreturn_t dummy_handler(int irq, void *dev_id) | ||
150 | { | ||
151 | BUG(); | ||
152 | return IRQ_HANDLED; | ||
153 | } | ||
154 | |||
155 | void __cpuinit xen_init_lock_cpu(int cpu) | ||
156 | { | ||
157 | int irq; | ||
158 | const char *name; | ||
159 | |||
160 | name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); | ||
161 | irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, | ||
162 | cpu, | ||
163 | dummy_handler, | ||
164 | IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, | ||
165 | name, | ||
166 | NULL); | ||
167 | |||
168 | if (irq >= 0) { | ||
169 | disable_irq(irq); /* make sure it's never delivered */ | ||
170 | per_cpu(lock_kicker_irq, cpu) = irq; | ||
171 | } | ||
172 | |||
173 | printk("cpu %d spinlock event irq %d\n", cpu, irq); | ||
174 | } | ||
175 | |||
176 | void __init xen_init_spinlocks(void) | ||
177 | { | ||
178 | pv_lock_ops.spin_is_locked = xen_spin_is_locked; | ||
179 | pv_lock_ops.spin_is_contended = xen_spin_is_contended; | ||
180 | pv_lock_ops.spin_lock = xen_spin_lock; | ||
181 | pv_lock_ops.spin_trylock = xen_spin_trylock; | ||
182 | pv_lock_ops.spin_unlock = xen_spin_unlock; | ||
183 | } | ||
diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S index 2497a30f41de..42786f59d9c0 100644 --- a/arch/x86/xen/xen-asm_32.S +++ b/arch/x86/xen/xen-asm_32.S | |||
@@ -298,7 +298,7 @@ check_events: | |||
298 | push %eax | 298 | push %eax |
299 | push %ecx | 299 | push %ecx |
300 | push %edx | 300 | push %edx |
301 | call force_evtchn_callback | 301 | call xen_force_evtchn_callback |
302 | pop %edx | 302 | pop %edx |
303 | pop %ecx | 303 | pop %ecx |
304 | pop %eax | 304 | pop %eax |
diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S index 7f58304fafb3..3b9bda46487a 100644 --- a/arch/x86/xen/xen-asm_64.S +++ b/arch/x86/xen/xen-asm_64.S | |||
@@ -122,7 +122,7 @@ check_events: | |||
122 | push %r9 | 122 | push %r9 |
123 | push %r10 | 123 | push %r10 |
124 | push %r11 | 124 | push %r11 |
125 | call force_evtchn_callback | 125 | call xen_force_evtchn_callback |
126 | pop %r11 | 126 | pop %r11 |
127 | pop %r10 | 127 | pop %r10 |
128 | pop %r9 | 128 | pop %r9 |
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index dd3c23152a2e..3c70ebc50b1b 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h | |||
@@ -31,6 +31,7 @@ void xen_vcpu_restore(void); | |||
31 | 31 | ||
32 | void __init xen_build_dynamic_phys_to_machine(void); | 32 | void __init xen_build_dynamic_phys_to_machine(void); |
33 | 33 | ||
34 | void xen_init_irq_ops(void); | ||
34 | void xen_setup_timer(int cpu); | 35 | void xen_setup_timer(int cpu); |
35 | void xen_setup_cpu_clockevents(void); | 36 | void xen_setup_cpu_clockevents(void); |
36 | unsigned long xen_tsc_khz(void); | 37 | unsigned long xen_tsc_khz(void); |
@@ -50,6 +51,9 @@ void __init xen_setup_vcpu_info_placement(void); | |||
50 | #ifdef CONFIG_SMP | 51 | #ifdef CONFIG_SMP |
51 | void xen_smp_init(void); | 52 | void xen_smp_init(void); |
52 | 53 | ||
54 | void __init xen_init_spinlocks(void); | ||
55 | __cpuinit void xen_init_lock_cpu(int cpu); | ||
56 | |||
53 | extern cpumask_t xen_cpu_initialized_map; | 57 | extern cpumask_t xen_cpu_initialized_map; |
54 | #else | 58 | #else |
55 | static inline void xen_smp_init(void) {} | 59 | static inline void xen_smp_init(void) {} |
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index d4427cb86979..fff987b10e0f 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c | |||
@@ -60,7 +60,7 @@ | |||
60 | 60 | ||
61 | #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) | 61 | #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) |
62 | 62 | ||
63 | #define BALLOON_CLASS_NAME "memory" | 63 | #define BALLOON_CLASS_NAME "xen_memory" |
64 | 64 | ||
65 | struct balloon_stats { | 65 | struct balloon_stats { |
66 | /* We aim for 'current allocation' == 'target allocation'. */ | 66 | /* We aim for 'current allocation' == 'target allocation'. */ |
@@ -226,9 +226,8 @@ static int increase_reservation(unsigned long nr_pages) | |||
226 | } | 226 | } |
227 | 227 | ||
228 | set_xen_guest_handle(reservation.extent_start, frame_list); | 228 | set_xen_guest_handle(reservation.extent_start, frame_list); |
229 | reservation.nr_extents = nr_pages; | 229 | reservation.nr_extents = nr_pages; |
230 | rc = HYPERVISOR_memory_op( | 230 | rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); |
231 | XENMEM_populate_physmap, &reservation); | ||
232 | if (rc < nr_pages) { | 231 | if (rc < nr_pages) { |
233 | if (rc > 0) { | 232 | if (rc > 0) { |
234 | int ret; | 233 | int ret; |
@@ -236,7 +235,7 @@ static int increase_reservation(unsigned long nr_pages) | |||
236 | /* We hit the Xen hard limit: reprobe. */ | 235 | /* We hit the Xen hard limit: reprobe. */ |
237 | reservation.nr_extents = rc; | 236 | reservation.nr_extents = rc; |
238 | ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, | 237 | ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, |
239 | &reservation); | 238 | &reservation); |
240 | BUG_ON(ret != rc); | 239 | BUG_ON(ret != rc); |
241 | } | 240 | } |
242 | if (rc >= 0) | 241 | if (rc >= 0) |
@@ -464,136 +463,13 @@ static void balloon_exit(void) | |||
464 | 463 | ||
465 | module_exit(balloon_exit); | 464 | module_exit(balloon_exit); |
466 | 465 | ||
467 | static void balloon_update_driver_allowance(long delta) | 466 | #define BALLOON_SHOW(name, format, args...) \ |
468 | { | 467 | static ssize_t show_##name(struct sys_device *dev, \ |
469 | unsigned long flags; | 468 | struct sysdev_attribute *attr, \ |
470 | 469 | char *buf) \ | |
471 | spin_lock_irqsave(&balloon_lock, flags); | 470 | { \ |
472 | balloon_stats.driver_pages += delta; | 471 | return sprintf(buf, format, ##args); \ |
473 | spin_unlock_irqrestore(&balloon_lock, flags); | 472 | } \ |
474 | } | ||
475 | |||
476 | static int dealloc_pte_fn( | ||
477 | pte_t *pte, struct page *pmd_page, unsigned long addr, void *data) | ||
478 | { | ||
479 | unsigned long mfn = pte_mfn(*pte); | ||
480 | int ret; | ||
481 | struct xen_memory_reservation reservation = { | ||
482 | .nr_extents = 1, | ||
483 | .extent_order = 0, | ||
484 | .domid = DOMID_SELF | ||
485 | }; | ||
486 | set_xen_guest_handle(reservation.extent_start, &mfn); | ||
487 | set_pte_at(&init_mm, addr, pte, __pte_ma(0ull)); | ||
488 | set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY); | ||
489 | ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); | ||
490 | BUG_ON(ret != 1); | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | static struct page **alloc_empty_pages_and_pagevec(int nr_pages) | ||
495 | { | ||
496 | unsigned long vaddr, flags; | ||
497 | struct page *page, **pagevec; | ||
498 | int i, ret; | ||
499 | |||
500 | pagevec = kmalloc(sizeof(page) * nr_pages, GFP_KERNEL); | ||
501 | if (pagevec == NULL) | ||
502 | return NULL; | ||
503 | |||
504 | for (i = 0; i < nr_pages; i++) { | ||
505 | page = pagevec[i] = alloc_page(GFP_KERNEL); | ||
506 | if (page == NULL) | ||
507 | goto err; | ||
508 | |||
509 | vaddr = (unsigned long)page_address(page); | ||
510 | |||
511 | scrub_page(page); | ||
512 | |||
513 | spin_lock_irqsave(&balloon_lock, flags); | ||
514 | |||
515 | if (xen_feature(XENFEAT_auto_translated_physmap)) { | ||
516 | unsigned long gmfn = page_to_pfn(page); | ||
517 | struct xen_memory_reservation reservation = { | ||
518 | .nr_extents = 1, | ||
519 | .extent_order = 0, | ||
520 | .domid = DOMID_SELF | ||
521 | }; | ||
522 | set_xen_guest_handle(reservation.extent_start, &gmfn); | ||
523 | ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, | ||
524 | &reservation); | ||
525 | if (ret == 1) | ||
526 | ret = 0; /* success */ | ||
527 | } else { | ||
528 | ret = apply_to_page_range(&init_mm, vaddr, PAGE_SIZE, | ||
529 | dealloc_pte_fn, NULL); | ||
530 | } | ||
531 | |||
532 | if (ret != 0) { | ||
533 | spin_unlock_irqrestore(&balloon_lock, flags); | ||
534 | __free_page(page); | ||
535 | goto err; | ||
536 | } | ||
537 | |||
538 | totalram_pages = --balloon_stats.current_pages; | ||
539 | |||
540 | spin_unlock_irqrestore(&balloon_lock, flags); | ||
541 | } | ||
542 | |||
543 | out: | ||
544 | schedule_work(&balloon_worker); | ||
545 | flush_tlb_all(); | ||
546 | return pagevec; | ||
547 | |||
548 | err: | ||
549 | spin_lock_irqsave(&balloon_lock, flags); | ||
550 | while (--i >= 0) | ||
551 | balloon_append(pagevec[i]); | ||
552 | spin_unlock_irqrestore(&balloon_lock, flags); | ||
553 | kfree(pagevec); | ||
554 | pagevec = NULL; | ||
555 | goto out; | ||
556 | } | ||
557 | |||
558 | static void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) | ||
559 | { | ||
560 | unsigned long flags; | ||
561 | int i; | ||
562 | |||
563 | if (pagevec == NULL) | ||
564 | return; | ||
565 | |||
566 | spin_lock_irqsave(&balloon_lock, flags); | ||
567 | for (i = 0; i < nr_pages; i++) { | ||
568 | BUG_ON(page_count(pagevec[i]) != 1); | ||
569 | balloon_append(pagevec[i]); | ||
570 | } | ||
571 | spin_unlock_irqrestore(&balloon_lock, flags); | ||
572 | |||
573 | kfree(pagevec); | ||
574 | |||
575 | schedule_work(&balloon_worker); | ||
576 | } | ||
577 | |||
578 | static void balloon_release_driver_page(struct page *page) | ||
579 | { | ||
580 | unsigned long flags; | ||
581 | |||
582 | spin_lock_irqsave(&balloon_lock, flags); | ||
583 | balloon_append(page); | ||
584 | balloon_stats.driver_pages--; | ||
585 | spin_unlock_irqrestore(&balloon_lock, flags); | ||
586 | |||
587 | schedule_work(&balloon_worker); | ||
588 | } | ||
589 | |||
590 | |||
591 | #define BALLOON_SHOW(name, format, args...) \ | ||
592 | static ssize_t show_##name(struct sys_device *dev, \ | ||
593 | char *buf) \ | ||
594 | { \ | ||
595 | return sprintf(buf, format, ##args); \ | ||
596 | } \ | ||
597 | static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) | 473 | static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) |
598 | 474 | ||
599 | BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); | 475 | BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); |
@@ -604,7 +480,8 @@ BALLOON_SHOW(hard_limit_kb, | |||
604 | (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0); | 480 | (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0); |
605 | BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages)); | 481 | BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages)); |
606 | 482 | ||
607 | static ssize_t show_target_kb(struct sys_device *dev, char *buf) | 483 | static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr, |
484 | char *buf) | ||
608 | { | 485 | { |
609 | return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); | 486 | return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); |
610 | } | 487 | } |
@@ -614,19 +491,14 @@ static ssize_t store_target_kb(struct sys_device *dev, | |||
614 | const char *buf, | 491 | const char *buf, |
615 | size_t count) | 492 | size_t count) |
616 | { | 493 | { |
617 | char memstring[64], *endchar; | 494 | char *endchar; |
618 | unsigned long long target_bytes; | 495 | unsigned long long target_bytes; |
619 | 496 | ||
620 | if (!capable(CAP_SYS_ADMIN)) | 497 | if (!capable(CAP_SYS_ADMIN)) |
621 | return -EPERM; | 498 | return -EPERM; |
622 | 499 | ||
623 | if (count <= 1) | 500 | target_bytes = memparse(buf, &endchar); |
624 | return -EBADMSG; /* runt */ | ||
625 | if (count > sizeof(memstring)) | ||
626 | return -EFBIG; /* too long */ | ||
627 | strcpy(memstring, buf); | ||
628 | 501 | ||
629 | target_bytes = memparse(memstring, &endchar); | ||
630 | balloon_set_new_target(target_bytes >> PAGE_SHIFT); | 502 | balloon_set_new_target(target_bytes >> PAGE_SHIFT); |
631 | 503 | ||
632 | return count; | 504 | return count; |
@@ -694,20 +566,4 @@ static int register_balloon(struct sys_device *sysdev) | |||
694 | return error; | 566 | return error; |
695 | } | 567 | } |
696 | 568 | ||
697 | static void unregister_balloon(struct sys_device *sysdev) | ||
698 | { | ||
699 | int i; | ||
700 | |||
701 | sysfs_remove_group(&sysdev->kobj, &balloon_info_group); | ||
702 | for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) | ||
703 | sysdev_remove_file(sysdev, balloon_attrs[i]); | ||
704 | sysdev_unregister(sysdev); | ||
705 | sysdev_class_unregister(&balloon_sysdev_class); | ||
706 | } | ||
707 | |||
708 | static void balloon_sysfs_exit(void) | ||
709 | { | ||
710 | unregister_balloon(&balloon_sysdev); | ||
711 | } | ||
712 | |||
713 | MODULE_LICENSE("GPL"); | 569 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 0e0c28574af8..a0837036d898 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c | |||
@@ -84,17 +84,6 @@ static int irq_bindcount[NR_IRQS]; | |||
84 | /* Xen will never allocate port zero for any purpose. */ | 84 | /* Xen will never allocate port zero for any purpose. */ |
85 | #define VALID_EVTCHN(chn) ((chn) != 0) | 85 | #define VALID_EVTCHN(chn) ((chn) != 0) |
86 | 86 | ||
87 | /* | ||
88 | * Force a proper event-channel callback from Xen after clearing the | ||
89 | * callback mask. We do this in a very simple manner, by making a call | ||
90 | * down into Xen. The pending flag will be checked by Xen on return. | ||
91 | */ | ||
92 | void force_evtchn_callback(void) | ||
93 | { | ||
94 | (void)HYPERVISOR_xen_version(0, NULL); | ||
95 | } | ||
96 | EXPORT_SYMBOL_GPL(force_evtchn_callback); | ||
97 | |||
98 | static struct irq_chip xen_dynamic_chip; | 87 | static struct irq_chip xen_dynamic_chip; |
99 | 88 | ||
100 | /* Constructor for packed IRQ information. */ | 89 | /* Constructor for packed IRQ information. */ |
diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h index a44c4dc70590..06f786f4b4fb 100644 --- a/include/asm-x86/desc.h +++ b/include/asm-x86/desc.h | |||
@@ -24,6 +24,11 @@ static inline void fill_ldt(struct desc_struct *desc, | |||
24 | desc->d = info->seg_32bit; | 24 | desc->d = info->seg_32bit; |
25 | desc->g = info->limit_in_pages; | 25 | desc->g = info->limit_in_pages; |
26 | desc->base2 = (info->base_addr & 0xff000000) >> 24; | 26 | desc->base2 = (info->base_addr & 0xff000000) >> 24; |
27 | /* | ||
28 | * Don't allow setting of the lm bit. It is useless anyway | ||
29 | * because 64bit system calls require __USER_CS: | ||
30 | */ | ||
31 | desc->l = 0; | ||
27 | } | 32 | } |
28 | 33 | ||
29 | extern struct desc_ptr idt_descr; | 34 | extern struct desc_ptr idt_descr; |
@@ -97,7 +102,15 @@ static inline int desc_empty(const void *ptr) | |||
97 | native_write_gdt_entry(dt, entry, desc, type) | 102 | native_write_gdt_entry(dt, entry, desc, type) |
98 | #define write_idt_entry(dt, entry, g) \ | 103 | #define write_idt_entry(dt, entry, g) \ |
99 | native_write_idt_entry(dt, entry, g) | 104 | native_write_idt_entry(dt, entry, g) |
100 | #endif | 105 | |
106 | static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries) | ||
107 | { | ||
108 | } | ||
109 | |||
110 | static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries) | ||
111 | { | ||
112 | } | ||
113 | #endif /* CONFIG_PARAVIRT */ | ||
101 | 114 | ||
102 | static inline void native_write_idt_entry(gate_desc *idt, int entry, | 115 | static inline void native_write_idt_entry(gate_desc *idt, int entry, |
103 | const gate_desc *gate) | 116 | const gate_desc *gate) |
diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h index fbbde93f12d6..db9b0647b346 100644 --- a/include/asm-x86/paravirt.h +++ b/include/asm-x86/paravirt.h | |||
@@ -124,6 +124,9 @@ struct pv_cpu_ops { | |||
124 | int entrynum, const void *desc, int size); | 124 | int entrynum, const void *desc, int size); |
125 | void (*write_idt_entry)(gate_desc *, | 125 | void (*write_idt_entry)(gate_desc *, |
126 | int entrynum, const gate_desc *gate); | 126 | int entrynum, const gate_desc *gate); |
127 | void (*alloc_ldt)(struct desc_struct *ldt, unsigned entries); | ||
128 | void (*free_ldt)(struct desc_struct *ldt, unsigned entries); | ||
129 | |||
127 | void (*load_sp0)(struct tss_struct *tss, struct thread_struct *t); | 130 | void (*load_sp0)(struct tss_struct *tss, struct thread_struct *t); |
128 | 131 | ||
129 | void (*set_iopl_mask)(unsigned mask); | 132 | void (*set_iopl_mask)(unsigned mask); |
@@ -824,6 +827,16 @@ do { \ | |||
824 | (aux) = __aux; \ | 827 | (aux) = __aux; \ |
825 | } while (0) | 828 | } while (0) |
826 | 829 | ||
830 | static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries) | ||
831 | { | ||
832 | PVOP_VCALL2(pv_cpu_ops.alloc_ldt, ldt, entries); | ||
833 | } | ||
834 | |||
835 | static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries) | ||
836 | { | ||
837 | PVOP_VCALL2(pv_cpu_ops.free_ldt, ldt, entries); | ||
838 | } | ||
839 | |||
827 | static inline void load_TR_desc(void) | 840 | static inline void load_TR_desc(void) |
828 | { | 841 | { |
829 | PVOP_VCALL0(pv_cpu_ops.load_tr_desc); | 842 | PVOP_VCALL0(pv_cpu_ops.load_tr_desc); |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fdbbf72ca2eb..7889c2f9b75d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -176,7 +176,7 @@ extern int vsscanf(const char *, const char *, va_list) | |||
176 | 176 | ||
177 | extern int get_option(char **str, int *pint); | 177 | extern int get_option(char **str, int *pint); |
178 | extern char *get_options(const char *str, int nints, int *ints); | 178 | extern char *get_options(const char *str, int nints, int *ints); |
179 | extern unsigned long long memparse(char *ptr, char **retptr); | 179 | extern unsigned long long memparse(const char *ptr, char **retptr); |
180 | 180 | ||
181 | extern int core_kernel_text(unsigned long addr); | 181 | extern int core_kernel_text(unsigned long addr); |
182 | extern int __kernel_text_address(unsigned long addr); | 182 | extern int __kernel_text_address(unsigned long addr); |
diff --git a/lib/cmdline.c b/lib/cmdline.c index 5ba8a942a478..f5f3ad8b62ff 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c | |||
@@ -126,7 +126,7 @@ char *get_options(const char *str, int nints, int *ints) | |||
126 | * megabyte, or one gigabyte, respectively. | 126 | * megabyte, or one gigabyte, respectively. |
127 | */ | 127 | */ |
128 | 128 | ||
129 | unsigned long long memparse(char *ptr, char **retptr) | 129 | unsigned long long memparse(const char *ptr, char **retptr) |
130 | { | 130 | { |
131 | char *endptr; /* local pointer to end of parsed string */ | 131 | char *endptr; /* local pointer to end of parsed string */ |
132 | 132 | ||