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.


Minimize the copies for vnet and Virtio bridge
[palacios.git] / palacios / src / palacios / vmm_vnet.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) 2009, Lei Xia <lxia@northwestern.edu> 
11  * Copyright (c) 2009, Yuan Tang <ytang@northwestern.edu>  
12  * Copyright (c) 2009, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Author: Lei Xia <lxia@northwestern.edu>
16  *         Yuan Tang <ytang@northwestern.edu>
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21  
22 #include <palacios/vmm_vnet.h>
23 #include <palacios/vm_guest_mem.h>
24 #include <palacios/vmm_lock.h>
25 #include <palacios/vmm_queue.h>
26 #include <palacios/vmm_sprintf.h>
27
28 #ifndef CONFIG_DEBUG_VNET
29 #undef PrintDebug
30 #define PrintDebug(fmt, args...)
31 #endif
32
33
34
35 struct eth_hdr {
36     uint8_t dst_mac[6];
37     uint8_t src_mac[6];
38     uint16_t type; // indicates layer 3 protocol type
39 } __attribute__((packed));
40
41
42
43
44
45 struct vnet_dev {
46
47     uint8_t mac_addr[6];
48     struct v3_vm_info * vm;
49     
50     int (*input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data);
51     void * private_data;
52     
53     int dev_id;
54     struct list_head node;
55 } __attribute__((packed));
56
57
58 struct vnet_brg_dev {
59     struct v3_vm_info * vm;
60     
61     int (*input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data);
62     void * private_data;
63 } __attribute__((packed));
64
65
66
67
68
69 struct vnet_route_info {
70     struct v3_vnet_route route_def;
71
72     struct vnet_dev * dst_dev;
73     struct vnet_dev * src_dev;
74
75     struct list_head node;
76     struct list_head match_node; // used for route matching
77 };
78
79
80
81
82 struct route_list {
83     uint8_t hash_buf[VNET_HASH_SIZE];
84
85     uint32_t num_routes;
86     struct vnet_route_info * routes[0];
87 } __attribute__((packed));
88
89
90
91 static struct {
92     struct list_head routes;
93     struct list_head devs;
94     
95     int num_routes;
96     int num_devs;
97
98     struct vnet_brg_dev *bridge;
99
100     v3_lock_t lock;
101
102     struct gen_queue * inpkt_q;
103     struct hashtable * route_cache;
104
105 } vnet_state;
106
107
108
109
110 #ifdef CONFIG_DEBUG_VNET
111 static inline void mac_to_string(char mac[6], char * buf) {
112     snprintf(buf, 50, "%x:%x:%x:%x:%x:%x", 
113              mac[0], mac[1], mac[2],
114              mac[3], mac[4], mac[5]);
115 }
116
117 static void print_route(struct vnet_route_info *route){
118     char str[50];
119
120     mac_to_string(route->route_def.src_mac, str);
121     PrintDebug("Src Mac (%s),  src_qual (%d)\n", 
122                         str, route->route_def.src_mac_qual);
123     mac_to_string(route->route_def.dst_mac, str);
124     PrintDebug("Dst Mac (%s),  dst_qual (%d)\n", 
125                         str, route->route_def.dst_mac_qual);
126     PrintDebug("Src dev id (%d), src type (%d)", 
127                         route->route_def.src_id, 
128                         route->route_def.src_type);
129     PrintDebug("Dst dev id (%d), dst type (%d)\n", 
130                         route->route_def.dst_id, 
131                         route->route_def.dst_type);
132     if (route->route_def.dst_type == LINK_INTERFACE) {
133         PrintDebug("dst_dev (%p), dst_dev_id (%d), dst_dev_input (%p), dst_dev_data (%p)\n",
134                                         route->dst_dev,
135                                         route->dst_dev->dev_id,
136                                         route->dst_dev->input,
137                                         route->dst_dev->private_data);
138     }
139 }
140
141 static void dump_routes(){
142         struct vnet_route_info *route;
143
144         int i = 0;
145         PrintDebug("\n========Dump routes starts ============\n");
146         list_for_each_entry(route, &(vnet_state.routes), node) {
147                 PrintDebug("\nroute %d:\n", ++i);
148                 
149                 print_route(route);
150         }
151         PrintDebug("\n========Dump routes end ============\n");
152 }
153
154 #endif
155
156
157 /* 
158  * A VNET packet is a packed struct with the hashed fields grouped together.
159  * This means we can generate the hash from an offset into the pkt struct
160  */
161 static inline uint_t hash_fn(addr_t hdr_ptr) {    
162     uint8_t * hdr_buf = (uint8_t *)hdr_ptr;
163
164     return v3_hash_buffer(hdr_buf, VNET_HASH_SIZE);
165 }
166
167 static inline int hash_eq(addr_t key1, addr_t key2) {   
168     return (memcmp((uint8_t *)key1, (uint8_t *)key2, VNET_HASH_SIZE) == 0);
169 }
170
171
172 static int add_route_to_cache(const struct v3_vnet_pkt * pkt, struct route_list * routes) {
173     memcpy(routes->hash_buf, pkt->hash_buf, VNET_HASH_SIZE);    
174
175     if (v3_htable_insert(vnet_state.route_cache, (addr_t)routes->hash_buf, (addr_t)routes) == 0) {
176         PrintError("Vnet: Failed to insert new route entry to the cache\n");
177         return -1;
178     }
179     
180     return 0;
181 }
182
183 static int clear_hash_cache() {
184
185     v3_free_htable(vnet_state.route_cache, 1, 1);
186     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
187
188     return 0;
189 }
190
191 static int look_into_cache(const struct v3_vnet_pkt * pkt, struct route_list ** routes) {
192     
193     *routes = (struct route_list *)v3_htable_search(vnet_state.route_cache, (addr_t)(pkt->hash_buf));
194    
195     return 0;
196 }
197
198
199 static struct vnet_dev * find_dev_by_id(int idx) {
200     struct vnet_dev * dev = NULL; 
201     
202     list_for_each_entry(dev, &(vnet_state.devs), node) {
203         int dev_id = dev->dev_id;
204
205         if (dev_id == idx)
206             return dev;
207     }
208
209     return NULL;
210 }
211
212 static struct vnet_dev * find_dev_by_mac(char mac[6]) {
213     struct vnet_dev * dev = NULL; 
214     
215     list_for_each_entry(dev, &(vnet_state.devs), node) {
216         if (!memcmp(dev->mac_addr, mac, 6))
217             return dev;
218     }
219
220     return NULL;
221 }
222
223 int get_device_id_by_mac(char mac[6]){
224
225     struct vnet_dev *dev = find_dev_by_mac(mac);
226
227     if (dev == NULL)
228         return -1;
229
230     return dev->dev_id;
231 }
232
233
234 int v3_vnet_add_route(struct v3_vnet_route route) {
235     struct vnet_route_info * new_route = NULL;
236     unsigned long flags; 
237
238     new_route = (struct vnet_route_info *)V3_Malloc(sizeof(struct vnet_route_info));
239     memset(new_route, 0, sizeof(struct vnet_route_info));
240
241     PrintDebug("Vnet: vnet_add_route_entry: dst_id: %d, dst_type: %d\n",
242                         route.dst_id, route.dst_type);  
243     
244     memcpy(new_route->route_def.src_mac, route.src_mac, 6);
245     memcpy(new_route->route_def.dst_mac, route.dst_mac, 6);
246     new_route->route_def.src_mac_qual = route.src_mac_qual;
247     new_route->route_def.dst_mac_qual = route.dst_mac_qual;
248     new_route->route_def.dst_id = route.dst_id;
249     new_route->route_def.dst_type = route.dst_type;
250     new_route->route_def.src_id = route.src_id;
251     new_route->route_def.src_type = route.src_type;
252
253     if (new_route->route_def.dst_type == LINK_INTERFACE) {
254         new_route->dst_dev = find_dev_by_id(new_route->route_def.dst_id);
255         PrintDebug("Vnet: Add route, get device: dev_id %d, input : %p, private_data %p\n",
256                         new_route->dst_dev->dev_id, new_route->dst_dev->input, new_route->dst_dev->private_data);
257     }
258
259     if (new_route->route_def.src_type == LINK_INTERFACE) {
260         new_route->src_dev = find_dev_by_id(new_route->route_def.src_id);
261     }
262
263     flags = v3_lock_irqsave(vnet_state.lock);
264
265     list_add(&(new_route->node), &(vnet_state.routes));
266     clear_hash_cache();
267
268     v3_unlock_irqrestore(vnet_state.lock, flags);
269    
270
271 #ifdef CONFIG_DEBUG_VNET
272     dump_routes();
273 #endif
274
275     return 0;
276 }
277
278
279
280 // At the end allocate a route_list
281 // This list will be inserted into the cache so we don't need to free it
282 static struct route_list * match_route(const struct v3_vnet_pkt * pkt) {
283     struct vnet_route_info * route = NULL; 
284     struct route_list * matches = NULL;
285     int num_matches = 0;
286     int max_rank = 0;
287     struct list_head match_list;
288     struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
289     uint8_t src_type = pkt->src_type;
290     uint32_t src_link = pkt->src_id;
291
292 #ifdef CONFIG_DEBUG_VNET
293     {
294         char dst_str[50];
295         char src_str[50];
296
297         mac_to_string(hdr->src_mac, src_str);  
298         mac_to_string(hdr->dst_mac, dst_str);
299         PrintDebug("Vnet: match_route. pkt: SRC(%s), DEST(%s)\n", src_str, dst_str);
300     }
301 #endif
302
303     INIT_LIST_HEAD(&match_list);
304     
305 #define UPDATE_MATCHES(rank) do {                               \
306         if (max_rank < (rank)) {                                \
307             max_rank = (rank);                                  \
308             INIT_LIST_HEAD(&match_list);                        \
309                                                                 \
310             list_add(&(route->match_node), &match_list);        \
311             num_matches = 1;                                    \
312         } else if (max_rank == (rank)) {                        \
313             list_add(&(route->match_node), &match_list);        \
314             num_matches++;                                      \
315         }                                                       \
316     } while (0)
317     
318
319     list_for_each_entry(route, &(vnet_state.routes), node) {
320         struct v3_vnet_route * route_def = &(route->route_def);
321
322         // CHECK SOURCE TYPE HERE
323         if ( (route_def->src_type != LINK_ANY) && 
324              ( (route_def->src_type != src_type) || 
325                ( (route_def->src_id != src_link) &&
326                  (route_def->src_id != (uint32_t)-1)))) {
327             continue;
328         }
329
330
331         if ((route_def->dst_mac_qual == MAC_ANY) &&
332             (route_def->src_mac_qual == MAC_ANY)) {      
333             UPDATE_MATCHES(3);
334         }
335         
336         if (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) {
337             if (route_def->src_mac_qual != MAC_NOT) {
338                 if (route_def->dst_mac_qual == MAC_ANY) {
339                     UPDATE_MATCHES(6);
340                 } else if (route_def->dst_mac_qual != MAC_NOT &&
341                            memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
342                     UPDATE_MATCHES(8);
343                 }
344             }
345         }
346             
347         if (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0) {
348             if (route_def->dst_mac_qual != MAC_NOT) {
349                 if (route_def->src_mac_qual == MAC_ANY) {
350                     UPDATE_MATCHES(6);
351                 } else if ((route_def->src_mac_qual != MAC_NOT) && 
352                            (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {
353                     UPDATE_MATCHES(8);
354                 }
355             }
356         }
357             
358         if ((route_def->dst_mac_qual == MAC_NOT) &&
359             (memcmp(route_def->dst_mac, hdr->dst_mac, 6) != 0)) {
360             if (route_def->src_mac_qual == MAC_ANY) {
361                 UPDATE_MATCHES(5);
362             } else if ((route_def->src_mac_qual != MAC_NOT) && 
363                        (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0)) {     
364                 UPDATE_MATCHES(7);
365             }
366         }
367         
368         if ((route_def->src_mac_qual == MAC_NOT) &&
369             (memcmp(route_def->src_mac, hdr->src_mac, 6) != 0)) {
370             if (route_def->dst_mac_qual == MAC_ANY) {
371                 UPDATE_MATCHES(5);
372             } else if ((route_def->dst_mac_qual != MAC_NOT) &&
373                        (memcmp(route_def->dst_mac, hdr->dst_mac, 6) == 0)) {
374                 UPDATE_MATCHES(7);
375             }
376         }
377         
378         // Default route
379         if ( (memcmp(route_def->src_mac, hdr->src_mac, 6) == 0) &&
380              (route_def->dst_mac_qual == MAC_NONE)) {
381             UPDATE_MATCHES(4);
382         }
383     }
384
385     PrintDebug("Vnet: match_route: Matches=%d\n", num_matches);
386
387     if (num_matches == 0) {
388         return NULL;
389     }
390
391     matches = V3_Malloc(sizeof(struct route_list) + 
392                         (sizeof(struct vnet_route_info *) * num_matches));
393
394     matches->num_routes = num_matches;
395
396     {
397         int i = 0;
398         list_for_each_entry(route, &match_list, match_node) {
399             matches->routes[i++] = route;
400         }
401     }
402
403     return matches;
404 }
405
406
407 int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt, void * private_data) {
408     struct route_list * matched_routes = NULL;
409     unsigned long flags;
410     int i;
411
412 #ifdef CONFIG_DEBUG_VNET
413    {
414         struct eth_hdr * hdr = (struct eth_hdr *)(pkt->header);
415         char dest_str[30];
416         char src_str[30];
417
418         mac_to_string(hdr->src_mac, src_str);  
419         mac_to_string(hdr->dst_mac, dest_str);
420         PrintDebug("Vnet: HandleDataOverLink. SRC(%s), DEST(%s)\n", src_str, dest_str);
421    }
422 #endif
423
424     flags = v3_lock_irqsave(vnet_state.lock);
425
426     look_into_cache(pkt, &matched_routes);
427         
428     if (matched_routes == NULL) {  
429         PrintDebug("Vnet: send pkt Looking into routing table\n");
430         
431         matched_routes = match_route(pkt);
432                 
433         if (matched_routes) {
434             add_route_to_cache(pkt, matched_routes);
435         } else {
436             PrintDebug("Could not find route for packet... discards packet\n");
437             v3_unlock_irqrestore(vnet_state.lock, flags);
438             return -1;
439         }
440     }
441
442     v3_unlock_irqrestore(vnet_state.lock, flags);
443
444
445     PrintDebug("Vnet: send pkt route matches %d\n", matched_routes->num_routes);
446
447     for (i = 0; i < matched_routes->num_routes; i++) {
448          struct vnet_route_info * route = matched_routes->routes[i];
449         
450         if (route->route_def.dst_type == LINK_EDGE) {
451             pkt->dst_type = LINK_EDGE;
452             pkt->dst_id = route->route_def.dst_id;
453
454             if (vnet_state.bridge == NULL) {
455                 PrintDebug("VNET: No bridge to sent data to links\n");
456                 continue;
457             }
458
459             if (vnet_state.bridge->input(vnet_state.bridge->vm, pkt, vnet_state.bridge->private_data) == -1) {
460                 PrintDebug("VNET: Packet not sent properly\n");
461                 continue;
462             } 
463
464         } else if (route->route_def.dst_type == LINK_INTERFACE) {
465             if (route->dst_dev->input(route->dst_dev->vm, pkt, route->dst_dev->private_data) == -1) {
466                 PrintDebug("VNET: Packet not sent properly\n");
467                 continue;
468              }
469         } else {
470             PrintDebug("Vnet: Wrong Edge type\n");
471             continue;
472         }
473
474         PrintDebug("Vnet: HandleOnePacket: Forward packet according to Route\n");
475     }
476     
477     return 0;
478 }
479
480 int v3_vnet_add_dev(struct v3_vm_info *vm, uint8_t mac[6], 
481                     int (*netif_input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data), 
482                     void * priv_data){
483     struct vnet_dev * new_dev = NULL;
484     unsigned long flags;
485
486     new_dev = (struct vnet_dev *)V3_Malloc(sizeof(struct vnet_dev)); 
487
488     if (new_dev == NULL) {
489         PrintError("VNET: Malloc fails\n");
490         return -1;
491     }
492    
493     memcpy(new_dev->mac_addr, mac, 6);
494     new_dev->input = netif_input;
495     new_dev->private_data = priv_data;
496     new_dev->vm = vm;
497     new_dev->dev_id = 0;        
498
499     flags = v3_lock_irqsave(vnet_state.lock);
500
501     if (!find_dev_by_mac(mac)) {
502         list_add(&(new_dev->node), &(vnet_state.devs));
503         new_dev->dev_id = ++vnet_state.num_devs;
504     }
505
506     v3_unlock_irqrestore(vnet_state.lock, flags);
507
508     // if the device was found previosly the id should still be 0
509     if (new_dev->dev_id == 0) {
510         PrintError("Device Alrady exists\n");
511         return -1;
512     }
513
514     PrintDebug("Vnet: Add Device: dev_id %d, input : %p, private_data %p\n",
515                         new_dev->dev_id, new_dev->input, new_dev->private_data);
516
517     return new_dev->dev_id;
518 }
519
520
521 int v3_vnet_add_bridge(struct v3_vm_info * vm,
522                        int (*input)(struct v3_vm_info * vm, struct v3_vnet_pkt * pkt, void * private_data), 
523                        void * priv_data) {
524     unsigned long flags;
525     int bridge_free = 0;
526     struct vnet_brg_dev * tmp_bridge = NULL;
527     
528     
529     flags = v3_lock_irqsave(vnet_state.lock);
530
531     if (vnet_state.bridge == NULL) {
532         bridge_free = 1;
533         vnet_state.bridge = (void *)1;
534     }
535
536     v3_unlock_irqrestore(vnet_state.lock, flags);
537
538     if (bridge_free == 0) {
539         PrintError("Bridge already set\n");
540         return -1;
541     }
542
543     tmp_bridge = (struct vnet_brg_dev *)V3_Malloc(sizeof(struct vnet_brg_dev));
544
545     if (tmp_bridge == NULL) {
546         PrintError("Malloc Fails\n");
547         return -1;
548     }
549     
550     tmp_bridge->vm = vm;
551     tmp_bridge->input = input;
552     tmp_bridge->private_data = priv_data;
553
554     // make this atomic to avoid possible race conditions
555     flags = v3_lock_irqsave(vnet_state.lock);
556     vnet_state.bridge = tmp_bridge;
557     v3_unlock_irqrestore(vnet_state.lock, flags);
558
559     return 0;
560 }
561
562 int V3_init_vnet() {
563         
564     INIT_LIST_HEAD(&(vnet_state.routes));
565     INIT_LIST_HEAD(&(vnet_state.devs));
566
567     vnet_state.num_devs = 0;
568     vnet_state.num_routes = 0;
569
570     PrintDebug("VNET: Links and Routes tables initiated\n");
571
572     if (v3_lock_init(&(vnet_state.lock)) == -1){
573         PrintError("VNET: Failure to init lock for routes table\n");
574     }
575
576     PrintDebug("VNET: Locks initiated\n");
577
578     vnet_state.inpkt_q = v3_create_queue();
579     v3_init_queue(vnet_state.inpkt_q);
580     PrintDebug("VNET: Receiving queue initiated\n");
581
582     vnet_state.route_cache = v3_create_htable(0, &hash_fn, &hash_eq);
583
584     if (vnet_state.route_cache == NULL) {
585         PrintError("Vnet: Route Cache Init Fails\n");
586         return -1;
587     }
588
589     PrintDebug("VNET: initiated\n");
590
591     return 0;
592 }