[RFC PATCH 11/13] mm/vas: Introduce VAS segments - shareable address space regions

Till Smejkal
2017-03-13T22:14:13Z

VAS segments are an extension to first class virtual address spaces that
can be used to share specific memory regions between multiple first class
virtual address spaces. VAS segments have a specific size and position in a
virtual address space and can thereby be used to share in-memory pointer
based data structures between multiple address spaces as well as other
in-memory data without the need to represent them in mmap-able files or
use shmem.

Similar to first class virtual address spaces, VAS segments must be created
and destroyed explicitly by a user. The system will never automatically
destroy or create a virtual segment. Via attaching a VAS segment to a first
class virtual address space, the memory that is contained in the VAS
segment can be accessed and changed.

Signed-off-by: Till Smejkal <till.smejkal@gmail.com>
Signed-off-by: Marco Benatto <marco.antonio.780@gmail.com>
---
arch/x86/entry/syscalls/syscall_32.tbl | 7 +
arch/x86/entry/syscalls/syscall_64.tbl | 7 +
include/linux/syscalls.h | 10 +
include/linux/vas.h | 114 +++
include/linux/vas_types.h | 91 ++-
include/uapi/asm-generic/unistd.h | 16 +-
include/uapi/linux/vas.h | 12 +
kernel/sys_ni.c | 7 +
mm/vas.c | 1234 ++++++++++++++++++++++++++++++--
9 files changed, 1451 insertions(+), 47 deletions(-)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 8c553eef8c44..a4f91d14a856 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -398,3 +398,10 @@
389 i386 active_vas sys_active_vas
390 i386 vas_getattr sys_vas_getattr
391 i386 vas_setattr sys_vas_setattr
+392 i386 vas_seg_create sys_vas_seg_create
+393 i386 vas_seg_delete sys_vas_seg_delete
+394 i386 vas_seg_find sys_vas_seg_find
+395 i386 vas_seg_attach sys_vas_seg_attach
+396 i386 vas_seg_detach sys_vas_seg_detach
+397 i386 vas_seg_getattr sys_vas_seg_getattr
+398 i386 vas_seg_setattr sys_vas_seg_setattr
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 72f1f0495710..a0f9503c3d28 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -347,6 +347,13 @@
338 common active_vas sys_active_vas
339 common vas_getattr sys_vas_getattr
340 common vas_setattr sys_vas_setattr
+341 common vas_seg_create sys_vas_seg_create
+342 common vas_seg_delete sys_vas_seg_delete
+343 common vas_seg_find sys_vas_seg_find
+344 common vas_seg_attach sys_vas_seg_attach
+345 common vas_seg_detach sys_vas_seg_detach
+346 common vas_seg_getattr sys_vas_seg_getattr
+347 common vas_seg_setattr sys_vas_seg_setattr

#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index fdea27d37c96..7380dcdc4bc1 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -66,6 +66,7 @@ struct perf_event_attr;
struct file_handle;
struct sigaltstack;
struct vas_attr;
+struct vas_seg_attr;
union bpf_attr;

#include <linux/types.h>
@@ -914,4 +915,13 @@ asmlinkage long sys_active_vas(void);
asmlinkage long sys_vas_getattr(int vid, struct vas_attr __user *attr);
asmlinkage long sys_vas_setattr(int vid, struct vas_attr __user *attr);

+asmlinkage long sys_vas_seg_create(const char __user *name, unsigned long start,
+ unsigned long end, umode_t mode);
+asmlinkage long sys_vas_seg_delete(int sid);
+asmlinkage long sys_vas_seg_find(const char __user *name);
+asmlinkage long sys_vas_seg_attach(int vid, int sid, int type);
+asmlinkage long sys_vas_seg_detach(int vid, int sid);
+asmlinkage long sys_vas_seg_getattr(int sid, struct vas_seg_attr __user *attr);
+asmlinkage long sys_vas_seg_setattr(int sid, struct vas_seg_attr __user *attr);
+
#endif
diff --git a/include/linux/vas.h b/include/linux/vas.h
index 6a72e42f96d2..376b9fa1ee27 100644
--- a/include/linux/vas.h
+++ b/include/linux/vas.h
@@ -138,6 +138,120 @@ extern int vas_setattr(int vid, struct vas_attr *attr);

/***
+ * Management of VAS segments
+ ***/
+
+/**
+ * Lock and unlock helper for VAS segments.
+ **/
+#define vas_seg_lock(seg) mutex_lock(&(seg)->mtx)
+#define vas_seg_unlock(seg) mutex_unlock(&(seg)->mtx)
+
+/**
+ * Create a new VAS segment.
+ *
+ * @param[in] name: The name of the new VAS segment.
+ * @param[in] start: The address where the VAS segment begins.
+ * @param[in] end: The address where the VAS segment ends.
+ * @param[in] mode: The access rights for the VAS segment.
+ *
+ * @returns: The VAS segment ID on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_create(const char *name, unsigned long start,
+ unsigned long end, umode_t mode);
+
+/**
+ * Get a pointer to a VAS segment data structure.
+ *
+ * @param[in] sid: The ID of the VAS segment whose data structure
+ * should be returned.
+ *
+ * @returns: The pointer to the VAS segment data structure
+ * on success, or NULL otherwise.
+ **/
+extern struct vas_seg *vas_seg_get(int sid);
+
+/**
+ * Return a pointer to a VAS segment data structure again.
+ *
+ * @param[in] seg: The pointer to the VAS segment data structure
+ * that should be returned.
+ **/
+extern void vas_seg_put(struct vas_seg *seg);
+
+/**
+ * Get ID of the VAS segment belonging to a given name.
+ *
+ * @param[in] name: The name of the VAS segment for which the ID
+ * should be returned.
+ *
+ * @returns: The VAS segment ID on success, -ERRNO
+ * otherwise.
+ **/
+extern int vas_seg_find(const char *name);
+
+/**
+ * Delete the given VAS segment again.
+ *
+ * @param[in] id: The ID of the VAS segment which should be
+ * deleted.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_delete(int id);
+
+/**
+ * Attach a VAS segment to a VAS.
+ *
+ * @param[in] vid: The ID of the VAS to which the VAS segment
+ * should be attached.
+ * @param[in] sid: The ID of the VAS segment which should be
+ * attached.
+ * @param[in] type: The type how the VAS segment should be
+ * attached.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_attach(int vid, int sid, int type);
+
+/**
+ * Detach a VAS segment from a VAS.
+ *
+ * @param[in] vid: The ID of the VAS from which the VAS segment
+ * should be detached.
+ * @param[in] sid: The ID of the VAS segment which should be
+ * detached.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_detach(int vid, int sid);
+
+/**
+ * Get attributes of a VAS segment.
+ *
+ * @param[in] sid: The ID of the VAS segment for which the
+ * attributes should be returned.
+ * @param[out] attr: The pointer to the struct where the attributes
+ * should be saved.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_getattr(int sid, struct vas_seg_attr *attr);
+
+/**
+ * Set attributes of a VAS segment.
+ *
+ * @param[in] sid: The ID of the VAS segment for which the
+ * attributes should be updated.
+ * @param[in] attr: The pointer to the struct containing the new
+ * attributes.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+extern int vas_seg_setattr(int sid, struct vas_seg_attr *attr);
+
+
+/***
* Management of the VAS subsystem
***/

diff --git a/include/linux/vas_types.h b/include/linux/vas_types.h
index f06bfa9ef729..a5291a18ea07 100644
--- a/include/linux/vas_types.h
+++ b/include/linux/vas_types.h
@@ -24,8 +24,8 @@ struct task_struct;
* The struct representing a Virtual Address Space (VAS).
*
* This data structure contains all the necessary information of a VAS such as
- * its name, ID. It also contains access rights and other management
- * information.
+ * its name, ID, as well as the list of all the VAS segments which are attached
+ * to it. It also contains access rights and other management information.
**/
struct vas {
struct kobject kobj; /* < the internal kobject that we use *
@@ -38,7 +38,8 @@ struct vas {
struct mutex mtx; /* < lock for parallel access. */

struct mm_struct *mm; /* < a partial memory map containing *
- * all mappings of this VAS. */
+ * all mappings of this VAS and all *
+ * of its attached VAS segments. */

struct list_head link; /* < the link in the global VAS list. */
struct rcu_head rcu; /* < the RCU helper used for *
@@ -54,6 +55,11 @@ struct vas {
* of the current sharing state of *
* the VAS. */

+ struct list_head segments; /* < the list of attached VAS *
+ * segments. */
+ u32 nr_segments; /* < the number of VAS segments *
+ * attached to this VAS. */
+
umode_t mode; /* < the access rights to this VAS. */
kuid_t uid; /* < the UID of the owning user of *
* this VAS. */
@@ -85,4 +91,83 @@ struct att_vas {
int type; /* < the type of attaching (RO/RW). */
};

+/**
+ * The struct representing a VAS segment.
+ *
+ * A VAS segment is a region in memory. Accordingly, it is very similar to a
+ * vm_area. However, instead of a vm_area it can only represent a memory region
+ * and not a file and also knows where it is mapped. In addition VAS segments
+ * also have an ID, a name, access rights and a lock managing the way it can be
+ * shared between multiple VAS.
+ **/
+struct vas_seg {
+ struct kobject kobj; /* < the internal kobject that we use *
+ * for reference counting and sysfs *
+ * handling. */
+
+ int id; /* < ID */
+ char name[VAS_MAX_NAME_LENGTH]; /* < name */
+
+ struct mutex mtx; /* < lock for parallel access. */
+
+ unsigned long start; /* < the virtual address where the *
+ * VAS segment starts. */
+ unsigned long end; /* < the virtual address where the *
+ * VAS segment ends. */
+ unsigned long length; /* < the size of the VAS segment in *
+ * bytes. */
+
+ struct mm_struct *mm; /* < a partial memory map containing *
+ * all the mappings for this VAS *
+ * segment. */
+
+ struct list_head link; /* < the link in the global VAS *
+ * segment list. */
+ struct rcu_head rcu; /* < the RCU helper used for *
+ * asynchronous VAS segment *
+ * deletion. */
+
+ u16 refcount; /* < how often is the VAS segment *
+ * attached. */
+ struct list_head attaches; /* < the list of VASes which have *
+ * this VAS segment attached. */
+
+ spinlock_t share_lock; /* < lock for protecting sharing *
+ * state. */
+ u32 sharing; /* < the variable used to keep track *
+ * of the current sharing state of *
+ * the VAS segment. */
+
+ umode_t mode; /* < the access rights to this VAS *
+ * segment. */
+ kuid_t uid; /* < the UID of the owning user of *
+ * this VAS segment. */
+ kgid_t gid; /* < the GID of the owning group of *
+ * this VAS segment. */
+};
+
+/**
+ * The struct representing a VAS segment being attached to a VAS.
+ *
+ * Since a VAS segment can be attached to a multiple VAS this data structure is
+ * necessary. It forms the connection between the VAS and the VAS segment
+ * itself.
+ **/
+struct att_vas_seg {
+ struct vas_seg *seg; /* < the reference to the actual VAS *
+ * segment containing all the *
+ * information. */
+
+ struct vas *vas; /* < the reference to the VAS to *
+ * which the VAS segment is *
+ * attached to. */
+
+ struct list_head vas_link; /* < the link in the list managed *
+ * inside the VAS. */
+ struct list_head seg_link; /* < the link in the list managed *
+ * inside the VAS segment. */
+
+ int type; /* < the type of attaching (RO/RW). */
+};
+
#endif
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 35df7d40a443..4014b4bd2f18 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -748,9 +748,23 @@ __SYSCALL(__NR_active_vas, sys_active_vas)
__SYSCALL(__NR_vas_getattr, sys_vas_getattr)
#define __NR_vas_setattr 299
__SYSCALL(__NR_vas_setattr, sys_vas_setattr)
+#define __NR_vas_seg_create 300
+__SYSCALL(__NR_vas_seg_create, sys_vas_seg_create)
+#define __NR_vas_seg_delete 301
+__SYSCALL(__NR_vas_seg_delete, sys_vas_seg_delete)
+#define __NR_vas_seg_find 302
+__SYSCALL(__NR_vas_seg_find, sys_vas_seg_find)
+#define __NR_vas_seg_attach 303
+__SYSCALL(__NR_vas_seg_attach, sys_vas_seg_attach)
+#define __NR_vas_seg_detach 304
+__SYSCALL(__NR_vas_seg_detach, sys_vas_seg_detach)
+#define __NR_vas_seg_getattr 305
+__SYSCALL(__NR_vas_seg_getattr, sys_vas_seg_getattr)
+#define __NR_vas_seg_setattr 306
+__SYSCALL(__NR_vas_seg_setattr, sys_vas_seg_setattr)

#undef __NR_syscalls
-#define __NR_syscalls 300
+#define __NR_syscalls 307

/*
* All syscalls below here should go away really,
diff --git a/include/uapi/linux/vas.h b/include/uapi/linux/vas.h
index 02f70f88bdcb..a8858b013a44 100644
--- a/include/uapi/linux/vas.h
+++ b/include/uapi/linux/vas.h
@@ -13,4 +13,16 @@ struct vas_attr {
__kernel_gid_t group; /* < the owning group of the VAS. */
};

+/**
+ * The struct containing attributes of a VAS segment.
+ **/
+struct vas_seg_attr {
+ __kernel_mode_t mode; /* < the access rights to the VAS *
+ * segment. */
+ __kernel_uid_t user; /* < the owning user of the VAS *
+ * segment. */
+ __kernel_gid_t group; /* < the owning group of the VAS *
+ * segment. */
+};
+
#endif
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index f6f83c5ec1a1..659fe96afcfa 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -269,3 +269,10 @@ cond_syscall(sys_vas_switch);
cond_syscall(sys_active_vas);
cond_syscall(sys_vas_getattr);
cond_syscall(sys_vas_setattr);
+cond_syscall(sys_segment_create);
+cond_syscall(sys_segment_delete);
+cond_syscall(sys_segment_find);
+cond_syscall(sys_segment_attach);
+cond_syscall(sys_segment_detach);
+cond_syscall(sys_segment_getattr);
+cond_syscall(sys_segment_setattr);
diff --git a/mm/vas.c b/mm/vas.c
index 447d61e1da79..345b023c21aa 100644
--- a/mm/vas.c
+++ b/mm/vas.c
@@ -61,7 +61,7 @@
#define VAS_MAX_ID INT_MAX

/**
- * Masks and bits to implement sharing of VAS.
+ * Masks and bits to implement sharing of VAS and VAS segments.
**/
#define VAS_SHARE_READABLE (1 << 0)
#define VAS_SHARE_WRITABLE (1 << 16)
@@ -194,6 +194,8 @@ static void __dump_memory_map(const char *title, struct mm_struct *mm)
static struct kmem_cache *vas_cachep;
static struct kmem_cache *att_vas_cachep;
static struct kmem_cache *vas_context_cachep;
+static struct kmem_cache *seg_cachep;
+static struct kmem_cache *att_seg_cachep;

/**
* Global management data structures and their associated locks.
@@ -201,16 +203,21 @@ static struct kmem_cache *vas_context_cachep;
static struct idr vases;
static spinlock_t vases_lock;

+static struct idr vas_segs;
+static spinlock_t vas_segs_lock;
+
/**
* The place holder variables that are used to identify to-be-deleted items in
* our global management data structures.
**/
static struct vas *INVALID_VAS;
+static struct vas_seg *INVALID_VAS_SEG;

/**
* Kernel 'ksets' where all objects will be managed.
**/
static struct kset *vases_kset;
+static struct kset *vas_segs_kset;

/***
@@ -273,6 +280,40 @@ static inline void __delete_vas_context(struct vas_context *ctx)
kmem_cache_free(vas_context_cachep, ctx);
}

+static inline struct vas_seg *__new_vas_seg(void)
+{
+ return kmem_cache_zalloc(seg_cachep, GFP_KERNEL);
+}
+
+static inline void __delete_vas_seg(struct vas_seg *seg)
+{
+ WARN_ON(seg->refcount != 0);
+
+ mutex_destroy(&seg->mtx);
+
+ if (seg->mm)
+ mmput_async(seg->mm);
+ kmem_cache_free(seg_cachep, seg);
+}
+
+static inline void __delete_vas_seg_rcu(struct rcu_head *rp)
+{
+ struct vas_seg *seg = container_of(rp, struct vas_seg, rcu);
+
+ __delete_vas_seg(seg);
+}
+
+static inline struct att_vas_seg *__new_att_vas_seg(void)
+{
+ return kmem_cache_zalloc(att_seg_cachep, GFP_ATOMIC);
+}
+
+static inline void __delete_att_vas_seg(struct att_vas_seg *aseg)
+{
+ kmem_cache_free(att_seg_cachep, aseg);
+}
+
+
/***
* Kobject management of data structures
***/
@@ -418,6 +459,161 @@ static struct kobj_type vas_ktype = {
.default_attrs = vas_default_attr,
};

+/**
+ * Correctly get and put VAS segments.
+ **/
+static inline struct vas_seg *__vas_seg_get(struct vas_seg *seg)
+{
+ return container_of(kobject_get(&seg->kobj), struct vas_seg, kobj);
+}
+
+static inline void __vas_seg_put(struct vas_seg *seg)
+{
+ kobject_put(&seg->kobj);
+}
+
+/**
+ * The sysfs structure we need to handle attributes of a VAS segment.
+ **/
+struct vas_seg_sysfs_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct vas_seg *seg, struct vas_seg_sysfs_attr *ssattr,
+ char *buf);
+ ssize_t (*store)(struct vas_seg *seg, struct vas_seg_sysfs_attr *ssattr,
+ const char *buf, ssize_t count);
+};
+
+#define VAS_SEG_SYSFS_ATTR(NAME, MODE, SHOW, STORE) \
+static struct vas_seg_sysfs_attr vas_seg_sysfs_attr_##NAME = \
+ __ATTR(NAME, MODE, SHOW, STORE)
+
+/**
+ * Functions for all the sysfs operations for VAS segments.
+ **/
+static ssize_t __vas_seg_sysfs_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct vas_seg *seg;
+ struct vas_seg_sysfs_attr *ssattr;
+
+ seg = container_of(kobj, struct vas_seg, kobj);
+ ssattr = container_of(attr, struct vas_seg_sysfs_attr, attr);
+
+ if (!ssattr->show)
+ return -EIO;
+
+ return ssattr->show(seg, ssattr, buf);
+}
+
+static ssize_t __vas_seg_sysfs_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vas_seg *seg;
+ struct vas_seg_sysfs_attr *ssattr;
+
+ seg = container_of(kobj, struct vas_seg, kobj);
+ ssattr = container_of(attr, struct vas_seg_sysfs_attr, attr);
+
+ if (!ssattr->store)
+ return -EIO;
+
+ return ssattr->store(seg, ssattr, buf, count);
+}
+
+/**
+ * The sysfs operations structure for a VAS segment.
+ **/
+static const struct sysfs_ops vas_seg_sysfs_ops = {
+ .show = __vas_seg_sysfs_attr_show,
+ .store = __vas_seg_sysfs_attr_store,
+};
+
+/**
+ * Default attributes of a VAS segment.
+ **/
+static ssize_t __show_vas_seg_name(struct vas_seg *seg,
+ struct vas_seg_sysfs_attr *ssattr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%s", seg->name);
+}
+VAS_SEG_SYSFS_ATTR(name, 0444, __show_vas_seg_name, NULL);
+
+static ssize_t __show_vas_seg_mode(struct vas_seg *seg,
+ struct vas_seg_sysfs_attr *ssattr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%#03o", seg->mode);
+}
+VAS_SEG_SYSFS_ATTR(mode, 0444, __show_vas_seg_mode, NULL);
+
+static ssize_t __show_vas_seg_user(struct vas_seg *seg,
+ struct vas_seg_sysfs_attr *ssattr,
+ char *buf)
+{
+ struct user_namespace *ns = current_user_ns();
+
+ return scnprintf(buf, PAGE_SIZE, "%d", from_kuid(ns, seg->uid));
+}
+VAS_SEG_SYSFS_ATTR(user, 0444, __show_vas_seg_user, NULL);
+
+static ssize_t __show_vas_seg_group(struct vas_seg *seg,
+ struct vas_seg_sysfs_attr *ssattr,
+ char *buf)
+{
+ struct user_namespace *ns = current_user_ns();
+
+ return scnprintf(buf, PAGE_SIZE, "%d", from_kgid(ns, seg->gid));
+}
+VAS_SEG_SYSFS_ATTR(group, 0444, __show_vas_seg_group, NULL);
+
+static ssize_t __show_vas_seg_region(struct vas_seg *seg,
+ struct vas_seg_sysfs_attr *ssattr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%lx-%lx", seg->start, seg->end);
+}
+VAS_SEG_SYSFS_ATTR(region, 0444, __show_vas_seg_region, NULL);
+
+static struct attribute *vas_seg_default_attr[] = {
+ &vas_seg_sysfs_attr_name.attr,
+ &vas_seg_sysfs_attr_mode.attr,
+ &vas_seg_sysfs_attr_user.attr,
+ &vas_seg_sysfs_attr_group.attr,
+ &vas_seg_sysfs_attr_region.attr,
+ NULL
+};
+
+/**
+ * Function to release the VAS segment after its kobject is gone.
+ **/
+static void __vas_seg_release(struct kobject *kobj)
+{
+ struct vas_seg *seg = container_of(kobj, struct vas_seg, kobj);
+
+ /* Give up the ID in the IDR that was occupied by this VAS segment. */
+ spin_lock(&vas_segs_lock);
+ idr_remove(&vas_segs, seg->id);
+ spin_unlock(&vas_segs_lock);
+
+ /*
+ * Wait a full RCU grace period before actually deleting the VAS segment
+ * data structure since we haven't done it earlier.
+ */
+ call_rcu(&seg->rcu, __delete_vas_seg_rcu);
+}
+
+/**
+ * The ktype data structure representing a VAS segment.
+ **/
+static struct kobj_type vas_seg_ktype = {
+ .sysfs_ops = &vas_seg_sysfs_ops,
+ .release = __vas_seg_release,
+ .default_attrs = vas_seg_default_attr,
+};
+

/***
* Internally visible functions
@@ -526,8 +722,99 @@ static inline struct vas *vas_lookup_by_name(const char *name)
return vas;
}

+/**
+ * Working with the global VAS segments list.
+ **/
+static inline void vas_seg_remove(struct vas_seg *seg)
+{
+ spin_lock(&vas_segs_lock);
+
+ /*
+ * We only put a to-be-deleted place holder in the IDR at this point.
+ * See @vas_remove for more details.
+ */
+ idr_replace(&vas_segs, INVALID_VAS_SEG, seg->id);
+ spin_unlock(&vas_segs_lock);
+
+ /* No need to wait for grace period. See @vas_remove why. */
+ __vas_seg_put(seg);
+}
+
+static inline int vas_seg_insert(struct vas_seg *seg)
+{
+ int ret;
+
+ /* Add the VAS segment in the IDR cache. */
+ spin_lock(&vas_segs_lock);
+
+ ret = idr_alloc(&vas_segs, seg, 1, VAS_MAX_ID, GFP_KERNEL);
+
+ spin_unlock(&vas_segs_lock);
+
+ if (ret < 0) {
+ __delete_vas_seg(seg);
+ return ret;
+ }
+
+ /* Add the remaining data to the VAS segment's data structure. */
+ seg->id = ret;
+ seg->kobj.kset = vas_segs_kset;
+
+ /* Initialize the kobject and add it to the sysfs. */
+ ret = kobject_init_and_add(&seg->kobj, &vas_seg_ktype, NULL,
+ "%d", seg->id);
+ if (ret != 0) {
+ vas_seg_remove(seg);
+ return ret;
+ }
+
+ kobject_uevent(&seg->kobj, KOBJ_ADD);
+
+ return 0;
+}
+
+static inline struct vas_seg *vas_seg_lookup(int id)
+{
+ struct vas_seg *seg;
+
+ rcu_read_lock();
+
+ seg = idr_find(&vas_segs, id);
+ if (seg == INVALID_VAS_SEG)
+ seg = NULL;
+ if (seg)
+ seg = __vas_seg_get(seg);
+
+ rcu_read_unlock();
+
+ return seg;
+}
+
+static inline struct vas_seg *vas_seg_lookup_by_name(const char *name)
+{
+ struct vas_seg *seg;
+ int id;
+
+ rcu_read_lock();
+
+ idr_for_each_entry(&vas_segs, seg, id) {
+ if (seg == INVALID_VAS_SEG)
+ continue;
+
+ if (strcmp(seg->name, name) == 0)
+ break;
+ }
+
+ if (seg)
+ seg = __vas_seg_get(seg);
+
+ rcu_read_unlock();
+
+ return seg;
+}
+
/**
- * Management of the sharing of VAS.
+ * Management of the sharing of VAS and VAS segments.
**/
static inline int vas_take_share(int type, struct vas *vas)
{
@@ -562,6 +849,39 @@ static inline void vas_put_share(int type, struct vas *vas)
spin_unlock(&vas->share_lock);
}

+static inline int vas_seg_take_share(int type, struct vas_seg *seg)
+{
+ int ret;
+
+ spin_lock(&seg->share_lock);
+ if (type & MAY_WRITE) {
+ if ((seg->sharing & VAS_SHARE_READ_WRITE_MASK) == 0) {
+ seg->sharing += VAS_SHARE_WRITABLE;
+ ret = 1;
+ } else
+ ret = 0;
+ } else {
+ if ((seg->sharing & VAS_SHARE_WRITE_MASK) == 0) {
+ seg->sharing += VAS_SHARE_READABLE;
+ ret = 1;
+ } else
+ ret = 0;
+ }
+ spin_unlock(&seg->share_lock);
+
+ return ret;
+}
+
+static inline void vas_seg_put_share(int type, struct vas_seg *seg)
+{
+ spin_lock(&seg->share_lock);
+ if (type & MAY_WRITE)
+ seg->sharing -= VAS_SHARE_WRITABLE;
+ else
+ seg->sharing -= VAS_SHARE_READABLE;
+ spin_unlock(&seg->share_lock);
+}
+
/**
* Management of the memory maps.
**/
@@ -609,6 +929,59 @@ static int init_att_vas_mm(struct att_vas *avas, struct task_struct *owner)
return 0;
}

+static int init_vas_seg_mm(struct vas_seg *seg)
+{
+ struct mm_struct *mm;
+ unsigned long map_flags, page_prot_flags;
+ vm_flags_t vm_flags;
+ unsigned long map_addr;
+ int ret;
+
+ mm = mm_alloc();
+ if (!mm)
+ return -ENOMEM;
+
+ mm = mm_setup(mm);
+ if (!mm)
+ return -ENOMEM;
+
+ arch_pick_mmap_layout(mm);
+
+ map_flags = MAP_ANONYMOUS | MAP_FIXED;
+ page_prot_flags = PROT_READ | PROT_WRITE;
+ vm_flags = calc_vm_prot_bits(page_prot_flags, 0) |
+ calc_vm_flag_bits(map_flags) | mm->def_flags |
+ VM_DONTEXPAND | VM_DONTCOPY;
+
+ /* Find the possible mapping address for the VAS segment. */
+ map_addr = get_unmapped_area(mm, NULL, seg->start, seg->length,
+ 0, map_flags);
+ if (map_addr != seg->start) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ /* Insert the mapping into the mm_struct of the VAS segment. */
+ map_addr = mmap_region(mm, NULL, seg->start, seg->length,
+ vm_flags, 0);
+ if (map_addr != seg->start) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ /* Populate the VAS segments memory region. */
+ mm_populate(mm, seg->start, seg->length);
+
+ /* The mm_struct is properly setup. We are done here. */
+ seg->mm = mm;
+
+ return 0;
+
+out_free:
+ mmput(mm);
+ return ret;
+}
+
/**
* Lookup the corresponding vm_area in the referenced memory map.
*
@@ -1126,61 +1499,200 @@ static int task_unmerge(struct att_vas *avas, struct task_struct *tsk)
}

/**
- * Attach a VAS to a task -- update internal information ONLY
+ * Merge a VAS segment's memory map into a VAS memory map.
*
- * Requires that the VAS is already locked.
+ * Requires that the VAS and the VAS segment is already locked.
*
- * @param[in] avas: The pointer to the attached-VAS data structure
- * containing all the information of this attaching.
- * @param[in] tsk: The pointer to the task to which the VAS should be
- * attached.
- * @param[in] vas: The pointer to the VAS which should be attached.
+ * @param[in] vas: The pointer to the VAS into which the VAS segment should
+ * be merged.
+ * @param[in] seg: The pointer to the VAS segment that should be merged.
+ * @param[in] type: The type of attaching (see attach_segment for more
+ * information).
*
- * @returns: 0 on succes, -ERRNO otherwise.
+ * @returns: 0 on success, -ERRNO otherwise.
**/
-static int __vas_attach(struct att_vas *avas, struct task_struct *tsk,
- struct vas *vas)
+static int vas_seg_merge(struct vas *vas, struct vas_seg *seg, int type)
{
+ struct vm_area_struct *vma, *new_vma;
+ struct mm_struct *vas_mm, *seg_mm;
int ret;

- /* Before doing anything, synchronize the RSS-stat of the task. */
- sync_mm_rss(tsk->mm);
+ vas_mm = vas->mm;
+ seg_mm = seg->mm;

- /*
- * Try to acquire the VAS share with the proper type. This will ensure
- * that the different sharing possibilities of VAS are respected.
- */
- if (!vas_take_share(avas->type, vas)) {
- pr_vas_debug("VAS is already attached exclusively\n");
- return -EBUSY;
- }
+ dump_memory_map("Before VAS MM", vas_mm);
+ dump_memory_map("Before VAS segment MM", seg_mm);

- ret = vas_merge(avas, vas, avas->type);
- if (ret != 0)
- goto out_put_share;
+ if (down_write_killable(&vas_mm->mmap_sem))
+ return -EINTR;
+ down_read_nested(&seg_mm->mmap_sem, SINGLE_DEPTH_NESTING);

- ret = task_merge(avas, tsk);
- if (ret != 0)
- goto out_put_share;
+ /* Try to copy all VMAs of the VAS into the AS of the attached-VAS. */
+ for (vma = seg_mm->mmap; vma; vma = vma->vm_next) {
+ unsigned long merged_vm_flags = vma->vm_flags;

- vas->refcount++;
+ pr_vas_debug("Merging a VAS segment memory region (%#lx - %#lx)\n",
+ vma->vm_start, vma->vm_end);

- return 0;
+ /*
+ * Remove the writable bit from the vm_flags if the VAS segment
+ * is attached only readable.
+ */
+ if (!(type & MAY_WRITE))
+ merged_vm_flags &= ~(VM_WRITE | VM_MAYWRITE);

-out_put_share:
- vas_put_share(avas->type, vas);
- return ret;
-}
+ new_vma = __copy_vm_area(seg_mm, vma, vas_mm, merged_vm_flags);
+ if (!new_vma) {
+ pr_vas_debug("Failed to merge a VAS segment memory region (%#lx - %#lx)\n",
+ vma->vm_start, vma->vm_end);
+ ret = -EFAULT;
+ goto out_unlock;
+ }

-/**
- * Detach a VAS from a task -- update internal information ONLY
- *
- * Requires that the VAS is already locked.
- *
- * @param[in] avas: The pointer to the attached-VAS data structure
- * containing all the information of this attaching.
- * @param[in] tsk: The pointer to the task from which the VAS should be
- * detached.
+ /*
+ * Remember for the VMA that we just added it to the VAS that it
+ * actually belongs to the VAS segment.
+ */
+ new_vma->vas_reference = seg_mm;
+ }
+
+ ret = 0;
+
+out_unlock:
+ up_read(&seg_mm->mmap_sem);
+ up_write(&vas_mm->mmap_sem);
+
+ dump_memory_map("After VAS MM", vas_mm);
+ dump_memory_map("After VAS segment MM", seg_mm);
+
+ return ret;
+}
+
+/**
+ * Unmerge the VAS segment-related parts of a VAS' memory map back into the
+ * VAS segment's memory map.
+ *
+ * Requires that the VAS and the VAS segment are already locked.
+ *
+ * @param[in] vas: The pointer to the VAS from which the VAS segment
+ * related data should be taken.
+ * @param[in] seg: The pointer to the VAS segment for which the memory map
+ * should be updated again.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+static int vas_seg_unmerge(struct vas *vas, struct vas_seg *seg)
+{
+ struct vm_area_struct *vma, *next;
+ struct mm_struct *vas_mm, *seg_mm;
+ int ret;
+
+ vas_mm = vas->mm;
+ seg_mm = seg->mm;
+
+ dump_memory_map("Before VAS MM", vas_mm);
+ dump_memory_map("Before VAS segment MM", seg_mm);
+
+ if (down_write_killable(&vas_mm->mmap_sem))
+ return -EINTR;
+ down_write_nested(&seg_mm->mmap_sem, SINGLE_DEPTH_NESTING);
+
+ /* Update all memory regions which belonged to the VAS segment. */
+ for (vma = vas_mm->mmap, next = next_vma_safe(vma); vma;
+ vma = next, next = next_vma_safe(next)) {
+ struct mm_struct *ref_mm = vma->vas_reference;
+
+ if (ref_mm != seg_mm) {
+ pr_vas_debug("Skipping memory region (%#lx - %#lx) during VAS segment unmerging\n",
+ vma->vm_start, vma->vm_end);
+ continue;
+ } else {
+ struct vm_area_struct *upd_vma;
+
+ pr_vas_debug("Unmerging a VAS segment memory region (%#lx - %#lx)\n",
+ vma->vm_start, vma->vm_end);
+
+ upd_vma = __update_vm_area(vas_mm, vma, seg_mm, NULL);
+ if (!upd_vma) {
+ pr_vas_debug("Failed to unmerge a VAS segment memory region (%#lx - %#lx)\n",
+ vma->vm_start, vma->vm_end);
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+ }
+
+ /* Remove the current VMA from the VAS memory map. */
+ __remove_vm_area(vas_mm, vma);
+ }
+
+ ret = 0;
+
+out_unlock:
+ up_write(&seg_mm->mmap_sem);
+ up_write(&vas_mm->mmap_sem);
+
+ dump_memory_map("After VAS MM", vas_mm);
+ dump_memory_map("After VAS segment MM", seg_mm);
+
+ return ret;
+}
+
+/**
+ * Attach a VAS to a task -- update internal information ONLY
+ *
+ * Requires that the VAS is already locked.
+ *
+ * @param[in] avas: The pointer to the attached-VAS data structure
+ * containing all the information of this attaching.
+ * @param[in] tsk: The pointer to the task to which the VAS should be
+ * attached.
+ * @param[in] vas: The pointer to the VAS which should be attached.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+static int __vas_attach(struct att_vas *avas, struct task_struct *tsk,
+ struct vas *vas)
+{
+ int ret;
+
+ /* Before doing anything, synchronize the RSS-stat of the task. */
+ sync_mm_rss(tsk->mm);
+
+ /*
+ * Try to acquire the VAS share with the proper type. This will ensure
+ * that the different sharing possibilities of VAS are respected.
+ */
+ if (!vas_take_share(avas->type, vas)) {
+ pr_vas_debug("VAS is already attached exclusively\n");
+ return -EBUSY;
+ }
+
+ ret = vas_merge(avas, vas, avas->type);
+ if (ret != 0)
+ goto out_put_share;
+
+ ret = task_merge(avas, tsk);
+ if (ret != 0)
+ goto out_put_share;
+
+ vas->refcount++;
+
+ return 0;
+
+out_put_share:
+ vas_put_share(avas->type, vas);
+ return ret;
+}
+
+/**
+ * Detach a VAS from a task -- update internal information ONLY
+ *
+ * Requires that the VAS is already locked.
+ *
+ * @param[in] avas: The pointer to the attached-VAS data structure
+ * containing all the information of this attaching.
+ * @param[in] tsk: The pointer to the task from which the VAS should be
+ * detached.
* @param[in] vas: The pointer to the VAS which should be detached.
*
* @returns: 0 on success, -ERRNO otherwise.
@@ -1209,6 +1721,83 @@ static int __vas_detach(struct att_vas *avas, struct task_struct *tsk,
return 0;
}

+/**
+ * Attach a VAS segment to a VAS -- update internal information ONLY
+ *
+ * Requires that the VAS segment and the VAS are already locked.
+ *
+ * @param aseg: The pointer tot he attached VAS segment data structure
+ * containing all the information of this attaching.
+ * @param vas: The pointer to the VAS to which the VAS segment should
+ * be attached.
+ * @param seg: The pointer to the VAS segment which should be attached.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+static int __vas_seg_attach(struct att_vas_seg *aseg, struct vas *vas,
+ struct vas_seg *seg)
+{
+ int ret;
+
+ /*
+ * Try to acquire the VAS segment share with the proper type. This will
+ * ensure that the different sharing possibilities of VAS segments are
+ * respected.
+ */
+ if (!vas_seg_take_share(aseg->type, seg)) {
+ pr_vas_debug("VAS segment is already attached to a VAS writable\n");
+ return -EBUSY;
+ }
+
+ /* Update the memory map of the VAS. */
+ ret = vas_seg_merge(vas, seg, aseg->type);
+ if (ret != 0)
+ goto out_put_share;
+
+ seg->refcount++;
+ vas->nr_segments++;
+
+ return 0;
+
+out_put_share:
+ vas_seg_put_share(aseg->type, seg);
+ return ret;
+}
+
+/**
+ * Detach a VAS segment from a VAS -- update internal information ONLY
+ *
+ * Requires that the VAS segment and the VAS are already locked.
+ *
+ * @param aseg: The pointer to the attached VAS segment data structure
+ * containing all the information of this attaching.
+ * @param vas: The pointer to the VAS from which the VAS segment should
+ * be detached.
+ * @param seg: The pointer to the VAS segment which should be detached.
+ *
+ * @returns: 0 on success, -ERRNO otherwise.
+ **/
+static int __vas_seg_detach(struct att_vas_seg *aseg, struct vas *vas,
+ struct vas_seg *seg)
+{
+ int ret;
+
+ /* Update the memory maps of the VAS segment and the VAS. */
+ ret = vas_seg_unmerge(vas, seg);
+ if (ret != 0)
+ return ret;
+
+ seg->refcount--;
+ vas->nr_segments--;
+
+ /*
+ * We unlock the VAS segment here to ensure our sharing properties.
+ */
+ vas_seg_put_share(aseg->type, seg);
+
+ return 0;
+}
+
static int __sync_from_task(struct mm_struct *avas_mm, struct mm_struct *tsk_mm)
{
struct vm_area_struct *vma;
@@ -1542,6 +2131,9 @@ int vas_create(const char *name, umode_t mode)
spin_lock_init(&vas->share_lock);
vas->sharing = 0;

+ INIT_LIST_HEAD(&vas->segments);
+ vas->nr_segments = 0;
+
vas->mode = mode & 0666;
vas->uid = current_uid();
vas->gid = current_gid();
@@ -1596,6 +2188,7 @@ EXPORT_SYMBOL(vas_find);
int vas_delete(int vid)
{
struct vas *vas;
+ struct att_vas_seg *aseg, *s_aseg;
int ret;

vas = vas_get(vid);
@@ -1618,6 +2211,39 @@ int vas_delete(int vid)
goto out_unlock;
}

+ /* Detach all still attached VAS segments. */
+ list_for_each_entry_safe(aseg, s_aseg, &vas->segments, vas_link) {
+ struct vas_seg *seg = aseg->seg;
+ int error;
+
+ pr_vas_debug("Detaching VAS segment - name: %s - from to-be-deleted VAS - name: %s\n",
+ seg->name, vas->name);
+
+ /*
+ * Make sure that our VAS segment reference is not removed while
+ * we work with it.
+ */
+ __vas_seg_get(seg);
+
+ /*
+ * Since the VAS from which we detach this VAS segment is going
+ * to be deleted anyways we can shorten the detaching process.
+ */
+ vas_seg_lock(seg);
+
+ error = __vas_seg_detach(aseg, vas, seg);
+ if (error != 0)
+ pr_alert("Detaching VAS segment from VAS failed with %d\n",
+ error);
+
+ list_del(&aseg->seg_link);
+ list_del(&aseg->vas_link);
+ __delete_att_vas_seg(aseg);
+
+ vas_seg_unlock(seg);
+ __vas_seg_put(seg);
+ }
+
vas_unlock(vas);

vas_remove(vas);
@@ -1908,19 +2534,433 @@ int vas_setattr(int vid, struct vas_attr *attr)
}
EXPORT_SYMBOL(vas_setattr);

+int vas_seg_create(const char *name, unsigned long start, unsigned long end,
+ umode_t mode)
+{
+ struct vas_seg *seg;
+ int ret;
+
+ if (!name || !PAGE_ALIGNED(start) || !PAGE_ALIGNED(end) ||
+ (end <= start))
+ return -EINVAL;
+
+ if (vas_seg_find(name) > 0)
+ return -EEXIST;
+
+ pr_vas_debug("Creating a new VAS segment - name: %s start: %#lx end: %#lx\n",
+ name, start, end);
+
+ /* Allocate and initialize the VAS segment. */
+ seg = __new_vas_seg();
+ if (!seg)
+ return -ENOMEM;
+
+ if (strscpy(seg->name, name, VAS_MAX_NAME_LENGTH) < 0) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ mutex_init(&seg->mtx);
+
+ seg->start = start;
+ seg->end = end;
+ seg->length = end - start;
+
+ ret = init_vas_seg_mm(seg);
+ if (ret != 0)
+ goto out_free;
+
+ seg->refcount = 0;
+
+ INIT_LIST_HEAD(&seg->attaches);
+ spin_lock_init(&seg->share_lock);
+ seg->sharing = 0;
+
+ seg->mode = mode & 0666;
+ seg->uid = current_uid();
+ seg->gid = current_gid();
+
+ ret = vas_seg_insert(seg);
+ if (ret != 0)
+ /*
+ * We don't need to free anything here. @vas_seg_insert will
+ * care for the deletion if something went wrong.
+ */
+ return ret;
+
+ return seg->id;
+
+out_free:
+ __delete_vas_seg(seg);
+ return ret;
+}
+EXPORT_SYMBOL(vas_seg_create);
+
+struct vas_seg *vas_seg_get(int sid)
+{
+ return vas_seg_lookup(sid);
+}
+EXPORT_SYMBOL(vas_seg_get);
+
+void vas_seg_put(struct vas_seg *seg)
+{
+ if (!seg)
+ return;
+
+ return __vas_seg_put(seg);
+}
+EXPORT_SYMBOL(vas_seg_put);
+
+int vas_seg_find(const char *name)
+{
+ struct vas_seg *seg;
+
+ seg = vas_seg_lookup_by_name(name);
+ if (seg) {
+ int sid = seg->id;
+
+ vas_seg_put(seg);
+ return sid;
+ }
+
+ return -ESRCH;
+}
+EXPORT_SYMBOL(vas_seg_find);
+
+int vas_seg_delete(int id)
+{
+ struct vas_seg *seg;
+ int ret;
+
+ seg = vas_seg_get(id);
+ if (!seg)
+ return -EINVAL;
+
+ pr_vas_debug("Deleting VAS segment - name: %s\n", seg->name);
+
+ vas_seg_lock(seg);
+
+ if (seg->refcount != 0) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* The user needs write permission to the VAS segment to delete it. */
+ ret = __check_permission(seg->uid, seg->gid, seg->mode, MAY_WRITE);
+ if (ret != 0) {
+ pr_vas_debug("User doesn't have the appropriate permissions to delete the VAS segment\n");
+ goto out_unlock;
+ }
+
+ vas_seg_unlock(seg);
+
+ vas_seg_remove(seg);
+ vas_seg_put(seg);
+
+ return 0;
+
+out_unlock:
+ vas_seg_unlock(seg);
+ vas_seg_put(seg);
+
+ return ret;
+}
+EXPORT_SYMBOL(vas_seg_delete);
+
+int vas_seg_attach(int vid, int sid, int type)
+{
+ struct vas *vas;
+ struct vas_seg *seg;
+ struct att_vas_seg *aseg;
+ int ret;
+
+ type &= (MAY_READ | MAY_WRITE);
+
+ vas = vas_get(vid);
+ if (!vas)
+ return -EINVAL;
+
+ seg = vas_seg_get(sid);
+ if (!seg) {
+ vas_put(vas);
+ return -EINVAL;
+ }
+
+ pr_vas_debug("Attaching VAS segment - name: %s - to VAS - name: %s - %s\n",
+ seg->name, vas->name, access_type_str(type));
+
+ vas_lock(vas);
+ vas_seg_lock(seg);
+
+ /*
+ * Before we can attach the VAS segment to the VAS we have to make some
+ * sanity checks.
+ */
+
+ /*
+ * 1: Check that the user has adequate permissions to attach the VAS
+ * segment in the given way.
+ */
+ ret = __check_permission(seg->uid, seg->gid, seg->mode, type);
+ if (ret != 0) {
+ pr_vas_debug("User doesn't have the appropriate permissions to attach the VAS segment\n");
+ goto out_unlock;
+ }
+
+ /*
+ * 2: The user needs write permission to the VAS to attach a VAS segment
+ * to it. Check that this requirement is fulfilled.
+ */
+ ret = __check_permission(vas->uid, vas->gid, vas->mode, MAY_WRITE);
+ if (ret != 0) {
+ pr_vas_debug("User doesn't have the appropriate permissions on the VAS to attach the VAS segment\n");
+ goto out_unlock;
+ }
+
+
+ /*
+ * 3: Check if the VAS is attached to a process. We do not support
+ * changes to an attached VAS. A VAS must not be attached to a process
+ * to be able to make changes to it. This ensures that the page tables
+ * are always properly initialized.
+ */
+ if (vas->refcount != 0) {
+ pr_vas_debug("VAS is attached to a process\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /*
+ * 4: Check if the VAS segment is already attached to this particular
+ * VAS. Double-attaching would lead to unintended behavior.
+ */
+ list_for_each_entry(aseg, &seg->attaches, seg_link) {
+ if (aseg->vas == vas) {
+ pr_vas_debug("VAS segment is already attached to the VAS\n");
+ ret = 0;
+ goto out_unlock;
+ }
+ }
+
+ /* 5: Check if we reached the maximum number of shares for this VAS. */
+ if (seg->refcount == VAS_MAX_SHARES) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /*
+ * All sanity checks are done. It is safe to attach this VAS segment to
+ * the VAS now.
+ */
+
+ /* Allocate and initialize the attached VAS segment data structure. */
+ aseg = __new_att_vas_seg();
+ if (!aseg) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ aseg->seg = seg;
+ aseg->vas = vas;
+ aseg->type = type;
+
+ ret = __vas_seg_attach(aseg, vas, seg);
+ if (ret != 0)
+ goto out_free_aseg;
+
+ list_add(&aseg->vas_link, &vas->segments);
+ list_add(&aseg->seg_link, &seg->attaches);
+
+ ret = 0;
+
+out_unlock:
+ vas_seg_unlock(seg);
+ vas_seg_put(seg);
+
+ vas_unlock(vas);
+ vas_put(vas);
+
+ return ret;
+
+out_free_aseg:
+ __delete_att_vas_seg(aseg);
+ goto out_unlock;
+}
+EXPORT_SYMBOL(vas_seg_attach);
+
+int vas_seg_detach(int vid, int sid)
+{
+ struct vas *vas;
+ struct vas_seg *seg;
+ struct att_vas_seg *aseg;
+ bool is_attached;
+ int ret;
+
+ vas = vas_get(vid);
+ if (!vas)
+ return -EINVAL;
+
+ vas_lock(vas);
+
+ is_attached = false;
+ list_for_each_entry(aseg, &vas->segments, vas_link) {
+ if (aseg->seg->id == sid) {
+ is_attached = true;
+ break;
+ }
+ }
+ if (!is_attached) {
+ pr_vas_debug("VAS segment is not attached to the given VAS\n");
+ ret = -EINVAL;
+ goto out_unlock_vas;
+ }
+
+ seg = aseg->seg;
+
+ /*
+ * Make sure that our reference to the VAS segment is not deleted while
+ * we are working with it.
+ */
+ __vas_seg_get(seg);
+
+ vas_seg_lock(seg);
+
+ pr_vas_debug("Detaching VAS segment - name: %s - from VAS - name: %s\n",
+ seg->name, vas->name);
+
+ /*
+ * Before we can detach the VAS segment from the VAS we have to do some
+ * sanity checks.
+ */
+
+ /*
+ * 1: Check if the VAS is attached to a process. We do not support
+ * changes to an attached VAS. A VAS must not be attached to a process
+ * to be able to make changes to it. This ensures that the page tables
+ * are always properly initialized.
+ */
+ if (vas->refcount != 0) {
+ pr_vas_debug("VAS is attached to a process\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /*
+ * All sanity checks are done. It is safe to detach the VAS segment from
+ * the VAS now.
+ */
+ ret = __vas_seg_detach(aseg, vas, seg);
+ if (ret != 0)
+ goto out_unlock;
+
+ list_del(&aseg->seg_link);
+ list_del(&aseg->vas_link);
+ __delete_att_vas_seg(aseg);
+
+ ret = 0;
+
+out_unlock:
+ vas_seg_unlock(seg);
+ __vas_seg_put(seg);
+
+out_unlock_vas:
+ vas_unlock(vas);
+ vas_put(vas);
+
+ return ret;
+}
+EXPORT_SYMBOL(vas_seg_detach);
+
+int vas_seg_getattr(int sid, struct vas_seg_attr *attr)
+{
+ struct vas_seg *seg;
+ struct user_namespace *ns = current_user_ns();
+
+ if (!attr)
+ return -EINVAL;
+
+ seg = vas_seg_get(sid);
+ if (!seg)
+ return -EINVAL;
+
+ pr_vas_debug("Getting attributes for VAS segment - name: %s\n",
+ seg->name);
+
+ vas_seg_lock(seg);
+
+ memset(attr, 0, sizeof(struct vas_seg_attr));
+ attr->mode = seg->mode;
+ attr->user = from_kuid(ns, seg->uid);
+ attr->group = from_kgid(ns, seg->gid);
+
+ vas_seg_unlock(seg);
+ vas_seg_put(seg);
+
+ return 0;
+}
+EXPORT_SYMBOL(vas_seg_getattr);
+
+int vas_seg_setattr(int sid, struct vas_seg_attr *attr)
+{
+ struct vas_seg *seg;
+ struct user_namespace *ns = current_user_ns();
+ int ret;
+
+ if (!attr)
+ return -EINVAL;
+
+ seg = vas_seg_get(sid);
+ if (!seg)
+ return -EINVAL;
+
+ pr_vas_debug("Setting attributes for VAS segment - name: %s\n",
+ seg->name);
+
+ vas_seg_lock(seg);
+
+ /*
+ * The user needs write permission to change attributes for the
+ * VAS segment.
+ */
+ ret = __check_permission(seg->uid, seg->gid, seg->mode, MAY_WRITE);
+ if (ret != 0) {
+ pr_vas_debug("User doesn't have the appropriate permissions to set attributes for the VAS segment\n");
+ goto out_unlock;
+ }
+
+ seg->mode = attr->mode & 0666;
+ seg->uid = make_kuid(ns, attr->user);
+ seg->gid = make_kgid(ns, attr->group);
+
+ ret = 0;
+
+out_unlock:
+ vas_seg_unlock(seg);
+ vas_seg_put(seg);
+
+ return ret;
+}
+EXPORT_SYMBOL(vas_seg_setattr);
+
void __init vas_init(void)
{
/* Create the SLAB caches for our data structures. */
vas_cachep = KMEM_CACHE(vas, SLAB_PANIC|SLAB_NOTRACK);
att_vas_cachep = KMEM_CACHE(att_vas, SLAB_PANIC|SLAB_NOTRACK);
vas_context_cachep = KMEM_CACHE(vas_context, SLAB_PANIC|SLAB_NOTRACK);
+ seg_cachep = KMEM_CACHE(vas_seg, SLAB_PANIC|SLAB_NOTRACK);
+ att_seg_cachep = KMEM_CACHE(att_vas_seg, SLAB_PANIC|SLAB_NOTRACK);

/* Initialize the internal management data structures. */
idr_init(&vases);
spin_lock_init(&vases_lock);

+ idr_init(&vas_segs);
+ spin_lock_init(&vas_segs_lock);
+
/* Initialize the place holder variables. */
INVALID_VAS = __new_vas();
+ INVALID_VAS_SEG = __new_vas_seg();

/* Initialize the VAS context of the init task. */
vas_clone(0, &init_task);
@@ -1941,6 +2981,12 @@ static int __init vas_sysfs_init(void)
return -ENOMEM;
}

+ vas_segs_kset = kset_create_and_add("vas_segs", NULL, kernel_kobj);
+ if (!vas_segs_kset) {
+ pr_err("Failed to initialize the VAS segment sysfs directory\n");
+ return -ENOMEM;
+ }
+
return 0;
}
postcore_initcall(vas_sysfs_init);
@@ -2186,3 +3232,105 @@ SYSCALL_DEFINE2(vas_setattr, int, vid, struct vas_attr __user *, uattr)

return vas_setattr(vid, &attr);
}
+
+SYSCALL_DEFINE4(vas_seg_create, const char __user *, name, unsigned long, begin,
+ unsigned long, end, umode_t, mode)
+{
+ char seg_name[VAS_MAX_NAME_LENGTH];
+ int len;
+
+ if (!name)
+ return -EINVAL;
+
+ len = strlen(name);
+ if (len >= VAS_MAX_NAME_LENGTH)
+ return -EINVAL;
+
+ if (copy_from_user(seg_name, name, len) != 0)
+ return -EFAULT;
+
+ seg_name[len] = '\0';
+
+ return vas_seg_create(seg_name, begin, end, mode);
+}
+
+SYSCALL_DEFINE1(vas_seg_delete, int, id)
+{
+ if (id < 0)
+ return -EINVAL;
+
+ return vas_seg_delete(id);
+}
+
+SYSCALL_DEFINE1(vas_seg_find, const char __user *, name)
+{
+ char seg_name[VAS_MAX_NAME_LENGTH];
+ int len;
+
+ if (!name)
+ return -EINVAL;
+
+ len = strlen(name);
+ if (len >= VAS_MAX_NAME_LENGTH)
+ return -EINVAL;
+
+ if (copy_from_user(seg_name, name, len) != 0)
+ return -EFAULT;
+
+ seg_name[len] = '\0';
+
+ return vas_seg_find(seg_name);
+}
+
+SYSCALL_DEFINE3(vas_seg_attach, int, vid, int, sid, int, type)
+{
+ int vas_acc_type;
+
+ if (vid < 0 || sid < 0)
+ return -EINVAL;
+
+ vas_acc_type = __build_vas_access_type(type);
+ if (vas_acc_type == -1)
+ return -EINVAL;
+
+ return vas_seg_attach(vid, sid, vas_acc_type);
+}
+
+SYSCALL_DEFINE2(vas_seg_detach, int, vid, int, sid)
+{
+ if (vid < 0 || sid < 0)
+ return -EINVAL;
+
+ return vas_seg_detach(vid, sid);
+}
+
+SYSCALL_DEFINE2(vas_seg_getattr, int, sid, struct vas_seg_attr __user *, uattr)
+{
+ struct vas_seg_attr attr;
+ int ret;
+
+ if (sid < 0 || !uattr)
+ return -EINVAL;
+
+ ret = vas_seg_getattr(sid, &attr);
+ if (ret != 0)
+ return ret;
+
+ if (copy_to_user(uattr, &attr, sizeof(struct vas_seg_attr)) != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+SYSCALL_DEFINE2(vas_seg_setattr, int, sid, struct vas_seg_attr __user *, uattr)
+{
+ struct vas_seg_attr attr;
+
+ if (sid < 0 || !uattr)
+ return -EINVAL;
+
+ if (copy_from_user(&attr, uattr, sizeof(struct vas_seg_attr)) != 0)
+ return -EFAULT;
+
+ return vas_seg_setattr(sid, &attr);
+}
--
2.12.0