Изоляция модуля ядра от конкретного ядра с помощью процессора
Из пользовательского пространства мы можем использовать cpuset, чтобы фактически изолировать конкретное ядро в нашей системе и выполнить только один конкретный процесс для этого ядра.
Я пытаюсь сделать то же самое с модулем ядра. Поэтому я хочу, чтобы модуль выполнялся в изолированном ядре. Другими словами: как я могу использовать cpuset
внутри модуля ядра? *
Использование linux / cpuset.h в моем модуле ядра не работает. Итак, у меня есть такой модуль:
#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
...
При попытке загрузить этот модуль я получаю (в dmesg
) следующее сообщение cpusets is enabled!
, Но я также получаю сообщение Unknown symbol cpu_init (err 0)
,
Точно так же я пытался использовать sched_setaffinity
от linux/sched.h
чтобы переместить все запущенные процессы на конкретное ядро, а затем запустить мой модуль на изолированное ядро. Я получил то же сообщение об ошибке: Unknown symbol sched_setaffinity (err 0)
, Я думаю, что я получил "неизвестные символы", потому что эти функции не имеют EXPORT_SYMBOL
в ядре. Поэтому я пошел и попытался позвонить sys_sched_setaffinity
системный вызов (основанный на этом вопросе), но снова получил это сообщение: Unknown symbol sys_sched_setaffinity (err 0)
!
Кроме того, я не ищу решение, которое использует isolcpus
, который устанавливается при загрузке. Я хотел бы просто загрузить модуль, а затем произойти изоляция.
- (Точнее, я хочу, чтобы потоки ядра выполнялись в изолированных ядрах. Я знаю, что могу использовать привязку для привязки потоков к конкретным ядрам, но это не гарантирует, что ядра будут изолированы другими процессами, выполняющимися на них.)
3 ответа
Поэтому я хочу, чтобы модуль выполнялся в изолированном ядре.
а также
на самом деле изолировать конкретное ядро в нашей системе и выполнить только один конкретный процесс для этого ядра
Это рабочий исходный код, скомпилированный и протестированный на коробке Debian с использованием ядра 3.16. Сначала я опишу, как загружать и выгружать и что означает передаваемый параметр.
Все источники можно найти на github здесь...
https://github.com/harryjackson/doc/tree/master/linux/kernel/toy/toy
Построить и загрузить модуль...
make
insmod toy param_cpu_id=2
Для выгрузки модуля используйте
rmmod toy
Я не использую modprobe, потому что он ожидает некоторую конфигурацию и т. Д. Параметр, который мы передаем toy
Модуль ядра - это процессор, который мы хотим изолировать. Ни одна из вызываемых операций устройства не будет запущена, если они не выполняются на этом процессоре.
После загрузки модуля вы можете найти его здесь
/dev/toy
Простые операции, такие как
cat /dev/toy
создайте события, которые модуль ядра ловит и производит некоторый вывод. Вы можете увидеть результат, используя dmesg
,
Исходный код...
#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);
Приведенный выше код содержит два метода, которые вы запрашивали: изоляция процессора и init
работать на изолированном ядре.
На иници get_cpu
отключает вытеснение, то есть все, что идет после него, не будет вытеснено ядром и будет работать на одном ядре. Обратите внимание, это было сделано ядром с использованием 3.16, ваш пробег может варьироваться в зависимости от версии вашего ядра, но я думаю, что эти API были в течение длительного времени
Это 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
Заметки. get_cpu
объявлен в linux/smp.h
как
#define get_cpu() ({ preempt_disable(); smp_processor_id(); })
#define put_cpu() preempt_enable()
так что вам на самом деле не нужно звонить preempt_disable
перед звонком get_cpu
, Вызов get_cpu является оболочкой для следующей последовательности вызовов...
preempt_count_inc();
barrier();
и put_cpu действительно делает это...
barrier();
if (unlikely(preempt_count_dec_and_test())) {
__preempt_schedule();
}
Вы можете получить столько, сколько захотите, используя вышеперечисленное. Почти все это было взято из следующих источников..
Google для... smp_call_function_single
Разработка ядра Linux, книга Роберта Лава.
http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/
Вы указали в своем вопросе:
Я думаю, что я получил "неизвестные символы", потому что эти функции не имеют EXPORT_SYMBOL в ядре
Я думаю, что это ключевой момент вашей проблемы. Я вижу, вы включаете файл linux/cpuset.h
который определяет метод: cpuset_init
среди других. Тем не менее, как во время компиляции, так и с помощью команды nm
мы видим индикаторы, указывающие на то, что эта функция недоступна:
Компиляция:
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'
Увидеть WARNING: "cupset_init" [...] undefined!
, И используя 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
(Заметка: U
обозначает "неопределенный")
Тем не менее, я изучал символы ядра следующим образом:
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
Я вижу, что это экспортируется, но это не доступно в /lib/modules/$(uname -r)/build/Module.symvers
, Значит ты прав.
После дальнейшего расследования я обнаружил, что это на самом деле определено в:
http://lxr.free-electrons.com/source/kernel/cpuset.c#L2101
Это функция, которую нужно вызывать, поскольку она доступна в пространстве ядра. Таким образом, вам не понадобится доступ к пользовательскому пространству.
Обход, который я нашел, чтобы модуль мог вызывать эти символы, описан во втором ответе на этот вопрос. Обратите внимание, что вам не нужно включать 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");
Я успешно скомпилировал и установил insmod
, Сильфон это выход, который я получил в 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 ]---
Несмотря на ошибки, я бы сказал, что ответил на ваш первоначальный вопрос:
Как мне использовать cpuset изнутри модуля ядра? *
Наверное, не самым элегантным образом, так как я совсем не эксперт. Вы должны продолжить отсюда.
С уважением
С помощью on_each_cpu()
и работает фильтрация по желаемому CPU:
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
Вы пробовали work_struct с
struct workqueue_attrs {
cpumask_var_t cpumask; /* allowed CPUs */
}
Прежде всего, процессор должен быть изолирован через (например, процессор 0x1)
setenv bootargs isolcpus=\"0x1"\
и следующий
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);
}
Если вы хотите запустить (запустить __init) lkm на изолированном процессоре:
setenv bootargs isolcpus = \ "0x1" \
lsmod helper_module.ko с
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, struct cred *new) очистка) (struct subprocess_info *info), void * data); Используйте модуль ядра помощника, который должен запускать пользовательскую программу (helper_application) через набор задач, а маска должна быть из isolcpus. Вспомогательный модуль должен запускать только __init function() и возвращать -1, потому что только одна задача: запускать пользовательское приложение на изолированном процессоре.
Приложение-помощник пользователя должно быть следующим: lsmod для goal_module.ko, goal_module должен запускаться на том же изолированном процессоре.
Используйте workqueue, чтобы продолжить запуск изолированного модуля на изолированном процессоре.