IOCTL_USERFAULTFD(2) Руководство программиста Linux IOCTL_USERFAULTFD(2)

ИМЯ

ioctl_userfaultfd - создаёт файловый дескриптор для обработки страничных ошибок в пользовательском пространстве

ОБЗОР

#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...);

ОПИСАНИЕ

Над объектом userfaultfd (созданным вызовом userfaultfd(2)) можно выполнять различные операции ioctl(2) используя вызовы вида:

ioctl(fd, cmd, argp);
Здесь fd — файловый дескриптор, ссылающийся на объект userfaultfd, cmd — одна из команд, перечисленных ниже, а argp — указатель на структуру данных, используемую командой cmd.

Операции ioctl(2) описаны ниже. Операции UFFDIO_API, UFFDIO_REGISTER и UFFDIO_UNREGISTER используются для настройки поведения userfaultfd. Они позволяют вызывающему выбрать какие свойства нужно включить и какие типы событий будут доставляться приложению. Остальные операции являются операциями над диапазоном. Эти операции позволяют вызывающему приложению воспринимать события страничных ошибок.

(начиная с Linux 4.3) Включить работу с userfaultfd и выполнить согласование программного интерфейса.

В аргументе argp содержится указатель на структуру uffdio_api, определённую следующим образом:

struct uffdio_api {

    __u64 api;        /* запрашиваемая версия API (входные данные) */

    __u64 features;   /* запрашиваемые свойства (входные/выходные) */

    __u64 ioctls;     /* доступные операции ioctl() (выходные данные) */
};

В поле api задаётся версия программного интерфейса, запрашиваемого приложением.

Ядро проверяет, что поддерживает запрашиваемую версию программного интерфейса и изменять поля features и ioctls в битовой маске, представляющей все доступные свойства и общие операции ioctl(2).

В ядрах Linux до версии 4.11 полю features должен быть присвоен ноль перед вызовом UFFDIO_API, и при возврате из ioctl(2) ядро помещает ноль (т. е., нет бит свойств) в поле features.

Начиная с Linux 4.11 поле features можно использовать для запроса поддержки определённых свойств и явно включить свойства userfaultfd, которые по умолчанию выключены. Ядро всегда сообщает о всех доступных свойствах через поле features.

Чтобы включить свойство userfaultfd приложение должно установить соответствующий бит в поле features. Если ядро поддерживает все запрошенные свойств, то он включит их. В противном случае оно обнулит возвращаемую структуру uffdio_api и вернёт EINVAL.

Могут устанавливаться следующие биты свойств:

Если это свойство включено, то объекты userfaultfd, связанные с родительским процессом, дублируются в дочернем процессе при вызове fork(2), а событие UFFD_EVENT_FORK доставляется отслеживающему userfaultfd.
Если это свойство включено, то при вызове mremap(2) процессом с ошибкой отслеживающий userfaultfd будет получать событие с типом UFFD_EVENT_REMAP.
Если это свойство включено, то при вызове madvise(2)со значением совета MADV_DONTNEED или MADV_REMOVE для освобождения области виртуальной памяти процессом с ошибкой отслеживающий userfaultfd будет получать событие с типом UFFD_EVENT_REMOVE.
Если это свойство включено, то при явном вызове mremap(2) или неявном вызове mmap(2) или mremap(2) процессом с ошибкой отслеживающий userfaultfd будет получать событие с типом UFFD_EVENT_UNMAP.
Если этот бит установлен, то ядро включает поддержку регистрации диапазонов userfaultfd для областей виртуальной памяти hugetlbfs.
Если этот бит установлен, то ядро включает поддержку регистрации диапазонов userfaultfd для общих областей виртуальной памяти. К ним относятся все программные интерфейсы общей памяти ядра: общая память System V, tmpfs(5), общие отображения /dev/zero, mmap(2) с флагом MAP_SHARED, memfd_create(2) и т. д.
Если этот бит установлен, то события о страничных ошибках (UFFD_EVENT_PAGEFAULT) не доставляются. Вместо них в ошибшийся процесс будет послан сигнал SIGBUS. Приложениям, использующим этой свойство, не потребуется использовать слежение за userfaultfd для обработки доступа к областям памяти, зарегистрированным в userfaultfd.

Возвращаемое поле ioctls можно содержать следующие биты:

1 << _UFFDIO_API
Поддерживается операция UFFDIO_API.
1 << _UFFDIO_REGISTER
Поддерживается операция UFFDIO_REGISTER.
1 << _UFFDIO_UNREGISTER
Поддерживается операция UFFDIO_UNREGISTER.

При успешном выполнении операции ioctl(2) возвращается 0. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Значение argp ссылается на адрес, который находится вне доступного адресного пространства вызывающего процесса.
Дескриптор userfaultfd уже включён предыдущей операцией UFFDIO_API.
Версия программного интерфейса, запрошенная в поле api, не поддерживается данным ядром, или в переданном ядру поле features установлены биты, которые не поддерживаются текущей версией ядра.

(начиная с Linux 4.3) Зарегистрировать диапазон адресов памяти в объекте userfaultfd. Страницы в диапазоне должны быть «совместимыми».

До ядра Linux 4.11 только частные анонимные диапазоны совместимы для регистрации с помощью UFFDIO_REGISTER.

Начиная с Linux 4.11 общие диапазоны памяти и hugetlbfs также совместимы с UFFDIO_REGISTER.

В аргументе argp содержится указатель на структуру uffdio_register, определённую следующим образом:

struct uffdio_range {

    __u64 start;    /* начало диапазона */

    __u64 len;      /* длина диапазона (в байтах) */
};
struct uffdio_register {

    struct uffdio_range range;

    __u64 mode;     /* желаемый режим операции (входные данные) */

    __u64 ioctls;   /* доступные операции ioctl() (результат) */
};

В поле range задаётся диапазон памяти (начинающийся с start и длиной len байт), который должен обрабатываться userfaultfd.

В поле mode задаётся режим операции на этим диапазоном памяти. У режима userfaultfd могут быть указаны следующие значения (через операцию ИЛИ) для задаваемого диапазона:

Отслеживать страничные ошибки отсутствия страниц.
Отслеживать страничные ошибки защищённых от записи страниц.

В настоящее время поддерживается только режим UFFDIO_REGISTER_MODE_MISSING.

При успешном выполнении ядро изменяет битовую маску ioctls показывая какие операции ioctl(2) доступны для задаваемого диапазона. Здесь возвращается битовая маска как для UFFDIO_API.

При успешном выполнении операции ioctl(2) возвращается 0. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Отображение в указанном диапазоне зарегистрировано в другом объекте userfaultfd.
Значение argp ссылается на адрес, который находится вне доступного адресного пространства вызывающего процесса.
В поле mode указан некорректный или неподдерживаемый бит или поле mode равно нулю.
Отображение в указанном адресном диапазоне отсутствует.
Значение range.start или range.len не кратно размеру системной страницы или значение range.len равно или эти поля содержат другие некорректные значения.
В указанном адресном диапазоне имеется несовместимое отображение.

(начиная с Linux 4.3) Снять регистрацию диапазона адресов памяти в userfaultfd. Страницы в диапазоне должны быть «совместимыми» (смотрите описание UFFDIO_REGISTER).

Снимаемый с регистрации диапазон адресов задаётся в структуре uffdio_range, которая указывается в argp.

При успешном выполнении операции ioctl(2) возвращается 0. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Поле start или len структуры ufdio_range не кратно размеру системной страницы или поле len равно нулю или эти поля содержат другие некорректные значения.
В указанном адресном диапазоне имеется несовместимое отображение.
Отображение в указанном адресном диапазоне отсутствует.

(начиная с Linux 4.3) Атомарно копировать непрерывный участок памяти в зарегистрированный в userfault диапазон и разбудить заблокированную нить (не обязательно). Адреса источника и назначения и количество копируемых байт задаётся в полях src, dst и len структуры uffdio_copy, на которую указывает argp:

struct uffdio_copy {

    __u64 dst;    /* источник копирования */

    __u64 src;    /* назначение копирования */

    __u64 len;    /* количество копируемых байт */

    __u64 mode;   /* флаги, управляющие поведением копирования */

    __s64 copy;   /* количество скопированных байт или отрицательная ошибка */
};

Для изменения поведения операции UFFDIO_COPY можно использовать следующие значения mode (побитовое ИЛИ):

Не будить нить, которая ждёт решения страничной ошибки

Поле copy используется ядром для возврата количества байт, которые были скопированы, или ошибки (отрицательное значение, подобное errno). Если значение, возвращённое в copy, не совпадает со значением, указанным в len, то операция завершается ошибкой EAGAIN. Поле copy используется только для результата; оно не читается операцией UFFDIO_COPY.

При успешном выполнении операции ioctl(2) возвращается 0. В этом случае была скопирована область целиком. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Количество скопированных байт (т. е., значение, возвращаемое в поле copy) не равно значению, указанному в поле len.
Значение dst или len не кратно размеру системной страницы или диапазон, заданный в src и len или dst и len является неправильным.
В поле mode установлен недопустимый бит.
Процесс с ошибкой изменил раскладку своей виртуальной памяти одновременно имея незавершённую операцию UFFDIO_COPY.
Процесс с ошибкой завершил работу в момент выполнения операции UFFDIO_COPY.
Процесс с ошибкой завершил работу в момент выполнения операции UFFDIO_COPY.

(начиная с Linux 4.3) Обнулить диапазон памяти, зарегистрированный в userfaultfd.

Запрашиваемый диапазон указывается в поле range структуры uffdio_zeropage, на которую указывает argp:

struct uffdio_zeropage {

    struct uffdio_range range;

    __u64 mode;     /* флаги, определяющие поведение копирования */

    __s64 zeropage; /* количество обнуляемых байт или отрицательная ошибка */
};

Для изменения поведения операции UFFDIO_ZEROPAGE можно использовать следующие значения mode (побитовое ИЛИ):

Не будить нить, которая ждёт решения страничной ошибки.

Поле zeropage используется ядром для возврата количества байт, которые были обнулены, или ошибки (также, как для UFFDIO_COPY). Если значение, возвращённое в zeropage, не совпадает со значением, указанным в range.len, то операция завершается ошибкой EAGAIN. Поле zeropage используется только для результата; оно не читается операцией UFFDIO_ZEROPAGE.

При успешном выполнении операции ioctl(2) возвращается 0. В этом случае была обнулена вся область. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Количество обнулённых байт (т. е., значение, возвращаемое в поле zeropage) не равно значению, указанному в поле range.len.
Значение range.start или range.len не кратно размеру системной страницы, значение range.len равно нулю, указанный диапазон является неправильным.
В поле mode установлен недопустимый бит.
Процесс с ошибкой завершил работу в момент выполнения операции UFFDIO_ZEROPAGE.

(начиная с Linux 4.3) Разбудить нить, которая ждёт решения страничной ошибки в указанном диапазоне адресов памяти.

Операция UFFDIO_WAKE используется вместе с UFFDIO_COPY и UFFDIO_ZEROPAGE, у которых установлен бит UFFDIO_COPY_MODE_DONTWAKE или UFFDIO_ZEROPAGE_MODE_DONTWAKE в поле mode. При отслеживании userfault можно выполнять несколько операций UFFDIO_COPY и UFFDIO_ZEROPAGE вместе и затем явно будить нить с ошибкой с помощью UFFDIO_WAKE.

В аргументе argp содержится указатель на структуру uffdio_range (показана выше), в которой задаётся диапазон адресов.

При успешном выполнении операции ioctl(2) возвращается 0. При ошибке возвращается -1 и в errno записывается причина ошибки. Возможные значения:

Поле start или len структуры ufdio_range не кратно размеру системной страницы или поле len равно нулю или указанный диапазон является неправильным.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Смотрите описание приведённое выше для каждой операции.

ОШИБКИ

Смотрите описание приведённое выше для каждой операции. Также для всех описанных выше операций могут возникать общие ошибки:

Значение argp указывает на некорректный адрес памяти.
(для все операций кроме UFFDIO_API) Объект userfaultfd пока не включён (с помощью операции UFFDIO_API).

СООТВЕТСТВИЕ СТАНДАРТАМ

Данные операции ioctl(2) есть только в Linux.

ДЕФЕКТЫ

Чтобы определить доступные свойства userfault и включить некоторые из них нужно закрыть файловый дескриптор userfaultfd после первой операции UFFDIO_API, которая запрашивает доступность свойств, и повторно открыть его перед второй операцией UFFDIO_API, которая теперь включит желаемый свойства.

ПРИМЕР

Смотрите userfaultfd(2).

СМОТРИТЕ ТАКЖЕ

ioctl(2), mmap(2), userfaultfd(2)

Файл Documentation/admin-guide/mm/userfaultfd.rst из дерева исходного кода ядра Linux

2019-03-06 Linux