kttcp.c revision 1.27 1 /* $NetBSD: kttcp.c,v 1.27 2008/03/27 19:06:51 ad Exp $ */
2
3 /*
4 * Copyright (c) 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Frank van der Linden and Jason R. Thorpe for
8 * Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed for the NetBSD Project by
21 * Wasabi Systems, Inc.
22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
24 * written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * kttcp.c -- provides kernel support for testing network testing,
41 * see kttcp(4)
42 */
43
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: kttcp.c,v 1.27 2008/03/27 19:06:51 ad Exp $");
46
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50 #include <sys/file.h>
51 #include <sys/filedesc.h>
52 #include <sys/conf.h>
53 #include <sys/systm.h>
54 #include <sys/protosw.h>
55 #include <sys/proc.h>
56 #include <sys/resourcevar.h>
57 #include <sys/signal.h>
58 #include <sys/socketvar.h>
59 #include <sys/socket.h>
60 #include <sys/mbuf.h>
61 #include <sys/mount.h>
62 #include <sys/syscallargs.h>
63
64 #include <dev/kttcpio.h>
65
66 static int kttcp_send(struct lwp *l, struct kttcp_io_args *);
67 static int kttcp_recv(struct lwp *l, struct kttcp_io_args *);
68 static int kttcp_sosend(struct socket *, unsigned long long,
69 unsigned long long *, struct lwp *, int);
70 static int kttcp_soreceive(struct socket *, unsigned long long,
71 unsigned long long *, struct lwp *, int *);
72
73 void kttcpattach(int);
74
75 dev_type_ioctl(kttcpioctl);
76
77 const struct cdevsw kttcp_cdevsw = {
78 nullopen, nullclose, noread, nowrite, kttcpioctl,
79 nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
80 };
81
82 void
83 kttcpattach(int count)
84 {
85 /* Do nothing. */
86 }
87
88 int
89 kttcpioctl(dev_t dev, u_long cmd, void *data, int flag,
90 struct lwp *l)
91 {
92 int error;
93
94 if ((flag & FWRITE) == 0)
95 return EPERM;
96
97 switch (cmd) {
98 case KTTCP_IO_SEND:
99 error = kttcp_send(l, (struct kttcp_io_args *) data);
100 break;
101
102 case KTTCP_IO_RECV:
103 error = kttcp_recv(l, (struct kttcp_io_args *) data);
104 break;
105
106 default:
107 return EINVAL;
108 }
109
110 return error;
111 }
112
113 static int
114 kttcp_send(struct lwp *l, struct kttcp_io_args *kio)
115 {
116 struct socket *so;
117 int error;
118 struct timeval t0, t1;
119 unsigned long long len, done;
120
121 if (kio->kio_totalsize >= KTTCP_MAX_XMIT)
122 return EINVAL;
123
124 if ((error = fd_getsock(kio->kio_socket, &so)) != 0)
125 return error;
126
127 len = kio->kio_totalsize;
128 microtime(&t0);
129 do {
130 error = kttcp_sosend(so, len, &done, l, 0);
131 len -= done;
132 } while (error == 0 && len > 0);
133
134 fd_putfile(kio->kio_socket);
135
136 microtime(&t1);
137 if (error != 0)
138 return error;
139 timersub(&t1, &t0, &kio->kio_elapsed);
140
141 kio->kio_bytesdone = kio->kio_totalsize - len;
142
143 return 0;
144 }
145
146 static int
147 kttcp_recv(struct lwp *l, struct kttcp_io_args *kio)
148 {
149 struct socket *so;
150 int error;
151 struct timeval t0, t1;
152 unsigned long long len, done;
153
154 done = 0; /* XXX gcc */
155
156 if (kio->kio_totalsize > KTTCP_MAX_XMIT)
157 return EINVAL;
158
159 if ((error = fd_getsock(kio->kio_socket, &so)) != 0)
160 return error;
161 len = kio->kio_totalsize;
162 microtime(&t0);
163 do {
164 error = kttcp_soreceive(so, len, &done, l, NULL);
165 len -= done;
166 } while (error == 0 && len > 0 && done > 0);
167
168 fd_putfile(kio->kio_socket);
169
170 microtime(&t1);
171 if (error == EPIPE)
172 error = 0;
173 if (error != 0)
174 return error;
175 timersub(&t1, &t0, &kio->kio_elapsed);
176
177 kio->kio_bytesdone = kio->kio_totalsize - len;
178
179 return 0;
180 }
181
182 #define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
183
184 /*
185 * Slightly changed version of sosend()
186 */
187 static int
188 kttcp_sosend(struct socket *so, unsigned long long slen,
189 unsigned long long *done, struct lwp *l, int flags)
190 {
191 struct mbuf **mp, *m, *top;
192 long space, len, mlen;
193 int error, s, dontroute, atomic;
194 long long resid;
195
196 atomic = sosendallatonce(so);
197 resid = slen;
198 top = NULL;
199 /*
200 * In theory resid should be unsigned.
201 * However, space must be signed, as it might be less than 0
202 * if we over-committed, and we must use a signed comparison
203 * of space and resid. On the other hand, a negative resid
204 * causes us to loop sending 0-length segments to the protocol.
205 */
206 if (resid < 0) {
207 error = EINVAL;
208 goto out;
209 }
210 dontroute =
211 (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
212 (so->so_proto->pr_flags & PR_ATOMIC);
213 l->l_ru.ru_msgsnd++;
214 #define snderr(errno) { error = errno; splx(s); goto release; }
215
216 restart:
217 if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0)
218 goto out;
219 do {
220 s = splsoftnet();
221 if (so->so_state & SS_CANTSENDMORE)
222 snderr(EPIPE);
223 if (so->so_error) {
224 error = so->so_error;
225 so->so_error = 0;
226 splx(s);
227 goto release;
228 }
229 if ((so->so_state & SS_ISCONNECTED) == 0) {
230 if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
231 if ((so->so_state & SS_ISCONFIRMING) == 0)
232 snderr(ENOTCONN);
233 } else
234 snderr(EDESTADDRREQ);
235 }
236 space = sbspace(&so->so_snd);
237 if (flags & MSG_OOB)
238 space += 1024;
239 if ((atomic && resid > so->so_snd.sb_hiwat))
240 snderr(EMSGSIZE);
241 if (space < resid && (atomic || space < so->so_snd.sb_lowat)) {
242 if (so->so_nbio)
243 snderr(EWOULDBLOCK);
244 SBLASTRECORDCHK(&so->so_rcv,
245 "kttcp_soreceive sbwait 1");
246 SBLASTMBUFCHK(&so->so_rcv,
247 "kttcp_soreceive sbwait 1");
248 sbunlock(&so->so_snd);
249 error = sbwait(&so->so_snd);
250 splx(s);
251 if (error)
252 goto out;
253 goto restart;
254 }
255 splx(s);
256 mp = ⊤
257 do {
258 do {
259 if (top == 0) {
260 m = m_gethdr(M_WAIT, MT_DATA);
261 mlen = MHLEN;
262 m->m_pkthdr.len = 0;
263 m->m_pkthdr.rcvif = NULL;
264 } else {
265 m = m_get(M_WAIT, MT_DATA);
266 mlen = MLEN;
267 }
268 if (resid >= MINCLSIZE && space >= MCLBYTES) {
269 m_clget(m, M_WAIT);
270 if ((m->m_flags & M_EXT) == 0)
271 goto nopages;
272 mlen = MCLBYTES;
273 #ifdef MAPPED_MBUFS
274 len = lmin(MCLBYTES, resid);
275 #else
276 if (atomic && top == 0) {
277 len = lmin(MCLBYTES - max_hdr,
278 resid);
279 m->m_data += max_hdr;
280 } else
281 len = lmin(MCLBYTES, resid);
282 #endif
283 space -= len;
284 } else {
285 nopages:
286 len = lmin(lmin(mlen, resid), space);
287 space -= len;
288 /*
289 * For datagram protocols, leave room
290 * for protocol headers in first mbuf.
291 */
292 if (atomic && top == 0 && len < mlen)
293 MH_ALIGN(m, len);
294 }
295 resid -= len;
296 m->m_len = len;
297 *mp = m;
298 top->m_pkthdr.len += len;
299 if (error)
300 goto release;
301 mp = &m->m_next;
302 if (resid <= 0) {
303 if (flags & MSG_EOR)
304 top->m_flags |= M_EOR;
305 break;
306 }
307 } while (space > 0 && atomic);
308
309 s = splsoftnet();
310
311 if (so->so_state & SS_CANTSENDMORE)
312 snderr(EPIPE);
313
314 if (dontroute)
315 so->so_options |= SO_DONTROUTE;
316 if (resid > 0)
317 so->so_state |= SS_MORETOCOME;
318 error = (*so->so_proto->pr_usrreq)(so,
319 (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
320 top, NULL, NULL, l);
321 if (dontroute)
322 so->so_options &= ~SO_DONTROUTE;
323 if (resid > 0)
324 so->so_state &= ~SS_MORETOCOME;
325 splx(s);
326
327 top = 0;
328 mp = ⊤
329 if (error)
330 goto release;
331 } while (resid && space > 0);
332 } while (resid);
333
334 release:
335 sbunlock(&so->so_snd);
336 out:
337 if (top)
338 m_freem(top);
339 *done = slen - resid;
340 #if 0
341 printf("sosend: error %d slen %llu resid %lld\n", error, slen, resid);
342 #endif
343 return (error);
344 }
345
346 static int
347 kttcp_soreceive(struct socket *so, unsigned long long slen,
348 unsigned long long *done, struct lwp *l, int *flagsp)
349 {
350 struct mbuf *m, **mp;
351 int flags, len, error, s, offset, moff, type;
352 long long orig_resid, resid;
353 const struct protosw *pr;
354 struct mbuf *nextrecord;
355
356 pr = so->so_proto;
357 mp = NULL;
358 type = 0;
359 resid = orig_resid = slen;
360 if (flagsp)
361 flags = *flagsp &~ MSG_EOR;
362 else
363 flags = 0;
364 if (flags & MSG_OOB) {
365 m = m_get(M_WAIT, MT_DATA);
366 error = (*pr->pr_usrreq)(so, PRU_RCVOOB, m,
367 (struct mbuf *)(long)(flags & MSG_PEEK), NULL, NULL);
368 if (error)
369 goto bad;
370 do {
371 resid -= min(resid, m->m_len);
372 m = m_free(m);
373 } while (resid && error == 0 && m);
374 bad:
375 if (m)
376 m_freem(m);
377 return (error);
378 }
379 if (mp)
380 *mp = NULL;
381 if (so->so_state & SS_ISCONFIRMING && resid)
382 (*pr->pr_usrreq)(so, PRU_RCVD, NULL, NULL, NULL, NULL);
383
384 restart:
385 if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0)
386 return (error);
387 s = splsoftnet();
388
389 m = so->so_rcv.sb_mb;
390 /*
391 * If we have less data than requested, block awaiting more
392 * (subject to any timeout) if:
393 * 1. the current count is less than the low water mark,
394 * 2. MSG_WAITALL is set, and it is possible to do the entire
395 * receive operation at once if we block (resid <= hiwat), or
396 * 3. MSG_DONTWAIT is not set.
397 * If MSG_WAITALL is set but resid is larger than the receive buffer,
398 * we have to do the receive in sections, and thus risk returning
399 * a short count if a timeout or signal occurs after we start.
400 */
401 if (m == NULL || (((flags & MSG_DONTWAIT) == 0 &&
402 so->so_rcv.sb_cc < resid) &&
403 (so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
404 ((flags & MSG_WAITALL) && resid <= so->so_rcv.sb_hiwat)) &&
405 m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) {
406 #ifdef DIAGNOSTIC
407 if (m == NULL && so->so_rcv.sb_cc)
408 panic("receive 1");
409 #endif
410 if (so->so_error) {
411 if (m)
412 goto dontblock;
413 error = so->so_error;
414 if ((flags & MSG_PEEK) == 0)
415 so->so_error = 0;
416 goto release;
417 }
418 if (so->so_state & SS_CANTRCVMORE) {
419 if (m)
420 goto dontblock;
421 else
422 goto release;
423 }
424 for (; m; m = m->m_next)
425 if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
426 m = so->so_rcv.sb_mb;
427 goto dontblock;
428 }
429 if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
430 (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
431 error = ENOTCONN;
432 goto release;
433 }
434 if (resid == 0)
435 goto release;
436 if (so->so_nbio || (flags & MSG_DONTWAIT)) {
437 error = EWOULDBLOCK;
438 goto release;
439 }
440 sbunlock(&so->so_rcv);
441 error = sbwait(&so->so_rcv);
442 splx(s);
443 if (error)
444 return (error);
445 goto restart;
446 }
447 dontblock:
448 /*
449 * On entry here, m points to the first record of the socket buffer.
450 * While we process the initial mbufs containing address and control
451 * info, we save a copy of m->m_nextpkt into nextrecord.
452 */
453 #ifdef notyet /* XXXX */
454 if (uio->uio_lwp)
455 uio->uio_lwp->l_ru.ru_msgrcv++;
456 #endif
457 KASSERT(m == so->so_rcv.sb_mb);
458 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 1");
459 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 1");
460 nextrecord = m->m_nextpkt;
461 if (pr->pr_flags & PR_ADDR) {
462 #ifdef DIAGNOSTIC
463 if (m->m_type != MT_SONAME)
464 panic("receive 1a");
465 #endif
466 orig_resid = 0;
467 if (flags & MSG_PEEK) {
468 m = m->m_next;
469 } else {
470 sbfree(&so->so_rcv, m);
471 MFREE(m, so->so_rcv.sb_mb);
472 m = so->so_rcv.sb_mb;
473 }
474 }
475 while (m && m->m_type == MT_CONTROL && error == 0) {
476 if (flags & MSG_PEEK) {
477 m = m->m_next;
478 } else {
479 sbfree(&so->so_rcv, m);
480 MFREE(m, so->so_rcv.sb_mb);
481 m = so->so_rcv.sb_mb;
482 }
483 }
484
485 /*
486 * If m is non-NULL, we have some data to read. From now on,
487 * make sure to keep sb_lastrecord consistent when working on
488 * the last packet on the chain (nextrecord == NULL) and we
489 * change m->m_nextpkt.
490 */
491 if (m) {
492 if ((flags & MSG_PEEK) == 0) {
493 m->m_nextpkt = nextrecord;
494 /*
495 * If nextrecord == NULL (this is a single chain),
496 * then sb_lastrecord may not be valid here if m
497 * was changed earlier.
498 */
499 if (nextrecord == NULL) {
500 KASSERT(so->so_rcv.sb_mb == m);
501 so->so_rcv.sb_lastrecord = m;
502 }
503 }
504 type = m->m_type;
505 if (type == MT_OOBDATA)
506 flags |= MSG_OOB;
507 } else {
508 if ((flags & MSG_PEEK) == 0) {
509 KASSERT(so->so_rcv.sb_mb == m);
510 so->so_rcv.sb_mb = nextrecord;
511 SB_EMPTY_FIXUP(&so->so_rcv);
512 }
513 }
514 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 2");
515 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 2");
516
517 moff = 0;
518 offset = 0;
519 while (m && resid > 0 && error == 0) {
520 if (m->m_type == MT_OOBDATA) {
521 if (type != MT_OOBDATA)
522 break;
523 } else if (type == MT_OOBDATA)
524 break;
525 #ifdef DIAGNOSTIC
526 else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
527 panic("receive 3");
528 #endif
529 so->so_state &= ~SS_RCVATMARK;
530 len = resid;
531 if (so->so_oobmark && len > so->so_oobmark - offset)
532 len = so->so_oobmark - offset;
533 if (len > m->m_len - moff)
534 len = m->m_len - moff;
535 /*
536 * If mp is set, just pass back the mbufs.
537 * Otherwise copy them out via the uio, then free.
538 * Sockbuf must be consistent here (points to current mbuf,
539 * it points to next record) when we drop priority;
540 * we must note any additions to the sockbuf when we
541 * block interrupts again.
542 */
543 resid -= len;
544 if (len == m->m_len - moff) {
545 if (m->m_flags & M_EOR)
546 flags |= MSG_EOR;
547 if (flags & MSG_PEEK) {
548 m = m->m_next;
549 moff = 0;
550 } else {
551 nextrecord = m->m_nextpkt;
552 sbfree(&so->so_rcv, m);
553 if (mp) {
554 *mp = m;
555 mp = &m->m_next;
556 so->so_rcv.sb_mb = m = m->m_next;
557 *mp = NULL;
558 } else {
559 MFREE(m, so->so_rcv.sb_mb);
560 m = so->so_rcv.sb_mb;
561 }
562 /*
563 * If m != NULL, we also know that
564 * so->so_rcv.sb_mb != NULL.
565 */
566 KASSERT(so->so_rcv.sb_mb == m);
567 if (m) {
568 m->m_nextpkt = nextrecord;
569 if (nextrecord == NULL)
570 so->so_rcv.sb_lastrecord = m;
571 } else {
572 so->so_rcv.sb_mb = nextrecord;
573 SB_EMPTY_FIXUP(&so->so_rcv);
574 }
575 SBLASTRECORDCHK(&so->so_rcv,
576 "kttcp_soreceive 3");
577 SBLASTMBUFCHK(&so->so_rcv,
578 "kttcp_soreceive 3");
579 }
580 } else {
581 if (flags & MSG_PEEK)
582 moff += len;
583 else {
584 if (mp)
585 *mp = m_copym(m, 0, len, M_WAIT);
586 m->m_data += len;
587 m->m_len -= len;
588 so->so_rcv.sb_cc -= len;
589 }
590 }
591 if (so->so_oobmark) {
592 if ((flags & MSG_PEEK) == 0) {
593 so->so_oobmark -= len;
594 if (so->so_oobmark == 0) {
595 so->so_state |= SS_RCVATMARK;
596 break;
597 }
598 } else {
599 offset += len;
600 if (offset == so->so_oobmark)
601 break;
602 }
603 }
604 if (flags & MSG_EOR)
605 break;
606 /*
607 * If the MSG_WAITALL flag is set (for non-atomic socket),
608 * we must not quit until "uio->uio_resid == 0" or an error
609 * termination. If a signal/timeout occurs, return
610 * with a short count but without error.
611 * Keep sockbuf locked against other readers.
612 */
613 while (flags & MSG_WAITALL && m == NULL && resid > 0 &&
614 !sosendallatonce(so) && !nextrecord) {
615 if (so->so_error || so->so_state & SS_CANTRCVMORE)
616 break;
617 /*
618 * If we are peeking and the socket receive buffer is
619 * full, stop since we can't get more data to peek at.
620 */
621 if ((flags & MSG_PEEK) && sbspace(&so->so_rcv) <= 0)
622 break;
623 /*
624 * If we've drained the socket buffer, tell the
625 * protocol in case it needs to do something to
626 * get it filled again.
627 */
628 if ((pr->pr_flags & PR_WANTRCVD) && so->so_pcb)
629 (*pr->pr_usrreq)(so, PRU_RCVD, NULL,
630 (struct mbuf *)(long)flags, NULL, NULL);
631 SBLASTRECORDCHK(&so->so_rcv,
632 "kttcp_soreceive sbwait 2");
633 SBLASTMBUFCHK(&so->so_rcv,
634 "kttcp_soreceive sbwait 2");
635 error = sbwait(&so->so_rcv);
636 if (error) {
637 sbunlock(&so->so_rcv);
638 splx(s);
639 return (0);
640 }
641 if ((m = so->so_rcv.sb_mb) != NULL)
642 nextrecord = m->m_nextpkt;
643 }
644 }
645
646 if (m && pr->pr_flags & PR_ATOMIC) {
647 flags |= MSG_TRUNC;
648 if ((flags & MSG_PEEK) == 0)
649 (void) sbdroprecord(&so->so_rcv);
650 }
651 if ((flags & MSG_PEEK) == 0) {
652 if (m == NULL) {
653 /*
654 * First part is an SB_EMPTY_FIXUP(). Second part
655 * makes sure sb_lastrecord is up-to-date if
656 * there is still data in the socket buffer.
657 */
658 so->so_rcv.sb_mb = nextrecord;
659 if (so->so_rcv.sb_mb == NULL) {
660 so->so_rcv.sb_mbtail = NULL;
661 so->so_rcv.sb_lastrecord = NULL;
662 } else if (nextrecord->m_nextpkt == NULL)
663 so->so_rcv.sb_lastrecord = nextrecord;
664 }
665 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 4");
666 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 4");
667 if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
668 (*pr->pr_usrreq)(so, PRU_RCVD, NULL,
669 (struct mbuf *)(long)flags, NULL, NULL);
670 }
671 if (orig_resid == resid && orig_resid &&
672 (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
673 sbunlock(&so->so_rcv);
674 splx(s);
675 goto restart;
676 }
677
678 if (flagsp)
679 *flagsp |= flags;
680 release:
681 sbunlock(&so->so_rcv);
682 splx(s);
683 *done = slen - resid;
684 #if 0
685 printf("soreceive: error %d slen %llu resid %lld\n", error, slen, resid);
686 #endif
687 return (error);
688 }
689