From 92e959a414457715d10188913dd5a5bdc2bb23af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aram=20H=C4=83v=C4=83rneanu?= Date: Mon, 23 Mar 2015 13:51:12 +0100 Subject: [PATCH] syscall, net: use sendfile on Solaris Updates #5847. Change-Id: Ic93f2e5f9a6aa3bd49cf75b16474ec5e897d17e1 Reviewed-on: https://go-review.googlesource.com/7940 Reviewed-by: Brad Fitzpatrick Reviewed-by: Minux Ma Reviewed-by: Rob Pike --- src/net/sendfile_solaris.go | 103 ++++++++++++++++++++++++++ src/net/sendfile_stub.go | 2 +- src/syscall/syscall_solaris.go | 1 + src/syscall/syscall_solaris_amd64.go | 5 -- src/syscall/zsyscall_solaris_amd64.go | 12 +++ 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 src/net/sendfile_solaris.go diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go new file mode 100644 index 0000000000..78b8b83220 --- /dev/null +++ b/src/net/sendfile_solaris.go @@ -0,0 +1,103 @@ +// Copyright 2015 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) { + // Solaris 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 nauseam 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 Solaris'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 + } + + if err := c.writeLock(); err != nil { + return 0, err, true + } + defer c.writeUnlock() + + dst := c.sysfd + src := int(f.Fd()) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = c.pd.WaitWrite(); 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 = err1 + break + } + } + if lr != nil { + lr.N = remain + } + return written, err, written > 0 +} diff --git a/src/net/sendfile_stub.go b/src/net/sendfile_stub.go index 03426ef0df..a0760b4e52 100644 --- a/src/net/sendfile_stub.go +++ b/src/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 nacl netbsd openbsd solaris +// +build darwin nacl netbsd openbsd package net diff --git a/src/syscall/syscall_solaris.go b/src/syscall/syscall_solaris.go index 8ac5afe1ff..0f60e21618 100644 --- a/src/syscall/syscall_solaris.go +++ b/src/syscall/syscall_solaris.go @@ -473,6 +473,7 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) //sys Rename(from string, to string) (err error) //sys Rmdir(path string) (err error) //sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = lseek +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = libsendfile.sendfile //sysnb Setegid(egid int) (err error) //sysnb Seteuid(euid int) (err error) //sysnb Setgid(gid int) (err error) diff --git a/src/syscall/syscall_solaris_amd64.go b/src/syscall/syscall_solaris_amd64.go index 37cf06d706..67b8af1a90 100644 --- a/src/syscall/syscall_solaris_amd64.go +++ b/src/syscall/syscall_solaris_amd64.go @@ -30,8 +30,3 @@ func (iov *Iovec) SetLen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } - -func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { - // TODO(aram): implement this, see issue 5847. - panic("unimplemented") -} diff --git a/src/syscall/zsyscall_solaris_amd64.go b/src/syscall/zsyscall_solaris_amd64.go index 2630bf6dcb..be9bc28474 100644 --- a/src/syscall/zsyscall_solaris_amd64.go +++ b/src/syscall/zsyscall_solaris_amd64.go @@ -51,6 +51,7 @@ import "unsafe" //go:cgo_import_dynamic libc_Rename rename "libc.so" //go:cgo_import_dynamic libc_Rmdir rmdir "libc.so" //go:cgo_import_dynamic libc_lseek lseek "libc.so" +//go:cgo_import_dynamic libc_sendfile sendfile "libsendfile.so" //go:cgo_import_dynamic libc_Setegid setegid "libc.so" //go:cgo_import_dynamic libc_Seteuid seteuid "libc.so" //go:cgo_import_dynamic libc_Setgid setgid "libc.so" @@ -132,6 +133,7 @@ import "unsafe" //go:linkname libc_Rename libc_Rename //go:linkname libc_Rmdir libc_Rmdir //go:linkname libc_lseek libc_lseek +//go:linkname libc_sendfile libc_sendfile //go:linkname libc_Setegid libc_Setegid //go:linkname libc_Seteuid libc_Seteuid //go:linkname libc_Setgid libc_Setgid @@ -216,6 +218,7 @@ var ( libc_Rename, libc_Rmdir, libc_lseek, + libc_sendfile, libc_Setegid, libc_Seteuid, libc_Setgid, @@ -742,6 +745,15 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { return } +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_sendfile)), 4, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) + written = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + func Setegid(egid int) (err error) { _, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc_Setegid)), 1, uintptr(egid), 0, 0, 0, 0, 0) if e1 != 0 {