tap: fix the interrupt handling
[vpp.git] / src / vnet / devices / virtio / virtio.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <net/if.h>
22 #include <linux/if_tun.h>
23 #include <sys/ioctl.h>
24 #include <sys/eventfd.h>
25
26 #include <vlib/vlib.h>
27 #include <vlib/pci/pci.h>
28 #include <vlib/unix/unix.h>
29 #include <vnet/ethernet/ethernet.h>
30 #include <vnet/ip/ip4_packet.h>
31 #include <vnet/ip/ip6_packet.h>
32 #include <vnet/devices/virtio/virtio.h>
33 #include <vnet/devices/virtio/pci.h>
34 #include <vnet/interface/rx_queue_funcs.h>
35
36 virtio_main_t virtio_main;
37
38 #define _IOCTL(fd,a,...) \
39   if (ioctl (fd, a, __VA_ARGS__) < 0) \
40     { \
41       err = clib_error_return_unix (0, "ioctl(" #a ")"); \
42       goto error; \
43     }
44
45 static clib_error_t *
46 call_read_ready (clib_file_t * uf)
47 {
48   vnet_main_t *vnm = vnet_get_main ();
49   u64 b;
50
51   CLIB_UNUSED (ssize_t size) = read (uf->file_descriptor, &b, sizeof (b));
52   vnet_hw_if_rx_queue_set_int_pending (vnm, uf->private_data);
53
54   return 0;
55 }
56
57
58 clib_error_t *
59 virtio_vring_init (vlib_main_t * vm, virtio_if_t * vif, u16 idx, u16 sz)
60 {
61   virtio_vring_t *vring;
62   int i;
63
64   if (!is_pow2 (sz))
65     return clib_error_return (0, "ring size must be power of 2");
66
67   if (sz > 32768)
68     return clib_error_return (0, "ring size must be 32768 or lower");
69
70   if (sz == 0)
71     sz = 256;
72
73   if (idx % 2)
74     {
75       vlib_thread_main_t *thm = vlib_get_thread_main ();
76       vec_validate_aligned (vif->txq_vrings, TX_QUEUE_ACCESS (idx),
77                             CLIB_CACHE_LINE_BYTES);
78       vring = vec_elt_at_index (vif->txq_vrings, TX_QUEUE_ACCESS (idx));
79       if (thm->n_vlib_mains > vif->num_txqs)
80         clib_spinlock_init (&vring->lockp);
81     }
82   else
83     {
84       vec_validate_aligned (vif->rxq_vrings, RX_QUEUE_ACCESS (idx),
85                             CLIB_CACHE_LINE_BYTES);
86       vring = vec_elt_at_index (vif->rxq_vrings, RX_QUEUE_ACCESS (idx));
87     }
88   i = sizeof (vring_desc_t) * sz;
89   i = round_pow2 (i, CLIB_CACHE_LINE_BYTES);
90   vring->desc = clib_mem_alloc_aligned (i, CLIB_CACHE_LINE_BYTES);
91   clib_memset (vring->desc, 0, i);
92
93   i = sizeof (vring_avail_t) + sz * sizeof (vring->avail->ring[0]);
94   i = round_pow2 (i, CLIB_CACHE_LINE_BYTES);
95   vring->avail = clib_mem_alloc_aligned (i, CLIB_CACHE_LINE_BYTES);
96   clib_memset (vring->avail, 0, i);
97   // tell kernel that we don't need interrupt
98   vring->avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
99
100   i = sizeof (vring_used_t) + sz * sizeof (vring_used_elem_t);
101   i = round_pow2 (i, CLIB_CACHE_LINE_BYTES);
102   vring->used = clib_mem_alloc_aligned (i, CLIB_CACHE_LINE_BYTES);
103   clib_memset (vring->used, 0, i);
104
105   vring->queue_id = idx;
106   ASSERT (vring->buffers == 0);
107   vec_validate_aligned (vring->buffers, sz, CLIB_CACHE_LINE_BYTES);
108
109   if (idx & 1)
110     {
111       clib_memset_u32 (vring->buffers, ~0, sz);
112       // tx path: suppress the interrupts from kernel
113       vring->call_fd = -1;
114     }
115   else
116     vring->call_fd = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC);
117
118   vring->size = sz;
119   vring->kick_fd = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC);
120   virtio_log_debug (vif, "vring %u size %u call_fd %d kick_fd %d", idx,
121                     vring->size, vring->call_fd, vring->kick_fd);
122
123   return 0;
124 }
125
126 inline void
127 virtio_free_buffers (vlib_main_t * vm, virtio_vring_t * vring)
128 {
129   u16 used = vring->desc_in_use;
130   u16 last = vring->last_used_idx;
131   u16 mask = vring->size - 1;
132
133   while (used)
134     {
135       vlib_buffer_free (vm, &vring->buffers[last & mask], 1);
136       last++;
137       used--;
138     }
139 }
140
141 clib_error_t *
142 virtio_vring_free_rx (vlib_main_t * vm, virtio_if_t * vif, u32 idx)
143 {
144   virtio_vring_t *vring =
145     vec_elt_at_index (vif->rxq_vrings, RX_QUEUE_ACCESS (idx));
146
147   clib_file_del_by_index (&file_main, vring->call_file_index);
148   close (vring->kick_fd);
149   close (vring->call_fd);
150   if (vring->used)
151     {
152       virtio_free_buffers (vm, vring);
153       clib_mem_free (vring->used);
154     }
155   if (vring->desc)
156     clib_mem_free (vring->desc);
157   if (vring->avail)
158     clib_mem_free (vring->avail);
159   vec_free (vring->buffers);
160   return 0;
161 }
162
163 clib_error_t *
164 virtio_vring_free_tx (vlib_main_t * vm, virtio_if_t * vif, u32 idx)
165 {
166   virtio_vring_t *vring =
167     vec_elt_at_index (vif->txq_vrings, TX_QUEUE_ACCESS (idx));
168
169   close (vring->kick_fd);
170   if (vring->used)
171     {
172       virtio_free_buffers (vm, vring);
173       clib_mem_free (vring->used);
174     }
175   if (vring->desc)
176     clib_mem_free (vring->desc);
177   if (vring->avail)
178     clib_mem_free (vring->avail);
179   vec_free (vring->buffers);
180   gro_flow_table_free (vring->flow_table);
181   virtio_vring_buffering_free (vm, vring->buffering);
182   clib_spinlock_free (&vring->lockp);
183   return 0;
184 }
185
186 void
187 virtio_set_packet_coalesce (virtio_if_t * vif)
188 {
189   vnet_main_t *vnm = vnet_get_main ();
190   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vif->hw_if_index);
191   virtio_vring_t *vring;
192   vif->packet_coalesce = 1;
193   vec_foreach (vring, vif->txq_vrings)
194   {
195     gro_flow_table_init (&vring->flow_table,
196                          vif->type & (VIRTIO_IF_TYPE_TAP |
197                                       VIRTIO_IF_TYPE_PCI), hw->tx_node_index);
198   }
199 }
200
201 clib_error_t *
202 virtio_set_packet_buffering (virtio_if_t * vif, u16 buffering_size)
203 {
204   vnet_main_t *vnm = vnet_get_main ();
205   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vif->hw_if_index);
206   virtio_vring_t *vring;
207   clib_error_t *error = 0;
208   vif->packet_buffering = 1;
209
210   vec_foreach (vring, vif->txq_vrings)
211   {
212     if ((error =
213          virtio_vring_buffering_init (&vring->buffering, hw->tx_node_index,
214                                       buffering_size)))
215       {
216         break;
217       }
218   }
219
220   return error;
221 }
222
223 void
224 virtio_vring_set_rx_queues (vlib_main_t *vm, virtio_if_t *vif)
225 {
226   vnet_main_t *vnm = vnet_get_main ();
227   virtio_vring_t *vring;
228
229   vnet_hw_if_set_input_node (vnm, vif->hw_if_index, virtio_input_node.index);
230
231   vec_foreach (vring, vif->rxq_vrings)
232     {
233       vring->queue_index = vnet_hw_if_register_rx_queue (
234         vnm, vif->hw_if_index, RX_QUEUE_ACCESS (vring->queue_id),
235         VNET_HW_IF_RXQ_THREAD_ANY);
236       vring->buffer_pool_index = vlib_buffer_pool_get_default_for_numa (
237         vm, vnet_hw_if_get_rx_queue_numa_node (vnm, vring->queue_index));
238       if (vif->type == VIRTIO_IF_TYPE_TAP || vif->type == VIRTIO_IF_TYPE_TUN)
239         {
240
241           clib_file_t f = {
242             .read_function = call_read_ready,
243             .flags = UNIX_FILE_EVENT_EDGE_TRIGGERED,
244             .file_descriptor = vring->call_fd,
245             .private_data = vring->queue_index,
246             .description = format (0, "%U vring %u", format_virtio_device_name,
247                                    vif->dev_instance, vring->queue_id),
248           };
249
250           vring->call_file_index = clib_file_add (&file_main, &f);
251           vnet_hw_if_set_rx_queue_file_index (vnm, vring->queue_index,
252                                               vring->call_file_index);
253         }
254     }
255   vnet_hw_if_update_runtime_data (vnm, vif->hw_if_index);
256 }
257
258 inline void
259 virtio_set_net_hdr_size (virtio_if_t * vif)
260 {
261   if (vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_MRG_RXBUF) ||
262       vif->features & VIRTIO_FEATURE (VIRTIO_F_VERSION_1))
263     vif->virtio_net_hdr_sz = sizeof (virtio_net_hdr_v1_t);
264   else
265     vif->virtio_net_hdr_sz = sizeof (virtio_net_hdr_t);
266 }
267
268 inline void
269 virtio_show (vlib_main_t * vm, u32 * hw_if_indices, u8 show_descr, u32 type)
270 {
271   u32 i, j, hw_if_index;
272   virtio_if_t *vif;
273   vnet_main_t *vnm = &vnet_main;
274   virtio_main_t *mm = &virtio_main;
275   virtio_vring_t *vring;
276   struct feat_struct
277   {
278     u8 bit;
279     char *str;
280   };
281   struct feat_struct *feat_entry;
282
283   static struct feat_struct feat_array[] = {
284 #define _(s,b) { .str = #s, .bit = b, },
285     foreach_virtio_net_features
286 #undef _
287     {.str = NULL}
288   };
289
290   struct feat_struct *flag_entry;
291   static struct feat_struct flags_array[] = {
292 #define _(b,e,s) { .bit = b, .str = s, },
293     foreach_virtio_if_flag
294 #undef _
295     {.str = NULL}
296   };
297
298   if (!hw_if_indices)
299     return;
300
301   for (hw_if_index = 0; hw_if_index < vec_len (hw_if_indices); hw_if_index++)
302     {
303       vnet_hw_interface_t *hi =
304         vnet_get_hw_interface (vnm, hw_if_indices[hw_if_index]);
305       vif = pool_elt_at_index (mm->interfaces, hi->dev_instance);
306       if (vif->type != type)
307         continue;
308       vlib_cli_output (vm, "Interface: %U (ifindex %d)",
309                        format_vnet_hw_if_index_name, vnm,
310                        hw_if_indices[hw_if_index], vif->hw_if_index);
311       if (type == VIRTIO_IF_TYPE_PCI)
312         {
313           vlib_cli_output (vm, "  PCI Address: %U", format_vlib_pci_addr,
314                            &vif->pci_addr);
315         }
316       if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_TUN))
317         {
318           u8 *str = 0;
319           if (vif->host_if_name)
320             vlib_cli_output (vm, "  name \"%s\"", vif->host_if_name);
321           if (vif->net_ns)
322             vlib_cli_output (vm, "  host-ns \"%s\"", vif->net_ns);
323           if (vif->host_mtu_size)
324             vlib_cli_output (vm, "  host-mtu-size \"%d\"",
325                              vif->host_mtu_size);
326           if (type == VIRTIO_IF_TYPE_TAP)
327             vlib_cli_output (vm, "  host-mac-addr: %U",
328                              format_ethernet_address, vif->host_mac_addr);
329           vlib_cli_output (vm, "  host-carrier-up: %u", vif->host_carrier_up);
330
331           vec_foreach_index (i, vif->vhost_fds)
332             str = format (str, " %d", vif->vhost_fds[i]);
333           vlib_cli_output (vm, "  vhost-fds%v", str);
334           vec_free (str);
335           vec_foreach_index (i, vif->tap_fds)
336             str = format (str, " %d", vif->tap_fds[i]);
337           vlib_cli_output (vm, "  tap-fds%v", str);
338           vec_free (str);
339         }
340       vlib_cli_output (vm, "  gso-enabled %d", vif->gso_enabled);
341       vlib_cli_output (vm, "  csum-enabled %d", vif->csum_offload_enabled);
342       vlib_cli_output (vm, "  packet-coalesce %d", vif->packet_coalesce);
343       vlib_cli_output (vm, "  packet-buffering %d", vif->packet_buffering);
344       if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_PCI))
345         vlib_cli_output (vm, "  Mac Address: %U", format_ethernet_address,
346                          vif->mac_addr);
347       vlib_cli_output (vm, "  Device instance: %u", vif->dev_instance);
348       vlib_cli_output (vm, "  flags 0x%x", vif->flags);
349       flag_entry = (struct feat_struct *) &flags_array;
350       while (flag_entry->str)
351         {
352           if (vif->flags & (1ULL << flag_entry->bit))
353             vlib_cli_output (vm, "    %s (%d)", flag_entry->str,
354                              flag_entry->bit);
355           flag_entry++;
356         }
357       if (type == VIRTIO_IF_TYPE_PCI)
358         {
359           device_status (vm, vif);
360         }
361       vlib_cli_output (vm, "  features 0x%lx", vif->features);
362       feat_entry = (struct feat_struct *) &feat_array;
363       while (feat_entry->str)
364         {
365           if (vif->features & (1ULL << feat_entry->bit))
366             vlib_cli_output (vm, "    %s (%d)", feat_entry->str,
367                              feat_entry->bit);
368           feat_entry++;
369         }
370       vlib_cli_output (vm, "  remote-features 0x%lx", vif->remote_features);
371       feat_entry = (struct feat_struct *) &feat_array;
372       while (feat_entry->str)
373         {
374           if (vif->remote_features & (1ULL << feat_entry->bit))
375             vlib_cli_output (vm, "    %s (%d)", feat_entry->str,
376                              feat_entry->bit);
377           feat_entry++;
378         }
379       vlib_cli_output (vm, "  Number of RX Virtqueue  %u", vif->num_rxqs);
380       vlib_cli_output (vm, "  Number of TX Virtqueue  %u", vif->num_txqs);
381       if (vif->cxq_vring != NULL
382           && vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_CTRL_VQ))
383         vlib_cli_output (vm, "  Number of CTRL Virtqueue 1");
384       vec_foreach_index (i, vif->rxq_vrings)
385       {
386         vring = vec_elt_at_index (vif->rxq_vrings, i);
387         vlib_cli_output (vm, "  Virtqueue (RX) %d", vring->queue_id);
388         vlib_cli_output (vm,
389                          "    qsz %d, last_used_idx %d, desc_next %d, desc_in_use %d",
390                          vring->size, vring->last_used_idx, vring->desc_next,
391                          vring->desc_in_use);
392         if (vif->is_packed)
393           {
394             vlib_cli_output (vm,
395                              "    driver_event.flags 0x%x driver_event.off_wrap %d device_event.flags 0x%x device_event.off_wrap %d",
396                              vring->driver_event->flags,
397                              vring->driver_event->off_wrap,
398                              vring->device_event->flags,
399                              vring->device_event->off_wrap);
400             vlib_cli_output (vm,
401                              "    avail wrap counter %d, used wrap counter %d",
402                              vring->avail_wrap_counter,
403                              vring->used_wrap_counter);
404           }
405         else
406           vlib_cli_output (vm,
407                            "    avail.flags 0x%x avail.idx %d used.flags 0x%x used.idx %d",
408                            vring->avail->flags, vring->avail->idx,
409                            vring->used->flags, vring->used->idx);
410         if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_TUN))
411           {
412             vlib_cli_output (vm, "    kickfd %d, callfd %d", vring->kick_fd,
413                              vring->call_fd);
414           }
415         if (show_descr)
416           {
417             vlib_cli_output (vm, "\n  descriptor table:\n");
418             vlib_cli_output (vm,
419                              "   id          addr         len  flags  next/id      user_addr\n");
420             vlib_cli_output (vm,
421                              "  ===== ================== ===== ====== ======= ==================\n");
422             for (j = 0; j < vring->size; j++)
423               {
424                 if (vif->is_packed)
425                   {
426                     vring_packed_desc_t *desc = &vring->packed_desc[j];
427                     vlib_cli_output (vm,
428                                      "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
429                                      j, desc->addr,
430                                      desc->len,
431                                      desc->flags, desc->id, desc->addr);
432                   }
433                 else
434                   {
435                     vring_desc_t *desc = &vring->desc[j];
436                     vlib_cli_output (vm,
437                                      "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
438                                      j, desc->addr,
439                                      desc->len,
440                                      desc->flags, desc->next, desc->addr);
441                   }
442               }
443           }
444       }
445       vec_foreach_index (i, vif->txq_vrings)
446       {
447         vring = vec_elt_at_index (vif->txq_vrings, i);
448         vlib_cli_output (vm, "  Virtqueue (TX) %d", vring->queue_id);
449         vlib_cli_output (vm,
450                          "    qsz %d, last_used_idx %d, desc_next %d, desc_in_use %d",
451                          vring->size, vring->last_used_idx, vring->desc_next,
452                          vring->desc_in_use);
453         if (vif->is_packed)
454           {
455             vlib_cli_output (vm,
456                              "    driver_event.flags 0x%x driver_event.off_wrap %d device_event.flags 0x%x device_event.off_wrap %d",
457                              vring->driver_event->flags,
458                              vring->driver_event->off_wrap,
459                              vring->device_event->flags,
460                              vring->device_event->off_wrap);
461             vlib_cli_output (vm,
462                              "    avail wrap counter %d, used wrap counter %d",
463                              vring->avail_wrap_counter,
464                              vring->used_wrap_counter);
465           }
466         else
467           vlib_cli_output (vm,
468                            "    avail.flags 0x%x avail.idx %d used.flags 0x%x used.idx %d",
469                            vring->avail->flags, vring->avail->idx,
470                            vring->used->flags, vring->used->idx);
471         if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_TUN))
472           {
473             vlib_cli_output (vm, "    kickfd %d, callfd %d", vring->kick_fd,
474                              vring->call_fd);
475           }
476         if (vring->flow_table)
477           {
478             vlib_cli_output (vm, "    %U", gro_flow_table_format,
479                              vring->flow_table);
480           }
481         if (vif->packet_buffering)
482           {
483             vlib_cli_output (vm, "    %U", virtio_vring_buffering_format,
484                              vring->buffering);
485           }
486         if (show_descr)
487           {
488             vlib_cli_output (vm, "\n  descriptor table:\n");
489             vlib_cli_output (vm,
490                              "   id          addr         len  flags  next/id      user_addr\n");
491             vlib_cli_output (vm,
492                              "  ===== ================== ===== ====== ======== ==================\n");
493             for (j = 0; j < vring->size; j++)
494               {
495                 if (vif->is_packed)
496                   {
497                     vring_packed_desc_t *desc = &vring->packed_desc[j];
498                     vlib_cli_output (vm,
499                                      "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
500                                      j, desc->addr,
501                                      desc->len,
502                                      desc->flags, desc->id, desc->addr);
503                   }
504                 else
505                   {
506                     vring_desc_t *desc = &vring->desc[j];
507                     vlib_cli_output (vm,
508                                      "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
509                                      j, desc->addr,
510                                      desc->len,
511                                      desc->flags, desc->next, desc->addr);
512                   }
513               }
514           }
515       }
516       if (vif->cxq_vring != NULL
517           && vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_CTRL_VQ))
518         {
519           vring = vif->cxq_vring;
520           vlib_cli_output (vm, "  Virtqueue (CTRL) %d", vring->queue_id);
521           vlib_cli_output (vm,
522                            "    qsz %d, last_used_idx %d, desc_next %d, desc_in_use %d",
523                            vring->size, vring->last_used_idx,
524                            vring->desc_next, vring->desc_in_use);
525           if (vif->is_packed)
526             {
527               vlib_cli_output (vm,
528                                "    driver_event.flags 0x%x driver_event.off_wrap %d device_event.flags 0x%x device_event.off_wrap %d",
529                                vring->driver_event->flags,
530                                vring->driver_event->off_wrap,
531                                vring->device_event->flags,
532                                vring->device_event->off_wrap);
533               vlib_cli_output (vm,
534                                "    avail wrap counter %d, used wrap counter %d",
535                                vring->avail_wrap_counter,
536                                vring->used_wrap_counter);
537             }
538           else
539             {
540               vlib_cli_output (vm,
541                                "    avail.flags 0x%x avail.idx %d used.flags 0x%x used.idx %d",
542                                vring->avail->flags, vring->avail->idx,
543                                vring->used->flags, vring->used->idx);
544             }
545           if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_TUN))
546             {
547               vlib_cli_output (vm, "    kickfd %d, callfd %d", vring->kick_fd,
548                                vring->call_fd);
549             }
550           if (show_descr)
551             {
552               vlib_cli_output (vm, "\n  descriptor table:\n");
553               vlib_cli_output (vm,
554                                "   id          addr         len  flags  next/id      user_addr\n");
555               vlib_cli_output (vm,
556                                "  ===== ================== ===== ====== ======== ==================\n");
557               for (j = 0; j < vring->size; j++)
558                 {
559                   if (vif->is_packed)
560                     {
561                       vring_packed_desc_t *desc = &vring->packed_desc[j];
562                       vlib_cli_output (vm,
563                                        "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
564                                        j, desc->addr,
565                                        desc->len,
566                                        desc->flags, desc->id, desc->addr);
567                     }
568                   else
569                     {
570                       vring_desc_t *desc = &vring->desc[j];
571                       vlib_cli_output (vm,
572                                        "  %-5d 0x%016lx %-5d 0x%04x %-8d 0x%016lx\n",
573                                        j, desc->addr,
574                                        desc->len,
575                                        desc->flags, desc->next, desc->addr);
576                     }
577                 }
578             }
579         }
580
581     }
582
583 }
584
585 static clib_error_t *
586 virtio_init (vlib_main_t * vm)
587 {
588   virtio_main_t *vim = &virtio_main;
589   clib_error_t *error = 0;
590
591   vim->log_default = vlib_log_register_class ("virtio", 0);
592   vlib_log_debug (vim->log_default, "initialized");
593
594   return error;
595 }
596
597 VLIB_INIT_FUNCTION (virtio_init);
598
599 /*
600  * fd.io coding-style-patch-verification: ON
601  *
602  * Local Variables:
603  * eval: (c-set-style "gnu")
604  * End:
605  */