Skip to content

Commit 3d2606f

Browse files
author
Robert Richter
committed
oprofile, x86: Enable preemption during pci device setup in IBS init
IBS initialization is a mix of per-core register access and per-node pci device setup. Register access should be pinned to the cpu, but pci setup must run with preemption enabled. This patch better separates the code into non-/preemptible sections and fixes sleeping with preemption disabled. See bug message below. Fixes also freeing the eilvt entry by introducing put_eilvt(). BUG: sleeping function called from invalid context at mm/slub.c:824 in_atomic(): 1, irqs_disabled(): 0, pid: 32357, name: modprobe INFO: lockdep is turned off. Pid: 32357, comm: modprobe Not tainted 2.6.39-rc7+ torvalds#14 Call Trace: [<ffffffff8104bdc8>] __might_sleep+0x112/0x117 [<ffffffff81129693>] kmem_cache_alloc_trace+0x4b/0xe7 [<ffffffff81278f14>] kzalloc.constprop.0+0x29/0x2b [<ffffffff81278f4c>] pci_get_subsys+0x36/0x78 [<ffffffff81022689>] ? setup_APIC_eilvt+0xfb/0x139 [<ffffffff81278fa4>] pci_get_device+0x16/0x18 [<ffffffffa06c8b5d>] op_amd_init+0xd3/0x211 [oprofile] [<ffffffffa064d000>] ? 0xffffffffa064cfff [<ffffffffa064d298>] op_nmi_init+0x21e/0x26a [oprofile] [<ffffffffa064d062>] oprofile_arch_init+0xe/0x26 [oprofile] [<ffffffffa064d010>] oprofile_init+0x10/0x42 [oprofile] [<ffffffff81002099>] do_one_initcall+0x7f/0x13a [<ffffffff81096524>] sys_init_module+0x132/0x281 [<ffffffff814cc682>] system_call_fastpath+0x16/0x1b Reported-by: Dave Jones <davej@redhat.com> Cc: <stable@kernel.org> [2.6.37.x] Signed-off-by: Robert Richter <robert.richter@amd.com>
1 parent 61c4f2c commit 3d2606f

File tree

1 file changed

+54
-41
lines changed

1 file changed

+54
-41
lines changed

arch/x86/oprofile/op_model_amd.c

+54-41
Original file line numberDiff line numberDiff line change
@@ -316,33 +316,44 @@ static void op_amd_stop_ibs(void)
316316
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
317317
}
318318

319-
static inline int eilvt_is_available(int offset)
319+
static inline int get_eilvt(int offset)
320320
{
321-
/* check if we may assign a vector */
322321
return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
323322
}
324323

324+
static inline int put_eilvt(int offset)
325+
{
326+
return !setup_APIC_eilvt(offset, 0, 0, 1);
327+
}
328+
325329
static inline int ibs_eilvt_valid(void)
326330
{
327331
int offset;
328332
u64 val;
333+
int valid = 0;
334+
335+
preempt_disable();
329336

330337
rdmsrl(MSR_AMD64_IBSCTL, val);
331338
offset = val & IBSCTL_LVT_OFFSET_MASK;
332339

333340
if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
334341
pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n",
335342
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
336-
return 0;
343+
goto out;
337344
}
338345

339-
if (!eilvt_is_available(offset)) {
346+
if (!get_eilvt(offset)) {
340347
pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n",
341348
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
342-
return 0;
349+
goto out;
343350
}
344351

345-
return 1;
352+
valid = 1;
353+
out:
354+
preempt_enable();
355+
356+
return valid;
346357
}
347358

348359
static inline int get_ibs_offset(void)
@@ -600,67 +611,69 @@ static int setup_ibs_ctl(int ibs_eilvt_off)
600611

601612
static int force_ibs_eilvt_setup(void)
602613
{
603-
int i;
614+
int offset;
604615
int ret;
605616

606-
/* find the next free available EILVT entry */
607-
for (i = 1; i < 4; i++) {
608-
if (!eilvt_is_available(i))
609-
continue;
610-
ret = setup_ibs_ctl(i);
611-
if (ret)
612-
return ret;
613-
pr_err(FW_BUG "using offset %d for IBS interrupts\n", i);
614-
return 0;
617+
/*
618+
* find the next free available EILVT entry, skip offset 0,
619+
* pin search to this cpu
620+
*/
621+
preempt_disable();
622+
for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) {
623+
if (get_eilvt(offset))
624+
break;
615625
}
626+
preempt_enable();
616627

617-
printk(KERN_DEBUG "No EILVT entry available\n");
618-
619-
return -EBUSY;
620-
}
621-
622-
static int __init_ibs_nmi(void)
623-
{
624-
int ret;
625-
626-
if (ibs_eilvt_valid())
627-
return 0;
628+
if (offset == APIC_EILVT_NR_MAX) {
629+
printk(KERN_DEBUG "No EILVT entry available\n");
630+
return -EBUSY;
631+
}
628632

629-
ret = force_ibs_eilvt_setup();
633+
ret = setup_ibs_ctl(offset);
630634
if (ret)
631-
return ret;
635+
goto out;
632636

633-
if (!ibs_eilvt_valid())
634-
return -EFAULT;
637+
if (!ibs_eilvt_valid()) {
638+
ret = -EFAULT;
639+
goto out;
640+
}
635641

642+
pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset);
636643
pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
637644

638645
return 0;
646+
out:
647+
preempt_disable();
648+
put_eilvt(offset);
649+
preempt_enable();
650+
return ret;
639651
}
640652

641653
/*
642654
* check and reserve APIC extended interrupt LVT offset for IBS if
643655
* available
644-
*
645-
* init_ibs() preforms implicitly cpu-local operations, so pin this
646-
* thread to its current CPU
647656
*/
648657

649658
static void init_ibs(void)
650659
{
651-
preempt_disable();
652-
653660
ibs_caps = get_ibs_caps();
661+
654662
if (!ibs_caps)
663+
return;
664+
665+
if (ibs_eilvt_valid())
655666
goto out;
656667

657-
if (__init_ibs_nmi() < 0)
658-
ibs_caps = 0;
659-
else
660-
printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
668+
if (!force_ibs_eilvt_setup())
669+
goto out;
670+
671+
/* Failed to setup ibs */
672+
ibs_caps = 0;
673+
return;
661674

662675
out:
663-
preempt_enable();
676+
printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
664677
}
665678

666679
static int (*create_arch_files)(struct super_block *sb, struct dentry *root);

0 commit comments

Comments
 (0)