]> Git Repo - qemu.git/blob - block/nbd.c
block: take lock around bdrv_write implementations
[qemu.git] / block / nbd.c
1 /*
2  * QEMU Block driver for  NBD
3  *
4  * Copyright (C) 2008 Bull S.A.S.
5  *     Author: Laurent Vivier <[email protected]>
6  *
7  * Some parts:
8  *    Copyright (C) 2007 Anthony Liguori <[email protected]>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  */
28
29 #include "qemu-common.h"
30 #include "nbd.h"
31 #include "block_int.h"
32 #include "module.h"
33 #include "qemu_socket.h"
34
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 #define EN_OPTSTR ":exportname="
39
40 /* #define DEBUG_NBD */
41
42 #if defined(DEBUG_NBD)
43 #define logout(fmt, ...) \
44                 fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
45 #else
46 #define logout(fmt, ...) ((void)0)
47 #endif
48
49 typedef struct BDRVNBDState {
50     CoMutex lock;
51     int sock;
52     uint32_t nbdflags;
53     off_t size;
54     size_t blocksize;
55     char *export_name; /* An NBD server may export several devices */
56
57     /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
58      * it's a string of the form <hostname|ip4|\[ip6\]>:port
59      */
60     char *host_spec;
61 } BDRVNBDState;
62
63 static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
64 {
65     char *file;
66     char *export_name;
67     const char *host_spec;
68     const char *unixpath;
69     int err = -EINVAL;
70
71     file = g_strdup(filename);
72
73     export_name = strstr(file, EN_OPTSTR);
74     if (export_name) {
75         if (export_name[strlen(EN_OPTSTR)] == 0) {
76             goto out;
77         }
78         export_name[0] = 0; /* truncate 'file' */
79         export_name += strlen(EN_OPTSTR);
80         s->export_name = g_strdup(export_name);
81     }
82
83     /* extract the host_spec - fail if it's not nbd:... */
84     if (!strstart(file, "nbd:", &host_spec)) {
85         goto out;
86     }
87
88     /* are we a UNIX or TCP socket? */
89     if (strstart(host_spec, "unix:", &unixpath)) {
90         if (unixpath[0] != '/') { /* We demand  an absolute path*/
91             goto out;
92         }
93         s->host_spec = g_strdup(unixpath);
94     } else {
95         s->host_spec = g_strdup(host_spec);
96     }
97
98     err = 0;
99
100 out:
101     g_free(file);
102     if (err != 0) {
103         g_free(s->export_name);
104         g_free(s->host_spec);
105     }
106     return err;
107 }
108
109 static int nbd_establish_connection(BlockDriverState *bs)
110 {
111     BDRVNBDState *s = bs->opaque;
112     int sock;
113     int ret;
114     off_t size;
115     size_t blocksize;
116
117     if (s->host_spec[0] == '/') {
118         sock = unix_socket_outgoing(s->host_spec);
119     } else {
120         sock = tcp_socket_outgoing_spec(s->host_spec);
121     }
122
123     /* Failed to establish connection */
124     if (sock == -1) {
125         logout("Failed to establish connection to NBD server\n");
126         return -errno;
127     }
128
129     /* NBD handshake */
130     ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
131                                 &blocksize);
132     if (ret == -1) {
133         logout("Failed to negotiate with the NBD server\n");
134         closesocket(sock);
135         return -errno;
136     }
137
138     /* Now that we're connected, set the socket to be non-blocking */
139     socket_set_nonblock(sock);
140
141     s->sock = sock;
142     s->size = size;
143     s->blocksize = blocksize;
144
145     logout("Established connection with NBD server\n");
146     return 0;
147 }
148
149 static void nbd_teardown_connection(BlockDriverState *bs)
150 {
151     BDRVNBDState *s = bs->opaque;
152     struct nbd_request request;
153
154     request.type = NBD_CMD_DISC;
155     request.handle = (uint64_t)(intptr_t)bs;
156     request.from = 0;
157     request.len = 0;
158     nbd_send_request(s->sock, &request);
159
160     closesocket(s->sock);
161 }
162
163 static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
164 {
165     BDRVNBDState *s = bs->opaque;
166     int result;
167
168     /* Pop the config into our state object. Exit if invalid. */
169     result = nbd_config(s, filename, flags);
170     if (result != 0) {
171         return result;
172     }
173
174     /* establish TCP connection, return error if it fails
175      * TODO: Configurable retry-until-timeout behaviour.
176      */
177     result = nbd_establish_connection(bs);
178
179     qemu_co_mutex_init(&s->lock);
180     return result;
181 }
182
183 static int nbd_read(BlockDriverState *bs, int64_t sector_num,
184                     uint8_t *buf, int nb_sectors)
185 {
186     BDRVNBDState *s = bs->opaque;
187     struct nbd_request request;
188     struct nbd_reply reply;
189
190     request.type = NBD_CMD_READ;
191     request.handle = (uint64_t)(intptr_t)bs;
192     request.from = sector_num * 512;;
193     request.len = nb_sectors * 512;
194
195     if (nbd_send_request(s->sock, &request) == -1)
196         return -errno;
197
198     if (nbd_receive_reply(s->sock, &reply) == -1)
199         return -errno;
200
201     if (reply.error !=0)
202         return -reply.error;
203
204     if (reply.handle != request.handle)
205         return -EIO;
206
207     if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
208         return -EIO;
209
210     return 0;
211 }
212
213 static int nbd_write(BlockDriverState *bs, int64_t sector_num,
214                      const uint8_t *buf, int nb_sectors)
215 {
216     BDRVNBDState *s = bs->opaque;
217     struct nbd_request request;
218     struct nbd_reply reply;
219
220     request.type = NBD_CMD_WRITE;
221     request.handle = (uint64_t)(intptr_t)bs;
222     request.from = sector_num * 512;;
223     request.len = nb_sectors * 512;
224
225     if (nbd_send_request(s->sock, &request) == -1)
226         return -errno;
227
228     if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
229         return -EIO;
230
231     if (nbd_receive_reply(s->sock, &reply) == -1)
232         return -errno;
233
234     if (reply.error !=0)
235         return -reply.error;
236
237     if (reply.handle != request.handle)
238         return -EIO;
239
240     return 0;
241 }
242
243 static coroutine_fn int nbd_co_read(BlockDriverState *bs, int64_t sector_num,
244                                     uint8_t *buf, int nb_sectors)
245 {
246     int ret;
247     BDRVNBDState *s = bs->opaque;
248     qemu_co_mutex_lock(&s->lock);
249     ret = nbd_read(bs, sector_num, buf, nb_sectors);
250     qemu_co_mutex_unlock(&s->lock);
251     return ret;
252 }
253
254 static coroutine_fn int nbd_co_write(BlockDriverState *bs, int64_t sector_num,
255                                      const uint8_t *buf, int nb_sectors)
256 {
257     int ret;
258     BDRVNBDState *s = bs->opaque;
259     qemu_co_mutex_lock(&s->lock);
260     ret = nbd_write(bs, sector_num, buf, nb_sectors);
261     qemu_co_mutex_unlock(&s->lock);
262     return ret;
263 }
264
265 static void nbd_close(BlockDriverState *bs)
266 {
267     BDRVNBDState *s = bs->opaque;
268     g_free(s->export_name);
269     g_free(s->host_spec);
270
271     nbd_teardown_connection(bs);
272 }
273
274 static int64_t nbd_getlength(BlockDriverState *bs)
275 {
276     BDRVNBDState *s = bs->opaque;
277
278     return s->size;
279 }
280
281 static BlockDriver bdrv_nbd = {
282     .format_name        = "nbd",
283     .instance_size      = sizeof(BDRVNBDState),
284     .bdrv_file_open     = nbd_open,
285     .bdrv_read          = nbd_co_read,
286     .bdrv_write         = nbd_co_write,
287     .bdrv_close         = nbd_close,
288     .bdrv_getlength     = nbd_getlength,
289     .protocol_name      = "nbd",
290 };
291
292 static void bdrv_nbd_init(void)
293 {
294     bdrv_register(&bdrv_nbd);
295 }
296
297 block_init(bdrv_nbd_init);
This page took 0.040583 seconds and 4 git commands to generate.