github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/device/drivers/block.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  	"path/filepath"
    11  
    12  	"github.com/kata-containers/runtime/virtcontainers/device/api"
    13  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    14  	persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
    15  	"github.com/kata-containers/runtime/virtcontainers/utils"
    16  )
    17  
    18  const maxDevIDSize = 31
    19  
    20  // BlockDevice refers to a block storage device implementation.
    21  type BlockDevice struct {
    22  	*GenericDevice
    23  	BlockDrive *config.BlockDrive
    24  }
    25  
    26  // NewBlockDevice creates a new block device based on DeviceInfo
    27  func NewBlockDevice(devInfo *config.DeviceInfo) *BlockDevice {
    28  	return &BlockDevice{
    29  		GenericDevice: &GenericDevice{
    30  			ID:         devInfo.ID,
    31  			DeviceInfo: devInfo,
    32  		},
    33  	}
    34  }
    35  
    36  // Attach is standard interface of api.Device, it's used to add device to some
    37  // DeviceReceiver
    38  func (device *BlockDevice) Attach(devReceiver api.DeviceReceiver) (err error) {
    39  	skip, err := device.bumpAttachCount(true)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	if skip {
    44  		return nil
    45  	}
    46  
    47  	// Increment the block index for the sandbox. This is used to determine the name
    48  	// for the block device in the case where the block device is used as container
    49  	// rootfs and the predicted block device name needs to be provided to the agent.
    50  	index, err := devReceiver.GetAndSetSandboxBlockIndex()
    51  
    52  	defer func() {
    53  		if err != nil {
    54  			devReceiver.UnsetSandboxBlockIndex(index)
    55  			device.bumpAttachCount(false)
    56  		}
    57  	}()
    58  
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	drive := &config.BlockDrive{
    64  		File:     device.DeviceInfo.HostPath,
    65  		Format:   "raw",
    66  		ID:       utils.MakeNameID("drive", device.DeviceInfo.ID, maxDevIDSize),
    67  		Index:    index,
    68  		Pmem:     device.DeviceInfo.Pmem,
    69  		ReadOnly: device.DeviceInfo.ReadOnly,
    70  	}
    71  
    72  	if fs, ok := device.DeviceInfo.DriverOptions["fstype"]; ok {
    73  		drive.Format = fs
    74  	}
    75  
    76  	customOptions := device.DeviceInfo.DriverOptions
    77  	if customOptions == nil ||
    78  		customOptions["block-driver"] == "virtio-scsi" {
    79  		// User has not chosen a specific block device type
    80  		// Default to SCSI
    81  		scsiAddr, err := utils.GetSCSIAddress(index)
    82  		if err != nil {
    83  			return err
    84  		}
    85  
    86  		drive.SCSIAddr = scsiAddr
    87  	} else if customOptions["block-driver"] != "nvdimm" {
    88  		// When determine a drive name the block index needs to be incremented by
    89  		// an offset. The sandbox may have pre-allocated virtio-block drives that
    90  		// are not part of the sandbox BlockIndexMap.
    91  		globalIdx := index + devReceiver.GetSandboxBlockOffset()
    92  
    93  		driveName, err := utils.GetVirtDriveName(globalIdx)
    94  		if err != nil {
    95  			return err
    96  		}
    97  
    98  		drive.VirtPath = filepath.Join("/dev", driveName)
    99  	}
   100  
   101  	deviceLogger().WithField("device", device.DeviceInfo.HostPath).WithField("VirtPath", drive.VirtPath).Infof("Attaching %s device", customOptions["block-driver"])
   102  	device.BlockDrive = drive
   103  	if err = devReceiver.HotplugAddDevice(device, config.DeviceBlock); err != nil {
   104  		return err
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  // Detach is standard interface of api.Device, it's used to remove device from some
   111  // DeviceReceiver
   112  func (device *BlockDevice) Detach(devReceiver api.DeviceReceiver) error {
   113  	skip, err := device.bumpAttachCount(false)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	if skip {
   118  		return nil
   119  	}
   120  
   121  	defer func() {
   122  		if err != nil {
   123  			device.bumpAttachCount(true)
   124  		} else {
   125  			devReceiver.UnsetSandboxBlockIndex(device.BlockDrive.Index)
   126  		}
   127  	}()
   128  
   129  	deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging block device")
   130  
   131  	if err = devReceiver.HotplugRemoveDevice(device, config.DeviceBlock); err != nil {
   132  		deviceLogger().WithError(err).Error("Failed to unplug block device")
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  
   138  // DeviceType is standard interface of api.Device, it returns device type
   139  func (device *BlockDevice) DeviceType() config.DeviceType {
   140  	return config.DeviceBlock
   141  }
   142  
   143  // GetDeviceInfo returns device information used for creating
   144  func (device *BlockDevice) GetDeviceInfo() interface{} {
   145  	return device.BlockDrive
   146  }
   147  
   148  // Save converts Device to DeviceState
   149  func (device *BlockDevice) Save() persistapi.DeviceState {
   150  	ds := device.GenericDevice.Save()
   151  	ds.Type = string(device.DeviceType())
   152  
   153  	drive := device.BlockDrive
   154  	if drive != nil {
   155  		ds.BlockDrive = &persistapi.BlockDrive{
   156  			File:     drive.File,
   157  			Format:   drive.Format,
   158  			ID:       drive.ID,
   159  			Index:    drive.Index,
   160  			MmioAddr: drive.MmioAddr,
   161  			PCIPath:  drive.PCIPath,
   162  			SCSIAddr: drive.SCSIAddr,
   163  			NvdimmID: drive.NvdimmID,
   164  			VirtPath: drive.VirtPath,
   165  			DevNo:    drive.DevNo,
   166  			Pmem:     drive.Pmem,
   167  		}
   168  	}
   169  	return ds
   170  }
   171  
   172  // Load loads DeviceState and converts it to specific device
   173  func (device *BlockDevice) Load(ds persistapi.DeviceState) {
   174  	device.GenericDevice = &GenericDevice{}
   175  	device.GenericDevice.Load(ds)
   176  
   177  	bd := ds.BlockDrive
   178  	if bd == nil {
   179  		return
   180  	}
   181  	device.BlockDrive = &config.BlockDrive{
   182  		File:     bd.File,
   183  		Format:   bd.Format,
   184  		ID:       bd.ID,
   185  		Index:    bd.Index,
   186  		MmioAddr: bd.MmioAddr,
   187  		PCIPath:  bd.PCIPath,
   188  		SCSIAddr: bd.SCSIAddr,
   189  		NvdimmID: bd.NvdimmID,
   190  		VirtPath: bd.VirtPath,
   191  		DevNo:    bd.DevNo,
   192  		Pmem:     bd.Pmem,
   193  	}
   194  }
   195  
   196  // It should implement GetAttachCount() and DeviceID() as api.Device implementation
   197  // here it shares function from *GenericDevice so we don't need duplicate codes