head = strp->skb_head; if (head) { /* Message already in progress */ if (unlikely(orig_offset)) { /* Getting data with a non-zero offset when a message is * in progress is not expected. If it does happen, we * need to clone and pull since we can't deal with * offsets in the skbs for a message expect in the head.
*/
orig_skb = skb_clone(orig_skb, GFP_ATOMIC); if (!orig_skb) {
STRP_STATS_INCR(strp->stats.mem_fail);
desc->error = -ENOMEM; return 0;
} if (!pskb_pull(orig_skb, orig_offset)) {
STRP_STATS_INCR(strp->stats.mem_fail);
kfree_skb(orig_skb);
desc->error = -ENOMEM; return 0;
}
cloned_orig = true;
orig_offset = 0;
}
if (!strp->skb_nextp) { /* We are going to append to the frags_list of head. * Need to unshare the frag_list.
*/
err = skb_unclone(head, GFP_ATOMIC); if (err) {
STRP_STATS_INCR(strp->stats.mem_fail);
desc->error = err; return 0;
}
if (unlikely(skb_shinfo(head)->frag_list)) { /* We can't append to an sk_buff that already * has a frag_list. We create a new head, point * the frag_list of that to the old head, and * then are able to use the old head->next for * appending to the message.
*/ if (WARN_ON(head->next)) {
desc->error = -EINVAL; return 0;
}
while (eaten < orig_len) { /* Always clone since we will consume something */
skb = skb_clone(orig_skb, GFP_ATOMIC); if (!skb) {
STRP_STATS_INCR(strp->stats.mem_fail);
desc->error = -ENOMEM; break;
}
cand_len = orig_len - eaten;
head = strp->skb_head; if (!head) {
head = skb;
strp->skb_head = head; /* Will set skb_nextp on next packet if needed */
strp->skb_nextp = NULL;
stm = _strp_msg(head);
memset(stm, 0, sizeof(*stm));
stm->strp.offset = orig_offset + eaten;
} else { /* Unclone if we are appending to an skb that we * already share a frag_list with.
*/ if (skb_has_frag_list(skb)) {
err = skb_unclone(skb, GFP_ATOMIC); if (err) {
STRP_STATS_INCR(strp->stats.mem_fail);
desc->error = err; break;
}
}
if (!len) { /* Need more header to determine length */ if (!stm->accum_len) { /* Start RX timer for new message */
strp_start_timer(strp, timeo);
}
stm->accum_len += cand_len;
eaten += cand_len;
STRP_STATS_INCR(strp->stats.need_more_hdr);
WARN_ON(eaten != orig_len); break;
} elseif (len < 0) { if (len == -ESTRPIPE && stm->accum_len) {
len = -ENODATA;
strp->unrecov_intr = 1;
} else {
strp->interrupted = 1;
}
strp_parser_err(strp, len, desc); break;
} elseif (len > max_msg_size) { /* Message length exceeds maximum allowed */
STRP_STATS_INCR(strp->stats.msg_too_big);
strp_parser_err(strp, -EMSGSIZE, desc); break;
} elseif (len <= (ssize_t)head->len -
(ssize_t)skb->len - stm->strp.offset) { /* Length must be into new skb (and also * greater than zero)
*/
STRP_STATS_INCR(strp->stats.bad_hdr_len);
strp_parser_err(strp, -EPROTO, desc); break;
}
stm->strp.full_len = len;
}
extra = (ssize_t)(stm->accum_len + cand_len) -
stm->strp.full_len;
if (extra < 0) { /* Message not complete yet. */ if (stm->strp.full_len - stm->accum_len >
strp_peek_len(strp)) { /* Don't have the whole message in the socket * buffer. Set strp->need_bytes to wait for * the rest of the message. Also, set "early * eaten" since we've already buffered the skb * but don't consume yet per strp_read_sock.
*/
if (!stm->accum_len) { /* Start RX timer for new message */
strp_start_timer(strp, timeo);
}
/* Positive extra indicates more bytes than needed for the * message
*/
WARN_ON(extra > cand_len);
eaten += (cand_len - extra);
/* Hurray, we have a new message! */
cancel_delayed_work(&strp->msg_timer_work);
strp->skb_head = NULL;
strp->need_bytes = 0;
STRP_STATS_INCR(strp->stats.msgs);
/* Give skb to upper layer */
strp->cb.rcv_msg(strp, head);
staticint default_read_sock_done(struct strparser *strp, int err)
{ return err;
}
/* Called with lock held on lower socket */ staticint strp_read_sock(struct strparser *strp)
{ struct socket *sock = strp->sk->sk_socket;
read_descriptor_t desc;
if (unlikely(!sock || !sock->ops)) return -EBUSY;
if (unlikely(!strp->cb.read_sock && !sock->ops->read_sock)) return -EBUSY;
desc.arg.data = strp;
desc.error = 0;
desc.count = 1; /* give more than one skb per call */
/* sk should be locked here, so okay to do read_sock */ if (strp->cb.read_sock)
strp->cb.read_sock(strp, &desc, strp_recv); else
sock->ops->read_sock(strp->sk, &desc, strp_recv);
/* Lower sock lock held */ void strp_data_ready(struct strparser *strp)
{ if (unlikely(strp->stopped) || strp->paused) return;
/* This check is needed to synchronize with do_strp_work. * do_strp_work acquires a process lock (lock_sock) whereas * the lock held here is bh_lock_sock. The two locks can be * held by different threads at the same time, but bh_lock_sock * allows a thread in BH context to safely check if the process * lock is held. In this case, if the lock is held, queue work.
*/ if (sock_owned_by_user_nocheck(strp->sk)) {
queue_work(strp_wq, &strp->work); return;
}
if (strp->need_bytes) { if (strp_peek_len(strp) < strp->need_bytes) return;
}
if (strp_read_sock(strp) == -ENOMEM)
queue_work(strp_wq, &strp->work);
}
EXPORT_SYMBOL_GPL(strp_data_ready);
staticvoid do_strp_work(struct strparser *strp)
{ /* We need the read lock to synchronize with strp_data_ready. We * need the socket lock for calling strp_read_sock.
*/
strp->cb.lock(strp);
if (unlikely(strp->stopped)) goto out;
if (strp->paused) goto out;
if (strp_read_sock(strp) == -ENOMEM)
queue_work(strp_wq, &strp->work);
if (!cb || !cb->rcv_msg || !cb->parse_msg) return -EINVAL;
/* The sk (sock) arg determines the mode of the stream parser. * * If the sock is set then the strparser is in receive callback mode. * The upper layer calls strp_data_ready to kick receive processing * and strparser calls the read_sock function on the socket to * get packets. * * If the sock is not set then the strparser is in general mode. * The upper layer calls strp_process for each skb to be parsed.
*/
if (!sk) { if (!cb->lock || !cb->unlock) return -EINVAL;
}
/* strp must already be stopped so that strp_recv will no longer be called. * Note that strp_done is not called with the lower socket held.
*/ void strp_done(struct strparser *strp)
{
WARN_ON(!strp->stopped);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.