xref: /minix/minix/lib/libnetdriver/netdriver.c (revision 9f988b79)
1 /* The device-independent network driver framework. */
2 
3 #include <minix/drivers.h>
4 #include <minix/endpoint.h>
5 #include <minix/netdriver.h>
6 #include <minix/ds.h>
7 #include <assert.h>
8 
9 #include "netdriver.h"
10 
11 static const struct netdriver *netdriver_table = NULL;
12 
13 static int running;
14 
15 static int conf_expected;
16 
17 static endpoint_t pending_endpt;
18 static struct netdriver_data pending_recv, pending_send;
19 
20 static int defer_reply;
21 static unsigned int pending_flags;
22 static size_t pending_size;
23 
24 static ether_addr_t hw_addr;
25 
26 /*
27  * Announce we are up after a fresh start or restart.
28  */
29 void
30 netdriver_announce(void)
31 {
32 	const char *driver_prefix = "drv.net.";
33 	char label[DS_MAX_KEYLEN];
34 	char key[DS_MAX_KEYLEN];
35 	int r;
36 
37 	/* Publish a driver up event. */
38 	if ((r = ds_retrieve_label_name(label, sef_self())) != OK)
39 		panic("netdriver: unable to get own label: %d", r);
40 
41 	snprintf(key, sizeof(key), "%s%s", driver_prefix, label);
42 	if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
43 		panic("netdriver: unable to publish driver up event: %d", r);
44 }
45 
46 /*
47  * Prepare for copying.  Given a flat offset, return the vector element index
48  * and an offset into that element.  Panic if the request does not fall
49  * entirely within the vector.
50  */
51 size_t
52 netdriver_prepare_copy(struct netdriver_data * data, size_t off, size_t size,
53 	unsigned int * indexp)
54 {
55 	unsigned int i;
56 
57 	assert(data->size > 0);
58 
59 	/*
60 	 * In theory we could truncate when copying out, but this creates a
61 	 * problem for port-based I/O, where the size of the transfer is
62 	 * typically specified in advance.  We could do extra port-based I/O
63 	 * to discard the extra bytes, but the driver is better off doing such
64 	 * truncation itself.  Thus, we disallow copying (in and out) beyond
65 	 * the given data vector altogether.
66 	 */
67 	if (off + size > data->size)
68 		panic("netdriver: request to copy beyond data size");
69 
70 	/*
71 	 * Find the starting offset in the vector.  If this turns out to be
72 	 * expensive, this can be adapted to store the last <element,offset>
73 	 * pair in the "data" structure (this is the reason it is not 'const').
74 	 */
75 	for (i = 0; i < data->count; i++) {
76 		assert(data->iovec[i].iov_size > 0);
77 
78 		if (off >= data->iovec[i].iov_size)
79 			off -= data->iovec[i].iov_size;
80 		else
81 			break;
82 	}
83 
84 	assert(i < data->count);
85 
86 	*indexp = i;
87 	return off;
88 }
89 
90 /*
91  * Copy in or out packet data from/to a vector of grants.
92  */
93 static void
94 netdriver_copy(struct netdriver_data * data, size_t off, vir_bytes addr,
95 	size_t size, int copyin)
96 {
97 	struct vscp_vec vec[SCPVEC_NR];
98 	size_t chunk;
99 	unsigned int i, v;
100 	int r;
101 
102 	off = netdriver_prepare_copy(data, off, size, &i);
103 
104 	/* Generate a new vector with all the individual copies to make. */
105 	for (v = 0; size > 0; v++) {
106 		chunk = data->iovec[i].iov_size - off;
107 		if (chunk > size)
108 			chunk = size;
109 		assert(chunk > 0);
110 
111 		/*
112 		 * We should be able to fit the entire I/O request in a single
113 		 * copy vector.  If not, MINIX3 has been misconfigured.
114 		 */
115 		if (v >= SCPVEC_NR)
116 			panic("netdriver: invalid vector size constant");
117 
118 		if (copyin) {
119 			vec[v].v_from = data->endpt;
120 			vec[v].v_to = SELF;
121 		} else {
122 			vec[v].v_from = SELF;
123 			vec[v].v_to = data->endpt;
124 		}
125 		vec[v].v_gid = data->iovec[i].iov_grant;
126 		vec[v].v_offset = off;
127 		vec[v].v_addr = addr;
128 		vec[v].v_bytes = chunk;
129 
130 		i++;
131 		off = 0;
132 		addr += chunk;
133 		size -= chunk;
134 	}
135 
136 	assert(v > 0 && v <= SCPVEC_NR);
137 
138 	/*
139 	 * If only one vector element was generated, use a direct copy.  This
140 	 * saves the kernel from having to copy in the vector.
141 	 */
142 	if (v == 1) {
143 		if (copyin)
144 			r = sys_safecopyfrom(vec->v_from, vec->v_gid,
145 			    vec->v_offset, vec->v_addr, vec->v_bytes);
146 		else
147 			r = sys_safecopyto(vec->v_to, vec->v_gid,
148 			    vec->v_offset, vec->v_addr, vec->v_bytes);
149 	} else
150 		r = sys_vsafecopy(vec, v);
151 
152 	if (r != OK)
153 		panic("netdriver: unable to copy data: %d", r);
154 }
155 
156 /*
157  * Copy in packet data.
158  */
159 void
160 netdriver_copyin(struct netdriver_data * __restrict data, size_t off,
161 	void * __restrict ptr, size_t size)
162 {
163 
164 	netdriver_copy(data, off, (vir_bytes)ptr, size, TRUE /*copyin*/);
165 }
166 
167 /*
168  * Copy out packet data.
169  */
170 void
171 netdriver_copyout(struct netdriver_data * __restrict data, size_t off,
172 	const void * __restrict ptr, size_t size)
173 {
174 
175 	netdriver_copy(data, off, (vir_bytes)ptr, size, FALSE /*copyin*/);
176 }
177 
178 /*
179  * Send a reply to a request.
180  */
181 static void
182 send_reply(endpoint_t endpt, message * m_ptr)
183 {
184 	int r;
185 
186 	if ((r = ipc_send(endpt, m_ptr)) != OK)
187 		panic("netdriver: unable to send to %d: %d", endpt, r);
188 }
189 
190 /*
191  * Defer sending any replies to task requests until the next call to
192  * check_replies().  The purpose of this is aggregation of task replies to both
193  * send and receive requests into a single reply message, which saves on
194  * messages, in particular when processing interrupts.
195  */
196 static void
197 defer_replies(void)
198 {
199 
200 	assert(netdriver_table != NULL);
201 	assert(defer_reply == FALSE);
202 
203 	defer_reply = TRUE;
204 }
205 
206 /*
207  * Check if we have to reply to earlier task (I/O) requests, and if so, send
208  * the reply.  If deferred is FALSE and the call to this function was preceded
209  * by a call to defer_replies(), do not send a reply yet.  If always_send is
210  * TRUE, send a reply even if no tasks have completed yet.
211  */
212 static void
213 check_replies(int deferred, int always_send)
214 {
215 	message m_reply;
216 
217 	if (defer_reply && !deferred)
218 		return;
219 
220 	defer_reply = FALSE;
221 
222 	if (pending_flags == 0 && !always_send)
223 		return;
224 
225 	assert(pending_endpt != NONE);
226 
227 	memset(&m_reply, 0, sizeof(m_reply));
228 	m_reply.m_type = DL_TASK_REPLY;
229 	m_reply.m_netdrv_net_dl_task.flags = pending_flags;
230 	m_reply.m_netdrv_net_dl_task.count = pending_size;
231 
232 	send_reply(pending_endpt, &m_reply);
233 
234 	pending_flags = 0;
235 	pending_size = 0;
236 }
237 
238 /*
239  * Resume receiving packets.  In particular, if a receive request was pending,
240  * call the driver's receive function.  If the call is successful, schedule
241  * sending a reply to the requesting party.
242  */
243 void
244 netdriver_recv(void)
245 {
246 	ssize_t r;
247 
248 	if (pending_recv.size == 0)
249 		return;
250 
251 	assert(netdriver_table != NULL);
252 
253 	/*
254 	 * For convenience of driver writers: if the receive function returns
255 	 * zero, simply call it again, to simplify discarding invalid packets.
256 	 */
257 	do {
258 		r = netdriver_table->ndr_recv(&pending_recv,
259 		    pending_recv.size);
260 
261 		/*
262 		 * The default policy is: drop undersized packets, panic on
263 		 * oversized packets.  The driver may implement any other
264 		 * policy (e.g., pad small packets, drop or truncate large
265 		 * packets), but it should at least test against the given
266 		 * 'max' value.  The reason that truncation should be
267 		 * implemented in the driver rather than here, is explained in
268 		 * an earlier comment about truncating copy operations.
269 		 */
270 		if (r >= 0 && r < ETH_MIN_PACK_SIZE)
271 			r = 0;
272 		else if (r > (ssize_t)pending_recv.size)
273 			panic("netdriver: oversized packet returned: %zd", r);
274 	} while (r == 0);
275 
276 	if (r == SUSPEND)
277 		return;
278 	if (r < 0)
279 		panic("netdriver: driver reported receive failure: %d", r);
280 
281 	assert(r >= ETH_MIN_PACK_SIZE && (size_t)r <= pending_recv.size);
282 
283 	pending_flags |= DL_PACK_RECV;
284 	pending_size = r;
285 
286 	pending_recv.size = 0;
287 
288 	check_replies(FALSE /*deferred*/, FALSE /*always_send*/);
289 }
290 
291 /*
292  * Resume sending packets.  In particular, if a send request was pending, call
293  * the driver's send function.  If the call is successful, schedule sending a
294  * reply to the requesting party.  This function relies on being called
295  * between init_pending() and check_pending().
296  */
297 void
298 netdriver_send(void)
299 {
300 	int r;
301 
302 	if (pending_send.size == 0)
303 		return;
304 
305 	assert(netdriver_table != NULL);
306 
307 	r = netdriver_table->ndr_send(&pending_send, pending_send.size);
308 
309 	if (r == SUSPEND)
310 		return;
311 	if (r < 0)
312 		panic("netdriver: driver reported send failure: %d", r);
313 
314 	pending_flags |= DL_PACK_SEND;
315 
316 	pending_send.size = 0;
317 
318 	check_replies(FALSE /*deferred*/, FALSE /*always_send*/);
319 }
320 
321 /*
322  * Process a request to receive or send a packet.
323  */
324 static void
325 do_readwrite(const struct netdriver * __restrict ndp, endpoint_t endpt,
326 	cp_grant_id_t grant, unsigned int count, int do_write)
327 {
328 	struct netdriver_data *data;
329 	unsigned int i;
330 	int r;
331 
332 	/* Copy in the I/O vector. */
333 	data = (do_write) ? &pending_send : &pending_recv;
334 
335 	if (data->size != 0)
336 		panic("netdriver: multiple concurrent requests");
337 
338 	if (count == 0 || count > NR_IOREQS)
339 		panic("netdriver: bad I/O vector count: %u", count);
340 
341 	data->endpt = endpt;
342 	data->count = count;
343 
344 	if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes)data->iovec,
345 	    sizeof(data->iovec[0]) * count)) != OK)
346 		panic("netdriver: unable to copy in I/O vector: %d", r);
347 
348 	for (i = 0; i < count; i++)
349 		data->size += data->iovec[i].iov_size;
350 
351 	if (data->size < ETH_MIN_PACK_SIZE ||
352 	    (!do_write && data->size < ETH_MAX_PACK_SIZE_TAGGED))
353 		panic("netdriver: invalid I/O vector size: %zu\n", data->size);
354 
355 	/* Save the endpoint to which we should reply. */
356 	if (pending_endpt != NONE && pending_endpt != endpt)
357 		panic("netdriver: multiple request sources");
358 	pending_endpt = endpt;
359 
360 	/* Resume sending or receiving. */
361 	defer_replies();
362 
363 	if (do_write)
364 		netdriver_send();
365 	else
366 		netdriver_recv();
367 
368 	/* Always send a reply in this case, even if no flags are set. */
369 	check_replies(TRUE /*deferred*/, TRUE /*always_send*/);
370 }
371 
372 /*
373  * Process a request to configure the driver, by setting its mode and obtaining
374  * its ethernet hardware address.  We already have the latter as a result of
375  * calling the ndr_init callback function.
376  */
377 static void
378 do_conf(const struct netdriver * __restrict ndp,
379 	const message * __restrict m_ptr)
380 {
381 	message m_reply;
382 
383 	if (ndp->ndr_mode != NULL)
384 		ndp->ndr_mode(m_ptr->m_net_netdrv_dl_conf.mode);
385 
386 	memset(&m_reply, 0, sizeof(m_reply));
387 	m_reply.m_type = DL_CONF_REPLY;
388 	m_reply.m_netdrv_net_dl_conf.stat = OK; /* legacy */
389 	memcpy(&m_reply.m_netdrv_net_dl_conf.hw_addr, &hw_addr,
390 	    sizeof(m_reply.m_netdrv_net_dl_conf.hw_addr));
391 
392 	send_reply(m_ptr->m_source, &m_reply);
393 }
394 
395 /*
396  * Process a request to obtain statistics from the driver.
397  */
398 static void
399 do_getstat(const struct netdriver * __restrict ndp,
400 	const message * __restrict m_ptr)
401 {
402 	message m_reply;
403 	eth_stat_t st;
404 	int r;
405 
406 	memset(&st, 0, sizeof(st));
407 
408 	if (ndp->ndr_stat != NULL)
409 		ndp->ndr_stat(&st);
410 
411 	if ((r = sys_safecopyto(m_ptr->m_source,
412 	    m_ptr->m_net_netdrv_dl_getstat_s.grant, 0, (vir_bytes)&st,
413 	    sizeof(st))) != OK)
414 		panic("netdriver: unable to copy out statistics: %d", r);
415 
416 	memset(&m_reply, 0, sizeof(m_reply));
417 	m_reply.m_type = DL_STAT_REPLY;
418 
419 	send_reply(m_ptr->m_source, &m_reply);
420 }
421 
422 /*
423  * Process an incoming message, and send a reply.
424  */
425 void
426 netdriver_process(const struct netdriver * __restrict ndp,
427 	const message * __restrict m_ptr, int ipc_status)
428 {
429 
430 	netdriver_table = ndp;
431 
432 	/* Check for notifications first. */
433 	if (is_ipc_notify(ipc_status)) {
434 		defer_replies();
435 
436 		switch (_ENDPOINT_P(m_ptr->m_source)) {
437 		case HARDWARE:
438 			if (ndp->ndr_intr != NULL)
439 				ndp->ndr_intr(m_ptr->m_notify.interrupts);
440 			break;
441 
442 		case CLOCK:
443 			if (ndp->ndr_alarm != NULL)
444 				ndp->ndr_alarm(m_ptr->m_notify.timestamp);
445 			break;
446 
447 		default:
448 			if (ndp->ndr_other != NULL)
449 				ndp->ndr_other(m_ptr, ipc_status);
450 		}
451 
452 		/*
453 		 * Any of the above calls may end up invoking netdriver_send()
454 		 * and/or netdriver_recv(), which may in turn have deferred
455 		 * sending a reply to an earlier request.  See if we have to
456 		 * send the reply now.
457 		 */
458 		check_replies(TRUE /*deferred*/, FALSE /*always_send*/);
459 	}
460 
461 	/*
462 	 * Discard datalink requests preceding a first DL_CONF request, so that
463 	 * after a driver restart, any in-flight request is discarded.  This is
464 	 * a rather blunt approach and must be revised if the protocol is ever
465 	 * made less inefficient (i.e. not strictly serialized).  Note that for
466 	 * correct driver operation it is important that non-datalink requests,
467 	 * interrupts in particular, do not go through this check.
468 	 */
469 	if (IS_DL_RQ(m_ptr->m_type) && conf_expected) {
470 		if (m_ptr->m_type != DL_CONF)
471 			return; /* do not send a reply */
472 
473 		conf_expected = FALSE;
474 	}
475 
476 	switch (m_ptr->m_type) {
477 	case DL_CONF:
478 		do_conf(ndp, m_ptr);
479 		break;
480 
481 	case DL_GETSTAT_S:
482 		do_getstat(ndp, m_ptr);
483 		break;
484 
485 	case DL_READV_S:
486 		do_readwrite(ndp, m_ptr->m_source,
487 		    m_ptr->m_net_netdrv_dl_readv_s.grant,
488 		    m_ptr->m_net_netdrv_dl_readv_s.count, FALSE /*do_write*/);
489 		break;
490 
491 	case DL_WRITEV_S:
492 		do_readwrite(ndp, m_ptr->m_source,
493 		    m_ptr->m_net_netdrv_dl_writev_s.grant,
494 		    m_ptr->m_net_netdrv_dl_writev_s.count, TRUE /*do_write*/);
495 		break;
496 
497 	default:
498 		defer_replies();
499 
500 		if (ndp->ndr_other != NULL)
501 			ndp->ndr_other(m_ptr, ipc_status);
502 
503 		/* As above: see if we have to send a reply now. */
504 		check_replies(TRUE /*deferred*/, FALSE /*always_send*/);
505 	}
506 }
507 
508 /*
509  * Perform initialization.  Return OK or an error code.
510  */
511 int
512 netdriver_init(const struct netdriver * ndp)
513 {
514 	unsigned int instance;
515 	long v;
516 	int r;
517 
518 	/* Initialize global variables. */
519 	pending_recv.size = 0;
520 	pending_send.size = 0;
521 	pending_endpt = NONE;
522 	defer_reply = FALSE;
523 	pending_flags = 0;
524 	pending_size = 0;
525 	conf_expected = TRUE;
526 
527 	/* Get the card instance number. */
528 	v = 0;
529 	(void)env_parse("instance", "d", 0, &v, 0, 255);
530 	instance = (unsigned int)v;
531 
532 	/* Call the initialization routine. */
533 	memset(&hw_addr, 0, sizeof(hw_addr));
534 
535 	if (ndp->ndr_init != NULL &&
536 	    (r = ndp->ndr_init(instance, &hw_addr)) != OK)
537 		return r;
538 
539 	/* Announce we are up! */
540 	netdriver_announce();
541 
542 	return OK;
543 }
544 
545 /*
546  * SEF initialization function.
547  */
548 static int
549 do_init(int __unused type, sef_init_info_t * __unused info)
550 {
551 	const struct netdriver *ndp;
552 
553 	ndp = netdriver_table;
554 	assert(ndp != NULL);
555 
556 	return netdriver_init(ndp);
557 }
558 
559 /*
560  * Break out of the main loop after finishing the current request.
561  */
562 void
563 netdriver_terminate(void)
564 {
565 
566 	if (netdriver_table != NULL && netdriver_table->ndr_stop != NULL)
567 		netdriver_table->ndr_stop();
568 
569 	running = FALSE;
570 
571 	sef_cancel();
572 }
573 
574 /*
575  * The process has received a signal.  See if we have to terminate.
576  */
577 static void
578 got_signal(int sig)
579 {
580 
581 	if (sig != SIGTERM)
582 		return;
583 
584 	netdriver_terminate();
585 }
586 
587 /*
588  * Main program of any network driver.
589  */
590 void
591 netdriver_task(const struct netdriver * ndp)
592 {
593 	message mess;
594 	int r, ipc_status;
595 
596 	/* Perform SEF initialization. */
597 	sef_setcb_init_fresh(do_init);
598 	sef_setcb_signal_handler(got_signal);
599 
600 	netdriver_table = ndp;
601 
602 	sef_startup();
603 
604 	netdriver_table = NULL;
605 
606 	/* The main message loop. */
607 	running = TRUE;
608 
609 	while (running) {
610 		if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
611 			if (r == EINTR)
612 				continue;	/* sef_cancel() was called */
613 
614 			panic("netdriver: sef_receive_status failed: %d", r);
615 		}
616 
617 		netdriver_process(ndp, &mess, ipc_status);
618 	}
619 }
620