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:
parent
bacaf1148c
commit
06da81f012
|
@ -65,6 +65,9 @@ public:
|
|||
|
||||
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,
|
||||
bool* newlyCreated = NULL);
|
||||
status_t DeleteFileDevice(const char *filePath);
|
||||
|
@ -90,13 +93,9 @@ public:
|
|||
KDiskSystem *LoadDiskSystem(disk_system_id id);
|
||||
KDiskSystem *LoadNextDiskSystem(int32 *cookie);
|
||||
|
||||
// Watching
|
||||
|
||||
// TODO: Watching service for the kernel. The userland watching is handled
|
||||
// by the registrar.
|
||||
|
||||
status_t InitialDeviceScan();
|
||||
status_t RescanDiskSystems();
|
||||
status_t StartMonitoring();
|
||||
|
||||
private:
|
||||
static status_t _CheckMediaStatusDaemon(void* self);
|
||||
|
@ -117,10 +116,13 @@ private:
|
|||
status_t _ScanPartition(KPartition *partition);
|
||||
// used by the other _ScanPartition() version only
|
||||
|
||||
status_t _AddRemoveMonitoring(const char *path, bool add);
|
||||
|
||||
struct DeviceMap;
|
||||
struct DiskSystemMap;
|
||||
struct PartitionMap;
|
||||
struct PartitionSet;
|
||||
class DeviceWatcher;
|
||||
|
||||
BLocker fLock;
|
||||
DeviceMap *fDevices;
|
||||
|
@ -129,6 +131,7 @@ private:
|
|||
PartitionSet *fObsoletePartitions;
|
||||
thread_id fMediaChecker;
|
||||
volatile bool fTerminating;
|
||||
DeviceWatcher *fDeviceWatcher;
|
||||
|
||||
static KDiskDeviceManager *sDefaultManager;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
#include <KernelExport.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include <NodeMonitor.h>
|
||||
#include <node_monitor.h>
|
||||
#include <Notifications.h>
|
||||
#include <vfs.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.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
|
||||
is_active_job_status(uint32 status)
|
||||
{
|
||||
|
@ -105,7 +177,8 @@ KDiskDeviceManager::KDiskDeviceManager()
|
|||
fDiskSystems(new(nothrow) DiskSystemMap),
|
||||
fObsoletePartitions(new(nothrow) PartitionSet),
|
||||
fMediaChecker(-1),
|
||||
fTerminating(false)
|
||||
fTerminating(false),
|
||||
fDeviceWatcher(new(nothrow) DeviceWatcher(this))
|
||||
{
|
||||
if (InitCheck() != B_OK)
|
||||
return;
|
||||
|
@ -129,6 +202,9 @@ KDiskDeviceManager::~KDiskDeviceManager()
|
|||
status_t result;
|
||||
wait_for_thread(fMediaChecker, &result);
|
||||
|
||||
// stop all node monitoring
|
||||
_AddRemoveMonitoring("/dev/disk", false);
|
||||
|
||||
// remove all devices
|
||||
for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie);) {
|
||||
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
|
||||
partition_id
|
||||
KDiskDeviceManager::CreateFileDevice(const char *filePath, bool* newlyCreated)
|
||||
|
@ -758,6 +906,18 @@ KDiskDeviceManager::InitialDeviceScan()
|
|||
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
|
||||
status_t
|
||||
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
|
||||
KDiskDeviceManager::_CheckMediaStatus()
|
||||
{
|
||||
|
|
|
@ -516,8 +516,6 @@ vfs_mount_boot_file_system(kernel_args *args)
|
|||
// search for other disk systems
|
||||
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
|
||||
manager->RescanDiskSystems();
|
||||
manager->InitialDeviceScan();
|
||||
// TODO: later, the DDM should notice when there are new
|
||||
// partitions/devices available
|
||||
manager->StartMonitoring();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue