void
t3_cancel_ubuf(struct toepcb *toep, struct sockbuf *rcv)
{
	struct ddp_state *p = &toep->tp_ddp_state;
	int ubuf_pending = t3_ddp_ubuf_pending(toep);
	int err = 0, count = 0;
	
	if (p->ubuf == NULL)
		return;
	
	sockbuf_lock_assert(rcv);

	p->cancel_ubuf = 1;
	while (ubuf_pending && !(rcv->sb_state & SBS_CANTRCVMORE)) {
		CTR3(KTR_TOM,
		  "t3_cancel_ubuf: flags0 0x%x flags1 0x%x get_tcb_count %d",
		  p->buf_state[0].flags & (DDP_BF_NOFLIP | DDP_BF_NOCOPY), 
		  p->buf_state[1].flags & (DDP_BF_NOFLIP | DDP_BF_NOCOPY),
		  p->get_tcb_count);	
		if (p->get_tcb_count == 0)
			t3_cancel_ddpbuf(toep, p->cur_buf);
		else
			CTR5(KTR_TOM, "waiting err=%d get_tcb_count=%d timeo=%d rcv=%p SBS_CANTRCVMORE=%d",
			    err, p->get_tcb_count, rcv->sb_timeo, rcv,
			    !!(rcv->sb_state & SBS_CANTRCVMORE));
		
		while (p->get_tcb_count && !(rcv->sb_state & SBS_CANTRCVMORE)) {
			if (count & 0xfffffff)
				CTR5(KTR_TOM, "waiting err=%d get_tcb_count=%d timeo=%d rcv=%p count=%d",
				    err, p->get_tcb_count, rcv->sb_timeo, rcv, count);
			count++;
			err = sbwait(rcv);
		}
		ubuf_pending = t3_ddp_ubuf_pending(toep);
	}
	p->cancel_ubuf = 0;
	p->user_ddp_pending = 0;

}
Beispiel #2
0
int
soreceive(struct socket * so, 
   struct mbuf **aname,
   char * data,
   int * datalen,
   int   flags)
{
   struct mbuf *  m;
   int   len;
   int   error =  0;
   int   offset;
   struct protosw *  pr =  so->so_proto;
   struct mbuf *  nextrecord;
   int   moff;
   int   lflags;

   if (aname)
      *aname = 0;
   if (flags & MSG_OOB) 
   {
      m = m_get (M_WAIT, MT_RXDATA);
      if (m == NULL)
         return ENOBUFS;
      lflags = flags & MSG_PEEK;

      so->so_req = PRU_RCVOOB;
      error = (*pr->pr_usrreq)(so, m, LONG2MBUF((long)lflags));
      if (error == 0)
      {
         do 
         {
            len = *datalen;
            if (len > (int)m->m_len)
               len = m->m_len;

            MEMCPY(data, mtod(m, char*), len);
            data += len;
            *datalen = len;
            m = m_free(m);
         } while (*datalen && (error == 0) && m);
      }

      if (m)
         m_freem(m);
      return (error);
   }

restart:
   sblock (&so->so_rcv);
   INET_TRACE (INETM_IO,
    ("INET:soreceive sbcc %d soerror %d so_state %d *datalen %d\n",
    so->so_rcv.sb_cc, so->so_error, so->so_state, *datalen));

   /* If no data is ready, see if we should wait or return */
   if (so->so_rcv.sb_cc == 0) 
   {
      if (so->so_error) 
      {
         error = so->so_error;
         so->so_error = 0;
         goto release;
      }
      if (so->so_state & SS_CANTRCVMORE)
         goto release;
      if ((so->so_state & SS_ISCONNECTED) == 0 &&
          (so->so_proto->pr_flags & PR_CONNREQUIRED)) 
      {
         error = ENOTCONN;
         goto release;
      }
      if (*datalen == 0)
         goto release;
      if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) 
      {
         error = EWOULDBLOCK;
         goto release;
      }
      sbunlock(&so->so_rcv);
      sbwait(&so->so_rcv);
      goto restart;
   }
   m = so->so_rcv.sb_mb;
   if (m == 0)
      panic("sorecv 1");
   nextrecord = m->m_act;
   if (pr->pr_flags & PR_ADDR) 
   {
      if (m->m_type != MT_SONAME) 
      {
         dprintf ("sorecv:type %d not nam", m->m_type);
         panic("sorecv 2");
      }
      if (flags & MSG_PEEK) 
      {
         if (aname)
            *aname = m_copy (m, 0, m->m_len);
         m = m->m_next;
      } else 
      {
         sbfree (&so->so_rcv, m);
         if (aname) 
         {
            *aname = m;
            m = m->m_next;
            (*aname)->m_next = 0;
            so->so_rcv.sb_mb = m;
         } else 
         {
            MFREE(m, so->so_rcv.sb_mb);
            m = so->so_rcv.sb_mb;
         }
         if (m)
            m->m_act = nextrecord;
      }
   }
   moff = 0;
   offset = 0;
   while (m && (*datalen > 0) && (error == 0))
   {
      if (m->m_type != MT_RXDATA && m->m_type != MT_HEADER)
         panic("sorecv 3");
      len = *datalen;
      so->so_state &= ~SS_RCVATMARK;
      if (so->so_oobmark && (len > (int)(so->so_oobmark - offset)))
         len = (int)(so->so_oobmark - offset);
      if (len > (int)(m->m_len - moff))
         len = m->m_len - moff;

      INET_TRACE (INETM_IO, ("INET: soreceive, so %lx %d bytes, flags %x\n",
                  so, len, flags));

      /*
       * Copy mbufs info user buffer, then free them.
       * Sockbuf must be consistent here (points to current mbuf,
       * it points to next record) when we drop priority;
       * we must note any additions to the sockbuf when we
       * block interrupts again.
       */

      MEMCPY(data, (mtod(m, char *) + moff), len);
      data += len;
      *datalen -= len;

      if (len == (int)(m->m_len - moff))
      {
         if (flags & MSG_PEEK) 
         {
            m = m->m_next;
            moff = 0;
         } else 
         {
            nextrecord = m->m_act;
            sbfree(&so->so_rcv, m);
            {
               MFREE(m, so->so_rcv.sb_mb);
               m = so->so_rcv.sb_mb;
            }
            if (m)
               m->m_act = nextrecord;
         }
      } else 
      {
         if (flags & MSG_PEEK)
            moff += len;
         else 
         {
            m->m_data += len;
            m->m_len -= len;
            so->so_rcv.sb_cc -= len;
         }
      }
      if (so->so_oobmark) 
      {
         if ((flags & MSG_PEEK) == 0) 
         {
            so->so_oobmark -= len;
            if (so->so_oobmark == 0) 
            {
               so->so_state |= SS_RCVATMARK;
               break;
            }
         } else
            offset += len;
      }
   }

   if ((flags & MSG_PEEK) == 0) 
   {
      if (m == 0)
         so->so_rcv.sb_mb = nextrecord;
      else if (pr->pr_flags & PR_ATOMIC)
         (void) sbdroprecord(&so->so_rcv);
      if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
      {
         so->so_req = PRU_RCVD;
         (*pr->pr_usrreq)(so, (struct mbuf *)0,
          (struct mbuf *)0);
      }
   }
release:
   sbunlock(&so->so_rcv);
   return (error);
}
Beispiel #3
0
int
sosend(struct socket *so, 
       struct mbuf *nam,      /* sockaddr, if UDP socket, NULL if TCP */
       char  *data,           /* data to send */
       int   *data_length,    /* IN/OUT  length of (remaining) data */
       int   flags)
{
   struct mbuf *head = (struct mbuf *)NULL;
   struct mbuf *m;
   int   space;
   int   resid;
   int   len;
   int   error = 0;
   int   dontroute;
   int   first = 1;

   resid = *data_length;

   /*
    * In theory resid should be unsigned.
    * However, space must be signed, as it might be less than 0
    * if we over-committed, and we must use a signed comparison
    * of space and resid.  On the other hand, a negative resid
    * causes us to loop sending 0-length segments to the protocol.
    */
   if (resid < 0)
      return (EINVAL);

   INET_TRACE (INETM_IO, ("INET:sosend: so %lx resid %d sb_hiwat %d so_state %x\n",
               so, resid, so->so_snd.sb_hiwat, so->so_state));

   if (sosendallatonce(so) && (resid > (int)so->so_snd.sb_hiwat))
      return (EMSGSIZE);

   dontroute = (flags & MSG_DONTROUTE) &&
               ((so->so_options & SO_DONTROUTE) == 0) &&
               (so->so_proto->pr_flags & PR_ATOMIC);

#define     snderr(errno)     {  error =  errno;   goto  release; }

restart:
   sblock(&so->so_snd);
   do 
   {
      if (so->so_error) 
      {
         error = so->so_error;
         so->so_error = 0;          /* ??? */
         goto release;
      }
      if (so->so_state & SS_CANTSENDMORE)
         snderr(EPIPE);
      if ((so->so_state & SS_ISCONNECTED) == 0) 
      {
         if (so->so_proto->pr_flags & PR_CONNREQUIRED)
            snderr(ENOTCONN);
         if (nam == 0)
            snderr(EDESTADDRREQ);
      }
      if (flags & MSG_OOB)
         space = 1024;
      else 
      {
         space = (int)sbspace(&so->so_snd);
         if ((sosendallatonce(so) && (space < resid)) ||
             ((resid >= CLBYTES) && (space < CLBYTES) &&
              (so->so_snd.sb_cc >= CLBYTES) &&
              ((so->so_state & SS_NBIO) == 0) &&
              ((flags & MSG_DONTWAIT) == 0)))
         {
            if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT))
            {
               if (first)
                  error = EWOULDBLOCK;
               goto release;
            }
            sbunlock(&so->so_snd);
            sbwait(&so->so_snd);
            goto restart;
         }
      }
      if ( space <= 0 ) 
      {
         /* no space in socket send buffer - see if we can wait */
         if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT))
         {
            if (first)     /* report first error */
               error = EWOULDBLOCK;
            goto release;
         }
         /* If blocking socket, let someone else run */
         sbunlock(&so->so_snd);
         sbwait(&so->so_snd);
         goto restart;
      }

      while (space > 0) 
      {
         len = resid;
         if ( so->so_type == SOCK_STREAM )
         {
            m = m_getwithdata(MT_TXDATA, len);
            if (!m)   
               snderr(ENOBUFS);
            MEMCPY(m->m_data, data, len);
            so->so_snd.sb_flags |= SB_MBCOMP;   /* allow compression */
         }
         else
         {
            m = m_get (M_WAIT, MT_TXDATA);
            m->m_data = data;
         }
         INET_TRACE (INETM_IO,
          ("sosend:got %d bytes so %lx mlen %d, off %d mtod %x\n",
             len, so, m->m_len, m->m_off, mtod (m, caddr_t)));

         *data_length -= len;
         resid -= len;
         data += len;
         m->m_len = len;
         if (head == (struct mbuf *)NULL)
            head = m;
         if (error)
            goto release;
         if (*data_length <= 0)
            break;
      }

      if (dontroute)
         so->so_options |= SO_DONTROUTE;

      so->so_req = (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND;
      error = (*so->so_proto->pr_usrreq)(so, head, nam);

      if (dontroute)
         so->so_options &= ~SO_DONTROUTE;

      head = (struct mbuf *)NULL;
      first = 0;
   } while ((resid != 0) && (error == 0));

release:
   sbunlock(&so->so_snd);  
   if (head)
      m_freem(head);
   return error;
}
Beispiel #4
0
static int
kttcp_soreceive(struct socket *so, unsigned long long slen,
    unsigned long long *done, struct lwp *l, int *flagsp)
{
	struct mbuf *m, **mp;
	int flags, len, error, offset, moff, type;
	long long orig_resid, resid;
	const struct protosw *pr;
	struct mbuf *nextrecord;

	pr = so->so_proto;
	mp = NULL;
	type = 0;
	resid = orig_resid = slen;
	if (flagsp)
		flags = *flagsp &~ MSG_EOR;
	else
 		flags = 0;
	if (flags & MSG_OOB) {
		m = m_get(M_WAIT, MT_DATA);
		solock(so);
		error = (*pr->pr_usrreqs->pr_recvoob)(so, m, flags & MSG_PEEK);
		sounlock(so);
		if (error)
			goto bad;
		do {
			resid -= min(resid, m->m_len);
			m = m_free(m);
		} while (resid && error == 0 && m);
 bad:
		if (m)
			m_freem(m);
		return (error);
	}
	if (mp)
		*mp = NULL;
	solock(so);
 restart:
	if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0)
		return (error);
	m = so->so_rcv.sb_mb;
	/*
	 * If we have less data than requested, block awaiting more
	 * (subject to any timeout) if:
	 *   1. the current count is less than the low water mark,
	 *   2. MSG_WAITALL is set, and it is possible to do the entire
	 *	receive operation at once if we block (resid <= hiwat), or
	 *   3. MSG_DONTWAIT is not set.
	 * If MSG_WAITALL is set but resid is larger than the receive buffer,
	 * we have to do the receive in sections, and thus risk returning
	 * a short count if a timeout or signal occurs after we start.
	 */
	if (m == NULL || (((flags & MSG_DONTWAIT) == 0 &&
	    so->so_rcv.sb_cc < resid) &&
	    (so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
	    ((flags & MSG_WAITALL) && resid <= so->so_rcv.sb_hiwat)) &&
	    m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) {
#ifdef DIAGNOSTIC
		if (m == NULL && so->so_rcv.sb_cc)
			panic("receive 1");
#endif
		if (so->so_error) {
			if (m)
				goto dontblock;
			error = so->so_error;
			if ((flags & MSG_PEEK) == 0)
				so->so_error = 0;
			goto release;
		}
		if (so->so_state & SS_CANTRCVMORE) {
			if (m)
				goto dontblock;
			else
				goto release;
		}
		for (; m; m = m->m_next)
			if (m->m_type == MT_OOBDATA  || (m->m_flags & M_EOR)) {
				m = so->so_rcv.sb_mb;
				goto dontblock;
			}
		if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
		    (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
			error = ENOTCONN;
			goto release;
		}
		if (resid == 0)
			goto release;
		if ((so->so_state & SS_NBIO) ||
		    (flags & (MSG_DONTWAIT|MSG_NBIO))) {
			error = EWOULDBLOCK;
			goto release;
		}
		sbunlock(&so->so_rcv);
		error = sbwait(&so->so_rcv);
		if (error) {
			sounlock(so);
			return (error);
		}
		goto restart;
	}
 dontblock:
	/*
	 * On entry here, m points to the first record of the socket buffer.
	 * While we process the initial mbufs containing address and control
	 * info, we save a copy of m->m_nextpkt into nextrecord.
	 */
#ifdef notyet /* XXXX */
	if (uio->uio_lwp)
		uio->uio_lwp->l_ru.ru_msgrcv++;
#endif
	KASSERT(m == so->so_rcv.sb_mb);
	SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 1");
	SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 1");
	nextrecord = m->m_nextpkt;
	if (pr->pr_flags & PR_ADDR) {
#ifdef DIAGNOSTIC
		if (m->m_type != MT_SONAME)
			panic("receive 1a");
#endif
		orig_resid = 0;
		if (flags & MSG_PEEK) {
			m = m->m_next;
		} else {
			sbfree(&so->so_rcv, m);
			MFREE(m, so->so_rcv.sb_mb);
			m = so->so_rcv.sb_mb;
		}
	}
	while (m && m->m_type == MT_CONTROL && error == 0) {
		if (flags & MSG_PEEK) {
			m = m->m_next;
		} else {
			sbfree(&so->so_rcv, m);
			MFREE(m, so->so_rcv.sb_mb);
			m = so->so_rcv.sb_mb;
		}
	}

	/*
	 * If m is non-NULL, we have some data to read.  From now on,
	 * make sure to keep sb_lastrecord consistent when working on
	 * the last packet on the chain (nextrecord == NULL) and we
	 * change m->m_nextpkt.
	 */
	if (m) {
		if ((flags & MSG_PEEK) == 0) {
			m->m_nextpkt = nextrecord;
			/*
			 * If nextrecord == NULL (this is a single chain),
			 * then sb_lastrecord may not be valid here if m
			 * was changed earlier.
			 */
			if (nextrecord == NULL) {
				KASSERT(so->so_rcv.sb_mb == m);
				so->so_rcv.sb_lastrecord = m;
			}
		}
		type = m->m_type;
		if (type == MT_OOBDATA)
			flags |= MSG_OOB;
	} else {
		if ((flags & MSG_PEEK) == 0) {
			KASSERT(so->so_rcv.sb_mb == m);
			so->so_rcv.sb_mb = nextrecord;
			SB_EMPTY_FIXUP(&so->so_rcv);
		}
	}
	SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 2");
	SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 2");

	moff = 0;
	offset = 0;
	while (m && resid > 0 && error == 0) {
		if (m->m_type == MT_OOBDATA) {
			if (type != MT_OOBDATA)
				break;
		} else if (type == MT_OOBDATA)
			break;
#ifdef DIAGNOSTIC
		else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
			panic("receive 3");
#endif
		so->so_state &= ~SS_RCVATMARK;
		len = resid;
		if (so->so_oobmark && len > so->so_oobmark - offset)
			len = so->so_oobmark - offset;
		if (len > m->m_len - moff)
			len = m->m_len - moff;
		/*
		 * If mp is set, just pass back the mbufs.
		 * Otherwise copy them out via the uio, then free.
		 * Sockbuf must be consistent here (points to current mbuf,
		 * it points to next record) when we drop priority;
		 * we must note any additions to the sockbuf when we
		 * block interrupts again.
		 */
		resid -= len;
		if (len == m->m_len - moff) {
			if (m->m_flags & M_EOR)
				flags |= MSG_EOR;
			if (flags & MSG_PEEK) {
				m = m->m_next;
				moff = 0;
			} else {
				nextrecord = m->m_nextpkt;
				sbfree(&so->so_rcv, m);
				if (mp) {
					*mp = m;
					mp = &m->m_next;
					so->so_rcv.sb_mb = m = m->m_next;
					*mp = NULL;
				} else {
					MFREE(m, so->so_rcv.sb_mb);
					m = so->so_rcv.sb_mb;
				}
				/*
				 * If m != NULL, we also know that
				 * so->so_rcv.sb_mb != NULL.
				 */
				KASSERT(so->so_rcv.sb_mb == m);
				if (m) {
					m->m_nextpkt = nextrecord;
					if (nextrecord == NULL)
						so->so_rcv.sb_lastrecord = m;
				} else {
					so->so_rcv.sb_mb = nextrecord;
					SB_EMPTY_FIXUP(&so->so_rcv);
				}
				SBLASTRECORDCHK(&so->so_rcv,
				    "kttcp_soreceive 3");
				SBLASTMBUFCHK(&so->so_rcv,
				    "kttcp_soreceive 3");
			}
		} else {
			if (flags & MSG_PEEK)
				moff += len;
			else {
				if (mp) {
					sounlock(so);
					*mp = m_copym(m, 0, len, M_WAIT);
					solock(so);
				}
				m->m_data += len;
				m->m_len -= len;
				so->so_rcv.sb_cc -= len;
			}
		}
		if (so->so_oobmark) {
			if ((flags & MSG_PEEK) == 0) {
				so->so_oobmark -= len;
				if (so->so_oobmark == 0) {
					so->so_state |= SS_RCVATMARK;
					break;
				}
			} else {
				offset += len;
				if (offset == so->so_oobmark)
					break;
			}
		}
		if (flags & MSG_EOR)
			break;
		/*
		 * If the MSG_WAITALL flag is set (for non-atomic socket),
		 * we must not quit until "uio->uio_resid == 0" or an error
		 * termination.  If a signal/timeout occurs, return
		 * with a short count but without error.
		 * Keep sockbuf locked against other readers.
		 */
		while (flags & MSG_WAITALL && m == NULL && resid > 0 &&
		    !sosendallatonce(so) && !nextrecord) {
			if (so->so_error || so->so_state & SS_CANTRCVMORE)
				break;
			/*
			 * If we are peeking and the socket receive buffer is
			 * full, stop since we can't get more data to peek at.
			 */
			if ((flags & MSG_PEEK) && sbspace(&so->so_rcv) <= 0)
				break;
			/*
			 * If we've drained the socket buffer, tell the
			 * protocol in case it needs to do something to
			 * get it filled again.
			 */
			if ((pr->pr_flags & PR_WANTRCVD) && so->so_pcb) {
				(*pr->pr_usrreqs->pr_rcvd)(so, flags, l);
			}
			SBLASTRECORDCHK(&so->so_rcv,
			    "kttcp_soreceive sbwait 2");
			SBLASTMBUFCHK(&so->so_rcv,
			    "kttcp_soreceive sbwait 2");
			error = sbwait(&so->so_rcv);
			if (error) {
				sbunlock(&so->so_rcv);
				sounlock(so);
				return (0);
			}
			if ((m = so->so_rcv.sb_mb) != NULL)
				nextrecord = m->m_nextpkt;
		}
	}

	if (m && pr->pr_flags & PR_ATOMIC) {
		flags |= MSG_TRUNC;
		if ((flags & MSG_PEEK) == 0)
			(void) sbdroprecord(&so->so_rcv);
	}
	if ((flags & MSG_PEEK) == 0) {
		if (m == NULL) {
			/*
			 * First part is an SB_EMPTY_FIXUP().  Second part
			 * makes sure sb_lastrecord is up-to-date if
			 * there is still data in the socket buffer.
			 */
			so->so_rcv.sb_mb = nextrecord;
			if (so->so_rcv.sb_mb == NULL) {
				so->so_rcv.sb_mbtail = NULL;
				so->so_rcv.sb_lastrecord = NULL;
			} else if (nextrecord->m_nextpkt == NULL)
				so->so_rcv.sb_lastrecord = nextrecord;
		}
		SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 4");
		SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 4");
		if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) {
			(*pr->pr_usrreqs->pr_rcvd)(so, flags, l);
		}
	}
	if (orig_resid == resid && orig_resid &&
	    (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
		sbunlock(&so->so_rcv);
		goto restart;
	}

	if (flagsp)
		*flagsp |= flags;
 release:
	sbunlock(&so->so_rcv);
	sounlock(so);
	*done = slen - resid;
#if 0
	printf("soreceive: error %d slen %llu resid %lld\n", error, slen, resid);
#endif
	return (error);
}
Beispiel #5
0
/*
 * Slightly changed version of sosend()
 */
static int
kttcp_sosend(struct socket *so, unsigned long long slen,
	     unsigned long long *done, struct lwp *l, int flags)
{
	struct mbuf **mp, *m, *top;
	long space, len, mlen;
	int error, dontroute, atomic;
	long long resid;

	atomic = sosendallatonce(so);
	resid = slen;
	top = NULL;
	/*
	 * In theory resid should be unsigned.
	 * However, space must be signed, as it might be less than 0
	 * if we over-committed, and we must use a signed comparison
	 * of space and resid.  On the other hand, a negative resid
	 * causes us to loop sending 0-length segments to the protocol.
	 */
	if (resid < 0) {
		error = EINVAL;
		goto out;
	}
	dontroute =
	    (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
	    (so->so_proto->pr_flags & PR_ATOMIC);
	l->l_ru.ru_msgsnd++;
#define	snderr(errno)	{ error = errno; goto release; }
	solock(so);
 restart:
	if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0)
		goto out;
	do {
		if (so->so_state & SS_CANTSENDMORE)
			snderr(EPIPE);
		if (so->so_error) {
			error = so->so_error;
			so->so_error = 0;
			goto release;
		}
		if ((so->so_state & SS_ISCONNECTED) == 0) {
			if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
				snderr(ENOTCONN);
			} else {
				snderr(EDESTADDRREQ);
			}
		}
		space = sbspace(&so->so_snd);
		if (flags & MSG_OOB)
			space += 1024;
		if ((atomic && resid > so->so_snd.sb_hiwat))
			snderr(EMSGSIZE);
		if (space < resid && (atomic || space < so->so_snd.sb_lowat)) {
			if (so->so_state & SS_NBIO)
				snderr(EWOULDBLOCK);
			SBLASTRECORDCHK(&so->so_rcv,
			    "kttcp_soreceive sbwait 1");
			SBLASTMBUFCHK(&so->so_rcv,
			    "kttcp_soreceive sbwait 1");
			sbunlock(&so->so_snd);
			error = sbwait(&so->so_snd);
			if (error)
				goto out;
			goto restart;
		}
		mp = &top;
		do {
			sounlock(so);
			do {
				if (top == 0) {
					m = m_gethdr(M_WAIT, MT_DATA);
					mlen = MHLEN;
					m->m_pkthdr.len = 0;
					m->m_pkthdr.rcvif = NULL;
				} else {
					m = m_get(M_WAIT, MT_DATA);
					mlen = MLEN;
				}
				if (resid >= MINCLSIZE && space >= MCLBYTES) {
					m_clget(m, M_WAIT);
					if ((m->m_flags & M_EXT) == 0)
						goto nopages;
					mlen = MCLBYTES;
#ifdef	MAPPED_MBUFS
					len = lmin(MCLBYTES, resid);
#else
					if (atomic && top == 0) {
						len = lmin(MCLBYTES - max_hdr,
						    resid);
						m->m_data += max_hdr;
					} else
						len = lmin(MCLBYTES, resid);
#endif
					space -= len;
				} else {
nopages:
					len = lmin(lmin(mlen, resid), space);
					space -= len;
					/*
					 * For datagram protocols, leave room
					 * for protocol headers in first mbuf.
					 */
					if (atomic && top == 0 && len < mlen)
						MH_ALIGN(m, len);
				}
				resid -= len;
				m->m_len = len;
				*mp = m;
				top->m_pkthdr.len += len;
				if (error)
					goto release;
				mp = &m->m_next;
				if (resid <= 0) {
					if (flags & MSG_EOR)
						top->m_flags |= M_EOR;
					break;
				}
			} while (space > 0 && atomic);
			solock(so);

			if (so->so_state & SS_CANTSENDMORE)
				snderr(EPIPE);
			if (dontroute)
				so->so_options |= SO_DONTROUTE;
			if (resid > 0)
				so->so_state |= SS_MORETOCOME;
			if (flags & MSG_OOB)
				error = (*so->so_proto->pr_usrreqs->pr_sendoob)(so,
				    top, NULL);
			else
				error = (*so->so_proto->pr_usrreqs->pr_send)(so,
				    top, NULL, NULL, l);
			if (dontroute)
				so->so_options &= ~SO_DONTROUTE;
			if (resid > 0)
				so->so_state &= ~SS_MORETOCOME;
			top = 0;
			mp = &top;
			if (error)
				goto release;
		} while (resid && space > 0);
	} while (resid);

 release:
	sbunlock(&so->so_snd);
 out:
 	sounlock(so);
	if (top)
		m_freem(top);
	*done = slen - resid;
#if 0
	printf("sosend: error %d slen %llu resid %lld\n", error, slen, resid);
#endif
	return (error);
}
Beispiel #6
0
static int
handle_ddp(struct socket *so, struct uio *uio, int flags, int error)
{
	struct sockbuf *sb = &so->so_rcv;
	struct tcpcb *tp = so_sototcpcb(so);
	struct toepcb *toep = tp->t_toe;
	struct adapter *sc = td_adapter(toep->td);
	vm_page_t *pages;
	int npages, db_idx, rc, buf_flag;
	struct ddp_buffer *db;
	struct wrqe *wr;
	uint64_t ddp_flags;

	SOCKBUF_LOCK_ASSERT(sb);

#if 0
	if (sbused(sb) + sc->tt.ddp_thres > uio->uio_resid) {
		CTR4(KTR_CXGBE, "%s: sb_cc %d, threshold %d, resid %d",
		    __func__, sbused(sb), sc->tt.ddp_thres, uio->uio_resid);
	}
#endif

	/* XXX: too eager to disable DDP, could handle NBIO better than this. */
	if (sbused(sb) >= uio->uio_resid || uio->uio_resid < sc->tt.ddp_thres ||
	    uio->uio_resid > MAX_DDP_BUFFER_SIZE || uio->uio_iovcnt > 1 ||
	    so->so_state & SS_NBIO || flags & (MSG_DONTWAIT | MSG_NBIO) ||
	    error || so->so_error || sb->sb_state & SBS_CANTRCVMORE)
		goto no_ddp;

	/*
	 * Fault in and then hold the pages of the uio buffers.  We'll wire them
	 * a bit later if everything else works out.
	 */
	SOCKBUF_UNLOCK(sb);
	if (hold_uio(uio, &pages, &npages) != 0) {
		SOCKBUF_LOCK(sb);
		goto no_ddp;
	}
	SOCKBUF_LOCK(sb);
	if (__predict_false(so->so_error || sb->sb_state & SBS_CANTRCVMORE)) {
		vm_page_unhold_pages(pages, npages);
		free(pages, M_CXGBE);
		goto no_ddp;
	}

	/*
	 * Figure out which one of the two DDP buffers to use this time.
	 */
	db_idx = select_ddp_buffer(sc, toep, pages, npages,
	    (uintptr_t)uio->uio_iov->iov_base & PAGE_MASK, uio->uio_resid);
	pages = NULL;	/* handed off to select_ddp_buffer */
	if (db_idx < 0)
		goto no_ddp;
	db = toep->db[db_idx];
	buf_flag = db_idx == 0 ? DDP_BUF0_ACTIVE : DDP_BUF1_ACTIVE;

	/*
	 * Build the compound work request that tells the chip where to DMA the
	 * payload.
	 */
	ddp_flags = select_ddp_flags(so, flags, db_idx);
	wr = mk_update_tcb_for_ddp(sc, toep, db_idx, sbused(sb), ddp_flags);
	if (wr == NULL) {
		/*
		 * Just unhold the pages.  The DDP buffer's software state is
		 * left as-is in the toep.  The page pods were written
		 * successfully and we may have an opportunity to use it in the
		 * future.
		 */
		vm_page_unhold_pages(db->pages, db->npages);
		goto no_ddp;
	}

	/* Wire (and then unhold) the pages, and give the chip the go-ahead. */
	wire_ddp_buffer(db);
	t4_wrq_tx(sc, wr);
	sb->sb_flags &= ~SB_DDP_INDICATE;
	toep->ddp_flags |= buf_flag;

	/*
	 * Wait for the DDP operation to complete and then unwire the pages.
	 * The return code from the sbwait will be the final return code of this
	 * function.  But we do need to wait for DDP no matter what.
	 */
	rc = sbwait(sb);
	while (toep->ddp_flags & buf_flag) {
		/* XXXGL: shouldn't here be sbwait() call? */
		sb->sb_flags |= SB_WAIT;
		msleep(&sb->sb_acc, &sb->sb_mtx, PSOCK , "sbwait", 0);
	}
	unwire_ddp_buffer(db);
	return (rc);
no_ddp:
	disable_ddp(sc, toep);
	discourage_ddp(toep);
	sb->sb_flags &= ~SB_DDP_INDICATE;
	return (0);
}
Beispiel #7
0
static int
dhcp_get_offer(struct dhcp_context * context, int wait_ticks)
{
    int				error = 0;
    int	 			gather_count = 0;
    const struct in_addr * 	ip;
    int				last_rating = 0;
    int				len;
    int				n;
    int 			rating;
    struct dhcp *		reply;
    struct in_addr		server_id;
    struct socket * 		timer_arg;

    timer_arg = context->so;
    reply = dhcp_context_reply(context);
    timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks);
    while (1) {
	error = receive_packet(context->so, context->reply,
			       sizeof(context->reply), &n);
	if (error == 0) {
	    dhcpol_t		options;

	    dprintf(("\ndhcp: received packet length %d\n", n));
	    if (n < (int)sizeof(struct dhcp)) {
		dprintf(("dhcp: packet is too short %d < %d\n",
			 n, (int)sizeof(struct dhcp)));
		continue;
	    }
	    if (ntohl(reply->dp_xid) != context->xid
		|| reply->dp_yiaddr.s_addr == 0
		|| reply->dp_yiaddr.s_addr == INADDR_BROADCAST
		|| bcmp(reply->dp_chaddr,
			link_address(context->dl_p), 
			link_address_length(context->dl_p)) != 0) {
		/* not for us */
		continue;
	    }
	    (void)dhcpol_parse_packet(&options, reply, n);
	    if (get_dhcp_msgtype(&options) != dhcp_msgtype_offer_e) {
		/* not an offer */
		goto next_packet;
	    }
	    ip = (const struct in_addr *)
		dhcpol_find(&options, 
			    dhcptag_server_identifier_e, &len, NULL);
	    if (ip == NULL || len < (int)sizeof(*ip)) {
		/* missing/invalid server identifier */
		goto next_packet;
	    }
	    printf("dhcp: received OFFER: server " IP_FORMAT
		   " IP address "  IP_FORMAT "\n",
		   IP_LIST(ip), IP_LIST(&reply->dp_yiaddr));
	    server_id = *ip;
	    rating = rate_packet(&options);
	    if (rating > last_rating) {
		context->iaddr = reply->dp_yiaddr;
		ip = (const struct in_addr *)
		    dhcpol_find(&options, 
				dhcptag_subnet_mask_e, &len, NULL);
		if (ip != NULL && len >= (int)sizeof(*ip)) {
		    context->netmask = *ip;
		}
		ip = (const struct in_addr *)
		    dhcpol_find(&options, dhcptag_router_e, &len, NULL);
		if (ip != NULL && len >= (int)sizeof(*ip)) {
		    context->router = *ip;
		}
		context->server_id = server_id;
	    }
	    if (rating >= GOOD_RATING) {
		dhcpol_free(&options);
		/* packet is good enough */
		untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
		break;
	    }
	    if (gather_count == 0) {
		untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
		timer_arg = context->so;
		timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, 
			hz * GATHER_TIME_SECS);
	    }
	    gather_count = 1;
	next_packet:
	    dhcpol_free(&options);
	}
	else if ((error != EWOULDBLOCK)) {
	    untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
	    break;
	}
	else if (timer_arg == NULL) { /* timed out */
	    if (gather_count != 0) {
		dprintf(("dhcp: gathering time has expired\n"));
		error = 0;
	    }
	    break;
	}
	else {
	    socket_lock(context->so, 1);
	    error = sbwait(&context->so->so_rcv);
	    socket_unlock(context->so, 1);
	}
    }
    return (error);
}
Beispiel #8
0
static int
dhcp_get_ack(struct dhcp_context * context, int wait_ticks)
{
    int				error = 0;
    const struct in_addr * 	ip;
    int				len;
    int				n;
    struct dhcp *		reply;
    struct in_addr		server_id;
    struct socket * 		timer_arg;

    timer_arg = context->so;
    reply = dhcp_context_reply(context);
    timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks);
    while (1) {
	error = receive_packet(context->so, context->reply,
			       sizeof(context->reply), &n);
	if (error == 0) {
	    dhcp_msgtype_t	msg;
	    dhcpol_t		options;

	    dprintf(("\ndhcp: received packet length %d\n", n));
	    if (n < (int)sizeof(struct dhcp)) {
		dprintf(("dhcp: packet is too short %d < %d\n",
			 n, (int)sizeof(struct dhcp)));
		continue;
	    }
	    if (ntohl(reply->dp_xid) != context->xid
		|| bcmp(reply->dp_chaddr, link_address(context->dl_p), 
			link_address_length(context->dl_p)) != 0) {
		/* not for us */
		continue;
	    }
	    (void)dhcpol_parse_packet(&options, reply, n);
	    server_id.s_addr = 0;
	    ip = (const struct in_addr *)
		dhcpol_find(&options, 
			    dhcptag_server_identifier_e, &len, NULL);
	    if (ip != NULL && len >= (int)sizeof(*ip)) {
		server_id = *ip;
	    }
	    msg = get_dhcp_msgtype(&options);
	    if (msg == dhcp_msgtype_nak_e
		&& server_id.s_addr == context->server_id.s_addr) {
		/* server NAK'd us, start over */
		dhcpol_free(&options);
		error = EPROTO;
		untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
		break;
	    }
	    if (msg != dhcp_msgtype_ack_e
		|| reply->dp_yiaddr.s_addr == 0
		|| reply->dp_yiaddr.s_addr == INADDR_BROADCAST) {
		/* ignore the packet */
		goto next_packet;
	    }
	    printf("dhcp: received ACK: server " IP_FORMAT
		   " IP address "  IP_FORMAT "\n",
		   IP_LIST(&server_id), IP_LIST(&reply->dp_yiaddr));
	    context->iaddr = reply->dp_yiaddr;
	    ip = (const struct in_addr *)
		dhcpol_find(&options, 
			    dhcptag_subnet_mask_e, &len, NULL);
	    if (ip != NULL && len >= (int)sizeof(*ip)) {
		context->netmask = *ip;
	    }
	    ip = (const struct in_addr *)
		dhcpol_find(&options, dhcptag_router_e, &len, NULL);
	    if (ip != NULL && len >= (int)sizeof(*ip)) {
		context->router = *ip;
	    }
	    dhcpol_free(&options);
	    untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
	    break;

	next_packet:
	    dhcpol_free(&options);
	}
	else if ((error != EWOULDBLOCK)) {
	    /* if some other error occurred, we're done */
	    untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
	    break;
	}
	else if (timer_arg == NULL) { 
	    /* timed out */
	    break;
	}
	else {
	    /* wait for a wait to arrive, or a timeout to occur */
	    socket_lock(context->so, 1);
	    error = sbwait(&context->so->so_rcv);
	    socket_unlock(context->so, 1);
	}
    }
    return (error);
}