Started a cache module that is supposed to speed up application
launching and the boot process. Doesn't do much yet beyond collecting data, though. Anyway, this is a test, it might not end up in the distribution. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13893 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -8,3 +8,6 @@ KernelAddon log : kernel file_cache :
|
||||
log.cpp
|
||||
;
|
||||
|
||||
KernelAddon launch_speedup : kernel file_cache :
|
||||
launch_speedup.cpp
|
||||
;
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
/** This module memorizes all opened files for a certain session. A session
|
||||
* can be the start of an application or the boot process.
|
||||
* When a session is started, it will prefetch all files from an earlier
|
||||
* session in order to speed up the launching or booting process.
|
||||
*/
|
||||
|
||||
|
||||
#include "launch_speedup.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <util/kernel_cpp.h>
|
||||
#include <util/khash.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <thread.h>
|
||||
#include <team.h>
|
||||
#include <file_cache.h>
|
||||
#include <fs/fd.h>
|
||||
#include <fs/KPath.h>
|
||||
#include <generic_syscall.h>
|
||||
#include <Node.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// ToDo: combine the last 3-5 sessions to their intersection
|
||||
// ToDo: maybe ignore sessions if the node count is < 3 (without system libs)
|
||||
|
||||
#define TRACE_CACHE_MODULE
|
||||
#ifdef TRACE_CACHE_MODULE
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
struct data_part {
|
||||
off_t offset;
|
||||
off_t size;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct node *next;
|
||||
node_ref ref;
|
||||
int32 ref_count;
|
||||
bigtime_t timestamp;
|
||||
data_part parts[5];
|
||||
size_t part_count;
|
||||
};
|
||||
|
||||
class Session {
|
||||
public:
|
||||
Session(team_id team, const char *name, mount_id device,
|
||||
vnode_id node, int32 seconds = 60);
|
||||
~Session();
|
||||
|
||||
status_t InitCheck();
|
||||
team_id Team() const { return fTeam; }
|
||||
const char *Name() const { return fName; }
|
||||
bool IsActive() const { return fActiveUntil >= system_time(); }
|
||||
|
||||
void AddNode(mount_id device, vnode_id node);
|
||||
void RemoveNode(mount_id device, vnode_id node);
|
||||
|
||||
void Lock() { mutex_lock(&fLock); }
|
||||
void Unlock() { mutex_unlock(&fLock); }
|
||||
|
||||
static uint32 NextOffset() { return offsetof(Session, fNext); }
|
||||
|
||||
private:
|
||||
struct node *_FindNode(mount_id device, vnode_id node);
|
||||
|
||||
Session *fNext;
|
||||
char fName[B_OS_NAME_LENGTH];
|
||||
mutex fLock;
|
||||
hash_table *fNodeHash;
|
||||
team_id fTeam;
|
||||
node_ref fNodeRef;
|
||||
bigtime_t fActiveUntil;
|
||||
};
|
||||
|
||||
class SessionGetter {
|
||||
public:
|
||||
SessionGetter(team_id team, Session **_session);
|
||||
~SessionGetter();
|
||||
|
||||
status_t New(const char *name, mount_id device, vnode_id node,
|
||||
Session **_session);
|
||||
|
||||
private:
|
||||
Session *fSession;
|
||||
};
|
||||
|
||||
static Session *sMainSession;
|
||||
static hash_table *sTeamHash;
|
||||
static mutex sLock;
|
||||
|
||||
|
||||
node_ref::node_ref()
|
||||
{
|
||||
// part of libbe.so
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
node_compare(void *_node, const void *_key)
|
||||
{
|
||||
struct node *node = (struct node *)_node;
|
||||
const struct node_ref *key = (node_ref *)_key;
|
||||
|
||||
if (node->ref.device == key->device && node->ref.node == key->node)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
node_hash(void *_node, const void *_key, uint32 range)
|
||||
{
|
||||
struct node *node = (struct node *)_node;
|
||||
const struct node_ref *key = (node_ref *)_key;
|
||||
|
||||
#define VHASH(mountid, vnodeid) (((uint32)((vnodeid) >> 32) + (uint32)(vnodeid)) ^ (uint32)(mountid))
|
||||
|
||||
if (node != NULL)
|
||||
return (VHASH(node->ref.device, node->ref.node) % range);
|
||||
|
||||
return (VHASH(key->device, key->node) % range);
|
||||
|
||||
#undef VHASH
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
team_compare(void *_session, const void *_key)
|
||||
{
|
||||
Session *session = (Session *)_session;
|
||||
const team_id *team = (const team_id *)_key;
|
||||
|
||||
if (session->Team() == *team)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
team_hash(void *_session, const void *_key, uint32 range)
|
||||
{
|
||||
Session *session = (Session *)_session;
|
||||
const team_id *team = (const team_id *)_key;
|
||||
|
||||
if (session != NULL)
|
||||
return session->Team() % range;
|
||||
|
||||
return *team % range;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
stop_session(Session *session)
|
||||
{
|
||||
if (session == NULL)
|
||||
return;
|
||||
|
||||
TRACE(("stop_session(%s)\n", session->Name()));
|
||||
|
||||
session->Lock();
|
||||
|
||||
MutexLocker locker(&sLock);
|
||||
|
||||
if (session->Team() >= B_OK)
|
||||
hash_remove(sTeamHash, session);
|
||||
|
||||
delete session;
|
||||
}
|
||||
|
||||
|
||||
static Session *
|
||||
start_session(team_id team, mount_id device, vnode_id node, const char *name)
|
||||
{
|
||||
MutexLocker locker(&sLock);
|
||||
|
||||
Session *session = new Session(team, name, device, node);
|
||||
if (session == NULL)
|
||||
return NULL;
|
||||
|
||||
if (session->InitCheck() != B_OK) {
|
||||
delete session;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (team >= B_OK)
|
||||
hash_insert(sTeamHash, session);
|
||||
|
||||
session->Lock();
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
team_gone(team_id team, void *_session)
|
||||
{
|
||||
Session *session = (Session *)_session;
|
||||
stop_session(session);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Session::Session(team_id team, const char *name, mount_id device,
|
||||
vnode_id node, int32 seconds)
|
||||
:
|
||||
fTeam(team)
|
||||
{
|
||||
if (name != NULL) {
|
||||
size_t length = strlen(name) + 1;
|
||||
if (length > B_OS_NAME_LENGTH)
|
||||
name += length - B_OS_NAME_LENGTH;
|
||||
|
||||
strlcpy(fName, name, B_OS_NAME_LENGTH);
|
||||
} else
|
||||
fName[0] = '\0';
|
||||
|
||||
mutex_init(&fLock, "launch speedup session");
|
||||
fNodeHash = hash_init(64, 0, &node_compare, &node_hash);
|
||||
fActiveUntil = system_time() + seconds * 1000000LL;
|
||||
|
||||
fNodeRef.device = device;
|
||||
fNodeRef.node = node;
|
||||
|
||||
TRACE(("start session %ld:%Ld \"%s\", system_time: %Ld, active until: %Ld\n",
|
||||
device, node, Name(), system_time(), fActiveUntil));
|
||||
}
|
||||
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
|
||||
// free all nodes
|
||||
uint32 cookie = 0;
|
||||
struct node *node;
|
||||
while ((node = (struct node *)hash_remove_first(fNodeHash, &cookie)) != NULL) {
|
||||
TRACE((" node %ld:%Ld\n", node->ref.device, node->ref.node));
|
||||
free(node);
|
||||
}
|
||||
|
||||
hash_uninit(fNodeHash);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Session::InitCheck()
|
||||
{
|
||||
if (fLock.sem < B_OK)
|
||||
return fLock.sem;
|
||||
|
||||
if (fNodeHash == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
if (Team() >= B_OK && start_watching_team(Team(), team_gone, this) != B_OK)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
node *
|
||||
Session::_FindNode(mount_id device, vnode_id node)
|
||||
{
|
||||
node_ref key;
|
||||
key.device = device;
|
||||
key.node = node;
|
||||
|
||||
return (struct node *)hash_lookup(fNodeHash, &key);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Session::AddNode(mount_id device, vnode_id id)
|
||||
{
|
||||
struct node *node = _FindNode(device, id);
|
||||
if (node != NULL) {
|
||||
node->ref_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
node = new ::node;
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
node->ref.device = device;
|
||||
node->ref.node = id;
|
||||
node->timestamp = system_time();
|
||||
|
||||
hash_insert(fNodeHash, node);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Session::RemoveNode(mount_id device, vnode_id id)
|
||||
{
|
||||
struct node *node = _FindNode(device, id);
|
||||
if (node != NULL && --node->ref_count <= 0)
|
||||
hash_remove(fNodeHash, node);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
SessionGetter::SessionGetter(team_id team, Session **_session)
|
||||
{
|
||||
MutexLocker locker(&sLock);
|
||||
|
||||
if (sMainSession != NULL)
|
||||
fSession = sMainSession;
|
||||
else
|
||||
fSession = (Session *)hash_lookup(sTeamHash, &team);
|
||||
|
||||
if (fSession != NULL)
|
||||
fSession->Lock();
|
||||
|
||||
*_session = fSession;
|
||||
}
|
||||
|
||||
|
||||
SessionGetter::~SessionGetter()
|
||||
{
|
||||
if (fSession != NULL)
|
||||
fSession->Unlock();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SessionGetter::New(const char *name, mount_id device, vnode_id node,
|
||||
Session **_session)
|
||||
{
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
fSession = start_session(thread->team->id, device, node, name);
|
||||
|
||||
if (fSession != NULL) {
|
||||
*_session = fSession;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static void
|
||||
node_opened(void *vnode, int32 fdType, mount_id device, vnode_id parent,
|
||||
vnode_id node, const char *name, off_t size)
|
||||
{
|
||||
Session *session;
|
||||
SessionGetter getter(team_get_current_team_id(), &session);
|
||||
|
||||
if (session == NULL) {
|
||||
char buffer[B_FILE_NAME_LENGTH];
|
||||
if (name == NULL
|
||||
&& vfs_get_vnode_name(vnode, buffer, sizeof(buffer)) == B_OK)
|
||||
name = buffer;
|
||||
|
||||
// create new session for this team
|
||||
getter.New(name, device, node, &session);
|
||||
}
|
||||
|
||||
if (session == NULL || !session->IsActive()) {
|
||||
if (session != NULL && sMainSession != NULL) {
|
||||
// ToDo: this opens a race condition with the "stop session" syscall
|
||||
stop_session(sMainSession);
|
||||
sMainSession = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
session->AddNode(device, node);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
node_closed(void *vnode, int32 fdType, mount_id device, vnode_id node, int32 accessType)
|
||||
{
|
||||
Session *session;
|
||||
SessionGetter getter(team_get_current_team_id(), &session);
|
||||
|
||||
if (session == NULL)
|
||||
return;
|
||||
|
||||
if (accessType == FILE_CACHE_NO_IO)
|
||||
session->RemoveNode(device, node);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
launch_speedup_control(const char *subsystem, uint32 function,
|
||||
void *buffer, size_t bufferSize)
|
||||
{
|
||||
switch (function) {
|
||||
case LAUNCH_SPEEDUP_START_SESSION:
|
||||
{
|
||||
char name[B_OS_NAME_LENGTH];
|
||||
if (!IS_USER_ADDRESS(buffer)
|
||||
|| user_strlcpy(name, (const char *)buffer, B_OS_NAME_LENGTH) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
sMainSession = start_session(-1, -1, -1, name);
|
||||
sMainSession->Unlock();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case LAUNCH_SPEEDUP_STOP_SESSION:
|
||||
{
|
||||
char name[B_OS_NAME_LENGTH];
|
||||
if (!IS_USER_ADDRESS(buffer)
|
||||
|| user_strlcpy(name, (const char *)buffer, B_OS_NAME_LENGTH) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// ToDo: this check is not thread-safe
|
||||
if (sMainSession == NULL || strcmp(sMainSession->Name(), name))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
stop_session(sMainSession);
|
||||
sMainSession = NULL;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
uninit()
|
||||
{
|
||||
unregister_generic_syscall(LAUNCH_SPEEDUP_SYSCALLS, 1);
|
||||
hash_uninit(sTeamHash);
|
||||
mutex_destroy(&sLock);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
init()
|
||||
{
|
||||
sTeamHash = hash_init(64, Session::NextOffset(), &team_compare, &team_hash);
|
||||
if (sTeamHash == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status;
|
||||
|
||||
if (mutex_init(&sLock, "launch speedup") < B_OK) {
|
||||
status = sLock.sem;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
// register kernel syscalls
|
||||
if (register_generic_syscall(LAUNCH_SPEEDUP_SYSCALLS,
|
||||
launch_speedup_control, 1, 0) != B_OK) {
|
||||
status = B_ERROR;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
sMainSession = start_session(-1, -1, -1, "system boot");
|
||||
sMainSession->Unlock();
|
||||
return B_OK;
|
||||
|
||||
err1:
|
||||
hash_uninit(sTeamHash);
|
||||
err2:
|
||||
mutex_destroy(&sLock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
return init();
|
||||
|
||||
case B_MODULE_UNINIT:
|
||||
uninit();
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct cache_module_info sLaunchSpeedupModule = {
|
||||
{
|
||||
CACHE_MODULES_NAME "/launch_speedup/v1",
|
||||
0,
|
||||
std_ops,
|
||||
},
|
||||
node_opened,
|
||||
node_closed,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sLaunchSpeedupModule,
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef LAUNCH_SPEEDUP_H
|
||||
#define LAUNCH_SPEEDUP_H
|
||||
|
||||
|
||||
// generic syscall interface
|
||||
#define LAUNCH_SPEEDUP_SYSCALLS "launch_speedup"
|
||||
|
||||
#define LAUNCH_SPEEDUP_START_SESSION 1
|
||||
#define LAUNCH_SPEEDUP_STOP_SESSION 2
|
||||
|
||||
|
||||
#endif /* LAUNCH_SPEEDUP_H */
|
||||
Reference in New Issue
Block a user