Abstract: multi-processor systems are becoming more and more common. Although most of the user space code will still run perfectly, and in some cases, the advantages of SMP can be exploited without additional code, however, the kernel space code must be written to be "SMP aware" and "SMP secure ". The following sections explain how to do this.
Multi-processor systems are becoming more and more common. Although most of the user space code will still run perfectly, and in some cases, the advantages of SMP can be exploited without additional code, however, the kernel space code must be written to be "SMP aware" and "SMP secure ". The following sections explain how to do this.
Problem
When multiple CPUs exist, the same code may be executed on two or more CPUs at the same time. This may cause problems in the following routine for initializing an image device.
Void init_hardware (void)
{
Outb (0x1, hardware_base + 0x30 );
Outb (0x2, hardware_base + 0x30 );
Outb (0x3, hardware_base + 0x30 );
Outb (0x4, hardware_base + 0x30 );
}
Assume that the hardware depends on the register 0x30 and is set to 0, 1, 2, and 3 in order for initialization. If there is another CPU parameter, the problem will get worse. Imagine two CPUs, both of which are executing this routine, but the CPU entry on the 2nd is a little slow:
CPU 1 CPU 2
0x30 = 1
0x30 = 2 0x30 = 1
0x30 = 3 0x30 = 2
0x30 = 4 0x30 = 3
0x30 = 4
What will happen? From the perspective of the hardware device we imagine, the bytes it receives on the register 0x30 are in the order of 1, 2, 1, 3, 2, 4, 3, and 4.
Ah! The second CPU was a mess. Fortunately, we have a way to prevent such incidents.
History of spin locks
In version 2.0.x, the Linux kernel introduces a global variable to the entire kernel to prevent problems caused by more than one CPU. This means that only one CPU can execute code from the kernel space at any time. In this way, although it can work, but when the system starts to appear with more than two CPUs, the expansion performance is not very good.
Kernel series of version 2.1.x have added more granular SMP support. This means that it is no longer dependent on the "big lock" that previously appeared as a global variable, but every smp-conscious routine now needs its own spin lock. The ASM/spinlock. h file defines several types of spin locks.
With the localized spin lock, more than one CPU can execute the kernel space code at the same time.
Simple spin lock
The simplest way to understand a spin lock is to treat it as a variable that marks a routine or as "I am currently running on another CPU. Please wait ", or mark it as "I am not running now ". If CPU 1 first enters this routine, it obtains the spin lock. When the number 2 CPU attempts to enter the same routine, the spin lock tells it that it is already held by the number 1 CPU. It can only enter after the number 1 CPU is released.
Spinlock_t my_spinlock = spin_lock_unlocked;
Unsigned long flags;
Spin_lock (& my_spinlock );
...
Critical Section
...
Spin_unlock (& my_spinlock );
Interrupted
Imagine that our hardware driver has another interrupt handler. This handler needs to modify some global variables defined by our driver. This can cause confusion. How can we solve this problem?
The first way to protect a Data Structure from interruption is to globally prohibit interruption. This is very inefficient when it is known that only your own interrupt will modify your driver variables. Fortunately, we have a better solution now. We only disable interruption during use of shared variables, and then re-enable.
There are three functions to implement this method:
Disable_irq ()
Enable_irq ()
Disable_irq_nosync ()
All three functions use an interrupt number as the parameter. Note: disabling an interruption for too long will make it difficult to track program defects, lose data, or even worse.
The non-synchronous version of The disable_irq function allows the specified IRQ handler to continue running, provided that it is already running. For general disable_irq, the specified IRQ handler is not running on the CPU.
If you need to modify the spin lock in the interrupt handler, you cannot use the common spin_lock () and spin_unlock (), but should save the interrupt status. This can be easily done by adding the _ irqsave suffix to the two functions:
Spinlock_t my_spinlock = spin_lock_unlocked;
Unsigned long flags;
Spin_lock_irqsave (& my_spinlock, flags );
...
Critical Section
...
Spin_unlock_irqrestore (& my_spinlock, flags );