mirror of
https://github.com/golang/go
synced 2024-11-20 10:54:49 -07:00
syscall, net: sendfile for FreeBSD
R=golang-dev, rsc, bradfitz, devon.odell CC=golang-dev https://golang.org/cl/6221054
This commit is contained in:
parent
ab14888712
commit
a9a8d7b544
104
src/pkg/net/sendfile_freebsd.go
Normal file
104
src/pkg/net/sendfile_freebsd.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user