From 448bb661d6759c1e20c18084604b150bff08ada4 Mon Sep 17 00:00:00 2001
From: holyfei <yangfeiyu20092010@163.com>
Date: Tue, 18 Aug 2020 11:49:26 +0800
Subject: [PATCH 36/50] mount: limit the maximum number of virtio-scsi bus
 slots

reason:
1. add SCSIBus functions for add or remove device about ScsiBus
2. limit the maximum number of virtio-scsi bus slots to 25

Signed-off-by: yangfeiyu <yangfeiyu2@huawei.com>
---
 virtcontainers/qemu.go       | 20 +++++++++++++++++
 virtcontainers/types/scsi.go | 53 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)
 create mode 100644 virtcontainers/types/scsi.go

diff --git a/virtcontainers/qemu.go b/virtcontainers/qemu.go
index bb83b1bb..c2b65376 100644
--- a/virtcontainers/qemu.go
+++ b/virtcontainers/qemu.go
@@ -65,6 +65,7 @@ type CPUDevice struct {
 // QemuState keeps Qemu's state
 type QemuState struct {
 	Bridges []types.Bridge
+	ScsiBus *types.SCSIBus
 	// HotpluggedCPUs is the list of CPUs that were hot-added
 	HotpluggedVCPUs      []CPUDevice
 	HotpluggedMemory     int
@@ -411,6 +412,13 @@ func (q *qemu) buildDevices(initrdPath string) ([]govmmQemu.Device, *govmmQemu.I
 
 	var ioThread *govmmQemu.IOThread
 	if q.config.BlockDeviceDriver == config.VirtioSCSI {
+		if q.state.ScsiBus == nil {
+			// only use `scsi0.0`
+			q.state.ScsiBus = &types.SCSIBus{
+				Address: make(map[uint32]int),
+				ID:      fmt.Sprintf("%s.0", scsiControllerID),
+			}
+		}
 		return q.arch.appendSCSIController(devices, q.config.EnableIOThreads)
 	}
 
@@ -1168,6 +1176,16 @@ func (q *qemu) hotplugAddBlockDevice(drive *config.BlockDrive, op operation, dev
 
 		// Bus exposed by the SCSI Controller
 		bus := scsiControllerID + ".0"
+		var err error
+		if err = q.state.ScsiBus.AddDevToScsiBus(drive.Index); err != nil {
+			return err
+		}
+
+		defer func() {
+			if err != nil {
+				q.state.ScsiBus.RemoveDevFromScsiBus(drive.Index)
+			}
+		}()
 
 		// Get SCSI-id and LUN based on the order of attaching drives.
 		scsiID, lun, err := utils.GetSCSIIdLun(drive.Index)
@@ -1234,6 +1252,8 @@ func (q *qemu) hotplugBlockDevice(drive *config.BlockDrive, op operation) error
 			if err := q.arch.removeDeviceFromBridge(drive.ID); err != nil {
 				return err
 			}
+		} else if q.config.BlockDeviceDriver == config.VirtioSCSI {
+			q.state.ScsiBus.RemoveDevFromScsiBus(drive.Index)
 		}
 
 		if err := q.qmpMonitorCh.qmp.ExecuteDeviceDel(q.qmpMonitorCh.ctx, devID); err != nil {
diff --git a/virtcontainers/types/scsi.go b/virtcontainers/types/scsi.go
new file mode 100644
index 00000000..f4d489f0
--- /dev/null
+++ b/virtcontainers/types/scsi.go
@@ -0,0 +1,53 @@
+// Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+// Description: common functions
+// Author: leizhongkai
+// Create: 2019-06-30
+
+package types
+
+import (
+	"fmt"
+)
+
+const (
+	scsiMaxCapacity = 25
+)
+
+type SCSIBus struct {
+	// Address contains information about devices plugged and number limit
+	Address map[uint32]int
+
+	// ID is used to identify the bus exposed by the SCSI Controller
+	ID string
+}
+
+func (sb *SCSIBus) AddDevToScsiBus(index int) error {
+	var addr uint32
+
+	// looking for the first available address
+	for i := uint32(1); i <= scsiMaxCapacity; i++ {
+		if _, ok := sb.Address[i]; !ok {
+			addr = i
+			break
+		}
+	}
+
+	if addr == 0 {
+		return fmt.Errorf("Scsi bus capacity limited.")
+	}
+
+	// save address and device
+	sb.Address[addr] = index
+
+	return nil
+}
+
+func (sb *SCSIBus) RemoveDevFromScsiBus(index int) {
+	for addr, i := range sb.Address {
+		if i == index {
+			delete(sb.Address, addr)
+			return
+		}
+	}
+}
-- 
2.14.3 (Apple Git-98)

