usbip: inline thin wrappers and drop pass-through darwin session

Apply the <10-LoC / <3-non-test-caller heuristic across the package:
inline writeControlFrame, removeLeaseWaiter, vhciAttach, vhciHubForSpeed,
applyControlSnapshot, applyRemoteEntries, shouldRetryBusID, entrySerial,
and the trivial darwin/linux import-host constructors at their call
sites.

Delete the darwinClientSession wrapper -- darwinVirtualController already
satisfies DataSession, so attach the Description() method to it directly
and return the controller from darwinImportHost.Attach.

Fold the 11-line data_session.go into host.go beside AttachedSession,
which embeds DataSession.
This commit is contained in:
世界
2026-05-14 15:11:02 +08:00
parent 14778f5961
commit a40b8217b3
13 changed files with 75 additions and 121 deletions
+10 -1
View File
@@ -139,7 +139,16 @@ func (c *ClientService) runBusIDLoop(ctx context.Context, busid, description str
if ctx.Err() != nil {
return
}
if !c.shouldRetryBusID(ctx, busid) {
retry := true
if !c.assignment.Matched() {
err = c.syncRemoteStateContext(ctx)
if err != nil {
c.logger.Warn("refresh remote exports after releasing ", busid, ": ", err)
} else {
retry = c.assignment.IsRetryDesired(busid)
}
}
if !retry {
c.logger.Info("remote export ", busid, " disappeared; stopping import worker")
return
}
+11 -18
View File
@@ -72,7 +72,9 @@ func (s *clientControlSession) requestLease(ctx context.Context, busid string) (
Version: controlProtocolVersion,
}, request)
if err != nil {
s.removeLeaseWaiter(nonce)
s.access.Lock()
delete(s.pending, nonce)
s.access.Unlock()
return controlLeaseResponse{}, err
}
@@ -80,7 +82,9 @@ func (s *clientControlSession) requestLease(ctx context.Context, busid string) (
case result := <-waiter:
return result.response, result.err
case <-ctx.Done():
s.removeLeaseWaiter(nonce)
s.access.Lock()
delete(s.pending, nonce)
s.access.Unlock()
return controlLeaseResponse{}, ctx.Err()
}
}
@@ -100,12 +104,6 @@ func (s *clientControlSession) deliverLeaseResponse(response controlLeaseRespons
return true
}
func (s *clientControlSession) removeLeaseWaiter(nonce uint64) {
s.access.Lock()
delete(s.pending, nonce)
s.access.Unlock()
}
func (s *clientControlSession) closeWithError(err error) {
s.access.Lock()
if s.closed {
@@ -144,15 +142,6 @@ func (c *ClientService) requestImportLease(ctx context.Context, busid string) (c
}, nil
}
func (c *ClientService) applyControlSnapshot(snapshot controlDeviceSnapshot) {
devices := deviceInfoV2Map(snapshot.Devices)
values := sortedDeviceInfoV2Values(devices)
c.remoteAccess.Lock()
c.remoteDevicesV2 = devices
c.remoteAccess.Unlock()
c.applyRemoteDeviceState(values)
}
func (c *ClientService) applyControlDelta(delta controlDeviceDelta) {
c.remoteAccess.Lock()
if c.remoteDevicesV2 == nil {
@@ -194,6 +183,10 @@ func (c *ClientService) syncRemoteStateAndResetControlState(ctx context.Context)
c.remoteAccess.Lock()
c.remoteDevicesV2 = devices
c.remoteAccess.Unlock()
c.applyRemoteEntries(entries)
if !c.assignment.Matched() {
c.applyRemoteExports(entries)
return nil
}
c.applyMatchedExportsWithRetained(entries, nil)
return nil
}
+5 -1
View File
@@ -34,7 +34,7 @@ type darwinPendingSubmit struct {
reply chan SubmitResponse
}
var _ DataSession = (*darwinVirtualController)(nil)
var _ AttachedSession = (*darwinVirtualController)(nil)
type darwinVirtualController struct {
ctx context.Context
@@ -124,6 +124,10 @@ func (c *darwinVirtualController) Err() error {
return c.runErr
}
func (c *darwinVirtualController) Description() string {
return "IOUSBHostControllerInterface"
}
func (c *darwinVirtualController) enqueueEvent(event darwinControllerEvent) {
select {
case c.events <- event:
+10 -21
View File
@@ -99,11 +99,11 @@ func (c *ClientService) runControlSession() error {
if err != nil {
return E.Cause(errControlUnsupported, "write control preface: ", err)
}
err = writeControlFrame(conn, controlFrame{
err = writeControlMessage(conn, controlFrame{
Type: controlFrameHello,
Version: controlProtocolVersion,
Capabilities: controlCapabilities,
})
}, nil)
if err != nil {
return E.Cause(errControlUnsupported, "write control hello: ", err)
}
@@ -190,7 +190,12 @@ func (c *ClientService) runControlSession() error {
return E.Cause(errImmediateReconnect, "read device snapshot: ", err)
}
lastSeq = frame.Sequence
c.applyControlSnapshot(snapshot)
devices := deviceInfoV2Map(snapshot.Devices)
values := sortedDeviceInfoV2Values(devices)
c.remoteAccess.Lock()
c.remoteDevicesV2 = devices
c.remoteAccess.Unlock()
c.applyRemoteDeviceState(values)
case controlFrameDeviceDelta:
if !extended {
return E.Cause(errImmediateReconnect, "unexpected control frame ", frame.Type)
@@ -254,16 +259,12 @@ func (c *ClientService) syncRemoteStateContext(ctx context.Context) error {
if err != nil {
return err
}
c.applyRemoteEntries(entries)
return nil
}
func (c *ClientService) applyRemoteEntries(entries []DeviceEntry) {
if !c.assignment.Matched() {
c.applyRemoteExports(entries)
return
return nil
}
c.applyMatchedExportsWithRetained(entries, nil)
return nil
}
func (c *ClientService) applyRemoteDeviceState(devices []DeviceInfoV2) {
@@ -448,15 +449,3 @@ func (c *ClientService) fetchDevList(ctx context.Context) ([]DeviceEntry, error)
}
return ReadOpRepDevListBody(conn)
}
func (c *ClientService) shouldRetryBusID(ctx context.Context, busid string) bool {
if c.assignment.Matched() {
return true
}
err := c.syncRemoteStateContext(ctx)
if err != nil {
c.logger.Warn("refresh remote exports after releasing ", busid, ": ", err)
return true
}
return c.assignment.IsRetryDesired(busid)
}
+5 -5
View File
@@ -161,10 +161,6 @@ func (cr *controlReader) read(r io.Reader) (controlMessage, error) {
return controlMessage{Frame: frame, Payload: payload}, nil
}
func writeControlFrame(w io.Writer, frame controlFrame) error {
return writeControlMessage(w, frame, nil)
}
func writeControlMessage(w io.Writer, frame controlFrame, payload any) error {
rawPayload, err := marshalControlPayload(payload)
if err != nil {
@@ -224,12 +220,16 @@ func deviceInfoV2FromEntry(entry DeviceEntry, backend string, stableID string, s
if state == "" {
state = deviceStateAvailable
}
serial := entry.Serial
if serial == "" {
serial = entry.Info.SerialString()
}
return DeviceInfoV2{
BusID: entry.Info.BusIDString(),
StableID: stableID,
Backend: backend,
Path: cstring(entry.Info.Path[:]),
Serial: entrySerial(entry),
Serial: serial,
VendorID: entry.Info.IDVendor,
ProductID: entry.Info.IDProduct,
BCDDevice: entry.Info.BCDDevice,
+4 -4
View File
@@ -144,7 +144,7 @@ func TestDarwinClientSessionClosesOnContextCancel(t *testing.T) {
defer serverConn.Close()
controller := newDarwinVirtualController(context.Background(), newTestLogger(t), clientConn, DeviceInfoTruncated{})
go controller.readLoop()
session := &darwinClientSession{controller: controller}
session := controller
ctx, cancel := context.WithCancel(context.Background())
cancel()
@@ -304,11 +304,11 @@ func (s *darwinFakeUSBIPServer) handleControlConn(conn net.Conn) {
return
}
capabilities := hello.Capabilities & controlCapabilities
err = writeControlFrame(conn, controlFrame{
err = writeControlMessage(conn, controlFrame{
Type: controlFrameAck,
Version: controlProtocolVersion,
Capabilities: capabilities,
})
}, nil)
if err != nil {
return
}
@@ -345,7 +345,7 @@ func (s *darwinFakeUSBIPServer) handleControlConn(conn net.Conn) {
}
return
}
err = writeControlFrame(conn, controlFrame{Type: controlFramePong, Version: controlProtocolVersion})
err = writeControlMessage(conn, controlFrame{Type: controlFramePong, Version: controlProtocolVersion}, nil)
if err != nil {
return
}
-11
View File
@@ -1,11 +0,0 @@
package usbip
// DataSession implementations MUST close the channel returned by Done
// when the session terminates for any reason. Err is only valid after
// Done is closed; it returns nil for a clean detach. Close is idempotent
// and safe to call from any goroutine.
type DataSession interface {
Done() <-chan struct{}
Err() error
Close() error
}
+10
View File
@@ -48,6 +48,16 @@ type ExportSnapshot struct {
RawStatus int
}
// DataSession implementations MUST close the channel returned by Done
// when the session terminates for any reason. Err is only valid after
// Done is closed; it returns nil for a clean detach. Close is idempotent
// and safe to call from any goroutine.
type DataSession interface {
Done() <-chan struct{}
Err() error
Close() error
}
type AttachedSession interface {
DataSession
Description() string
+2 -26
View File
@@ -24,7 +24,7 @@ func newPlatformExportHost(logger log.ContextLogger, matches []option.USBIPDevic
}
func newPlatformImportHost(logger log.ContextLogger) (ImportHost, error) {
return newDarwinImportHost(logger), nil
return &darwinImportHost{logger: logger}, nil
}
// darwinExportHost retains stale captures: devices that reconcile
@@ -285,10 +285,6 @@ type darwinImportHost struct {
logger log.ContextLogger
}
func newDarwinImportHost(logger log.ContextLogger) *darwinImportHost {
return &darwinImportHost{logger: logger}
}
func (h *darwinImportHost) Start(ctx context.Context) error {
return nil
}
@@ -304,27 +300,7 @@ func (h *darwinImportHost) Attach(ctx context.Context, info DeviceInfoTruncated,
_ = controller.Close()
return nil, err
}
return &darwinClientSession{controller: controller}, nil
}
type darwinClientSession struct {
controller *darwinVirtualController
}
func (s *darwinClientSession) Done() <-chan struct{} {
return s.controller.Done()
}
func (s *darwinClientSession) Err() error {
return s.controller.Err()
}
func (s *darwinClientSession) Close() error {
return s.controller.Close()
}
func (s *darwinClientSession) Description() string {
return "IOUSBHostControllerInterface"
return controller, nil
}
var _ DataSession = (*darwinServerDataSession)(nil)
+6 -9
View File
@@ -26,7 +26,10 @@ func newPlatformExportHost(logger log.ContextLogger, matches []option.USBIPDevic
}
func newPlatformImportHost(logger log.ContextLogger) (ImportHost, error) {
return newLinuxImportHost(logger), nil
return &linuxImportHost{
logger: logger,
ports: make(map[int]struct{}),
}, nil
}
func isMissingUSBDeviceError(err error) bool {
@@ -463,13 +466,6 @@ type linuxImportHost struct {
ports map[int]struct{}
}
func newLinuxImportHost(logger log.ContextLogger) *linuxImportHost {
return &linuxImportHost{
logger: logger,
ports: make(map[int]struct{}),
}
}
func (h *linuxImportHost) Start(ctx context.Context) error {
return ensureKernelPath(sysVHCIControllerV0, "vhci-hcd", "vhci_hcd.0")
}
@@ -512,7 +508,8 @@ func (h *linuxImportHost) attachOnce(ctx context.Context, info DeviceInfoTruncat
triedPorts[port] = struct{}{}
continue
}
err = vhciAttach(port, handoff.file.Fd(), info.DevID(), info.Speed)
attachLine := fmt.Sprintf("%d %d %d %d", port, int(handoff.file.Fd()), info.DevID(), info.Speed)
err = writeSysfs(filepath.Join(sysVHCIControllerV0, "attach"), attachLine)
if err != nil {
h.releasePort(port)
if errors.Is(err, unix.EBUSY) {
+5 -8
View File
@@ -287,18 +287,15 @@ func trailingCString(b []byte) string {
return ""
}
func entrySerial(entry DeviceEntry) string {
if entry.Serial != "" {
return entry.Serial
}
return entry.Info.SerialString()
}
func entryDeviceKey(entry DeviceEntry) DeviceKey {
serial := entry.Serial
if serial == "" {
serial = entry.Info.SerialString()
}
return DeviceKey{
BusID: entry.Info.BusIDString(),
VendorID: entry.Info.IDVendor,
ProductID: entry.Info.IDProduct,
Serial: entrySerial(entry),
Serial: serial,
}
}
+2 -2
View File
@@ -198,12 +198,12 @@ func (s *ServerService) handleControlConn(conn net.Conn) {
capabilities := hello.Capabilities & controlCapabilities
sub, seq := s.ledger.Subscribe(s.ctx, conn, capabilities)
defer s.ledger.Unsubscribe(sub)
err = writeControlFrame(conn, controlFrame{
err = writeControlMessage(conn, controlFrame{
Type: controlFrameAck,
Version: controlProtocolVersion,
Capabilities: capabilities,
Sequence: seq,
})
}, nil)
if err != nil {
s.logger.Debug("write control ack: ", err)
return
+5 -15
View File
@@ -177,7 +177,11 @@ func vhciPickFreePort(speed uint32, skip map[int]struct{}) (int, error) {
if err != nil {
return -1, err
}
targetHub := vhciHubForSpeed(speed)
targetHub := "hs"
switch speed {
case SpeedSuper, SpeedSuperPlus:
targetHub = "ss"
}
for _, record := range records {
if record.hub != targetHub || record.state != 4 {
continue
@@ -190,11 +194,6 @@ func vhciPickFreePort(speed uint32, skip map[int]struct{}) (int, error) {
return -1, E.New("no free ", targetHub, " vhci port")
}
func vhciAttach(port int, fd uintptr, devid uint32, speed uint32) error {
line := fmt.Sprintf("%d %d %d %d", port, int(fd), devid, speed)
return writeSysfs(filepath.Join(sysVHCIControllerV0, "attach"), line)
}
func readVHCIStatus() ([]vhciStatusRecord, error) {
raw, err := os.ReadFile(filepath.Join(sysVHCIControllerV0, "status"))
if err != nil {
@@ -237,15 +236,6 @@ func parseVHCIStatus(raw string) []vhciStatusRecord {
return records
}
func vhciHubForSpeed(speed uint32) string {
switch speed {
case SpeedSuper, SpeedSuperPlus:
return "ss"
default:
return "hs"
}
}
func ensureKernelPath(path string, module string, description string) error {
_, err := os.Stat(path)
if err == nil {