SMP处理器中要用到cpu位图,用来维护系统内CPU的状态信息,具有代表性的有:
cpu_possible_map、cpu_online_map、cpu_present_map。
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly
DECLARE_BITMAP宏定义如下
#define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)]
所以上面定义了三个unsigned long数组,数组大小有BITS_TO_LONGS宏确定,1~31个cpu,则为1;32~63个cpu,则为2。
目前的多核处理器还没有超过31个的,所以这个数组大小一般为1.
unsigned long cpu_possible_map[BITS_TO_LONGS(CONFIG_NR_CPUS)] unsigned long cpu_online_map[BITS_TO_LONGS(CONFIG_NR_CPUS)] unsigned long cpu_present_map [BITS_TO_LONGS(CONFIG_NR_CPUS)]
内核中还是用下面的一种结构体,用来表示cpu位图,于上面的结构基本等价。
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; /* struct cpumask { unsigned long bits[BITS_TO_LONGS(NR_CPUS)] } */
并且定义了to_cpumask宏用于从bit_map数组转换到cpumask结构体。
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits); const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits); const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
#define to_cpumask(bitmap) \ ((struct cpumask *)(1 ? (bitmap) \ : (void *)sizeof(__check_is_bitmap(bitmap)))) static inline int __check_is_bitmap(const unsigned long *bitmap) { return 1; }
内核中bitmap的设置与清除
static inline void set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long flags; _atomic_spin_lock_irqsave(p, flags); *p |= mask; _atomic_spin_unlock_irqrestore(p, flags); } static inline void clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long flags; _atomic_spin_lock_irqsave(p, flags); *p &= ~mask; _atomic_spin_unlock_irqrestore(p, flags); } static inline void change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long flags; _atomic_spin_lock_irqsave(p, flags); *p ^= mask; _atomic_spin_unlock_irqrestore(p, flags); } static inline int test_and_set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old; unsigned long flags; _atomic_spin_lock_irqsave(p, flags); old = *p; *p = old | mask; _atomic_spin_unlock_irqrestore(p, flags); return (old & mask) != 0; } static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old; unsigned long flags; _atomic_spin_lock_irqsave(p, flags); old = *p; *p = old & ~mask; _atomic_spin_unlock_irqrestore(p, flags); return (old & mask) != 0; } static inline int test_and_change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old; unsigned long flags; _atomic_spin_lock_irqsave(p, flags); old = *p; *p = old ^ mask; _atomic_spin_unlock_irqrestore(p, flags); return (old & mask) != 0; }
最后几个bitmap中常用的宏
#define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
把位图中所有bit设置为1
static inline void bitmap_fill(unsigned long *dst, int nbits) { size_t nlongs = BITS_TO_LONGS(nbits); if (!small_const_nbits(nbits)) { int len = (nlongs - 1) * sizeof(unsigned long); memset(dst, 0xff, len); } dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); }
上面用到两个宏,先判断nbis是否超过32,即表示位图的数组元素是否多余1个
#define small_const_nbits(nbits) \ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
最后一个32位可能没有全用到,下面的后过滤出使用到的bit
#define BITMAP_LAST_WORD_MASK(nbits) \ ( \ ((nbits) % BITS_PER_LONG) ? \ (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ )
遍历多cpu
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
展开为
/** * for_each_cpu - iterate over every cpu in a mask * @cpu: the (optionally unsigned) integer iterator * @mask: the cpumask pointer * * After the loop, cpu is >= nr_cpu_ids. */ #define for_each_cpu(cpu, mask) \ for ((cpu) = -1; \ (cpu) = cpumask_next((cpu), (mask)), \ (cpu) < nr_cpu_ids;)
/** * cpumask_next - get the next cpu in a cpumask * @n: the cpu prior to the place to search (ie. return will be > @n) * @srcp: the cpumask pointer * * Returns >= nr_cpu_ids if no further cpus set. */ static inline unsigned int cpumask_next(int n, const struct cpumask *srcp) { /* -1 is a legal arg here. */ if (n != -1) cpumask_check(n); return find_next_bit(cpumask_bits(srcp), nr_cpumask_bits, n+1); }