ena: Amazon Elastic Network Adapter (ENA) native driver
[vpp.git] / src / plugins / dev_ena / ena.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright (c) 2023 Cisco Systems, Inc.
3  */
4
5 #include <vnet/vnet.h>
6 #include <vnet/dev/dev.h>
7 #include <vnet/dev/pci.h>
8 #include <dev_ena/ena.h>
9 #include <dev_ena/ena_inlines.h>
10 #include <vnet/ethernet/ethernet.h>
11 #include <vnet/plugin/plugin.h>
12 #include <vpp/app/version.h>
13
14 static ena_aq_host_info_t host_info = {
15   .os_type = 3, /* DPDK */
16   .kernel_ver_str = VPP_BUILD_VER,
17   .os_dist_str = VPP_BUILD_VER,
18   .driver_version = {
19     .major = 16,
20     .minor = 0,
21     .sub_minor = 0,
22   },
23   .ena_spec_version = {
24     .major = 2,
25     .minor = 0,
26   },
27   .driver_supported_features = {
28     .rx_offset = 1,
29     .rss_configurable_function_key = 1,
30   }
31 };
32
33 VLIB_REGISTER_LOG_CLASS (ena_log, static) = {
34   .class_name = "ena",
35   .subclass_name = "init",
36 };
37
38 #define _(f, n, s, d)                                                         \
39   { .name = #n, .desc = d, .severity = VL_COUNTER_SEVERITY_##s },
40
41 static vlib_error_desc_t ena_rx_node_counters[] = {
42   foreach_ena_rx_node_counter
43 };
44 static vlib_error_desc_t ena_tx_node_counters[] = {
45   foreach_ena_tx_node_counter
46 };
47 #undef _
48
49 vnet_dev_node_t ena_rx_node = {
50   .error_counters = ena_rx_node_counters,
51   .n_error_counters = ARRAY_LEN (ena_rx_node_counters),
52   .format_trace = format_ena_rx_trace,
53 };
54
55 vnet_dev_node_t ena_tx_node = {
56   .error_counters = ena_tx_node_counters,
57   .n_error_counters = ARRAY_LEN (ena_tx_node_counters),
58 };
59
60 static void
61 ena_deinit (vlib_main_t *vm, vnet_dev_t *dev)
62 {
63   ena_aenq_stop (vm, dev);
64   ena_aq_stop (vm, dev);
65 }
66
67 static vnet_dev_rv_t
68 ena_alloc (vlib_main_t *vm, vnet_dev_t *dev)
69 {
70   ena_device_t *ed = vnet_dev_get_data (dev);
71   vnet_dev_rv_t rv;
72
73   if ((rv = vnet_dev_dma_mem_alloc (vm, dev, 4096, 4096,
74                                     (void **) &ed->host_info)))
75     return rv;
76
77   if ((rv = vnet_dev_dma_mem_alloc (vm, dev, sizeof (ena_mmio_resp_t), 0,
78                                     (void **) &ed->mmio_resp)))
79     return rv;
80
81   if ((rv = ena_aq_olloc (vm, dev, ENA_ADMIN_QUEUE_DEPTH)))
82     return rv;
83
84   if ((rv = ena_aenq_olloc (vm, dev, ENA_ASYNC_QUEUE_DEPTH)))
85     return rv;
86
87   return VNET_DEV_OK;
88 }
89
90 static void
91 ena_free (vlib_main_t *vm, vnet_dev_t *dev)
92 {
93   ena_device_t *ed = vnet_dev_get_data (dev);
94
95   ena_aenq_free (vm, dev);
96   ena_aq_free (vm, dev);
97
98   vnet_dev_dma_mem_free (vm, dev, ed->host_info);
99   vnet_dev_dma_mem_free (vm, dev, ed->mmio_resp);
100 }
101
102 static vnet_dev_rv_t
103 ena_init (vlib_main_t *vm, vnet_dev_t *dev)
104 {
105   ena_device_t *ed = vnet_dev_get_data (dev);
106   ena_aq_feat_host_attr_config_t host_attr = {};
107   vlib_pci_config_hdr_t pci_cfg_hdr;
108   vnet_dev_rv_t rv = VNET_DEV_OK;
109
110   vnet_dev_port_add_args_t port = {
111     .port = {
112       .attr = {
113         .type = VNET_DEV_PORT_TYPE_ETHERNET,
114       },
115       .ops = {
116         .init = ena_port_init,
117         .start = ena_port_start,
118         .stop = ena_port_stop,
119         .config_change = ena_port_cfg_change,
120         .config_change_validate = ena_port_cfg_change_validate,
121       },
122       .data_size = sizeof (ena_port_t),
123     },
124     .rx_node = &ena_rx_node,
125     .tx_node = &ena_tx_node,
126     .rx_queue = {
127       .config = {
128         .data_size = sizeof (ena_rxq_t),
129         .default_size = 512,
130         .min_size = 32,
131         .size_is_power_of_two = 1,
132       },
133       .ops = {
134         .alloc = ena_rx_queue_alloc,
135         .start = ena_rx_queue_start,
136         .stop = ena_rx_queue_stop,
137         .free = ena_rx_queue_free,
138       },
139     },
140     .tx_queue = {
141       .config = {
142         .data_size = sizeof (ena_txq_t),
143         .default_size = 512,
144         .min_size = 32,
145         .size_is_power_of_two = 1,
146       },
147       .ops = {
148         .alloc = ena_tx_queue_alloc,
149         .start = ena_tx_queue_start,
150         .stop = ena_tx_queue_stop,
151         .free = ena_tx_queue_free,
152       },
153     },
154   };
155
156   if ((rv = vnet_dev_pci_read_config_header (vm, dev, &pci_cfg_hdr)))
157     goto err;
158
159   log_debug (dev, "revision_id 0x%x", pci_cfg_hdr.revision_id);
160
161   ed->readless = (pci_cfg_hdr.revision_id & 1) == 0;
162
163   if ((rv = vnet_dev_pci_map_region (vm, dev, 0, &ed->reg_bar)))
164     goto err;
165
166   if ((rv = ena_reg_reset (vm, dev, ENA_RESET_REASON_NORMAL)))
167     goto err;
168
169   if ((rv = ena_aq_start (vm, dev)))
170     goto err;
171
172   *ed->host_info = host_info;
173   ed->host_info->num_cpus = vlib_get_n_threads ();
174   ena_set_mem_addr (vm, dev, &host_attr.os_info_ba, ed->host_info);
175
176   if ((rv = ena_aq_set_feature (vm, dev, ENA_ADMIN_FEAT_ID_HOST_ATTR_CONFIG,
177                                 &host_attr)))
178     return rv;
179
180   if ((rv = ena_aq_get_feature (vm, dev, ENA_ADMIN_FEAT_ID_DEVICE_ATTRIBUTES,
181                                 &ed->dev_attr)))
182     return rv;
183
184   if (ena_aq_feature_is_supported (dev, ENA_ADMIN_FEAT_ID_MAX_QUEUES_EXT))
185     {
186       ena_aq_feat_max_queue_ext_t max_q_ext;
187       if ((rv = ena_aq_get_feature (vm, dev, ENA_ADMIN_FEAT_ID_MAX_QUEUES_EXT,
188                                     &max_q_ext)))
189         goto err;
190       port.port.attr.max_rx_queues =
191         clib_min (max_q_ext.max_rx_cq_num, max_q_ext.max_rx_sq_num);
192       port.port.attr.max_tx_queues =
193         clib_min (max_q_ext.max_tx_cq_num, max_q_ext.max_tx_sq_num);
194       port.rx_queue.config.max_size =
195         clib_min (max_q_ext.max_rx_cq_depth, max_q_ext.max_rx_sq_depth);
196       port.tx_queue.config.max_size =
197         clib_min (max_q_ext.max_tx_cq_depth, max_q_ext.max_tx_sq_depth);
198     }
199   else
200     {
201       log_err (dev, "device doesn't support MAX_QUEUES_EXT");
202       return VNET_DEV_ERR_UNSUPPORTED_DEVICE_VER;
203     }
204
205   if ((rv = ena_aenq_start (vm, dev)))
206     goto err;
207
208   port.port.attr.max_supported_rx_frame_size = ed->dev_attr.max_mtu;
209
210   if (ena_aq_feature_is_supported (dev, ENA_ADMIN_FEAT_ID_MTU))
211     port.port.attr.caps.change_max_rx_frame_size = 1;
212
213   vnet_dev_set_hw_addr_eth_mac (&port.port.attr.hw_addr,
214                                 ed->dev_attr.mac_addr);
215
216   return vnet_dev_port_add (vm, dev, 0, &port);
217
218 err:
219   ena_free (vm, dev);
220   return rv;
221 }
222
223 static u8 *
224 ena_probe (vlib_main_t *vm, vnet_dev_bus_index_t bus_index, void *dev_info)
225 {
226   vnet_dev_bus_pci_device_info_t *di = dev_info;
227   const struct
228   {
229     u16 device_id;
230     char *description;
231   } ena_dev_types[] = {
232     { .device_id = 0x0ec2, .description = "Elastic Network Adapter (ENA) PF" },
233     { .device_id = 0xec20, .description = "Elastic Network Adapter (ENA) VF" },
234   };
235
236   if (di->vendor_id != 0x1d0f) /* AMAZON */
237     return 0;
238
239   FOREACH_ARRAY_ELT (dt, ena_dev_types)
240     {
241       if (dt->device_id == di->device_id)
242         return format (0, "%s", dt->description);
243     }
244
245   return 0;
246 }
247
248 VNET_DEV_REGISTER_DRIVER (ena) = {
249   .name = "ena",
250   .bus = "pci",
251   .device_data_sz = sizeof (ena_device_t),
252   .ops = {
253     .alloc = ena_alloc,
254     .init = ena_init,
255     .deinit = ena_deinit,
256     .free = ena_free,
257     .format_info = format_ena_dev_info,
258     .probe = ena_probe,
259   },
260 };
261
262 VLIB_PLUGIN_REGISTER () = {
263   .version = VPP_BUILD_VER,
264   .description = "dev_ena",
265 };