From 2f6f547ae05c19871138e558ba6943ff07f4c68c Mon Sep 17 00:00:00 2001 From: GnoCiYeH Date: Sat, 4 Nov 2023 21:35:25 +0800 Subject: [PATCH] Patch fix sched (#419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.解决waitqueue sleep的时候,由于preempt count不为0,导致sched失败,从而导致该waitqueue下一次wakeup时,会把pcb多次加入调度队列的bug 2.修复socket inode 的read和write方法里面没有使用no_preempt的问题 3. 修复cpu0的内核栈由于脏数据导致new_idle的时候set pcb报错的问题 --------- Co-authored-by: longjin --- kernel/src/driver/net/e1000e/e1000e_driver.rs | 1 + kernel/src/lib.rs | 1 - kernel/src/libs/rwlock.rs | 52 ++++++++++++++++--- kernel/src/libs/wait_queue.rs | 23 ++++++++ kernel/src/net/socket.rs | 8 ++- kernel/src/net/syscall.rs | 16 +++--- kernel/src/process/idle.rs | 6 ++- kernel/src/process/mod.rs | 36 ++++++++++++- kernel/src/sched/core.rs | 18 +++++++ tools/.gitignore | 3 +- tools/create_hdd_image.sh | 4 +- tools/run-qemu.sh | 4 +- 12 files changed, 144 insertions(+), 28 deletions(-) diff --git a/kernel/src/driver/net/e1000e/e1000e_driver.rs b/kernel/src/driver/net/e1000e/e1000e_driver.rs index 3dd72d44..1593bab4 100644 --- a/kernel/src/driver/net/e1000e/e1000e_driver.rs +++ b/kernel/src/driver/net/e1000e/e1000e_driver.rs @@ -88,6 +88,7 @@ impl phy::TxToken for E1000ETxToken { let result = f(buffer.as_mut_slice()); let mut device = self.driver.inner.lock(); device.e1000e_transmit(buffer); + buffer.free_buffer(); return result; } } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index aefc146c..5c1f7fbc 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -130,5 +130,4 @@ pub fn panic(info: &PanicInfo) -> ! { println!("Current PCB:\n\t{:?}", *(ProcessManager::current_pcb())); ProcessManager::exit(usize::MAX); - unreachable!(); } diff --git a/kernel/src/libs/rwlock.rs b/kernel/src/libs/rwlock.rs index 4b545ca5..c1f183c6 100644 --- a/kernel/src/libs/rwlock.rs +++ b/kernel/src/libs/rwlock.rs @@ -44,6 +44,7 @@ pub struct RwLock { pub struct RwLockReadGuard<'a, T: 'a> { data: *const T, lock: &'a AtomicU32, + irq_guard: Option, } /// @brief UPGRADED是介于READER和WRITER之间的一种锁,它可以升级为WRITER, @@ -53,6 +54,7 @@ pub struct RwLockReadGuard<'a, T: 'a> { pub struct RwLockUpgradableGuard<'a, T: 'a> { data: *const T, inner: &'a RwLock, + irq_guard: Option, } /// @brief WRITER守卫的数据结构 @@ -144,6 +146,7 @@ impl RwLock { return Some(RwLockReadGuard { data: unsafe { &*self.data.get() }, lock: &self.lock, + irq_guard: None, }); } } @@ -160,6 +163,19 @@ impl RwLock { } //忙等待 } + pub fn read_irqsave(&self) -> RwLockReadGuard { + loop { + let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; + match self.try_read() { + Some(mut guard) => { + guard.irq_guard = Some(irq_guard); + return guard; + } + None => spin_loop(), + } + } + } + #[allow(dead_code)] #[inline] /// @brief 获取读者+UPGRADER的数量, 不能保证能否获得同步值 @@ -256,6 +272,7 @@ impl RwLock { return Some(RwLockUpgradableGuard { inner: self, data: unsafe { &mut *self.data.get() }, + irq_guard: None, }); } else { return None; @@ -274,6 +291,21 @@ impl RwLock { } } + #[inline] + /// @brief 获得UPGRADER守卫 + pub fn upgradeable_read_irqsave(&self) -> RwLockUpgradableGuard { + loop { + let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; + match self.try_upgradeable_read() { + Some(mut guard) => { + guard.irq_guard = Some(irq_guard); + return guard; + } + None => spin_loop(), + } + } + } + #[allow(dead_code)] #[inline] //extremely unsafe behavior @@ -332,7 +364,7 @@ impl<'rwlock, T> RwLockUpgradableGuard<'rwlock, T> { #[allow(dead_code)] #[inline] /// @brief 尝试将UPGRADER守卫升级为WRITER守卫 - pub fn try_upgrade(self) -> Result, Self> { + pub fn try_upgrade(mut self) -> Result, Self> { let res = self.inner.lock.compare_exchange( UPGRADED, WRITER, @@ -343,13 +375,13 @@ impl<'rwlock, T> RwLockUpgradableGuard<'rwlock, T> { if res.is_ok() { let inner = self.inner; - + let irq_guard = self.irq_guard.take(); mem::forget(self); Ok(RwLockWriteGuard { data: unsafe { &mut *inner.data.get() }, inner, - irq_guard: None, + irq_guard, }) } else { Err(self) @@ -373,19 +405,20 @@ impl<'rwlock, T> RwLockUpgradableGuard<'rwlock, T> { #[allow(dead_code)] #[inline] /// @brief UPGRADER降级为READER - pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T> { + pub fn downgrade(mut self) -> RwLockReadGuard<'rwlock, T> { while self.inner.current_reader().is_err() { spin_loop(); } let inner: &RwLock = self.inner; - + let irq_guard = self.irq_guard.take(); // 自动移去UPGRADED比特位 mem::drop(self); RwLockReadGuard { data: unsafe { &*inner.data.get() }, lock: &inner.lock, + irq_guard, } } @@ -426,26 +459,27 @@ impl<'rwlock, T> RwLockWriteGuard<'rwlock, T> { #[allow(dead_code)] #[inline] /// @brief 将WRITER降级为READER - pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T> { + pub fn downgrade(mut self) -> RwLockReadGuard<'rwlock, T> { while self.inner.current_reader().is_err() { spin_loop(); } //本质上来说绝对保证没有任何读者 let inner = self.inner; - + let irq_guard = self.irq_guard.take(); mem::drop(self); return RwLockReadGuard { data: unsafe { &*inner.data.get() }, lock: &inner.lock, + irq_guard, }; } #[allow(dead_code)] #[inline] /// @brief 将WRITER降级为UPGRADER - pub fn downgrade_to_upgradeable(self) -> RwLockUpgradableGuard<'rwlock, T> { + pub fn downgrade_to_upgradeable(mut self) -> RwLockUpgradableGuard<'rwlock, T> { debug_assert_eq!( self.inner.lock.load(Ordering::Acquire) & (WRITER | UPGRADED), WRITER @@ -455,11 +489,13 @@ impl<'rwlock, T> RwLockWriteGuard<'rwlock, T> { let inner = self.inner; + let irq_guard = self.irq_guard.take(); mem::forget(self); return RwLockUpgradableGuard { inner, data: unsafe { &*inner.data.get() }, + irq_guard, }; } } diff --git a/kernel/src/libs/wait_queue.rs b/kernel/src/libs/wait_queue.rs index 71376f8f..d894561a 100644 --- a/kernel/src/libs/wait_queue.rs +++ b/kernel/src/libs/wait_queue.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +use core::intrinsics::unlikely; + use alloc::{collections::LinkedList, sync::Arc, vec::Vec}; use crate::{ @@ -26,8 +28,20 @@ pub struct WaitQueue(SpinLock); impl WaitQueue { pub const INIT: WaitQueue = WaitQueue(SpinLock::new(InnerWaitQueue::INIT)); + fn before_sleep_check(&self, max_preempt: usize) { + let pcb = ProcessManager::current_pcb(); + if unlikely(pcb.preempt_count() > max_preempt) { + kwarn!( + "Process {:?}: Try to sleep when preempt count is {}", + pcb.pid(), + pcb.preempt_count() + ); + } + } + /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断 pub fn sleep(&self) { + self.before_sleep_check(0); let mut guard: SpinLockGuard = self.0.lock_irqsave(); ProcessManager::mark_sleep(true).unwrap_or_else(|e| { panic!("sleep error: {:?}", e); @@ -42,6 +56,7 @@ impl WaitQueue { where F: FnOnce(), { + self.before_sleep_check(0); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).unwrap_or_else(|e| { @@ -50,6 +65,7 @@ impl WaitQueue { drop(irq_guard); guard.wait_list.push_back(ProcessManager::current_pcb()); f(); + drop(guard); sched(); } @@ -69,6 +85,7 @@ impl WaitQueue { /// 由于sleep_without_schedule不会调用调度函数,因此,如果开发者忘记在执行本函数之后,手动调用调度函数, /// 由于时钟中断到来或者‘其他cpu kick了当前cpu’,可能会导致一些未定义的行为。 pub unsafe fn sleep_without_schedule(&self) { + self.before_sleep_check(0); // 安全检查:确保当前处于中断禁止状态 assert!(CurrentIrqArch::is_irq_enabled() == false); let mut guard: SpinLockGuard = self.0.lock(); @@ -80,6 +97,7 @@ impl WaitQueue { } pub unsafe fn sleep_without_schedule_uninterruptible(&self) { + self.before_sleep_check(0); // 安全检查:确保当前处于中断禁止状态 assert!(CurrentIrqArch::is_irq_enabled() == false); let mut guard: SpinLockGuard = self.0.lock(); @@ -91,6 +109,7 @@ impl WaitQueue { } /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断 pub fn sleep_uninterruptible(&self) { + self.before_sleep_check(0); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(false).unwrap_or_else(|e| { @@ -105,6 +124,7 @@ impl WaitQueue { /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断。 /// 在当前进程的pcb加入队列后,解锁指定的自旋锁。 pub fn sleep_unlock_spinlock(&self, to_unlock: SpinLockGuard) { + self.before_sleep_check(1); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).unwrap_or_else(|e| { @@ -120,6 +140,7 @@ impl WaitQueue { /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断。 /// 在当前进程的pcb加入队列后,解锁指定的Mutex。 pub fn sleep_unlock_mutex(&self, to_unlock: MutexGuard) { + self.before_sleep_check(1); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).unwrap_or_else(|e| { @@ -135,6 +156,7 @@ impl WaitQueue { /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断。 /// 在当前进程的pcb加入队列后,解锁指定的自旋锁。 pub fn sleep_uninterruptible_unlock_spinlock(&self, to_unlock: SpinLockGuard) { + self.before_sleep_check(1); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(false).unwrap_or_else(|e| { @@ -150,6 +172,7 @@ impl WaitQueue { /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断。 /// 在当前进程的pcb加入队列后,解锁指定的Mutex。 pub fn sleep_uninterruptible_unlock_mutex(&self, to_unlock: MutexGuard) { + self.before_sleep_check(1); let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(false).unwrap_or_else(|e| { diff --git a/kernel/src/net/socket.rs b/kernel/src/net/socket.rs index 31a10fea..3fd90314 100644 --- a/kernel/src/net/socket.rs +++ b/kernel/src/net/socket.rs @@ -1144,6 +1144,10 @@ impl SocketInode { pub fn inner(&self) -> SpinLockGuard> { return self.0.lock(); } + + pub unsafe fn inner_no_preempt(&self) -> SpinLockGuard> { + return self.0.lock_no_preempt(); + } } impl IndexNode for SocketInode { @@ -1173,7 +1177,7 @@ impl IndexNode for SocketInode { buf: &mut [u8], _data: &mut crate::filesystem::vfs::FilePrivateData, ) -> Result { - return self.0.lock().read(&mut buf[0..len]).0; + return self.0.lock_no_preempt().read(&mut buf[0..len]).0; } fn write_at( @@ -1183,7 +1187,7 @@ impl IndexNode for SocketInode { buf: &[u8], _data: &mut crate::filesystem::vfs::FilePrivateData, ) -> Result { - return self.0.lock().write(&buf[0..len], None); + return self.0.lock_no_preempt().write(&buf[0..len], None); } fn poll(&self) -> Result { diff --git a/kernel/src/net/syscall.rs b/kernel/src/net/syscall.rs index 36ec8f01..4b267339 100644 --- a/kernel/src/net/syscall.rs +++ b/kernel/src/net/syscall.rs @@ -173,7 +173,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let mut socket = socket.inner(); + let mut socket = unsafe { socket.inner_no_preempt() }; // kdebug!("connect to {:?}...", endpoint); socket.connect(endpoint)?; return Ok(0); @@ -191,7 +191,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let mut socket = socket.inner(); + let mut socket = unsafe { socket.inner_no_preempt() }; socket.bind(endpoint)?; return Ok(0); } @@ -221,7 +221,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let socket = socket.inner(); + let socket = unsafe { socket.inner_no_preempt() }; return socket.write(buf, endpoint); } @@ -244,7 +244,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let socket = socket.inner(); + let socket = unsafe { socket.inner_no_preempt() }; let (n, endpoint) = socket.read(buf); drop(socket); @@ -275,7 +275,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let socket = socket.inner(); + let socket = unsafe { socket.inner_no_preempt() }; let mut buf = iovs.new_buf(true); // 从socket中读取数据 @@ -304,7 +304,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let mut socket = socket.inner(); + let mut socket = unsafe { socket.inner_no_preempt() }; socket.listen(backlog)?; return Ok(0); } @@ -319,7 +319,7 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - let socket = socket.inner(); + let socket = unsafe { socket.inner_no_preempt() }; socket.shutdown(ShutdownType::try_from(how as i32)?)?; return Ok(0); } @@ -336,7 +336,7 @@ impl Syscall { .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; // kdebug!("accept: socket={:?}", socket); - let mut socket = socket.inner(); + let mut socket = unsafe { socket.inner_no_preempt() }; // 从socket中接收连接 let (new_socket, remote_endpoint) = socket.accept()?; drop(socket); diff --git a/kernel/src/process/idle.rs b/kernel/src/process/idle.rs index 89fa1c48..70aec18a 100644 --- a/kernel/src/process/idle.rs +++ b/kernel/src/process/idle.rs @@ -37,8 +37,10 @@ impl ProcessManager { let stack_ptr = VirtAddr::new(Self::stack_ptr().data() & (!(KernelStack::ALIGN - 1))); // 初始化bsp的idle进程 - unsafe { KernelStack::from_existed(stack_ptr) } - .expect("Failed to create kernel stack struct for BSP.") + let mut ks = unsafe { KernelStack::from_existed(stack_ptr) } + .expect("Failed to create kernel stack struct for BSP."); + unsafe { ks.clear_pcb(true) }; + ks } else { KernelStack::new().unwrap_or_else(|e| { panic!("Failed to create kernel stack struct for AP {}: {:?}", i, e) diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 04ec9042..b3bdeddf 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -34,7 +34,7 @@ use crate::{ constant::{FutexFlag, FUTEX_BITSET_MATCH_ANY}, futex::Futex, }, - rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + rwlock::{RwLock, RwLockReadGuard, RwLockUpgradableGuard, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, wait_queue::WaitQueue, }, @@ -235,6 +235,7 @@ impl ProcessManager { /// /// - 进入当前函数之前,不能持有sched_info的锁 /// - 进入当前函数之前,必须关闭中断 + /// - 进入当前函数之后必须保证逻辑的正确性,避免被重复加入调度队列 pub fn mark_sleep(interruptable: bool) -> Result<(), SystemError> { assert_eq!( CurrentIrqArch::is_irq_enabled(), @@ -692,6 +693,17 @@ impl ProcessControlBlock { return self.sched_info.read(); } + #[allow(dead_code)] + #[inline(always)] + pub fn sched_info_irqsave(&self) -> RwLockReadGuard { + return self.sched_info.read_irqsave(); + } + + #[inline(always)] + pub fn sched_info_upgradeable_irqsave(&self) -> RwLockUpgradableGuard { + return self.sched_info.upgradeable_read(); + } + #[inline(always)] pub fn sched_info_mut(&self) -> RwLockWriteGuard { return self.sched_info.write(); @@ -977,7 +989,7 @@ impl ProcessSchedulerInfo { return self.state; } - fn set_state(&mut self, state: ProcessState) { + pub fn set_state(&mut self, state: ProcessState) { self.state = state; } @@ -1069,6 +1081,7 @@ impl KernelStack { // 如果内核栈的最低地址处已经有了一个pcb,那么,这里就不再设置,直接返回错误 if unlikely(unsafe { !(*stack_bottom_ptr).is_null() }) { + kerror!("kernel stack bottom is not null: {:p}", *stack_bottom_ptr); return Err(SystemError::EPERM); } // 将pcb的地址放到内核栈的最低地址处 @@ -1079,6 +1092,25 @@ impl KernelStack { return Ok(()); } + /// 清除内核栈的pcb指针 + /// + /// ## 参数 + /// + /// - `force` : 如果为true,那么,即使该内核栈的pcb指针不为null,也会被强制清除而不处理Weak指针问题 + pub unsafe fn clear_pcb(&mut self, force: bool) { + let stack_bottom_ptr = self.start_address().data() as *mut *const ProcessControlBlock; + if unlikely(unsafe { (*stack_bottom_ptr).is_null() }) { + return; + } + + if !force { + let pcb_ptr: Weak = Weak::from_raw(*stack_bottom_ptr); + drop(pcb_ptr); + } + + *stack_bottom_ptr = core::ptr::null(); + } + /// 返回指向当前内核栈pcb的Arc指针 #[allow(dead_code)] pub unsafe fn pcb(&self) -> Option> { diff --git a/kernel/src/sched/core.rs b/kernel/src/sched/core.rs index 56554d60..ff58ab67 100644 --- a/kernel/src/sched/core.rs +++ b/kernel/src/sched/core.rs @@ -98,6 +98,24 @@ pub trait Scheduler { pub fn do_sched() -> Option> { // 当前进程持有锁,不切换,避免死锁 if ProcessManager::current_pcb().preempt_count() != 0 { + let binding = ProcessManager::current_pcb(); + let mut guard = binding.sched_info_upgradeable_irqsave(); + let state = guard.state(); + if state.is_blocked() { + // try to upgrade + for _ in 0..50 { + match guard.try_upgrade() { + Ok(mut writer) => { + // 被mark_sleep但是还在临界区的进程将其设置为Runnable + writer.set_state(ProcessState::Runnable); + break; + } + Err(s) => { + guard = s; + } + } + } + } return None; } compiler_fence(core::sync::atomic::Ordering::SeqCst); diff --git a/tools/.gitignore b/tools/.gitignore index aea86584..832dff97 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -5,4 +5,5 @@ arch/i386/efi/grub/* arch/x86_64/efi/grub/* *.tar.gz -build/* \ No newline at end of file +build/* +target/ \ No newline at end of file diff --git a/tools/create_hdd_image.sh b/tools/create_hdd_image.sh index 111c851f..0d7263d9 100755 --- a/tools/create_hdd_image.sh +++ b/tools/create_hdd_image.sh @@ -48,8 +48,8 @@ EOF echo "Creating virtual disk image..." ARGS=`getopt -o P: -- "$@"` -# 创建一至少为64MB磁盘镜像(类型选择raw) -qemu-img create -f raw disk.img 64M +# 创建一至少为256MB磁盘镜像(类型选择raw) +qemu-img create -f raw disk.img 256M #将规范化后的命令行参数分配至位置参数($1,$2,...) eval set -- "${ARGS}" #echo formatted parameters=[$@] diff --git a/tools/run-qemu.sh b/tools/run-qemu.sh index 4c45e4fd..8536b735 100644 --- a/tools/run-qemu.sh +++ b/tools/run-qemu.sh @@ -62,9 +62,9 @@ QEMU_DRIVE="id=disk,file=${QEMU_DISK_IMAGE},if=none" # ps: 下面这条使用tap的方式,无法dhcp获取到ip,暂时不知道为什么 # QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -net nic,netdev=nic0 -netdev tap,id=nic0,model=virtio-net-pci,script=qemu/ifup-nat,downscript=qemu/ifdown-nat -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine accel=${qemu_accel} -machine q35 " -# QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -device virtio-net-pci,vectors=5,netdev=hostnet0,id=net0 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine accel=${qemu_accel} -machine q35 " +QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -device virtio-net-pci,vectors=5,netdev=hostnet0,id=net0 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine accel=${qemu_accel} -machine q35 " # E1000E -QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -net nic,model=e1000e,netdev=hostnet0,id=net0 -netdev user,id=hostnet1,hostfwd=tcp::12581-:12581 -device virtio-net-pci,vectors=5,netdev=hostnet1,id=net1 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine accel=${qemu_accel} -machine q35 " +#QEMU_DEVICES="-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -netdev user,id=hostnet0,hostfwd=tcp::12580-:12580 -net nic,model=e1000e,netdev=hostnet0,id=net0 -netdev user,id=hostnet1,hostfwd=tcp::12581-:12581 -device virtio-net-pci,vectors=5,netdev=hostnet1,id=net1 -usb -device qemu-xhci,id=xhci,p2=8,p3=4 -machine accel=${qemu_accel} -machine q35 " QEMU_ARGUMENT="-d ${QEMU_DISK_IMAGE} -m ${QEMU_MEMORY} -smp ${QEMU_SMP} -boot order=d -monitor ${QEMU_MONITOR} -d ${qemu_trace_std} " QEMU_ARGUMENT+="-s -S -enable-kvm -cpu ${QEMU_CPU_FEATURES} -rtc ${QEMU_RTC_CLOCK} -serial ${QEMU_SERIAL} -drive ${QEMU_DRIVE} ${QEMU_DEVICES}"