linux - qué - Aislar el módulo del kernel a un núcleo específico usando Cpuset
linux y sus versiones (3)
Así que quiero que el módulo se ejecute en un núcleo aislado.
y
en realidad aísla un núcleo específico en nuestro sistema y ejecuta solo un proceso específico para ese núcleo
Este es un código fuente de trabajo compilado y probado en un cuadro de Debian usando el kernel 3.16. Describiré cómo cargar y descargar primero 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
Construye y carga el módulo ...
make
insmod toy param_cpu_id=2
Para descargar el módulo de uso.
rmmod toy
No estoy usando modprobe porque espera alguna configuración, etc. El parámetro que estamos pasando al módulo del kernel de toy
es la CPU que queremos aislar. Ninguna de las operaciones del dispositivo a las que se llama se ejecutará a menos que se estén ejecutando en esa CPU.
Una vez cargado el módulo puedes encontrarlo aquí.
/dev/toy
Operaciones simples como
cat /dev/toy
Crea eventos que el módulo del kernel atrapa y produce algunos resultados. 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 primer lugar, la ejecución en un núcleo aislado.
En init, get_cpu
desactiva la preferencia, es decir, todo lo que venga después de que no sea eliminado por el núcleo y se ejecutará en un núcleo. Tenga en cuenta que esto se hizo con kernel 3.16, su kilometraje puede variar dependiendo de la versión de su kernel, pero creo que estas API han existido por 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 no es necesario llamar a preempt_disable
antes de llamar a get_cpu
. La llamada get_cpu es una envoltura alrededor 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();
}
Usted puede obtener tan elegante como desee utilizando 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/writing-a-linux-kernel-module-part-2-a-character-device/
Desde el espacio de usuario, podemos usar cpuset para aislar realmente un núcleo específico en nuestro sistema y ejecutar solo un proceso específico a ese núcleo.
Estoy tratando de hacer lo mismo con un módulo del kernel. Así que quiero que el módulo se ejecute en un núcleo aislado. En otras palabras: ¿Cómo utilizo cpuset
''s dentro de un módulo del kernel? *
Usar linux/cpuset.h en mi módulo del kernel no funciona. Entonces, tengo un módulo como este:
#include <linux/module.h>
#include <linux/cpuset.h>
...
#ifdef CONFIG_CPUSETS
printk(KERN_INFO, "cpusets is enabled!");
#endif
cpuset_init(); // this function is declared in cpuset.h
...
Al intentar cargar este módulo, recibo (en dmesg
) que el siguiente mensaje cpusets is enabled!
. Pero también recibo el mensaje Unknown symbol cpu_init (err 0)
.
De manera similar, intenté usar sched_setaffinity
desde linux/sched.h
para mover todos los procesos en ejecución a un núcleo específico y luego ejecutar mi módulo a un núcleo aislado. Unknown symbol sched_setaffinity (err 0)
el mismo mensaje de error: Unknown symbol sched_setaffinity (err 0)
. Supongo que obtuve los "símbolos desconocidos" porque esas funciones no tienen EXPORT_SYMBOL
en el kernel. Así que fui e intenté llamar al sistema sys_sched_setaffinity
llamada (basado en esta question ) pero nuevamente obtuve este mensaje: ¡ Unknown symbol sys_sched_setaffinity (err 0)
!
Además, no estoy buscando una solución que use isolcpus
, que se establece durante el arranque. Me gustaría simplemente cargar el módulo y luego el aislamiento para que se produzca.
- (Más preciso, quiero que los subprocesos del núcleo se ejecuten en núcleos aislados . Sé que puedo usar la afinidad para unir los hilos a núcleos específicos, pero esto no me garantiza que los núcleos vayan a estar aislados por otros procesos que se ejecutan en ellos. )
¿Intentaste trabajar con
struct workqueue_attrs {
cpumask_var_t cpumask; /* allowed CPUs */
}
En primer lugar, la CPU se debe aislar mediante (por ejemplo, CPU 0x1)
setenv bootargs isolcpus=/"0x1"/
y después
struct lkm_sample {
struct work_struct lkm_work_struct;
struct workqueue_struct *lkm_wq_struct;
...
};
static struct lkm_sample lkm_smpl;
static void work(struct work_struct *work)
{
struct lkm_sample *tmp = container_of(work, struct lkm_sample, lkm_work_struct);
....
return;
}
static int __init lkm_init(void)
{
//see: https://lwn.net/Articles/540999/
lkm_smpl.lkm_wq_struct = create_singlethread_workqueue("you_wq_name");
INIT_WORK(&lkm_smpl.lkm_wq_struct, work);
}
Si desea iniciar (ejecute __init) lkm en una CPU aislada:
setenv bootargs isolcpus = / "0x1" /
lsmod helper_module.ko con
call_usermodehelper_setup struct subprocess_info * call_usermodehelper_setup (char * path, char ** argv, / * taskset 0x00000001 helper_application * / char ** envp, gfp_t gfp_mask, int (* init) (struct subprocess_info * info) limpieza) (struct subprocess_info * info), void * data); Use el módulo del kernel auxiliar que debe ejecutar el programa de espacio de usuario (helper_application) a través del conjunto de tareas y la máscara debe ser de isolcpus. El módulo auxiliar debería ejecutar solo la función __init () y devolver -1 porque solo una tarea: ejecutar la aplicación de espacio de usuario en una CPU aislada.
A continuación, la aplicación de ayuda del espacio de usuario debe simplemente: lsmod para goal_module.ko, goal_module debe comenzar en la misma CPU aislada.
Use la cola de trabajo para continuar ejecutando el módulo aislado en la CPU aislada.
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 su problema. Veo que está incluyendo el archivo linux/cpuset.h
que define el método: cpuset_init
entre otros. Sin embargo, tanto durante la compilación como con el comando nm
podemos ver indicadores que nos indican que esta función no está disponible:
Compilando
root@hectorvp-pc:/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''
Consulte la WARNING: "cupset_init" [...] undefined!
. Y usando nm
:
root@hectorvp-pc:/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 como sigue:
root@hectorvp-pc:/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 se exportó pero no está disponible en /lib/modules/$(uname -r)/build/Module.symvers
. Así que tienes razón.
Después de una investigación adicional, encontré 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ás acceso al espacio de usuario.
El trabajo que encontré para hacer que el módulo sea capaz de llamar a estos símbolos se informa en la segunda respuesta de esta pregunta . Tenga en cuenta que ya no necesita incluir linux/cpuset.h
:
#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
. A continuación se muestra la salida 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 tu pregunta inicial:
¿Cómo uso cpuset''s dentro de un módulo del kernel? *
Probablemente no de la forma más elegante, ya que no soy un experto en absoluto. Necesitas continuar desde aquí.
Saludos