Vous avez pointé dans votre question :
Je suppose que j'ai eu les "symboles inconnus" parce que ces fonctions n'ont pas EXPORT_SYMBOL dans le noyau
Je pense que c'est le point clé de ton problème. Je vois que vous incluez le fichier linux/cpuset.h
qui définit la méthode :cpuset_init
entre autres. Cependant, à la fois lors de la compilation et à l'aide de la commande nm
nous pouvons voir des indicateurs nous indiquant que cette fonction n'est pas disponible :
Compilation :
[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'
Voir le WARNING: "cupset_init" [...] undefined!
. Et en utilisant 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
(Remarque :U
signifie 'indéfini')
Cependant , j'ai exploré les symboles du noyau comme suit :
[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
Je vois qu'il est exporté mais qu'il n'est pas disponible dans /lib/modules/$(uname -r)/build/Module.symvers
. Vous avez donc raison.
Après une enquête plus approfondie, j'ai découvert qu'il était en fait défini dans :
http://lxr.free-electrons.com/source/kernel/cpuset.c#L2101
C'est la fonction que vous devez appeler car elle est disponible dans l'espace du noyau. Ainsi, vous n'aurez pas besoin d'accéder à l'espace utilisateur.
Le travail que j'ai trouvé pour rendre le module capable d'appeler ces symboles est rapporté dans la deuxième réponse de cette question. Notez que vous n'avez pas besoin d'inclure linux/cpuset.h
plus :
#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");
Je l'ai compilé avec succès et installé avec insmod
. Ci-dessous la sortie que j'ai obtenue en 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 ]---
Malgré les erreurs, je dirais que j'ai répondu à votre question initiale :
Comment utiliser les cpuset depuis un module du noyau ? *
Probablement pas de la manière la plus élégante car je ne suis pas du tout un expert. Vous devez continuer à partir d'ici.
Cordialement
Je veux donc que le module soit exécuté dans un noyau isolé.
et
isoler en fait un noyau spécifique dans notre système et exécuter un seul processus spécifique sur ce noyau
Il s'agit d'un code source fonctionnel compilé et testé sur une machine Debian utilisant le noyau 3.16. Je vais décrire comment charger et décharger en premier et ce que signifie le paramètre passé.
Toutes les sources peuvent être trouvées sur github ici...
https://github.com/harryjackson/doc/tree/master/linux/kernel/toy/toy
Compilez et chargez le module...
make
insmod toy param_cpu_id=2
Pour décharger le module, utilisez
rmmod toy
Je n'utilise pas modprobe car il attend une configuration, etc. Le paramètre que nous passons au toy
module noyau est le processeur que nous voulons isoler. Aucune des opérations de périphérique appelées ne s'exécutera à moins qu'elles ne s'exécutent sur ce processeur.
Une fois le module chargé, vous pouvez le trouver ici
/dev/toy
Des opérations simples comme
cat /dev/toy
créer des événements que le module du noyau intercepte et produit une sortie. Vous pouvez voir la sortie en utilisant dmesg
.
Code source...
#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);
Le code ci-dessus contient les deux méthodes que vous avez demandées, c'est-à-dire l'isolation du processeur et sur init
exécuté sur un cœur isolé.
A l'initialisation get_cpu
désactive la préemption, c'est-à-dire que tout ce qui vient après ne sera pas préempté par le noyau et fonctionnera sur un cœur. Remarque, cela a été fait avec le noyau 3.16, votre kilométrage peut varier en fonction de la version de votre noyau, mais je pense que ces API existent depuis longtemps
C'est le 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
Remarques. get_cpu
est déclaré en linux/smp.h
comme
#define get_cpu() ({ preempt_disable(); smp_processor_id(); })
#define put_cpu() preempt_enable()
vous n'avez donc pas besoin d'appeler le preempt_disable
avant d'appeler le get_cpu
.L'appel get_cpu est un wrapper autour de la séquence d'appels suivante...
preempt_count_inc();
barrier();
et put_cpu fait vraiment ça...
barrier();
if (unlikely(preempt_count_dec_and_test())) {
__preempt_schedule();
}
Vous pouvez obtenir autant de fantaisie que vous le souhaitez en utilisant ce qui précède. Presque tout cela a été tiré des sources suivantes..
Google pour... smp_call_function_single
Développement du noyau Linux, livre de Robert Love.
http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/
https://github.com/vsinitsyn/reverse/blob/master/reverse.c
Utilisation de on_each_cpu()
et le filtrage du CPU souhaité fonctionne :
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