lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 26 Oct 2009 17:32:35 -0700
From:	Darren Hart <dvhltc@...ibm.com>
To:	"lkml, " <linux-kernel@...r.kernel.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Peter Zijlstra <peterz@...radead.org>,
	Ingo Molnar <mingo@...e.hu>,
	Eric Dumazet <eric.dumazet@...il.com>,
	Dinakar Guniguntala <dino@...ibm.com>,
	"Stultz, John" <johnstul@...ibm.com>
Subject: Re: [PATCH] RFC: futex: make futex_lock_pi interruptible

Darren Hart wrote:
>  From 9ea67856951c87a03a5177e1382d836622642521 Mon Sep 17 00:00:00 2001
> From: Darren Hart <dvhltc@...ibm.com>
> Date: Mon, 26 Oct 2009 17:15:54 -0700
> Subject: [PATCH] RFC: futex: make futex_lock_pi interruptible
> 

The following C test case demonstrates how this patch could be used to
implement interruptible locking. There is an awful lot of debug code and
some other relics of a hacked together test in there now, but if anyone
wanted to test the futex changes, this will do the trick.


/******************************************************************************
 *
 *   Copyright © International Business Machines  Corp., 2009
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * NAME
 *      mutexint.c
 *
 * DESCRIPTION
 *      Provide an interruptible PI-aware locking construct.
 *
 * AUTHOR
 *      Darren Hart <dvhltc@...ibm.com>
 *
 * HISTORY
 *      2009-Oct-26: Initial version by Darren Hart <dvhltc@...ibm.com>
 *
 *****************************************************************************/

#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <syscall.h>
#include <sys/types.h>
#include <linux/futex.h>
#include <errno.h>

/*
 * The futex() call has been removed from the include/futex.h header, implement
 * our own version.
 */
//#define SYS_futex 202
#define FUTEX_PRIVATE 128
#define FUTEX_INTERRUPTIBLE 512
#define futex(uaddr, op, val, timeout, uaddr2, val3) \
	syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3)

#define futex_lock_pi(uaddr, val, timeout) \
	futex(uaddr, FUTEX_LOCK_PI|FUTEX_INTERRUPTIBLE|FUTEX_PRIVATE, \
	      val, timeout, NULL, 0)

#define futex_unlock_pi(uaddr, val) \
	futex(uaddr, FUTEX_UNLOCK_PI|FUTEX_PRIVATE, val, NULL, NULL, 0)

#define gettid() syscall(SYS_gettid)

#define SIGMUTEXINT SIGRTMIN

/*
 * Implement cmpxchg using gcc atomic builtins.
 * http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
 */
static inline int cmpxchg(volatile int32_t *addr, int32_t old, int32_t new)
{
	return __sync_val_compare_and_swap(addr, old, new);
}

struct mutexint {
	volatile int32_t lock_val;
};

struct mutexint_attr {
	/* placeholder for future expansion */
	u_int32_t flags;
};

void sigmutexint_handler(int signo)
{
	printf("handled cancel signal: %d\n", signo);
}

int mutexint_init(struct mutexint *mutex, struct mutexint_attr *attr)
{
	mutex->lock_val = 0;
	/* future: process attr->flags */
	return 0;
}

int mutexint_lock(struct mutexint *mutex)
{
	int32_t val;
	int ret = 0;
	pid_t tid = gettid();
	printf("%s: initial lock_val: 0x%x\n", __FUNCTION__, mutex->lock_val);
	if ((val = cmpxchg(&mutex->lock_val, 0, tid)) != 0) {
		printf("\tfirst cmpxchg old_val: 0x%x lock_val: 0x%x\n", val, mutex->lock_val);
		do {
			printf("\tdo cmpxchg old_val: 0x%x\n", val);
			if ((val & FUTEX_WAITERS) || 
				(val = cmpxchg(&mutex->lock_val, val, val | FUTEX_WAITERS))) {
				printf("\tinner cmpxchg old_val: 0x%x lock_val: 0x%x\n", val, mutex->lock_val);
				printf("\tcalling futex_lock_pi:\n");
				printf("\top: 0x%x\n", FUTEX_LOCK_PI | FUTEX_PRIVATE | FUTEX_INTERRUPTIBLE);
				printf("\t&lock_val: %p\n", &mutex->lock_val);
				ret = futex_lock_pi(&mutex->lock_val, val, NULL);
				if (ret == -1) {
					ret = -errno;
					printf("futex_lock_pi returned -1, errno is %d\n", ret);
				}
				/*
				 * If -EINTR is returned, the lock may no longer
				 * have owners, but we have no good way to detect that.
				 */
				printf("\tfutex_lock_pi returned. ret=%d lock_val=0x%x\n",
				       ret, mutex->lock_val);
				if (ret != -EWOULDBLOCK)
					break;
			}
		} while ((val = cmpxchg(&mutex->lock_val, 0, tid)) != 0);
	}

	/* 
	 * Catch the case where futex_lock_pi() returns EWOULDBLOCK, but the
	 * while conditional acquires the lock atomically and reset ret.
	 */
	if ((val & ~FUTEX_WAITERS) == tid)
		ret = 0;

	printf("%s: returning: %d\n", __FUNCTION__, ret);
	return ret;
}

int mutexint_unlock(struct mutexint *mutex)
{
	int32_t val;
	int ret = 0;
	pid_t tid = gettid();
	if ((val = cmpxchg(&mutex->lock_val, tid, 0)) != 0) {
		ret = futex_unlock_pi(&mutex->lock_val, val);
	}
	return ret;
}

int mutexint_interrupt(pthread_t pthread, struct mutexint *mutex)
{
	/* 
	 * FIXME: return -EINVAL if the pthread is blocked on a different
	 * thread ?
	 */
	return pthread_kill(pthread, SIGMUTEXINT);
}

int mutexint_prepare()
{
	struct sigaction sa;
	sa.sa_handler = sigmutexint_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	if (sigaction(SIGMUTEXINT, &sa, NULL)) {
		perror("sigaction");
		return 1;
	}
	return 0;
}

/* unit test */
#define UNIT_TEST 1
#ifdef UNIT_TEST

struct mutexint mutex;

void *child_thread(void *arg)
{
	int ret;
	mutexint_prepare();

	printf("\tchild acquiring the mutex (will block)\n");
	ret = mutexint_lock(&mutex);
	printf("\tchild returned %d\n", ret);

	*(int *)arg = ret;
	return NULL;
}

int main(int argc, char *argv[])
{
	struct mutexint_attr m_attr;
	pthread_t child;
	int child_ret;
	int ret = 0;

	/* prepare the mutex, ensuring we use PROCESS_PRIVATE */
	mutexint_init(&mutex, &m_attr);

	printf("\nTesting canceled mutex lock scenario.\n");
	printf("\tmain() acquiring the mutex\n");
	mutexint_lock(&mutex);
	/* prepare and start the child thread */
	ret = pthread_create(&child, NULL, child_thread, &child_ret);
	if (ret) {
		perror("Failed to create pthread");
		return ret;
	}

	/* ensure the child has blocked on the lock */
	sleep(3);

	/* cancel the child thread blocking on the mutex */
	printf("\tmain() canceling the child\n");
	ret = mutexint_interrupt(child, &mutex);
	printf("\tmain() pthread_kill returned %d\n", ret);
	
	pthread_join(child, NULL);

	printf("Result: %s\n", child_ret == -EINTR ? "PASS" : "FAIL");
	return ret;
}
#endif

-- 
Darren Hart
IBM Linux Technology Center
Real-Time Linux Team
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists