/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/statusProcfs.c#4 $
*
* Proc filesystem interface to the old GET_DEDUPE_STATS and
* GET_KERNEL_STATS ioctls, which can no longer be supported in 4.4
* and later kernels. These files return the same data as the old
* ioctls do, in order to require minimal changes to our (and
* customers') utilties and test code.
*
* +--+----- /proc/vdo procfsRoot
* |
* +-+----- vdo<n> config->poolName
* |
* +------- dedupe_stats GET_DEDUPE_STATS ioctl
* +------- kernel_stats GET_KERNEL_STATS ioctl
*
*/
#include "statusProcfs.h"
#include <linux/version.h>
#include "memoryAlloc.h"
#include "releaseVersions.h"
#include "statistics.h"
#include "vdo.h"
#include "dedupeIndex.h"
#include "ioSubmitter.h"
#include "kernelStatistics.h"
#include "logger.h"
#include "memoryUsage.h"
#include "threadDevice.h"
#include "vdoCommon.h"
static struct proc_dir_entry *procfsRoot = NULL;
/**********************************************************************/
static int statusDedupeShow(struct seq_file *m, void *v)
{
KernelLayer *layer = (KernelLayer *) m->private;
VDOStatistics *stats;
size_t len = sizeof(VDOStatistics);
RegisteredThread allocatingThread, instanceThread;
registerAllocatingThread(&allocatingThread, NULL);
registerThreadDevice(&instanceThread, layer);
int result = ALLOCATE(1, VDOStatistics, __func__, &stats);
if (result == VDO_SUCCESS) {
getKVDOStatistics(&layer->kvdo, stats);
seq_write(m, stats, len);
FREE(stats);
}
unregisterThreadDeviceID();
unregisterAllocatingThread();
return result;
}
/**********************************************************************/
static int statusDedupeOpen(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
return single_open(file, statusDedupeShow, PDE_DATA(inode));
#else
return single_open(file, statusDedupeShow, PDE(inode)->data);
#endif
}
static const struct file_operations vdoProcfsDedupeOps = {
.open = statusDedupeOpen,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**********************************************************************/
static void copyBioStat(BioStats *b, const AtomicBioStats *a)
{
b->read = atomic64_read(&a->read);
b->write = atomic64_read(&a->write);
b->discard = atomic64_read(&a->discard);
b->flush = atomic64_read(&a->flush);
b->fua = atomic64_read(&a->fua);
}
/**********************************************************************/
static BioStats subtractBioStats(BioStats minuend, BioStats subtrahend)
{
return (BioStats) {
.read = minuend.read - subtrahend.read,
.write = minuend.write - subtrahend.write,
.discard = minuend.discard - subtrahend.discard,
.flush = minuend.flush - subtrahend.flush,
.fua = minuend.fua - subtrahend.fua,
};
}
/**********************************************************************/
void getKernelStats(KernelLayer *layer, KernelStatistics *stats)
{
stats->version = STATISTICS_VERSION;
stats->releaseVersion = CURRENT_RELEASE_VERSION_NUMBER;
stats->instance = layer->instance;
getLimiterValuesAtomically(&layer->requestLimiter,
&stats->currentVIOsInProgress, &stats->maxVIOs);
// albireoTimeoutReport gives the number of timeouts, and dedupeContextBusy
// gives the number of queries not made because of earlier timeouts.
stats->dedupeAdviceTimeouts = (getEventCount(&layer->albireoTimeoutReporter)
+ atomic64_read(&layer->dedupeContextBusy));
stats->flushOut = atomic64_read(&layer->flushOut);
stats->logicalBlockSize = layer->deviceConfig->logicalBlockSize;
copyBioStat(&stats->biosIn, &layer->biosIn);
copyBioStat(&stats->biosInPartial, &layer->biosInPartial);
copyBioStat(&stats->biosOut, &layer->biosOut);
copyBioStat(&stats->biosMeta, &layer->biosMeta);
copyBioStat(&stats->biosJournal, &layer->biosJournal);
copyBioStat(&stats->biosPageCache, &layer->biosPageCache);
copyBioStat(&stats->biosOutCompleted, &layer->biosOutCompleted);
copyBioStat(&stats->biosMetaCompleted, &layer->biosMetaCompleted);
copyBioStat(&stats->biosJournalCompleted, &layer->biosJournalCompleted);
copyBioStat(&stats->biosPageCacheCompleted,
&layer->biosPageCacheCompleted);
copyBioStat(&stats->biosAcknowledged, &layer->biosAcknowledged);
copyBioStat(&stats->biosAcknowledgedPartial,
&layer->biosAcknowledgedPartial);
stats->biosInProgress = subtractBioStats(stats->biosIn,
stats->biosAcknowledged);
stats->memoryUsage = getMemoryUsage();
getIndexStatistics(layer->dedupeIndex, &stats->index);
}
/**********************************************************************/
static int statusKernelShow(struct seq_file *m, void *v)
{
KernelLayer *layer = (KernelLayer *) m->private;
KernelStatistics *stats;
size_t len = sizeof(KernelStatistics);
RegisteredThread allocatingThread, instanceThread;
registerAllocatingThread(&allocatingThread, NULL);
registerThreadDevice(&instanceThread, layer);
int result = ALLOCATE(1, KernelStatistics, __func__, &stats);
if (result == VDO_SUCCESS) {
getKernelStats(layer, stats);
seq_write(m, stats, len);
FREE(stats);
}
unregisterThreadDeviceID();
unregisterAllocatingThread();
return result;
}
/**********************************************************************/
static int statusKernelOpen(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
return single_open(file, statusKernelShow, PDE_DATA(inode));
#else
return single_open(file, statusKernelShow, PDE(inode)->data);
#endif
}
static const struct file_operations vdoProcfsKernelOps = {
.open = statusKernelOpen,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**********************************************************************/
int vdoInitProcfs()
{
const char *procfsName = getProcRoot();
procfsRoot = proc_mkdir(procfsName, NULL);
if (procfsRoot == NULL) {
logWarning("Could not create proc filesystem root %s\n", procfsName);
return -ENOMEM;
}
return VDO_SUCCESS;
}
/**********************************************************************/
void vdoDestroyProcfs()
{
remove_proc_entry(getProcRoot(), NULL);
procfsRoot = NULL;
}
/**********************************************************************/
int vdoCreateProcfsEntry(KernelLayer *layer, const char *name, void **private)
{
int result = VDO_SUCCESS;
if (procfsRoot != NULL) {
struct proc_dir_entry *fsDir;
fsDir = proc_mkdir(name, procfsRoot);
if (fsDir == NULL) {
result = -ENOMEM;
} else {
if (proc_create_data(getVDOStatisticsProcFile(), 0644, fsDir,
&vdoProcfsDedupeOps, layer) == NULL) {
result = -ENOMEM;
} else if (proc_create_data(getKernelStatisticsProcFile(), 0644, fsDir,
&vdoProcfsKernelOps, layer) == NULL) {
result = -ENOMEM;
}
}
if (result < 0) {
vdoDestroyProcfsEntry(name, fsDir);
} else {
*private = fsDir;
}
} else {
logWarning("No proc filesystem root set, skipping %s\n", name);
}
return result;
}
/**********************************************************************/
void vdoDestroyProcfsEntry(const char *name, void *private)
{
if (procfsRoot != NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
remove_proc_subtree(name, procfsRoot);
#else
struct proc_dir_entry *fsDir = (struct proc_dir_entry *) private;
remove_proc_entry(getVDOStatisticsProcFile(), fsDir);
remove_proc_entry(getKernelStatisticsProcFile(), fsDir);
remove_proc_entry(name, procfsRoot);
#endif
}
}