per-CPU data
Locks are the usual way to let multiple CPUs share data safely, but locks have a cost: cores contend for them, and the cache line holding shared data ping-pongs between cores, which is slow. Per-CPU data is a clever way to avoid the whole problem for certain data: instead of one shared variable that all cores fight over, you give EACH core its own private copy. A core only ever touches its own copy, so there is nothing to contend for and, in the common case, no lock needed at all.
Picture a statistics counter the kernel updates on every network packet. As one shared counter, every core incrementing it would contend on the same lock and bounce the same cache line - a serious bottleneck on a busy machine. As per-CPU data, each core increments its own local counter with no coordination; the cores never touch each other's copies, so there is no contention and no cache-line ping-pong. The kernel arranges this so that the very same variable name resolves to a different physical location depending on which CPU is running. When some code genuinely needs the grand total, it walks all the per-CPU copies and sums them - reading is occasional and can afford that, while the hot path (the frequent updates) stays fast and lock-free.
Why it matters: per-CPU data is a cornerstone of kernel scalability, turning a contended shared resource into many uncontended local ones. But it comes with a subtle requirement you must respect: while a thread is accessing its per-CPU variable, it must not be migrated to a different CPU or be preempted in a way that lets another task on the same CPU stomp on that data - so per-CPU access is paired with disabling preemption (or using the right helpers) for the duration. It also trades memory for speed (N copies instead of one) and makes reading the global total more expensive (you must sum N copies), which is the honest price for eliminating contention on the write-heavy fast path.
shared counter: every core does lock; counter++; unlock -- contention + cache-line bounce. per-CPU: each core does its_own_counter++ with no lock; to read the total, sum all cores' copies. Access pins to the current CPU (preemption disabled).
Give each core its own copy so updates need no lock and no cache-line bouncing; sum the copies only when you need the total.
Per-CPU data is only safe if the task cannot move to another CPU mid-access, so it is used with preemption disabled (or the proper get_cpu_var/this_cpu helpers). It trades memory (one copy per CPU) and a costlier global read (summing all copies) for a contention-free write path - do not assume reads are cheap.