linux-kernelmultiprocessingmsr

How to execute a piece of kernel code on all CPUs?


I'm trying to make a kernel module to enable FOP compatibility mode for x87 FPU. This is done via setting bit 2 in IA32_MISC_ENABLE MSR. Here's the code:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/msr-index.h>
#include <asm/msr.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("10110111");
MODULE_DESCRIPTION("Module to enable FOPcode compatibility mode");
MODULE_VERSION("0.1");

static int __init fopCompat_init(void)
{
   unsigned long long misc_enable=native_read_msr(MSR_IA32_MISC_ENABLE);
   printk(KERN_INFO "Before trying to set FOP_COMPAT, IA32_MISC_ENABLE=%llx,"
                    " i.e. FOP_COMPAT is %senabled\n"
                    ,misc_enable,misc_enable&MSR_IA32_MISC_ENABLE_X87_COMPAT?"":"NOT ");

   wrmsrl(MSR_IA32_MISC_ENABLE,misc_enable|MSR_IA32_MISC_ENABLE_X87_COMPAT);
   misc_enable=native_read_msr(MSR_IA32_MISC_ENABLE);

   printk(KERN_INFO "Tried to set FOP_COMPAT. Result: IA32_MISC_ENABLE=%llx,"
                    " i.e. FOP_COMPAT is now %senabled\n"
                    ,misc_enable,misc_enable&MSR_IA32_MISC_ENABLE_X87_COMPAT?"":"NOT ");
   return 0;
}

static void __exit fopCompat_exit(void)
{
   const unsigned long long misc_enable=native_read_msr(MSR_IA32_MISC_ENABLE);
   printk(KERN_INFO "Quitting FOP-compat with IA32_MISC_ENABLE=%llx\n",misc_enable);
   if(!(misc_enable & MSR_IA32_MISC_ENABLE_X87_COMPAT))
       printk(KERN_INFO "NOTE: seems some CPUs still have to be set up, "
                        "or compatibility mode will work inconsistently\n");
   printk(KERN_INFO "\n");
}

module_init(fopCompat_init);
module_exit(fopCompat_exit);

It seems to work, but on multiple insmod/rmmod cycles I sometimes get dmesg output that the compatibility mode wasn't still enabled, although it was immediately after doing wrmsr. After some thinking I realized that it's because the module code was executed on different logical CPUs (I have Core i7 with 4 cores*HT=8 logical CPUs), so I had a 1/8 chance of getting "enabled" print on doing rmmod. After repeating the cycle for about 20 times I got consistent "enabled" prints, and my userspace application happily works with it.

So now my question is: how can I make my code execute on all logical CPUs present on the system, so as to enable compatibility mode for all of them?


Solution

  • For execute code on every CPU use on_each_cpu function.

    Signature:

    int on_each_cpu(void (*func) (void *info), void *info, int wait)
    

    Description:

    Call a function on all processors.

    If wait parameter is non-zero, it waits for function's completion on all CPUs.

    Function func shouldn't sleep, but whole on_each_cpu() call shouldn't be done in atomic context.