func NewClient(conn io.ReadWriteCloser, o ...ClientOpt) (*Client, error) {
	c := &Client{
		conn:        conn,
		tagPool:     pool{start: 1, limit: uint64(noTag)},
		fidPool:     pool{start: 1, limit: uint64(noFID)},
		pending:     make(map[tag]*response),
		recvr:       make(chan bool, 1),
		messageSize: DefaultMessageSize,
		log:         ulog.Null,
		version: highestSupportedVersion,
	}
	for _, opt := range o {
		if err := opt(c); err != nil {
			return nil, err
		}
	}
	c.payloadSize = roundDown(c.messageSize-msgDotLRegistry.largestFixedSize, 512)
	requested := c.version
	for {
		rversion := rversion{}
		err := c.sendRecv(&tversion{Version: versionString(version9P2000L, requested), MSize: c.messageSize}, &rversion)
		if err == linux.EAGAIN {
			if requested == lowestSupportedVersion {
				return nil, ErrVersionsExhausted
			}
			requested--
			continue
		}
		if err != nil {
			return nil, err
		}
		baseVersion, version, ok := parseVersion(rversion.Version)
		if !ok {
			log.Printf("server returned bad version string %q", rversion.Version)
			return nil, ErrBadVersionString
		}
		if baseVersion != version9P2000L {
			log.Printf("server returned unsupported base version %q (version %q)", baseVersion, rversion.Version)
			return nil, ErrBadVersionString
		}
		c.version = version
		break
	}
	return c, nil
}
NewClient creates a new client. It performs a Tversion exchange with
the server to assert that messageSize is ok to use.

You should not use the same conn for multiple clients.
package p9
import (
	"errors"
	"fmt"
	"io"
	"log"
	"sync"
	"github.com/hugelgupf/p9/internal/linux"
	"github.com/u-root/u-root/pkg/ulog"
)
var ErrOutOfTags = errors.New("out of tags -- messages lost?")
var ErrOutOfFIDs = errors.New("out of FIDs -- messages lost?")
var ErrUnexpectedTag = errors.New("unexpected tag in response")
var ErrVersionsExhausted = errors.New("exhausted all versions to negotiate")
var ErrBadVersionString = errors.New("bad version string")
type ErrBadResponse struct {
	Got  msgType
	Want msgType
}
func (e *ErrBadResponse) Error() string {
	return fmt.Sprintf("unexpected message type: got %v, want %v", e.Got, e.Want)
}
type response struct {
	r    message
	done chan error
}
var responsePool = sync.Pool{
	New: func() interface{} {
		return &response{
			done: make(chan error, 1),
		}
	},
}
type Client struct {
	conn io.ReadWriteCloser
	tagPool pool
	fidPool pool
	pending   map[tag]*response
	pendingMu sync.Mutex
	sendMu sync.Mutex
	recvr chan bool
	messageSize uint32
	payloadSize uint32
	version uint32
	log ulog.Logger
}
type ClientOpt func(*Client) error
func WithMessageSize(m uint32) ClientOpt {
	return func(c *Client) error {
		if m <= msgDotLRegistry.largestFixedSize {
			return &ErrMessageTooLarge{
				size:  m,
				msize: msgDotLRegistry.largestFixedSize,
			}
		}
		c.messageSize = m
		return nil
	}
}
func WithClientLogger(l ulog.Logger) ClientOpt {
	return func(c *Client) error {
		c.log = l
		return nil
	}
}
func roundDown(p uint32, align uint32) uint32 {
	if p > align && p%align != 0 {
		return p - p%align
	}
	return p
}