/* write packet to share memory socket buffer */ void shm_xmit(struct shm_rbctl *rbctl, struct sk_buff *skb) { struct shm_skctl *skctl = rbctl->skctl_va; /* * we always write to the next slot !? * thinking the situation of the first slot in the first accessing */ int slot = shm_get_next_tx_slot(rbctl, skctl->ap_wptr); if (!skb) { printk(KERN_ERR "shm_xmit skb is null..\n"); return; } memcpy(SHM_PACKET_PTR(rbctl->tx_va, slot, rbctl->tx_skbuf_size), skb->data, skb->len); skctl->ap_wptr = slot; /* advance pointer index */ }
static void data_path_tx_func(unsigned long arg) { struct data_path *dp = (struct data_path *)arg; struct shm_rbctl *rbctl = dp->rbctl; struct shm_skctl *skctl = rbctl->skctl_va; struct shm_psd_skhdr *skhdr; struct sk_buff *packet; int slot = 0; int pending_slot; int free_slots; int prio; int remain_bytes; int used_bytes; int consumed_slot = 0; int consumed_packets = 0; int start_q_len; int max_tx_shots = dp->max_tx_shots; pending_slot = -1; remain_bytes = rbctl->tx_skbuf_size - sizeof(struct shm_psd_skhdr); used_bytes = 0; start_q_len = tx_q_length(dp); dp->stat.tx_sched_cnt++; while (consumed_slot < max_tx_shots) { if (!cp_is_synced) { tx_q_clean(dp); break; } free_slots = shm_free_tx_skbuf(rbctl); if (free_slots == 0) { /* * notify cp only if we still have packets in queue * otherwise, simply break * also check current fc status, if tx_stopped is * already sent to cp, do not try to interrupt cp again * it is useless, and just make cp busier * BTW: * this may have race condition here, but as cp side * have a watermark for resume interrupt, * we can assume it is safe */ if (tx_q_length(dp) && !rbctl->is_ap_xmit_stopped) { shm_notify_ap_tx_stopped(rbctl); acipc_notify_ap_psd_tx_stopped(); } break; } else if (free_slots == 1 && pending_slot != -1) { /* * the only left slot is our pending slot * check if we still have enough space in this * pending slot */ packet = tx_q_peek(dp, NULL); if (!packet) break; /* packet is too large, notify cp and break */ if (padded_size(packet->len) > remain_bytes && !rbctl->is_ap_xmit_stopped) { shm_notify_ap_tx_stopped(rbctl); acipc_notify_ap_psd_tx_stopped(); break; } } packet = tx_q_dequeue(dp, &prio); if (!packet) break; /* push to ring buffer */ /* we have one slot pending */ if (pending_slot != -1) { /* * the packet is too large for the pending slot * send out the pending slot firstly */ if (padded_size(packet->len) > remain_bytes) { shm_flush_dcache(rbctl, SHM_PACKET_PTR(rbctl->tx_va, pending_slot, rbctl->tx_skbuf_size), used_bytes + sizeof(struct shm_psd_skhdr)); skctl->ap_wptr = pending_slot; pending_slot = -1; consumed_slot++; dp->stat.tx_slots++; dp->stat.tx_free_bytes += remain_bytes; dp->stat.tx_used_bytes += used_bytes; } else slot = pending_slot; } /* * each priority has one hard limit to guarantee higher priority * packet is not affected by lower priority packet * if we reach this limit, we can only send higher priority * packets * but in the other hand, if this packet can be filled into our * pending slot, allow it anyway */ if (!has_enough_free_tx_slot(dp, free_slots, prio) && ((pending_slot == -1) || !dp->enable_piggyback)) { /* push back the packets and schedule delayed tx */ tx_q_queue_head(dp, packet, prio); __data_path_schedule_tx(dp, true); dp->stat.tx_force_sched_cnt++; break; } /* get a new slot from ring buffer */ if (pending_slot == -1) { slot = shm_get_next_tx_slot(dp->rbctl, skctl->ap_wptr); remain_bytes = rbctl->tx_skbuf_size - sizeof(struct shm_psd_skhdr); used_bytes = 0; pending_slot = slot; } consumed_packets++; dp->stat.tx_packets[prio]++; dp->stat.tx_bytes += packet->len; skhdr = (struct shm_psd_skhdr *) SHM_PACKET_PTR(rbctl->tx_va, slot, rbctl->tx_skbuf_size); /* we are sure our remains is enough for current packet */ skhdr->length = used_bytes + padded_size(packet->len); memcpy((unsigned char *)(skhdr + 1) + used_bytes, packet->data, packet->len); used_bytes += padded_size(packet->len); remain_bytes -= padded_size(packet->len); trace_psd_xmit(packet, slot); dp->stat.tx_packets_delay[prio] += ktime_to_ns(net_timedelta(skb_get_ktime(packet))); dev_kfree_skb_any(packet); } /* send out the pending slot */ if (pending_slot != -1) { shm_flush_dcache(rbctl, SHM_PACKET_PTR(rbctl->tx_va, pending_slot, rbctl->tx_skbuf_size), used_bytes + sizeof(struct shm_psd_skhdr)); skctl->ap_wptr = pending_slot; pending_slot = -1; consumed_slot++; dp->stat.tx_slots++; dp->stat.tx_free_bytes += remain_bytes; dp->stat.tx_used_bytes += used_bytes; } if (consumed_slot > 0) { trace_psd_xmit_irq(consumed_slot); acipc_notify_psd_packet_sent(); dp->stat.tx_interrupts++; dp->stat.tx_sched_q_len += start_q_len; } if (consumed_slot >= max_tx_shots) { data_path_schedule_tx(dp); dp->stat.tx_resched_cnt++; } /* * ring buffer is stopped, just notify upper layer * do not need to check is_tx_stopped here, as we need to handle * following situation: * a new on-demand PDP is activated after tx_stop is called */ if (rbctl->is_ap_xmit_stopped) { if (!dp->is_tx_stopped) pr_err("%s tx stop\n", __func__); dp->is_tx_stopped = true; /* notify upper layer tx stopped */ if (dp->cbs->tx_stop) dp->cbs->tx_stop(); /* reschedule tx to polling the ring buffer */ if (tx_q_length(dp)) __data_path_schedule_tx(dp, true); } /* * ring buffer is resumed and the remain packets * in queue is also sent out */ if (!rbctl->is_ap_xmit_stopped && dp->is_tx_stopped && tx_q_length(dp) == 0) { pr_err("%s tx resume\n", __func__); /* notify upper layer tx resumed */ if (dp->cbs->tx_resume) dp->cbs->tx_resume(); dp->is_tx_stopped = false; } }