예제 #1
0
파일: udp.c 프로젝트: MrJoe/gtk-gnutella
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;
}
예제 #2
0
/**
 * 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;
}
예제 #3
0
/**
 * 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 */
}