Подтвердить что ты не робот

Изолировать модуль ядра конкретному ядру с помощью Cpuset

Из пользовательского пространства мы можем использовать 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, чтобы переместить все запущенные procceses в определенное ядро, а затем запустить мой модуль в изолированное ядро. Я получил ту же ошибку: Unknown symbol sched_setaffinity (err 0). Наверное, у меня есть "неизвестные символы", потому что эти функции не имеют EXPORT_SYMBOL в ядре. Поэтому я пошел и попытался вызвать системный вызов sys_sched_setaffinity (на основе этого question), но снова получил это сообщение: Unknown symbol sys_sched_setaffinity (err 0)!

Кроме того, я не ищу решение, которое использует isolcpus, который устанавливается при загрузке. Я хотел бы просто загрузить модуль, а потом изолировать.

  • (Точнее, я хочу, чтобы его потоки ядра выполнялись в ядрах изолированных. Я знаю, что я могу использовать аффинность для привязки потоков к конкретным ядрам, но это не гарантирует мне, что ядра идут быть изолированным другими процессами, запущенными на них.)
4b9b3361

Ответ 1

Поэтому я хочу, чтобы модуль выполнялся в изолированном ядре.

и

фактически изолировать конкретное ядро ​​в нашей системе и выполнить только один конкретный процесс для этого ядра

Это рабочий исходный код, скомпилированный и протестированный в ящике 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, является CPU, который мы хотим изолировать. Ни одна из операций, вызываемых вызываемым устройством, не будет выполняться, если они не выполняются на этом CPU.

После загрузки модуля вы можете найти его здесь

/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 выполняются на изолированном ядре.

В init get_cpu отключается preemption, т.е. все, что приходит после него, не будет выгружено ядром и будет работать на одном ядре. Обратите внимание, что это было сделано с использованием 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 Kernel Development, книга Роберта Лава.

http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/

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

Ответ 2

Вы указали в своем вопросе:

Я предполагаю, что получил "неизвестные символы", потому что эти функции не имеют EXPORT_SYMBOL в ядре

Я думаю, что это ключевой момент вашей проблемы. Я вижу, что вы включаете файл linux/cpuset.h, который определяет метод: cpuset_init среди других. Однако как во время компиляции, так и с использованием команды nm мы можем видеть индикаторы, указывающие на то, что эта функция недоступна:

Компиляция:

[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'

См. WARNING: "cupset_init" [...] undefined!. И с помощью 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

(Примечание: U означает 'undefined')

Однако, я изучал символы ядра следующим образом:

[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

Я вижу, что он экспортирован, но он недоступен в /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. Bellow - это результат, который я получил в 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 из модуля ядра? *

Возможно, это не самый элегантный способ, поскольку я не эксперт. Вам нужно продолжить здесь.

Привет

Ответ 3

Вы пытались выполнить work_struct с помощью

struct workqueue_attrs {
cpumask_var_t           cpumask;        /* allowed CPUs */
}

Прежде всего, cpu должен быть изолирован (например, cpu 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 * путь, char ** argv,/* taskset 0x00000001 helper_application */ char ** envp, gfp_t gfp_mask, int (* init) (struct subprocess_info * info, struct cred * new), void (* cleanup) (struct subprocess_info * info), void * data); Используйте вспомогательный модуль ядра, который должен запускать программу пользовательского пространства (helper_application) через набор задач, а mask должен быть от isolcpus. Модуль-помощник должен запускать только функцию __init() и возвращать -1, потому что только одна задача: запустить приложение пользовательского пространства на изолированном процессоре.

  • Вспомогательное приложение для вспомогательного пространства для пользователей должно просто: lsmod для goal_module.ko, goal_module должен начинаться с того же изолированного процессора.

  • Использовать workqueue для продолжения работы изолированного модуля на изолированном процессоре.