/* read packet from share memory socket buffer */ struct sk_buff *shm_recv(struct shm_rbctl *rbctl) { struct shm_skctl *skctl = rbctl->skctl_va; enum shm_rb_type rb_type = rbctl - shm_rbctl; /* yes, we always read from the next slot either */ int slot = shm_get_next_rx_slot(rbctl, skctl->ap_rptr); /* get the total packet size first for memory allocate */ unsigned char *hdr = SHM_PACKET_PTR(rbctl->rx_va, slot, rbctl->rx_skbuf_size); int count = 0; struct sk_buff *skb = NULL; if (rb_type == shm_rb_main) { struct shm_skhdr *skhdr = (struct shm_skhdr*)hdr; count = skhdr->length + sizeof(*skhdr); if (skhdr->length <= 0) { printk(KERN_EMERG "MSOCK: shm_recv: slot = %d, skhdr->length = %d\n", slot, skhdr->length); goto error_length; } } else if (rb_type == shm_rb_psd) { struct shm_psd_skhdr *skhdr = (struct shm_psd_skhdr*)hdr; count = skhdr->length + sizeof(*skhdr); printk(KERN_WARNING "MSOCK: shm_recv: calling in psd ring buffer!!!\n"); } if (count > rbctl->rx_skbuf_size) { printk(KERN_EMERG "MSOCK: shm_recv: slot = %d, count = %d\n", slot, count); goto error_length; } skb = alloc_skb(count, GFP_ATOMIC); if (!skb) return NULL; /* write all the packet data including header to sk_buff */ memcpy(skb_put(skb, count), hdr, count); error_length: /* advance reader pointer */ skctl->ap_rptr = slot; return skb; }
/* 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_rx_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; int slot; int count; enum data_path_result result; int i; int max_rx_shots = dp->max_rx_shots; dp->stat.rx_sched_cnt++; for (i = 0; i < max_rx_shots; i++) { if (!cp_is_synced) { /* if not sync, just return */ break; } /* process share memory socket buffer flow control */ if (rbctl->is_cp_xmit_stopped && shm_has_enough_free_rx_skbuf(rbctl)) { shm_notify_cp_tx_resume(rbctl); acipc_notify_cp_psd_tx_resume(); } if (shm_is_recv_empty(rbctl)) break; slot = shm_get_next_rx_slot(rbctl, skctl->ap_rptr); skhdr = (struct shm_psd_skhdr *)SHM_PACKET_PTR(rbctl->rx_va, slot, rbctl-> rx_skbuf_size); shm_invalidate_dcache(rbctl, skhdr, rbctl->rx_skbuf_size); count = skhdr->length + sizeof(*skhdr); if (count > rbctl->rx_skbuf_size) { pr_err( "%s: slot = %d, count = %d\n", __func__, slot, count); goto error_length; } trace_psd_recv(slot); dp->stat.rx_slots++; dp->stat.rx_bytes += count - sizeof(*skhdr); dp->stat.rx_used_bytes += count; dp->stat.rx_free_bytes += rbctl->rx_skbuf_size - count; if (dp->cbs && dp->cbs->data_rx) result = dp->cbs->data_rx((unsigned char *)(skhdr + 1), skhdr->length); else result = dp_success; /* * upper layer decide to keep the packet as pending * and we need to return now */ if (result == dp_rx_keep_pending) { pr_err("%s: packet is pending\n", __func__); break; } error_length: skctl->ap_rptr = slot; } if (i == max_rx_shots) { dp->stat.rx_sched_cnt++; data_path_schedule_rx(dp); } }
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; } }