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