Merge "msm: bam_dmux: Introduce watermarking in client transmit queue" into msm-3.0
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index ede298d..e3ca6d1 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -45,6 +45,8 @@
#define POLLING_MAX_SLEEP 1050 /* 1.05 ms */
#define POLLING_INACTIVITY 40 /* cycles before switch to intr mode */
+#define LOW_WATERMARK 2
+#define HIGH_WATERMARK 4
static int msm_bam_dmux_debug_enable;
module_param_named(debug_enable, msm_bam_dmux_debug_enable,
@@ -104,6 +106,8 @@
spinlock_t lock;
struct platform_device *pdev;
char name[BAM_DMUX_CH_NAME_MAX_LEN];
+ int num_tx_pkts;
+ int use_wm;
};
struct tx_pkt_info {
@@ -318,6 +322,7 @@
case BAM_MUX_HDR_CMD_OPEN:
spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags);
bam_ch[rx_hdr->ch_id].status |= BAM_CH_REMOTE_OPEN;
+ bam_ch[rx_hdr->ch_id].num_tx_pkts = 0;
spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags);
queue_rx();
ret = platform_device_add(bam_ch[rx_hdr->ch_id].pdev);
@@ -400,6 +405,7 @@
struct tx_pkt_info *info;
unsigned long event_data;
struct list_head *node;
+ unsigned long flags;
if (in_global_reset)
return;
@@ -418,6 +424,9 @@
hdr = (struct bam_mux_hdr *)skb->data;
DBG_INC_WRITE_CNT(skb->data_len);
event_data = (unsigned long)(skb);
+ spin_lock_irqsave(&bam_ch[hdr->ch_id].lock, flags);
+ bam_ch[hdr->ch_id].num_tx_pkts--;
+ spin_unlock_irqrestore(&bam_ch[hdr->ch_id].lock, flags);
if (bam_ch[hdr->ch_id].notify)
bam_ch[hdr->ch_id].notify(
bam_ch[hdr->ch_id].priv, BAM_DMUX_WRITE_DONE,
@@ -449,6 +458,13 @@
pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
return -ENODEV;
}
+
+ if (bam_ch[id].use_wm &&
+ (bam_ch[id].num_tx_pkts >= HIGH_WATERMARK)) {
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ pr_err("%s: watermark exceeded: %d\n", __func__, id);
+ return -EAGAIN;
+ }
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
read_lock(&ul_wakeup_lock);
@@ -520,6 +536,10 @@
DBG_INC_TX_SPS_FAILURE_CNT();
spin_unlock(&bam_tx_pool_spinlock);
kfree(pkt);
+ } else {
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].num_tx_pkts++;
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
}
ul_packet_written = 1;
read_unlock(&ul_wakeup_lock);
@@ -578,6 +598,8 @@
bam_ch[id].notify = notify;
bam_ch[id].priv = priv;
bam_ch[id].status |= BAM_CH_LOCAL_OPEN;
+ bam_ch[id].num_tx_pkts = 0;
+ bam_ch[id].use_wm = 0;
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
read_lock(&ul_wakeup_lock);
@@ -655,6 +677,47 @@
return rc;
}
+int msm_bam_dmux_is_ch_full(uint32_t id)
+{
+ unsigned long flags;
+ int ret;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].use_wm = 1;
+ ret = bam_ch[id].num_tx_pkts >= HIGH_WATERMARK;
+ DBG("%s: ch %d num tx pkts=%d, HWM=%d\n", __func__,
+ id, bam_ch[id].num_tx_pkts, ret);
+ if (!bam_ch_is_local_open(id)) {
+ ret = -ENODEV;
+ pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ }
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ return ret;
+}
+
+int msm_bam_dmux_is_ch_low(uint32_t id)
+{
+ int ret;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+
+ bam_ch[id].use_wm = 1;
+ ret = bam_ch[id].num_tx_pkts <= LOW_WATERMARK;
+ DBG("%s: ch %d num tx pkts=%d, LWM=%d\n", __func__,
+ id, bam_ch[id].num_tx_pkts, ret);
+ if (!bam_ch_is_local_open(id)) {
+ ret = -ENODEV;
+ pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ }
+
+ return ret;
+}
+
static void rx_timer_work_func(struct work_struct *work)
{
struct sps_iovec iov;
@@ -1034,6 +1097,7 @@
for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
temp_remote_status = bam_ch_is_remote_open(i);
bam_ch[i].status &= ~BAM_CH_REMOTE_OPEN;
+ bam_ch[i].num_tx_pkts = 0;
if (bam_ch_is_local_open(i))
bam_ch[i].status |= BAM_CH_IN_RESET;
if (temp_remote_status) {
diff --git a/arch/arm/mach-msm/include/mach/bam_dmux.h b/arch/arm/mach-msm/include/mach/bam_dmux.h
index a2b0126..fb70da4 100644
--- a/arch/arm/mach-msm/include/mach/bam_dmux.h
+++ b/arch/arm/mach-msm/include/mach/bam_dmux.h
@@ -59,6 +59,10 @@
int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb);
void msm_bam_dmux_kickoff_ul_wakeup(void);
+
+int msm_bam_dmux_is_ch_full(uint32_t id);
+
+int msm_bam_dmux_is_ch_low(uint32_t id);
#else
int msm_bam_dmux_open(uint32_t id, void *priv,
void (*notify)(void *priv, int event_type,
@@ -80,5 +84,15 @@
void msm_bam_dmux_kickoff_ul_wakeup(void)
{
}
+
+int msm_bam_dmux_is_ch_full(uint32_t id)
+{
+ return -ENODEV;
+}
+
+int msm_bam_dmux_is_ch_low(uint32_t id)
+{
+ return -ENODEV;
+}
#endif
#endif /* _BAM_DMUX_H */
diff --git a/drivers/net/msm_rmnet_bam.c b/drivers/net/msm_rmnet_bam.c
index bb20a3f..99a2c04 100644
--- a/drivers/net/msm_rmnet_bam.c
+++ b/drivers/net/msm_rmnet_bam.c
@@ -342,7 +342,12 @@
((struct net_device *)(dev))->name, p->stats.tx_packets,
skb->len, skb->mark);
dev_kfree_skb_any(skb);
- netif_wake_queue(dev);
+ if (netif_queue_stopped(dev) &&
+ msm_bam_dmux_is_ch_low(p->ch_id)) {
+ DBG0("%s: Low WM hit, waking queue=%p\n",
+ __func__, skb);
+ netif_wake_queue(dev);
+ }
}
static void bam_notify(void *dev, int event, unsigned long data)
@@ -452,7 +457,6 @@
return 0;
}
- netif_stop_queue(dev);
if (!ul_is_connected) {
p->waiting_for_ul = 1;
msm_bam_dmux_kickoff_ul_wakeup();
@@ -460,6 +464,11 @@
}
_rmnet_xmit(skb, dev);
+ if (msm_bam_dmux_is_ch_full(p->ch_id)) {
+ netif_stop_queue(dev);
+ DBG0("%s: High WM hit, stopping queue=%p\n", __func__, skb);
+ }
+
return 0;
}