????
| Current Path : /var/lib/dkms/file_protector/1.1-1578/source/ |
| Current File : //var/lib/dkms/file_protector/1.1-1578/source/module_rundown_protection.c |
/**
@file module_rundown_protection.c
@brief Global module rundown protection used for safe syscalls handling
@details Copyright (c) 2022 Acronis International GmbH
@author Denis Kopyrin (Denis.Kopyrin@acronis.com)
@since $Id: $
*/
#include "module_rundown_protection.h"
#include "rundown_protection.h"
#include "module_ref.h"
#include <linux/module.h>
#include <linux/spinlock.h>
typedef struct {
rundown_protection_t rundown_protection;
// 'spinlock' protects flags below
spinlock_t spinlock;
bool active;
bool referenced;
} mod_rundown_protection_t;
static mod_rundown_protection_t mod_rundown_protection;
static void mod_rundown_protection_finalizer(void *ctx) {
bool active, referenced;
mod_rundown_protection_t *mrp = (mod_rundown_protection_t*) ctx;
IPRINTF("");
spin_lock(&mrp->spinlock);
active = mrp->active;
referenced = mrp->referenced;
if (!active) {
mrp->referenced = false;
}
spin_unlock(&mrp->spinlock);
IPRINTF("active=%d, referenced=%d", active, referenced);
if (active) {
IPRINTF("mod is currently active, doing nothing and considering finalizer spurious");
return;
}
if (referenced) {
MODULE_PUT();
} else {
IPRINTF("module is not referenced already");
}
}
void __init mod_rundown_protection_init(bool ready) {
rundown_protection_init(&mod_rundown_protection.rundown_protection, mod_rundown_protection_finalizer, &mod_rundown_protection, ready);
spin_lock_init(&mod_rundown_protection.spinlock);
mod_rundown_protection.referenced = false;
mod_rundown_protection.active = false;
}
bool mod_rundown_protection_lock(void) {
return rundown_protection_lock(&mod_rundown_protection.rundown_protection);
}
void mod_rundown_protection_unlock(void) {
return rundown_protection_unlock(&mod_rundown_protection.rundown_protection);
}
void mod_rundown_protection_set_rundown_active(void) {
int64_t pending_count = 0;
bool need_unreference = false;
IPRINTF("");
// Notice that during 'set_rundown_active' spurious finalizer might be called
rundown_protection_set_rundown_active(&mod_rundown_protection.rundown_protection);
// Because 'mod_rundown_protection.active=false' is set after 'set_rundown_active' is done, finalizer might be missed.
// In this case, we might have to unreference from here too if 'get_pending_count' is 0.
spin_lock(&mod_rundown_protection.spinlock);
mod_rundown_protection.active = false;
pending_count = rundown_protection_get_pending_count(&mod_rundown_protection.rundown_protection);
if (0 == pending_count) {
need_unreference = mod_rundown_protection.referenced;
mod_rundown_protection.referenced = false;
}
spin_unlock(&mod_rundown_protection.spinlock);
IPRINTF("pending_count=%lld need_unreference=%d", pending_count, need_unreference);
if (need_unreference) {
MODULE_PUT();
}
}
void mod_rundown_protection_wait_for_rundown(void) {
return rundown_protection_wait_for_rundown(&mod_rundown_protection.rundown_protection);
}
bool mod_rundown_protection_wait_for_rundown_timeout(unsigned long timeout_jiffies) {
return rundown_protection_wait_for_rundown_timeout(&mod_rundown_protection.rundown_protection, timeout_jiffies);
}
void mod_rundown_protection_set_ready(void) {
bool module_was_already_referenced;
IPRINTF("");
MODULE_GET();
spin_lock(&mod_rundown_protection.spinlock);
mod_rundown_protection.active = true;
module_was_already_referenced = mod_rundown_protection.referenced;
mod_rundown_protection.referenced = true;
spin_unlock(&mod_rundown_protection.spinlock);
rundown_protection_set_ready(&mod_rundown_protection.rundown_protection);
IPRINTF("module_was_already_referenced=%d", module_was_already_referenced);
if (module_was_already_referenced) {
MODULE_PUT();
}
}