https://github.com/torvalds/linux
Revision 0c36b390a546055b6815d4b93a2c9fed4d980ffb authored by Sebastian Ott on 04 June 2014, 13:58:24 UTC, committed by Tejun Heo on 04 June 2014, 16:12:29 UTC
The percpu-refcount infrastructure uses the underscore variants of
this_cpu_ops in order to modify percpu reference counters.
(e.g. __this_cpu_inc()).

However the underscore variants do not atomically update the percpu
variable, instead they may be implemented using read-modify-write
semantics (more than one instruction).  Therefore it is only safe to
use the underscore variant if the context is always the same (process,
softirq, or hardirq). Otherwise it is possible to lose updates.

This problem is something that Sebastian has seen within the aio
subsystem which uses percpu refcounters both in process and softirq
context leading to reference counts that never dropped to zeroes; even
though the number of "get" and "put" calls matched.

Fix this by using the non-underscore this_cpu_ops variant which
provides correct per cpu atomic semantics and fixes the corrupted
reference counts.

Cc: Kent Overstreet <kmo@daterainc.com>
Cc: <stable@vger.kernel.org> # v3.11+
Reported-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
References: http://lkml.kernel.org/g/alpine.LFD.2.11.1406041540520.21183@denkbrett
1 parent 5a838c3
Raw File
Tip revision: 0c36b390a546055b6815d4b93a2c9fed4d980ffb authored by Sebastian Ott on 04 June 2014, 13:58:24 UTC
percpu-refcount: fix usage of this_cpu_ops
Tip revision: 0c36b39
objdiff
#!/bin/bash

# objdiff - a small script for validating that a commit or series of commits
# didn't change object code.
#
# Copyright 2014, Jason Cooper <jason@lakedaemon.net>
#
# Licensed under the terms of the GNU GPL version 2

# usage example:
#
# $ git checkout COMMIT_A
# $ <your fancy build command here>
# $ ./scripts/objdiff record path/to/*.o
#
# $ git checkout COMMIT_B
# $ <your fancy build command here>
# $ ./scripts/objdiff record path/to/*.o
#
# $ ./scripts/objdiff diff COMMIT_A COMMIT_B
# $

# And to clean up (everything is in .tmp_objdiff/*)
# $ ./scripts/objdiff clean all
#
# Note: 'make mrproper' will also remove .tmp_objdiff

GIT_DIR="`git rev-parse --git-dir`"

if [ -d "$GIT_DIR" ]; then
	TMPD="${GIT_DIR%git}tmp_objdiff"

	[ -d "$TMPD" ] || mkdir "$TMPD"
else
	echo "ERROR: git directory not found."
	exit 1
fi

usage() {
	echo "Usage: $0 <command> <args>"
	echo "  record    <list of object files>"
	echo "  diff      <commitA> <commitB>"
	echo "  clean     all | <commit>"
	exit 1
}

dorecord() {
	[ $# -eq 0 ] && usage

	FILES="$*"

	CMT="`git rev-parse --short HEAD`"

	OBJDUMP="${CROSS_COMPILE}objdump"
	OBJDIFFD="$TMPD/$CMT"

	[ ! -d "$OBJDIFFD" ] && mkdir -p "$OBJDIFFD"

	for f in $FILES; do
		dn="${f%/*}"
		bn="${f##*/}"

		[ ! -d "$OBJDIFFD/$dn" ] && mkdir -p "$OBJDIFFD/$dn"

		# remove addresses for a more clear diff
		# http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and
		$OBJDUMP -D "$f" | sed "s/^[[:space:]]\+[0-9a-f]\+//" \
			>"$OBJDIFFD/$dn/$bn"
	done
}

dodiff() {
	[ $# -ne 2 ] && [ $# -ne 0 ] && usage

	if [ $# -eq 0 ]; then
		SRC="`git rev-parse --short HEAD^`"
		DST="`git rev-parse --short HEAD`"
	else
		SRC="`git rev-parse --short $1`"
		DST="`git rev-parse --short $2`"
	fi

	DIFF="`which colordiff`"

	if [ ${#DIFF} -eq 0 ] || [ ! -x "$DIFF" ]; then
		DIFF="`which diff`"
	fi

	SRCD="$TMPD/$SRC"
	DSTD="$TMPD/$DST"

	if [ ! -d "$SRCD" ]; then
		echo "ERROR: $SRCD doesn't exist"
		exit 1
	fi

	if [ ! -d "$DSTD" ]; then
		echo "ERROR: $DSTD doesn't exist"
		exit 1
	fi

	$DIFF -Nurd $SRCD $DSTD
}

doclean() {
	[ $# -eq 0 ] && usage
	[ $# -gt 1 ] && usage

	if [ "x$1" = "xall" ]; then
		rm -rf $TMPD/*
	else
		CMT="`git rev-parse --short $1`"

		if [ -d "$TMPD/$CMT" ]; then
			rm -rf $TMPD/$CMT
		else
			echo "$CMT not found"
		fi
	fi
}

[ $# -eq 0 ] &&	usage

case "$1" in
	record)
		shift
		dorecord $*
		;;
	diff)
		shift
		dodiff $*
		;;
	clean)
		shift
		doclean $*
		;;
	*)
		echo "Unrecognized command '$1'"
		exit 1
		;;
esac
back to top