Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


403d9d5a4c3f5aead3c44346dfa5606b7d7d1f9d
[palacios.git] / palacios / src / devices / netdisk.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <interfaces/vmm_socket.h>
23
24 #ifndef V3_CONFIG_DEBUG_IDE
25 #undef PrintDebug
26 #define PrintDebug(fmt, args...)
27 #endif
28
29
30 #define NBD_READ_CMD 0x1
31 #define NBD_WRITE_CMD 0x2
32 #define NBD_CAPACITY_CMD 0x3
33
34 #define NBD_STATUS_OK 0x00
35 #define NBD_STATUS_ERR 0xff
36
37
38 struct disk_state {
39     uint64_t capacity; // in bytes
40
41     v3_sock_t socket;
42
43     uint32_t ip_addr;
44     uint16_t port;
45
46     struct v3_vm_info * vm;
47
48     char disk_name[32];
49
50 };
51
52
53 static int send_all(v3_sock_t socket, char * buf, int length) {
54     int bytes_sent = 0;
55     
56     PrintDebug(VM_NONE, VCORE_NONE, "Sending %d bytes\n", length - bytes_sent);
57     while (bytes_sent < length) {
58         int tmp_bytes = v3_socket_send(socket, buf + bytes_sent, length - bytes_sent);
59         PrintDebug(VM_NONE, VCORE_NONE, "Sent %d bytes\n", tmp_bytes);
60         
61         if (tmp_bytes == 0) {
62             PrintError(VM_NONE, VCORE_NONE, "Connection Closed unexpectedly\n");
63             return -1;
64         }
65         
66         bytes_sent += tmp_bytes;
67     }
68     
69     return 0;
70 }
71
72
73 static int recv_all(v3_sock_t socket, char * buf, int length) {
74     int bytes_read = 0;
75     
76     PrintDebug(VM_NONE, VCORE_NONE, "Reading %d bytes\n", length - bytes_read);
77     while (bytes_read < length) {
78         int tmp_bytes = v3_socket_recv(socket, buf + bytes_read, length - bytes_read);
79         PrintDebug(VM_NONE, VCORE_NONE, "Received %d bytes\n", tmp_bytes);
80         
81         if (tmp_bytes == 0) {
82             PrintError(VM_NONE, VCORE_NONE, "Connection Closed unexpectedly\n");
83             return -1;
84         }
85         
86         bytes_read += tmp_bytes;
87     }
88     
89     return 0;
90 }
91
92
93 // HDs always read 512 byte blocks... ?
94 static int read(uint8_t * buf, uint64_t lba, uint64_t num_bytes, void * private_data) {
95     struct disk_state * disk = (struct disk_state *)private_data ;
96     uint8_t status;
97     uint32_t ret_len = 0;
98     char nbd_cmd[4] = {0,0,0,0};
99     uint64_t offset = lba;
100     uint64_t length = num_bytes;
101
102     nbd_cmd[0] = NBD_READ_CMD;
103     
104     if (send_all(disk->socket, nbd_cmd, 4) == -1) {
105         PrintError(VM_NONE, VCORE_NONE, "Error sending read command\n");
106         return -1;
107     }
108     
109     if (send_all(disk->socket, (char *)&offset, 8) == -1) {
110         PrintError(VM_NONE, VCORE_NONE, "Error sending read offset\n");
111         return -1;
112     }
113
114     if (send_all(disk->socket, (char *)&length, 4) == -1) {
115         PrintError(VM_NONE, VCORE_NONE, "Error sending read length\n");
116         return -1;
117     }
118
119     if (recv_all(disk->socket, (char *)&status, 1) == -1) {
120         PrintError(VM_NONE, VCORE_NONE, "Error receiving status\n");
121         return -1;
122     }
123
124     if (status != NBD_STATUS_OK) {
125         PrintError(VM_NONE, VCORE_NONE, "NBD Error....\n");
126         return -1;
127     }
128
129     PrintDebug(VM_NONE, VCORE_NONE, "Reading Data Ret Length\n");
130
131     if (recv_all(disk->socket, (char *)&ret_len, 4) == -1) {
132         PrintError(VM_NONE, VCORE_NONE, "Error receiving Return read length\n");
133         return -1;
134     }
135
136     if (ret_len != length) {
137         PrintError(VM_NONE, VCORE_NONE, "Read length mismatch (req=%llu) (result=%u)\n", length, ret_len);
138         return -1;
139     }
140
141     PrintDebug(VM_NONE, VCORE_NONE, "Reading Data (%d bytes)\n", ret_len);
142
143     if (recv_all(disk->socket, (char *)buf, ret_len) == -1) {
144         PrintError(VM_NONE, VCORE_NONE, "Read Data Error\n");
145         return -1;
146     }
147
148     return 0;
149 }
150
151
152 static int write(uint8_t * buf, uint64_t lba, uint64_t num_bytes, void * private_data) {
153     struct disk_state * disk = (struct disk_state *)private_data ;
154     uint64_t offset = lba;
155     int length = num_bytes;
156     uint8_t status;
157     char nbd_cmd[4] = {0,0,0,0};
158
159     nbd_cmd[0] = NBD_WRITE_CMD;
160
161     if (send_all(disk->socket, nbd_cmd, 4) == -1) {
162         PrintError(VM_NONE, VCORE_NONE, "Error sending write command\n");
163         return -1;
164     }
165
166     if (send_all(disk->socket, (char *)&offset, 8) == -1) {
167         PrintError(VM_NONE, VCORE_NONE, "Error sending write offset\n");
168         return -1;
169     }
170
171     if (send_all(disk->socket, (char *)&length, 4) == -1) {
172         PrintError(VM_NONE, VCORE_NONE, "Error sending write length\n");
173         return -1;
174     }
175
176     PrintDebug(VM_NONE, VCORE_NONE, "Writing Data (%d bytes)\n", length);
177
178     if (send_all(disk->socket, (char *)buf, length) == -1) {
179         PrintError(VM_NONE, VCORE_NONE, "Write Data Error\n");
180         return -1;
181     }
182
183     if (recv_all(disk->socket, (char *)&status, 1) == -1) {
184         PrintError(VM_NONE, VCORE_NONE, "Error receiving status\n");
185         return -1;
186     }
187
188     if (status != NBD_STATUS_OK) {
189         PrintError(VM_NONE, VCORE_NONE, "NBD Error....\n");
190         return -1;
191     }
192
193     return 0;
194 }
195
196
197 static uint64_t get_capacity(void * private_data) {
198     struct disk_state * disk = (struct disk_state *)private_data;
199
200     return disk->capacity;
201 }
202
203 static struct v3_dev_blk_ops blk_ops = {
204     .read = read, 
205     .write = write,
206     .get_capacity = get_capacity,
207 };
208
209
210
211
212
213 static int disk_free(struct disk_state * disk) {
214
215     v3_socket_close(disk->socket);
216
217     V3_Free(disk);
218     return 0;
219 }
220
221 static struct v3_device_ops dev_ops = {
222     .free = (int (*)(void *))disk_free,
223 };
224
225
226 static int socket_init(struct disk_state * disk) {
227     char header[64];
228     
229     PrintDebug(VM_NONE, VCORE_NONE, "Intializing Net Disk\n");
230
231     disk->socket = v3_create_tcp_socket(disk->vm);
232
233     PrintDebug(VM_NONE, VCORE_NONE, "DISK socket: %p\n", disk->socket);
234     PrintDebug(VM_NONE, VCORE_NONE, "Connecting to: %s:%d\n", v3_inet_ntoa(disk->ip_addr), disk->port);
235
236     v3_connect_to_ip(disk->socket, v3_ntohl(disk->ip_addr), disk->port);
237
238     PrintDebug(VM_NONE, VCORE_NONE, "Connected to NBD server\n");
239
240     //snprintf(header, 64, "V3_NBD_1 %s\n", cd->disk_name);
241     strcpy(header, "V3_NBD_1 ");
242     strncat(header, disk->disk_name, 32);
243     strncat(header, "\n", 1);
244
245
246     if (send_all(disk->socket, header, strlen(header)) == -1) {
247         PrintError(VM_NONE, VCORE_NONE, "Error connecting to Network Block Device: %s\n", disk->disk_name);
248         return -1;
249     }
250
251     // store local copy of capacity
252     {
253         char nbd_cmd[4] = {0,0,0,0};
254
255         nbd_cmd[0] = NBD_CAPACITY_CMD;
256         
257         if (send_all(disk->socket, nbd_cmd, 4) == -1) {
258             PrintError(VM_NONE, VCORE_NONE, "Error sending capacity command\n");
259             return -1;
260         }
261
262         if (recv_all(disk->socket, (char *)&(disk->capacity), 8) == -1) {
263             PrintError(VM_NONE, VCORE_NONE, "Error Receiving Capacity\n");
264             return -1;
265         }       
266
267         PrintDebug(VM_NONE, VCORE_NONE, "Capacity: %p\n", (void *)(addr_t)disk->capacity);
268     }
269
270
271
272     return 0;
273 }
274
275
276 static int disk_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
277     struct disk_state * disk = (struct disk_state *)V3_Malloc(sizeof(struct disk_state));
278
279     if (!disk) {
280         PrintError(vm, VCORE_NONE, "Cannot allocate in init\n");
281         return -1;
282     }
283
284     char * ip_str = v3_cfg_val(cfg, "IP");
285     char * port_str = v3_cfg_val(cfg, "port");
286     char * disk_tag = v3_cfg_val(cfg, "tag");
287     char * dev_id = v3_cfg_val(cfg, "ID");
288
289     v3_cfg_tree_t * frontend_cfg = v3_cfg_subtree(cfg, "frontend");
290
291     PrintDebug(vm, VCORE_NONE, "Registering Net disk at %s:%s disk=%s\n", ip_str, port_str, disk_tag);
292
293     strncpy(disk->disk_name, disk_tag, sizeof(disk->disk_name));
294     disk->ip_addr = v3_inet_addr(ip_str);
295     disk->port = atoi(port_str);
296     disk->vm = vm;
297
298     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, disk);
299
300     if (dev == NULL) {
301         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
302         V3_Free(disk);
303         return -1;
304     }
305
306     if (socket_init(disk) == -1) {
307         PrintError(vm, VCORE_NONE, "could not initialize network connection\n");
308         v3_remove_device(dev);
309         return -1;
310     }
311
312     PrintDebug(vm, VCORE_NONE, "Registering Disk\n");
313
314     if (v3_dev_connect_blk(vm, v3_cfg_val(frontend_cfg, "tag"), 
315                            &blk_ops, frontend_cfg, disk) == -1) {
316         PrintError(vm, VCORE_NONE, "Could not connect %s to frontend\n", dev_id);
317         v3_remove_device(dev);
318         return -1;
319     }
320
321     PrintDebug(vm, VCORE_NONE, "intialization done\n");
322
323     return 0;
324 }
325
326
327 device_register("NETDISK", disk_init)