summaryrefslogtreecommitdiff
path: root/ipc
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2018-03-23 00:29:57 -0500
committerBen Hutchings <ben@decadent.org.uk>2018-10-21 08:45:03 +0100
commitecc23ca1da7596dcf39d2ef98fe33e4e05532a8a (patch)
tree7eb14d834daad8480204d837863f40cd2a839d44 /ipc
parentf042dd1bdaea0960ac374510b75650ab6c9e765d (diff)
ipc/shm: Fix shmctl(..., IPC_STAT, ...) between pid namespaces.
commit 98f929b1bd4d0b7c7a77d0d9776d1b924db2e454 upstream. Today shm_cpid and shm_lpid are remembered in the pid namespace of the creator and the processes that last touched a sysvipc shared memory segment. If you have processes in multiple pid namespaces that is just wrong, and I don't know how this has been over-looked for so long. As only creation and shared memory attach and shared memory detach update the pids I do not expect there to be a repeat of the issues when struct pid was attached to each af_unix skb, which in some notable cases cut the performance in half. The problem was threads of the same process updating same struct pid from different cpus causing the cache line to be highly contended and bounce between cpus. As creation, attach, and detach are expected to be rare operations for sysvipc shared memory segments I do not expect that kind of cache line ping pong to cause probems. In addition because the pid is at a fixed location in the structure instead of being dynamic on a skb, the reference count of the pid does not need to be updated on each operation if the pid is the same. This ability to simply skip the pid reference count changes if the pid is unchanging further reduces the likelihood of the a cache line holding a pid reference count ping-ponging between cpus. Fixes: b488893a390e ("pid namespaces: changes to show virtual ids to user") Reviewed-by: Nagarathnam Muthusamy <nagarathnam.muthusamy@oracle.com> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> [bwh: Backported to 3.16: adjust filename, context] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/shm.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index f16a53bedfc2..e9268fe1fdc1 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -198,7 +198,7 @@ static int __shm_open(struct vm_area_struct *vma)
return PTR_ERR(shp);
shp->shm_atim = get_seconds();
- shp->shm_lprid = task_tgid_vnr(current);
+ ipc_update_pid(&shp->shm_lprid, task_tgid(current));
shp->shm_nattch++;
shm_unlock(shp);
return 0;
@@ -238,6 +238,8 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
else if (shp->mlock_user)
user_shm_unlock(file_inode(shm_file)->i_size, shp->mlock_user);
fput(shm_file);
+ ipc_update_pid(&shp->shm_cprid, NULL);
+ ipc_update_pid(&shp->shm_lprid, NULL);
ipc_rcu_putref(shp, shm_rcu_free);
}
@@ -282,7 +284,7 @@ static void shm_close(struct vm_area_struct *vma)
if (WARN_ON_ONCE(IS_ERR(shp)))
goto done; /* no-op */
- shp->shm_lprid = task_tgid_vnr(current);
+ ipc_update_pid(&shp->shm_lprid, task_tgid(current));
shp->shm_dtim = get_seconds();
shp->shm_nattch--;
if (shm_may_destroy(ns, shp))
@@ -581,8 +583,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
if (IS_ERR(file))
goto no_file;
- shp->shm_cprid = task_tgid_vnr(current);
- shp->shm_lprid = 0;
+ shp->shm_cprid = get_pid(task_tgid(current));
+ shp->shm_lprid = NULL;
shp->shm_atim = shp->shm_dtim = 0;
shp->shm_ctim = get_seconds();
shp->shm_segsz = size;
@@ -614,6 +616,8 @@ no_id:
user_shm_unlock(size, shp->mlock_user);
fput(file);
no_file:
+ ipc_update_pid(&shp->shm_cprid, NULL);
+ ipc_update_pid(&shp->shm_lprid, NULL);
ipc_rcu_putref(shp, shm_rcu_free);
return error;
}
@@ -952,8 +956,8 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
tbuf.shm_atime = shp->shm_atim;
tbuf.shm_dtime = shp->shm_dtim;
tbuf.shm_ctime = shp->shm_ctim;
- tbuf.shm_cpid = shp->shm_cprid;
- tbuf.shm_lpid = shp->shm_lprid;
+ tbuf.shm_cpid = pid_vnr(shp->shm_cprid);
+ tbuf.shm_lpid = pid_vnr(shp->shm_lprid);
tbuf.shm_nattch = shp->shm_nattch;
rcu_read_unlock();
@@ -1363,6 +1367,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
#ifdef CONFIG_PROC_FS
static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
{
+ struct pid_namespace *pid_ns = ipc_seq_pid_ns(s);
struct user_namespace *user_ns = seq_user_ns(s);
struct shmid_kernel *shp = it;
unsigned long rss = 0, swp = 0;
@@ -1383,8 +1388,8 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
shp->shm_perm.id,
shp->shm_perm.mode,
shp->shm_segsz,
- shp->shm_cprid,
- shp->shm_lprid,
+ pid_nr_ns(shp->shm_cprid, pid_ns),
+ pid_nr_ns(shp->shm_lprid, pid_ns),
shp->shm_nattch,
from_kuid_munged(user_ns, shp->shm_perm.uid),
from_kgid_munged(user_ns, shp->shm_perm.gid),