| 1 | #include <aio.h> |
| 2 | #include <errno.h> |
| 3 | #include <unistd.h> |
| 4 | #include <string.h> |
| 5 | #include "pthread_impl.h" |
| 6 | |
| 7 | struct lio_state { |
| 8 | 	struct sigevent *sev; |
| 9 | 	int cnt; |
| 10 | 	struct aiocb *cbs[]; |
| 11 | }; |
| 12 | |
| 13 | static int lio_wait(struct lio_state *st) |
| 14 | { |
| 15 | 	int i, err, got_err = 0; |
| 16 | 	int cnt = st->cnt; |
| 17 | 	struct aiocb **cbs = st->cbs; |
| 18 | |
| 19 | 	for (;;) { |
| 20 | 		for (i=0; i<cnt; i++) { |
| 21 | 			if (!cbs[i]) continue; |
| 22 | 			err = aio_error(cbs[i]); |
| 23 | 			if (err==EINPROGRESS) |
| 24 | 				break; |
| 25 | 			if (err) got_err=1; |
| 26 | 			cbs[i] = 0; |
| 27 | 		} |
| 28 | 		if (i==cnt) { |
| 29 | 			if (got_err) { |
| 30 | 				errno = EIO; |
| 31 | 				return -1; |
| 32 | 			} |
| 33 | 			return 0; |
| 34 | 		} |
| 35 | 		if (aio_suspend((void *)cbs, cnt, 0)) |
| 36 | 			return -1; |
| 37 | 	} |
| 38 | } |
| 39 | |
| 40 | static void notify_signal(struct sigevent *sev) |
| 41 | { |
| 42 | 	siginfo_t si = { |
| 43 | 		.si_signo = sev->sigev_signo, |
| 44 | 		.si_value = sev->sigev_value, |
| 45 | 		.si_code = SI_ASYNCIO, |
| 46 | 		.si_pid = getpid(), |
| 47 | 		.si_uid = getuid() |
| 48 | 	}; |
| 49 | 	__syscall(SYS_rt_sigqueueinfo, si.si_pid, si.si_signo, &si); |
| 50 | } |
| 51 | |
| 52 | static void *wait_thread(void *p) |
| 53 | { |
| 54 | 	struct lio_state *st = p; |
| 55 | 	struct sigevent *sev = st->sev; |
| 56 | 	lio_wait(st); |
| 57 | 	free(st); |
| 58 | 	switch (sev->sigev_notify) { |
| 59 | 	case SIGEV_SIGNAL: |
| 60 | 		notify_signal(sev); |
| 61 | 		break; |
| 62 | 	case SIGEV_THREAD: |
| 63 | 		sev->sigev_notify_function(sev->sigev_value); |
| 64 | 		break; |
| 65 | 	} |
| 66 | 	return 0; |
| 67 | } |
| 68 | |
| 69 | int lio_listio(int mode, struct aiocb *restrict const *restrict cbs, int cnt, struct sigevent *restrict sev) |
| 70 | { |
| 71 | 	int i, ret; |
| 72 | 	struct lio_state *st=0; |
| 73 | |
| 74 | 	if (cnt < 0) { |
| 75 | 		errno = EINVAL; |
| 76 | 		return -1; |
| 77 | 	} |
| 78 | |
| 79 | 	if (mode == LIO_WAIT || (sev && sev->sigev_notify != SIGEV_NONE)) { |
| 80 | 		if (!(st = malloc(sizeof *st + cnt*sizeof *cbs))) { |
| 81 | 			errno = EAGAIN; |
| 82 | 			return -1; |
| 83 | 		} |
| 84 | 		st->cnt = cnt; |
| 85 | 		st->sev = sev; |
| 86 | 		memcpy(st->cbs, (void*) cbs, cnt*sizeof *cbs); |
| 87 | 	} |
| 88 | |
| 89 | 	for (i=0; i<cnt; i++) { |
| 90 | 		if (!cbs[i]) continue; |
| 91 | 		switch (cbs[i]->aio_lio_opcode) { |
| 92 | 		case LIO_READ: |
| 93 | 			ret = aio_read(cbs[i]); |
| 94 | 			break; |
| 95 | 		case LIO_WRITE: |
| 96 | 			ret = aio_write(cbs[i]); |
| 97 | 			break; |
| 98 | 		default: |
| 99 | 			continue; |
| 100 | 		} |
| 101 | 		if (ret) { |
| 102 | 			free(st); |
| 103 | 			errno = EAGAIN; |
| 104 | 			return -1; |
| 105 | 		} |
| 106 | 	} |
| 107 | |
| 108 | 	if (mode == LIO_WAIT) { |
| 109 | 		ret = lio_wait(st); |
| 110 | 		free(st); |
| 111 | 		return ret; |
| 112 | 	} |
| 113 | |
| 114 | 	if (st) { |
| 115 | 		pthread_attr_t a; |
| 116 | 		sigset_t set, set_old; |
| 117 | 		pthread_t td; |
| 118 | |
| 119 | 		if (sev->sigev_notify == SIGEV_THREAD) { |
| 120 | 			if (sev->sigev_notify_attributes) |
| 121 | 				a = *sev->sigev_notify_attributes; |
| 122 | 			else |
| 123 | 				pthread_attr_init(&a); |
| 124 | 		} else { |
| 125 | 			pthread_attr_init(&a); |
| 126 | 			pthread_attr_setstacksize(&a, PAGE_SIZE); |
| 127 | 			pthread_attr_setguardsize(&a, 0); |
| 128 | 		} |
| 129 | 		pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); |
| 130 | 		sigfillset(&set); |
| 131 | 		pthread_sigmask(SIG_BLOCK, &set, &set_old); |
| 132 | 		if (pthread_create(&td, &a, wait_thread, st)) { |
| 133 | 			free(st); |
| 134 | 			errno = EAGAIN; |
| 135 | 			return -1; |
| 136 | 		} |
| 137 | 		pthread_sigmask(SIG_SETMASK, &set_old, 0); |
| 138 | 	} |
| 139 | |
| 140 | 	return 0; |
| 141 | } |