1 /*
2  * Copyright (c) 2007, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <sys/statvfs.h>
36 #include <sys/stat.h>
37 #include <sys/ioctl.h>
38 
39 #include "blk.h"
40 #include "tapdisk.h"
41 #include "tapdisk-driver.h"
42 #include "tapdisk-interface.h"
43 
44 #define MAX_AIO_REQS         TAPDISK_DATA_REQUESTS
45 
46 struct tdaio_state;
47 
48 struct aio_request {
49 	td_request_t         treq;
50 	struct tiocb         tiocb;
51 	struct tdaio_state  *state;
52 };
53 
54 struct tdaio_state {
55 	int                  fd;
56 	td_driver_t         *driver;
57 
58 	int                  aio_free_count;
59 	struct aio_request   aio_requests[MAX_AIO_REQS];
60 	struct aio_request  *aio_free_list[MAX_AIO_REQS];
61 };
62 
63 /*Get Image size, secsize*/
tdaio_get_image_info(int fd,td_disk_info_t * info)64 static int tdaio_get_image_info(int fd, td_disk_info_t *info)
65 {
66 	int ret;
67 	long size;
68 	unsigned long total_size;
69 	struct statvfs statBuf;
70 	struct stat stat;
71 
72 	ret = fstat(fd, &stat);
73 	if (ret != 0) {
74 		DPRINTF("ERROR: fstat failed, Couldn't stat image");
75 		return -EINVAL;
76 	}
77 
78 	if (S_ISBLK(stat.st_mode)) {
79 		/*Accessing block device directly*/
80 		info->size = 0;
81 		if (blk_getimagesize(fd, &info->size) != 0)
82 			return -EINVAL;
83 
84 		DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
85 			"sector_shift [%llu]\n",
86 			(long long unsigned)(info->size << SECTOR_SHIFT),
87 			(long long unsigned)info->size);
88 
89 		/*Get the sector size*/
90 		if (blk_getsectorsize(fd, &info->sector_size) != 0)
91 			info->sector_size = DEFAULT_SECTOR_SIZE;
92 
93 	} else {
94 		/*Local file? try fstat instead*/
95 		info->size = (stat.st_size >> SECTOR_SHIFT);
96 		info->sector_size = DEFAULT_SECTOR_SIZE;
97 		DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
98 			"sector_shift [%llu]\n",
99 			(long long unsigned)(info->size << SECTOR_SHIFT),
100 			(long long unsigned)info->size);
101 	}
102 
103 	if (info->size == 0) {
104 		info->size =((uint64_t) 16836057);
105 		info->sector_size = DEFAULT_SECTOR_SIZE;
106 	}
107 	info->info = 0;
108 
109 	return 0;
110 }
111 
112 /* Open the disk file and initialize aio state. */
tdaio_open(td_driver_t * driver,const char * name,td_flag_t flags)113 int tdaio_open(td_driver_t *driver, const char *name, td_flag_t flags)
114 {
115 	int i, fd, ret, o_flags;
116 	struct tdaio_state *prv;
117 
118 	ret = 0;
119 	prv = (struct tdaio_state *)driver->data;
120 
121 	DPRINTF("block-aio open('%s')", name);
122 
123 	memset(prv, 0, sizeof(struct tdaio_state));
124 
125 	prv->aio_free_count = MAX_AIO_REQS;
126 	for (i = 0; i < MAX_AIO_REQS; i++)
127 		prv->aio_free_list[i] = &prv->aio_requests[i];
128 
129 	/* Open the file */
130 	o_flags = O_DIRECT | O_LARGEFILE |
131 		((flags & TD_OPEN_RDONLY) ? O_RDONLY : O_RDWR);
132         fd = open(name, o_flags);
133 
134         if ( (fd == -1) && (errno == EINVAL) ) {
135 
136                 /* Maybe O_DIRECT isn't supported. */
137 		o_flags &= ~O_DIRECT;
138                 fd = open(name, o_flags);
139                 if (fd != -1) DPRINTF("WARNING: Accessing image without"
140                                      "O_DIRECT! (%s)\n", name);
141 
142         } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name);
143 
144         if (fd == -1) {
145 		DPRINTF("Unable to open [%s] (%d)!\n", name, 0 - errno);
146         	ret = 0 - errno;
147         	goto done;
148         }
149 
150 	ret = tdaio_get_image_info(fd, &driver->info);
151 	if (ret) {
152 		close(fd);
153 		goto done;
154 	}
155 
156         prv->fd = fd;
157 
158 done:
159 	return ret;
160 }
161 
tdaio_complete(void * arg,struct tiocb * tiocb,int err)162 void tdaio_complete(void *arg, struct tiocb *tiocb, int err)
163 {
164 	struct aio_request *aio = (struct aio_request *)arg;
165 	struct tdaio_state *prv = aio->state;
166 
167 	td_complete_request(aio->treq, err);
168 	prv->aio_free_list[prv->aio_free_count++] = aio;
169 }
170 
tdaio_queue_read(td_driver_t * driver,td_request_t treq)171 void tdaio_queue_read(td_driver_t *driver, td_request_t treq)
172 {
173 	int size;
174 	uint64_t offset;
175 	struct aio_request *aio;
176 	struct tdaio_state *prv;
177 
178 	prv    = (struct tdaio_state *)driver->data;
179 	size   = treq.secs * driver->info.sector_size;
180 	offset = treq.sec  * (uint64_t)driver->info.sector_size;
181 
182 	if (prv->aio_free_count == 0)
183 		goto fail;
184 
185 	aio        = prv->aio_free_list[--prv->aio_free_count];
186 	aio->treq  = treq;
187 	aio->state = prv;
188 
189 	td_prep_read(&aio->tiocb, prv->fd, treq.buf,
190 		     size, offset, tdaio_complete, aio);
191 	td_queue_tiocb(driver, &aio->tiocb);
192 
193 	return;
194 
195 fail:
196 	td_complete_request(treq, -EBUSY);
197 }
198 
tdaio_queue_write(td_driver_t * driver,td_request_t treq)199 void tdaio_queue_write(td_driver_t *driver, td_request_t treq)
200 {
201 	int size;
202 	uint64_t offset;
203 	struct aio_request *aio;
204 	struct tdaio_state *prv;
205 
206 	prv     = (struct tdaio_state *)driver->data;
207 	size    = treq.secs * driver->info.sector_size;
208 	offset  = treq.sec  * (uint64_t)driver->info.sector_size;
209 
210 	if (prv->aio_free_count == 0)
211 		goto fail;
212 
213 	aio        = prv->aio_free_list[--prv->aio_free_count];
214 	aio->treq  = treq;
215 	aio->state = prv;
216 
217 	td_prep_write(&aio->tiocb, prv->fd, treq.buf,
218 		      size, offset, tdaio_complete, aio);
219 	td_queue_tiocb(driver, &aio->tiocb);
220 
221 	return;
222 
223 fail:
224 	td_complete_request(treq, -EBUSY);
225 }
226 
tdaio_close(td_driver_t * driver)227 int tdaio_close(td_driver_t *driver)
228 {
229 	struct tdaio_state *prv = (struct tdaio_state *)driver->data;
230 
231 	close(prv->fd);
232 
233 	return 0;
234 }
235 
tdaio_get_parent_id(td_driver_t * driver,td_disk_id_t * id)236 int tdaio_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
237 {
238 	return TD_NO_PARENT;
239 }
240 
tdaio_validate_parent(td_driver_t * driver,td_driver_t * pdriver,td_flag_t flags)241 int tdaio_validate_parent(td_driver_t *driver,
242 			  td_driver_t *pdriver, td_flag_t flags)
243 {
244 	return -EINVAL;
245 }
246 
247 struct tap_disk tapdisk_aio = {
248 	.disk_type          = "tapdisk_aio",
249 	.flags              = 0,
250 	.private_data_size  = sizeof(struct tdaio_state),
251 	.td_open            = tdaio_open,
252 	.td_close           = tdaio_close,
253 	.td_queue_read      = tdaio_queue_read,
254 	.td_queue_write     = tdaio_queue_write,
255 	.td_get_parent_id   = tdaio_get_parent_id,
256 	.td_validate_parent = tdaio_validate_parent,
257 	.td_debug           = NULL,
258 };
259