In short, what about this? Compile it with either -DSPIN to use spinlocks
or without -DSPIN to use atomic_add/atomic_sub. It will work on intel
only. If you remove rdtsc/flush stuff, it will work on all architectures
which do spin_lock/spin_unlock and/or atomic_add/atomic_sub/atomic_read
inline, without kernel function support. Ignore warnings in compilation,
I'm printing value of atomic_t/spinlock_t directly to save some ifdefs.
I also added third process which prints spin value continuously to see
whether deadlock (bug in code) occured (for example while (atomic_read())
spinning endlessly).
Best regards,
Petr Vandrovec
vandrove@vc.cvut.cz
#define __SMP__
#define CONFIG_SMP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <asm/spinlock.h>
#include <asm/atomic.h>
extern void iopl(int);
typedef struct {
unsigned int pad0;
unsigned int pad1;
unsigned int pad2;
unsigned int i;
unsigned int j;
#ifdef SPIN
spinlock_t spin;
#else
atomic_t spin;
#endif
unsigned int pad3;
unsigned int pad4;
} PARS __attribute__((packed));
#define KEY 0xdeadface
#define PAGE_SIZE 0x1000
#define FIRST_FLAGS (IPC_EXCL|IPC_CREAT|SHM_R|SHM_W)
#define NEXT_FLAGS (IPC_EXCL|SHM_R|SHM_W)
#define PROT (PROT_READ|PROT_WRITE)
#define FLAGS (MAP_FIXED|MAP_SHARED)
#if !defined(MAP_FAILED)
#define MAP_FAILED (void *) -1
#endif
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*
* This gets a unique number. Since it takes CPU cycles to call this
* thing, it will be different for every caller.
*/
static __inline__ unsigned int unique()
{
int ret;
__asm__ __volatile__( "rdtsc\n"
: "=eax" (ret)
: /* No inputs */
: "edx" );
return ret;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static __inline__ void flush()
{
__asm__ __volatile__("pushl %cs\n"
"\tpushl $1f\n"
"\tretl\n"
"\t1:\n"
);
}
#define ERRORS(s) \
{ \
fprintf(stderr, "Error from line %d, file %s, call %s() (%s)\n", \
__LINE__,__FILE__,s,strerror(errno)); \
exit(EXIT_FAILURE); \
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*
* This initialzes one page of shared memory. If it doesn't exist,
* it is created.
*/
void init_shmem(PARS **pars)
{
int i;
if(*pars == NULL)
{
if((i = shmget(KEY, PAGE_SIZE, FIRST_FLAGS)) < 0)
if((i = shmget(KEY, PAGE_SIZE, NEXT_FLAGS)) < 0)
ERRORS("shmget");
if((*pars = (PARS *) shmat(i, NULL, 0)) == MAP_FAILED)
ERRORS("shmat");
}
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*
* This detaches a shared memory segment.
*/
void quit_shmem(PARS **pars)
{
if(shmdt((const void *) *pars) < 0)
ERRORS("shmdt");
*pars = NULL;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
int main()
{
PARS *pars = NULL;
unsigned int key;
init_shmem(&pars);
iopl(3);
memset(pars, 0x00, PAGE_SIZE);
fprintf(stderr, "Spin = %u\n", pars->spin);
fprintf(stderr, "J = %u\n", pars->j);
fprintf(stderr, "I = %u\n", pars->i);
switch(fork())
{
case 0:
quit_shmem(&pars);
init_shmem(&pars);
if (fork()) {
while (1) {
printf("%08X\r", pars->spin);
flush(stdout);
}
}
fprintf(stderr, "CHILD\n");
fprintf(stderr, "Spin = %u\n", pars->spin);
fprintf(stderr, "J = %u\n", pars->j);
fprintf(stderr, "I = %u\n", pars->i);
key = unique();
for(;;)
{
#ifdef SPIN
spin_lock(&pars->spin);
#else
unsigned int readval;
while (atomic_read(&pars->spin)) key = unique();
atomic_add(key, &pars->spin);
if ((readval = atomic_read(&pars->spin)) != key) {
//flush();
atomic_sub(key, &pars->spin);
fprintf(stderr, " Child spinning with %u\n", readval);
fprintf(stderr, " Key value was %u\n", key);
fprintf(stderr, "Pad0 = %u\n", pars->pad0);
fprintf(stderr, "Pad1 = %u\n", pars->pad1);
fprintf(stderr, "Pad2 = %u\n", pars->pad2);
fprintf(stderr, "Pad3 = %u\n", pars->pad3);
usleep(rand() % 10000);
continue;
}
#endif
if(pars->i < pars->j)
fprintf(stderr, "i = %08x, j = %08x\n", pars->i, pars->j);
else
fprintf(stderr, "Good\n");
#ifdef SPIN
spin_unlock(&pars->spin);
#else
atomic_sub(key, &pars->spin);
#endif
}
case -1:
ERRORS("fork");
default:
fprintf(stderr, "PARENT\n");
fprintf(stderr, "Spin = %u\n", pars->spin);
fprintf(stderr, "J = %u\n", pars->j);
fprintf(stderr, "I = %u\n", pars->i);
key = unique();
for(;;)
{
#ifdef SPIN
spin_lock(&pars->spin);
#else
unsigned int readval;
while (atomic_read(&pars->spin)) key = unique();
atomic_add(key, &pars->spin);
if((readval = atomic_read(&pars->spin)) != key)
{
//flush();
atomic_sub(key, &pars->spin);
fprintf(stderr, "Parent spinning with %u\n", readval);
fprintf(stderr, " Key value was %u\n", key);
usleep(rand() % 10000);
continue;
}
#endif
pars->i++;
pars->j++;
#ifdef SPIN
spin_unlock(&pars->spin);
#else
atomic_sub(key, &pars->spin);
#endif
}
}
return 0;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/