@@ -18,6 +18,26 @@ type ControllerID struct {
1818 BusNumber int32
1919}
2020
21+ var (
22+ // MaxSCSISlotsByType is the maximum number of devices per SCSI controller
23+ // type.
24+ // Note: The controller itself occupies one slot.
25+ MaxSCSISlotsByType = map [vmopv1.SCSIControllerType ]int32 {
26+ vmopv1 .SCSIControllerTypeParaVirtualSCSI : 63 , // 64 targets - 1 for controller
27+ vmopv1 .SCSIControllerTypeBusLogic : 15 , // 16 targets - 1 for controller
28+ vmopv1 .SCSIControllerTypeLsiLogic : 15 , // 16 targets - 1 for controller
29+ vmopv1 .SCSIControllerTypeLsiLogicSAS : 15 , // 16 targets - 1 for controller
30+ }
31+
32+ // MaxSCSIControllersPerVM is the maximum number of SCSI controllers
33+ // allowed per VM. (4 controllers, indexed 0-3).
34+ MaxSCSIControllersPerVM = int32 (4 )
35+
36+ // SCSIControllerUnitNumber is the unit number reserved for SCSI controller
37+ // on its own bus.
38+ SCSIControllerUnitNumber = int32 (7 )
39+ )
40+
2141// CNSAttachmentNameForVolume returns the name of the CnsNodeVmAttachment based
2242// on the VM and Volume name.
2343// This matches the naming used in previous code but there are situations where
@@ -57,3 +77,64 @@ func SanitizeCNSErrorMessage(msg string) string {
5777
5878 return msg
5979}
80+
81+ // MaxSlotsForControllerType returns the maximum number of slots for a
82+ // controller type.
83+ // If the controller type is unknown, it defaults to ParaVirtual.
84+ //
85+ // PVSCSI supports 64 devices per controller starting
86+ // vSphere 6.7+. Since min supported vSphere version is >=
87+ // 8.0, this is a safe assumption.
88+ //
89+ // For all these sub-types, the max slots is 1 less than the capacity
90+ // since SCSI slot 7 is reserved for the controller itself.
91+ func MaxSlotsForControllerType (ctrlType vmopv1.SCSIControllerType ) int32 {
92+ if _ , ok := MaxSCSISlotsByType [ctrlType ]; ! ok {
93+ // Unknown controller type, default to ParaVirtual
94+ return MaxSCSISlotsByType [vmopv1 .SCSIControllerTypeParaVirtualSCSI ]
95+ }
96+
97+ return MaxSCSISlotsByType [ctrlType ]
98+ }
99+
100+ // ControllerHasAvailableSlots checks if a controller has available slots for
101+ // new devices.
102+ func ControllerHasAvailableSlots (
103+ controller * vmopv1.SCSIControllerSpec ,
104+ busNumber int32 ,
105+ statusDeviceCounts , volumeAssignments map [int32 ]int32 ,
106+ ) bool {
107+ // Get max slots for this controller type
108+ maxSlots := MaxSlotsForControllerType (controller .Type )
109+
110+ // Get current device count from status
111+ currentDevices := statusDeviceCounts [busNumber ]
112+
113+ // Get number of volumes already assigned to this controller in this mutation
114+ assignedVolumes := volumeAssignments [busNumber ]
115+
116+ // Check if adding one more volume would exceed capacity
117+ return (currentDevices + assignedVolumes + 1 ) <= maxSlots
118+ }
119+
120+ // FindControllerWithSharing finds a controller with the specified sharing mode
121+ // and has available slots.
122+ // Returns the controller if found, nil otherwise.
123+ func FindControllerWithSharing (
124+ sharingMode vmopv1.VirtualControllerSharingMode ,
125+ controllers map [int32 ]* vmopv1.SCSIControllerSpec ,
126+ statusDeviceCounts , volumeAssignments map [int32 ]int32 ,
127+ ) * vmopv1.SCSIControllerSpec {
128+
129+ for busNum := range MaxSCSIControllersPerVM {
130+ if controller , exists := controllers [busNum ]; exists {
131+ if controller .SharingMode == sharingMode &&
132+ ControllerHasAvailableSlots (controller , busNum ,
133+ statusDeviceCounts , volumeAssignments ) {
134+ return controller
135+ }
136+ }
137+ }
138+
139+ return nil
140+ }
0 commit comments