From a9a8d7b544b62c08095c945cb642d8a307cfb4cf Mon Sep 17 00:00:00 2001 From: L Campbell Date: Mon, 25 Jun 2012 20:26:19 -0400 Subject: [PATCH] syscall, net: sendfile for FreeBSD R=golang-dev, rsc, bradfitz, devon.odell CC=golang-dev https://golang.org/cl/6221054 --- src/pkg/net/sendfile_freebsd.go | 104 +++++++++++++++++++++++ src/pkg/net/sendfile_stub.go | 2 +- src/pkg/syscall/asm_freebsd_amd64.s | 34 ++++++++ src/pkg/syscall/syscall_freebsd.go | 13 ++- src/pkg/syscall/syscall_freebsd_amd64.go | 2 + 5 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 src/pkg/net/sendfile_freebsd.go diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go new file mode 100644 index 00000000000..7ec6f7ff7e7 --- /dev/null +++ b/src/pkg/net/sendfile_freebsd.go @@ -0,0 +1,104 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "io" + "os" + "syscall" +) + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// sendFile copies the contents of r to c using the sendfile +// system call to minimize copies. +// +// if handled == true, sendFile returns the number of bytes copied and any +// non-EOF error. +// +// if handled == false, sendFile performed no work. +func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { + // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the + // file contains, it will loop back to the beginning ad nauseum until it's sent + // exactly the number of bytes told to. As such, we need to know exactly how many + // bytes to send. + var remain int64 = 0 + + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 0 { + return 0, nil, true + } + } + f, ok := r.(*os.File) + if !ok { + return 0, nil, false + } + + if remain == 0 { + fi, err := f.Stat() + if err != nil { + return 0, err, false + } + + remain = fi.Size() + } + + // The other quirk with FreeBSD's sendfile implementation is that it doesn't + // use the current position of the file -- if you pass it offset 0, it starts + // from offset 0. There's no way to tell it "start from current position", so + // we have to manage that explicitly. + pos, err := f.Seek(0, os.SEEK_CUR) + if err != nil { + return 0, err, false + } + + c.wio.Lock() + defer c.wio.Unlock() + if err := c.incref(false); err != nil { + return 0, err, true + } + defer c.decref() + + dst := c.sysfd + src := int(f.Fd()) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + n, err1 := syscall.Sendfile(dst, src, pos, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN && c.wdeadline >= 0 { + if err1 = pollserver.WaitWrite(c); err1 == nil { + continue + } + } + if err1 == syscall.EINTR { + continue + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile together) + err = &OpError{"sendfile", c.net, c.raddr, err1} + break + } + } + if lr != nil { + lr.N = remain + } + return written, err, written > 0 +} diff --git a/src/pkg/net/sendfile_stub.go b/src/pkg/net/sendfile_stub.go index ff76ab9cf0e..3660849c182 100644 --- a/src/pkg/net/sendfile_stub.go +++ b/src/pkg/net/sendfile_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd netbsd openbsd +// +build darwin netbsd openbsd package net diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s index 022db697a99..fbf917804f4 100644 --- a/src/pkg/syscall/asm_freebsd_amd64.s +++ b/src/pkg/syscall/asm_freebsd_amd64.s @@ -8,6 +8,7 @@ // func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); // func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64); +// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64) // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),7,$0 @@ -56,6 +57,39 @@ ok6: CALL runtime·exitsyscall(SB) RET +TEXT ·Syscall9(SB),7,$0 + CALL runtime·entersyscall(SB) + MOVQ 8(SP), AX + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 40(SP), R10 + MOVQ 48(SP), R8 + MOVQ 56(SP), R9 + + // shift around the last three arguments so they're at the + // top of the stack when the syscall is called. + MOVQ 64(SP), R11 // arg 7 + MOVQ R11, 8(SP) + MOVQ 72(SP), R11 // arg 8 + MOVQ R11, 16(SP) + MOVQ 80(SP), R11 // arg 9 + MOVQ R11, 24(SP) + + SYSCALL + JCC ok9 + MOVQ $-1, 88(SP) // r1 + MOVQ $0, 96(SP) // r2 + MOVQ AX, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET +ok9: + MOVQ AX, 88(SP) // r1 + MOVQ DX, 96(SP) // r2 + MOVQ $0, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET + TEXT ·RawSyscall(SB),7,$0 MOVQ 16(SP), DI MOVQ 24(SP), SI diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go index 6556ea8de9a..1339f4c454a 100644 --- a/src/pkg/syscall/syscall_freebsd.go +++ b/src/pkg/syscall/syscall_freebsd.go @@ -89,9 +89,16 @@ func Pipe(p []int) (err error) { return } -// TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { - return -1, ENOSYS +func Sendfile(outfd int, infd int, offset int64, count int) (written int, err error) { + var writtenOut uint64 = 0 + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) + + written = int(writtenOut) + + if e1 != 0 { + err = e1 + } + return } func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) { diff --git a/src/pkg/syscall/syscall_freebsd_amd64.go b/src/pkg/syscall/syscall_freebsd_amd64.go index 8c1ddf6db4a..488c0b9ad86 100644 --- a/src/pkg/syscall/syscall_freebsd_amd64.go +++ b/src/pkg/syscall/syscall_freebsd_amd64.go @@ -40,3 +40,5 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } + +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)