1 /*
2  * Native Solaris async IO engine
3  *
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <signal.h>
9 #include <errno.h>
10 
11 #include "../fio.h"
12 
13 #include <sys/asynch.h>
14 
15 struct solarisaio_data {
16 	struct io_u **aio_events;
17 	unsigned int aio_pending;
18 	unsigned int nr;
19 	unsigned int max_depth;
20 };
21 
fio_solarisaio_cancel(struct thread_data fio_unused * td,struct io_u * io_u)22 static int fio_solarisaio_cancel(struct thread_data fio_unused *td,
23 			       struct io_u *io_u)
24 {
25 	return aiocancel(&io_u->resultp);
26 }
27 
fio_solarisaio_prep(struct thread_data fio_unused * td,struct io_u * io_u)28 static int fio_solarisaio_prep(struct thread_data fio_unused *td,
29 			    struct io_u *io_u)
30 {
31 	struct solarisaio_data *sd = td->io_ops->data;
32 
33 	io_u->resultp.aio_return = AIO_INPROGRESS;
34 	io_u->engine_data = sd;
35 	return 0;
36 }
37 
wait_for_event(struct timeval * tv)38 static void wait_for_event(struct timeval *tv)
39 {
40 	struct solarisaio_data *sd;
41 	struct io_u *io_u;
42 	aio_result_t *res;
43 
44 	res = aiowait(tv);
45 	if (res == (aio_result_t *) -1) {
46 		int err = errno;
47 
48 		if (err != EINVAL) {
49 			log_err("fio: solarisaio got %d in aiowait\n", err);
50 			exit(err);
51 		}
52 		return;
53 	} else if (!res)
54 		return;
55 
56 	io_u = container_of(res, struct io_u, resultp);
57 	sd = io_u->engine_data;
58 
59 	if (io_u->resultp.aio_return >= 0) {
60 		io_u->resid = io_u->xfer_buflen - io_u->resultp.aio_return;
61 		io_u->error = 0;
62 	} else
63 		io_u->error = io_u->resultp.aio_errno;
64 
65 	/*
66 	 * For SIGIO, we need a write barrier between the two, so that
67 	 * the ->aio_pending store is seen after the ->aio_events store
68 	 */
69 	sd->aio_events[sd->aio_pending] = io_u;
70 	write_barrier();
71 	sd->aio_pending++;
72 	sd->nr--;
73 }
74 
fio_solarisaio_getevents(struct thread_data * td,unsigned int min,unsigned int max,const struct timespec * t)75 static int fio_solarisaio_getevents(struct thread_data *td, unsigned int min,
76 				    unsigned int max, const struct timespec *t)
77 {
78 	struct solarisaio_data *sd = td->io_ops->data;
79 	struct timeval tv;
80 	int ret;
81 
82 	if (!min || !t) {
83 		tv.tv_sec = 0;
84 		tv.tv_usec = 0;
85 	} else {
86 		tv.tv_sec = t->tv_sec;
87 		tv.tv_usec = t->tv_nsec / 1000;
88 	}
89 
90 	while (sd->aio_pending < min)
91 		wait_for_event(&tv);
92 
93 	/*
94 	 * should be OK without locking, as int operations should be atomic
95 	 */
96 	ret = sd->aio_pending;
97 	sd->aio_pending -= ret;
98 	return ret;
99 }
100 
fio_solarisaio_event(struct thread_data * td,int event)101 static struct io_u *fio_solarisaio_event(struct thread_data *td, int event)
102 {
103 	struct solarisaio_data *sd = td->io_ops->data;
104 
105 	return sd->aio_events[event];
106 }
107 
fio_solarisaio_queue(struct thread_data fio_unused * td,struct io_u * io_u)108 static int fio_solarisaio_queue(struct thread_data fio_unused *td,
109 			      struct io_u *io_u)
110 {
111 	struct solarisaio_data *sd = td->io_ops->data;
112 	struct fio_file *f = io_u->file;
113 	off_t off;
114 	int ret;
115 
116 	fio_ro_check(td, io_u);
117 
118 	if (io_u->ddir == DDIR_SYNC) {
119 		if (sd->nr)
120 			return FIO_Q_BUSY;
121 		if (fsync(f->fd) < 0)
122 			io_u->error = errno;
123 
124 		return FIO_Q_COMPLETED;
125 	}
126 
127 	if (io_u->ddir == DDIR_DATASYNC) {
128 		if (sd->nr)
129 			return FIO_Q_BUSY;
130 		if (fdatasync(f->fd) < 0)
131 			io_u->error = errno;
132 
133 		return FIO_Q_COMPLETED;
134 	}
135 
136 	if (sd->nr == sd->max_depth)
137 		return FIO_Q_BUSY;
138 
139 	off = io_u->offset;
140 	if (io_u->ddir == DDIR_READ)
141 		ret = aioread(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off,
142 					SEEK_SET, &io_u->resultp);
143 	else
144 		ret = aiowrite(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off,
145 					SEEK_SET, &io_u->resultp);
146 	if (ret) {
147 		io_u->error = errno;
148 		td_verror(td, io_u->error, "xfer");
149 		return FIO_Q_COMPLETED;
150 	}
151 
152 	sd->nr++;
153 	return FIO_Q_QUEUED;
154 }
155 
fio_solarisaio_cleanup(struct thread_data * td)156 static void fio_solarisaio_cleanup(struct thread_data *td)
157 {
158 	struct solarisaio_data *sd = td->io_ops->data;
159 
160 	if (sd) {
161 		free(sd->aio_events);
162 		free(sd);
163 	}
164 }
165 
166 /*
167  * Set USE_SIGNAL_COMPLETIONS to use SIGIO as completion events.
168  */
169 #ifdef USE_SIGNAL_COMPLETIONS
fio_solarisaio_sigio(int sig)170 static void fio_solarisaio_sigio(int sig)
171 {
172 	wait_for_event(NULL);
173 }
174 
fio_solarisaio_init_sigio(void)175 static void fio_solarisaio_init_sigio(void)
176 {
177 	struct sigaction act;
178 
179 	memset(&act, 0, sizeof(act));
180 	act.sa_handler = fio_solarisaio_sigio;
181 	act.sa_flags = SA_RESTART;
182 	sigaction(SIGIO, &act, NULL);
183 }
184 #endif
185 
fio_solarisaio_init(struct thread_data * td)186 static int fio_solarisaio_init(struct thread_data *td)
187 {
188 	struct solarisaio_data *sd = malloc(sizeof(*sd));
189 	unsigned int max_depth;
190 
191 	max_depth = td->o.iodepth;
192 	if (max_depth > MAXASYNCHIO) {
193 		max_depth = MAXASYNCHIO;
194 		log_info("fio: lower depth to %d due to OS constraints\n",
195 							max_depth);
196 	}
197 
198 	memset(sd, 0, sizeof(*sd));
199 	sd->aio_events = malloc(max_depth * sizeof(struct io_u *));
200 	memset(sd->aio_events, 0, max_depth * sizeof(struct io_u *));
201 	sd->max_depth = max_depth;
202 
203 #ifdef USE_SIGNAL_COMPLETIONS
204 	fio_solarisaio_init_sigio();
205 #endif
206 
207 	td->io_ops->data = sd;
208 	return 0;
209 }
210 
211 static struct ioengine_ops ioengine = {
212 	.name		= "solarisaio",
213 	.version	= FIO_IOOPS_VERSION,
214 	.init		= fio_solarisaio_init,
215 	.prep		= fio_solarisaio_prep,
216 	.queue		= fio_solarisaio_queue,
217 	.cancel		= fio_solarisaio_cancel,
218 	.getevents	= fio_solarisaio_getevents,
219 	.event		= fio_solarisaio_event,
220 	.cleanup	= fio_solarisaio_cleanup,
221 	.open_file	= generic_open_file,
222 	.close_file	= generic_close_file,
223 	.get_file_size	= generic_get_file_size,
224 };
225 
fio_solarisaio_register(void)226 static void fio_init fio_solarisaio_register(void)
227 {
228 	register_ioengine(&ioengine);
229 }
230 
fio_solarisaio_unregister(void)231 static void fio_exit fio_solarisaio_unregister(void)
232 {
233 	unregister_ioengine(&ioengine);
234 }
235