static int llc_ui_wait_for_busy_core(struct sock *sk, int timeout) { DECLARE_WAITQUEUE(wait, current); struct llc_sock *llc = llc_sk(sk); int rc; add_wait_queue_exclusive(sk->sk_sleep, &wait); for (;;) { dprintk("%s: looping...\n", __FUNCTION__); __set_current_state(TASK_INTERRUPTIBLE); rc = -ENOTCONN; if (sk->sk_shutdown & RCV_SHUTDOWN) break; rc = 0; if (llc_data_accept_state(llc->state) || llc->p_flag) { release_sock(sk); timeout = schedule_timeout(timeout); lock_sock(sk); } else break; rc = -ERESTARTSYS; if (signal_pending(current)) break; rc = -EAGAIN; if (!timeout) break; } __set_current_state(TASK_RUNNING); remove_wait_queue(sk->sk_sleep, &wait); return rc; }
/** * llc_build_and_send_pkt - Connection data sending for upper layers. * @sk: connection * @skb: packet to send * * This function is called when upper layer wants to send data using * connection oriented communication mode. During sending data, connection * will be locked and received frames and expired timers will be queued. * Returns 0 for success, -ECONNABORTED when the connection already * closed and -EBUSY when sending data is not permitted in this state or * LLC has send an I pdu with p bit set to 1 and is waiting for it's * response. */ int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) { struct llc_conn_state_ev *ev; int rc = -ECONNABORTED; struct llc_opt *llc = llc_sk(sk); if (llc->state == LLC_CONN_STATE_ADM) goto out; rc = -EBUSY; if (llc_data_accept_state(llc->state)) { /* data_conn_refuse */ llc->failed_data_req = 1; goto out; } if (llc->p_flag) { llc->failed_data_req = 1; goto out; } ev = llc_conn_ev(skb); ev->type = LLC_CONN_EV_TYPE_PRIM; ev->prim = LLC_DATA_PRIM; ev->prim_type = LLC_PRIM_TYPE_REQ; skb->dev = llc->dev; rc = llc_conn_state_process(sk, skb); out: return rc; }
/** * llc_ui_send_data - send data via reliable llc2 connection * @sk: Connection the socket is using. * @skb: Data the user wishes to send. * @addr: Source and destination fields provided by the user. * @noblock: can we block waiting for data? * * Send data via reliable llc2 connection. * Returns 0 upon success, non-zero if action did not succeed. */ static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock) { struct llc_sock* llc = llc_sk(sk); int rc = 0; if (llc_data_accept_state(llc->state) || llc->p_flag) { int timeout = sock_sndtimeo(sk, noblock); rc = llc_ui_wait_for_busy_core(sk, timeout); } if (!rc) rc = llc_build_and_send_pkt(sk, skb); return rc; }
/** * llc_conn_service - finds transition and changes state of connection * @sk: connection * @skb: happened event * * This function finds transition that matches with happened event, then * executes related actions and finally changes state of connection. * Returns 0 for success, 1 for failure. */ static int llc_conn_service(struct sock *sk, struct sk_buff *skb) { int rc = 1; struct llc_sock *llc = llc_sk(sk); struct llc_conn_state_trans *trans; if (llc->state > NBR_CONN_STATES) goto out; rc = 0; trans = llc_qualify_conn_ev(sk, skb); if (trans) { rc = llc_exec_conn_trans_actions(sk, trans, skb); if (!rc && trans->next_state != NO_STATE_CHANGE) { llc->state = trans->next_state; if (!llc_data_accept_state(llc->state)) sk->sk_state_change(sk); } } out: return rc; }
/** * llc_conn_state_process - sends event to connection state machine * @sk: connection * @skb: occurred event * * Sends an event to connection state machine. After processing event * (executing it's actions and changing state), upper layer will be * indicated or confirmed, if needed. Returns 0 for success, 1 for * failure. The socket lock has to be held before calling this function. */ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) { int rc; struct llc_sock *llc = llc_sk(skb->sk); struct llc_conn_state_ev *ev = llc_conn_ev(skb); /* * We have to hold the skb, because llc_conn_service will kfree it in * the sending path and we need to look at the skb->cb, where we encode * llc_conn_state_ev. */ skb_get(skb); ev->ind_prim = ev->cfm_prim = 0; /* * Send event to state machine */ rc = llc_conn_service(skb->sk, skb); if (unlikely(rc != 0)) { ; goto out_kfree_skb; } if (unlikely(!ev->ind_prim && !ev->cfm_prim)) { /* indicate or confirm not required */ if (!skb->next) goto out_kfree_skb; goto out_skb_put; } if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */ skb_get(skb); switch (ev->ind_prim) { case LLC_DATA_PRIM: llc_save_primitive(sk, skb, LLC_DATA_PRIM); if (unlikely(sock_queue_rcv_skb(sk, skb))) { /* * shouldn't happen */ // printk(KERN_ERR "%s: sock_queue_rcv_skb failed!\n", ; kfree_skb(skb); } break; case LLC_CONN_PRIM: /* * Can't be sock_queue_rcv_skb, because we have to leave the * skb->sk pointing to the newly created struct sock in * llc_conn_handler. -acme */ skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_state_change(sk); break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_ESTABLISHED) { sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; if (!sock_flag(sk, SOCK_DEAD)) { sock_set_flag(sk, SOCK_DEAD); sk->sk_state_change(sk); } } kfree_skb(skb); sock_put(sk); break; case LLC_RESET_PRIM: /* * FIXME: * RESET is not being notified to upper layers for now */ ; kfree_skb(skb); break; default: if (ev->ind_prim) { // printk(KERN_INFO "%s: received unknown %d prim!\n", ; kfree_skb(skb); } /* No indication */ break; } switch (ev->cfm_prim) { case LLC_DATA_PRIM: if (!llc_data_accept_state(llc->state)) sk->sk_write_space(sk); else rc = llc->failed_data_req = 1; break; case LLC_CONN_PRIM: if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_SYN_SENT) { if (ev->status) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; } else { sk->sk_socket->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; } sk->sk_state_change(sk); } break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; sk->sk_state_change(sk); } sock_put(sk); break; case LLC_RESET_PRIM: /* * FIXME: * RESET is not being notified to upper layers for now */ ; break; default: if (ev->cfm_prim) { // printk(KERN_INFO "%s: received unknown %d prim!\n", ; break; } goto out_skb_put; /* No confirmation */ } out_kfree_skb: kfree_skb(skb); out_skb_put: kfree_skb(skb); return rc; }
int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) { int rc; struct llc_sock *llc = llc_sk(skb->sk); struct llc_conn_state_ev *ev = llc_conn_ev(skb); skb_get(skb); ev->ind_prim = ev->cfm_prim = 0; rc = llc_conn_service(skb->sk, skb); if (unlikely(rc != 0)) { printk(KERN_ERR "%s: llc_conn_service failed\n", __func__); goto out_kfree_skb; } if (unlikely(!ev->ind_prim && !ev->cfm_prim)) { if (!skb->next) goto out_kfree_skb; goto out_skb_put; } if (unlikely(ev->ind_prim && ev->cfm_prim)) skb_get(skb); switch (ev->ind_prim) { case LLC_DATA_PRIM: llc_save_primitive(sk, skb, LLC_DATA_PRIM); if (unlikely(sock_queue_rcv_skb(sk, skb))) { printk(KERN_ERR "%s: sock_queue_rcv_skb failed!\n", __func__); kfree_skb(skb); } break; case LLC_CONN_PRIM: skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_state_change(sk); break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_ESTABLISHED) { sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; if (!sock_flag(sk, SOCK_DEAD)) { sock_set_flag(sk, SOCK_DEAD); sk->sk_state_change(sk); } } kfree_skb(skb); sock_put(sk); break; case LLC_RESET_PRIM: printk(KERN_INFO "%s: received a reset ind!\n", __func__); kfree_skb(skb); break; default: if (ev->ind_prim) { printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->ind_prim); kfree_skb(skb); } break; } switch (ev->cfm_prim) { case LLC_DATA_PRIM: if (!llc_data_accept_state(llc->state)) sk->sk_write_space(sk); else rc = llc->failed_data_req = 1; break; case LLC_CONN_PRIM: if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_SYN_SENT) { if (ev->status) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; } else { sk->sk_socket->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; } sk->sk_state_change(sk); } break; case LLC_DISC_PRIM: sock_hold(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) { sk->sk_socket->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; sk->sk_state_change(sk); } sock_put(sk); break; case LLC_RESET_PRIM: printk(KERN_INFO "%s: received a reset conf!\n", __func__); break; default: if (ev->cfm_prim) { printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->cfm_prim); break; } goto out_skb_put; } out_kfree_skb: kfree_skb(skb); out_skb_put: kfree_skb(skb); return rc; }