Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add workarounds to prevent filesystem corruption on Apple Virtualization VMs. #5919

Merged
merged 6 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Configuration/UTMAppleConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,13 @@ extension UTMAppleConfiguration {
}
if !ignoringDrives {
vzconfig.storageDevices = try drives.compactMap { drive in
guard let attachment = try drive.vzDiskImage() else {
guard let attachment = try drive.vzDiskImage(useFsWorkAround: system.boot.operatingSystem == .linux) else {
return nil
}
if #available(macOS 13, *), drive.isExternal {
return VZUSBMassStorageDeviceConfiguration(attachment: attachment)
} else if #available(macOS 14, *), drive.isNvme, system.boot.operatingSystem == .linux {
gnattu marked this conversation as resolved.
Show resolved Hide resolved
return VZNVMExpressControllerDeviceConfiguration(attachment: attachment)
} else {
return VZVirtioBlockDeviceConfiguration(attachment: attachment)
}
Expand Down
19 changes: 16 additions & 3 deletions Configuration/UTMAppleConfigurationDrive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
var sizeMib: Int = 0
var isReadOnly: Bool
var isExternal: Bool
var isNvme: Bool
var imageURL: URL?
var imageName: String?

Expand All @@ -36,6 +37,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {

private enum CodingKeys: String, CodingKey {
case isReadOnly = "ReadOnly"
case isNvme = "Nvme"
case imageName = "ImageName"
case bookmark = "Bookmark" // legacy only
case identifier = "Identifier"
Expand All @@ -55,12 +57,14 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
sizeMib = newSize
isReadOnly = false
isExternal = false
isNvme = false
}

init(existingURL url: URL?, isExternal: Bool = false) {
init(existingURL url: URL?, isExternal: Bool = false, isNvme: Bool = false) {
self.imageURL = url
self.isReadOnly = isExternal
self.isExternal = isExternal
self.isNvme = isNvme
}

init(from decoder: Decoder) throws {
Expand All @@ -83,6 +87,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
isExternal = true
}
isReadOnly = try container.decodeIfPresent(Bool.self, forKey: .isReadOnly) ?? isExternal
isNvme = try container.decodeIfPresent(Bool.self, forKey: .isNvme) ?? false
id = try container.decode(String.self, forKey: .identifier)
}

Expand All @@ -92,12 +97,18 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
try container.encodeIfPresent(imageName, forKey: .imageName)
}
try container.encode(isReadOnly, forKey: .isReadOnly)
try container.encode(isNvme, forKey: .isNvme)
try container.encode(id, forKey: .identifier)
}

func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? {
func vzDiskImage(useFsWorkAround: Bool = false) throws -> VZDiskImageStorageDeviceAttachment? {
if let imageURL = imageURL {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
// Use cached caching mode for virtio drive to prevent fs corruption on linux when possible
if #available(macOS 12.0, *), !isNvme, useFsWorkAround {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly, cachingMode: .cached, synchronizationMode: .full)
} else {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
}
} else {
return nil
}
Expand All @@ -107,6 +118,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
imageName?.hash(into: &hasher)
sizeMib.hash(into: &hasher)
isReadOnly.hash(into: &hasher)
isNvme.hash(into: &hasher)
isExternal.hash(into: &hasher)
id.hash(into: &hasher)
}
Expand All @@ -127,6 +139,7 @@ extension UTMAppleConfigurationDrive {
sizeMib = oldDrive.sizeMib
isReadOnly = oldDrive.isReadOnly
isExternal = oldDrive.isExternal
isNvme = false
imageURL = oldDrive.imageURL
}
}
7 changes: 6 additions & 1 deletion Platform/Shared/VMWizardState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ enum VMBootDevice: Int, Identifiable {
@Published var sharingReadOnly: Bool = false
@Published var name: String?
@Published var isOpenSettingsAfterCreation: Bool = false
@Published var useNvmeAsDiskInterface = false

/// SwiftUI BUG: on macOS 12, when VoiceOver is enabled and isBusy changes the disable state of a button being clicked,
var isNeverDisabledWorkaround: Bool {
Expand Down Expand Up @@ -342,7 +343,11 @@ enum VMBootDevice: Int, Identifiable {
}
}
if !isSkipDiskCreate {
config.drives.append(UTMAppleConfigurationDrive(newSize: storageSizeGib * bytesInGib / bytesInMib))
var newDisk = UTMAppleConfigurationDrive(newSize: storageSizeGib * bytesInGib / bytesInMib)
if #available(macOS 14, *), useNvmeAsDiskInterface {
newDisk.isNvme = true
gnattu marked this conversation as resolved.
Show resolved Hide resolved
}
config.drives.append(newDisk)
}
if #available(macOS 12, *), let sharingDirectoryURL = sharingDirectoryURL {
config.sharedDirectories = [UTMAppleConfigurationSharedDirectory(directoryURL: sharingDirectoryURL, isReadOnly: sharingReadOnly)]
Expand Down
6 changes: 6 additions & 0 deletions Platform/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1141,3 +1141,9 @@

// UTMQemuMonitor.m
"Guest panic" = "ゲストがパニック状態に陥りました";

/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "NVMe を使用します";
"If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors." = "チェックされている場合、ディスクインターフェースとして virtio の代わりに NVMe を使用します。macOS 14+ では Linux ゲストのみ利用可能です。このインターフェースは遅いですが、ファイルシステムエラーが発生する可能性が低いです。";

6 changes: 6 additions & 0 deletions Platform/macOS/VMConfigAppleDriveCreateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@ struct VMConfigAppleDriveCreateView: View {
if newValue {
config.sizeMib = 0
config.isReadOnly = true
config.isNvme = false
} else {
config.sizeMib = 10240
config.isReadOnly = false
}
}
if #available(macOS 14, *), !config.isExternal {
Toggle(isOn: $config.isNvme.animation(), label: {
Text("Use NVMe Interface")
}).help("If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors.")
}
if !config.isExternal {
SizeTextField($config.sizeMib)
}
Expand Down
6 changes: 6 additions & 0 deletions Platform/macOS/VMConfigAppleDriveDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ struct VMConfigAppleDriveDetailsView: View {
TextField("Name", text: .constant(config.imageURL?.lastPathComponent ?? NSLocalizedString("(New Drive)", comment: "VMConfigAppleDriveDetailsView")))
.disabled(true)
Toggle("Read Only?", isOn: $config.isReadOnly)
if #available(macOS 14, *), !config.isExternal {
Toggle(isOn: $config.isNvme,
label: {
Text("Use NVMe Interface")
}).help("If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors.")
}
if #unavailable(macOS 12) {
Button {
requestDriveDelete = config
Expand Down
5 changes: 5 additions & 0 deletions Platform/zh-HK.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2297,3 +2297,8 @@

/* No comment provided by engineer. */
"Zoom" = "縮放";

/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁碟介面";
"If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors." = "如果勾選,將使用 NVMe 而非 virtio 作為磁碟介面,僅在 macOS 14+ 中適用於 Linux 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";
5 changes: 5 additions & 0 deletions Platform/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2297,3 +2297,8 @@

/* No comment provided by engineer. */
"Zoom" = "缩放";

/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁盘接口";
"If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors." = "如果选中,使用 NVMe 而不是 virtio 作为磁盘接口,仅适用于 macOS 14+ 上的 Linux 客户机。此接口速度较慢,但不太容易遇到文件系统错误。";
5 changes: 5 additions & 0 deletions Platform/zh-Hant.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2002,3 +2002,8 @@
/* No comment provided by engineer. */
"Zoom" = "縮放";

/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁碟介面";
"If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors." = "如果選取,將使用 NVMe 而非 virtio 作為磁碟介面,僅在 macOS 14+ 中適用於 Linux 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";

Loading