1 | /* |
2 | * Copyright (c) 2019-2021 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | #include <skywalk/os_skywalk_private.h> |
30 | #include <skywalk/nexus/netif/nx_netif.h> |
31 | #include <skywalk/nexus/flowswitch/fsw_var.h> |
32 | #include <skywalk/nexus/flowswitch/nx_flowswitch.h> |
33 | |
34 | |
35 | /* |
36 | * Notification destination, can be either direct netif device, |
37 | * or a flowswitch. |
38 | */ |
39 | struct __notif_dest { |
40 | uint8_t dest_type; |
41 | #define __NOTIF_DEST_NONE 0 |
42 | #define __NOTIF_DEST_FSW 1 |
43 | #define __NOTIF_DEST_NETIF 2 |
44 | union { |
45 | struct nx_flowswitch *dest_fsw; |
46 | struct nx_netif *dest_netif; |
47 | }; |
48 | const char *dest_desc; |
49 | }; |
50 | |
51 | /* Create a notification destination from an ifnet device */ |
52 | static inline errno_t |
53 | __notif_dest_by_ifp(struct __notif_dest *dest, const ifnet_t ifp) |
54 | { |
55 | struct nx_flowswitch *fsw; |
56 | struct nx_netif *netif; |
57 | |
58 | if (dest == NULL || ifp == NULL) { |
59 | return EINVAL; |
60 | } |
61 | |
62 | if (!IF_FULLY_ATTACHED(ifp)) { |
63 | return ENXIO; |
64 | } |
65 | |
66 | if ((fsw = fsw_ifp_to_fsw(ifp)) != NULL) { |
67 | dest->dest_type = __NOTIF_DEST_FSW; |
68 | dest->dest_fsw = fsw; |
69 | dest->dest_desc = if_name(ifp); |
70 | return 0; |
71 | } |
72 | |
73 | if ((netif = NA(ifp)->nifna_netif) != NULL) { |
74 | dest->dest_type = __NOTIF_DEST_NETIF; |
75 | dest->dest_netif = netif; |
76 | dest->dest_desc = if_name(ifp); |
77 | return 0; |
78 | } |
79 | |
80 | return ENOENT; |
81 | } |
82 | |
83 | /* Create a notification destination from a flowswitch uuid */ |
84 | static inline errno_t |
85 | __notif_dest_by_nx_uuid(struct __notif_dest *dest, const uuid_t nx_uuid) |
86 | { |
87 | struct kern_nexus *nx; |
88 | struct nx_flowswitch *fsw; |
89 | |
90 | if (dest == NULL) { |
91 | return EINVAL; |
92 | } |
93 | |
94 | if ((nx = nx_find(nx_uuid, FALSE)) == NULL) { |
95 | return ENOENT; |
96 | } |
97 | |
98 | if ((fsw = NX_FSW_PRIVATE(nx)) == NULL) { |
99 | return ENOENT; |
100 | } |
101 | |
102 | dest->dest_type = __NOTIF_DEST_FSW; |
103 | dest->dest_fsw = fsw; |
104 | dest->dest_desc = (fsw->fsw_ifp != NULL) |
105 | ? if_name(fsw->fsw_ifp) |
106 | : "detached fsw" ; |
107 | return 0; |
108 | } |
109 | |
110 | /* function to send a packet channel event. |
111 | * |
112 | * Note on the event length limitations: |
113 | * The event that goes onto the channel is emplaced |
114 | * in a stack-allocated buffer, which includes |
115 | * the space for the packet channel event data. |
116 | * The size of the payload is governed by the |
117 | * `CHANNEL_EVENT_MAX_PAYLOAD_LEN' constant. |
118 | * See more details in `os_channel_event.h' |
119 | */ |
120 | |
121 | static inline errno_t |
122 | kern_channel_packet_event_notify(struct __notif_dest *dest, |
123 | os_channel_event_type_t event_type, size_t event_dlen, |
124 | uint8_t *event_data, uint32_t nx_port_id) |
125 | { |
126 | char buf[CHANNEL_EVENT_MAX_LEN] |
127 | __attribute((aligned(sizeof(uint64_t)))); |
128 | struct __kern_channel_event *event = |
129 | (struct __kern_channel_event *)(void *)buf; |
130 | |
131 | if (dest == NULL || dest->dest_desc == NULL) { |
132 | return EINVAL; |
133 | } |
134 | |
135 | if (sizeof(buf) < sizeof(event) + event_dlen) { |
136 | return EINVAL; |
137 | } |
138 | if ((event_type < CHANNEL_EVENT_MIN) || (CHANNEL_EVENT_MAX < event_type)) { |
139 | return EINVAL; |
140 | } |
141 | |
142 | event->ev_type = event_type; |
143 | event->ev_flags = 0; |
144 | event->_reserved = 0; |
145 | event->ev_dlen = (uint16_t)event_dlen; |
146 | memcpy(dst: event->ev_data, src: event_data, n: event_dlen); |
147 | |
148 | SK_DF(SK_VERB_EVENTS, "%s[%d] kern_channel_event: %p dest_type: %hu len: %hu " |
149 | "type: %u flags: %u res: %hu dlen: %hu" , |
150 | dest->dest_desc, nx_port_id, event, event_dlen, |
151 | event->ev_type, event->ev_flags, event->_reserved, event->ev_dlen); |
152 | |
153 | switch (dest->dest_type) { |
154 | case __NOTIF_DEST_NETIF: |
155 | return netif_vp_na_channel_event(dest->dest_netif, |
156 | nx_port_id, event, CHANNEL_EVENT_MAX_LEN); |
157 | case __NOTIF_DEST_FSW: |
158 | return fsw_vp_na_channel_event(fsw: dest->dest_fsw, |
159 | nx_port_id, event, CHANNEL_EVENT_MAX_LEN); |
160 | default: |
161 | return EINVAL; |
162 | } |
163 | } |
164 | |
165 | errno_t |
166 | kern_channel_event_transmit_status_with_packet(const kern_packet_t ph, |
167 | const ifnet_t ifp) |
168 | { |
169 | errno_t err; |
170 | uint32_t nx_port_id; |
171 | os_channel_event_packet_transmit_status_t pkt_tx_status; |
172 | struct __notif_dest dest = {0, {NULL}, NULL}; |
173 | |
174 | if ((err = __notif_dest_by_ifp(dest: &dest, ifp)) != 0) { |
175 | return err; |
176 | } |
177 | |
178 | (void) __packet_get_tx_completion_status(ph, |
179 | status: &pkt_tx_status.packet_status); |
180 | if (pkt_tx_status.packet_status == KERN_SUCCESS) { |
181 | return 0; |
182 | } |
183 | err = __packet_get_packetid(ph, pktid: &pkt_tx_status.packet_id); |
184 | if (__improbable(err != 0)) { |
185 | return err; |
186 | } |
187 | err = __packet_get_tx_nx_port_id(ph, nx_port_id: &nx_port_id); |
188 | if (__improbable(err != 0)) { |
189 | return err; |
190 | } |
191 | |
192 | return kern_channel_packet_event_notify(dest: &dest, |
193 | event_type: CHANNEL_EVENT_PACKET_TRANSMIT_STATUS, |
194 | event_dlen: sizeof(pkt_tx_status), event_data: (uint8_t*)&pkt_tx_status, nx_port_id); |
195 | } |
196 | |
197 | errno_t |
198 | kern_channel_event_transmit_status(const ifnet_t ifp, |
199 | os_channel_event_packet_transmit_status_t *pkt_tx_status, |
200 | uint32_t nx_port_id) |
201 | { |
202 | errno_t err; |
203 | struct __notif_dest dest = {0, {NULL}, NULL}; |
204 | |
205 | if ((err = __notif_dest_by_ifp(dest: &dest, ifp)) != 0) { |
206 | return err; |
207 | } |
208 | |
209 | return kern_channel_packet_event_notify(dest: &dest, |
210 | event_type: CHANNEL_EVENT_PACKET_TRANSMIT_STATUS, |
211 | event_dlen: sizeof(*pkt_tx_status), event_data: (uint8_t*)pkt_tx_status, nx_port_id); |
212 | } |
213 | |
214 | errno_t |
215 | kern_channel_event_transmit_status_with_nexus(const uuid_t nx_uuid, |
216 | os_channel_event_packet_transmit_status_t *pkt_tx_status, |
217 | uint32_t nx_port_id) |
218 | { |
219 | errno_t err; |
220 | struct __notif_dest dest = {0, {NULL}, NULL}; |
221 | |
222 | if ((err = __notif_dest_by_nx_uuid(dest: &dest, nx_uuid)) != 0) { |
223 | return err; |
224 | } |
225 | |
226 | return kern_channel_packet_event_notify(dest: &dest, |
227 | event_type: CHANNEL_EVENT_PACKET_TRANSMIT_STATUS, |
228 | event_dlen: sizeof(*pkt_tx_status), event_data: (uint8_t*)pkt_tx_status, nx_port_id); |
229 | } |
230 | |
231 | errno_t |
232 | kern_channel_event_transmit_expired(const ifnet_t ifp, |
233 | os_channel_event_packet_transmit_expired_t *pkt_tx_expired, |
234 | uint32_t nx_port_id) |
235 | { |
236 | errno_t err; |
237 | struct __notif_dest dest = {0, {NULL}, NULL}; |
238 | |
239 | if ((err = __notif_dest_by_ifp(dest: &dest, ifp)) != 0) { |
240 | return err; |
241 | } |
242 | |
243 | return kern_channel_packet_event_notify(dest: &dest, |
244 | event_type: CHANNEL_EVENT_PACKET_TRANSMIT_EXPIRED, |
245 | event_dlen: sizeof(*pkt_tx_expired), event_data: (uint8_t*)pkt_tx_expired, nx_port_id); |
246 | } |
247 | |
248 | extern errno_t |
249 | kern_channel_event_transmit_expired_with_nexus(const uuid_t nx_uuid, |
250 | os_channel_event_packet_transmit_expired_t *pkt_tx_expired, |
251 | uint32_t nx_port_id) |
252 | { |
253 | errno_t err; |
254 | struct __notif_dest dest = {0, {NULL}, NULL}; |
255 | |
256 | if ((err = __notif_dest_by_nx_uuid(dest: &dest, nx_uuid)) != 0) { |
257 | return err; |
258 | } |
259 | |
260 | return kern_channel_packet_event_notify(dest: &dest, |
261 | event_type: CHANNEL_EVENT_PACKET_TRANSMIT_EXPIRED, |
262 | event_dlen: sizeof(*pkt_tx_expired), event_data: (uint8_t*)pkt_tx_expired, nx_port_id); |
263 | } |
264 | |
265 | /* routine to post kevent notification for the event ring */ |
266 | void |
267 | kern_channel_event_notify(struct __kern_channel_ring *kring) |
268 | { |
269 | ASSERT(kring->ckr_tx == NR_TX); |
270 | |
271 | SK_DF(SK_VERB_EVENTS, "%s(%d) na \"%s\" (0x%llx) kr 0x%llx" , |
272 | sk_proc_name_address(current_proc()), sk_proc_pid(current_proc()), |
273 | KRNA(kring)->na_name, SK_KVA(KRNA(kring)), SK_KVA(kring)); |
274 | |
275 | na_post_event(kring, TRUE, FALSE, FALSE, CHAN_FILT_HINT_CHANNEL_EVENT); |
276 | } |
277 | |
278 | /* sync routine for the event ring */ |
279 | int |
280 | kern_channel_event_sync(struct __kern_channel_ring *kring, struct proc *p, |
281 | uint32_t flags) |
282 | { |
283 | #pragma unused(p, flags) |
284 | (void) kr_reclaim(kr: kring); |
285 | return 0; |
286 | } |
287 | |