| 1 | #include <aio.h> |
| 2 | #include <pthread.h> |
| 3 | #include <semaphore.h> |
| 4 | #include <limits.h> |
| 5 | #include <errno.h> |
| 6 | #include <unistd.h> |
| 7 | #include <stdlib.h> |
| 8 | #include <sys/auxv.h> |
| 9 | #include "syscall.h" |
| 10 | #include "atomic.h" |
| 11 | #include "pthread_impl.h" |
| 12 | #include "aio_impl.h" |
| 13 | |
| 14 | /* The following is a threads-based implementation of AIO with minimal |
| 15 | * dependence on implementation details. Most synchronization is |
| 16 | * performed with pthread primitives, but atomics and futex operations |
| 17 | * are used for notification in a couple places where the pthread |
| 18 | * primitives would be inefficient or impractical. |
| 19 | * |
| 20 | * For each fd with outstanding aio operations, an aio_queue structure |
| 21 | * is maintained. These are reference-counted and destroyed by the last |
| 22 | * aio worker thread to exit. Accessing any member of the aio_queue |
| 23 | * structure requires a lock on the aio_queue. Adding and removing aio |
| 24 | * queues themselves requires a write lock on the global map object, |
| 25 | * a 4-level table mapping file descriptor numbers to aio queues. A |
| 26 | * read lock on the map is used to obtain locks on existing queues by |
| 27 | * excluding destruction of the queue by a different thread while it is |
| 28 | * being locked. |
| 29 | * |
| 30 | * Each aio queue has a list of active threads/operations. Presently there |
| 31 | * is a one to one relationship between threads and operations. The only |
| 32 | * members of the aio_thread structure which are accessed by other threads |
| 33 | * are the linked list pointers, op (which is immutable), running (which |
| 34 | * is updated atomically), and err (which is synchronized via running), |
| 35 | * so no locking is necessary. Most of the other other members are used |
| 36 | * for sharing data between the main flow of execution and cancellation |
| 37 | * cleanup handler. |
| 38 | * |
| 39 | * Taking any aio locks requires having all signals blocked. This is |
| 40 | * necessary because aio_cancel is needed by close, and close is required |
| 41 | * to be async-signal safe. All aio worker threads run with all signals |
| 42 | * blocked permanently. |
| 43 | */ |
| 44 | |
| 45 | struct aio_thread { |
| 46 | 	pthread_t td; |
| 47 | 	struct aiocb *cb; |
| 48 | 	struct aio_thread *next, *prev; |
| 49 | 	struct aio_queue *q; |
| 50 | 	volatile int running; |
| 51 | 	int err, op; |
| 52 | 	ssize_t ret; |
| 53 | }; |
| 54 | |
| 55 | struct aio_queue { |
| 56 | 	int fd, seekable, append, ref, init; |
| 57 | 	pthread_mutex_t lock; |
| 58 | 	pthread_cond_t cond; |
| 59 | 	struct aio_thread *head; |
| 60 | }; |
| 61 | |
| 62 | struct aio_args { |
| 63 | 	struct aiocb *cb; |
| 64 | 	struct aio_queue *q; |
| 65 | 	int op; |
| 66 | 	sem_t sem; |
| 67 | }; |
| 68 | |
| 69 | static pthread_rwlock_t maplock = PTHREAD_RWLOCK_INITIALIZER; |
| 70 | static struct aio_queue *****map; |
| 71 | static volatile int aio_fd_cnt; |
| 72 | volatile int __aio_fut; |
| 73 | |
| 74 | static size_t io_thread_stack_size; |
| 75 | |
| 76 | #define MAX(a,b) ((a)>(b) ? (a) : (b)) |
| 77 | |
| 78 | static struct aio_queue *__aio_get_queue(int fd, int need) |
| 79 | { |
| 80 | 	sigset_t allmask, origmask; |
| 81 | 	int masked = 0; |
| 82 | 	if (fd < 0) { |
| 83 | 		errno = EBADF; |
| 84 | 		return 0; |
| 85 | 	} |
| 86 | 	int a=fd>>24; |
| 87 | 	unsigned char b=fd>>16, c=fd>>8, d=fd; |
| 88 | 	struct aio_queue *q = 0; |
| 89 | 	pthread_rwlock_rdlock(&maplock); |
| 90 | 	if ((!map || !map[a] || !map[a][b] || !map[a][b][c] || !(q=map[a][b][c][d])) && need) { |
| 91 | 		pthread_rwlock_unlock(&maplock); |
| 92 | 		if (fcntl(fd, F_GETFD) < 0) return 0; |
| 93 | 		sigfillset(&allmask); |
| 94 | 		masked = 1; |
| 95 | 		pthread_sigmask(SIG_BLOCK, &allmask, &origmask); |
| 96 | 		pthread_rwlock_wrlock(&maplock); |
| 97 | 		if (!io_thread_stack_size) { |
| 98 | 			unsigned long val = __getauxval(AT_MINSIGSTKSZ); |
| 99 | 			io_thread_stack_size = MAX(MINSIGSTKSZ+2048, val+512); |
| 100 | 		} |
| 101 | 		if (!map) map = calloc(sizeof *map, (-1U/2+1)>>24); |
| 102 | 		if (!map) goto out; |
| 103 | 		if (!map[a]) map[a] = calloc(sizeof **map, 256); |
| 104 | 		if (!map[a]) goto out; |
| 105 | 		if (!map[a][b]) map[a][b] = calloc(sizeof ***map, 256); |
| 106 | 		if (!map[a][b]) goto out; |
| 107 | 		if (!map[a][b][c]) map[a][b][c] = calloc(sizeof ****map, 256); |
| 108 | 		if (!map[a][b][c]) goto out; |
| 109 | 		if (!(q = map[a][b][c][d])) { |
| 110 | 			map[a][b][c][d] = q = calloc(sizeof *****map, 1); |
| 111 | 			if (q) { |
| 112 | 				q->fd = fd; |
| 113 | 				pthread_mutex_init(&q->lock, 0); |
| 114 | 				pthread_cond_init(&q->cond, 0); |
| 115 | 				a_inc(&aio_fd_cnt); |
| 116 | 			} |
| 117 | 		} |
| 118 | 	} |
| 119 | 	if (q) pthread_mutex_lock(&q->lock); |
| 120 | out: |
| 121 | 	pthread_rwlock_unlock(&maplock); |
| 122 | 	if (masked) pthread_sigmask(SIG_SETMASK, &origmask, 0); |
| 123 | 	return q; |
| 124 | } |
| 125 | |
| 126 | static void __aio_unref_queue(struct aio_queue *q) |
| 127 | { |
| 128 | 	if (q->ref > 1) { |
| 129 | 		q->ref--; |
| 130 | 		pthread_mutex_unlock(&q->lock); |
| 131 | 		return; |
| 132 | 	} |
| 133 | |
| 134 | 	/* This is potentially the last reference, but a new reference |
| 135 | 	 * may arrive since we cannot free the queue object without first |
| 136 | 	 * taking the maplock, which requires releasing the queue lock. */ |
| 137 | 	pthread_mutex_unlock(&q->lock); |
| 138 | 	pthread_rwlock_wrlock(&maplock); |
| 139 | 	pthread_mutex_lock(&q->lock); |
| 140 | 	if (q->ref == 1) { |
| 141 | 		int fd=q->fd; |
| 142 | 		int a=fd>>24; |
| 143 | 		unsigned char b=fd>>16, c=fd>>8, d=fd; |
| 144 | 		map[a][b][c][d] = 0; |
| 145 | 		a_dec(&aio_fd_cnt); |
| 146 | 		pthread_rwlock_unlock(&maplock); |
| 147 | 		pthread_mutex_unlock(&q->lock); |
| 148 | 		free(q); |
| 149 | 	} else { |
| 150 | 		q->ref--; |
| 151 | 		pthread_rwlock_unlock(&maplock); |
| 152 | 		pthread_mutex_unlock(&q->lock); |
| 153 | 	} |
| 154 | } |
| 155 | |
| 156 | static void cleanup(void *ctx) |
| 157 | { |
| 158 | 	struct aio_thread *at = ctx; |
| 159 | 	struct aio_queue *q = at->q; |
| 160 | 	struct aiocb *cb = at->cb; |
| 161 | 	struct sigevent sev = cb->aio_sigevent; |
| 162 | |
| 163 | 	/* There are four potential types of waiters we could need to wake: |
| 164 | 	 * 1. Callers of aio_cancel/close. |
| 165 | 	 * 2. Callers of aio_suspend with a single aiocb. |
| 166 | 	 * 3. Callers of aio_suspend with a list. |
| 167 | 	 * 4. AIO worker threads waiting for sequenced operations. |
| 168 | 	 * Types 1-3 are notified via atomics/futexes, mainly for AS-safety |
| 169 | 	 * considerations. Type 4 is notified later via a cond var. */ |
| 170 | |
| 171 | 	cb->__ret = at->ret; |
| 172 | 	if (a_swap(&at->running, 0) < 0) |
| 173 | 		__wake(&at->running, -1, 1); |
| 174 | 	if (a_swap(&cb->__err, at->err) != EINPROGRESS) |
| 175 | 		__wake(&cb->__err, -1, 1); |
| 176 | 	if (a_swap(&__aio_fut, 0)) |
| 177 | 		__wake(&__aio_fut, -1, 1); |
| 178 | |
| 179 | 	pthread_mutex_lock(&q->lock); |
| 180 | |
| 181 | 	if (at->next) at->next->prev = at->prev; |
| 182 | 	if (at->prev) at->prev->next = at->next; |
| 183 | 	else q->head = at->next; |
| 184 | |
| 185 | 	/* Signal aio worker threads waiting for sequenced operations. */ |
| 186 | 	pthread_cond_broadcast(&q->cond); |
| 187 | |
| 188 | 	__aio_unref_queue(q); |
| 189 | |
| 190 | 	if (sev.sigev_notify == SIGEV_SIGNAL) { |
| 191 | 		siginfo_t si = { |
| 192 | 			.si_signo = sev.sigev_signo, |
| 193 | 			.si_value = sev.sigev_value, |
| 194 | 			.si_code = SI_ASYNCIO, |
| 195 | 			.si_pid = getpid(), |
| 196 | 			.si_uid = getuid() |
| 197 | 		}; |
| 198 | 		__syscall(SYS_rt_sigqueueinfo, si.si_pid, si.si_signo, &si); |
| 199 | 	} |
| 200 | 	if (sev.sigev_notify == SIGEV_THREAD) { |
| 201 | 		a_store(&__pthread_self()->cancel, 0); |
| 202 | 		sev.sigev_notify_function(sev.sigev_value); |
| 203 | 	} |
| 204 | } |
| 205 | |
| 206 | static void *io_thread_func(void *ctx) |
| 207 | { |
| 208 | 	struct aio_thread at, *p; |
| 209 | |
| 210 | 	struct aio_args *args = ctx; |
| 211 | 	struct aiocb *cb = args->cb; |
| 212 | 	int fd = cb->aio_fildes; |
| 213 | 	int op = args->op; |
| 214 | 	void *buf = (void *)cb->aio_buf; |
| 215 | 	size_t len = cb->aio_nbytes; |
| 216 | 	off_t off = cb->aio_offset; |
| 217 | |
| 218 | 	struct aio_queue *q = args->q; |
| 219 | 	ssize_t ret; |
| 220 | |
| 221 | 	pthread_mutex_lock(&q->lock); |
| 222 | 	sem_post(&args->sem); |
| 223 | |
| 224 | 	at.op = op; |
| 225 | 	at.running = 1; |
| 226 | 	at.ret = -1; |
| 227 | 	at.err = ECANCELED; |
| 228 | 	at.q = q; |
| 229 | 	at.td = __pthread_self(); |
| 230 | 	at.cb = cb; |
| 231 | 	at.prev = 0; |
| 232 | 	if ((at.next = q->head)) at.next->prev = &at; |
| 233 | 	q->head = &at; |
| 234 | |
| 235 | 	if (!q->init) { |
| 236 | 		int seekable = lseek(fd, 0, SEEK_CUR) >= 0; |
| 237 | 		q->seekable = seekable; |
| 238 | 		q->append = !seekable || (fcntl(fd, F_GETFL) & O_APPEND); |
| 239 | 		q->init = 1; |
| 240 | 	} |
| 241 | |
| 242 | 	pthread_cleanup_push(cleanup, &at); |
| 243 | |
| 244 | 	/* Wait for sequenced operations. */ |
| 245 | 	if (op!=LIO_READ && (op!=LIO_WRITE || q->append)) { |
| 246 | 		for (;;) { |
| 247 | 			for (p=at.next; p && p->op!=LIO_WRITE; p=p->next); |
| 248 | 			if (!p) break; |
| 249 | 			pthread_cond_wait(&q->cond, &q->lock); |
| 250 | 		} |
| 251 | 	} |
| 252 | |
| 253 | 	pthread_mutex_unlock(&q->lock); |
| 254 | |
| 255 | 	switch (op) { |
| 256 | 	case LIO_WRITE: |
| 257 | 		ret = q->append ? write(fd, buf, len) : pwrite(fd, buf, len, off); |
| 258 | 		break; |
| 259 | 	case LIO_READ: |
| 260 | 		ret = !q->seekable ? read(fd, buf, len) : pread(fd, buf, len, off); |
| 261 | 		break; |
| 262 | 	case O_SYNC: |
| 263 | 		ret = fsync(fd); |
| 264 | 		break; |
| 265 | 	case O_DSYNC: |
| 266 | 		ret = fdatasync(fd); |
| 267 | 		break; |
| 268 | 	} |
| 269 | 	at.ret = ret; |
| 270 | 	at.err = ret<0 ? errno : 0; |
| 271 | 	 |
| 272 | 	pthread_cleanup_pop(1); |
| 273 | |
| 274 | 	return 0; |
| 275 | } |
| 276 | |
| 277 | static int submit(struct aiocb *cb, int op) |
| 278 | { |
| 279 | 	int ret = 0; |
| 280 | 	pthread_attr_t a; |
| 281 | 	sigset_t allmask, origmask; |
| 282 | 	pthread_t td; |
| 283 | 	struct aio_queue *q = __aio_get_queue(cb->aio_fildes, 1); |
| 284 | 	struct aio_args args = { .cb = cb, .op = op, .q = q }; |
| 285 | 	sem_init(&args.sem, 0, 0); |
| 286 | |
| 287 | 	if (!q) { |
| 288 | 		if (errno != EBADF) errno = EAGAIN; |
| 289 | 		cb->__ret = -1; |
| 290 | 		cb->__err = errno; |
| 291 | 		return -1; |
| 292 | 	} |
| 293 | 	q->ref++; |
| 294 | 	pthread_mutex_unlock(&q->lock); |
| 295 | |
| 296 | 	if (cb->aio_sigevent.sigev_notify == SIGEV_THREAD) { |
| 297 | 		if (cb->aio_sigevent.sigev_notify_attributes) |
| 298 | 			a = *cb->aio_sigevent.sigev_notify_attributes; |
| 299 | 		else |
| 300 | 			pthread_attr_init(&a); |
| 301 | 	} else { |
| 302 | 		pthread_attr_init(&a); |
| 303 | 		pthread_attr_setstacksize(&a, io_thread_stack_size); |
| 304 | 		pthread_attr_setguardsize(&a, 0); |
| 305 | 	} |
| 306 | 	pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); |
| 307 | 	sigfillset(&allmask); |
| 308 | 	pthread_sigmask(SIG_BLOCK, &allmask, &origmask); |
| 309 | 	cb->__err = EINPROGRESS; |
| 310 | 	if (pthread_create(&td, &a, io_thread_func, &args)) { |
| 311 | 		pthread_mutex_lock(&q->lock); |
| 312 | 		__aio_unref_queue(q); |
| 313 | 		cb->__err = errno = EAGAIN; |
| 314 | 		cb->__ret = ret = -1; |
| 315 | 	} |
| 316 | 	pthread_sigmask(SIG_SETMASK, &origmask, 0); |
| 317 | |
| 318 | 	if (!ret) { |
| 319 | 		while (sem_wait(&args.sem)); |
| 320 | 	} |
| 321 | |
| 322 | 	return ret; |
| 323 | } |
| 324 | |
| 325 | int aio_read(struct aiocb *cb) |
| 326 | { |
| 327 | 	return submit(cb, LIO_READ); |
| 328 | } |
| 329 | |
| 330 | int aio_write(struct aiocb *cb) |
| 331 | { |
| 332 | 	return submit(cb, LIO_WRITE); |
| 333 | } |
| 334 | |
| 335 | int aio_fsync(int op, struct aiocb *cb) |
| 336 | { |
| 337 | 	if (op != O_SYNC && op != O_DSYNC) { |
| 338 | 		errno = EINVAL; |
| 339 | 		return -1; |
| 340 | 	} |
| 341 | 	return submit(cb, op); |
| 342 | } |
| 343 | |
| 344 | ssize_t aio_return(struct aiocb *cb) |
| 345 | { |
| 346 | 	return cb->__ret; |
| 347 | } |
| 348 | |
| 349 | int aio_error(const struct aiocb *cb) |
| 350 | { |
| 351 | 	a_barrier(); |
| 352 | 	return cb->__err & 0x7fffffff; |
| 353 | } |
| 354 | |
| 355 | int aio_cancel(int fd, struct aiocb *cb) |
| 356 | { |
| 357 | 	sigset_t allmask, origmask; |
| 358 | 	int ret = AIO_ALLDONE; |
| 359 | 	struct aio_thread *p; |
| 360 | 	struct aio_queue *q; |
| 361 | |
| 362 | 	/* Unspecified behavior case. Report an error. */ |
| 363 | 	if (cb && fd != cb->aio_fildes) { |
| 364 | 		errno = EINVAL; |
| 365 | 		return -1; |
| 366 | 	} |
| 367 | |
| 368 | 	sigfillset(&allmask); |
| 369 | 	pthread_sigmask(SIG_BLOCK, &allmask, &origmask); |
| 370 | |
| 371 | 	errno = ENOENT; |
| 372 | 	if (!(q = __aio_get_queue(fd, 0))) { |
| 373 | 		if (errno == EBADF) ret = -1; |
| 374 | 		goto done; |
| 375 | 	} |
| 376 | |
| 377 | 	for (p = q->head; p; p = p->next) { |
| 378 | 		if (cb && cb != p->cb) continue; |
| 379 | 		/* Transition target from running to running-with-waiters */ |
| 380 | 		if (a_cas(&p->running, 1, -1)) { |
| 381 | 			pthread_cancel(p->td); |
| 382 | 			__wait(&p->running, 0, -1, 1); |
| 383 | 			if (p->err == ECANCELED) ret = AIO_CANCELED; |
| 384 | 		} |
| 385 | 	} |
| 386 | |
| 387 | 	pthread_mutex_unlock(&q->lock); |
| 388 | done: |
| 389 | 	pthread_sigmask(SIG_SETMASK, &origmask, 0); |
| 390 | 	return ret; |
| 391 | } |
| 392 | |
| 393 | int __aio_close(int fd) |
| 394 | { |
| 395 | 	a_barrier(); |
| 396 | 	if (aio_fd_cnt) aio_cancel(fd, 0); |
| 397 | 	return fd; |
| 398 | } |
| 399 | |
| 400 | void __aio_atfork(int who) |
| 401 | { |
| 402 | 	if (who<0) { |
| 403 | 		pthread_rwlock_rdlock(&maplock); |
| 404 | 		return; |
| 405 | 	} else if (!who) { |
| 406 | 		pthread_rwlock_unlock(&maplock); |
| 407 | 		return; |
| 408 | 	} |
| 409 | 	aio_fd_cnt = 0; |
| 410 | 	if (pthread_rwlock_tryrdlock(&maplock)) { |
| 411 | 		/* Obtaining lock may fail if _Fork was called nor via |
| 412 | 		 * fork. In this case, no further aio is possible from |
| 413 | 		 * child and we can just null out map so __aio_close |
| 414 | 		 * does not attempt to do anything. */ |
| 415 | 		map = 0; |
| 416 | 		return; |
| 417 | 	} |
| 418 | 	if (map) for (int a=0; a<(-1U/2+1)>>24; a++) |
| 419 | 		if (map[a]) for (int b=0; b<256; b++) |
| 420 | 			if (map[a][b]) for (int c=0; c<256; c++) |
| 421 | 				if (map[a][b][c]) for (int d=0; d<256; d++) |
| 422 | 					map[a][b][c][d] = 0; |
| 423 | 	/* Re-initialize the rwlock rather than unlocking since there |
| 424 | 	 * may have been more than one reference on it in the parent. |
| 425 | 	 * We are not a lock holder anyway; the thread in the parent was. */ |
| 426 | 	pthread_rwlock_init(&maplock, 0); |
| 427 | } |