Dear RT Folks, I'm pleased to announce the 3.0.12-rt30 stable release. You can get this release via the git tree at: git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git Head SHA1: b6154617aa6dc001d1ccf0e90a19cd857619dd03 Or to build 3.0.12-rt30 directly, the following patches should be applied: http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.0.tar.xz http://www.kernel.org/pub/linux/kernel/v3.0/patch-3.0.12.xz http://www.kernel.org/pub/linux/kernel/projects/rt/3.0/patch-3.0.12-rt30.patch.xz You can also build from 3.0.12-rt29 by applying the incremental patch: http://www.kernel.org/pub/linux/kernel/projects/rt/3.0/incr/patch-3.0.12-rt29-rt30.patch.xz The broken out patches are available at: http://www.kernel.org/pub/linux/kernel/projects/rt/3.0/patches-3.0.12-rt30.tar.xz Enjoy, -- Steve Changes from 3.0.12-rt29: --- Andre Przywara (1): KVM: fix XSAVE bit scanning (now properly) Avi Kivity (1): KVM: Sanitize cpuid Clark Williams (1): ACPI: Convert embedded controller lock to raw spinlock Edward Donovan (1): genirq: fix regression in irqfixup, irqpoll Ingo Molnar (1): tasklet/rt: Prevent tasklets from going into infinite spin in RT Peter Zijlstra (2): slab, lockdep: Fix silly bug slab, lockdep: Annotate all slab caches Roland Dreier (1): intel-iommu: Fix AB-BA lockdep report Steven Rostedt (1): Linux 3.0.12-rt30 Thomas Gleixner (3): wait: Provide __wake_up_all_locked pci: Use __wake_up_all_locked pci_unblock_user_cfg_access() acpi: Make gbl_[hardware|gpe]_lock raw ---- arch/x86/kvm/x86.c | 44 +++++++- drivers/acpi/acpica/acglobal.h | 4 +- drivers/acpi/acpica/evgpe.c | 4 +- drivers/acpi/acpica/evgpeblk.c | 8 +- drivers/acpi/acpica/evgpeutil.c | 12 +- drivers/acpi/acpica/evxface.c | 10 +- drivers/acpi/acpica/evxfgpe.c | 24 +++--- drivers/acpi/acpica/hwregs.c | 4 +- drivers/acpi/acpica/hwxface.c | 4 +- drivers/acpi/acpica/utmutex.c | 21 +---- drivers/acpi/ec.c | 22 ++-- drivers/acpi/internal.h | 2 +- drivers/pci/access.c | 2 +- drivers/pci/intel-iommu.c | 4 +- include/linux/interrupt.h | 39 ++++---- include/linux/wait.h | 5 +- kernel/irq/spurious.c | 4 +- kernel/sched.c | 4 +- kernel/softirq.c | 208 ++++++++++++++++++++++++++++----------- localversion-rt | 2 +- mm/slab.c | 55 ++++++----- 21 files changed, 302 insertions(+), 180 deletions(-) --------------------------- diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 545c61b..3567c76 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2283,6 +2283,13 @@ static void do_cpuid_1_ent(struct kvm_cpuid_entry2 *entry, u32 function, entry->flags = 0; } +static bool supported_xcr0_bit(unsigned bit) +{ + u64 mask = ((u64)1 << bit); + + return mask & (XSTATE_FP | XSTATE_SSE | XSTATE_YMM) & host_xcr0; +} + #define F(x) bit(X86_FEATURE_##x) static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, @@ -2393,6 +2400,8 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, } break; } + case 9: + break; case 0xb: { int i, level_type; @@ -2410,16 +2419,17 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, break; } case 0xd: { - int i; + int idx, i; entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - for (i = 1; *nent < maxnent && i < 64; ++i) { - if (entry[i].eax == 0) + for (idx = 1, i = 1; *nent < maxnent && idx < 64; ++idx) { + do_cpuid_1_ent(&entry[i], function, idx); + if (entry[i].eax == 0 || !supported_xcr0_bit(idx)) continue; - do_cpuid_1_ent(&entry[i], function, i); entry[i].flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; ++*nent; + ++i; } break; } @@ -2451,6 +2461,24 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, entry->ecx &= kvm_supported_word6_x86_features; cpuid_mask(&entry->ecx, 6); break; + case 0x80000008: { + unsigned g_phys_as = (entry->eax >> 16) & 0xff; + unsigned virt_as = max((entry->eax >> 8) & 0xff, 48U); + unsigned phys_as = entry->eax & 0xff; + + if (!g_phys_as) + g_phys_as = phys_as; + entry->eax = g_phys_as | (virt_as << 8); + entry->ebx = entry->edx = 0; + break; + } + case 0x80000019: + entry->ecx = entry->edx = 0; + break; + case 0x8000001a: + break; + case 0x8000001d: + break; /*Add support for Centaur's CPUID instruction*/ case 0xC0000000: /*Just support up to 0xC0000004 now*/ @@ -2460,10 +2488,16 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, entry->edx &= kvm_supported_word5_x86_features; cpuid_mask(&entry->edx, 5); break; + case 3: /* Processor serial number */ + case 5: /* MONITOR/MWAIT */ + case 6: /* Thermal management */ + case 0xA: /* Architectural Performance Monitoring */ + case 0x80000007: /* Advanced power management */ case 0xC0000002: case 0xC0000003: case 0xC0000004: - /*Now nothing to do, reserved for the future*/ + default: + entry->eax = entry->ebx = entry->ecx = entry->edx = 0; break; } diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 73863d8..6c169a2 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -229,8 +229,8 @@ ACPI_EXTERN u8 acpi_gbl_global_lock_pending; * Spinlocks are used for interfaces that can be possibly called at * interrupt level */ -ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ -ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ +extern raw_spinlock_t acpi_gbl_gpe_lock; /* For GPE data structs and registers */ +extern raw_spinlock_t acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ /***************************************************************************** * diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 65c79ad..36e7e10 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -357,7 +357,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) * Note: Not necessary to obtain the hardware lock, since the GPE * registers are owned by the gpe_lock. */ - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Examine all GPE blocks attached to this interrupt level */ @@ -440,7 +440,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return (int_status); } diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index ca2c41a..60c47b9 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -95,7 +95,7 @@ acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, /* Install the new block at the end of the list with lock */ - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); if (gpe_xrupt_block->gpe_block_list_head) { next_gpe_block = gpe_xrupt_block->gpe_block_list_head; while (next_gpe_block->next) { @@ -109,7 +109,7 @@ acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, } gpe_block->xrupt_block = gpe_xrupt_block; - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); unlock_and_exit: status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); @@ -156,7 +156,7 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) } else { /* Remove the block on this interrupt with lock */ - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); if (gpe_block->previous) { gpe_block->previous->next = gpe_block->next; } else { @@ -167,7 +167,7 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) if (gpe_block->next) { gpe_block->next->previous = gpe_block->previous; } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); } acpi_current_gpe_count -= gpe_block->gpe_count; diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 80a81d0..895b68ab 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -70,7 +70,7 @@ acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) ACPI_FUNCTION_TRACE(ev_walk_gpe_list); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Walk the interrupt level descriptor list */ @@ -101,7 +101,7 @@ acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) } unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -237,7 +237,7 @@ struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number) /* Install new interrupt descriptor with spin lock */ - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); if (acpi_gbl_gpe_xrupt_list_head) { next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; while (next_gpe_xrupt->next) { @@ -249,7 +249,7 @@ struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number) } else { acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); /* Install new interrupt handler if not SCI_INT */ @@ -306,7 +306,7 @@ acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) /* Unlink the interrupt block with lock */ - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); if (gpe_xrupt->previous) { gpe_xrupt->previous->next = gpe_xrupt->next; } else { @@ -318,7 +318,7 @@ acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) if (gpe_xrupt->next) { gpe_xrupt->next->previous = gpe_xrupt->previous; } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); /* Free the block */ diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index e114140..e849c10 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -750,7 +750,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, goto unlock_and_exit; } - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -798,14 +798,14 @@ acpi_install_gpe_handler(acpi_handle gpe_device, ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); return_ACPI_STATUS(status); free_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); ACPI_FREE(handler); goto unlock_and_exit; } @@ -852,7 +852,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, return_ACPI_STATUS(status); } - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -903,7 +903,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, ACPI_FREE(handler); unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); return_ACPI_STATUS(status); diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 52aaff3..ce07ebb 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -121,7 +121,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) ACPI_FUNCTION_TRACE(acpi_enable_gpe); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -130,7 +130,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) status = acpi_ev_add_gpe_reference(gpe_event_info); } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } ACPI_EXPORT_SYMBOL(acpi_enable_gpe) @@ -158,7 +158,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) ACPI_FUNCTION_TRACE(acpi_disable_gpe); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -167,7 +167,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) status = acpi_ev_remove_gpe_reference(gpe_event_info) ; } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } ACPI_EXPORT_SYMBOL(acpi_disable_gpe) @@ -214,7 +214,7 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, return_ACPI_STATUS(AE_BAD_PARAMETER); } - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -270,7 +270,7 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, status = AE_OK; unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) @@ -300,7 +300,7 @@ acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 ac ACPI_FUNCTION_TRACE(acpi_set_gpe_wake_mask); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* * Ensure that we have a valid GPE number and that this GPE is in @@ -346,7 +346,7 @@ acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 ac } unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -372,7 +372,7 @@ acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) ACPI_FUNCTION_TRACE(acpi_clear_gpe); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -385,7 +385,7 @@ acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) status = acpi_hw_clear_gpe(gpe_event_info); unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -415,7 +415,7 @@ acpi_get_gpe_status(acpi_handle gpe_device, ACPI_FUNCTION_TRACE(acpi_get_gpe_status); - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + raw_spin_lock_irqsave(&acpi_gbl_gpe_lock, flags); /* Ensure that we have a valid GPE number */ @@ -433,7 +433,7 @@ acpi_get_gpe_status(acpi_handle gpe_device, *event_status |= ACPI_EVENT_FLAG_HANDLE; unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + raw_spin_unlock_irqrestore(&acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 55accb7..4772930 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -263,7 +263,7 @@ acpi_status acpi_hw_clear_acpi_status(void) ACPI_BITMASK_ALL_FIXED_STATUS, ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); - lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + raw_spin_lock_irqsave(&acpi_gbl_hardware_lock, lock_flags); /* Clear the fixed events in PM1 A/B */ @@ -278,7 +278,7 @@ acpi_status acpi_hw_clear_acpi_status(void) status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); unlock_and_exit: - acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); + raw_spin_unlock_irqrestore(&acpi_gbl_hardware_lock, lock_flags); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index f75f81a..76159ba 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -386,7 +386,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value) return_ACPI_STATUS(AE_BAD_PARAMETER); } - lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + raw_spin_lock_irqsave(&acpi_gbl_hardware_lock, lock_flags); /* * At this point, we know that the parent register is one of the @@ -447,7 +447,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value) unlock_and_exit: - acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); + raw_spin_unlock_irqrestore(&acpi_gbl_hardware_lock, lock_flags); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 7d797e2..420eecf 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -52,6 +52,9 @@ static acpi_status acpi_ut_create_mutex(acpi_mutex_handle mutex_id); static void acpi_ut_delete_mutex(acpi_mutex_handle mutex_id); +DEFINE_RAW_SPINLOCK(acpi_gbl_gpe_lock); +DEFINE_RAW_SPINLOCK(acpi_gbl_hardware_lock); + /******************************************************************************* * * FUNCTION: acpi_ut_mutex_initialize @@ -81,18 +84,6 @@ acpi_status acpi_ut_mutex_initialize(void) } } - /* Create the spinlocks for use at interrupt level */ - - status = acpi_os_create_lock (&acpi_gbl_gpe_lock); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } - - status = acpi_os_create_lock (&acpi_gbl_hardware_lock); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } - /* Mutex for _OSI support */ status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); if (ACPI_FAILURE(status)) { @@ -132,13 +123,7 @@ void acpi_ut_mutex_terminate(void) acpi_os_delete_mutex(acpi_gbl_osi_mutex); - /* Delete the spinlocks */ - - acpi_os_delete_lock(acpi_gbl_gpe_lock); - acpi_os_delete_lock(acpi_gbl_hardware_lock); - /* Delete the reader/writer lock */ - acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock); return_VOID; } diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b19a18d..5812e01 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -152,10 +152,10 @@ static int ec_transaction_done(struct acpi_ec *ec) { unsigned long flags; int ret = 0; - spin_lock_irqsave(&ec->curr_lock, flags); + raw_spin_lock_irqsave(&ec->curr_lock, flags); if (!ec->curr || ec->curr->done) ret = 1; - spin_unlock_irqrestore(&ec->curr_lock, flags); + raw_spin_unlock_irqrestore(&ec->curr_lock, flags); return ret; } @@ -169,7 +169,7 @@ static void start_transaction(struct acpi_ec *ec) static void advance_transaction(struct acpi_ec *ec, u8 status) { unsigned long flags; - spin_lock_irqsave(&ec->curr_lock, flags); + raw_spin_lock_irqsave(&ec->curr_lock, flags); if (!ec->curr) goto unlock; if (ec->curr->wlen > ec->curr->wi) { @@ -194,7 +194,7 @@ err: if (in_interrupt()) ++ec->curr->irq_count; unlock: - spin_unlock_irqrestore(&ec->curr_lock, flags); + raw_spin_unlock_irqrestore(&ec->curr_lock, flags); } static int acpi_ec_sync_query(struct acpi_ec *ec); @@ -232,9 +232,9 @@ static int ec_poll(struct acpi_ec *ec) if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) break; pr_debug(PREFIX "controller reset, restart transaction\n"); - spin_lock_irqsave(&ec->curr_lock, flags); + raw_spin_lock_irqsave(&ec->curr_lock, flags); start_transaction(ec); - spin_unlock_irqrestore(&ec->curr_lock, flags); + raw_spin_unlock_irqrestore(&ec->curr_lock, flags); } return -ETIME; } @@ -247,17 +247,17 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, if (EC_FLAGS_MSI) udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ - spin_lock_irqsave(&ec->curr_lock, tmp); + raw_spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ ec->curr = t; start_transaction(ec); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - spin_unlock_irqrestore(&ec->curr_lock, tmp); + raw_spin_unlock_irqrestore(&ec->curr_lock, tmp); ret = ec_poll(ec); - spin_lock_irqsave(&ec->curr_lock, tmp); + raw_spin_lock_irqsave(&ec->curr_lock, tmp); ec->curr = NULL; - spin_unlock_irqrestore(&ec->curr_lock, tmp); + raw_spin_unlock_irqrestore(&ec->curr_lock, tmp); return ret; } @@ -678,7 +678,7 @@ static struct acpi_ec *make_acpi_ec(void) mutex_init(&ec->lock); init_waitqueue_head(&ec->wait); INIT_LIST_HEAD(&ec->list); - spin_lock_init(&ec->curr_lock); + raw_spin_lock_init(&ec->curr_lock); return ec; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ca75b9c..68ed95f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -62,7 +62,7 @@ struct acpi_ec { wait_queue_head_t wait; struct list_head list; struct transaction *curr; - spinlock_t curr_lock; + raw_spinlock_t curr_lock; }; extern struct acpi_ec *first_ec; diff --git a/drivers/pci/access.c b/drivers/pci/access.c index fdaa42a..1a6cc67 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -441,7 +441,7 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev) WARN_ON(!dev->block_ucfg_access); dev->block_ucfg_access = 0; - wake_up_all(&pci_ucfg_wait); + wake_up_all_locked(&pci_ucfg_wait); raw_spin_unlock_irqrestore(&pci_lock, flags); } EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access); diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8c2564d..bc05a51 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3569,6 +3569,8 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, found = 1; } + spin_unlock_irqrestore(&device_domain_lock, flags); + if (found == 0) { unsigned long tmp_flags; spin_lock_irqsave(&domain->iommu_lock, tmp_flags); @@ -3585,8 +3587,6 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, spin_unlock_irqrestore(&iommu->lock, tmp_flags); } } - - spin_unlock_irqrestore(&device_domain_lock, flags); } static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index a62158f..3142442 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -501,8 +501,9 @@ extern void __send_remote_softirq(struct call_single_data *cp, int cpu, to be executed on some cpu at least once after this. * If the tasklet is already scheduled, but its execution is still not started, it will be executed only once. - * If this tasklet is already running on another CPU (or schedule is called - from tasklet itself), it is rescheduled for later. + * If this tasklet is already running on another CPU, it is rescheduled + for later. + * Schedule must not be called from the tasklet itself (a lockup occurs) * Tasklet is strictly serialized wrt itself, but not wrt another tasklets. If client needs some intertask synchronization, he makes it with spinlocks. @@ -527,27 +528,36 @@ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ - TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ + TASKLET_STATE_RUN, /* Tasklet is running (SMP only) */ + TASKLET_STATE_PENDING /* Tasklet is pending */ }; -#ifdef CONFIG_SMP +#define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED) +#define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN) +#define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING) + +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) static inline int tasklet_trylock(struct tasklet_struct *t) { return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state); } +static inline int tasklet_tryunlock(struct tasklet_struct *t) +{ + return cmpxchg(&t->state, TASKLET_STATEF_RUN, 0) == TASKLET_STATEF_RUN; +} + static inline void tasklet_unlock(struct tasklet_struct *t) { smp_mb__before_clear_bit(); clear_bit(TASKLET_STATE_RUN, &(t)->state); } -static inline void tasklet_unlock_wait(struct tasklet_struct *t) -{ - while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); } -} +extern void tasklet_unlock_wait(struct tasklet_struct *t); + #else #define tasklet_trylock(t) 1 +#define tasklet_tryunlock(t) 1 #define tasklet_unlock_wait(t) do { } while (0) #define tasklet_unlock(t) do { } while (0) #endif @@ -596,17 +606,8 @@ static inline void tasklet_disable(struct tasklet_struct *t) smp_mb(); } -static inline void tasklet_enable(struct tasklet_struct *t) -{ - smp_mb__before_atomic_dec(); - atomic_dec(&t->count); -} - -static inline void tasklet_hi_enable(struct tasklet_struct *t) -{ - smp_mb__before_atomic_dec(); - atomic_dec(&t->count); -} +extern void tasklet_enable(struct tasklet_struct *t); +extern void tasklet_hi_enable(struct tasklet_struct *t); extern void tasklet_kill(struct tasklet_struct *t); extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu); diff --git a/include/linux/wait.h b/include/linux/wait.h index 3efc9f3..1e904b8 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -157,7 +157,7 @@ void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key); void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, void *key); -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode); +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_bit(wait_queue_head_t *, void *, int); int __wait_on_bit(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned); @@ -170,7 +170,8 @@ wait_queue_head_t *bit_waitqueue(void *, int); #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL) #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL) -#define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL) +#define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL, 1) +#define wake_up_all_locked(x) __wake_up_locked((x), TASK_NORMAL, 0) #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL) diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index bfe1004..d09e0f5 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -84,7 +84,9 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) */ action = desc->action; if (!action || !(action->flags & IRQF_SHARED) || - (action->flags & __IRQF_TIMER) || !action->next) + (action->flags & __IRQF_TIMER) || + (action->handler(irq, action->dev_id) == IRQ_HANDLED) || + !action->next) goto out; /* Already running on another processor */ diff --git a/kernel/sched.c b/kernel/sched.c index 640f740..63aeba0 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4642,9 +4642,9 @@ EXPORT_SYMBOL(__wake_up); /* * Same as __wake_up but called with the spinlock in wait_queue_head_t held. */ -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode) +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr) { - __wake_up_common(q, mode, 1, 0, NULL); + __wake_up_common(q, mode, nr, 0, NULL); } EXPORT_SYMBOL_GPL(__wake_up_locked); diff --git a/kernel/softirq.c b/kernel/softirq.c index 026a283..3489d06 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -21,6 +21,7 @@ #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/rcupdate.h> +#include <linux/delay.h> #include <linux/ftrace.h> #include <linux/smp.h> #include <linux/tick.h> @@ -670,15 +671,45 @@ struct tasklet_head static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec); +static void inline +__tasklet_common_schedule(struct tasklet_struct *t, struct tasklet_head *head, unsigned int nr) +{ + if (tasklet_trylock(t)) { +again: + /* We may have been preempted before tasklet_trylock + * and __tasklet_action may have already run. + * So double check the sched bit while the takslet + * is locked before adding it to the list. + */ + if (test_bit(TASKLET_STATE_SCHED, &t->state)) { + t->next = NULL; + *head->tail = t; + head->tail = &(t->next); + raise_softirq_irqoff(nr); + tasklet_unlock(t); + } else { + /* This is subtle. If we hit the corner case above + * It is possible that we get preempted right here, + * and another task has successfully called + * tasklet_schedule(), then this function, and + * failed on the trylock. Thus we must be sure + * before releasing the tasklet lock, that the + * SCHED_BIT is clear. Otherwise the tasklet + * may get its SCHED_BIT set, but not added to the + * list + */ + if (!tasklet_tryunlock(t)) + goto again; + } + } +} + void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); - t->next = NULL; - *__this_cpu_read(tasklet_vec.tail) = t; - __this_cpu_write(tasklet_vec.tail, &(t->next)); - raise_softirq_irqoff(TASKLET_SOFTIRQ); + __tasklet_common_schedule(t, &__get_cpu_var(tasklet_vec), TASKLET_SOFTIRQ); local_irq_restore(flags); } @@ -689,10 +720,7 @@ void __tasklet_hi_schedule(struct tasklet_struct *t) unsigned long flags; local_irq_save(flags); - t->next = NULL; - *__this_cpu_read(tasklet_hi_vec.tail) = t; - __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); - raise_softirq_irqoff(HI_SOFTIRQ); + __tasklet_common_schedule(t, &__get_cpu_var(tasklet_hi_vec), HI_SOFTIRQ); local_irq_restore(flags); } @@ -700,50 +728,119 @@ EXPORT_SYMBOL(__tasklet_hi_schedule); void __tasklet_hi_schedule_first(struct tasklet_struct *t) { - BUG_ON(!irqs_disabled()); - - t->next = __this_cpu_read(tasklet_hi_vec.head); - __this_cpu_write(tasklet_hi_vec.head, t); - __raise_softirq_irqoff(HI_SOFTIRQ); + __tasklet_hi_schedule(t); } EXPORT_SYMBOL(__tasklet_hi_schedule_first); + +void tasklet_enable(struct tasklet_struct *t) +{ + if (!atomic_dec_and_test(&t->count)) + return; + if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) + tasklet_schedule(t); +} + +EXPORT_SYMBOL(tasklet_enable); -static void tasklet_action(struct softirq_action *a) +void tasklet_hi_enable(struct tasklet_struct *t) { - struct tasklet_struct *list; + if (!atomic_dec_and_test(&t->count)) + return; + if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) + tasklet_hi_schedule(t); +} - local_irq_disable(); - list = __this_cpu_read(tasklet_vec.head); - __this_cpu_write(tasklet_vec.head, NULL); - __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head); - local_irq_enable(); +EXPORT_SYMBOL(tasklet_hi_enable); + +static void +__tasklet_action(struct softirq_action *a, struct tasklet_struct *list) +{ + int loops = 1000000; while (list) { struct tasklet_struct *t = list; list = list->next; - if (tasklet_trylock(t)) { - if (!atomic_read(&t->count)) { - if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) - BUG(); - t->func(t->data); - tasklet_unlock(t); - continue; - } - tasklet_unlock(t); + /* + * Should always succeed - after a tasklist got on the + * list (after getting the SCHED bit set from 0 to 1), + * nothing but the tasklet softirq it got queued to can + * lock it: + */ + if (!tasklet_trylock(t)) { + WARN_ON(1); + continue; } - local_irq_disable(); t->next = NULL; - *__this_cpu_read(tasklet_vec.tail) = t; - __this_cpu_write(tasklet_vec.tail, &(t->next)); - __raise_softirq_irqoff(TASKLET_SOFTIRQ); - local_irq_enable(); + + /* + * If we cannot handle the tasklet because it's disabled, + * mark it as pending. tasklet_enable() will later + * re-schedule the tasklet. + */ + if (unlikely(atomic_read(&t->count))) { +out_disabled: + /* implicit unlock: */ + wmb(); + t->state = TASKLET_STATEF_PENDING; + continue; + } + + /* + * After this point on the tasklet might be rescheduled + * on another CPU, but it can only be added to another + * CPU's tasklet list if we unlock the tasklet (which we + * dont do yet). + */ + if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) + WARN_ON(1); + +again: + t->func(t->data); + + /* + * Try to unlock the tasklet. We must use cmpxchg, because + * another CPU might have scheduled or disabled the tasklet. + * We only allow the STATE_RUN -> 0 transition here. + */ + while (!tasklet_tryunlock(t)) { + /* + * If it got disabled meanwhile, bail out: + */ + if (atomic_read(&t->count)) + goto out_disabled; + /* + * If it got scheduled meanwhile, re-execute + * the tasklet function: + */ + if (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) + goto again; + if (!--loops) { + printk("hm, tasklet state: %08lx\n", t->state); + WARN_ON(1); + tasklet_unlock(t); + break; + } + } } } +static void tasklet_action(struct softirq_action *a) +{ + struct tasklet_struct *list; + + local_irq_disable(); + list = __get_cpu_var(tasklet_vec).head; + __get_cpu_var(tasklet_vec).head = NULL; + __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; + local_irq_enable(); + + __tasklet_action(a, list); +} + static void tasklet_hi_action(struct softirq_action *a) { struct tasklet_struct *list; @@ -754,29 +851,7 @@ static void tasklet_hi_action(struct softirq_action *a) __this_cpu_write(tasklet_hi_vec.tail, &__get_cpu_var(tasklet_hi_vec).head); local_irq_enable(); - while (list) { - struct tasklet_struct *t = list; - - list = list->next; - - if (tasklet_trylock(t)) { - if (!atomic_read(&t->count)) { - if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) - BUG(); - t->func(t->data); - tasklet_unlock(t); - continue; - } - tasklet_unlock(t); - } - - local_irq_disable(); - t->next = NULL; - *__this_cpu_read(tasklet_hi_vec.tail) = t; - __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); - __raise_softirq_irqoff(HI_SOFTIRQ); - local_irq_enable(); - } + __tasklet_action(a, list); } @@ -799,7 +874,7 @@ void tasklet_kill(struct tasklet_struct *t) while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { do { - yield(); + msleep(1); } while (test_bit(TASKLET_STATE_SCHED, &t->state)); } tasklet_unlock_wait(t); @@ -1005,6 +1080,23 @@ void __init softirq_init(void) open_softirq(HI_SOFTIRQ, tasklet_hi_action); } +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) +void tasklet_unlock_wait(struct tasklet_struct *t) +{ + while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { + /* + * Hack for now to avoid this busy-loop: + */ +#ifdef CONFIG_PREEMPT_RT_FULL + msleep(1); +#else + barrier(); +#endif + } +} +EXPORT_SYMBOL(tasklet_unlock_wait); +#endif + static int run_ksoftirqd(void * __bind_cpu) { ksoftirqd_set_sched_params(); diff --git a/localversion-rt b/localversion-rt index 90290c6..b72862e 100644 --- a/localversion-rt +++ b/localversion-rt @@ -1 +1 @@ --rt29 +-rt30 diff --git a/mm/slab.c b/mm/slab.c index 015cd76..5251b99 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -594,6 +594,7 @@ static enum { PARTIAL_AC, PARTIAL_L3, EARLY, + LATE, FULL } g_cpucache_up; @@ -605,6 +606,12 @@ int slab_is_available(void) return g_cpucache_up >= EARLY; } +/* + * Guard access to the cache-chain. + */ +static DEFINE_MUTEX(cache_chain_mutex); +static struct list_head cache_chain; + #ifdef CONFIG_LOCKDEP /* @@ -666,38 +673,41 @@ static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep) slab_set_debugobj_lock_classes_node(cachep, node); } -static void init_node_lock_keys(int q) +static void init_lock_keys(struct kmem_cache *cachep, int node) { - struct cache_sizes *s = malloc_sizes; + struct kmem_list3 *l3; - if (g_cpucache_up != FULL) + if (g_cpucache_up < LATE) return; - for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) { - struct kmem_list3 *l3; + l3 = cachep->nodelists[node]; + if (!l3 || OFF_SLAB(cachep)) + return; - l3 = s->cs_cachep->nodelists[q]; - if (!l3 || OFF_SLAB(s->cs_cachep)) - continue; + slab_set_lock_classes(cachep, &on_slab_l3_key, &on_slab_alc_key, node); +} - slab_set_lock_classes(s->cs_cachep, &on_slab_l3_key, - &on_slab_alc_key, q); - } +static void init_node_lock_keys(int node) +{ + struct kmem_cache *cachep; + + list_for_each_entry(cachep, &cache_chain, next) + init_lock_keys(cachep, node); } -static inline void init_lock_keys(void) +static inline void init_cachep_lock_keys(struct kmem_cache *cachep) { int node; for_each_node(node) - init_node_lock_keys(node); + init_lock_keys(cachep, node); } #else -static void init_node_lock_keys(int q) +static void init_node_lock_keys(int node) { } -static inline void init_lock_keys(void) +static void init_cachep_lock_keys(struct kmem_cache *cachep) { } @@ -710,12 +720,6 @@ static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep) } #endif -/* - * Guard access to the cache-chain. - */ -static DEFINE_MUTEX(cache_chain_mutex); -static struct list_head cache_chain; - static DEFINE_PER_CPU(struct delayed_work, slab_reap_work); static DEFINE_PER_CPU(struct list_head, slab_free_list); static DEFINE_LOCAL_IRQ_LOCK(slab_lock); @@ -1725,14 +1729,15 @@ void __init kmem_cache_init_late(void) { struct kmem_cache *cachep; - /* Annotate slab for lockdep -- annotate the malloc caches */ - init_lock_keys(); + g_cpucache_up = LATE; /* 6) resize the head arrays to their final sizes */ mutex_lock(&cache_chain_mutex); - list_for_each_entry(cachep, &cache_chain, next) + list_for_each_entry(cachep, &cache_chain, next) { + init_cachep_lock_keys(cachep); if (enable_cpucache(cachep, GFP_NOWAIT)) BUG(); + } mutex_unlock(&cache_chain_mutex); /* Done! */ @@ -2543,6 +2548,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, slab_set_debugobj_lock_classes(cachep); } + init_cachep_lock_keys(cachep); + /* cache setup completed, link it into the list */ list_add(&cachep->next, &cache_chain); oops:
Attachment:
signature.asc
Description: This is a digitally signed message part