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 
lio_wait(struct lio_state * st)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 
notify_signal(struct sigevent * sev)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 
wait_thread(void * p)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 
lio_listio(int mode,struct aiocb * restrict const * restrict cbs,int cnt,struct sigevent * restrict sev)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 }
142 
143 weak_alias(lio_listio, lio_listio64);
144