Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63fefa05 authored by Toke Høiland-Jørgensen's avatar Toke Høiland-Jørgensen Committed by Kalle Valo
Browse files

ath9k: Introduce airtime fairness scheduling between stations



This reworks the ath9k driver to schedule transmissions to connected
stations in a way that enforces airtime fairness between them. It
accomplishes this by measuring the time spent transmitting to or
receiving from a station at TX and RX completion, and accounting this to
a per-station, per-QoS level airtime deficit. Then, an FQ-CoDel based
deficit scheduler is employed at packet dequeue time, to control which
station gets the next transmission opportunity.

Airtime fairness can significantly improve the efficiency of the network
when station rates vary. The following throughput values are from a
simple three-station test scenario, where two stations operate at the
highest HT20 rate, and one station at the lowest, and the scheduler is
employed at the access point:

                  Before   /   After
Fast station 1:    19.17   /   25.09 Mbps
Fast station 2:    19.83   /   25.21 Mbps
Slow station:       2.58   /    1.77 Mbps
Total:             41.58   /   52.07 Mbps

The benefit of airtime fairness goes up the more stations are present.
In a 30-station test with one station artificially limited to 1 Mbps,
we have seen aggregate throughput go from 2.14 to 17.76 Mbps.

Signed-off-by: default avatarToke Høiland-Jørgensen <toke@toke.dk>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 4bca5303
Loading
Loading
Loading
Loading
+24 −1
Original line number Original line Diff line number Diff line
@@ -112,6 +112,8 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
#define ATH_TXFIFO_DEPTH           8
#define ATH_TXFIFO_DEPTH           8
#define ATH_TX_ERROR               0x01
#define ATH_TX_ERROR               0x01


#define ATH_AIRTIME_QUANTUM        300 /* usec */

/* Stop tx traffic 1ms before the GO goes away */
/* Stop tx traffic 1ms before the GO goes away */
#define ATH_P2P_PS_STOP_TIME       1000
#define ATH_P2P_PS_STOP_TIME       1000


@@ -247,6 +249,9 @@ struct ath_atx_tid {
	bool has_queued;
	bool has_queued;
};
};


void __ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid);
void ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid);

struct ath_node {
struct ath_node {
	struct ath_softc *sc;
	struct ath_softc *sc;
	struct ieee80211_sta *sta; /* station struct we're part of */
	struct ieee80211_sta *sta; /* station struct we're part of */
@@ -258,9 +263,12 @@ struct ath_node {


	bool sleeping;
	bool sleeping;
	bool no_ps_filter;
	bool no_ps_filter;
	s64 airtime_deficit[IEEE80211_NUM_ACS];
	u32 airtime_rx_start;


#ifdef CONFIG_ATH9K_STATION_STATISTICS
#ifdef CONFIG_ATH9K_STATION_STATISTICS
	struct ath_rx_rate_stats rx_rate_stats;
	struct ath_rx_rate_stats rx_rate_stats;
	struct ath_airtime_stats airtime_stats;
#endif
#endif
	u8 key_idx[4];
	u8 key_idx[4];


@@ -317,10 +325,16 @@ struct ath_rx {
/* Channel Context */
/* Channel Context */
/*******************/
/*******************/


struct ath_acq {
	struct list_head acq_new;
	struct list_head acq_old;
	spinlock_t lock;
};

struct ath_chanctx {
struct ath_chanctx {
	struct cfg80211_chan_def chandef;
	struct cfg80211_chan_def chandef;
	struct list_head vifs;
	struct list_head vifs;
	struct list_head acq[IEEE80211_NUM_ACS];
	struct ath_acq acq[IEEE80211_NUM_ACS];
	int hw_queue_base;
	int hw_queue_base;


	/* do not dereference, use for comparison only */
	/* do not dereference, use for comparison only */
@@ -575,6 +589,8 @@ void ath_txq_schedule_all(struct ath_softc *sc);
int ath_tx_init(struct ath_softc *sc, int nbufs);
int ath_tx_init(struct ath_softc *sc, int nbufs);
int ath_txq_update(struct ath_softc *sc, int qnum,
int ath_txq_update(struct ath_softc *sc, int qnum,
		   struct ath9k_tx_queue_info *q);
		   struct ath9k_tx_queue_info *q);
u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen,
		     int width, int half_gi, bool shortPreamble);
void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
void ath_assign_seq(struct ath_common *common, struct sk_buff *skb);
void ath_assign_seq(struct ath_common *common, struct sk_buff *skb);
int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -963,6 +979,11 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);


#define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
#define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */


#define AIRTIME_USE_TX		BIT(0)
#define AIRTIME_USE_RX		BIT(1)
#define AIRTIME_USE_NEW_QUEUES	BIT(2)
#define AIRTIME_ACTIVE(flags) (!!(flags & (AIRTIME_USE_TX|AIRTIME_USE_RX)))

struct ath_softc {
struct ath_softc {
	struct ieee80211_hw *hw;
	struct ieee80211_hw *hw;
	struct device *dev;
	struct device *dev;
@@ -1005,6 +1026,8 @@ struct ath_softc {
	short nbcnvifs;
	short nbcnvifs;
	unsigned long ps_usecount;
	unsigned long ps_usecount;


	u16 airtime_flags; /* AIRTIME_* */

	struct ath_rx rx;
	struct ath_rx rx;
	struct ath_tx tx;
	struct ath_tx tx;
	struct ath_beacon beacon;
	struct ath_beacon beacon;
+10 −4
Original line number Original line Diff line number Diff line
@@ -118,8 +118,11 @@ void ath_chanctx_init(struct ath_softc *sc)
		INIT_LIST_HEAD(&ctx->vifs);
		INIT_LIST_HEAD(&ctx->vifs);
		ctx->txpower = ATH_TXPOWER_MAX;
		ctx->txpower = ATH_TXPOWER_MAX;
		ctx->flush_timeout = HZ / 5; /* 200ms */
		ctx->flush_timeout = HZ / 5; /* 200ms */
		for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
		for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) {
			INIT_LIST_HEAD(&ctx->acq[j]);
			INIT_LIST_HEAD(&ctx->acq[j].acq_new);
			INIT_LIST_HEAD(&ctx->acq[j].acq_old);
			spin_lock_init(&ctx->acq[j].lock);
		}
	}
	}
}
}


@@ -1345,8 +1348,11 @@ void ath9k_offchannel_init(struct ath_softc *sc)
	ctx->txpower = ATH_TXPOWER_MAX;
	ctx->txpower = ATH_TXPOWER_MAX;
	cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
	cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);


	for (i = 0; i < ARRAY_SIZE(ctx->acq); i++)
	for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) {
		INIT_LIST_HEAD(&ctx->acq[i]);
		INIT_LIST_HEAD(&ctx->acq[i].acq_new);
		INIT_LIST_HEAD(&ctx->acq[i].acq_old);
		spin_lock_init(&ctx->acq[i].lock);
	}


	sc->offchannel.chan.offchannel = true;
	sc->offchannel.chan.offchannel = true;
}
}
+3 −0
Original line number Original line Diff line number Diff line
@@ -1399,5 +1399,8 @@ int ath9k_init_debug(struct ath_hw *ah)
	debugfs_create_file("tpc", S_IRUSR | S_IWUSR,
	debugfs_create_file("tpc", S_IRUSR | S_IWUSR,
			    sc->debug.debugfs_phy, sc, &fops_tpc);
			    sc->debug.debugfs_phy, sc, &fops_tpc);


	debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR,
			   sc->debug.debugfs_phy, &sc->airtime_flags);

	return 0;
	return 0;
}
}
+13 −0
Original line number Original line Diff line number Diff line
@@ -221,6 +221,11 @@ struct ath_rx_rate_stats {
	} cck_stats[4];
	} cck_stats[4];
};
};


struct ath_airtime_stats {
	u32 rx_airtime;
	u32 tx_airtime;
};

#define ANT_MAIN 0
#define ANT_MAIN 0
#define ANT_ALT  1
#define ANT_ALT  1


@@ -314,12 +319,20 @@ ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause)
void ath_debug_rate_stats(struct ath_softc *sc,
void ath_debug_rate_stats(struct ath_softc *sc,
			  struct ath_rx_status *rs,
			  struct ath_rx_status *rs,
			  struct sk_buff *skb);
			  struct sk_buff *skb);
void ath_debug_airtime(struct ath_softc *sc,
		       struct ath_node *an,
		       u32 rx, u32 tx);
#else
#else
static inline void ath_debug_rate_stats(struct ath_softc *sc,
static inline void ath_debug_rate_stats(struct ath_softc *sc,
					struct ath_rx_status *rs,
					struct ath_rx_status *rs,
					struct sk_buff *skb)
					struct sk_buff *skb)
{
{
}
}
static inline void ath_debug_airtime(struct ath_softc *sc,
			      struct ath_node *an,
			      u32 rx, u32 tx)
{
}
#endif /* CONFIG_ATH9K_STATION_STATISTICS */
#endif /* CONFIG_ATH9K_STATION_STATISTICS */


#endif /* DEBUG_H */
#endif /* DEBUG_H */
+54 −0
Original line number Original line Diff line number Diff line
@@ -242,6 +242,59 @@ static const struct file_operations fops_node_recv = {
	.llseek = default_llseek,
	.llseek = default_llseek,
};
};


void ath_debug_airtime(struct ath_softc *sc,
		struct ath_node *an,
		u32 rx,
		u32 tx)
{
	struct ath_airtime_stats *astats = &an->airtime_stats;

	astats->rx_airtime += rx;
	astats->tx_airtime += tx;
}

static ssize_t read_airtime(struct file *file, char __user *user_buf,
			size_t count, loff_t *ppos)
{
	struct ath_node *an = file->private_data;
	struct ath_airtime_stats *astats;
	static const char *qname[4] = {
		"VO", "VI", "BE", "BK"
	};
	u32 len = 0, size = 256;
	char *buf;
	size_t retval;
	int i;

	buf = kzalloc(size, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;

	astats = &an->airtime_stats;

	len += scnprintf(buf + len, size - len, "RX: %u us\n", astats->rx_airtime);
	len += scnprintf(buf + len, size - len, "TX: %u us\n", astats->tx_airtime);
	len += scnprintf(buf + len, size - len, "Deficit: ");
	for (i = 0; i < 4; i++)
		len += scnprintf(buf+len, size - len, "%s: %lld us ", qname[i], an->airtime_deficit[i]);
	if (len < size)
		buf[len++] = '\n';

	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);

	return retval;
}


static const struct file_operations fops_airtime = {
	.read = read_airtime,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};


void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
			   struct ieee80211_vif *vif,
			   struct ieee80211_vif *vif,
			   struct ieee80211_sta *sta,
			   struct ieee80211_sta *sta,
@@ -251,4 +304,5 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,


	debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr);
	debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr);
	debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv);
	debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv);
	debugfs_create_file("airtime", S_IRUGO, dir, an, &fops_airtime);
}
}
Loading