Fix usbip host bind recovery
This commit is contained in:
@@ -201,6 +201,10 @@ func newTestUSBIPOps(t *testing.T) usbipOps {
|
||||
t.Fatalf("unexpected hostUnbind")
|
||||
return nil
|
||||
},
|
||||
reloadHostDriver: func() error {
|
||||
t.Fatalf("unexpected reloadHostDriver")
|
||||
return nil
|
||||
},
|
||||
readUsbipStatus: func(string) (int, error) {
|
||||
t.Fatalf("unexpected readUsbipStatus")
|
||||
return 0, nil
|
||||
@@ -837,6 +841,70 @@ func TestServerReconcileExportsBindsMatchesAndSkipsHub(t *testing.T) {
|
||||
}, server.snapshotExports())
|
||||
}
|
||||
|
||||
func TestServerBindOneRetriesAfterStaleHostMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
device := newTestDevice("1-1", 0x1d6b, 0x0104, "regular", SpeedHigh)
|
||||
ops := newTestUSBIPOps(t)
|
||||
var actions []string
|
||||
bindCalls := 0
|
||||
ops.currentDriver = func(busid string) (string, error) {
|
||||
return "usb", nil
|
||||
}
|
||||
ops.unbindFromDriver = func(busid, driver string) error {
|
||||
actions = append(actions, "unbind "+busid+" "+driver)
|
||||
return nil
|
||||
}
|
||||
ops.hostMatchBusID = func(busid string, add bool) error {
|
||||
actions = append(actions, "match "+busid+" "+map[bool]string{true: "add", false: "del"}[add])
|
||||
return nil
|
||||
}
|
||||
ops.hostBind = func(busid string) error {
|
||||
bindCalls++
|
||||
actions = append(actions, "hostbind "+busid)
|
||||
if bindCalls == 1 {
|
||||
return &os.PathError{Op: "write", Path: filepath.Join(sysUsbipHostDriver, "bind"), Err: unix.ENODEV}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ops.bindToDriver = func(busid, driver string) error {
|
||||
actions = append(actions, "bind "+busid+" "+driver)
|
||||
return nil
|
||||
}
|
||||
ops.reloadHostDriver = func() error {
|
||||
actions = append(actions, "reload")
|
||||
return nil
|
||||
}
|
||||
|
||||
server := &ServerService{
|
||||
ctx: context.Background(),
|
||||
logger: newTestLogger(),
|
||||
exports: make(map[string]serverExport),
|
||||
controlSubs: make(map[uint64]*serverControlConn),
|
||||
ops: ops,
|
||||
}
|
||||
|
||||
require.NoError(t, server.bindOne(&device))
|
||||
require.Equal(t, []string{
|
||||
"unbind 1-1 usb",
|
||||
"match 1-1 add",
|
||||
"hostbind 1-1",
|
||||
"match 1-1 del",
|
||||
"bind 1-1 usb",
|
||||
"reload",
|
||||
"unbind 1-1 usb",
|
||||
"match 1-1 add",
|
||||
"hostbind 1-1",
|
||||
}, actions)
|
||||
require.Equal(t, map[string]serverExport{
|
||||
"1-1": {
|
||||
busid: "1-1",
|
||||
managed: true,
|
||||
originalDriver: "usb",
|
||||
},
|
||||
}, server.snapshotExports())
|
||||
}
|
||||
|
||||
func TestServerReconcileExportsSkipsVHCIDevices(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1047,7 +1115,6 @@ func TestServerCloseSerializesRollbackWithActiveReconcile(t *testing.T) {
|
||||
record("hostunbind " + busid)
|
||||
return nil
|
||||
}
|
||||
|
||||
server := &ServerService{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
|
||||
@@ -19,6 +19,7 @@ type usbipOps struct {
|
||||
hostMatchBusID func(busid string, add bool) error
|
||||
hostBind func(busid string) error
|
||||
hostUnbind func(busid string) error
|
||||
reloadHostDriver func() error
|
||||
readUsbipStatus func(busid string) (int, error)
|
||||
writeUsbipSockfd func(busid string, fd int) error
|
||||
newUEventListener func() (usbEventListener, error)
|
||||
@@ -40,6 +41,7 @@ var systemUSBIPOps = usbipOps{
|
||||
hostMatchBusID: hostMatchBusID,
|
||||
hostBind: hostBind,
|
||||
hostUnbind: hostUnbind,
|
||||
reloadHostDriver: reloadHostDriver,
|
||||
readUsbipStatus: readUsbipStatus,
|
||||
writeUsbipSockfd: writeUsbipSockfd,
|
||||
newUEventListener: func() (usbEventListener, error) {
|
||||
|
||||
@@ -173,8 +173,7 @@ func (s *ServerService) reconcileExports() (bool, error) {
|
||||
continue
|
||||
}
|
||||
if err := s.bindOne(&device); err != nil {
|
||||
s.logger.Warn("bind ", busid, ": ", err)
|
||||
continue
|
||||
return changed, E.Cause(err, "bind ", busid)
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
@@ -192,6 +191,24 @@ func (s *ServerService) reconcileExports() (bool, error) {
|
||||
}
|
||||
|
||||
func (s *ServerService) bindOne(d *sysfsDevice) error {
|
||||
var err error
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
err = s.bindOneOnce(d)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if attempt > 0 || !errors.Is(err, unix.ENODEV) {
|
||||
break
|
||||
}
|
||||
s.logger.Warn("reset usbip-host after bind failure on ", d.BusID, ": ", err)
|
||||
if resetErr := s.resetHostDriverForBindRetry(); resetErr != nil {
|
||||
return E.Cause(resetErr, "reset usbip-host after bind failure")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ServerService) bindOneOnce(d *sysfsDevice) error {
|
||||
driver, err := s.ops.currentDriver(d.BusID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -228,6 +245,13 @@ func (s *ServerService) bindOne(d *sysfsDevice) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerService) resetHostDriverForBindRetry() error {
|
||||
if len(s.snapshotExports()) > 0 {
|
||||
return E.New("active usbip-host exports are present")
|
||||
}
|
||||
return s.ops.reloadHostDriver()
|
||||
}
|
||||
|
||||
func (s *ServerService) releaseExport(export serverExport, restore bool) error {
|
||||
if !export.managed {
|
||||
s.deleteExport(export.busid)
|
||||
|
||||
@@ -215,6 +215,18 @@ func hostUnbind(busid string) error {
|
||||
return writeSysfs(filepath.Join(sysUsbipHostDriver, "unbind"), busid)
|
||||
}
|
||||
|
||||
func reloadHostDriver() error {
|
||||
modprobePath, err := findModprobePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := shell.Exec(modprobePath, "-r", "usbip-host").Read()
|
||||
if err != nil {
|
||||
return E.Extend(E.Cause(err, "unload kernel module usbip-host"), strings.TrimSpace(output))
|
||||
}
|
||||
return ensureHostDriver()
|
||||
}
|
||||
|
||||
// readUsbipStatus returns the usbip_status attribute value for busid.
|
||||
func readUsbipStatus(busid string) (int, error) {
|
||||
raw, err := os.ReadFile(filepath.Join(sysBusUSBDevices, busid, "usbip_status"))
|
||||
|
||||
Reference in New Issue
Block a user