Exemplo n.º 1
0
/*
 * Return the rix with the lowest average_tx_time,
 * or -1 if all the average_tx_times are 0.
 */
static __inline int
pick_best_rate(struct ath_node *an, const HAL_RATE_TABLE *rt,
    int size_bin, int require_acked_before)
{
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
        int best_rate_rix, best_rate_tt, best_rate_pct;
	uint32_t mask;
	int rix, tt, pct;

        best_rate_rix = 0;
        best_rate_tt = 0;
	best_rate_pct = 0;
	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
		if ((mask & 1) == 0)		/* not a supported rate */
			continue;

		/* Don't pick a non-HT rate for a HT node */
		if ((an->an_node.ni_flags & IEEE80211_NODE_HT) &&
		    (rt->info[rix].phy != IEEE80211_T_HT)) {
			continue;
		}

		tt = sn->stats[size_bin][rix].average_tx_time;
		if (tt <= 0 ||
		    (require_acked_before &&
		     !sn->stats[size_bin][rix].packets_acked))
			continue;

		/* Calculate percentage if possible */
		if (sn->stats[size_bin][rix].total_packets > 0) {
			pct = sn->stats[size_bin][rix].ewma_pct;
		} else {
			/* XXX for now, assume 95% ok */
			pct = 95;
		}

		/* don't use a bit-rate that has been failing */
		if (sn->stats[size_bin][rix].successive_failures > 3)
			continue;

		/*
		 * For HT, Don't use a bit rate that is much more
		 * lossy than the best.
		 *
		 * XXX this isn't optimal; it's just designed to
		 * eliminate rates that are going to be obviously
		 * worse.
		 */
		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
			if (best_rate_pct > (pct + 50))
				continue;
		}

		/*
		 * For non-MCS rates, use the current average txtime for
		 * comparison.
		 */
		if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) {
			if (best_rate_tt == 0 || tt <= best_rate_tt) {
				best_rate_tt = tt;
				best_rate_rix = rix;
				best_rate_pct = pct;
			}
		}

		/*
		 * Since 2 stream rates have slightly higher TX times,
		 * allow a little bit of leeway. This should later
		 * be abstracted out and properly handled.
		 */
		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
			if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) {
				best_rate_tt = tt;
				best_rate_rix = rix;
				best_rate_pct = pct;
			}
		}
        }
        return (best_rate_tt ? best_rate_rix : -1);
}
Exemplo n.º 2
0
/*
 * Pick a good "random" bit-rate to sample other than the current one.
 */
static __inline int
pick_sample_rate(struct sample_softc *ssc , struct ath_node *an,
    const HAL_RATE_TABLE *rt, int size_bin)
{
#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	int current_rix, rix;
	unsigned current_tt;
	uint32_t mask;
	
	current_rix = sn->current_rix[size_bin];
	if (current_rix < 0) {
		/* no successes yet, send at the lowest bit-rate */
		/* XXX should return MCS0 if HT */
		return 0;
	}

	current_tt = sn->stats[size_bin][current_rix].average_tx_time;

	rix = sn->last_sample_rix[size_bin]+1;	/* next sample rate */
	mask = sn->ratemask &~ (1<<current_rix);/* don't sample current rate */
	while (mask != 0) {
		if ((mask & (1<<rix)) == 0) {	/* not a supported rate */
	nextrate:
			if (++rix >= rt->rateCount)
				rix = 0;
			continue;
		}

		/* if the node is HT and the rate isn't HT, don't bother sample */
		if ((an->an_node.ni_flags & IEEE80211_NODE_HT) &&
		    (rt->info[rix].phy != IEEE80211_T_HT)) {
			mask &= ~(1<<rix);
			goto nextrate;
		}

		/* this bit-rate is always worse than the current one */
		if (sn->stats[size_bin][rix].perfect_tx_time > current_tt) {
			mask &= ~(1<<rix);
			goto nextrate;
		}

		/* rarely sample bit-rates that fail a lot */
		if (sn->stats[size_bin][rix].successive_failures > ssc->max_successive_failures &&
		    ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout) {
			mask &= ~(1<<rix);
			goto nextrate;
		}

		/*
		 * When doing aggregation, successive failures don't happen
		 * as often, as sometimes some of the sub-frames get through.
		 *
		 * If the sample rix average tx time is greater than the
		 * average tx time of the current rix, don't immediately use
		 * the rate for sampling.
		 */
		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
			if ((sn->stats[size_bin][rix].average_tx_time * 10 >
			    sn->stats[size_bin][current_rix].average_tx_time * 9) &&
			    (ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout)) {
				mask &= ~(1<<rix);
				goto nextrate;
			}
		}

		/*
		 * XXX TODO
		 * For HT, limit sample somehow?
		 */

		/* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */
		if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) {
			if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) {
				mask &= ~(1<<rix);
				goto nextrate;
			}
		}

		sn->last_sample_rix[size_bin] = rix;
		return rix;
	}
	return current_rix;
#undef DOT11RATE
#undef	MCS
}
Exemplo n.º 3
0
/*
 * Initialize the tables for a node.
 */
static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
#define	DOT11RATE(_ix)	(rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL)
	struct ath_node *an = ATH_NODE(ni);
	const struct ieee80211_txparam *tp = ni->ni_txparms;
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const HAL_RATE_TABLE *rt = sc->sc_currates;
#ifdef IEEE80211_DEBUG
	char ethstr[ETHER_ADDRSTRLEN + 1];
#endif
	int x, y, srate, rix;

	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));

	KASSERT(sc->sc_curmode < IEEE80211_MODE_MAX+2,
	    ("curmode %u", sc->sc_curmode));
	sn->sched = mrr_schedules[sc->sc_curmode];
	KASSERT(sn->sched != NULL,
	    ("no mrr schedule for mode %u", sc->sc_curmode));

        sn->static_rix = -1;
	if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
		/*
		 * A fixed rate is to be used; ucastrate is the IEEE code
		 * for this rate (sans basic bit).  Check this against the
		 * negotiated rate set for the node.  Note the fixed rate
		 * may not be available for various reasons so we only
		 * setup the static rate index if the lookup is successful.
		 * XXX handle MCS
		 */
		for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--)
			if (RATE(srate) == tp->ucastrate) {
				sn->static_rix = sc->sc_rixmap[tp->ucastrate];
				break;
			}
#ifdef IEEE80211_DEBUG
			if (sn->static_rix == -1) {
				IEEE80211_NOTE(ni->ni_vap,
				    IEEE80211_MSG_RATECTL, ni,
				    "%s: ucastrate %u not found, nrates %u",
				    __func__, tp->ucastrate,
				    ni->ni_rates.rs_nrates);
			}
#endif
	}

	/*
	 * Construct a bitmask of usable rates.  This has all
	 * negotiated rates minus those marked by the hal as
	 * to be ignored for doing rate control.
	 */
	sn->ratemask = 0;
	for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
		rix = sc->sc_rixmap[RATE(x)];
		if (rix == 0xff)
			continue;
		/* skip rates marked broken by hal */
		if (!rt->info[rix].valid)
			continue;
		KASSERT(rix < SAMPLE_MAXRATES,
		    ("rate %u has rix %d", RATE(x), rix));
		sn->ratemask |= 1<<rix;
	}
#ifdef IEEE80211_DEBUG
	if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) {
		uint32_t mask;

		ieee80211_note(ni->ni_vap, "[%s] %s: size 1600 rate/tt",
		    kether_ntoa(ni->ni_macaddr, ethstr), __func__);
		for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
			if ((mask & 1) == 0)
				continue;
			kprintf(" %d/%d", DOT11RATE(rix) / 2,
			    calc_usecs_unicast_packet(sc, 1600, rix, 0,0));
		}
		kprintf("\n");
	}
Exemplo n.º 4
0
/*
 * Initialize the tables for a node.
 */
static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
#define	DOT11RATE(_ix)	(rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL)
#define	MCS(_ix)	(ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS)
	struct ath_node *an = ATH_NODE(ni);
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	int x, y, rix;

	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));

	KASSERT(sc->sc_curmode < IEEE80211_MODE_MAX+2,
	    ("curmode %u", sc->sc_curmode));
	sn->sched = mrr_schedules[sc->sc_curmode];
	KASSERT(sn->sched != NULL,
	    ("no mrr schedule for mode %u", sc->sc_curmode));

        sn->static_rix = -1;
	ath_rate_update_static_rix(sc, ni);

	/*
	 * Construct a bitmask of usable rates.  This has all
	 * negotiated rates minus those marked by the hal as
	 * to be ignored for doing rate control.
	 */
	sn->ratemask = 0;
	/* MCS rates */
	if (ni->ni_flags & IEEE80211_NODE_HT) {
		for (x = 0; x < ni->ni_htrates.rs_nrates; x++) {
			rix = sc->sc_rixmap[MCS(x)];
			if (rix == 0xff)
				continue;
			/* skip rates marked broken by hal */
			if (!rt->info[rix].valid)
				continue;
			KASSERT(rix < SAMPLE_MAXRATES,
			    ("mcs %u has rix %d", MCS(x), rix));
			sn->ratemask |= 1<<rix;
		}
	}

	/* Legacy rates */
	for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
		rix = sc->sc_rixmap[RATE(x)];
		if (rix == 0xff)
			continue;
		/* skip rates marked broken by hal */
		if (!rt->info[rix].valid)
			continue;
		KASSERT(rix < SAMPLE_MAXRATES,
		    ("rate %u has rix %d", RATE(x), rix));
		sn->ratemask |= 1<<rix;
	}
#ifdef IEEE80211_DEBUG
	if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) {
		uint32_t mask;

		ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt",
		    ni->ni_macaddr, ":", __func__);
		for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
			if ((mask & 1) == 0)
				continue;
			printf(" %d %s/%d", dot11rate(rt, rix), dot11rate_label(rt, rix),
			    calc_usecs_unicast_packet(sc, 1600, rix, 0,0,
			        (ni->ni_chw == 40)));
		}
		printf("\n");
	}
Exemplo n.º 5
0
static void
update_stats(struct ath_softc *sc, struct ath_node *an, 
		  int frame_size,
		  int rix0, int tries0,
		  int rix1, int tries1,
		  int rix2, int tries2,
		  int rix3, int tries3,
		  int short_tries, int tries, int status)
{
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
	const int size_bin = size_to_bin(frame_size);
	const int size = bin_to_size(size_bin);
	int tt, tries_so_far;

	if (!IS_RATE_DEFINED(sn, rix0))
		return;
	tt = calc_usecs_unicast_packet(sc, size, rix0, short_tries,
		MIN(tries0, tries) - 1);
	tries_so_far = tries0;

	if (tries1 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix1))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix1, short_tries,
			MIN(tries1 + tries_so_far, tries) - tries_so_far - 1);
		tries_so_far += tries1;
	}

	if (tries2 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix2))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix2, short_tries,
			MIN(tries2 + tries_so_far, tries) - tries_so_far - 1);
		tries_so_far += tries2;
	}

	if (tries3 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix3))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix3, short_tries,
			MIN(tries3 + tries_so_far, tries) - tries_so_far - 1);
	}

	if (sn->stats[size_bin][rix0].total_packets < ssc->smoothing_minpackets) {
		/* just average the first few packets */
		int avg_tx = sn->stats[size_bin][rix0].average_tx_time;
		int packets = sn->stats[size_bin][rix0].total_packets;
		sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+1);
	} else {
		/* use a ewma */
		sn->stats[size_bin][rix0].average_tx_time = 
			((sn->stats[size_bin][rix0].average_tx_time * ssc->smoothing_rate) + 
			 (tt * (100 - ssc->smoothing_rate))) / 100;
	}
	
	if (status != 0) {
		int y;
		sn->stats[size_bin][rix0].successive_failures++;
		for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) {
			/*
			 * Also say larger packets failed since we
			 * assume if a small packet fails at a
			 * bit-rate then a larger one will also.
			 */
			sn->stats[y][rix0].successive_failures++;
			sn->stats[y][rix0].last_tx = ticks;
			sn->stats[y][rix0].tries += tries;
			sn->stats[y][rix0].total_packets++;
		}
	} else {
		sn->stats[size_bin][rix0].packets_acked++;
		sn->stats[size_bin][rix0].successive_failures = 0;
	}
	sn->stats[size_bin][rix0].tries += tries;
	sn->stats[size_bin][rix0].last_tx = ticks;
	sn->stats[size_bin][rix0].total_packets++;

	if (rix0 == sn->current_sample_rix[size_bin]) {
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		   &an->an_node,
"%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)", 
		    __func__, 
		    size,
		    status ? "FAIL" : "OK",
		    rix0, short_tries, tries, tt, 
		    sn->stats[size_bin][rix0].average_tx_time,
		    sn->stats[size_bin][rix0].perfect_tx_time);
		sn->sample_tt[size_bin] = tt;
		sn->current_sample_rix[size_bin] = -1;
	}
}
Exemplo n.º 6
0
void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
	const struct ath_buf *bf)
{
	struct ifnet *ifp = sc->sc_ifp;
	struct ieee80211com *ic = ifp->if_l2com;
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
	const struct ath_desc *ds0 = &bf->bf_desc[0];
	int final_rix, short_tries, long_tries, frame_size;
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	int mrr;

	final_rix = rt->rateCodeToIndex[ts->ts_rate];
	short_tries = ts->ts_shortretry;
	long_tries = ts->ts_longretry + 1;
	frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
	if (frame_size == 0)		    /* NB: should not happen */
		frame_size = 1500;

	if (sn->ratemask == 0) {
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		    &an->an_node,
		    "%s: size %d %s rate/try %d/%d no rates yet", 
		    __func__,
		    bin_to_size(size_to_bin(frame_size)),
		    ts->ts_status ? "FAIL" : "OK",
		    short_tries, long_tries);
		return;
	}
	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);
	if (!mrr || ts->ts_finaltsi == 0) {
		if (!IS_RATE_DEFINED(sn, final_rix)) {
			badrate(ifp, 0, ts->ts_rate, long_tries, ts->ts_status);
			return;
		}
		/*
		 * Only one rate was used; optimize work.
		 */
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		     &an->an_node, "%s: size %d %s rate/try %d/%d/%d",
		     __func__,
		     bin_to_size(size_to_bin(frame_size)),
		     ts->ts_status ? "FAIL" : "OK",
		     final_rix, short_tries, long_tries);
		update_stats(sc, an, frame_size, 
			     final_rix, long_tries,
			     0, 0,
			     0, 0,
			     0, 0,
			     short_tries, long_tries, ts->ts_status);
	} else {
		int hwrate0, rix0, tries0;
		int hwrate1, rix1, tries1;
		int hwrate2, rix2, tries2;
		int hwrate3, rix3, tries3;
		int finalTSIdx = ts->ts_finaltsi;

		/*
		 * Process intermediate rates that failed.
		 */
		if (sc->sc_ah->ah_magic != 0x20065416) {
			hwrate0 = MS(ds0->ds_ctl3, AR_XmitRate0);
			hwrate1 = MS(ds0->ds_ctl3, AR_XmitRate1);
			hwrate2 = MS(ds0->ds_ctl3, AR_XmitRate2);
			hwrate3 = MS(ds0->ds_ctl3, AR_XmitRate3);
		} else {
			hwrate0 = MS(ds0->ds_ctl3, AR5416_XmitRate0);
			hwrate1 = MS(ds0->ds_ctl3, AR5416_XmitRate1);
			hwrate2 = MS(ds0->ds_ctl3, AR5416_XmitRate2);
			hwrate3 = MS(ds0->ds_ctl3, AR5416_XmitRate3);
		}

		rix0 = rt->rateCodeToIndex[hwrate0];
		tries0 = MS(ds0->ds_ctl2, AR_XmitDataTries0);

		rix1 = rt->rateCodeToIndex[hwrate1];
		tries1 = MS(ds0->ds_ctl2, AR_XmitDataTries1);

		rix2 = rt->rateCodeToIndex[hwrate2];
		tries2 = MS(ds0->ds_ctl2, AR_XmitDataTries2);

		rix3 = rt->rateCodeToIndex[hwrate3];
		tries3 = MS(ds0->ds_ctl2, AR_XmitDataTries3);

		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		    &an->an_node,
"%s: size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]", 
		     __func__,
		     bin_to_size(size_to_bin(frame_size)),
		     finalTSIdx,
		     long_tries, 
		     ts->ts_status ? "FAIL" : "OK",
		     rix0, tries0,
		     rix1, tries1,
		     rix2, tries2,
		     rix3, tries3);

		if (tries0 && !IS_RATE_DEFINED(sn, rix0))
			badrate(ifp, 0, hwrate0, tries0, ts->ts_status);
		if (tries1 && !IS_RATE_DEFINED(sn, rix1))
			badrate(ifp, 1, hwrate1, tries1, ts->ts_status);
		if (tries2 && !IS_RATE_DEFINED(sn, rix2))
			badrate(ifp, 2, hwrate2, tries2, ts->ts_status);
		if (tries3 && !IS_RATE_DEFINED(sn, rix3))
			badrate(ifp, 3, hwrate3, tries3, ts->ts_status);

		/*
		 * NB: series > 0 are not penalized for failure
		 * based on the try counts under the assumption
		 * that losses are often bursty and since we
		 * sample higher rates 1 try at a time doing so
		 * may unfairly penalize them.
		 */
		if (tries0) {
			update_stats(sc, an, frame_size, 
				     rix0, tries0, 
				     rix1, tries1, 
				     rix2, tries2, 
				     rix3, tries3, 
				     short_tries, long_tries, 
				     long_tries > tries0);
			long_tries -= tries0;
		}
		
		if (tries1 && finalTSIdx > 0) {
			update_stats(sc, an, frame_size, 
				     rix1, tries1, 
				     rix2, tries2, 
				     rix3, tries3, 
				     0, 0, 
				     short_tries, long_tries, 
				     ts->ts_status);
			long_tries -= tries1;
		}

		if (tries2 && finalTSIdx > 1) {
			update_stats(sc, an, frame_size, 
				     rix2, tries2, 
				     rix3, tries3, 
				     0, 0,
				     0, 0,
				     short_tries, long_tries, 
				     ts->ts_status);
			long_tries -= tries2;
		}

		if (tries3 && finalTSIdx > 2) {
			update_stats(sc, an, frame_size, 
				     rix3, tries3, 
				     0, 0,
				     0, 0,
				     0, 0,
				     short_tries, long_tries, 
				     ts->ts_status);
		}
	}
}
Exemplo n.º 7
0
void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
                     const struct ath_rc_series *rc, const struct ath_tx_status *ts,
                     int frame_size, int nframes, int nbad)
{
    struct ifnet *ifp = sc->sc_ifp;
    struct ieee80211com *ic = ifp->if_l2com;
    struct sample_node *sn = ATH_NODE_SAMPLE(an);
    int final_rix, short_tries, long_tries;
    const HAL_RATE_TABLE *rt = sc->sc_currates;
    int status = ts->ts_status;
    int mrr;

    final_rix = rt->rateCodeToIndex[ts->ts_rate];
    short_tries = ts->ts_shortretry;
    long_tries = ts->ts_longretry + 1;

    if (nframes == 0) {
        device_printf(sc->sc_dev, "%s: nframes=0?\n", __func__);
        return;
    }

    if (frame_size == 0)		    /* NB: should not happen */
        frame_size = 1500;

    if (sn->ratemask == 0) {
        IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
                       &an->an_node,
                       "%s: size %d %s rate/try %d/%d no rates yet",
                       __func__,
                       bin_to_size(size_to_bin(frame_size)),
                       status ? "FAIL" : "OK",
                       short_tries, long_tries);
        return;
    }
    mrr = sc->sc_mrretry;
    /* XXX check HT protmode too */
    if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot))
        mrr = 0;

    if (!mrr || ts->ts_finaltsi == 0) {
        if (!IS_RATE_DEFINED(sn, final_rix)) {
            device_printf(sc->sc_dev, "%s: ts_rate=%d ts_finaltsi=%d\n",
                          __func__, ts->ts_rate, ts->ts_finaltsi);
            badrate(ifp, 0, ts->ts_rate, long_tries, status);
            return;
        }
        /*
         * Only one rate was used; optimize work.
         */
        IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
                       &an->an_node, "%s: size %d (%d bytes) %s rate/short/long %d %s/%d/%d nframes/nbad [%d/%d]",
                       __func__,
                       bin_to_size(size_to_bin(frame_size)),
                       frame_size,
                       status ? "FAIL" : "OK",
                       dot11rate(rt, final_rix), dot11rate_label(rt, final_rix),
                       short_tries, long_tries, nframes, nbad);
        update_stats(sc, an, frame_size,
                     final_rix, long_tries,
                     0, 0,
                     0, 0,
                     0, 0,
                     short_tries, long_tries, status,
                     nframes, nbad);

    } else {
        int finalTSIdx = ts->ts_finaltsi;
        int i;

        /*
         * Process intermediate rates that failed.
         */

        IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
                       &an->an_node,
                       "%s: size %d (%d bytes) finaltsidx %d short %d long %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d] nframes/nbad [%d/%d]",
                       __func__,
                       bin_to_size(size_to_bin(frame_size)),
                       frame_size,
                       finalTSIdx,
                       short_tries,
                       long_tries,
                       status ? "FAIL" : "OK",
                       dot11rate(rt, rc[0].rix),
                       dot11rate_label(rt, rc[0].rix), rc[0].tries,
                       dot11rate(rt, rc[1].rix),
                       dot11rate_label(rt, rc[1].rix), rc[1].tries,
                       dot11rate(rt, rc[2].rix),
                       dot11rate_label(rt, rc[2].rix), rc[2].tries,
                       dot11rate(rt, rc[3].rix),
                       dot11rate_label(rt, rc[3].rix), rc[3].tries,
                       nframes, nbad);

        for (i = 0; i < 4; i++) {
            if (rc[i].tries && !IS_RATE_DEFINED(sn, rc[i].rix))
                badrate(ifp, 0, rc[i].ratecode, rc[i].tries,
                        status);
        }

        /*
         * NB: series > 0 are not penalized for failure
         * based on the try counts under the assumption
         * that losses are often bursty and since we
         * sample higher rates 1 try at a time doing so
         * may unfairly penalize them.
         */
        if (rc[0].tries) {
            update_stats(sc, an, frame_size,
                         rc[0].rix, rc[0].tries,
                         rc[1].rix, rc[1].tries,
                         rc[2].rix, rc[2].tries,
                         rc[3].rix, rc[3].tries,
                         short_tries, long_tries,
                         long_tries > rc[0].tries,
                         nframes, nbad);
            long_tries -= rc[0].tries;
        }

        if (rc[1].tries && finalTSIdx > 0) {
            update_stats(sc, an, frame_size,
                         rc[1].rix, rc[1].tries,
                         rc[2].rix, rc[2].tries,
                         rc[3].rix, rc[3].tries,
                         0, 0,
                         short_tries, long_tries,
                         status,
                         nframes, nbad);
            long_tries -= rc[1].tries;
        }

        if (rc[2].tries && finalTSIdx > 1) {
            update_stats(sc, an, frame_size,
                         rc[2].rix, rc[2].tries,
                         rc[3].rix, rc[3].tries,
                         0, 0,
                         0, 0,
                         short_tries, long_tries,
                         status,
                         nframes, nbad);
            long_tries -= rc[2].tries;
        }

        if (rc[3].tries && finalTSIdx > 2) {
            update_stats(sc, an, frame_size,
                         rc[3].rix, rc[3].tries,
                         0, 0,
                         0, 0,
                         0, 0,
                         short_tries, long_tries,
                         status,
                         nframes, nbad);
        }
    }
}
Exemplo n.º 8
0
/*
 * Initialize the tables for a node.
 */
static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
	struct ieee80211com *ic = &sc->sc_ic;
	struct ath_node *an = ATH_NODE(ni);
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	int x, y, srate;

	KASSERTMSG(rt != NULL, "no rate table, mode %u", sc->sc_curmode);
        sn->static_rate_ndx = -1;
	if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
		/*
		 * A fixed rate is to be used; ic_fixed_rate is an
		 * index into the supported rate set.  Convert this
		 * to the index into the negotiated rate set for
		 * the node.  We know the rate is there because the
		 * rate set is checked when the station associates.
		 */
		const struct ieee80211_rateset *rs =
			&ic->ic_sup_rates[ic->ic_curmode];
		int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
		/* NB: the rate set is assumed sorted */
		srate = ni->ni_rates.rs_nrates - 1;
		for (; srate >= 0 && RATE(srate) != r; srate--)
			;
		KASSERTMSG(srate >= 0,
			"fixed rate %d not in rate set", ic->ic_fixed_rate);
                sn->static_rate_ndx = srate;
	}

        DPRINTF(sc, "%s: %s size 1600 rate/tt", __func__, ether_sprintf(ni->ni_macaddr));

	sn->num_rates = ni->ni_rates.rs_nrates;
        for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
		sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
		sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
		sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
		sn->rates[x].shortPreambleRateCode = 
			rt->info[sn->rates[x].rix].rateCode | 
			rt->info[sn->rates[x].rix].shortPreamble;

		DPRINTF(sc, " %d/%d", sn->rates[x].rate,
			calc_usecs_unicast_packet(sc, 1600, sn->rates[x].rix, 
						  0,0));
	}
	DPRINTF(sc, "%s\n", "");
	
	/* set the visible bit-rate to the lowest one available */
	ni->ni_txrate = 0;
	sn->num_rates = ni->ni_rates.rs_nrates;
	
	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
		int size = bin_to_size(y);
		int ndx = 0;
		sn->packets_sent[y] = 0;
		sn->current_sample_ndx[y] = -1;
		sn->last_sample_ndx[y] = 0;
		
		for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
			sn->stats[y][x].successive_failures = 0;
			sn->stats[y][x].tries = 0;
			sn->stats[y][x].total_packets = 0;
			sn->stats[y][x].packets_acked = 0;
			sn->stats[y][x].last_tx = 0;
			
			sn->stats[y][x].perfect_tx_time = 
				calc_usecs_unicast_packet(sc, size, 
							  sn->rates[x].rix,
							  0, 0);
			sn->stats[y][x].average_tx_time = sn->stats[y][x].perfect_tx_time;
		}

		/* set the initial rate */
		for (ndx = sn->num_rates-1; ndx > 0; ndx--) {
			if (sn->rates[ndx].rate <= 72) {
				break;
			}
		}
		sn->current_rate[y] = ndx;
	}

	DPRINTF(sc, "%s: %s %d rates %d%sMbps (%dus)- %d%sMbps (%dus)\n",
		__func__, ether_sprintf(ni->ni_macaddr), 
		sn->num_rates,
		sn->rates[0].rate/2, sn->rates[0].rate % 0x1 ? ".5" : "",
		sn->stats[1][0].perfect_tx_time,
		sn->rates[sn->num_rates-1].rate/2,
			sn->rates[sn->num_rates-1].rate % 0x1 ? ".5" : "",
		sn->stats[1][sn->num_rates-1].perfect_tx_time
	);

	ni->ni_txrate = sn->current_rate[0];
#undef RATE
}
Exemplo n.º 9
0
/*
 * Initialize the tables for a node.
 */
static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
	struct ieee80211com *ic = &sc->sc_ic;
	struct ath_node *an = ATH_NODE(ni);
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	int x, y, srate;

	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
        sn->static_rate_ndx = -1;
	if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
		/*
		 * A fixed rate is to be used; ic_fixed_rate is the
		 * IEEE code for this rate (sans basic bit).  Convert this
		 * to the index into the negotiated rate set for
		 * the node.
		 */
		/* NB: the rate set is assumed sorted */
		srate = ni->ni_rates.rs_nrates - 1;
		for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
			;
		/*
		 * The fixed rate may not be available due to races
		 * and mode settings.  Also orphaned nodes created in
		 * adhoc mode may not have any rate set so this lookup
		 * can fail.
		 */
		if (srate >= 0)
			sn->static_rate_ndx = srate;
	}

        DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s size 1600 rate/tt",
	    __func__, ether_sprintf(ni->ni_macaddr));

	sn->num_rates = ni->ni_rates.rs_nrates;
        for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
		sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
		sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
		if (sn->rates[x].rix == 0xff) {
			DPRINTF(sc, ATH_DEBUG_RATE,
			    "%s: ignore bogus rix at %d\n", __func__, x);
			continue;
		}
		sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
		sn->rates[x].shortPreambleRateCode = 
			rt->info[sn->rates[x].rix].rateCode | 
			rt->info[sn->rates[x].rix].shortPreamble;

		DPRINTF(sc, ATH_DEBUG_RATE, " %d/%d", sn->rates[x].rate,
		    calc_usecs_unicast_packet(sc, 1600, sn->rates[x].rix, 0,0));
	}
	DPRINTF(sc, ATH_DEBUG_RATE, "%s\n", "");
	
	/* set the visible bit-rate to the lowest one available */
	ni->ni_txrate = 0;
	sn->num_rates = ni->ni_rates.rs_nrates;
	
	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
		int size = bin_to_size(y);
		int ndx = 0;
		sn->packets_sent[y] = 0;
		sn->current_sample_ndx[y] = -1;
		sn->last_sample_ndx[y] = 0;
		
		for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
			sn->stats[y][x].successive_failures = 0;
			sn->stats[y][x].tries = 0;
			sn->stats[y][x].total_packets = 0;
			sn->stats[y][x].packets_acked = 0;
			sn->stats[y][x].last_tx = 0;
			
			sn->stats[y][x].perfect_tx_time = 
				calc_usecs_unicast_packet(sc, size, 
							  sn->rates[x].rix,
							  0, 0);
			sn->stats[y][x].average_tx_time = sn->stats[y][x].perfect_tx_time;
		}

		/* set the initial rate */
		for (ndx = sn->num_rates-1; ndx > 0; ndx--) {
			if (sn->rates[ndx].rate <= 72) {
				break;
			}
		}
		sn->current_rate[y] = ndx;
	}

	DPRINTF(sc, ATH_DEBUG_RATE,
	    "%s: %s %d rates %d%sMbps (%dus)- %d%sMbps (%dus)\n",
	    __func__, ether_sprintf(ni->ni_macaddr), 
	    sn->num_rates,
	    sn->rates[0].rate/2, sn->rates[0].rate % 0x1 ? ".5" : "",
	    sn->stats[1][0].perfect_tx_time,
	    sn->rates[sn->num_rates-1].rate/2,
		sn->rates[sn->num_rates-1].rate % 0x1 ? ".5" : "",
	    sn->stats[1][sn->num_rates-1].perfect_tx_time
	);

        if (sn->static_rate_ndx != -1)
		ni->ni_txrate = sn->static_rate_ndx;
	else
		ni->ni_txrate = sn->current_rate[0];
#undef RATE
}
Exemplo n.º 10
0
/*
 * Pick a good "random" bit-rate to sample other than the current one.
 */
static __inline int
pick_sample_rate(struct sample_softc *ssc , struct ath_node *an,
                 const HAL_RATE_TABLE *rt, int size_bin)
{
#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
    struct sample_node *sn = ATH_NODE_SAMPLE(an);
    int current_rix, rix;
    unsigned current_tt;
    uint64_t mask;

    current_rix = sn->current_rix[size_bin];
    if (current_rix < 0) {
        /* no successes yet, send at the lowest bit-rate */
        /* XXX should return MCS0 if HT */
        return 0;
    }

    current_tt = sn->stats[size_bin][current_rix].average_tx_time;

    rix = sn->last_sample_rix[size_bin]+1;	/* next sample rate */
    mask = sn->ratemask &~ ((uint64_t) 1<<current_rix);/* don't sample current rate */
    while (mask != 0) {
        if ((mask & ((uint64_t) 1<<rix)) == 0) {	/* not a supported rate */
nextrate:
            if (++rix >= rt->rateCount)
                rix = 0;
            continue;
        }

        /*
         * The following code stops trying to sample
         * non-MCS rates when speaking to an MCS node.
         * However, at least for CCK rates in 2.4GHz mode,
         * the non-MCS rates MAY actually provide better
         * PER at the very far edge of reception.
         *
         * However! Until ath_rate_form_aggr() grows
         * some logic to not form aggregates if the
         * selected rate is non-MCS, this won't work.
         *
         * So don't disable this code until you've taught
         * ath_rate_form_aggr() to drop out if any of
         * the selected rates are non-MCS.
         */
#if 1
        /* if the node is HT and the rate isn't HT, don't bother sample */
        if ((an->an_node.ni_flags & IEEE80211_NODE_HT) &&
                (rt->info[rix].phy != IEEE80211_T_HT)) {
            mask &= ~((uint64_t) 1<<rix);
            goto nextrate;
        }
#endif

        /* this bit-rate is always worse than the current one */
        if (sn->stats[size_bin][rix].perfect_tx_time > current_tt) {
            mask &= ~((uint64_t) 1<<rix);
            goto nextrate;
        }

        /* rarely sample bit-rates that fail a lot */
        if (sn->stats[size_bin][rix].successive_failures > ssc->max_successive_failures &&
                ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout) {
            mask &= ~((uint64_t) 1<<rix);
            goto nextrate;
        }

        /*
         * For HT, only sample a few rates on either side of the
         * current rix; there's quite likely a lot of them.
         */
        if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
            if (rix < (current_rix - 3) ||
                    rix > (current_rix + 3)) {
                mask &= ~((uint64_t) 1<<rix);
                goto nextrate;
            }
        }

        /* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */
        if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) {
            if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) {
                mask &= ~((uint64_t) 1<<rix);
                goto nextrate;
            }
        }

        sn->last_sample_rix[size_bin] = rix;
        return rix;
    }
    return current_rix;
#undef DOT11RATE
#undef	MCS
}
Exemplo n.º 11
0
void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
	const struct ath_buf *bf)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
	const struct ath_desc *ds0 = &bf->bf_desc[0];
	int final_rate, short_tries, long_tries, frame_size;
	int mrr;

	final_rate = sc->sc_hwmap[ts->ts_rate &~ HAL_TXSTAT_ALTRATE].ieeerate;
	short_tries = ts->ts_shortretry;
	long_tries = ts->ts_longretry + 1;
	frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
	if (frame_size == 0)		    /* NB: should not happen */
		frame_size = 1500;

	if (sn->num_rates <= 0) {
		DPRINTF(sc, ATH_DEBUG_RATE,
		    "%s: %s size %d %s rate/try %d/%d no rates yet\n", 
		    __func__, ether_sprintf(an->an_node.ni_macaddr),
		    bin_to_size(size_to_bin(frame_size)),
		    ts->ts_status ? "FAIL" : "OK",
		    short_tries, long_tries);
		return;
	}
	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);
	if (!mrr || !(ts->ts_rate & HAL_TXSTAT_ALTRATE)) {
		int ndx = rate_to_ndx(sn, final_rate);

		/*
		 * Only one rate was used; optimize work.
		 */
		DPRINTF(sc, ATH_DEBUG_RATE,
		    "%s: %s size %d %s rate/try %d/%d/%d\n", 
		     __func__, ether_sprintf(an->an_node.ni_macaddr),
		     bin_to_size(size_to_bin(frame_size)),
		     ts->ts_status ? "FAIL" : "OK",
		     final_rate, short_tries, long_tries);
		update_stats(sc, an, frame_size, 
			     ndx, long_tries,
			     0, 0,
			     0, 0,
			     0, 0,
			     short_tries, long_tries, ts->ts_status);
	} else {
		int hwrate0, rate0, tries0, ndx0;
		int hwrate1, rate1, tries1, ndx1;
		int hwrate2, rate2, tries2, ndx2;
		int hwrate3, rate3, tries3, ndx3;
		int finalTSIdx = ts->ts_finaltsi;

		/*
		 * Process intermediate rates that failed.
		 */
		if (sc->sc_ah->ah_magic != 0x20065416) {
			hwrate0 = MS(ds0->ds_ctl3, AR_XmitRate0);
			hwrate1 = MS(ds0->ds_ctl3, AR_XmitRate1);
			hwrate2 = MS(ds0->ds_ctl3, AR_XmitRate2);
			hwrate3 = MS(ds0->ds_ctl3, AR_XmitRate3);
		} else {
			hwrate0 = MS(ds0->ds_ctl3, AR5416_XmitRate0);
			hwrate1 = MS(ds0->ds_ctl3, AR5416_XmitRate1);
			hwrate2 = MS(ds0->ds_ctl3, AR5416_XmitRate2);
			hwrate3 = MS(ds0->ds_ctl3, AR5416_XmitRate3);
		}

		rate0 = sc->sc_hwmap[hwrate0].ieeerate;
		tries0 = MS(ds0->ds_ctl2, AR_XmitDataTries0);
		ndx0 = rate_to_ndx(sn, rate0);

		rate1 = sc->sc_hwmap[hwrate1].ieeerate;
		tries1 = MS(ds0->ds_ctl2, AR_XmitDataTries1);
		ndx1 = rate_to_ndx(sn, rate1);

		rate2 = sc->sc_hwmap[hwrate2].ieeerate;
		tries2 = MS(ds0->ds_ctl2, AR_XmitDataTries2);
		ndx2 = rate_to_ndx(sn, rate2);

		rate3 = sc->sc_hwmap[hwrate3].ieeerate;
		tries3 = MS(ds0->ds_ctl2, AR_XmitDataTries3);
		ndx3 = rate_to_ndx(sn, rate3);

		DPRINTF(sc, ATH_DEBUG_RATE,
"%s: %s size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]\n", 
		     __func__, ether_sprintf(an->an_node.ni_macaddr),
		     bin_to_size(size_to_bin(frame_size)),
		     finalTSIdx,
		     long_tries, 
		     ts->ts_status ? "FAIL" : "OK",
		     rate0, tries0,
		     rate1, tries1,
		     rate2, tries2,
		     rate3, tries3);

		/*
		 * NB: series > 0 are not penalized for failure
		 * based on the try counts under the assumption
		 * that losses are often bursty and since we
		 * sample higher rates 1 try at a time doing so
		 * may unfairly penalize them.
		 */
		if (tries0) {
			update_stats(sc, an, frame_size, 
				     ndx0, tries0, 
				     ndx1, tries1, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     short_tries, long_tries, 
				     long_tries > tries0);
			long_tries -= tries0;
		}
		
		if (tries1 && finalTSIdx > 0) {
			update_stats(sc, an, frame_size, 
				     ndx1, tries1, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     0, 0, 
				     short_tries, long_tries, 
				     ts->ts_status);
			long_tries -= tries1;
		}

		if (tries2 && finalTSIdx > 1) {
			update_stats(sc, an, frame_size, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     0, 0,
				     0, 0,
				     short_tries, long_tries, 
				     ts->ts_status);
			long_tries -= tries2;
		}

		if (tries3 && finalTSIdx > 2) {
			update_stats(sc, an, frame_size, 
				     ndx3, tries3, 
				     0, 0,
				     0, 0,
				     0, 0,
				     short_tries, long_tries, 
				     ts->ts_status);
		}
	}
}
Exemplo n.º 12
0
static void
update_stats(struct ath_softc *sc, struct ath_node *an, 
		  int frame_size,
		  int ndx0, int tries0,
		  int ndx1, int tries1,
		  int ndx2, int tries2,
		  int ndx3, int tries3,
		  int short_tries, int tries, int status)
{
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
	int tt = 0;
	int tries_so_far = 0;
	int size_bin = 0;
	int size = 0;
	int rate = 0;

	size_bin = size_to_bin(frame_size);
	size = bin_to_size(size_bin);

	if (!(0 <= ndx0 && ndx0 < sn->num_rates)) {
		printf("%s: bogus ndx0 %d, max %u, mode %u\n",
		    __func__, ndx0, sn->num_rates, sc->sc_curmode);
		return;
	}
	rate = sn->rates[ndx0].rate;

	tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx0].rix, 
					short_tries,
					MIN(tries0, tries) - 1);
	tries_so_far += tries0;
	if (tries1 && tries0 < tries) {
		if (!(0 <= ndx1 && ndx1 < sn->num_rates)) {
			printf("%s: bogus ndx1 %d, max %u, mode %u\n",
			    __func__, ndx1, sn->num_rates, sc->sc_curmode);
			return;
		}
		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx1].rix, 
						short_tries,
						MIN(tries1 + tries_so_far, tries) - tries_so_far - 1);
	}
	tries_so_far += tries1;

	if (tries2 && tries0 + tries1 < tries) {
		if (!(0 <= ndx2 && ndx2 < sn->num_rates)) {
			printf("%s: bogus ndx2 %d, max %u, mode %u\n",
			    __func__, ndx2, sn->num_rates, sc->sc_curmode);
			return;
		}
		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx2].rix, 
					       short_tries,
						MIN(tries2 + tries_so_far, tries) - tries_so_far - 1);
	}

	tries_so_far += tries2;

	if (tries3 && tries0 + tries1 + tries2 < tries) {
		if (!(0 <= ndx3 && ndx3 < sn->num_rates)) {
			printf("%s: bogus ndx3 %d, max %u, mode %u\n",
			    __func__, ndx3, sn->num_rates, sc->sc_curmode);
			return;
		}
		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx3].rix, 
						short_tries,
						MIN(tries3 + tries_so_far, tries) - tries_so_far - 1);
	}
	if (sn->stats[size_bin][ndx0].total_packets < (100 / (100 - ssc->ath_smoothing_rate))) {
		/* just average the first few packets */
		int avg_tx = sn->stats[size_bin][ndx0].average_tx_time;
		int packets = sn->stats[size_bin][ndx0].total_packets;
		sn->stats[size_bin][ndx0].average_tx_time = (tt+(avg_tx*packets))/(packets+1);
	} else {
		/* use a ewma */
		sn->stats[size_bin][ndx0].average_tx_time = 
			((sn->stats[size_bin][ndx0].average_tx_time * ssc->ath_smoothing_rate) + 
			 (tt * (100 - ssc->ath_smoothing_rate))) / 100;
	}
	
	if (status) {
		int y;
		sn->stats[size_bin][ndx0].successive_failures++;
		for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) {
			/* also say larger packets failed since we
			 * assume if a small packet fails at a lower
			 * bit-rate then a larger one will also.
			 */
			sn->stats[y][ndx0].successive_failures++;
			sn->stats[y][ndx0].last_tx = ticks;
			sn->stats[y][ndx0].tries += tries;
			sn->stats[y][ndx0].total_packets++;
		}
	} else {
		sn->stats[size_bin][ndx0].packets_acked++;
		sn->stats[size_bin][ndx0].successive_failures = 0;
	}
	sn->stats[size_bin][ndx0].tries += tries;
	sn->stats[size_bin][ndx0].last_tx = ticks;
	sn->stats[size_bin][ndx0].total_packets++;


	if (ndx0 == sn->current_sample_ndx[size_bin]) {
		DPRINTF(sc, ATH_DEBUG_RATE,
"%s: %s size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)\n", 
		    __func__, ether_sprintf(an->an_node.ni_macaddr), 
		    size,
		    status ? "FAIL" : "OK",
		    rate, short_tries, tries, tt, 
		    sn->stats[size_bin][ndx0].average_tx_time,
		    sn->stats[size_bin][ndx0].perfect_tx_time);
		sn->sample_tt[size_bin] = tt;
		sn->current_sample_ndx[size_bin] = -1;
	}
}
Exemplo n.º 13
0
void
ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
		  int shortPreamble, size_t frameLen,
		  u_int8_t *rix, int *try0, u_int8_t *txrate)
{
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
	struct ieee80211com *ic = &sc->sc_ic;
	int ndx, size_bin, mrr, best_ndx, change_rates;
	unsigned average_tx_time;

	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);
	size_bin = size_to_bin(frameLen);
	best_ndx = best_rate_ndx(sn, size_bin, !mrr);

	if (best_ndx >= 0) {
		average_tx_time = sn->stats[size_bin][best_ndx].average_tx_time;
	} else {
		average_tx_time = 0;
	}
	
	if (sn->static_rate_ndx != -1) {
		ndx = sn->static_rate_ndx;
		*try0 = ATH_TXMAXTRY;
	} else {
		*try0 = mrr ? 2 : ATH_TXMAXTRY;
		
		if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->ath_sample_rate/100)) {
			/*
			 * we want to limit the time measuring the performance
			 * of other bit-rates to ath_sample_rate% of the
			 * total transmission time.
			 */
			ndx = pick_sample_ndx(sn, size_bin);
			if (ndx != sn->current_rate[size_bin]) {
				sn->current_sample_ndx[size_bin] = ndx;
			} else {
				sn->current_sample_ndx[size_bin] = -1;
			}
			sn->packets_since_sample[size_bin] = 0;

		} else {
			change_rates = 0;
			if (!sn->packets_sent[size_bin] || best_ndx == -1) {
				/* no packet has been sent successfully yet */
				for (ndx = sn->num_rates-1; ndx > 0; ndx--) {
					/* 
					 * pick the highest rate <= 36 Mbps
					 * that hasn't failed.
					 */
					if (sn->rates[ndx].rate <= 72 && 
					    sn->stats[size_bin][ndx].successive_failures == 0) {
						break;
					}
				}
				change_rates = 1;
				best_ndx = ndx;
			} else if (sn->packets_sent[size_bin] < 20) {
				/* let the bit-rate switch quickly during the first few packets */
				change_rates = 1;
			} else if (ticks - ((hz*MIN_SWITCH_MS)/1000) > sn->ticks_since_switch[size_bin]) {
				/* 2 seconds have gone by */
				change_rates = 1;
			} else if (average_tx_time * 2 < sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time) {
				/* the current bit-rate is twice as slow as the best one */
				change_rates = 1;
			}

			sn->packets_since_sample[size_bin]++;
			
			if (change_rates) {
				if (best_ndx != sn->current_rate[size_bin]) {
					DPRINTF(sc, ATH_DEBUG_RATE,
"%s: %s size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d\n",
					    __func__,
					    ether_sprintf(an->an_node.ni_macaddr),
					    packet_size_bins[size_bin],
					    sn->rates[sn->current_rate[size_bin]].rate,
					    sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time,
					    sn->stats[size_bin][sn->current_rate[size_bin]].perfect_tx_time,
					    sn->rates[best_ndx].rate,
					    sn->stats[size_bin][best_ndx].average_tx_time,
					    sn->stats[size_bin][best_ndx].perfect_tx_time,
					    sn->packets_since_switch[size_bin],
					    mrr);
				}
				sn->packets_since_switch[size_bin] = 0;
				sn->current_rate[size_bin] = best_ndx;
				sn->ticks_since_switch[size_bin] = ticks;
			}
			ndx = sn->current_rate[size_bin];
			sn->packets_since_switch[size_bin]++;
			if (size_bin == 0) {
	    			/* 
	    			 * set the visible txrate for this node
			         * to the rate of small packets
			         */
				an->an_node.ni_txrate = ndx;
			}
		}
	}

	KASSERT(ndx >= 0 && ndx < sn->num_rates, ("ndx is %d", ndx));

	*rix = sn->rates[ndx].rix;
	if (shortPreamble) {
		*txrate = sn->rates[ndx].shortPreambleRateCode;
	} else {
		*txrate = sn->rates[ndx].rateCode;
	}
	sn->packets_sent[size_bin]++;
}
Exemplo n.º 14
0
void
ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
		  int shortPreamble, size_t frameLen,
		  u_int8_t *rix0, int *try0, u_int8_t *txrate)
{
#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
#define	RATE(ix)	(DOT11RATE(ix) / 2)
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
	struct ifnet *ifp = sc->sc_ifp;
	struct ieee80211com *ic = ifp->if_l2com;
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	const int size_bin = size_to_bin(frameLen);
	int rix, mrr, best_rix, change_rates;
	unsigned average_tx_time;

	ath_rate_update_static_rix(sc, &an->an_node);

	if (sn->static_rix != -1) {
		rix = sn->static_rix;
		*try0 = ATH_TXMAXTRY;
		goto done;
	}

	/* XXX TODO: this doesn't know about 11gn vs 11g protection; teach it */
	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);

	best_rix = pick_best_rate(an, rt, size_bin, !mrr);
	if (best_rix >= 0) {
		average_tx_time = sn->stats[size_bin][best_rix].average_tx_time;
	} else {
		average_tx_time = 0;
	}
	/*
	 * Limit the time measuring the performance of other tx
	 * rates to sample_rate% of the total transmission time.
	 */
	if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) {
		rix = pick_sample_rate(ssc, an, rt, size_bin);
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		     &an->an_node, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s",
		     average_tx_time,
		     sn->sample_tt[size_bin],
		     bin_to_size(size_bin),
		     dot11rate(rt, rix),
		     dot11rate_label(rt, rix),
		     dot11rate(rt, sn->current_rix[size_bin]),
		     dot11rate_label(rt, sn->current_rix[size_bin]));
		if (rix != sn->current_rix[size_bin]) {
			sn->current_sample_rix[size_bin] = rix;
		} else {
			sn->current_sample_rix[size_bin] = -1;
		}
		sn->packets_since_sample[size_bin] = 0;
	} else {
		change_rates = 0;
		if (!sn->packets_sent[size_bin] || best_rix == -1) {
			/* no packet has been sent successfully yet */
			change_rates = 1;
			if (an->an_node.ni_flags & IEEE80211_NODE_HT)
				best_rix =
				    ath_rate_pick_seed_rate_ht(sc, an, frameLen);
			else
				best_rix =
				    ath_rate_pick_seed_rate_legacy(sc, an, frameLen);
		} else if (sn->packets_sent[size_bin] < 20) {
			/* let the bit-rate switch quickly during the first few packets */
			IEEE80211_NOTE(an->an_node.ni_vap,
			    IEEE80211_MSG_RATECTL, &an->an_node,
			    "%s: switching quickly..", __func__);
			change_rates = 1;
		} else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) {
			/* min_switch seconds have gone by */
			IEEE80211_NOTE(an->an_node.ni_vap,
			    IEEE80211_MSG_RATECTL, &an->an_node,
			    "%s: min_switch %d > ticks_since_switch %d..",
			    __func__, ticks - ssc->min_switch, sn->ticks_since_switch[size_bin]);
			change_rates = 1;
		} else if ((! (an->an_node.ni_flags & IEEE80211_NODE_HT)) &&
		    (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time)) {
			/* the current bit-rate is twice as slow as the best one */
			IEEE80211_NOTE(an->an_node.ni_vap,
			    IEEE80211_MSG_RATECTL, &an->an_node,
			    "%s: 2x att (= %d) < cur_rix att %d",
			    __func__,
			    2 * average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time);
			change_rates = 1;
		} else if ((an->an_node.ni_flags & IEEE80211_NODE_HT)) {
			int cur_rix = sn->current_rix[size_bin];
			int cur_att = sn->stats[size_bin][cur_rix].average_tx_time;
			/*
			 * If the node is HT, upgrade it if the MCS rate is
			 * higher and the average tx time is within 20% of
			 * the current rate. It can fail a little.
			 *
			 * This is likely not optimal!
			 */
#if 0
			printf("cur rix/att %x/%d, best rix/att %x/%d\n",
			    MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time);
#endif
			if ((MCS(best_rix) > MCS(cur_rix)) &&
			    (average_tx_time * 8) <= (cur_att * 10)) {
				IEEE80211_NOTE(an->an_node.ni_vap,
				    IEEE80211_MSG_RATECTL, &an->an_node,
				    "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d",
				    __func__,
				    MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att);
				change_rates = 1;
			}
		}

		sn->packets_since_sample[size_bin]++;
		
		if (change_rates) {
			if (best_rix != sn->current_rix[size_bin]) {
				IEEE80211_NOTE(an->an_node.ni_vap,
				    IEEE80211_MSG_RATECTL,
				    &an->an_node,
"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
				    __func__,
				    bin_to_size(size_bin),
				    RATE(sn->current_rix[size_bin]),
				    sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time,
				    sn->stats[size_bin][sn->current_rix[size_bin]].perfect_tx_time,
				    RATE(best_rix),
				    sn->stats[size_bin][best_rix].average_tx_time,
				    sn->stats[size_bin][best_rix].perfect_tx_time,
				    sn->packets_since_switch[size_bin],
				    mrr);
			}
			sn->packets_since_switch[size_bin] = 0;
			sn->current_rix[size_bin] = best_rix;
			sn->ticks_since_switch[size_bin] = ticks;
			/* 
			 * Set the visible txrate for this node.
			 */
			an->an_node.ni_txrate = (rt->info[best_rix].phy == IEEE80211_T_HT) ?  MCS(best_rix) : DOT11RATE(best_rix);
		}
		rix = sn->current_rix[size_bin];
		sn->packets_since_switch[size_bin]++;
	}
	*try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY;
done:
	KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix));

	*rix0 = rix;
	*txrate = rt->info[rix].rateCode
		| (shortPreamble ? rt->info[rix].shortPreamble : 0);
	sn->packets_sent[size_bin]++;
#undef DOT11RATE
#undef MCS
#undef RATE
}
Exemplo n.º 15
0
void
ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
		  int shortPreamble, size_t frameLen,
		  u_int8_t *rix0, int *try0, u_int8_t *txrate)
{
#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
#define	RATE(ix)	(DOT11RATE(ix) / 2)
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
	struct ifnet *ifp = sc->sc_ifp;
	struct ieee80211com *ic = ifp->if_l2com;
	const HAL_RATE_TABLE *rt = sc->sc_currates;
	const int size_bin = size_to_bin(frameLen);
	int rix, mrr, best_rix, change_rates;
	unsigned average_tx_time;

	if (sn->static_rix != -1) {
		rix = sn->static_rix;
		*try0 = ATH_TXMAXTRY;
		goto done;
	}

	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);

	best_rix = pick_best_rate(sn, rt, size_bin, !mrr);
	if (best_rix >= 0) {
		average_tx_time = sn->stats[size_bin][best_rix].average_tx_time;
	} else {
		average_tx_time = 0;
	}
	/*
	 * Limit the time measuring the performance of other tx
	 * rates to sample_rate% of the total transmission time.
	 */
	if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) {
		rix = pick_sample_rate(ssc, sn, rt, size_bin);
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		     &an->an_node, "size %u sample rate %d current rate %d",
		     bin_to_size(size_bin), RATE(rix),
		     RATE(sn->current_rix[size_bin]));
		if (rix != sn->current_rix[size_bin]) {
			sn->current_sample_rix[size_bin] = rix;
		} else {
			sn->current_sample_rix[size_bin] = -1;
		}
		sn->packets_since_sample[size_bin] = 0;
	} else {
		change_rates = 0;
		if (!sn->packets_sent[size_bin] || best_rix == -1) {
			/* no packet has been sent successfully yet */
			for (rix = rt->rateCount-1; rix > 0; rix--) {
				if ((sn->ratemask & (1<<rix)) == 0)
					continue;
				/* 
				 * Pick the highest rate <= 36 Mbps
				 * that hasn't failed.
				 */
				if (DOT11RATE(rix) <= 72 && 
				    sn->stats[size_bin][rix].successive_failures == 0) {
					break;
				}
			}
			change_rates = 1;
			best_rix = rix;
		} else if (sn->packets_sent[size_bin] < 20) {
			/* let the bit-rate switch quickly during the first few packets */
			change_rates = 1;
		} else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) {
			/* min_switch seconds have gone by */
			change_rates = 1;
		} else if (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time) {
			/* the current bit-rate is twice as slow as the best one */
			change_rates = 1;
		}

		sn->packets_since_sample[size_bin]++;
		
		if (change_rates) {
			if (best_rix != sn->current_rix[size_bin]) {
				IEEE80211_NOTE(an->an_node.ni_vap,
				    IEEE80211_MSG_RATECTL,
				    &an->an_node,
"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
				    __func__,
				    bin_to_size(size_bin),
				    RATE(sn->current_rix[size_bin]),
				    sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time,
				    sn->stats[size_bin][sn->current_rix[size_bin]].perfect_tx_time,
				    RATE(best_rix),
				    sn->stats[size_bin][best_rix].average_tx_time,
				    sn->stats[size_bin][best_rix].perfect_tx_time,
				    sn->packets_since_switch[size_bin],
				    mrr);
			}
			sn->packets_since_switch[size_bin] = 0;
			sn->current_rix[size_bin] = best_rix;
			sn->ticks_since_switch[size_bin] = ticks;
			/* 
			 * Set the visible txrate for this node.
			 */
			an->an_node.ni_txrate = DOT11RATE(best_rix);
		}
		rix = sn->current_rix[size_bin];
		sn->packets_since_switch[size_bin]++;
	}
	*try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY;
done:
	KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix));

	*rix0 = rix;
	*txrate = rt->info[rix].rateCode
		| (shortPreamble ? rt->info[rix].shortPreamble : 0);
	sn->packets_sent[size_bin]++;
#undef DOT11RATE
#undef RATE
}
Exemplo n.º 16
0
static void
update_stats(struct ath_softc *sc, struct ath_node *an, 
		  int frame_size,
		  int rix0, int tries0,
		  int rix1, int tries1,
		  int rix2, int tries2,
		  int rix3, int tries3,
		  int short_tries, int tries, int status,
		  int nframes, int nbad)
{
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
#ifdef IEEE80211_DEBUG
	const HAL_RATE_TABLE *rt = sc->sc_currates;
#endif
	const int size_bin = size_to_bin(frame_size);
	const int size = bin_to_size(size_bin);
	int tt, tries_so_far;
	int is_ht40 = (an->an_node.ni_chw == 40);

	if (!IS_RATE_DEFINED(sn, rix0))
		return;
	tt = calc_usecs_unicast_packet(sc, size, rix0, short_tries,
		MIN(tries0, tries) - 1, is_ht40);
	tries_so_far = tries0;

	if (tries1 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix1))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix1, short_tries,
			MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
		tries_so_far += tries1;
	}

	if (tries2 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix2))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix2, short_tries,
			MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
		tries_so_far += tries2;
	}

	if (tries3 && tries_so_far < tries) {
		if (!IS_RATE_DEFINED(sn, rix3))
			return;
		tt += calc_usecs_unicast_packet(sc, size, rix3, short_tries,
			MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
	}

	if (sn->stats[size_bin][rix0].total_packets < ssc->smoothing_minpackets) {
		/* just average the first few packets */
		int avg_tx = sn->stats[size_bin][rix0].average_tx_time;
		int packets = sn->stats[size_bin][rix0].total_packets;
		sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes);
	} else {
		/* use a ewma */
		sn->stats[size_bin][rix0].average_tx_time = 
			((sn->stats[size_bin][rix0].average_tx_time * ssc->smoothing_rate) + 
			 (tt * (100 - ssc->smoothing_rate))) / 100;
	}
	
	/*
	 * XXX Don't mark the higher bit rates as also having failed; as this
	 * unfortunately stops those rates from being tasted when trying to
	 * TX. This happens with 11n aggregation.
	 */
	if (nframes == nbad) {
#if 0
		int y;
#endif
		sn->stats[size_bin][rix0].successive_failures += nbad;
#if 0
		for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) {
			/*
			 * Also say larger packets failed since we
			 * assume if a small packet fails at a
			 * bit-rate then a larger one will also.
			 */
			sn->stats[y][rix0].successive_failures += nbad;
			sn->stats[y][rix0].last_tx = ticks;
			sn->stats[y][rix0].tries += tries;
			sn->stats[y][rix0].total_packets += nframes;
		}
#endif
	} else {
		sn->stats[size_bin][rix0].packets_acked += (nframes - nbad);
		sn->stats[size_bin][rix0].successive_failures = 0;
	}
	sn->stats[size_bin][rix0].tries += tries;
	sn->stats[size_bin][rix0].last_tx = ticks;
	sn->stats[size_bin][rix0].total_packets += nframes;

	if (rix0 == sn->current_sample_rix[size_bin]) {
		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
		   &an->an_node,
"%s: size %d %s sample rate %d %s tries (%d/%d) tt %d avg_tt (%d/%d) nfrm %d nbad %d", 
		    __func__, 
		    size,
		    status ? "FAIL" : "OK",
		    dot11rate(rt, rix0),
		    dot11rate_label(rt, rix0),
		    short_tries, tries, tt, 
		    sn->stats[size_bin][rix0].average_tx_time,
		    sn->stats[size_bin][rix0].perfect_tx_time,
		    nframes, nbad);
		sn->sample_tt[size_bin] = tt;
		sn->current_sample_rix[size_bin] = -1;
	}
}
Exemplo n.º 17
0
void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
	const struct ath_desc *ds, const struct ath_desc *ds0)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct sample_node *sn = ATH_NODE_SAMPLE(an);
	const struct ar5212_desc *ads = (const struct ar5212_desc *)&ds->ds_ctl0;
	int final_rate, short_tries, long_tries, frame_size;
	int ndx = -1;
	int mrr;

	final_rate = sc->sc_hwmap[ds->ds_txstat.ts_rate &~ HAL_TXSTAT_ALTRATE].ieeerate;
	short_tries = ds->ds_txstat.ts_shortretry + 1;
	long_tries = ds->ds_txstat.ts_longretry + 1;
	frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
	if (frame_size == 0)		    /* NB: should not happen */
		frame_size = 1500;

	if (sn->num_rates <= 0) {
		DPRINTF(sc, "%s: %s size %d status %d rate/try %d/%d "
			"no rates yet\n", 
			__func__, ether_sprintf(an->an_node.ni_macaddr),
			bin_to_size(size_to_bin(frame_size)),
			ds->ds_txstat.ts_status,
			short_tries, long_tries);
		return;
	}

	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);

	if (sc->sc_mrretry && ds->ds_txstat.ts_status) {
		/* this packet failed */
		DPRINTF(sc, "%s: %s size %d rate/try %d/%d %d/%d %d/%d %d/%d status %s retries (%d/%d)\n", 
			__func__,
			ether_sprintf(an->an_node.ni_macaddr),
			bin_to_size(size_to_bin(frame_size)),
			sc->sc_hwmap[ads->xmit_rate0].ieeerate,
				ads->xmit_tries0,
			sc->sc_hwmap[ads->xmit_rate1].ieeerate,
				ads->xmit_tries1,
			sc->sc_hwmap[ads->xmit_rate2].ieeerate,
				ads->xmit_tries2,
			sc->sc_hwmap[ads->xmit_rate3].ieeerate,
				ads->xmit_tries3,
			ds->ds_txstat.ts_status ? "FAIL" : "OK",
			short_tries, 
			long_tries);
	}

	if (!mrr || !(ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)) {
		/* only one rate was used */
		ndx = rate_to_ndx(sn, final_rate);
		DPRINTF(sc, "%s: %s size %d status %d rate/try %d/%d/%d\n", 
			__func__, ether_sprintf(an->an_node.ni_macaddr),
			bin_to_size(size_to_bin(frame_size)),
			ds->ds_txstat.ts_status,
			ndx, short_tries, long_tries);
		if (ndx >= 0 && ndx < sn->num_rates) {
			update_stats(sc, an, frame_size, 
				     ndx, long_tries,
				     0, 0,
				     0, 0,
				     0, 0,
				     short_tries, long_tries, ds->ds_txstat.ts_status);
		}
	} else {
		int rate0, tries0, ndx0;
		int rate1, tries1, ndx1;
		int rate2, tries2, ndx2;
		int rate3, tries3, ndx3;
		int finalTSIdx = ads->final_ts_index;

		/*
		 * Process intermediate rates that failed.
		 */

		rate0 = sc->sc_hwmap[ads->xmit_rate0].ieeerate;
		tries0 = ads->xmit_tries0;
		ndx0 = rate_to_ndx(sn, rate0);
		
		rate1 = sc->sc_hwmap[ads->xmit_rate1].ieeerate;
		tries1 = ads->xmit_tries1;
		ndx1 = rate_to_ndx(sn, rate1);
		
		rate2 = sc->sc_hwmap[ads->xmit_rate2].ieeerate;
		tries2 = ads->xmit_tries2;
		ndx2 = rate_to_ndx(sn, rate2);
		
		rate3 = sc->sc_hwmap[ads->xmit_rate3].ieeerate;
		tries3 = ads->xmit_tries3;
		ndx3 = rate_to_ndx(sn, rate3);
		
#if 1
		DPRINTF(sc, "%s: %s size %d finaltsidx %d tries %d status %d rate/try %d/%d %d/%d %d/%d %d/%d\n", 
			__func__, ether_sprintf(an->an_node.ni_macaddr),
			bin_to_size(size_to_bin(frame_size)),
			finalTSIdx,
			long_tries, 
			ds->ds_txstat.ts_status,
			rate0, tries0,
			rate1, tries1,
			rate2, tries2,
			rate3, tries3);
#endif

		if (tries0) {
			update_stats(sc, an, frame_size, 
				     ndx0, tries0, 
				     ndx1, tries1, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     short_tries, ds->ds_txstat.ts_longretry + 1, 
				     long_tries > tries0);
		}
		
		if (tries1 && finalTSIdx > 0) {
			update_stats(sc, an, frame_size, 
				     ndx1, tries1, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     0, 0, 
				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0, 
				     ds->ds_txstat.ts_status);
		}

		if (tries2 && finalTSIdx > 1) {
			update_stats(sc, an, frame_size, 
				     ndx2, tries2, 
				     ndx3, tries3, 
				     0, 0,
				     0, 0,
				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0 - tries1, 
				     ds->ds_txstat.ts_status);
		}

		if (tries3 && finalTSIdx > 2) {
			update_stats(sc, an, frame_size, 
				     ndx3, tries3, 
				     0, 0,
				     0, 0,
				     0, 0,
				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0 - tries1 - tries2, 
				     ds->ds_txstat.ts_status);
		}
	}
}