Implement node monitoring in the kernel disk device manager.
* Added {Create|Delete}Device() analogous to {Create|Delete}FileDevice
* Added a small DeviceWatcher class that reacts to entry creation/removal
* Implemented a way to start/stop node monitoring
* Start watching for devices after the boot volume has been mounted and the
the second initial scan was run
The disk device manager now creates and scans a device when a "raw" node is
published and deletes the device on removal. This makes hot-plugging of disk
devices (for example memory sticks using usb_disk) work. Their partitions will
be scanned and published so they can be mounted. Somehow the removal of the
partitions does not yet work however, any insights are welcome.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24777 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -65,6 +65,9 @@ public:
|
|||||||
|
|
||||||
status_t ScanPartition(KPartition* partition);
|
status_t ScanPartition(KPartition* partition);
|
||||||
|
|
||||||
|
partition_id CreateDevice(const char *path, bool *newlyCreated = NULL);
|
||||||
|
status_t DeleteDevice(const char *path);
|
||||||
|
|
||||||
partition_id CreateFileDevice(const char* filePath,
|
partition_id CreateFileDevice(const char* filePath,
|
||||||
bool* newlyCreated = NULL);
|
bool* newlyCreated = NULL);
|
||||||
status_t DeleteFileDevice(const char *filePath);
|
status_t DeleteFileDevice(const char *filePath);
|
||||||
@@ -90,13 +93,9 @@ public:
|
|||||||
KDiskSystem *LoadDiskSystem(disk_system_id id);
|
KDiskSystem *LoadDiskSystem(disk_system_id id);
|
||||||
KDiskSystem *LoadNextDiskSystem(int32 *cookie);
|
KDiskSystem *LoadNextDiskSystem(int32 *cookie);
|
||||||
|
|
||||||
// Watching
|
|
||||||
|
|
||||||
// TODO: Watching service for the kernel. The userland watching is handled
|
|
||||||
// by the registrar.
|
|
||||||
|
|
||||||
status_t InitialDeviceScan();
|
status_t InitialDeviceScan();
|
||||||
status_t RescanDiskSystems();
|
status_t RescanDiskSystems();
|
||||||
|
status_t StartMonitoring();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static status_t _CheckMediaStatusDaemon(void* self);
|
static status_t _CheckMediaStatusDaemon(void* self);
|
||||||
@@ -117,10 +116,13 @@ private:
|
|||||||
status_t _ScanPartition(KPartition *partition);
|
status_t _ScanPartition(KPartition *partition);
|
||||||
// used by the other _ScanPartition() version only
|
// used by the other _ScanPartition() version only
|
||||||
|
|
||||||
|
status_t _AddRemoveMonitoring(const char *path, bool add);
|
||||||
|
|
||||||
struct DeviceMap;
|
struct DeviceMap;
|
||||||
struct DiskSystemMap;
|
struct DiskSystemMap;
|
||||||
struct PartitionMap;
|
struct PartitionMap;
|
||||||
struct PartitionSet;
|
struct PartitionSet;
|
||||||
|
class DeviceWatcher;
|
||||||
|
|
||||||
BLocker fLock;
|
BLocker fLock;
|
||||||
DeviceMap *fDevices;
|
DeviceMap *fDevices;
|
||||||
@@ -129,6 +131,7 @@ private:
|
|||||||
PartitionSet *fObsoletePartitions;
|
PartitionSet *fObsoletePartitions;
|
||||||
thread_id fMediaChecker;
|
thread_id fMediaChecker;
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
DeviceWatcher *fDeviceWatcher;
|
||||||
|
|
||||||
static KDiskDeviceManager *sDefaultManager;
|
static KDiskDeviceManager *sDefaultManager;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,11 @@
|
|||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
#include <NodeMonitor.h>
|
||||||
|
#include <node_monitor.h>
|
||||||
|
#include <Notifications.h>
|
||||||
|
#include <vfs.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <module.h>
|
#include <module.h>
|
||||||
@@ -87,6 +92,73 @@ struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// DeviceWatcher
|
||||||
|
class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
|
||||||
|
public:
|
||||||
|
DeviceWatcher(KDiskDeviceManager *manager)
|
||||||
|
: fManager(manager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~DeviceWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void EventOccured(NotificationService &service,
|
||||||
|
const KMessage *event)
|
||||||
|
{
|
||||||
|
int32 opCode = event->GetInt32("opcode", -1);
|
||||||
|
switch (opCode) {
|
||||||
|
case B_ENTRY_CREATED:
|
||||||
|
case B_ENTRY_REMOVED:
|
||||||
|
{
|
||||||
|
const char *name = event->GetString("name", "");
|
||||||
|
dev_t device = event->GetInt32("device", -1);
|
||||||
|
ino_t directory = event->GetInt64("directory", -1);
|
||||||
|
ino_t node = event->GetInt64("node", -1);
|
||||||
|
|
||||||
|
// TODO: it would be nice if we could stat based on the
|
||||||
|
// node here instead of having to resolve the path
|
||||||
|
KPath path(B_PATH_NAME_LENGTH + 1);
|
||||||
|
if (path.InitCheck() != B_OK
|
||||||
|
|| vfs_entry_ref_to_path(device, directory, name,
|
||||||
|
path.LockBuffer(), path.BufferSize()) != B_OK)
|
||||||
|
break;
|
||||||
|
path.UnlockBuffer();
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path.Path(), &st) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
if (opCode == B_ENTRY_CREATED)
|
||||||
|
add_node_listener(device, node, B_WATCH_DIRECTORY,
|
||||||
|
*this);
|
||||||
|
else
|
||||||
|
remove_node_listener(device, node, *this);
|
||||||
|
} else {
|
||||||
|
if (strcmp(name, "raw") == 0) {
|
||||||
|
// a new raw device was added/removed
|
||||||
|
if (opCode == B_ENTRY_CREATED)
|
||||||
|
fManager->CreateDevice(path.Path());
|
||||||
|
else
|
||||||
|
fManager->DeleteDevice(path.Path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
KDiskDeviceManager *fManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_active_job_status(uint32 status)
|
is_active_job_status(uint32 status)
|
||||||
{
|
{
|
||||||
@@ -105,7 +177,8 @@ KDiskDeviceManager::KDiskDeviceManager()
|
|||||||
fDiskSystems(new(nothrow) DiskSystemMap),
|
fDiskSystems(new(nothrow) DiskSystemMap),
|
||||||
fObsoletePartitions(new(nothrow) PartitionSet),
|
fObsoletePartitions(new(nothrow) PartitionSet),
|
||||||
fMediaChecker(-1),
|
fMediaChecker(-1),
|
||||||
fTerminating(false)
|
fTerminating(false),
|
||||||
|
fDeviceWatcher(new(nothrow) DeviceWatcher(this))
|
||||||
{
|
{
|
||||||
if (InitCheck() != B_OK)
|
if (InitCheck() != B_OK)
|
||||||
return;
|
return;
|
||||||
@@ -129,6 +202,9 @@ KDiskDeviceManager::~KDiskDeviceManager()
|
|||||||
status_t result;
|
status_t result;
|
||||||
wait_for_thread(fMediaChecker, &result);
|
wait_for_thread(fMediaChecker, &result);
|
||||||
|
|
||||||
|
// stop all node monitoring
|
||||||
|
_AddRemoveMonitoring("/dev/disk", false);
|
||||||
|
|
||||||
// remove all devices
|
// remove all devices
|
||||||
for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie);) {
|
for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie);) {
|
||||||
PartitionRegistrar _(device);
|
PartitionRegistrar _(device);
|
||||||
@@ -488,6 +564,78 @@ KDiskDeviceManager::ScanPartition(KPartition* partition)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
partition_id
|
||||||
|
KDiskDeviceManager::CreateDevice(const char *path, bool *newlyCreated)
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
status_t error = B_ERROR;
|
||||||
|
if (ManagerLocker locker = this) {
|
||||||
|
KDiskDevice *device = FindDevice(path);
|
||||||
|
if (device != NULL) {
|
||||||
|
// we already know this device
|
||||||
|
if (newlyCreated)
|
||||||
|
*newlyCreated = false;
|
||||||
|
|
||||||
|
return device->ID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a KDiskDevice for it
|
||||||
|
device = new(nothrow) KDiskDevice;
|
||||||
|
if (!device)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// initialize and add the device
|
||||||
|
error = device->SetTo(path);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// cleanup on error
|
||||||
|
if (error != B_OK) {
|
||||||
|
delete device;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == B_OK) {
|
||||||
|
// scan for partitions
|
||||||
|
_ScanPartition(device, false);
|
||||||
|
device->UnmarkBusy(true);
|
||||||
|
|
||||||
|
if (newlyCreated)
|
||||||
|
*newlyCreated = true;
|
||||||
|
|
||||||
|
return device->ID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
KDiskDeviceManager::DeleteDevice(const char *path)
|
||||||
|
{
|
||||||
|
KDiskDevice *device = FindDevice(path);
|
||||||
|
if (device == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
PartitionRegistrar _(device, true);
|
||||||
|
if (DeviceWriteLocker locker = device) {
|
||||||
|
if (_RemoveDevice(device))
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// CreateFileDevice
|
// CreateFileDevice
|
||||||
partition_id
|
partition_id
|
||||||
KDiskDeviceManager::CreateFileDevice(const char *filePath, bool* newlyCreated)
|
KDiskDeviceManager::CreateFileDevice(const char *filePath, bool* newlyCreated)
|
||||||
@@ -758,6 +906,18 @@ KDiskDeviceManager::InitialDeviceScan()
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
KDiskDeviceManager::StartMonitoring()
|
||||||
|
{
|
||||||
|
// do another scan, this will populate the devfs directories
|
||||||
|
InitialDeviceScan();
|
||||||
|
// start monitoring all dirs under /dev/disk
|
||||||
|
status_t result = _AddRemoveMonitoring("/dev/disk", true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// _RescanDiskSystems
|
// _RescanDiskSystems
|
||||||
status_t
|
status_t
|
||||||
KDiskDeviceManager::_RescanDiskSystems(bool fileSystems)
|
KDiskDeviceManager::_RescanDiskSystems(bool fileSystems)
|
||||||
@@ -1126,6 +1286,50 @@ KDiskDeviceManager::_ScanPartition(KPartition *partition)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
KDiskDeviceManager::_AddRemoveMonitoring(const char *path, bool add)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path, &st) < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
status_t error = B_ENTRY_NOT_FOUND;
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
if (add) {
|
||||||
|
error = add_node_listener(st.st_dev, st.st_ino, B_WATCH_DIRECTORY,
|
||||||
|
*fDeviceWatcher);
|
||||||
|
} else {
|
||||||
|
error = remove_node_listener(st.st_dev, st.st_ino,
|
||||||
|
*fDeviceWatcher);
|
||||||
|
}
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (!dir)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
while (dirent *entry = readdir(dir)) {
|
||||||
|
// skip "." and ".."
|
||||||
|
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
KPath entryPath;
|
||||||
|
if (entryPath.SetPath(path) != B_OK
|
||||||
|
|| entryPath.Append(entry->d_name) != B_OK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_AddRemoveMonitoring(entryPath.Path(), add) == B_OK)
|
||||||
|
error = B_OK;
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
KDiskDeviceManager::_CheckMediaStatus()
|
KDiskDeviceManager::_CheckMediaStatus()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -516,8 +516,6 @@ vfs_mount_boot_file_system(kernel_args *args)
|
|||||||
// search for other disk systems
|
// search for other disk systems
|
||||||
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
|
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
|
||||||
manager->RescanDiskSystems();
|
manager->RescanDiskSystems();
|
||||||
manager->InitialDeviceScan();
|
manager->StartMonitoring();
|
||||||
// TODO: later, the DDM should notice when there are new
|
|
||||||
// partitions/devices available
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user