versiones qué nucleo licencia informacion distribuye como cargar caracteristicas bajo linux linux-kernel linux-device-driver cpuset

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/

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

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:

  1. setenv bootargs isolcpus = / "0x1" /

  2. 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.

  3. 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.

  4. 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