xref: /dragonfly/test/debug/umtx.c (revision c37c9ab3)
1 /*
2  * UMTX file[:offset] command
3  *
4  * $DragonFly: src/test/debug/umtx.c,v 1.1 2005/01/14 04:15:12 dillon Exp $
5  */
6 
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <sys/mman.h>
10 #include <sys/stat.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <assert.h>
18 #include <signal.h>
19 
20 int cmp_and_exg(volatile int *lockp, int old, int new);
21 
22 struct umtx {
23     volatile int lock;
24 };
25 
26 #define MTX_LOCKED	0x80000000
27 
28 static int userland_get_mutex(struct umtx *mtx, int timo);
29 static int userland_get_mutex_contested(struct umtx *mtx, int timo);
30 static void userland_rel_mutex(struct umtx *mtx);
31 static void userland_rel_mutex_contested(struct umtx *mtx);
32 static void docleanup(int signo);
33 
34 static struct umtx *cleanup_mtx_contested;
35 static struct umtx *cleanup_mtx_held;
36 
37 int verbose_opt;
38 
39 int
40 main(int ac, char **av)
41 {
42     char *path;
43     char *str;
44     off_t off = 0;
45     pid_t pid;
46     int ch;
47     int fd;
48     int pgsize;
49     int pgmask;
50     int timo = 0;
51     struct stat st;
52     struct umtx *mtx;
53 
54     signal(SIGINT, docleanup);
55 
56     while ((ch = getopt(ac, av, "t:v")) != -1) {
57 	switch(ch) {
58 	case 't':
59 	    timo = strtol(optarg, NULL, 0);
60 	    break;
61 	case 'v':
62 	    verbose_opt = 1;
63 	    break;
64 	default:
65 	    fprintf(stderr, "unknown option: -%c\n", optopt);
66 	    exit(1);
67 	}
68     }
69     ac -= optind;
70     av += optind;
71 
72     if (ac < 2) {
73 	fprintf(stderr, "umtx file[:offset] command\n");
74 	exit(1);
75     }
76     path = av[0];
77     if ((str = strchr(path, ':')) != NULL) {
78 	*str++ = 0;
79 	off = strtoull(str, NULL, 0);
80     }
81     if ((fd = open(path, O_RDWR|O_CREAT, 0666)) < 0) {
82 	perror("open");
83 	exit(1);
84     }
85     if (fstat(fd, &st) < 0) {
86 	perror("fstat");
87 	exit(1);
88     }
89     if (off + 4 > st.st_size) {
90 	int v = 0;
91 	lseek(fd, off, 0);
92 	write(fd, &v, sizeof(v));
93     }
94     pgsize = getpagesize();
95     pgmask = pgsize - 1;
96     str = mmap(NULL, pgsize, PROT_READ|PROT_WRITE, MAP_SHARED,
97 		fd, off & ~(off_t)pgmask);
98     mtx = (struct umtx *)(str + ((int)off & pgmask));
99     if (userland_get_mutex(mtx, timo) < 0) {
100 	fprintf(stderr, "Mutex at %s:%ld timed out\n", path, off);
101 	exit(1);
102     }
103     if (verbose_opt)
104 	fprintf(stderr, "Obtained mutex at %s:%ld\n", path, off);
105     if ((pid = fork()) == 0) {
106 	execvp(av[1], av + 1);
107 	_exit(0);
108     } else if (pid > 0) {
109 	while (waitpid(pid, NULL, 0) != pid)
110 	    ;
111     } else {
112 	fprintf(stderr, "Unable to exec %s\n", av[1]);
113     }
114     userland_rel_mutex(mtx);
115     close(fd);
116     return(0);
117 }
118 
119 static int
120 userland_get_mutex(struct umtx *mtx, int timo)
121 {
122     int v;
123 
124     for (;;) {
125 	v = mtx->lock;
126 	if ((v & MTX_LOCKED) == 0) {
127 	    /*
128 	     * not locked, attempt to lock.
129 	     */
130 	    if (cmp_and_exg(&mtx->lock, v, v | MTX_LOCKED) == 0) {
131 		cleanup_mtx_held = mtx;
132 		return(0);
133 	    }
134 	} else {
135 	    /*
136 	     * Locked, bump the contested count and obtain the contested
137 	     * mutex.
138 	     */
139 	    if (cmp_and_exg(&mtx->lock, v, v + 1) == 0) {
140 		cleanup_mtx_contested = mtx;
141 		return(userland_get_mutex_contested(mtx, timo));
142 	    }
143 	}
144     }
145 }
146 
147 static int
148 userland_get_mutex_contested(struct umtx *mtx, int timo)
149 {
150     int v;
151 
152     for (;;) {
153 	v = mtx->lock;
154 	assert(v & ~MTX_LOCKED);	/* our contesting count still there */
155 	if ((v & MTX_LOCKED) == 0) {
156 	    /*
157 	     * not locked, attempt to remove our contested count and
158 	     * lock at the same time.
159 	     */
160 	    if (cmp_and_exg(&mtx->lock, v, (v - 1) | MTX_LOCKED) == 0) {
161 		cleanup_mtx_contested = NULL;
162 		cleanup_mtx_held = mtx;
163 		return(0);
164 	    }
165 	} else {
166 	    /*
167 	     * Still locked, sleep and try again.
168 	     */
169 	    if (verbose_opt)
170 		fprintf(stderr, "waiting on mutex timeout=%d\n", timo);
171 	    if (timo == 0) {
172 		umtx_sleep(&mtx->lock, v, 0);
173 	    } else {
174 		if (umtx_sleep(&mtx->lock, v, 1000000) < 0) {
175 		    if (errno == EAGAIN && --timo == 0) {
176 			cleanup_mtx_contested = NULL;
177 			userland_rel_mutex_contested(mtx);
178 			return(-1);
179 		    }
180 		}
181 	    }
182 	}
183     }
184 }
185 
186 static void
187 userland_rel_mutex(struct umtx *mtx)
188 {
189     int v;
190 
191     for (;;) {
192 	v = mtx->lock;
193 	assert(v & MTX_LOCKED);	/* we still have it locked */
194 	if (v == MTX_LOCKED) {
195 	    /*
196 	     * We hold an uncontested lock, try to set to an unlocked
197 	     * state.
198 	     */
199 	    if (cmp_and_exg(&mtx->lock, MTX_LOCKED, 0) == 0) {
200 		if (verbose_opt)
201 		    fprintf(stderr, "releasing uncontested mutex\n");
202 		return;
203 	    }
204 	} else {
205 	    /*
206 	     * We hold a contested lock, unlock and wakeup exactly
207 	     * one sleeper.  It is possible for this to race a new
208 	     * thread obtaining a lock, in which case any contested
209 	     * sleeper we wake up will simply go back to sleep.
210 	     */
211 	    if (cmp_and_exg(&mtx->lock, v, v & ~MTX_LOCKED) == 0) {
212 		umtx_wakeup(&mtx->lock, 1);
213 		if (verbose_opt)
214 		    fprintf(stderr, "releasing contested mutex\n");
215 		return;
216 	    }
217 	}
218     }
219 }
220 
221 static void
222 userland_rel_mutex_contested(struct umtx *mtx)
223 {
224     int v;
225 
226     for (;;) {
227 	if (cmp_and_exg(&mtx->lock, v, v - 1) == 0)
228 	    return;
229 	v = mtx->lock;
230 	assert(v & ~MTX_LOCKED);
231     }
232 }
233 
234 static void
235 docleanup(int signo)
236 {
237     printf("cleanup\n");
238     if (cleanup_mtx_contested)
239 	userland_rel_mutex_contested(cleanup_mtx_contested);
240     if (cleanup_mtx_held)
241 	userland_rel_mutex(cleanup_mtx_held);
242     exit(1);
243 }
244 
245 __asm(
246 	"		.text\n"
247 	"cmp_and_exg:\n"
248 	"		movl 4(%esp),%ebx\n"
249 	"		movl 8(%esp),%eax\n"
250 	"		movl 12(%esp),%edx\n"
251 	"		lock cmpxchgl %edx,(%ebx)\n"
252 	"		jz 1f\n"
253 	"		movl $-1,%eax\n"
254 	"		ret\n"
255 	"1:\n"
256 	"		subl %eax,%eax\n"
257 	"		ret\n"
258 );
259 
260