Usted señaló en su pregunta:
Supongo que obtuve los "símbolos desconocidos" porque esas funciones no tienen EXPORT_SYMBOL en el kernel
Creo que este es el punto clave de tu problema. Veo que estás incluyendo el archivo linux/cpuset.h
que define el método:cpuset_init
entre otros. Sin embargo, tanto durante la compilación como usando el comando nm
podemos ver indicadores que nos indican que esta función no está disponible:
Compilando:
[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'
Ver el WARNING: "cupset_init" [...] undefined!
. Y 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
significa 'indefinido')
Sin embargo , he estado explorando los símbolos del kernel de la siguiente manera:
[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
Veo que está exportado pero no está disponible en /lib/modules/$(uname -r)/build/Module.symvers
. Así que tienes razón.
Después de una mayor investigación, descubrí que en realidad está definido en:
http://lxr.free-electrons.com/source/kernel/cpuset.c#L2101
Esta es la función a la que debe llamar, ya que está disponible en el espacio del kernel. Por lo tanto, no necesitará acceder al espacio de usuario.
El trabajo que encontré para hacer que el módulo pueda llamar a estos símbolos se informa en la segunda respuesta de esta pregunta. Tenga en cuenta que no necesita incluir linux/cpuset.h
más :
#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");
Lo compilé con éxito y lo instalé con insmod
. Abajo está el resultado que obtuve 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 ]---
A pesar de los errores, diría que he respondido a su pregunta inicial:
¿Cómo uso cpuset desde dentro de un módulo kernel? *
Probablemente no de la manera más elegante, ya que no soy un experto en absoluto. Tienes que continuar desde aquí.
Saludos
Así que quiero que el módulo se ejecute en un núcleo aislado.
y
realmente aislar un núcleo específico en nuestro sistema y ejecutar solo un proceso específico para ese núcleo
Este es un código fuente de trabajo compilado y probado en una caja Debian usando el kernel 3.16. Primero describiré cómo cargar y descargar y qué significa el parámetro pasado.
Todas las fuentes se pueden encontrar en github aquí...
https://github.com/harryjackson/doc/tree/master/linux/kernel/toy/toy
Compile y cargue el módulo...
make
insmod toy param_cpu_id=2
Para descargar el uso del módulo
rmmod toy
No estoy usando modprobe porque espera alguna configuración, etc. El parámetro que estamos pasando al toy
El módulo kernel es la CPU que queremos aislar. Ninguna de las operaciones de dispositivo a las que se llama se ejecutará a menos que se estén ejecutando en esa CPU.
Una vez que se carga el módulo, puede encontrarlo aquí
/dev/toy
Operaciones simples como
cat /dev/toy
crear eventos que el módulo del kernel capture y produzca alguna salida. Puedes ver la salida usando dmesg
.
Código fuente...
#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);
El código anterior contiene los dos métodos que solicitó, es decir, el aislamiento de la CPU y en init
se ejecuta en un núcleo aislado.
En inicio get_cpu
deshabilita la preferencia, es decir, cualquier cosa que venga después no será reemplazada por el núcleo y se ejecutará en un núcleo. Tenga en cuenta que esto se hizo con el kernel usando 3.16, su kilometraje puede variar según la versión de su kernel, pero creo que estas API existen desde hace mucho tiempo
Este es el 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
notas get_cpu
se declara en linux/smp.h
como
#define get_cpu() ({ preempt_disable(); smp_processor_id(); })
#define put_cpu() preempt_enable()
por lo que en realidad no necesita llamar a preempt_disable
antes de llamar get_cpu
.La llamada get_cpu es un contenedor de la siguiente secuencia de llamadas...
preempt_count_inc();
barrier();
y put_cpu realmente está haciendo esto...
barrier();
if (unlikely(preempt_count_dec_and_test())) {
__preempt_schedule();
}
Puedes ponerte tan elegante como quieras usando lo anterior. Casi todo esto fue tomado de las siguientes fuentes..
Google para... smp_call_function_single
Linux Kernel Development, libro de Robert Love.
http://derekmolloy.ie/escritura-de-linux-kernel-module-part-2-a-character-device/
https://github.com/vsinitsyn/reverse/blob/master/reverse.c
Usando on_each_cpu()
y el filtrado de las obras de CPU deseadas:
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