wctc4xxp: Prevent exhausting memory in firmware.

If the host system sends to many packets to the DTE to process, the on-card
memory can be exhausted which will result in an out of memory alert. In commit
2ac2338247, the driver will halt all communication
with the card and request a reload if any alert is received.

Now the driver will silently drop any "burst" traffic that was sent to the
transcoder as opposed to expecting the firmware to do it. There is currently a
limit of 640 samples (80ms of audio) in flight to the firmware at any one time
allowed.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
This commit is contained in:
Shaun Ruffell 2014-05-29 15:53:45 -05:00 committed by Russ Meyerriecks
parent e10f740476
commit d7c0b0aba1

View File

@ -306,6 +306,11 @@ struct channel_pvt {
}; };
struct channel_stats stats; struct channel_stats stats;
struct list_head rx_queue; /* Transcoded packets for this channel. */ struct list_head rx_queue; /* Transcoded packets for this channel. */
/* Used to prevent user space from flooding the firmware. */
struct list_head node;
long samples_in_flight;
unsigned long send_time;
}; };
struct wcdte { struct wcdte {
@ -1812,6 +1817,7 @@ wctc4xxp_cleanup_channel_private(struct wcdte *wc,
spin_lock_irqsave(&cpvt->lock, flags); spin_lock_irqsave(&cpvt->lock, flags);
list_splice_init(&cpvt->rx_queue, &local_list); list_splice_init(&cpvt->rx_queue, &local_list);
dahdi_tc_clear_data_waiting(dtc); dahdi_tc_clear_data_waiting(dtc);
cpvt->samples_in_flight = 0;
spin_unlock_irqrestore(&cpvt->lock, flags); spin_unlock_irqrestore(&cpvt->lock, flags);
memset(&cpvt->stats, 0, sizeof(cpvt->stats)); memset(&cpvt->stats, 0, sizeof(cpvt->stats));
@ -2035,12 +2041,10 @@ wctc4xxp_operation_release(struct dahdi_transcoder_channel *dtc)
packets_received = atomic_read(&cpvt->stats.packets_received); packets_received = atomic_read(&cpvt->stats.packets_received);
packets_sent = atomic_read(&cpvt->stats.packets_sent); packets_sent = atomic_read(&cpvt->stats.packets_sent);
if ((packets_sent - packets_received) > 5) { DTE_DEBUG(DTE_DEBUG_ETH_STATS,
DTE_DEBUG(DTE_DEBUG_GENERAL, "%s channel %d sent %d packets " "%s channel %d sent %d packets and received %d packets.\n",
"and received %d packets.\n", (cpvt->encoder) ? (cpvt->encoder) ? "encoder" : "decoder", cpvt->chan_out_num,
"encoder" : "decoder", cpvt->chan_out_num,
packets_sent, packets_received); packets_sent, packets_received);
}
/* Remove any packets that are waiting on the outbound queue. */ /* Remove any packets that are waiting on the outbound queue. */
@ -2243,6 +2247,9 @@ wctc4xxp_write(struct file *file, const char __user *frame,
struct channel_pvt *cpvt = dtc->pvt; struct channel_pvt *cpvt = dtc->pvt;
struct wcdte *wc = cpvt->wc; struct wcdte *wc = cpvt->wc;
struct tcb *cmd; struct tcb *cmd;
u32 samples;
unsigned long flags;
const unsigned long MAX_SAMPLES_IN_FLIGHT = 640;
BUG_ON(!cpvt); BUG_ON(!cpvt);
BUG_ON(!wc); BUG_ON(!wc);
@ -2280,6 +2287,24 @@ wctc4xxp_write(struct file *file, const char __user *frame,
} }
} }
/* Do not flood the firmware with packets. This can result in out of
* memory conditions in the firmware. */
spin_lock_irqsave(&cpvt->lock, flags);
if (time_after(jiffies, cpvt->send_time)) {
cpvt->samples_in_flight = max(0L,
cpvt->samples_in_flight - 160L);
}
samples = wctc4xxp_bytes_to_samples(dtc->srcfmt, count);
if ((cpvt->samples_in_flight + samples) > MAX_SAMPLES_IN_FLIGHT) {
spin_unlock_irqrestore(&cpvt->lock, flags);
/* This should most likely be an error, but it results in
* codec_dahdi spamming when it's not set to wait for new
* packets. Instead we will silently drop the bytes. */
return count;
}
cpvt->send_time = jiffies + msecs_to_jiffies(20);
spin_unlock_irqrestore(&cpvt->lock, flags);
cmd = wctc4xxp_create_rtp_cmd(wc, dtc, count); cmd = wctc4xxp_create_rtp_cmd(wc, dtc, count);
if (!cmd) if (!cmd)
return -ENOMEM; return -ENOMEM;
@ -2548,6 +2573,7 @@ queue_rtp_packet(struct wcdte *wc, struct tcb *cmd)
struct channel_pvt *cpvt; struct channel_pvt *cpvt;
struct rtp_packet *packet = cmd->data; struct rtp_packet *packet = cmd->data;
unsigned long flags; unsigned long flags;
long samples;
if (unlikely(ip_fast_csum((void *)(&packet->iphdr), if (unlikely(ip_fast_csum((void *)(&packet->iphdr),
packet->iphdr.ihl))) { packet->iphdr.ihl))) {
@ -2584,15 +2610,20 @@ queue_rtp_packet(struct wcdte *wc, struct tcb *cmd)
} }
cpvt = dtc->pvt; cpvt = dtc->pvt;
if (dahdi_tc_is_busy(dtc)) { if (!dahdi_tc_is_busy(dtc)) {
free_cmd(cmd);
return;
}
spin_lock_irqsave(&cpvt->lock, flags); spin_lock_irqsave(&cpvt->lock, flags);
samples = wctc4xxp_bytes_to_samples(dtc->dstfmt,
be16_to_cpu(packet->udphdr.len) -
sizeof(struct rtphdr) - sizeof(struct udphdr));
cpvt->samples_in_flight = max(cpvt->samples_in_flight - samples, 0L);
list_add_tail(&cmd->node, &cpvt->rx_queue); list_add_tail(&cmd->node, &cpvt->rx_queue);
dahdi_tc_set_data_waiting(dtc); dahdi_tc_set_data_waiting(dtc);
spin_unlock_irqrestore(&cpvt->lock, flags); spin_unlock_irqrestore(&cpvt->lock, flags);
dahdi_transcoder_alert(dtc); dahdi_transcoder_alert(dtc);
} else {
free_cmd(cmd);
}
return; return;
} }