diff --git a/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h b/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h index 6a2989cff7..7c4858a55b 100644 --- a/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h +++ b/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h @@ -81,6 +81,7 @@ public: // manager must be locked status_t AddJobQueue(KDiskDeviceJobQueue *jobQueue); + // the device must be write locked status_t RemoveJobQueue(KDiskDeviceJobQueue *jobQueue); status_t DeleteJobQueue(KDiskDeviceJobQueue *jobQueue); // called when the execution is done @@ -89,6 +90,11 @@ public: KDiskDeviceJobFactory *JobFactory() const; + // manager must *not* be locked + status_t UpdateBusyPartitions(KDiskDevice *device); + status_t UpdateJobStatus(KDiskDeviceJob *job, uint32 status, + bool updateBusyPartitions); + // Disk Systems // manager must be locked @@ -118,8 +124,13 @@ private: bool _RemoveJobQueue(KDiskDeviceJobQueue *jobQueue); + status_t _UpdateBusyPartitions(KDiskDevice *device); + status_t _UpdateJobStatus(KDiskDeviceJob *job, uint32 status, + bool updateBusyPartitions); + status_t _Scan(const char *path); status_t _ScanPartition(KPartition *partition); + // the manager must be locked and the device write locked struct DeviceMap; struct DiskSystemMap; diff --git a/src/kernel/core/disk_device_manager/KDiskDeviceJobQueue.cpp b/src/kernel/core/disk_device_manager/KDiskDeviceJobQueue.cpp index a0ca345019..da810f40f2 100644 --- a/src/kernel/core/disk_device_manager/KDiskDeviceJobQueue.cpp +++ b/src/kernel/core/disk_device_manager/KDiskDeviceJobQueue.cpp @@ -187,9 +187,8 @@ KDiskDeviceJobQueue::Execute() fSyncSemaphore = -1; return fThread; } + fFlags |= JOB_QUEUE_EXECUTING; resume_thread(fThread); - // wait till the thread has resumed its work - acquire_sem(fSyncSemaphore); return B_OK; } @@ -301,45 +300,45 @@ KDiskDeviceJobQueue::IsPauseRequested() const int32 KDiskDeviceJobQueue::_ThreadLoop() { -// TODO: notifications - // mark all jobs scheduled - for (int32 i = 0; KDiskDeviceJob *job = JobAt(i); i++) - job->SetStatus(B_DISK_DEVICE_JOB_SCHEDULED); - // mark the queue executing and notify the thread waiting in Execute() - fFlags |= JOB_QUEUE_EXECUTING; - release_sem(fSyncSemaphore); KDiskDeviceManager *manager = KDiskDeviceManager::Default(); // main loop: process all jobs + bool failed = false; while (KDiskDeviceJob *activeJob = ActiveJob()) { - activeJob->SetStatus(B_DISK_DEVICE_JOB_IN_PROGRESS); + manager->UpdateJobStatus(activeJob, B_DISK_DEVICE_JOB_IN_PROGRESS, + true); status_t error = activeJob->Do(); if (error == B_OK) { if (ManagerLocker locker = manager) { - // if canceled, mark this and all succeeding jobs canceled - if (IsCanceled()) { - for (int32 i = fActiveJob; KDiskDeviceJob *job = JobAt(i); - i++) { - job->SetStatus(B_DISK_DEVICE_JOB_CANCELED); - } + // if canceled, simply bail out -- this and all succeeding + // jobs will be canceled + if (IsCanceled()) break; - } // if pause was requested ignore the request if (IsPauseRequested()) Continue(); - // mark the job succeeded and go to the next one - activeJob->SetStatus(B_DISK_DEVICE_JOB_SUCCEEDED); + // go to the next job fActiveJob++; } else break; + // mark the job succeeded + manager->UpdateJobStatus(activeJob, B_DISK_DEVICE_JOB_SUCCEEDED, + true); } else { - // job failed: mark this and all succeeding jobs failed - if (ManagerLocker locker = manager) { - for (int32 i = fActiveJob; KDiskDeviceJob *job = JobAt(i); i++) - job->SetStatus(B_DISK_DEVICE_JOB_FAILED); - } + // job failed: this and all succeeding jobs will be marked failed + failed = true; break; } } + // All jobs that remain need to be marked failed or canceled. + bool updatePartitions = false; + for (int32 i = fActiveJob; KDiskDeviceJob *job = JobAt(i); i++) { + manager->UpdateJobStatus(job, (failed ? B_DISK_DEVICE_JOB_FAILED + : B_DISK_DEVICE_JOB_CANCELED), + false); + updatePartitions = true; + } + if (updatePartitions) + manager->UpdateBusyPartitions(Device()); // tell the manager, that we are done. if (ManagerLocker locker = manager) { fFlags &= ~(uint32)JOB_QUEUE_EXECUTING; diff --git a/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp b/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp index 8b941180fc..8a10e89923 100644 --- a/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp +++ b/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp @@ -22,6 +22,8 @@ #include "KFileSystem.h" #include "KPartition.h" #include "KPartitioningSystem.h" +#include "KPartitionVisitor.h" +#include "KShadowPartition.h" // debugging //#define DBG(x) @@ -36,6 +38,15 @@ using BPrivate::DiskDevice::KDiskDeviceJobQueue; static const char *kPartitioningSystemPrefix = "partitioning_systems"; static const char *kFileSystemPrefix = "file_systems"; +// is_active_job_status +static +bool +is_active_job_status(uint32 status) +{ + return (status == B_DISK_DEVICE_JOB_SCHEDULED + || status == B_DISK_DEVICE_JOB_IN_PROGRESS); +} + // GetPartitionID struct GetPartitionID { inline partition_id operator()(const KPartition *partition) const @@ -494,6 +505,11 @@ KDiskDeviceManager::CreateFileDevice(const char *filePath) return B_NO_MEMORY; // initialize and add the device error = device->SetTo(filePath); + // Note: Here we are allowed to lock a device although already having + // the manager locked, since it is not yet added to the manager. + DeviceWriteLocker deviceLocker(device); + if (error == B_OK && !deviceLocker.IsLocked()) + error = B_ERROR; if (error == B_OK && !_AddDevice(device)) error = B_NO_MEMORY; // scan device @@ -650,11 +666,20 @@ KDiskDeviceManager::AddJobQueue(KDiskDeviceJobQueue *jobQueue) return error; } } - // TODO: mark the concerned partitions busy /descendant busy - // start its execution + // mark the jobs scheduled + for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) + _UpdateJobStatus(job, B_DISK_DEVICE_JOB_SCHEDULED, false); + _UpdateBusyPartitions(jobQueue->Device()); + // start the execution of the queue error = jobQueue->Execute(); - if (error != B_OK) + if (error != B_OK) { + // resuming the execution failed -- mark all jobs failed and + // remove the queue + for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) + _UpdateJobStatus(job, B_DISK_DEVICE_JOB_FAILED, false); _RemoveJobQueue(jobQueue); + _UpdateBusyPartitions(jobQueue->Device()); + } return error; } @@ -704,6 +729,39 @@ KDiskDeviceManager::JobFactory() const return fJobFactory; } +// UpdateBusyPartitions +status_t +KDiskDeviceManager::UpdateBusyPartitions(KDiskDevice *device) +{ + if (!device) + return B_BAD_VALUE; + if (DeviceWriteLocker deviceLocker = device) { + if (ManagerLocker locker = this) + return _UpdateBusyPartitions(device); + } + return B_ERROR; +} + +// UpdateJobStatus +status_t +KDiskDeviceManager::UpdateJobStatus(KDiskDeviceJob *job, uint32 status, + bool updateBusyPartitions) +{ + // check parameters + if (!job) + return B_BAD_VALUE; + KDiskDeviceJobQueue *jobQueue = job->JobQueue(); + KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); + if (!device) + return B_BAD_VALUE; + // lock device and manager + if (DeviceWriteLocker deviceLocker = device) { + if (ManagerLocker locker = this) + return _UpdateJobStatus(job, status, updateBusyPartitions); + } + return B_ERROR; +} + // FindDiskSystem KDiskSystem * KDiskDeviceManager::FindDiskSystem(const char *name) @@ -801,13 +859,20 @@ KDiskDeviceManager::InitialDeviceScan() error = _Scan("/dev/disk"); if (error != B_OK) return error; - // scan the devices for partitions - int32 cookie = 0; - while (KDiskDevice *device = NextDevice(&cookie)) { - error = _ScanPartition(device); - if (error != B_OK) - break; - } + } + // scan the devices for partitions + int32 cookie = 0; + while (KDiskDevice *device = RegisterNextDevice(&cookie)) { + PartitionRegistrar _(device, true); + if (DeviceWriteLocker deviceLocker = device) { + if (ManagerLocker locker = this) { + error = _ScanPartition(device); + if (error != B_OK) + break; + } else + return B_ERROR; + } else + return B_ERROR; } return error; } @@ -893,6 +958,88 @@ KDiskDeviceManager::_RemoveJobQueue(KDiskDeviceJobQueue *jobQueue) return true; } +// _UpdateBusyPartitions +status_t +KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device) +{ + if (!device) + return B_BAD_VALUE; + // mark all partitions un-busy + struct UnmarkBusyVisitor : KPartitionVisitor { + virtual bool VisitPre(KPartition *partition) + { + partition->ClearFlags(B_PARTITION_BUSY + | B_PARTITION_DESCENDANT_BUSY); + return false; + } + } visitor; + device->VisitEachDescendant(&visitor); + if (device->ShadowPartition()) + device->ShadowPartition()->VisitEachDescendant(&visitor); + // Iterate through all job queues and all jobs scheduled or in + // progress and mark their scope busy. + for (int32 cookie = 0; + KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) { + if (jobQueue->Device() != device) + continue; + for (int32 i = jobQueue->ActiveJobIndex(); + KDiskDeviceJob *job = jobQueue->JobAt(i); i++) { + if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS + && job->Status() == B_DISK_DEVICE_JOB_SCHEDULED) { + continue; + } + KPartition *partition = FindPartition(job->ScopeID()); + if (!partition || partition->Device() != device) + continue; + partition->AddFlags(B_PARTITION_BUSY); + if (KPartition *shadow = partition->ShadowPartition()) + shadow->AddFlags(B_PARTITION_BUSY); + } + } + // mark all anscestors of busy partitions descendant busy + struct MarkBusyVisitor : KPartitionVisitor { + virtual bool VisitPost(KPartition *partition) + { + if ((partition->IsBusy() || partition->IsDescendantBusy()) + && partition->Parent()) { + partition->Parent()->AddFlags( + B_PARTITION_DESCENDANT_BUSY); + } + return false; + } + } visitor2; + device->VisitEachDescendant(&visitor2); + if (device->ShadowPartition()) + device->ShadowPartition()->VisitEachDescendant(&visitor2); + return B_OK; +} + +// _UpdateJobStatus +status_t +KDiskDeviceManager::_UpdateJobStatus(KDiskDeviceJob *job, uint32 status, + bool updateBusyPartitions) +{ + // check parameters + if (!job) + return B_BAD_VALUE; + KDiskDeviceJobQueue *jobQueue = job->JobQueue(); + KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); + if (!device) + return B_BAD_VALUE; + if (job->Status() == status) + return B_OK; + // check migration of a schedule/in progress to a terminal state + // or vice versa + updateBusyPartitions &= (is_active_job_status(job->Status()) + != is_active_job_status(status)); + // set new state and update the partitions' busy flags + job->SetStatus(status); + if (updateBusyPartitions) + return _UpdateBusyPartitions(device); + // TODO: notifications + return B_OK; +} + // _Scan status_t KDiskDeviceManager::_Scan(const char *path)