static bool udp_ping_register(const struct guid *muid, host_addr_t addr, uint16 port, udp_ping_cb_t cb, void *data, bool multiple) { struct udp_ping *ping; uint length; g_assert(muid); g_return_val_if_fail(udp_pings, FALSE); if (hash_list_contains(udp_pings, muid)) { /* Probably a duplicate */ return FALSE; } /* random early drop */ length = hash_list_length(udp_pings); if (length >= UDP_PING_MAX) { return FALSE; } else if (length > (UDP_PING_MAX / 4) * 3) { if (random_value(UDP_PING_MAX - 1) < length) return FALSE; } WALLOC(ping); ping->muid = *muid; ping->added = tm_time(); { gnet_host_t host; gnet_host_set(&host, addr, port); ping->host = atom_host_get(&host); } if (cb != NULL) { WALLOC0(ping->callback); ping->callback->cb = cb; ping->callback->data = data; ping->callback->multiple = booleanize(multiple); } else { ping->callback = NULL; } hash_list_append(udp_pings, ping); return TRUE; }
/** * Send message (eslist iterator callback). * * @return TRUE if message was sent and freed up. */ static bool udp_tx_desc_send(void *data, void *udata) { struct udp_tx_desc *txd = data; udp_sched_t *us = udata; unsigned prio; udp_sched_check(us); udp_tx_desc_check(txd); if (us->used_all) return FALSE; /* * Avoid flushing consecutive queued messages to the same destination, * for regular (non-prioritary) messages. * * This serves two purposes: * * 1- It makes sure one single host does not capture all the available * outgoing bandwidth. * * 2- It somehow delays consecutive packets to a given host thereby reducing * flooding and hopefully avoiding saturation of its RX flow. */ prio = pmsg_prio(txd->mb); if (PMSG_P_DATA == prio && hset_contains(us->seen, txd->to)) { udp_sched_log(2, "%p: skipping mb=%p (%d bytes) to %s", us, txd->mb, pmsg_size(txd->mb), gnet_host_to_string(txd->to)); return FALSE; } if (udp_sched_mb_sendto(us, txd->mb, txd->to, txd->tx, txd->cb)) { if (PMSG_P_DATA == prio && pmsg_was_sent(txd->mb)) hset_insert(us->seen, atom_host_get(txd->to)); } else { return FALSE; /* Unsent, leave it in the queue */ } us->buffered = size_saturate_sub(us->buffered, pmsg_size(txd->mb)); udp_tx_desc_flag_release(txd, us); return TRUE; }
/** * Send datagram. * * @param us the UDP scheduler responsible for sending the datagram * @param mb the message to send * @param to the IP:port destination of the message * @param tx the TX stack sending the message * @param cb callback actions on the datagram * * @return 0 if message was unsent, length of message if sent, queued or * dropped. */ size_t udp_sched_send(udp_sched_t *us, pmsg_t *mb, const gnet_host_t *to, const txdrv_t *tx, const struct tx_dgram_cb *cb) { int len; struct udp_tx_desc *txd; uint prio; len = pmsg_size(mb); /* * Try to send immediately if we have bandwidth. */ if (!us->used_all && udp_sched_mb_sendto(us, mb, to, tx, cb)) return len; /* Message "sent" */ /* * If we already have enough data enqueued, flow-control the upper * layer by acting as if we do not have enough bandwidth. * * However, we now always accept traffic sent with the highest priority * since it is important to send those as soon as possible, i.e. ahead * of any other pending data we would otherwise flush locally before * servicing upper queues. * --RAM, 2012-10-12 */ prio = pmsg_prio(mb); if ( PMSG_P_HIGHEST != prio && us->buffered >= UDP_SCHED_FACTOR * udp_sched_bw_per_second(us) ) { udp_sched_log(1, "%p: flow-controlled", us); us->flow_controlled = TRUE; return 0; /* Flow control upper layers */ } /* * Message is going to be enqueued. * * However, from the upper layers (the message queue in particular), * the message is considered as being sent, and therefore these layers * are going to call pmsg_free() on the message. * * We do not want to pmsg_clone() the message because that would render * uses of pmsg_was_sent() useless in free routines, and upper layers * would think the message was dropped if they installed a free routine * on the message. * * Hence we use pmsg_ref(). */ txd = palloc(us->txpool); txd->magic = UDP_TX_DESC_MAGIC; txd->mb = pmsg_ref(mb); /* Take ownership of message */ txd->to = atom_host_get(to); txd->tx = tx; txd->cb = cb; txd->expire = time_advance(tm_time(), UDP_SCHED_EXPIRE); udp_sched_log(4, "%p: queuing mb=%p (%d bytes) prio=%u", us, mb, pmsg_size(mb), pmsg_prio(mb)); /* * The queue used is a LIFO to avoid buffering delaying all the messages. * Since UDP traffic is unordered, it's better to send the most recent * datagrams first, to reduce the perceived average latency. */ g_assert(prio < N_ITEMS(us->lifo)); eslist_prepend(&us->lifo[prio], txd); us->buffered = size_saturate_add(us->buffered, len); return len; /* Message queued, but tell upper layers it's sent */ }