Commit 52125738 authored by Jeff Sickel's avatar Jeff Sickel Committed by David du Colombier

net: plan9 changes for default net directory

This change include updates to the probeIPv4Stack
and probeIPv6Stack to ensure that one or both
protocols are supported by ip(3).
The addition of fdMutex to netFD fixes the
TestTCPConcurrentAccept failures.
Additional changes add support for keepalive.

R=golang-codereviews, 0intro
CC=golang-codereviews, rsc
https://golang.org/cl/49920048
parent ae562107
...@@ -13,12 +13,23 @@ import ( ...@@ -13,12 +13,23 @@ import (
// Network file descritor. // Network file descritor.
type netFD struct { type netFD struct {
proto, name, dir string // locking/lifetime of sysfd + serialize access to Read and Write methods
fdmu fdMutex
// immutable until Close
proto string
n string
dir string
ctl, data *os.File ctl, data *os.File
laddr, raddr Addr laddr, raddr Addr
} }
var (
netdir string // default network
)
func sysInit() { func sysInit() {
netdir = "/net"
} }
func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
...@@ -27,16 +38,99 @@ func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline ti ...@@ -27,16 +38,99 @@ func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline ti
return dialChannel(net, ra, dialer, deadline) return dialChannel(net, ra, dialer, deadline)
} }
func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD { func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr} return &netFD{proto: proto, n: name, dir: netdir + "/" + proto + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
}
func (fd *netFD) init() error {
// stub for future fd.pd.Init(fd)
return nil
}
func (fd *netFD) name() string {
var ls, rs string
if fd.laddr != nil {
ls = fd.laddr.String()
}
if fd.raddr != nil {
rs = fd.raddr.String()
}
return fd.proto + ":" + ls + "->" + rs
} }
func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil } func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
func (fd *netFD) destroy() {
if !fd.ok() {
return
}
err := fd.ctl.Close()
if fd.data != nil {
if err1 := fd.data.Close(); err1 != nil && err == nil {
err = err1
}
}
fd.ctl = nil
fd.data = nil
}
// Add a reference to this fd.
// Returns an error if the fd cannot be used.
func (fd *netFD) incref() error {
if !fd.fdmu.Incref() {
return errClosing
}
return nil
}
// Remove a reference to this FD and close if we've been asked to do so
// (and there are no references left).
func (fd *netFD) decref() {
if fd.fdmu.Decref() {
fd.destroy()
}
}
// Add a reference to this fd and lock for reading.
// Returns an error if the fd cannot be used.
func (fd *netFD) readLock() error {
if !fd.fdmu.RWLock(true) {
return errClosing
}
return nil
}
// Unlock for reading and remove a reference to this FD.
func (fd *netFD) readUnlock() {
if fd.fdmu.RWUnlock(true) {
fd.destroy()
}
}
// Add a reference to this fd and lock for writing.
// Returns an error if the fd cannot be used.
func (fd *netFD) writeLock() error {
if !fd.fdmu.RWLock(false) {
return errClosing
}
return nil
}
// Unlock for writing and remove a reference to this FD.
func (fd *netFD) writeUnlock() {
if fd.fdmu.RWUnlock(false) {
fd.destroy()
}
}
func (fd *netFD) Read(b []byte) (n int, err error) { func (fd *netFD) Read(b []byte) (n int, err error) {
if !fd.ok() || fd.data == nil { if !fd.ok() || fd.data == nil {
return 0, syscall.EINVAL return 0, syscall.EINVAL
} }
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
n, err = fd.data.Read(b) n, err = fd.data.Read(b)
if fd.proto == "udp" && err == io.EOF { if fd.proto == "udp" && err == io.EOF {
n = 0 n = 0
...@@ -49,6 +143,10 @@ func (fd *netFD) Write(b []byte) (n int, err error) { ...@@ -49,6 +143,10 @@ func (fd *netFD) Write(b []byte) (n int, err error) {
if !fd.ok() || fd.data == nil { if !fd.ok() || fd.data == nil {
return 0, syscall.EINVAL return 0, syscall.EINVAL
} }
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
return fd.data.Write(b) return fd.data.Write(b)
} }
...@@ -67,6 +165,9 @@ func (fd *netFD) CloseWrite() error { ...@@ -67,6 +165,9 @@ func (fd *netFD) CloseWrite() error {
} }
func (fd *netFD) Close() error { func (fd *netFD) Close() error {
if !fd.fdmu.IncrefAndClose() {
return errClosing
}
if !fd.ok() { if !fd.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
......
...@@ -43,7 +43,7 @@ func newFileFD(f *os.File) (net *netFD, err error) { ...@@ -43,7 +43,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
} }
comp := splitAtBytes(path, "/") comp := splitAtBytes(path, "/")
n := len(comp) n := len(comp)
if n < 3 || comp[0] != "net" { if n < 3 || comp[0][0:3] != "net" {
return nil, syscall.EPLAN9 return nil, syscall.EPLAN9
} }
...@@ -58,7 +58,7 @@ func newFileFD(f *os.File) (net *netFD, err error) { ...@@ -58,7 +58,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
} }
defer close(fd) defer close(fd)
dir := "/net/" + comp[n-2] dir := netdir + "/" + comp[n-2]
ctl = os.NewFile(uintptr(fd), dir+"/"+file) ctl = os.NewFile(uintptr(fd), dir+"/"+file)
ctl.Seek(0, 0) ctl.Seek(0, 0)
var buf [16]byte var buf [16]byte
...@@ -71,19 +71,19 @@ func newFileFD(f *os.File) (net *netFD, err error) { ...@@ -71,19 +71,19 @@ func newFileFD(f *os.File) (net *netFD, err error) {
if len(comp) < 4 { if len(comp) < 4 {
return nil, errors.New("could not find control file for connection") return nil, errors.New("could not find control file for connection")
} }
dir := "/net/" + comp[1] + "/" + name dir := netdir + "/" + comp[1] + "/" + name
ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0) ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer close(int(ctl.Fd())) defer close(int(ctl.Fd()))
} }
dir := "/net/" + comp[1] + "/" + name dir := netdir + "/" + comp[1] + "/" + name
laddr, err := readPlan9Addr(comp[1], dir+"/local") laddr, err := readPlan9Addr(comp[1], dir+"/local")
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newFD(comp[1], name, ctl, nil, laddr, nil), nil return newFD(comp[1], name, ctl, nil, laddr, nil)
} }
func newFileConn(f *os.File) (c Conn, err error) { func newFileConn(f *os.File) (c Conn, err error) {
......
...@@ -12,19 +12,45 @@ import ( ...@@ -12,19 +12,45 @@ import (
"syscall" "syscall"
) )
func probe(filename, query string, bufSize int) bool {
var file *file
var err error
if file, err = open(filename); err != nil {
return false
}
r := false
for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
f := getFields(line)
if len(f) < 3 {
continue
}
for i := 0; i < len(f); i++ {
if query == f[i] {
r = true
break
}
}
}
file.close()
return r
}
func probeIPv4Stack() bool { func probeIPv4Stack() bool {
// TODO(mikio): implement this when Plan 9 supports IPv6-only return probe(netdir+"/ipselftab", "127.0.0.1", 128)
// kernel.
return true
} }
// probeIPv6Stack returns two boolean values. If the first boolean // probeIPv6Stack returns two boolean values. If the first boolean
// value is true, kernel supports basic IPv6 functionality. If the // value is true, kernel supports basic IPv6 functionality. If the
// second boolean value is true, kernel supports IPv6 IPv4-mapping. // second boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
// TODO(mikio): implement this once Plan 9 gets an IPv6 // Plan 9 uses IPv6 natively, see ip(3).
// protocol stack implementation. r := probe(netdir+"/iproute", "6i", 128)
return false, false v := false
if r {
v = probe(netdir+"/iproute", "4b", 128)
}
return r, v
} }
// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
...@@ -133,18 +159,18 @@ func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) { ...@@ -133,18 +159,18 @@ func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
f.Close() f.Close()
return nil, &OpError{"dial", f.Name(), raddr, err} return nil, &OpError{"dial", f.Name(), raddr, err}
} }
data, err := os.OpenFile("/net/"+proto+"/"+name+"/data", os.O_RDWR, 0) data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
if err != nil { if err != nil {
f.Close() f.Close()
return nil, &OpError{"dial", net, raddr, err} return nil, &OpError{"dial", net, raddr, err}
} }
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
if err != nil { if err != nil {
data.Close() data.Close()
f.Close() f.Close()
return nil, &OpError{"dial", proto, raddr, err} return nil, &OpError{"dial", proto, raddr, err}
} }
return newFD(proto, name, f, data, laddr, raddr), nil return newFD(proto, name, f, data, laddr, raddr)
} }
func listenPlan9(net string, laddr Addr) (fd *netFD, err error) { func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
...@@ -158,20 +184,24 @@ func listenPlan9(net string, laddr Addr) (fd *netFD, err error) { ...@@ -158,20 +184,24 @@ func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
f.Close() f.Close()
return nil, &OpError{"announce", proto, laddr, err} return nil, &OpError{"announce", proto, laddr, err}
} }
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
if err != nil { if err != nil {
f.Close() f.Close()
return nil, &OpError{Op: "listen", Net: net, Err: err} return nil, &OpError{Op: "listen", Net: net, Err: err}
} }
return newFD(proto, name, f, nil, laddr, nil), nil return newFD(proto, name, f, nil, laddr, nil)
} }
func (l *netFD) netFD() *netFD { func (l *netFD) netFD() (*netFD, error) {
return newFD(l.proto, l.name, l.ctl, l.data, l.laddr, l.raddr) return newFD(l.proto, l.n, l.ctl, l.data, l.laddr, l.raddr)
} }
func (l *netFD) acceptPlan9() (fd *netFD, err error) { func (l *netFD) acceptPlan9() (fd *netFD, err error) {
defer func() { netErr(err) }() defer func() { netErr(err) }()
if err := l.readLock(); err != nil {
return nil, err
}
defer l.readUnlock()
f, err := os.Open(l.dir + "/listen") f, err := os.Open(l.dir + "/listen")
if err != nil { if err != nil {
return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
...@@ -183,16 +213,16 @@ func (l *netFD) acceptPlan9() (fd *netFD, err error) { ...@@ -183,16 +213,16 @@ func (l *netFD) acceptPlan9() (fd *netFD, err error) {
return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
} }
name := string(buf[:n]) name := string(buf[:n])
data, err := os.OpenFile("/net/"+l.proto+"/"+name+"/data", os.O_RDWR, 0) data, err := os.OpenFile(netdir+"/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
if err != nil { if err != nil {
f.Close() f.Close()
return nil, &OpError{"accept", l.proto, l.laddr, err} return nil, &OpError{"accept", l.proto, l.laddr, err}
} }
raddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote") raddr, err := readPlan9Addr(l.proto, netdir+"/"+l.proto+"/"+name+"/remote")
if err != nil { if err != nil {
data.Close() data.Close()
f.Close() f.Close()
return nil, &OpError{"accept", l.proto, l.laddr, err} return nil, &OpError{"accept", l.proto, l.laddr, err}
} }
return newFD(l.proto, name, f, data, l.laddr, raddr), nil return newFD(l.proto, name, f, data, l.laddr, raddr)
} }
...@@ -49,7 +49,7 @@ func queryCS(net, host, service string) (res []string, err error) { ...@@ -49,7 +49,7 @@ func queryCS(net, host, service string) (res []string, err error) {
if host == "" { if host == "" {
host = "*" host = "*"
} }
return query("/net/cs", net+"!"+host+"!"+service, 128) return query(netdir+"/cs", net+"!"+host+"!"+service, 128)
} }
func queryCS1(net string, ip IP, port int) (clone, dest string, err error) { func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
...@@ -70,7 +70,7 @@ func queryCS1(net string, ip IP, port int) (clone, dest string, err error) { ...@@ -70,7 +70,7 @@ func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
} }
func queryDNS(addr string, typ string) (res []string, err error) { func queryDNS(addr string, typ string) (res []string, err error) {
return query("/net/dns", addr+" "+typ, 1024) return query(netdir+"/dns", addr+" "+typ, 1024)
} }
// toLower returns a lower-case version of in. Restricting us to // toLower returns a lower-case version of in. Restricting us to
...@@ -97,7 +97,7 @@ func toLower(in string) string { ...@@ -97,7 +97,7 @@ func toLower(in string) string {
// lookupProtocol looks up IP protocol name and returns // lookupProtocol looks up IP protocol name and returns
// the corresponding protocol number. // the corresponding protocol number.
func lookupProtocol(name string) (proto int, err error) { func lookupProtocol(name string) (proto int, err error) {
lines, err := query("/net/cs", "!protocol="+toLower(name), 128) lines, err := query(netdir+"/cs", "!protocol="+toLower(name), 128)
if err != nil { if err != nil {
return 0, err return 0, err
} }
...@@ -117,7 +117,7 @@ func lookupProtocol(name string) (proto int, err error) { ...@@ -117,7 +117,7 @@ func lookupProtocol(name string) (proto int, err error) {
} }
func lookupHost(host string) (addrs []string, err error) { func lookupHost(host string) (addrs []string, err error) {
// Use /net/cs instead of /net/dns because cs knows about // Use netdir/cs instead of netdir/dns because cs knows about
// host names in local network (e.g. from /lib/ndb/local) // host names in local network (e.g. from /lib/ndb/local)
lines, err := queryCS("net", host, "1") lines, err := queryCS("net", host, "1")
if err != nil { if err != nil {
......
// Copyright 2014 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
func setKeepAlive(fd *netFD, keepalive bool) error {
if keepalive {
_, e := fd.ctl.WriteAt([]byte("keepalive"), 0)
return e
}
return nil
}
...@@ -62,12 +62,18 @@ func (c *TCPConn) SetLinger(sec int) error { ...@@ -62,12 +62,18 @@ func (c *TCPConn) SetLinger(sec int) error {
// SetKeepAlive sets whether the operating system should send // SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection. // keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error { func (c *TCPConn) SetKeepAlive(keepalive bool) error {
if !c.ok() {
return syscall.EPLAN9 return syscall.EPLAN9
}
return setKeepAlive(c.fd, keepalive)
} }
// SetKeepAlivePeriod sets period between keep alives. // SetKeepAlivePeriod sets period between keep alives.
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error { func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EPLAN9 return syscall.EPLAN9
}
return setKeepAlivePeriod(c.fd, d)
} }
// SetNoDelay controls whether the operating system should delay // SetNoDelay controls whether the operating system should delay
......
// Copyright 2014 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.
// TCP socket options for plan9
package net
import (
"time"
)
// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
cmd := "keepalive " + string(int64(d/time.Millisecond))
_, e := fd.ctl.WriteAt([]byte(cmd), 0)
return e
}
...@@ -190,7 +190,8 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { ...@@ -190,7 +190,8 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newUDPConn(l.netFD()), nil fd, err := l.netFD()
return newUDPConn(fd), err
} }
// ListenMulticastUDP listens for incoming multicast UDP packets // ListenMulticastUDP listens for incoming multicast UDP packets
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment