|
@@ -27,20 +27,26 @@
|
|
#include <linux/ioctl.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/security.h>
|
|
#include <linux/security.h>
|
|
|
|
|
|
|
|
+static struct kmem_cache *userfaultfd_ctx_cachep __read_mostly;
|
|
|
|
+
|
|
enum userfaultfd_state {
|
|
enum userfaultfd_state {
|
|
UFFD_STATE_WAIT_API,
|
|
UFFD_STATE_WAIT_API,
|
|
UFFD_STATE_RUNNING,
|
|
UFFD_STATE_RUNNING,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Start with fault_pending_wqh and fault_wqh so they're more likely
|
|
|
|
+ * to be in the same cacheline.
|
|
|
|
+ */
|
|
struct userfaultfd_ctx {
|
|
struct userfaultfd_ctx {
|
|
- /* pseudo fd refcounting */
|
|
|
|
- atomic_t refcount;
|
|
|
|
/* waitqueue head for the pending (i.e. not read) userfaults */
|
|
/* waitqueue head for the pending (i.e. not read) userfaults */
|
|
wait_queue_head_t fault_pending_wqh;
|
|
wait_queue_head_t fault_pending_wqh;
|
|
/* waitqueue head for the userfaults */
|
|
/* waitqueue head for the userfaults */
|
|
wait_queue_head_t fault_wqh;
|
|
wait_queue_head_t fault_wqh;
|
|
/* waitqueue head for the pseudo fd to wakeup poll/read */
|
|
/* waitqueue head for the pseudo fd to wakeup poll/read */
|
|
wait_queue_head_t fd_wqh;
|
|
wait_queue_head_t fd_wqh;
|
|
|
|
+ /* pseudo fd refcounting */
|
|
|
|
+ atomic_t refcount;
|
|
/* userfaultfd syscall flags */
|
|
/* userfaultfd syscall flags */
|
|
unsigned int flags;
|
|
unsigned int flags;
|
|
/* state machine */
|
|
/* state machine */
|
|
@@ -130,7 +136,7 @@ static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx)
|
|
VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock));
|
|
VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock));
|
|
VM_BUG_ON(waitqueue_active(&ctx->fd_wqh));
|
|
VM_BUG_ON(waitqueue_active(&ctx->fd_wqh));
|
|
mmput(ctx->mm);
|
|
mmput(ctx->mm);
|
|
- kfree(ctx);
|
|
|
|
|
|
+ kmem_cache_free(userfaultfd_ctx_cachep, ctx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1028,6 +1034,15 @@ static const struct file_operations userfaultfd_fops = {
|
|
.llseek = noop_llseek,
|
|
.llseek = noop_llseek,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static void init_once_userfaultfd_ctx(void *mem)
|
|
|
|
+{
|
|
|
|
+ struct userfaultfd_ctx *ctx = (struct userfaultfd_ctx *) mem;
|
|
|
|
+
|
|
|
|
+ init_waitqueue_head(&ctx->fault_pending_wqh);
|
|
|
|
+ init_waitqueue_head(&ctx->fault_wqh);
|
|
|
|
+ init_waitqueue_head(&ctx->fd_wqh);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* userfaultfd_file_create - Creates an userfaultfd file pointer.
|
|
* userfaultfd_file_create - Creates an userfaultfd file pointer.
|
|
* @flags: Flags for the userfaultfd file.
|
|
* @flags: Flags for the userfaultfd file.
|
|
@@ -1058,14 +1073,11 @@ static struct file *userfaultfd_file_create(int flags)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
file = ERR_PTR(-ENOMEM);
|
|
file = ERR_PTR(-ENOMEM);
|
|
- ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
|
|
|
|
|
+ ctx = kmem_cache_alloc(userfaultfd_ctx_cachep, GFP_KERNEL);
|
|
if (!ctx)
|
|
if (!ctx)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
atomic_set(&ctx->refcount, 1);
|
|
atomic_set(&ctx->refcount, 1);
|
|
- init_waitqueue_head(&ctx->fault_pending_wqh);
|
|
|
|
- init_waitqueue_head(&ctx->fault_wqh);
|
|
|
|
- init_waitqueue_head(&ctx->fd_wqh);
|
|
|
|
ctx->flags = flags;
|
|
ctx->flags = flags;
|
|
ctx->state = UFFD_STATE_WAIT_API;
|
|
ctx->state = UFFD_STATE_WAIT_API;
|
|
ctx->released = false;
|
|
ctx->released = false;
|
|
@@ -1076,7 +1088,7 @@ static struct file *userfaultfd_file_create(int flags)
|
|
file = anon_inode_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
|
|
file = anon_inode_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
|
|
O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS));
|
|
O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS));
|
|
if (IS_ERR(file))
|
|
if (IS_ERR(file))
|
|
- kfree(ctx);
|
|
|
|
|
|
+ kmem_cache_free(userfaultfd_ctx_cachep, ctx);
|
|
out:
|
|
out:
|
|
return file;
|
|
return file;
|
|
}
|
|
}
|
|
@@ -1105,3 +1117,14 @@ err_put_unused_fd:
|
|
|
|
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+static int __init userfaultfd_init(void)
|
|
|
|
+{
|
|
|
|
+ userfaultfd_ctx_cachep = kmem_cache_create("userfaultfd_ctx_cache",
|
|
|
|
+ sizeof(struct userfaultfd_ctx),
|
|
|
|
+ 0,
|
|
|
|
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC,
|
|
|
|
+ init_once_userfaultfd_ctx);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+__initcall(userfaultfd_init);
|