GNU/Linux >> Linux Esercitazione >  >> Linux

Isolare il modulo del kernel su un core specifico utilizzando Cpuset

Hai indicato nella tua domanda:

Immagino di aver ottenuto i "simboli sconosciuti" perché quelle funzioni non hanno EXPORT_SYMBOL nel kernel

Penso che questo sia il punto chiave del tuo problema. Vedo che stai includendo il file linux/cpuset.h che definisce il metodo:cpuset_init tra gli altri. Tuttavia, sia durante la compilazione che utilizzando il comando nm possiamo vedere indicatori che ci indicano che questa funzione non è disponibile:

Compilazione:

[email protected]:/home/hectorvp/cpuset/cpuset_try# make
make -C /lib/modules/3.19.0-31-generic/build M=/home/hectorvp/cpuset/cpuset_try modules 
make[1]: Entering directory '/usr/src/linux-headers-3.19.0-31-generic'
  CC [M]  /home/hectorvp/cpuset/cpuset_try/cpuset_try.o
  Building modules, stage 2. 
  MODPOST 1 modules 
  WARNING: "cpuset_init" [/home/hectorvp/cpuset/cpuset_try/cpuset_try.ko] undefined!
  CC      /home/hectorvp/cpuset/cpuset_try/cpuset_try.mod.o
  LD [M]  /home/hectorvp/cpuset/cpuset_try/cpuset_try.ko
make[1]: Leaving directory '/usr/src/linux-headers-3.19.0-31-generic'

Vedi il WARNING: "cupset_init" [...] undefined! . E usando nm :

[email protected]:/home/hectorvp/cpuset/cpuset_try# nm cpuset_try.ko
0000000000000030 T cleanup_module
                 U cpuset_init
                 U __fentry__
0000000000000000 T init_module
000000000000002f r __module_depends
                 U printk
0000000000000000 D __this_module
0000000000000000 r __UNIQUE_ID_license0
000000000000000c r __UNIQUE_ID_srcversion1
0000000000000038 r __UNIQUE_ID_vermagic0
0000000000000000 r ____versions

(Nota:U sta per 'non definito')

Tuttavia , ho esplorato i simboli del kernel come segue:

[email protected]:/home/hectorvp/cpuset/cpuset_try# cat /proc/kallsyms | grep cpuset_init
ffffffff8110dc40 T cpuset_init_current_mems_allowed
ffffffff81d722ae T cpuset_init
ffffffff81d72342 T cpuset_init_smp

Vedo che è esportato ma non è disponibile in /lib/modules/$(uname -r)/build/Module.symvers . Quindi hai ragione.

Dopo ulteriori indagini ho scoperto che in realtà è definito in:

http://lxr.free-electrons.com/source/kernel/cpuset.c#L2101

Questa è la funzione che devi chiamare poiché è disponibile nello spazio del kernel. Quindi non avrai bisogno di accedere allo spazio utente.

Il lavoro che ho trovato per rendere il modulo in grado di chiamare questi simboli è riportato nella seconda risposta di questa domanda. Tieni presente che non è necessario includere linux/cpuset.h più :

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
//#include <linux/cpuset.h>
#include <linux/kallsyms.h>


int init_module(void)
{
        static void (*cpuset_init_p)(void);
        cpuset_init_p = (void*) kallsyms_lookup_name("cpuset_init");
        printk(KERN_INFO "Starting ...\n");
        #ifdef CONFIG_CPUSETS
            printk(KERN_INFO "cpusets is enabled!");
        #endif
        (*cpuset_init_p)();
        /* 
         * A non 0 return means init_module failed; module can't be loaded. 
         */
        return 0;
}

void cleanup_module(void)
{
        printk(KERN_INFO "Ending ...\n");
}

MODULE_LICENSE("GPL");

L'ho compilato con successo e installato con insmod . Di seguito è riportato l'output che ho ottenuto in dmesg :

[ 1713.738925] Starting ...
[ 1713.738929] cpusets is enabled!
[ 1713.738943] kernel tried to execute NX-protected page - exploit attempt? (uid: 0)
[ 1713.739042] BUG: unable to handle kernel paging request at ffffffff81d7237b
[ 1713.739074] IP: [<ffffffff81d7237b>] cpuset_init+0x0/0x94
[ 1713.739102] PGD 1c16067 PUD 1c17063 PMD 30bc74063 PTE 8000000001d72163
[ 1713.739136] Oops: 0011 [#1] SMP 
[ 1713.739153] Modules linked in: cpuset_try(OE+) xt_conntrack ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 xt_addrtype iptable_filter ip_tables x_tables nf_nat nf_conntrack br_netfilter bridge stp llc pci_stub vboxpci(OE) vboxnetadp(OE) vboxnetflt(OE) vboxdrv(OE) aufs binfmt_misc cfg80211 nls_iso8859_1 snd_hda_codec_hdmi snd_hda_codec_realtek intel_rapl snd_hda_codec_generic iosf_mbi snd_hda_intel x86_pkg_temp_thermal intel_powerclamp snd_hda_controller snd_hda_codec snd_hwdep coretemp kvm_intel amdkfd kvm snd_pcm snd_seq_midi snd_seq_midi_event amd_iommu_v2 snd_rawmidi radeon snd_seq crct10dif_pclmul crc32_pclmul snd_seq_device aesni_intel ttm aes_x86_64 drm_kms_helper drm snd_timer i2c_algo_bit dcdbas mei_me lrw gf128mul mei snd glue_helper ablk_helper
[ 1713.739533]  cryptd soundcore shpchp lpc_ich serio_raw 8250_fintek mac_hid video parport_pc ppdev lp parport autofs4 hid_generic usbhid hid e1000e ahci psmouse ptp libahci pps_core
[ 1713.739628] CPU: 2 PID: 24679 Comm: insmod Tainted: G           OE  3.19.0-56-generic #62-Ubuntu
[ 1713.739663] Hardware name: Dell Inc. OptiPlex 9020/0PC5F7, BIOS A03 09/17/2013
[ 1713.739693] task: ffff8800d29f09d0 ti: ffff88009177c000 task.ti: ffff88009177c000
[ 1713.739723] RIP: 0010:[<ffffffff81d7237b>]  [<ffffffff81d7237b>] cpuset_init+0x0/0x94
[ 1713.739757] RSP: 0018:ffff88009177fd10  EFLAGS: 00010292
[ 1713.739779] RAX: 0000000000000013 RBX: ffffffff81c1a080 RCX: 0000000000000013
[ 1713.739808] RDX: 000000000000c928 RSI: 0000000000000246 RDI: 0000000000000246
[ 1713.739836] RBP: ffff88009177fd18 R08: 000000000000000a R09: 00000000000003db
[ 1713.739865] R10: 0000000000000092 R11: 00000000000003db R12: ffff8800ad1aaee0
[ 1713.739893] R13: 0000000000000000 R14: ffffffffc0947000 R15: ffff88009177fef8
[ 1713.739923] FS:  00007fbf45be8700(0000) GS:ffff88031dd00000(0000) knlGS:0000000000000000
[ 1713.739955] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1713.739979] CR2: ffffffff81d7237b CR3: 00000000a3733000 CR4: 00000000001407e0
[ 1713.740007] Stack:
[ 1713.740016]  ffffffffc094703e ffff88009177fd98 ffffffff81002148 0000000000000001
[ 1713.740052]  0000000000000001 ffff8802479de200 0000000000000001 ffff88009177fd78
[ 1713.740087]  ffffffff811d79e9 ffffffff810fb058 0000000000000018 ffffffffc0949000
[ 1713.740122] Call Trace:
[ 1713.740137]  [<ffffffffc094703e>] ? init_module+0x3e/0x50 [cpuset_try]
[ 1713.740175]  [<ffffffff81002148>] do_one_initcall+0xd8/0x210
[ 1713.740190]  [<ffffffff811d79e9>] ? kmem_cache_alloc_trace+0x189/0x200
[ 1713.740207]  [<ffffffff810fb058>] ? load_module+0x15b8/0x1d00
[ 1713.740222]  [<ffffffff810fb092>] load_module+0x15f2/0x1d00
[ 1713.740236]  [<ffffffff810f6850>] ? store_uevent+0x40/0x40
[ 1713.740250]  [<ffffffff810fb916>] SyS_finit_module+0x86/0xb0
[ 1713.740265]  [<ffffffff817ce10d>] system_call_fastpath+0x16/0x1b
[ 1713.740280] Code: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 53 58 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <00> 00 00 00 00 1c 00 00 00 c0 92 2c 7d c0 92 2c 7d a0 fc 69 ee 
[ 1713.740398] RIP  [<ffffffff81d7237b>] cpuset_init+0x0/0x94
[ 1713.740413]  RSP <ffff88009177fd10>
[ 1713.740421] CR2: ffffffff81d7237b
[ 1713.746177] ---[ end trace 25614103c0658b94 ]---

Nonostante gli errori, direi di aver risposto alla tua domanda iniziale:

Come posso usare cpuset dall'interno di un modulo del kernel? *

Probabilmente non nel modo più elegante dato che non sono affatto un esperto. Devi continuare da qui.

Saluti


Quindi voglio che il modulo venga eseguito in un core isolato.

e

isolare effettivamente un core specifico nel nostro sistema ed eseguire solo un processo specifico su quel core

Questo è un codice sorgente funzionante compilato e testato su una macchina Debian usando il kernel 3.16. Descriverò prima come caricare e scaricare e cosa significa il parametro passato.

Tutte le fonti possono essere trovate su github qui...

https://github.com/harryjackson/doc/tree/master/linux/kernel/toy/toy

Compila e carica il modulo...

make
insmod toy param_cpu_id=2

Per scaricare il modulo usa

rmmod toy

Non sto usando modprobe perché si aspetta qualche configurazione ecc. Il parametro che stiamo passando al toy modulo kernel è la CPU che vogliamo isolare. Nessuna delle operazioni del dispositivo che vengono chiamate verrà eseguita a meno che non sia in esecuzione su quella CPU.

Una volta caricato il modulo, puoi trovarlo qui

/dev/toy

Semplici operazioni come

cat /dev/toy

crea eventi che il modulo del kernel rileva e produce un output. Puoi vedere l'output usando dmesg .

Codice sorgente...

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harry");
MODULE_DESCRIPTION("toy kernel module");
MODULE_VERSION("0.1"); 
#define  DEVICE_NAME "toy"
#define  CLASS_NAME  "toy"

static int    param_cpu_id;
module_param(param_cpu_id    , int, (S_IRUSR | S_IRGRP | S_IROTH));
MODULE_PARM_DESC(param_cpu_id, "CPU ID that operations run on");

//static void    bar(void *arg);
//static void    foo(void *cpu);
static int     toy_open(   struct inode *inodep, struct file *fp);
static ssize_t toy_read(   struct file *fp     , char *buffer, size_t len, loff_t * offset);
static ssize_t toy_write(  struct file *fp     , const char *buffer, size_t len, loff_t *);
static int     toy_release(struct inode *inodep, struct file *fp);

static struct file_operations toy_fops = {
  .owner = THIS_MODULE,
  .open = toy_open,
  .read = toy_read,
  .write = toy_write,
  .release = toy_release,
};

static struct miscdevice toy_device = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = "toy",
  .fops = &toy_fops
};

//static int CPU_IDS[64] = {0};
static int toy_open(struct inode *inodep, struct file *filep) {
  int this_cpu = get_cpu();
  printk(KERN_INFO "open: called on CPU:%d\n", this_cpu);
  if(this_cpu == param_cpu_id) {
    printk(KERN_INFO "open: is on requested CPU: %d\n", smp_processor_id());
  }
  else {
    printk(KERN_INFO "open: not on requested CPU:%d\n", smp_processor_id());
  }
  put_cpu();
  return 0;
}
static ssize_t toy_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
  int this_cpu = get_cpu();
  printk(KERN_INFO "read: called on CPU:%d\n", this_cpu);
  if(this_cpu == param_cpu_id) {
    printk(KERN_INFO "read: is on requested CPU: %d\n", smp_processor_id());
  }
  else {
    printk(KERN_INFO "read: not on requested CPU:%d\n", smp_processor_id());
  }
  put_cpu();
  return 0;
}
static ssize_t toy_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
  int this_cpu = get_cpu();
  printk(KERN_INFO "write called on CPU:%d\n", this_cpu);
  if(this_cpu == param_cpu_id) {
    printk(KERN_INFO "write: is on requested CPU: %d\n", smp_processor_id());
  }
  else {
    printk(KERN_INFO "write: not on requested CPU:%d\n", smp_processor_id());
  }
  put_cpu();
  return 0;
}
static int toy_release(struct inode *inodep, struct file *filep){
  int this_cpu = get_cpu();
  printk(KERN_INFO "release called on CPU:%d\n", this_cpu);
  if(this_cpu == param_cpu_id) {
    printk(KERN_INFO "release: is on requested CPU: %d\n", smp_processor_id());
  }
  else {
    printk(KERN_INFO "release: not on requested CPU:%d\n", smp_processor_id());
  }
  put_cpu();
  return 0;
}

static int __init toy_init(void) {
  int cpu_id;
  if(param_cpu_id < 0 || param_cpu_id > 4) {
    printk(KERN_INFO "toy: unable to load module without cpu parameter\n");
    return -1;
  }
  printk(KERN_INFO "toy: loading to device driver, param_cpu_id: %d\n", param_cpu_id);
  //preempt_disable(); // See notes below
  cpu_id = get_cpu();
  printk(KERN_INFO "toy init called and running on CPU: %d\n", cpu_id);
  misc_register(&toy_device);
  //preempt_enable(); // See notes below
  put_cpu();
  //smp_call_function_single(1,foo,(void *)(uintptr_t) 1,1);
  return 0;
}

static void __exit toy_exit(void) {
    misc_deregister(&toy_device);
    printk(KERN_INFO "toy exit called\n");
}

module_init(toy_init);
module_exit(toy_exit); 

Il codice sopra contiene i due metodi che hai richiesto, ovvero l'isolamento della CPU e su init eseguito su un core isolato.

Su init get_cpu disabilita la prelazione, cioè tutto ciò che viene dopo non sarà annullato dal kernel e verrà eseguito su un core. Nota, questo è stato fatto con il kernel 3.16, il tuo chilometraggio può variare a seconda della versione del tuo kernel, ma penso che queste API siano in circolazione da molto tempo

Questo è il Makefile...

obj-m += toy.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Appunti. get_cpu è dichiarato in linux/smp.h come

#define get_cpu()   ({ preempt_disable(); smp_processor_id(); })
#define put_cpu()   preempt_enable()

quindi non hai effettivamente bisogno di chiamare preempt_disable prima di chiamare get_cpu .La chiamata get_cpu è un wrapper attorno alla seguente sequenza di chiamate...

preempt_count_inc();
barrier();

e put_cpu lo sta davvero facendo...

barrier();
if (unlikely(preempt_count_dec_and_test())) {
  __preempt_schedule();
}   

Puoi diventare fantasioso quanto vuoi usando quanto sopra. Quasi tutto questo è stato preso dalle seguenti fonti...

Google per... smp_call_function_single

Linux Kernel Development, libro di Robert Love.

http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/

https://github.com/vsinitsyn/reverse/blob/master/reverse.c


Usando on_each_cpu() e il filtro per la CPU desiderata funziona:

targetcpu.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

const static int TARGET_CPU = 4;

static void func(void *info){
    int cpu = get_cpu();
    if(cpu == TARGET_CPU){
        printk("on target cpu: %d\n", cpu);
    }
    put_cpu();
}

int init_module(void) {
    printk("enter\n");
    on_each_cpu(func, NULL, 1);
    return 0;
}

void cleanup_module(void) {
    printk("exit\n");
}

Makefile

obj-m += targetcpu.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Linux
  1. Linux:un kernel contaminato in Linux?

  2. Esempio di utilizzo di getnstimeofday nel kernel Linux

  3. Come ottenere un indirizzo di memoria specifico usando C

  4. modifiche ai parametri del modulo del kernel (usando /sys/module)

  5. È possibile sviluppare il modulo del kernel Linux in CLion?

Come caricare o scaricare un modulo del kernel Linux

Comando Lsmod in Linux (Elenca i moduli del kernel)

Comando Modprobe in Linux

Elenca le informazioni sul modulo del kernel caricato utilizzando il comando Lsmod

Come elencare il modulo del kernel caricato utilizzando il comando Lsmod

Cos'è un kernel contaminato in Linux?