github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/device/drivers/vhost_user_blk.go (about)

     1  // Copyright (c) 2017-2018 Intel Corporation
     2  // Copyright (c) 2018-2019 Huawei Corporation
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  //
     6  
     7  package drivers
     8  
     9  import (
    10  	"github.com/kata-containers/runtime/virtcontainers/device/api"
    11  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    12  	persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
    13  	"github.com/kata-containers/runtime/virtcontainers/utils"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  // VhostUserBlkDevice is a block vhost-user based device
    18  type VhostUserBlkDevice struct {
    19  	*GenericDevice
    20  	VhostUserDeviceAttrs *config.VhostUserDeviceAttrs
    21  }
    22  
    23  // NewVhostUserBlkDevice creates a new vhost-user block device based on DeviceInfo
    24  func NewVhostUserBlkDevice(devInfo *config.DeviceInfo) *VhostUserBlkDevice {
    25  	return &VhostUserBlkDevice{
    26  		GenericDevice: &GenericDevice{
    27  			ID:         devInfo.ID,
    28  			DeviceInfo: devInfo,
    29  		},
    30  	}
    31  }
    32  
    33  //
    34  // VhostUserBlkDevice's implementation of the device interface:
    35  //
    36  
    37  // Attach is standard interface of api.Device, it's used to add device to some
    38  // DeviceReceiver
    39  func (device *VhostUserBlkDevice) Attach(devReceiver api.DeviceReceiver) (err error) {
    40  	skip, err := device.bumpAttachCount(true)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	if skip {
    45  		return nil
    46  	}
    47  
    48  	// From the explanation of function attach in block.go, block index of
    49  	// a general block device is utilized for some situation.
    50  	// Since vhost-user-blk uses "vd" prefix in Linux kernel, not "sd",
    51  	// sandbox block index should be updated only if sandbox default block
    52  	// driver is "virtio-blk"/"virtio-blk-ccw"/"virtio-mmio" which uses
    53  	// "vd" prefix in Linux kernel.
    54  	index := -1
    55  	updateBlockIndex := isVirtioBlkBlockDriver(device.DeviceInfo.DriverOptions)
    56  	if updateBlockIndex {
    57  		index, err = devReceiver.GetAndSetSandboxBlockIndex()
    58  	}
    59  
    60  	defer func() {
    61  		if err != nil {
    62  			if updateBlockIndex {
    63  				devReceiver.UnsetSandboxBlockIndex(index)
    64  			}
    65  			device.bumpAttachCount(false)
    66  		}
    67  	}()
    68  
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	vAttrs := &config.VhostUserDeviceAttrs{
    74  		DevID:      utils.MakeNameID("blk", device.DeviceInfo.ID, maxDevIDSize),
    75  		SocketPath: device.DeviceInfo.HostPath,
    76  		Type:       config.VhostUserBlk,
    77  		Index:      index,
    78  	}
    79  
    80  	deviceLogger().WithFields(logrus.Fields{
    81  		"device":     device.DeviceInfo.HostPath,
    82  		"SocketPath": vAttrs.SocketPath,
    83  		"Type":       config.VhostUserBlk,
    84  		"Index":      index,
    85  	}).Info("Attaching device")
    86  
    87  	device.VhostUserDeviceAttrs = vAttrs
    88  	if err = devReceiver.HotplugAddDevice(device, config.VhostUserBlk); err != nil {
    89  		return err
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  func isVirtioBlkBlockDriver(customOptions map[string]string) bool {
    96  	var blockDriverOption string
    97  
    98  	if customOptions == nil {
    99  		// User has not chosen a specific block device type
   100  		// Default to SCSI
   101  		blockDriverOption = "virtio-scsi"
   102  	} else {
   103  		blockDriverOption = customOptions["block-driver"]
   104  	}
   105  
   106  	if blockDriverOption == "virtio-blk" ||
   107  		blockDriverOption == "virtio-blk-ccw" ||
   108  		blockDriverOption == "virtio-mmio" {
   109  		return true
   110  	}
   111  
   112  	return false
   113  }
   114  
   115  // Detach is standard interface of api.Device, it's used to remove device from some
   116  // DeviceReceiver
   117  func (device *VhostUserBlkDevice) Detach(devReceiver api.DeviceReceiver) error {
   118  	skip, err := device.bumpAttachCount(false)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	if skip {
   123  		return nil
   124  	}
   125  
   126  	defer func() {
   127  		if err != nil {
   128  			device.bumpAttachCount(true)
   129  		} else {
   130  			updateBlockIndex := isVirtioBlkBlockDriver(device.DeviceInfo.DriverOptions)
   131  			if updateBlockIndex {
   132  				devReceiver.UnsetSandboxBlockIndex(device.VhostUserDeviceAttrs.Index)
   133  			}
   134  		}
   135  	}()
   136  
   137  	deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging vhost-user-blk device")
   138  
   139  	if err = devReceiver.HotplugRemoveDevice(device, config.VhostUserBlk); err != nil {
   140  		deviceLogger().WithError(err).Error("Failed to unplug vhost-user-blk device")
   141  		return err
   142  	}
   143  	return nil
   144  }
   145  
   146  // DeviceType is standard interface of api.Device, it returns device type
   147  func (device *VhostUserBlkDevice) DeviceType() config.DeviceType {
   148  	return config.VhostUserBlk
   149  }
   150  
   151  // GetDeviceInfo returns device information used for creating
   152  func (device *VhostUserBlkDevice) GetDeviceInfo() interface{} {
   153  	return device.VhostUserDeviceAttrs
   154  }
   155  
   156  // Save converts Device to DeviceState
   157  func (device *VhostUserBlkDevice) Save() persistapi.DeviceState {
   158  	ds := device.GenericDevice.Save()
   159  	ds.Type = string(device.DeviceType())
   160  
   161  	vAttr := device.VhostUserDeviceAttrs
   162  	if vAttr != nil {
   163  		ds.VhostUserDev = &persistapi.VhostUserDeviceAttrs{
   164  			DevID:      vAttr.DevID,
   165  			SocketPath: vAttr.SocketPath,
   166  			Type:       string(vAttr.Type),
   167  			PCIPath:    vAttr.PCIPath,
   168  			Index:      vAttr.Index,
   169  		}
   170  	}
   171  	return ds
   172  }
   173  
   174  // Load loads DeviceState and converts it to specific device
   175  func (device *VhostUserBlkDevice) Load(ds persistapi.DeviceState) {
   176  	device.GenericDevice = &GenericDevice{}
   177  	device.GenericDevice.Load(ds)
   178  
   179  	dev := ds.VhostUserDev
   180  	if dev == nil {
   181  		return
   182  	}
   183  
   184  	device.VhostUserDeviceAttrs = &config.VhostUserDeviceAttrs{
   185  		DevID:      dev.DevID,
   186  		SocketPath: dev.SocketPath,
   187  		Type:       config.DeviceType(dev.Type),
   188  		PCIPath:    dev.PCIPath,
   189  		Index:      dev.Index,
   190  	}
   191  }
   192  
   193  // It should implement GetAttachCount() and DeviceID() as api.Device implementation
   194  // here it shares function from *GenericDevice so we don't need duplicate codes