]> Cypherpunks repositories - gostls13.git/commitdiff
syscall, net: sendfile for FreeBSD
authorL Campbell <unpantsu@gmail.com>
Tue, 26 Jun 2012 00:26:19 +0000 (20:26 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 26 Jun 2012 00:26:19 +0000 (20:26 -0400)
R=golang-dev, rsc, bradfitz, devon.odell
CC=golang-dev
https://golang.org/cl/6221054

src/pkg/net/sendfile_freebsd.go [new file with mode: 0644]
src/pkg/net/sendfile_stub.go
src/pkg/syscall/asm_freebsd_amd64.s
src/pkg/syscall/syscall_freebsd.go
src/pkg/syscall/syscall_freebsd_amd64.go

diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go
new file mode 100644 (file)
index 0000000..7ec6f7f
--- /dev/null
@@ -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
+}
index ff76ab9cf0e88f711832299ca67af25614f86a61..3660849c182c5cc4ee93a86eee699ed9955182f6 100644 (file)
@@ -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
 
index 022db697a9943926bb9ca18b67ae83061441ed89..fbf917804f4fca036f98545eac0219bb4b221834 100644 (file)
@@ -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
index 6556ea8de9a60e4df142617502e1a9f3a380440c..1339f4c454a3618eac15a84894b1752d4f040699 100644 (file)
@@ -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) {
index 8c1ddf6db4a2a830ecbd757f4ebc623d04047d75..488c0b9ad869c69626b7ebcce3dacedb23fd6156 100644 (file)
@@ -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)