static void pass_skb_to_net(struct mem_link_device *mld, struct sk_buff *skb) { struct link_device *ld = &mld->link_dev; struct skbuff_private *priv; struct io_device *iod; int ret; priv = skbpriv(skb); if (unlikely(!priv)) { mif_err("%s: ERR! No PRIV in skb@%p\n", ld->name, skb); dev_kfree_skb_any(skb); modemctl_notify_event(MDM_CRASH_INVALID_SKBCB); return; } iod = priv->iod; if (unlikely(!iod)) { mif_err("%s: ERR! No IOD in skb@%p\n", ld->name, skb); dev_kfree_skb_any(skb); modemctl_notify_event(MDM_CRASH_INVALID_SKBIOD); return; } log_ipc_pkt(LNK_RX, iod->id, skb); ret = iod->recv_net_skb(iod, ld, skb); if (unlikely(ret < 0)) { struct modem_ctl *mc = ld->mc; mif_err_limited("%s: %s<-%s: ERR! %s->recv_net_skb fail (%d)\n", ld->name, iod->name, mc->name, iod->name, ret); dev_kfree_skb_any(skb); } }
/** @brief extract all IPC link frames from an SBD RB In a while loop,\n 1) receives each IPC link frame stored in the @b @@RB.\n 2) passes it to the DEMUX layer immediately.\n @param rb the pointer to a mem_ring_buffer instance @retval "> 0" if valid data received @retval "= 0" if no data received @retval "< 0" if ANY error */ static int rx_ipc_frames_from_rb(struct sbd_ring_buffer *rb) { int rcvd = 0; struct link_device *ld = rb->ld; struct mem_link_device *mld = ld_to_mem_link_device(ld); unsigned int qlen = rb->len; unsigned int in = *rb->wp; unsigned int out = *rb->rp; unsigned int num_frames = circ_get_usage(qlen, in, out); while (rcvd < num_frames) { struct sk_buff *skb; skb = sbd_pio_rx(rb); if (!skb) { #ifdef CONFIG_SEC_MODEM_DEBUG panic("skb alloc failed."); #else modemctl_notify_event(MDM_CRASH_NO_MEM); #endif break; } /* The $rcvd must be accumulated here, because $skb can be freed in pass_skb_to_demux(). */ rcvd++; if (skbpriv(skb)->lnk_hdr) { u8 ch = rb->ch; u8 fch = sipc5_get_ch(skb->data); if (fch != ch) { mif_err("frm.ch:%d != rb.ch:%d\n", fch, ch); dev_kfree_skb_any(skb); modemctl_notify_event(MDM_EVENT_CP_ABNORMAL_RX); continue; } } pass_skb_to_demux(mld, skb); } if (rcvd < num_frames) { struct io_device *iod = rb->iod; struct modem_ctl *mc = ld->mc; mif_err("%s: %s<-%s: WARN! rcvd %d < num_frames %d\n", ld->name, iod->name, mc->name, rcvd, num_frames); } return rcvd; }
static bool forbid_cp_sleep_wait(struct mem_link_device *mld) { long res; if (mld->forbid_cp_sleep) mld->forbid_cp_sleep(mld); #ifndef CONFIG_SEC_MODEM_XMM7260_CAT6 if (atomic_read(&mld->cp_boot_done)) { #endif /* As of now, tx path of iosm message should always guarantee * process context. We don't have to care rx path because cp * might try to mount lli i/f before sending data. */ res = wait_event_interruptible_timeout(mld->wq, mld->link_active(mld), msecs_to_jiffies(5000)); switch (res) { case 0: mif_err("timeout for link active event\n"); #ifdef CONFIG_SEC_MODEM_XMM7260_CAT6 modemctl_notify_event(MDM_CRASH_BY_IOSM); #endif return false; case -ERESTARTSYS: mif_info("woken by a signal\n"); return false; default: mif_info("got link active event\n"); } #ifndef CONFIG_SEC_MODEM_XMM7260_CAT6 } #endif return true; }
static void pm_cp_fail_cb(struct modem_link_pm *pm) { struct link_device *ld = pm_to_link_device(pm); struct mem_link_device *mld = ld_to_mem_link_device(ld); struct modem_ctl *mc = ld->mc; struct io_device *iod = mc->iod; unsigned long flags; spin_lock_irqsave(&mc->lock, flags); if (cp_online(mc)) { spin_unlock_irqrestore(&mc->lock, flags); if (mld->stop_pm) mld->stop_pm(mld); modemctl_notify_event(MDM_CRASH_PM_CP_FAIL); return; } if (cp_booting(mc)) { iod->modem_state_changed(iod, STATE_OFFLINE); ld->reload(ld); spin_unlock_irqrestore(&mc->lock, flags); return; } spin_unlock_irqrestore(&mc->lock, flags); }
static inline bool lli_check_max_intr(void) { if (++intr_cnt >= max_intr) { if (time_before(jiffies, expired)) { mif_info("cp_crash due to irq cnt %d\n", intr_cnt); intr_cnt = 0; modemctl_notify_event(MDM_EVENT_CP_FORCE_CRASH); return true; } lli_mark_last_busy(); } return false; }
static void cmd_crash_reset_handler(struct mem_link_device *mld) { struct link_device *ld = &mld->link_dev; struct modem_ctl *mc = ld->mc; unsigned long flags; spin_lock_irqsave(&ld->lock, flags); ld->state = LINK_STATE_OFFLINE; spin_unlock_irqrestore(&ld->lock, flags); mif_err("%s<-%s: ERR! CP_CRASH_RESET\n", ld->name, mc->name); modemctl_notify_event(MDM_CRASH_CMD_RESET); }
static void cmd_crash_exit_handler(struct mem_link_device *mld) { struct link_device *ld = &mld->link_dev; struct modem_ctl *mc = ld->mc; unsigned long flags; spin_lock_irqsave(&ld->lock, flags); ld->state = LINK_STATE_CP_CRASH; spin_unlock_irqrestore(&ld->lock, flags); if (timer_pending(&mc->crash_ack_timer)) del_timer(&mc->crash_ack_timer); mif_err("%s<-%s: ERR! CP_CRASH_EXIT\n", ld->name, mc->name); modemctl_notify_event(MDM_CRASH_CMD_EXIT); }
/** @brief pass a socket buffer to the DEMUX layer Invokes the recv_skb_single method in the io_device instance to perform receiving IPC messages from each skb. @param mld the pointer to a mem_link_device instance @param skb the pointer to an sk_buff instance @retval "> 0" if succeeded to pass an @b @@skb to the DEMUX layer @retval "< 0" an error code */ static void pass_skb_to_demux(struct mem_link_device *mld, struct sk_buff *skb) { struct link_device *ld = &mld->link_dev; struct io_device *iod = skbpriv(skb)->iod; int ret; u8 ch = skbpriv(skb)->sipc_ch; if (unlikely(!iod)) { mif_err("%s: ERR! No IOD for CH.%d\n", ld->name, ch); dev_kfree_skb_any(skb); modemctl_notify_event(MDM_CRASH_INVALID_IOD); return; } log_ipc_pkt(LNK_RX, ch, skb); ret = iod->recv_skb_single(iod, ld, skb); if (unlikely(ret < 0)) { struct modem_ctl *mc = ld->mc; mif_err_limited("%s: %s<-%s: ERR! %s->recv_skb fail (%d)\n", ld->name, iod->name, mc->name, iod->name, ret); dev_kfree_skb_any(skb); } }
static void pm_fail_cb(struct modem_link_pm *pm) { modemctl_notify_event(MDM_CRASH_PM_FAIL); }
static void pm_fail_cb(struct modem_link_pm *pm) { mipi_lli_debug_info(); modemctl_notify_event(MDM_CRASH_PM_FAIL); }
static enum hrtimer_restart sbd_tx_timer_func(struct hrtimer *timer) { struct mem_link_device *mld; struct link_device *ld; struct modem_ctl *mc; struct sbd_link_device *sl; int i; bool need_schedule; u16 mask; unsigned long flags = 0; mld = container_of(timer, struct mem_link_device, sbd_tx_timer); ld = &mld->link_dev; mc = ld->mc; sl = &mld->sbd_link_dev; need_schedule = false; mask = 0; spin_lock_irqsave(&mc->lock, flags); if (unlikely(!ipc_active(mld))) { spin_unlock_irqrestore(&mc->lock, flags); goto exit; } spin_unlock_irqrestore(&mc->lock, flags); if (mld->link_active) { if (!mld->link_active(mld)) { need_schedule = true; goto exit; } } for (i = 0; i < sl->num_channels; i++) { struct sbd_ring_buffer *rb = sbd_id2rb(sl, i, TX); int ret; ret = tx_frames_to_rb(rb); if (unlikely(ret < 0)) { if (ret == -EBUSY || ret == -ENOSPC) { need_schedule = true; mask = MASK_SEND_DATA; continue; } else { modemctl_notify_event(MDM_CRASH_INVALID_RB); need_schedule = false; goto exit; } } if (ret > 0) mask = MASK_SEND_DATA; if (!skb_queue_empty(&rb->skb_q)) need_schedule = true; } if (!need_schedule) { for (i = 0; i < sl->num_channels; i++) { struct sbd_ring_buffer *rb; rb = sbd_id2rb(sl, i, TX); if (!rb_empty(rb)) { need_schedule = true; break; } } } if (mask) { spin_lock_irqsave(&mc->lock, flags); if (unlikely(!ipc_active(mld))) { spin_unlock_irqrestore(&mc->lock, flags); need_schedule = false; goto exit; } send_ipc_irq(mld, mask2int(mask)); spin_unlock_irqrestore(&mc->lock, flags); } exit: if (need_schedule) { ktime_t ktime = ktime_set(0, ms2ns(TX_PERIOD_MS)); hrtimer_start(timer, ktime, HRTIMER_MODE_REL); } return HRTIMER_NORESTART; }
void mem_forced_cp_crash(struct mem_link_device *mld) { struct link_device *ld = &mld->link_dev; struct modem_ctl *mc = ld->mc; bool duplicated = false; unsigned long flags; /* Disable normal IPC */ set_magic(mld, MEM_CRASH_MAGIC); set_access(mld, 0); spin_lock_irqsave(&mld->lock, flags); if (atomic_read(&mc->forced_cp_crash)) duplicated = true; else atomic_set(&mc->forced_cp_crash, 1); spin_unlock_irqrestore(&mld->lock, flags); if (duplicated) { evt_log(0, "%s: %s: ALREADY in progress <%pf>\n", FUNC, ld->name, CALLER); return; } if (!cp_online(mc)) { evt_log(0, "%s: %s: %s.state %s != ONLINE <%pf>\n", FUNC, ld->name, mc->name, mc_state(mc), CALLER); return; } if (mc->wake_lock) { if (!wake_lock_active(mc->wake_lock)) { wake_lock(mc->wake_lock); mif_err("%s->wake_lock locked\n", mc->name); } } if (mld->attrs & LINK_ATTR(LINK_ATTR_MEM_DUMP)) { stop_net_ifaces(ld); if (mld->debug_info) mld->debug_info(); /** * If there is no CRASH_ACK from CP in a timeout, * handle_no_cp_crash_ack() will be executed. */ mif_add_timer(&mc->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, handle_no_cp_crash_ack, (unsigned long)mld); /* Send CRASH_EXIT command to a CP */ send_ipc_irq(mld, cmd2int(CMD_CRASH_EXIT)); } else { modemctl_notify_event(MDM_EVENT_CP_FORCE_CRASH); } evt_log(0, "%s->%s: CP_CRASH_REQ <%pf>\n", ld->name, mc->name, CALLER); #ifdef DEBUG_MODEM_IF if (in_interrupt()) queue_work(system_nrt_wq, &mld->dump_work); else save_mem_dump(mld); #endif }