/** * Free routine for the extended message blocks we send to the UDP layer. */ static void g2_qh2_pmsg_free(pmsg_t *mb, void *arg) { struct g2_qh2_pmsg_info *pmi = arg; gnutella_node_t *n; g2_qh2_pmsg_info_check(pmi); g_assert(pmsg_is_extended(mb)); if (pmsg_was_sent(mb)) goto done; /* * Message was unsent, probably because the UDP address in the /Q2 was * wrong for some reason. * * If we're still connected to the hub which passed us this /Q2, then * we can relay back the /QH2 to the hub and it will hopefully be able * to deliver it back to the querying node. */ n = node_by_id(pmi->hub_id); if (NULL == n) { if (GNET_PROPERTY(g2_debug) > 1) { g_debug("%s(): could not send %s, relaying hub is gone, dropping.", G_STRFUNC, g2_msg_infostr_mb(mb)); } gnet_stats_inc_general(GNR_UDP_G2_HITS_UNDELIVERED); goto done; } else { pmsg_t *nmb; if (GNET_PROPERTY(g2_debug) > 1) { g_debug("%s(): could not send %s, giving back to %s for relaying", G_STRFUNC, g2_msg_infostr_mb(mb), node_infostr(n)); } nmb = pmsg_clone_plain(mb); pmsg_clear_reliable(nmb); g2_node_send(n, nmb); gnet_stats_inc_general(GNR_UDP_G2_HITS_REROUTED_TO_HUB); } done: nid_unref(pmi->hub_id); pmi->magic = 0; WFREE(pmi); }
/** * 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; }
/** * Free routine for query hit message. */ static void dh_pmsg_free(pmsg_t *mb, void *arg) { struct dh_pmsg_info *pmi = arg; const struct guid *muid; dqhit_t *dh; g_assert(pmsg_is_extended(mb)); muid = gnutella_header_get_muid(pmsg_start(mb)); dh = dh_locate(muid); if (dh == NULL) goto cleanup; /* * It can happen that an initial query hit comes and is queued for * transmission, but the node is so clogged we don't actually send * it before the entry expires in our tracking tables. When we later * get the ACK that it was sent, we can therefore get obsolete data. * Hence we're very careful updating the stats, and we can't assert * that we're tracking everything correctly. * --RAM, 2004-09-04 */ if (pmsg_was_sent(mb)) dh->hits_sent += pmi->hits; if (dh->msg_queued == 0) /* We did not expect this ACK */ goto cleanup; dh->msg_queued--; if (dh->hits_queued >= pmi->hits) dh->hits_queued -= pmi->hits; /* FALL THROUGH */ cleanup: WFREE(pmi); }
/** * Enqueue message, which becomes owned by the queue. * * The data held in `to' is copied, so the structure can be reclaimed * immediately by the caller. */ void mq_udp_putq(mqueue_t *q, pmsg_t *mb, const gnet_host_t *to) { size_t size; char *mbs; uint8 function; pmsg_t *mbe = NULL; /* Extended message with destination info */ bool error = FALSE; mq_check_consistency(q); dump_tx_udp_packet(to, mb); again: mq_check_consistency(q); g_assert(mb); g_assert(!pmsg_was_sent(mb)); g_assert(pmsg_is_unread(mb)); g_assert(q->ops == &mq_udp_ops); /* Is an UDP queue */ /* * Trap messages enqueued whilst in the middle of an mq_clear() operation * by marking them as sent and dropping them. Idem if queue was * put in "discard" mode. */ if (q->flags & (MQ_CLEAR | MQ_DISCARD)) { pmsg_mark_sent(mb); /* Let them think it was sent */ pmsg_free(mb); /* Drop message */ return; } mq_check(q, 0); size = pmsg_size(mb); if (size == 0) { g_carp("%s: called with empty message", G_STRFUNC); goto cleanup; } /* * Protect against recursion: we must not invoke puthere() whilst in * the middle of another putq() or we would corrupt the qlink array: * Messages received during recursion are inserted into the qwait list * and will be stuffed back into the queue when the initial putq() ends. * --RAM, 2006-12-29 */ if (q->putq_entered > 0) { pmsg_t *extended; if (debugging(20)) g_warning("%s: %s recursion detected (%u already pending)", G_STRFUNC, mq_info(q), slist_length(q->qwait)); /* * We insert extended messages into the waiting queue since we need * the destination information as well. */ extended = mq_udp_attach_metadata(mb, to); slist_append(q->qwait, extended); return; } q->putq_entered++; mbs = pmsg_start(mb); function = gmsg_function(mbs); gnet_stats_count_queued(q->node, function, mbs, size); /* * If queue is empty, attempt a write immediatly. */ if (q->qhead == NULL) { ssize_t written; if (pmsg_check(mb, q)) { written = tx_sendto(q->tx_drv, mb, to); } else { gnet_stats_count_flowc(mbs, FALSE); node_inc_txdrop(q->node); /* Dropped during TX */ written = (ssize_t) -1; } if ((ssize_t) -1 == written) goto cleanup; node_add_tx_given(q->node, written); if ((size_t) written == size) { if (GNET_PROPERTY(mq_udp_debug) > 5) g_debug("MQ UDP sent %s", gmsg_infostr_full(pmsg_start(mb), pmsg_written_size(mb))); goto cleanup; } /* * Since UDP respects write boundaries, the following can never * happen in practice: either we write the whole datagram, or none * of it. */ if (written > 0) { g_warning( "partial UDP write (%zu bytes) to %s for %zu-byte datagram", written, gnet_host_to_string(to), size); goto cleanup; } /* FALL THROUGH */ } if (GNET_PROPERTY(mq_udp_debug) > 5) g_debug("MQ UDP queued %s", gmsg_infostr_full(pmsg_start(mb), pmsg_written_size(mb))); /* * Attach the destination information as metadata to the message, unless * it is already known (possible only during unfolding of the queued data * during re-entrant calls). * * This is later extracted via pmsg_get_metadata() on the extended * message by the message queue to get the destination information. * * Then enqueue the extended message. */ if (NULL == mbe) mbe = mq_udp_attach_metadata(mb, to); q->cops->puthere(q, mbe, size); mb = NULL; /* FALL THROUGH */ cleanup: if (mb) { pmsg_free(mb); mb = NULL; } /* * When reaching that point with a zero putq_entered counter, it means * we triggered an early error condition. Bail out. */ g_assert(q->putq_entered >= 0); if (q->putq_entered == 0) error = TRUE; else q->putq_entered--; mq_check(q, 0); /* * If we're exiting here with no other putq() registered, then we must * pop an item off the head of the list and iterate again. */ if (0 == q->putq_entered && !error) { mbe = slist_shift(q->qwait); if (mbe) { struct mq_udp_info *mi = pmsg_get_metadata(mbe); mb = mbe; /* An extended message "is-a" message */ to = &mi->to; if (debugging(20)) g_warning( "%s: %s flushing waiting to %s (%u still pending)", G_STRFUNC, mq_info(q), gnet_host_to_string(to), slist_length(q->qwait)); goto again; } } return; }
/** * Free routine for our extended message blocks. */ static void revent_pmsg_free(pmsg_t *mb, void *arg) { struct revent_pmsg_info *pmi = arg; struct revent_ops *ops; void *obj; pmi_check(pmi); g_assert(pmsg_is_extended(mb)); ops = pmi->ops; /* * It is possible that whilst the message was in the message queue, * the operation was terminated. Therefore, we need to ensure that the * recorded user is still alive. */ obj = (*ops->is_alive)(pmi->rid); if (NULL == obj) { if (*ops->debug > 2) g_debug("DHT %s[%s] late UDP message %s", ops->name, nid_to_string(&pmi->rid), pmsg_was_sent(mb) ? "sending" : "dropping"); goto cleanup; } /* * Signal message freeing, so that user structure can decrement the * amount of pending messsages if necessary. */ if (ops->freeing_msg) (*ops->freeing_msg)(obj); /* * If the RPC callback triggered before the UDP message queue could * process the message on the way out, then we don't need to do anything * as the RPC is already dead and has been processed as such... */ if (pmi->rpc_done) goto cleanup; pmi->rpi->pmi = NULL; /* Break x-ref as message was processed */ if (pmsg_was_sent(mb)) { knode_t *kn = pmi->kn; /* * Message was successfully sent from the queue. */ kn->last_sent = tm_time(); if (ops->msg_sent) (*ops->msg_sent)(obj, mb); if (*ops->debug > 4) g_debug("DHT %s[%s] sent %s (%d bytes) to %s, RTT=%u", ops->name, nid_to_string(&pmi->rid), kmsg_infostr(pmsg_phys_base(mb)), pmsg_written_size(mb), knode_to_string(kn), kn->rtt); } else { knode_t *kn = pmi->kn; guid_t *muid; if (*ops->debug > 2) g_debug("DHT %s[%s] message %s%u to %s dropped by UDP queue", ops->name, nid_to_string(&pmi->rid), ops->udata_name, pmi->rpi->udata, knode_to_string(kn)); /* * Message was not sent and dropped by the queue. */ if (ops->msg_dropped) (*ops->msg_dropped)(obj, kn, mb); /* * Cancel the RPC, since the message was never sent out... * The MUID is at the start of the message. */ g_assert(pmsg_written_size(mb) > GUID_RAW_SIZE); muid = cast_to_guid_ptr(pmsg_phys_base(mb)); dht_rpc_cancel(muid); if (ops->rpc_cancelled) (*ops->rpc_cancelled)(obj, pmi->rpi->udata); revent_rpi_free(pmi->rpi); /* Cancel does not invoke RPC callback */ } cleanup: revent_pmi_free(pmi); }