diff --git a/fs/proc/base.c b/fs/proc/base.c
index 87c9a9aacda3..e60c13dd087c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -45,6 +45,9 @@
  *
  *  Paul Mundt <paul.mundt@nokia.com>:
  *  Overall revision about smaps.
+ *
+ *  Till Smejkal <till.smejkal@gmail.com>:
+ *  Add entries for first class virtual address spaces.
  */
 
 #include <linux/uaccess.h>
@@ -87,6 +90,7 @@
 #include <linux/slab.h>
 #include <linux/flex_array.h>
 #include <linux/posix-timers.h>
+#include <linux/vas.h>
 #ifdef CONFIG_HARDWALL
 #include <asm/hardwall.h>
 #endif
@@ -2841,6 +2845,527 @@ static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
 	return err;
 }
 
+#ifdef CONFIG_VAS_PROCFS
+
+/**
+ * Get a string representation of the access type to a VAS.
+ **/
+#define vas_access_type_str(type) ((type) & MAY_WRITE ?			\
+				   ((type) & MAY_READ ? "rw" : "wo") : "ro")
+
+static int att_vas_show_status(struct seq_file *sf, void *unused)
+{
+	struct inode *inode = sf->private;
+	struct proc_inode *pi = PROC_I(inode);
+	struct task_struct *tsk;
+	struct vas_context *vas_ctx;
+	struct att_vas *avas;
+	int vid = pi->vas_id;
+
+	tsk = get_proc_task(inode);
+	if (!tsk)
+		return -ENOENT;
+
+	vas_ctx = tsk->vas_ctx;
+
+	vas_context_lock(vas_ctx);
+
+	list_for_each_entry(avas, &vas_ctx->vases, tsk_link) {
+		if (vid == avas->vas->id)
+			goto good_att_vas;
+	}
+
+	vas_context_unlock(vas_ctx);
+	put_task_struct(tsk);
+
+	return -ENOENT;
+
+good_att_vas:
+	seq_printf(sf,
+		   "pid:  %d\n"
+		   "vid:  %d\n"
+		   "type: %s\n",
+		   avas->tsk->pid, avas->vas->id,
+		   vas_access_type_str(avas->type));
+
+	vas_context_unlock(vas_ctx);
+	put_task_struct(tsk);
+
+	return 0;
+}
+
+static int att_vas_show_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, att_vas_show_status, inode);
+}
+
+static const struct file_operations att_vas_show_status_fops = {
+	.open		= att_vas_show_status_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int att_vas_show_mappings(struct seq_file *sf, void *unused)
+{
+	struct inode *inode = sf->private;
+	struct proc_inode *pi = PROC_I(inode);
+	struct task_struct *tsk;
+	struct vas_context *vas_ctx;
+	struct att_vas *avas;
+	struct mm_struct *mm;
+	struct vm_area_struct *vma;
+	int vid = pi->vas_id;
+
+	tsk = get_proc_task(inode);
+	if (!tsk)
+		return -ENOENT;
+
+	vas_ctx = tsk->vas_ctx;
+
+	vas_context_lock(vas_ctx);
+
+	list_for_each_entry(avas, &vas_ctx->vases, tsk_link) {
+		if (avas->vas->id == vid)
+			goto good_att_vas;
+	}
+
+	vas_context_unlock(vas_ctx);
+	put_task_struct(tsk);
+
+	return -ENOENT;
+
+good_att_vas:
+	mm = avas->mm;
+
+	down_read(&mm->mmap_sem);
+
+	if (!mm->mmap) {
+		seq_puts(sf, "EMPTY\n");
+		goto out_unlock;
+	}
+
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		vm_flags_t flags = vma->vm_flags;
+		struct file *file = vma->vm_file;
+		unsigned long long pgoff = 0;
+
+		if (file)
+			pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
+
+		seq_printf(sf, "%08lx-%08lx %c%c%c%c [%c:%c] %08llx",
+			   vma->vm_start, vma->vm_end,
+			   flags & VM_READ ? 'r' : '-',
+			   flags & VM_WRITE ? 'w' : '-',
+			   flags & VM_EXEC ? 'x' : '-',
+			   flags & VM_MAYSHARE ? 's' : 'p',
+			   vma->vas_reference ? 'v' : '-',
+			   vma->vas_attached ? 'a' : '-',
+			   pgoff);
+
+		seq_putc(sf, ' ');
+
+		if (file) {
+			seq_file_path(sf, file, "\n");
+		} else if (vma->vm_ops && vma->vm_ops->name) {
+			seq_puts(sf, vma->vm_ops->name(vma));
+		} else {
+			if (!vma->vm_mm)
+				seq_puts(sf, "[vdso]");
+			else if (vma->vm_start <= mm->brk &&
+				 vma->vm_end >= mm->start_brk)
+				seq_puts(sf, "[heap]");
+			else if (vma->vm_start <= mm->start_stack &&
+				 vma->vm_end >= mm->start_stack)
+				seq_puts(sf, "[stack]");
+		}
+
+		seq_putc(sf, '\n');
+	}
+
+out_unlock:
+	up_read(&mm->mmap_sem);
+
+	vas_context_unlock(vas_ctx);
+	put_task_struct(tsk);
+
+	return 0;
+}
+
+static int att_vas_show_mappings_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, att_vas_show_mappings, inode);
+}
+
+static const struct file_operations att_vas_show_mappings_fops = {
+	.open		= att_vas_show_mappings_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int att_vas_vas_link(char *name, int *buflen, int vid)
+{
+	int len;
+
+	len = scnprintf(name, *buflen, "/sys/kernel/vas/%d", vid);
+	if (len >= *buflen)
+		return -E2BIG;
+
+	*buflen = len;
+	return 0;
+}
+
+static int att_vas_vas_link_readlink(struct dentry *dentry, char __user *buffer,
+				     int buflen)
+{
+	char *name;
+	int len, ret;
+	int vid;
+
+	len = PATH_MAX;
+	name = kmalloc(len, GFP_TEMPORARY);
+	if (!name)
+		return -ENOMEM;
+
+	vid = PROC_I(d_inode(dentry))->vas_id;
+
+	ret = att_vas_vas_link(name, &len, vid);
+	if (ret != 0)
+		goto out_free;
+
+	if (len > buflen)
+		len = buflen;
+	if (copy_to_user(buffer, name, len) != 0) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+
+	ret = len;
+
+out_free:
+	kfree(name);
+
+	return ret;
+}
+
+static const char *att_vas_vas_link_get_link(struct dentry *dentry,
+					     struct inode *inode,
+					     struct delayed_call *done)
+{
+	char *name;
+	int len, ret;
+	int vid;
+
+	if (!dentry)
+		return ERR_PTR(-ECHILD);
+
+	len = PATH_MAX;
+	name = kmalloc(len, GFP_TEMPORARY);
+	if (!name)
+		return NULL;
+
+	vid = PROC_I(inode)->vas_id;
+
+	ret = att_vas_vas_link(name, &len, vid);
+	if (ret != 0) {
+		kfree(name);
+		return ERR_PTR(ret);
+	}
+
+	set_delayed_call(done, kfree_link, name);
+	return name;
+}
+
+static const struct inode_operations att_vas_vas_link_iops = {
+	.readlink	= att_vas_vas_link_readlink,
+	.get_link	= att_vas_vas_link_get_link,
+	.setattr	= proc_setattr
+};
+
+static const struct pid_entry att_vas_stuff[] = {
+	REG("status", 0444, att_vas_show_status_fops),
+	REG("maps", 0440, att_vas_show_mappings_fops),
+	NOD("vas", (S_IFLNK | 0777), &att_vas_vas_link_iops, NULL, {}),
+};
+
+static int att_vas_revalidate(struct dentry *dentry, unsigned int flags)
+{
+	struct inode *inode;
+	struct task_struct *tsk;
+	struct vas_context *vas_ctx;
+	struct att_vas *avas;
+	int vid;
+	int ret;
+
+	if (flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	inode = d_inode(dentry);
+	tsk = get_proc_task(inode);
+	if (!tsk)
+		return 0;
+
+	vid = PROC_I(inode)->vas_id;
+	vas_ctx = tsk->vas_ctx;
+
+	vas_context_lock(vas_ctx);
+
+	ret = 0;
+	list_for_each_entry(avas, &vas_ctx->vases, tsk_link) {
+		if (avas->vas->id == vid) {
+			ret = 1;
+			break;
+		}
+	}
+
+	vas_context_unlock(vas_ctx);
+	put_task_struct(tsk);
+
+	return ret;
+}
+
+static const struct dentry_operations att_vas_dops = {
+	.d_revalidate	= att_vas_revalidate,
+};
+
+static int att_vas_pident_instantiate(struct inode *dir,
+				      struct dentry *dentry,
+				      struct task_struct *task,
+				      const void *ptr)
+{
+	const struct pid_entry *p = ptr;
+	struct inode *inode;
+	struct proc_inode *ei;
+
+	inode = proc_pid_make_inode(dir->i_sb, task, p->mode);
+	if (!inode)
+		goto out;
+
+	ei = PROC_I(inode);
+	if (S_ISDIR(inode->i_mode))
+		set_nlink(inode, 2);
+	if (p->iop)
+		inode->i_op = p->iop;
+	if (p->fop)
+		inode->i_fop = p->fop;
+	ei->op = p->op;
+
+	/* Copy the VAS ID from the parent inode */
+	ei->vas_id = PROC_I(dir)->vas_id;
+
+	d_set_d_op(dentry, &att_vas_dops);
+	d_add(dentry, inode);
+
+	if (att_vas_revalidate(dentry, 0))
+		return 0;
+out:
+	return -ENOENT;
+}
+
+static struct dentry *att_vas_pident_lookup(struct inode *dir,
+					    struct dentry *dentry,
+					    unsigned int flags)
+{
+	int error;
+	struct task_struct *task = get_proc_task(dir);
+	const struct pid_entry *p, *last;
+	const struct pid_entry *entries = att_vas_stuff;
+	int nents = ARRAY_SIZE(att_vas_stuff);
+
+	error = -ENOENT;
+
+	if (!task)
+		goto out_no_task;
+
+	last = &entries[nents];
+	for (p = entries; p < last; p++) {
+		if (p->len != dentry->d_name.len)
+			continue;
+		if (!memcmp(dentry->d_name.name, p->name, p->len))
+			break;
+	}
+	if (p >= last)
+		goto out;
+
+	error = att_vas_pident_instantiate(dir, dentry, task, p);
+out:
+	put_task_struct(task);
+out_no_task:
+	return ERR_PTR(error);
+}
+
+static int att_vas_pident_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct task_struct *task = get_proc_task(file_inode(file));
+	const struct pid_entry *p, *last;
+	const struct pid_entry *entries = att_vas_stuff;
+	int nents = ARRAY_SIZE(att_vas_stuff);
+
+	if (!task)
+		return -ENOENT;
+
+	if (!dir_emit_dots(file, ctx))
+		goto out;
+
+	if (ctx->pos >= nents + 2)
+		goto out;
+
+	last = &entries[nents];
+	for (p = entries + (ctx->pos - 2); p < last; p++) {
+		if (!proc_fill_cache(file, ctx, p->name, p->len,
+				     att_vas_pident_instantiate, task, p))
+			break;
+		ctx->pos++;
+	}
+out:
+	put_task_struct(task);
+	return 0;
+}
+
+static const struct inode_operations proc_att_vas_iops = {
+	.lookup		= att_vas_pident_lookup,
+	.setattr	= proc_setattr,
+	.permission	= generic_permission,
+};
+
+static const struct file_operations proc_att_vas_fops = {
+	.read		= generic_read_dir,
+	.llseek		= generic_file_llseek,
+	.iterate_shared	= att_vas_pident_readdir,
+};
+
+static int proc_att_vas_dir_instantiate(struct inode *dir,
+					struct dentry *dentry,
+					struct task_struct *tsk,
+					const void *data)
+{
+	struct inode *inode;
+	struct proc_inode *pi;
+	const struct att_vas *avas = data;
+
+	inode = proc_pid_make_inode(dir->i_sb, tsk, S_IFDIR | 0555);
+
+	if (!inode)
+		return -ENOENT;
+
+	pi = PROC_I(inode);
+	pi->vas_id = avas->vas->id;
+
+	set_nlink(inode, 2);
+	inode->i_op = &proc_att_vas_iops;
+	inode->i_fop = &proc_att_vas_fops;
+
+	d_add(dentry, inode);
+	d_set_d_op(dentry, &att_vas_dops);
+
+	/*
+	 * No need to revalidate the dentry at this point, because we are still
+	 * holding the lock for the VAS context. Hence this VAS cannot be
+	 * detached from the task and hence the dentry is still valid.
+	 */
+	return 0;
+}
+
+static struct dentry *proc_att_vas_dir_lookup(struct inode *dir,
+					      struct dentry *dentry,
+					      unsigned int flags)
+{
+	struct task_struct *tsk;
+	struct vas_context *vas_ctx;
+	struct att_vas *avas;
+	int vid;
+	int ret;
+
+	tsk = get_proc_task(dir);
+	if (!tsk)
+		return ERR_PTR(-ENOENT);
+
+	if (kstrtoint(dentry->d_name.name, 10, &vid)) {
+		ret = -EINVAL;
+		goto out_put;
+	}
+
+	vas_ctx = tsk->vas_ctx;
+
+	vas_context_lock(vas_ctx);
+
+	ret = -ENOENT;
+	list_for_each_entry(avas, &vas_ctx->vases, tsk_link) {
+		if (vid == avas->vas->id) {
+			ret = proc_att_vas_dir_instantiate(dir, dentry,
+							   tsk, avas);
+			break;
+		}
+	}
+
+	vas_context_unlock(vas_ctx);
+
+out_put:
+	put_task_struct(tsk);
+
+	return ERR_PTR(ret);
+}
+
+static int proc_att_vas_dir_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct inode *inode = file_inode(file);
+	struct task_struct *tsk;
+	struct vas_context *vas_ctx;
+	struct att_vas *avas;
+	int pos = 2;
+
+	tsk = get_proc_task(inode);
+	if (!tsk)
+		return -ENOENT;
+
+	if (!dir_emit_dots(file, ctx))
+		goto out_put;
+
+	vas_ctx = tsk->vas_ctx;
+
+	vas_context_lock(vas_ctx);
+
+	list_for_each_entry(avas, &vas_ctx->vases, tsk_link) {
+		char name[PROC_NUMBUF];
+		int len;
+
+		if (++pos <= ctx->pos)
+			continue;
+
+		snprintf(name, sizeof(name), "%d", avas->vas->id);
+		len = strnlen(name, PROC_NUMBUF);
+
+		if (!proc_fill_cache(file, ctx, name, len,
+				     proc_att_vas_dir_instantiate,
+				     tsk, avas))
+			break;
+
+		ctx->pos++;
+	}
+
+	vas_context_unlock(vas_ctx);
+
+out_put:
+	put_task_struct(tsk);
+
+	return 0;
+}
+
+const struct inode_operations proc_att_vas_dir_iops = {
+	.lookup		= proc_att_vas_dir_lookup,
+	.setattr	= proc_setattr,
+	.permission	= generic_permission,
+};
+
+const struct file_operations proc_att_vas_dir_fops = {
+	.read		= generic_read_dir,
+	.llseek		= generic_file_llseek,
+	.iterate_shared	= proc_att_vas_dir_readdir,
+};
+
+#endif
+
 /*
  * Thread groups
  */
@@ -2856,6 +3381,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_NET
 	DIR("net",        S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations),
 #endif
+#ifdef CONFIG_VAS_PROCFS
+	DIR("vas",        S_IRUGO|S_IXUGO, proc_att_vas_dir_iops, proc_att_vas_dir_fops),
+#endif
 	REG("environ",    S_IRUSR, proc_environ_operations),
 	REG("auxv",       S_IRUSR, proc_auxv_operations),
 	ONE("status",     S_IRUGO, proc_pid_status),
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index cb2d5702bdce..cc8937d348df 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -63,6 +63,7 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
 	ei->pid = NULL;
 	ei->fd = 0;
 	ei->op.proc_get_link = NULL;
+	ei->vas_id = 0;
 	ei->pde = NULL;
 	ei->sysctl = NULL;
 	ei->sysctl_entry = NULL;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 2de5194ba378..0cb6bb39d61d 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -61,6 +61,7 @@ union proc_op {
 struct proc_inode {
 	struct pid *pid;
 	unsigned int fd;
+	int vas_id;
 	union proc_op op;
 	struct proc_dir_entry *pde;
 	struct ctl_table_header *sysctl;
diff --git a/mm/Kconfig b/mm/Kconfig
index 934c56bcdbf4..9ef3efc16bed 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -745,3 +745,12 @@ config VAS_DEBUG
 	help
 	  Enable extensive debugging output for the First Class Virtual Address
 	  Spaces feature.
+
+config VAS_PROCFS
+	bool "procfs entries for First Class Virtual Address Spaces"
+	depends on VAS && PROC_FS
+	default y
+	help
+	  Provide information in /proc/$PID about all First Class 
+	  Virtual Address Spaces that are currently attached to the
+	  corresponding process.
