Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

inotify: Extend ioctl to allow to request id of new watch descriptor

Watch descriptor is id of the watch created by inotify_add_watch().
It is allocated in inotify_add_to_idr(), and takes the numbers
starting from 1. Every new inotify watch obtains next available
number (usually, old + 1), as served by idr_alloc_cyclic().

CRIU (Checkpoint/Restore In Userspace) project supports inotify
files, and restores watched descriptors with the same numbers,
they had before dump. Since there was no kernel support, we
had to use cycle to add a watch with specific descriptor id:

while (1) {
int wd;

wd = inotify_add_watch(inotify_fd, path, mask);
if (wd < 0) {
break;
} else if (wd == desired_wd_id) {
ret = 0;
break;
}

inotify_rm_watch(inotify_fd, wd);
}

(You may find the actual code at the below link:
https://github.com/checkpoint-restore/criu/blob/v3.7/criu/fsnotify.c#L577)

The cycle is suboptiomal and very expensive, but since there is no better
kernel support, it was the only way to restore that. Happily, we had met
mostly descriptors with small id, and this approach had worked somehow.

But recent time containers with inotify with big watch descriptors
begun to come, and this way stopped to work at all. When descriptor id
is something about 0x34d71d6, the restoring process spins in busy loop
for a long time, and the restore hungs and delay of migration from node
to node could easily be watched.

This patch aims to solve this problem. It introduces new ioctl
INOTIFY_IOC_SETNEXTWD, which allows to request the number of next created
watch descriptor from userspace. It simply calls idr_set_cursor() primitive
to populate idr::idr_next, so that next idr_alloc_cyclic() allocation
will return this id, if it is not occupied. This is the way which is
used to restore some other resources from userspace. For example,
/proc/sys/kernel/ns_last_pid works the same for task pids.

The new code is under CONFIG_CHECKPOINT_RESTORE #define, so small system
may exclude it.

v2: Use INT_MAX instead of custom definition of max id,
as IDR subsystem guarantees id is between 0 and INT_MAX.

CC: Jan Kara <jack@suse.cz>
CC: Matthew Wilcox <willy@infradead.org>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Reviewed-by: Cyrill Gorcunov <gorcunov@openvz.org>
Reviewed-by: Matthew Wilcox <mawilcox@microsoft.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Kirill Tkhai and committed by
Jan Kara
e1603b6e 61f14c01

+22
+14
fs/notify/inotify/inotify_user.c
··· 307 307 spin_unlock(&group->notification_lock); 308 308 ret = put_user(send_len, (int __user *) p); 309 309 break; 310 + #ifdef CONFIG_CHECKPOINT_RESTORE 311 + case INOTIFY_IOC_SETNEXTWD: 312 + ret = -EINVAL; 313 + if (arg >= 1 && arg <= INT_MAX) { 314 + struct inotify_group_private_data *data; 315 + 316 + data = &group->inotify_data; 317 + spin_lock(&data->idr_lock); 318 + idr_set_cursor(&data->idr, (unsigned int)arg); 319 + spin_unlock(&data->idr_lock); 320 + ret = 0; 321 + } 322 + break; 323 + #endif /* CONFIG_CHECKPOINT_RESTORE */ 310 324 } 311 325 312 326 return ret;
+8
include/uapi/linux/inotify.h
··· 71 71 #define IN_CLOEXEC O_CLOEXEC 72 72 #define IN_NONBLOCK O_NONBLOCK 73 73 74 + /* 75 + * ioctl numbers: inotify uses 'I' prefix for all ioctls, 76 + * except historical FIONREAD, which is based on 'T'. 77 + * 78 + * INOTIFY_IOC_SETNEXTWD: set desired number of next created 79 + * watch descriptor. 80 + */ 81 + #define INOTIFY_IOC_SETNEXTWD _IOW('I', 0, __s32) 74 82 75 83 #endif /* _UAPI_LINUX_INOTIFY_H */