2014-11-21 10:51:34 +00:00
|
|
|
/*
|
|
|
|
* MARS Long Distance Replication Software
|
|
|
|
*
|
|
|
|
* This file is part of MARS project: http://schoebel.github.io/mars/
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010-2014 Thomas Schoebel-Theuer
|
|
|
|
* Copyright (C) 2011-2014 1&1 Internet AG
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
//#define BRICK_DEBUGGING
|
2013-01-20 22:09:09 +00:00
|
|
|
#define MARS_DEBUGGING
|
2011-09-27 08:46:14 +00:00
|
|
|
//#define IO_DEBUGGING
|
2013-01-22 10:30:52 +00:00
|
|
|
#define STAT_DEBUGGING
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/file.h>
|
2013-01-17 16:24:53 +00:00
|
|
|
#include <linux/blkdev.h>
|
2011-07-22 10:43:40 +00:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/utsname.h>
|
|
|
|
|
|
|
|
#include "strategy.h"
|
|
|
|
|
2014-03-30 06:18:47 +00:00
|
|
|
#include "../lib_mapfree.h"
|
2011-07-22 10:43:40 +00:00
|
|
|
#include "../mars_client.h"
|
|
|
|
|
|
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/kthread.h>
|
2011-11-08 12:33:13 +00:00
|
|
|
#include <linux/statfs.h>
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
#define SKIP_BIO false
|
|
|
|
|
2014-06-04 07:10:50 +00:00
|
|
|
// remove_this
|
|
|
|
#include <linux/wait.h>
|
2014-07-14 15:27:11 +00:00
|
|
|
#include <linux/version.h>
|
|
|
|
/* FIXME: some Redhat/openvz kernels seem to have both (backporting etc).
|
|
|
|
* The folling is an incomplete quickfix / workaround. TBD.
|
|
|
|
*/
|
|
|
|
#if !defined(__WAIT_ATOMIC_T_KEY_INITIALIZER) || defined(RHEL_RELEASE)
|
2014-06-04 07:10:50 +00:00
|
|
|
#define HAS_VFS_READDIR
|
2015-07-06 10:41:40 +00:00
|
|
|
#elif !defined(f_dentry)
|
|
|
|
#define __HAS_NEW_FILLDIR_T
|
2014-06-04 07:10:50 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// end_remove_this
|
2011-07-22 10:43:40 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// meta descriptions
|
|
|
|
|
|
|
|
const struct meta mars_kstat_meta[] = {
|
|
|
|
META_INI(ino, struct kstat, FIELD_INT),
|
|
|
|
META_INI(mode, struct kstat, FIELD_INT),
|
|
|
|
META_INI(size, struct kstat, FIELD_INT),
|
|
|
|
META_INI_SUB(atime, struct kstat, mars_timespec_meta),
|
|
|
|
META_INI_SUB(mtime, struct kstat, mars_timespec_meta),
|
|
|
|
META_INI_SUB(ctime, struct kstat, mars_timespec_meta),
|
|
|
|
META_INI(blksize, struct kstat, FIELD_INT),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL_GPL(mars_kstat_meta);
|
|
|
|
|
|
|
|
const struct meta mars_dent_meta[] = {
|
|
|
|
META_INI(d_name, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_rest, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_path, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_type, struct mars_dent, FIELD_INT),
|
|
|
|
META_INI(d_class, struct mars_dent, FIELD_INT),
|
|
|
|
META_INI(d_serial, struct mars_dent, FIELD_INT),
|
2014-03-30 06:18:47 +00:00
|
|
|
META_INI(d_corr_A, struct mars_dent, FIELD_INT),
|
|
|
|
META_INI(d_corr_B, struct mars_dent, FIELD_INT),
|
2011-07-22 10:43:40 +00:00
|
|
|
META_INI_SUB(new_stat,struct mars_dent, mars_kstat_meta),
|
|
|
|
META_INI_SUB(old_stat,struct mars_dent, mars_kstat_meta),
|
|
|
|
META_INI(new_link, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(old_link, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_args, struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_argv[0], struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_argv[1], struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_argv[2], struct mars_dent, FIELD_STRING),
|
|
|
|
META_INI(d_argv[3], struct mars_dent, FIELD_STRING),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL_GPL(mars_dent_meta);
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// some helpers
|
|
|
|
|
2014-03-24 07:03:41 +00:00
|
|
|
static inline
|
|
|
|
int _length_paranoia(int len, int line)
|
|
|
|
{
|
|
|
|
if (unlikely(len < 0)) {
|
|
|
|
MARS_ERR("implausible string length %d (line=%d)\n", len, line);
|
|
|
|
len = PAGE_SIZE - 2;
|
|
|
|
} else if (unlikely(len > PAGE_SIZE - 2)) {
|
|
|
|
MARS_WRN("string length %d will be truncated to %d (line=%d)\n",
|
|
|
|
len, (int)PAGE_SIZE - 2, line);
|
|
|
|
len = PAGE_SIZE - 2;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
int mars_stat(const char *path, struct kstat *stat, bool use_lstat)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
if (use_lstat) {
|
|
|
|
status = vfs_lstat((char*)path, stat);
|
|
|
|
} else {
|
|
|
|
status = vfs_stat((char*)path, stat);
|
|
|
|
}
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
2013-01-14 22:17:26 +00:00
|
|
|
if (likely(status >= 0)) {
|
|
|
|
set_lamport(&stat->mtime);
|
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_stat);
|
|
|
|
|
light: fix missing versionlink upon slow or defective IO
Some primary appeared to have died, and was rebooted.
In the meantime, the old secondary was forcefully switched
to primary.
Afterwards, the old primary = new secondary got stuck because 2
versionlinks, which had been _produced_ by _himself_, were
missing, but they were present at the new primary = old secondary!
How could this happen?
All transaction logfiles were fully present and correct everywhere.
However, the old primary kern.log showed that a problem with the
RAID system must have existed. In addition, the RAID controller
errorlog also reported some problems which appeared to have healed.
Problem analysis shows the following possibility:
The transaction logger can continue to write data, even via
fsync(), while the _writeback_ of other parts of the /mars filesystem
(e.g. symlink updates) got stuck for a long time due to an IO problem.
Usually, slow or even missing symlink updates are no problem because
upon recovery after a reboot, everything is healed by transaction
replay (possibly replaying much more data than really necessary,
but this does not affect semantics, and it is even advantageous
when RAID disks might contain defective data).
There is one exception: after a logrotate, the corresponding new
versionlink should appear after a small time. Otherwise, the
above mentioned scenario could emerge.
We use sync_filesystem() to ensure that any versionlink update
to a _new_ versionlink is either guaranteed to become persistent,
or (in case of IO problems) the mars_light thread will hang, which
will be (hopefully) noticed soon by monitoring.
2016-01-29 06:59:54 +00:00
|
|
|
void mars_sync(void)
|
|
|
|
{
|
|
|
|
struct file *f;
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
f = filp_open("/mars", O_DIRECTORY | O_RDONLY, 0);
|
|
|
|
set_fs(oldfs);
|
|
|
|
if (unlikely(IS_ERR(f)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (likely(f->f_mapping)) {
|
|
|
|
struct inode *inode = f->f_mapping->host;
|
|
|
|
|
|
|
|
if (likely(inode && inode->i_sb)) {
|
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
down_read(&sb->s_umount);
|
|
|
|
sync_filesystem(sb);
|
|
|
|
up_read(&sb->s_umount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
filp_close(f, NULL);
|
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
int mars_mkdir(const char *path)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_mkdir(path, 0700);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_mkdir);
|
|
|
|
|
2013-06-26 13:18:32 +00:00
|
|
|
int mars_rmdir(const char *path)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_rmdir(path);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_rmdir);
|
|
|
|
|
2011-10-24 10:54:15 +00:00
|
|
|
int mars_unlink(const char *path)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_unlink(path);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_unlink);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
int mars_symlink(const char *oldpath, const char *newpath, const struct timespec *stamp, uid_t uid)
|
|
|
|
{
|
|
|
|
char *tmp = backskip_replace(newpath, '/', true, "/.tmp-");
|
|
|
|
mm_segment_t oldfs;
|
2012-07-12 13:34:44 +00:00
|
|
|
struct kstat stat = {};
|
2012-12-18 04:50:04 +00:00
|
|
|
struct timespec times[2];
|
2011-07-22 10:43:40 +00:00
|
|
|
int status = -ENOMEM;
|
|
|
|
|
|
|
|
if (unlikely(!tmp))
|
|
|
|
goto done;
|
|
|
|
|
2012-07-12 13:34:44 +00:00
|
|
|
if (stamp)
|
2012-12-18 04:50:04 +00:00
|
|
|
memcpy(×[0], stamp, sizeof(times[0]));
|
|
|
|
else
|
|
|
|
get_lamport(×[0]);
|
|
|
|
|
2016-08-08 11:47:25 +00:00
|
|
|
#ifdef CONFIG_MARS_DEBUG
|
|
|
|
while (mars_hang_mode & 4)
|
|
|
|
brick_msleep(100);
|
|
|
|
#endif
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
2012-07-12 13:34:44 +00:00
|
|
|
/* Some filesystems have only full second resolution.
|
|
|
|
* Thus it may happen that the new timestamp is not
|
|
|
|
* truly moving forward when called twice shortly.
|
|
|
|
* This is a _workaround_, to be replaced by a better
|
|
|
|
* method somewhen.
|
|
|
|
*/
|
2012-12-18 04:50:04 +00:00
|
|
|
status = vfs_lstat((char*)newpath, &stat);
|
|
|
|
if (status >= 0 &&
|
|
|
|
!stamp &&
|
|
|
|
!stat.mtime.tv_nsec &&
|
|
|
|
times[0].tv_sec == stat.mtime.tv_sec) {
|
|
|
|
MARS_DBG("workaround timestamp tv_sec=%ld\n", stat.mtime.tv_sec);
|
|
|
|
times[0].tv_sec = stat.mtime.tv_sec + 1;
|
light: reduce cascades on lamport clock workaround
Signed-off-by: Thomas Schoebel-Theuer <tst@1und1.de>
Some filesystems like ext3 have only full second resolution.
Therefore, we _must_ advance the Lamport clock in whole seconds
when working on such gear, since we want to prevent lost
updates which would be caused by standstill Lamport clocks.
Sometimes, the lamport clock gets updated more frequently per second
than real time. In such cases, the Lamport clock will run much faster
than real time. After some weeks of operation, the Lamport clock
will be far in the future.
In general, we cannot do anything against that. When some fine-grained
information cannot be coded into some specific data type, it
cannot be coded.
However, when updates start to occur less frequently, we want to
_leave_ the workaround mode ASAP. The old code set tv_nsec to 0
which made it very likely that the workaround was triggered
again unnecessarily.
In order to _reduce_ that effect, we prevent unnecessary cascades
of whole-second leaps by setting the nanoseconds constantly to 1
if the full second was increased due to insufficient capabilities
of the underlying filesystem. At least in those cases where
Lamport timestamps are transferred over the network and/or we have
mixed configurations between ext3/ext4, we hope to
decrease the risk of endless cascades.
Experience shows that the new code behaves better.
2013-08-07 15:27:56 +00:00
|
|
|
/* Setting tv_nsec to 1 prevents from unnecessarily reentering
|
|
|
|
* this workaround again if accidentally the original tv_nsec
|
|
|
|
* had been 0 or if the workaround had been triggered.
|
|
|
|
*/
|
|
|
|
times[0].tv_nsec = 1;
|
2012-07-12 13:34:44 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
(void)sys_unlink(tmp);
|
2012-07-12 13:34:44 +00:00
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
status = sys_symlink(oldpath, tmp);
|
|
|
|
|
2012-12-18 04:50:04 +00:00
|
|
|
if (status >= 0) {
|
2011-07-22 10:43:40 +00:00
|
|
|
sys_lchown(tmp, uid, 0);
|
2012-12-18 04:50:04 +00:00
|
|
|
memcpy(×[1], ×[0], sizeof(struct timespec));
|
2011-07-22 10:43:40 +00:00
|
|
|
status = do_utimes(AT_FDCWD, tmp, times, AT_SYMLINK_NOFOLLOW);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status >= 0) {
|
2012-12-18 04:50:04 +00:00
|
|
|
set_lamport(×[0]);
|
2011-07-22 10:43:40 +00:00
|
|
|
status = mars_rename(tmp, newpath);
|
|
|
|
}
|
|
|
|
set_fs(oldfs);
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(tmp);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_symlink);
|
|
|
|
|
2012-12-12 14:10:26 +00:00
|
|
|
char *mars_readlink(const char *newpath)
|
|
|
|
{
|
2014-03-21 08:17:31 +00:00
|
|
|
char *res = NULL;
|
2012-12-12 14:10:26 +00:00
|
|
|
struct path path = {};
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
struct inode *inode;
|
2014-03-21 08:17:31 +00:00
|
|
|
int len;
|
2012-12-17 17:10:22 +00:00
|
|
|
int status = -ENOMEM;
|
2012-12-12 14:10:26 +00:00
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
|
|
|
|
status = user_path_at(AT_FDCWD, newpath, 0, &path);
|
|
|
|
if (unlikely(status < 0)) {
|
|
|
|
MARS_DBG("link '%s' does not exist, status = %d\n", newpath, status);
|
2012-12-17 17:10:22 +00:00
|
|
|
goto done_fs;
|
2012-12-12 14:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inode = path.dentry->d_inode;
|
2014-03-21 08:17:31 +00:00
|
|
|
if (unlikely(!inode || !S_ISLNK(inode->i_mode))) {
|
2012-12-12 14:10:26 +00:00
|
|
|
MARS_ERR("link '%s' has invalid inode\n", newpath);
|
2012-12-17 17:10:22 +00:00
|
|
|
status = -EINVAL;
|
|
|
|
goto done_put;
|
2012-12-12 14:10:26 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 08:17:31 +00:00
|
|
|
len = i_size_read(inode);
|
|
|
|
if (unlikely(len <= 0 || len > PAGE_SIZE)) {
|
|
|
|
MARS_ERR("link '%s' invalid length = %d\n", newpath, len);
|
|
|
|
status = -EINVAL;
|
|
|
|
goto done_put;
|
|
|
|
}
|
|
|
|
res = brick_string_alloc(len + 2);
|
|
|
|
|
|
|
|
status = inode->i_op->readlink(path.dentry, res, len + 1);
|
2012-12-12 14:10:26 +00:00
|
|
|
if (unlikely(status < 0)) {
|
|
|
|
MARS_ERR("cannot read link '%s', status = %d\n", newpath, status);
|
2012-12-18 04:50:04 +00:00
|
|
|
} else {
|
|
|
|
set_lamport(&inode->i_mtime);
|
2012-12-12 14:10:26 +00:00
|
|
|
}
|
|
|
|
|
2012-12-17 17:10:22 +00:00
|
|
|
done_put:
|
|
|
|
path_put(&path);
|
|
|
|
|
|
|
|
done_fs:
|
2012-12-12 14:10:26 +00:00
|
|
|
set_fs(oldfs);
|
2012-12-17 17:10:22 +00:00
|
|
|
if (unlikely(status < 0)) {
|
2014-03-21 08:17:31 +00:00
|
|
|
if (unlikely(!res)) {
|
|
|
|
res = brick_string_alloc(1);
|
|
|
|
}
|
2014-02-20 09:05:19 +00:00
|
|
|
res[0] = '\0';
|
2012-12-17 17:10:22 +00:00
|
|
|
}
|
2012-12-12 14:10:26 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_readlink);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
int mars_rename(const char *oldpath, const char *newpath)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_rename(oldpath, newpath);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_rename);
|
|
|
|
|
|
|
|
int mars_chmod(const char *path, mode_t mode)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_chmod(path, mode);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_chmod);
|
|
|
|
|
|
|
|
int mars_lchown(const char *path, uid_t uid)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
status = sys_lchown(path, uid, 0);
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_lchown);
|
|
|
|
|
2012-01-23 17:40:15 +00:00
|
|
|
loff_t _compute_space(struct kstatfs *kstatfs, loff_t raw_val)
|
|
|
|
{
|
|
|
|
int fsize = kstatfs->f_frsize;
|
|
|
|
if (fsize <= 0)
|
|
|
|
fsize = kstatfs->f_bsize;
|
|
|
|
|
|
|
|
MARS_INF("fsize = %d raw_val = %lld\n", fsize, raw_val);
|
|
|
|
// illegal values? cannot do anything....
|
|
|
|
if (fsize <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// prevent intermediate integer overflows
|
|
|
|
if (fsize <= 1024)
|
|
|
|
return raw_val / (1024 / fsize);
|
|
|
|
|
|
|
|
return raw_val * (fsize / 1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mars_remaining_space(const char *fspath, loff_t *total, loff_t *remaining)
|
2011-11-08 12:33:13 +00:00
|
|
|
{
|
|
|
|
struct path path = {};
|
|
|
|
struct kstatfs kstatfs = {};
|
2013-04-08 09:20:56 +00:00
|
|
|
mm_segment_t oldfs;
|
2012-01-23 17:40:15 +00:00
|
|
|
int res;
|
|
|
|
|
|
|
|
*total = *remaining = 0;
|
2011-11-08 12:33:13 +00:00
|
|
|
|
2013-04-08 09:20:56 +00:00
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
|
2011-11-08 12:33:13 +00:00
|
|
|
res = user_path_at(AT_FDCWD, fspath, 0, &path);
|
2013-04-08 09:20:56 +00:00
|
|
|
|
|
|
|
set_fs(oldfs);
|
|
|
|
|
2011-11-08 12:33:13 +00:00
|
|
|
if (unlikely(res < 0)) {
|
2013-04-08 09:20:56 +00:00
|
|
|
MARS_ERR("cannot get fspath '%s', err = %d\n\n", fspath, res);
|
2011-11-08 12:33:13 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (unlikely(!path.dentry)) {
|
|
|
|
MARS_ERR("bad dentry for fspath '%s'\n", fspath);
|
|
|
|
res = -ENXIO;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-09-18 06:13:33 +00:00
|
|
|
#ifdef ST_RDONLY
|
|
|
|
res = vfs_statfs(&path, &kstatfs);
|
|
|
|
#else
|
2011-11-08 12:33:13 +00:00
|
|
|
res = vfs_statfs(path.dentry, &kstatfs);
|
2012-09-18 06:13:33 +00:00
|
|
|
#endif
|
2011-11-08 12:33:13 +00:00
|
|
|
if (unlikely(res < 0)) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-01-23 17:40:15 +00:00
|
|
|
*total = _compute_space(&kstatfs, kstatfs.f_blocks);
|
2014-03-11 09:53:09 +00:00
|
|
|
*remaining = _compute_space(&kstatfs, kstatfs.f_bavail);
|
2011-11-08 12:33:13 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
path_put(&path);
|
2012-01-23 17:40:15 +00:00
|
|
|
err: ;
|
2011-11-08 12:33:13 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_remaining_space);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
// thread binding
|
|
|
|
|
|
|
|
void bind_to_dent(struct mars_dent *dent, struct say_channel **ch)
|
|
|
|
{
|
|
|
|
if (!dent) {
|
|
|
|
if (*ch) {
|
|
|
|
remove_binding_from(*ch, current);
|
|
|
|
*ch = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Memoize the channel. This is executed only once for each dent.
|
|
|
|
if (unlikely(!dent->d_say_channel)) {
|
|
|
|
struct mars_dent *test = dent->d_parent;
|
|
|
|
for (;;) {
|
|
|
|
if (!test) {
|
|
|
|
dent->d_say_channel = default_channel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (test->d_use_channel && test->d_path) {
|
2013-04-08 09:44:09 +00:00
|
|
|
dent->d_say_channel = make_channel(test->d_path, true);
|
2012-11-14 09:46:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
test = test->d_parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dent->d_say_channel != *ch) {
|
|
|
|
if (*ch)
|
|
|
|
remove_binding_from(*ch, current);
|
|
|
|
*ch = dent->d_say_channel;
|
|
|
|
if (*ch)
|
|
|
|
bind_to_channel(*ch, current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(bind_to_dent);
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
// infrastructure
|
|
|
|
|
|
|
|
struct mars_global *mars_global = NULL;
|
|
|
|
EXPORT_SYMBOL_GPL(mars_global);
|
|
|
|
|
|
|
|
static
|
|
|
|
void __mars_trigger(void)
|
|
|
|
{
|
|
|
|
if (mars_global) {
|
|
|
|
mars_global->main_trigger = true;
|
2012-01-17 10:48:56 +00:00
|
|
|
wake_up_interruptible_all(&mars_global->main_event);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-09 09:40:02 +00:00
|
|
|
bool mars_check_inputs(struct mars_brick *brick)
|
|
|
|
{
|
|
|
|
int max_inputs;
|
|
|
|
int i;
|
|
|
|
if (likely(brick->type)) {
|
|
|
|
max_inputs = brick->type->max_inputs;
|
|
|
|
} else {
|
|
|
|
MARS_ERR("uninitialized brick '%s' '%s'\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (i = 0; i < max_inputs; i++) {
|
|
|
|
struct mars_input *input = brick->inputs[i];
|
|
|
|
struct mars_output *prev_output;
|
|
|
|
struct mars_brick *prev_brick;
|
|
|
|
if (!input)
|
|
|
|
continue;
|
|
|
|
prev_output = input->connect;
|
|
|
|
if (!prev_output)
|
|
|
|
continue;
|
|
|
|
prev_brick = prev_output->brick;
|
|
|
|
CHECK_PTR(prev_brick, done);
|
|
|
|
if (prev_brick->power.led_on)
|
|
|
|
continue;
|
|
|
|
done:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_check_inputs);
|
|
|
|
|
|
|
|
bool mars_check_outputs(struct mars_brick *brick)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < brick->type->max_outputs; i++) {
|
|
|
|
struct mars_output *output = brick->outputs[i];
|
|
|
|
if (!output || !output->nr_connected)
|
|
|
|
continue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_check_outputs);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
int mars_power_button(struct mars_brick *brick, bool val, bool force_off)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
bool oldval = brick->power.button;
|
|
|
|
|
|
|
|
if (force_off && !val)
|
|
|
|
brick->power.force_off = true;
|
|
|
|
|
|
|
|
if (brick->power.force_off)
|
|
|
|
val = false;
|
|
|
|
|
|
|
|
if (val != oldval) {
|
2013-05-09 09:40:02 +00:00
|
|
|
// check whether switching is possible
|
|
|
|
status = -EINVAL;
|
|
|
|
if (val) { // check all inputs
|
|
|
|
if (unlikely(mars_check_inputs(brick))) {
|
|
|
|
MARS_ERR("CANNOT SWITCH ON: brick '%s' '%s' has a turned-off predecessor\n", brick->brick_name, brick->brick_path);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
} else { // check all outputs
|
|
|
|
if (unlikely(mars_check_outputs(brick))) {
|
|
|
|
/* For now, we have a strong rule:
|
|
|
|
* Switching off is only allowed when no successor brick
|
|
|
|
* exists at all. This could be relaxed to checking
|
|
|
|
* whether all successor bricks are actually switched off.
|
|
|
|
* Probabĺy it is a good idea to retain the stronger rule
|
|
|
|
* as long as nobody needs the relaxed one.
|
|
|
|
*/
|
|
|
|
MARS_ERR("CANNOT SWITCH OFF: brick '%s' '%s' has a successor\n", brick->brick_name, brick->brick_path);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MARS_DBG("brick '%s' '%s' type '%s' power button %d -> %d\n", brick->brick_name, brick->brick_path, brick->type->type_name, oldval, val);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
set_button(&brick->power, val, false);
|
2013-09-24 07:53:54 +00:00
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2013-09-24 07:53:54 +00:00
|
|
|
if (unlikely(!brick->ops)) {
|
|
|
|
MARS_ERR("brick '%s' '%s' has no brick_switch() method\n", brick->brick_name, brick->brick_path);
|
|
|
|
status = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2013-09-24 07:53:54 +00:00
|
|
|
/* Always call the switch function, even if nothing changes.
|
|
|
|
* The implementations must be idempotent.
|
|
|
|
* They may exploit the regular calls for some maintenance operations
|
|
|
|
* (e.g. changing disk capacity etc).
|
|
|
|
*/
|
|
|
|
status = brick->ops->brick_switch(brick);
|
|
|
|
|
|
|
|
if (val != oldval) {
|
2011-07-22 10:43:40 +00:00
|
|
|
mars_trigger();
|
|
|
|
}
|
2013-09-24 07:53:54 +00:00
|
|
|
|
2013-05-09 09:40:02 +00:00
|
|
|
done:
|
2011-07-22 10:43:40 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_power_button);
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// strategy layer
|
|
|
|
|
|
|
|
|
|
|
|
struct mars_cookie {
|
|
|
|
struct mars_global *global;
|
2011-10-24 10:54:15 +00:00
|
|
|
mars_dent_checker_fn checker;
|
2011-07-22 10:43:40 +00:00
|
|
|
char *path;
|
|
|
|
struct mars_dent *parent;
|
|
|
|
int allocsize;
|
|
|
|
int depth;
|
2014-06-18 04:56:00 +00:00
|
|
|
bool hit;
|
2011-07-22 10:43:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static
|
|
|
|
int get_inode(char *newpath, struct mars_dent *dent)
|
|
|
|
{
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status;
|
|
|
|
struct kstat tmp = {};
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
|
|
|
|
status = vfs_lstat(newpath, &tmp);
|
|
|
|
if (status < 0) {
|
2012-12-11 11:32:26 +00:00
|
|
|
MARS_WRN("cannot stat '%s', status = %d\n", newpath, status);
|
2011-07-22 10:43:40 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&dent->old_stat, &dent->new_stat, sizeof(dent->old_stat));
|
|
|
|
memcpy(&dent->new_stat, &tmp, sizeof(dent->new_stat));
|
|
|
|
|
|
|
|
if (S_ISLNK(dent->new_stat.mode)) {
|
|
|
|
struct path path = {};
|
|
|
|
int len = dent->new_stat.size;
|
|
|
|
struct inode *inode;
|
|
|
|
char *link;
|
|
|
|
|
|
|
|
if (unlikely(len <= 0)) {
|
|
|
|
MARS_ERR("symlink '%s' bad len = %d\n", newpath, len);
|
|
|
|
status = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
2014-03-24 07:03:41 +00:00
|
|
|
len = _length_paranoia(len, __LINE__);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
status = user_path_at(AT_FDCWD, newpath, 0, &path);
|
|
|
|
if (unlikely(status < 0)) {
|
2013-01-08 05:11:53 +00:00
|
|
|
MARS_WRN("cannot read link '%s'\n", newpath);
|
2011-07-22 10:43:40 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
inode = path.dentry->d_inode;
|
|
|
|
|
|
|
|
status = -ENOMEM;
|
2014-03-24 07:03:41 +00:00
|
|
|
link = brick_string_alloc(len + 2);
|
|
|
|
MARS_IO("len = %d\n", len);
|
|
|
|
status = inode->i_op->readlink(path.dentry, link, len + 1);
|
|
|
|
link[len] = '\0';
|
|
|
|
if (status < 0 ||
|
2015-12-17 10:04:33 +00:00
|
|
|
(dent->new_link && !strcmp(dent->new_link, link))) {
|
2014-03-24 07:03:41 +00:00
|
|
|
brick_string_free(link);
|
|
|
|
} else {
|
|
|
|
MARS_IO("symlink '%s' -> '%s' (%s) status = %d\n", newpath, link, dent->new_link ? dent->new_link : "", status);
|
|
|
|
brick_string_free(dent->old_link);
|
|
|
|
dent->old_link = dent->new_link;
|
|
|
|
dent->new_link = link;
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
path_put(&path);
|
2014-03-30 06:18:47 +00:00
|
|
|
} else if (S_ISREG(dent->new_stat.mode) && dent->d_name && !strncmp(dent->d_name, "log-", 4)) {
|
|
|
|
loff_t min = dent->new_stat.size;
|
|
|
|
loff_t max = 0;
|
|
|
|
|
|
|
|
dent->d_corr_A = 0;
|
|
|
|
dent->d_corr_B = 0;
|
|
|
|
mf_get_any_dirty(newpath, &min, &max, 0, 2);
|
|
|
|
if (min < dent->new_stat.size) {
|
|
|
|
MARS_DBG("file '%s' A size=%lld min=%lld max=%lld\n", newpath, dent->new_stat.size, min, max);
|
|
|
|
dent->d_corr_A = min;
|
|
|
|
}
|
|
|
|
mf_get_any_dirty(newpath, &min, &max, 0, 3);
|
|
|
|
if (min < dent->new_stat.size) {
|
|
|
|
MARS_DBG("file '%s' B size=%lld min=%lld max=%lld\n", newpath, dent->new_stat.size, min, max);
|
|
|
|
dent->d_corr_B = min;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dent->new_link)
|
|
|
|
MARS_IO("symlink '%s'\n", dent->new_link);
|
|
|
|
|
|
|
|
done:
|
|
|
|
set_fs(oldfs);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-04-08 08:03:06 +00:00
|
|
|
static
|
|
|
|
int dent_compare(struct mars_dent *a, struct mars_dent *b)
|
|
|
|
{
|
|
|
|
if (a->d_class < b->d_class) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (a->d_class > b->d_class) {
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
if (a->d_serial < b->d_serial) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (a->d_serial > b->d_serial) {
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
return strcmp(a->d_path, b->d_path);
|
|
|
|
}
|
|
|
|
|
2014-06-04 07:10:50 +00:00
|
|
|
// remove_this
|
|
|
|
#ifndef HAS_VFS_READDIR
|
|
|
|
// end_remove_this
|
|
|
|
struct mars_dir_context {
|
|
|
|
struct dir_context ctx;
|
|
|
|
struct mars_cookie *cookie;
|
|
|
|
};
|
|
|
|
// remove_this
|
|
|
|
#endif
|
|
|
|
// end_remove_this
|
|
|
|
|
2015-07-06 10:41:40 +00:00
|
|
|
#ifdef __HAS_NEW_FILLDIR_T
|
|
|
|
int mars_filler(struct dir_context *__buf, const char *name, int namlen, loff_t offset,
|
|
|
|
u64 ino, unsigned int d_type)
|
|
|
|
#else
|
2011-07-22 10:43:40 +00:00
|
|
|
static
|
|
|
|
int mars_filler(void *__buf, const char *name, int namlen, loff_t offset,
|
|
|
|
u64 ino, unsigned int d_type)
|
2015-07-06 10:41:40 +00:00
|
|
|
#endif
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
2014-06-04 07:10:50 +00:00
|
|
|
// remove_this
|
|
|
|
#ifdef HAS_VFS_READDIR
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_cookie *cookie = __buf;
|
2014-06-04 07:10:50 +00:00
|
|
|
#else
|
|
|
|
// end_remove_this
|
2015-07-06 10:41:40 +00:00
|
|
|
struct mars_dir_context *buf = (void *)__buf;
|
2014-06-04 07:10:50 +00:00
|
|
|
struct mars_cookie *cookie = buf->cookie;
|
|
|
|
// remove_this
|
|
|
|
#endif
|
|
|
|
// end_remove_this
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_global *global = cookie->global;
|
|
|
|
struct list_head *anchor = &global->dent_anchor;
|
2014-04-08 08:03:06 +00:00
|
|
|
struct list_head *start = anchor;
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_dent *dent;
|
|
|
|
struct list_head *tmp;
|
|
|
|
char *newpath;
|
|
|
|
int prefix = 0;
|
|
|
|
int pathlen;
|
|
|
|
int class;
|
|
|
|
int serial = 0;
|
2012-11-14 09:46:36 +00:00
|
|
|
bool use_channel = false;
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
MARS_IO("ino = %llu len = %d offset = %lld type = %u\n", ino, namlen, offset, d_type);
|
|
|
|
|
2014-06-18 04:56:00 +00:00
|
|
|
|
|
|
|
cookie->hit = true;
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
if (name[0] == '.') {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
class = cookie->checker(cookie->parent, name, namlen, d_type, &prefix, &serial, &use_channel);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (class < 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-02-20 11:55:36 +00:00
|
|
|
pathlen = strlen(cookie->path);
|
2011-10-12 14:57:53 +00:00
|
|
|
newpath = brick_string_alloc(pathlen + namlen + 2);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (unlikely(!newpath))
|
|
|
|
goto err_mem0;
|
|
|
|
memcpy(newpath, cookie->path, pathlen);
|
|
|
|
newpath[pathlen++] = '/';
|
|
|
|
memcpy(newpath + pathlen, name, namlen);
|
|
|
|
pathlen += namlen;
|
|
|
|
newpath[pathlen] = '\0';
|
|
|
|
|
|
|
|
MARS_IO("path = '%s'\n", newpath);
|
|
|
|
|
2014-04-08 08:03:06 +00:00
|
|
|
dent = brick_zmem_alloc(cookie->allocsize);
|
|
|
|
if (unlikely(!dent))
|
|
|
|
goto err_mem1;
|
|
|
|
|
|
|
|
dent->d_class = class;
|
|
|
|
dent->d_serial = serial;
|
|
|
|
dent->d_path = newpath;
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
for (tmp = anchor->next; tmp != anchor; tmp = tmp->next) {
|
2014-04-08 08:03:06 +00:00
|
|
|
struct mars_dent *test = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
int cmp = dent_compare(test, dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (!cmp) {
|
2014-04-08 08:03:06 +00:00
|
|
|
brick_mem_free(dent);
|
|
|
|
dent = test;
|
2011-07-22 10:43:40 +00:00
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
// keep the list sorted. find the next smallest member.
|
2014-04-08 08:03:06 +00:00
|
|
|
if (cmp > 0)
|
|
|
|
break;
|
|
|
|
start = tmp;
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
|
2011-08-25 10:16:32 +00:00
|
|
|
dent->d_name = brick_string_alloc(namlen + 1);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (unlikely(!dent->d_name))
|
|
|
|
goto err_mem2;
|
|
|
|
memcpy(dent->d_name, name, namlen);
|
|
|
|
dent->d_name[namlen] = '\0';
|
2011-08-25 10:16:32 +00:00
|
|
|
dent->d_rest = brick_strdup(dent->d_name + prefix);
|
|
|
|
if (unlikely(!dent->d_rest))
|
|
|
|
goto err_mem3;
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
newpath = NULL;
|
|
|
|
|
2014-04-07 12:56:03 +00:00
|
|
|
INIT_LIST_HEAD(&dent->dent_link);
|
2011-07-22 10:43:40 +00:00
|
|
|
INIT_LIST_HEAD(&dent->brick_list);
|
|
|
|
|
2014-04-08 08:03:06 +00:00
|
|
|
list_add(&dent->dent_link, start);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
found:
|
|
|
|
dent->d_type = d_type;
|
|
|
|
dent->d_class = class;
|
|
|
|
dent->d_serial = serial;
|
2014-02-20 15:59:37 +00:00
|
|
|
if (dent->d_parent)
|
|
|
|
dent->d_parent->d_child_count--;
|
2011-07-22 10:43:40 +00:00
|
|
|
dent->d_parent = cookie->parent;
|
2014-02-20 15:59:37 +00:00
|
|
|
if (dent->d_parent)
|
|
|
|
dent->d_parent->d_child_count++;
|
2011-07-22 10:43:40 +00:00
|
|
|
dent->d_depth = cookie->depth;
|
|
|
|
dent->d_global = global;
|
|
|
|
dent->d_killme = false;
|
2012-11-14 09:46:36 +00:00
|
|
|
dent->d_use_channel = use_channel;
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(newpath);
|
2011-07-22 10:43:40 +00:00
|
|
|
return 0;
|
|
|
|
|
2011-08-25 10:16:32 +00:00
|
|
|
err_mem3:
|
|
|
|
brick_mem_free(dent->d_name);
|
2011-07-22 10:43:40 +00:00
|
|
|
err_mem2:
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_mem_free(dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
err_mem1:
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(newpath);
|
2011-07-22 10:43:40 +00:00
|
|
|
err_mem0:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _mars_readdir(struct mars_cookie *cookie)
|
|
|
|
{
|
|
|
|
struct file *f;
|
2013-01-17 16:24:53 +00:00
|
|
|
struct address_space *mapping;
|
2011-07-22 10:43:40 +00:00
|
|
|
mm_segment_t oldfs;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
f = filp_open(cookie->path, O_DIRECTORY | O_RDONLY, 0);
|
|
|
|
set_fs(oldfs);
|
|
|
|
if (unlikely(IS_ERR(f))) {
|
|
|
|
return PTR_ERR(f);
|
|
|
|
}
|
2013-01-17 16:24:53 +00:00
|
|
|
if ((mapping = f->f_mapping)) {
|
|
|
|
mapping_set_gfp_mask(mapping, mapping_gfp_mask(mapping) & ~(__GFP_IO | __GFP_FS));
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
for (;;) {
|
2014-06-04 07:10:50 +00:00
|
|
|
// remove_this
|
|
|
|
#ifdef HAS_VFS_READDIR
|
2014-06-18 04:56:00 +00:00
|
|
|
cookie->hit = false;
|
2011-07-22 10:43:40 +00:00
|
|
|
status = vfs_readdir(f, mars_filler, cookie);
|
2014-06-04 07:10:50 +00:00
|
|
|
#else
|
|
|
|
// end_remove_this
|
|
|
|
struct mars_dir_context buf = {
|
|
|
|
.ctx.actor = mars_filler,
|
|
|
|
.cookie = cookie,
|
|
|
|
};
|
|
|
|
|
|
|
|
cookie->hit = false;
|
|
|
|
status = iterate_dir(f, &buf.ctx);
|
|
|
|
// remove_this
|
|
|
|
#endif
|
|
|
|
// end_remove_this
|
2014-06-18 04:56:00 +00:00
|
|
|
if (!cookie->hit)
|
2011-07-22 10:43:40 +00:00
|
|
|
break;
|
2014-06-18 04:56:00 +00:00
|
|
|
if (unlikely(status < 0)) {
|
|
|
|
MARS_ERR("readdir() on path='%s' status=%d\n", cookie->path, status);
|
|
|
|
break;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
filp_close(f, NULL);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2011-10-24 10:54:15 +00:00
|
|
|
int mars_dent_work(struct mars_global *global, char *dirname, int allocsize, mars_dent_checker_fn checker, mars_dent_worker_fn worker, void *buf, int maxdepth)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
|
|
|
static int version = 0;
|
|
|
|
struct mars_cookie cookie = {
|
|
|
|
.global = global,
|
|
|
|
.checker = checker,
|
|
|
|
.path = dirname,
|
2012-08-06 07:38:21 +00:00
|
|
|
.parent = NULL,
|
2011-07-22 10:43:40 +00:00
|
|
|
.allocsize = allocsize,
|
|
|
|
.depth = 0,
|
|
|
|
};
|
2012-11-14 09:46:36 +00:00
|
|
|
struct say_channel *say_channel = NULL;
|
2011-07-22 10:43:40 +00:00
|
|
|
struct list_head *tmp;
|
|
|
|
struct list_head *next;
|
|
|
|
int rounds = 0;
|
|
|
|
int status;
|
|
|
|
int total_status = 0;
|
|
|
|
bool found_dir;
|
|
|
|
|
|
|
|
/* Initialize the flat dent list
|
|
|
|
*/
|
|
|
|
version++;
|
2011-10-10 13:03:10 +00:00
|
|
|
global->global_version = version;
|
2011-07-22 10:43:40 +00:00
|
|
|
total_status = _mars_readdir(&cookie);
|
|
|
|
|
|
|
|
if (total_status || !worker) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
down_write(&global->dent_mutex);
|
|
|
|
|
|
|
|
restart:
|
2012-01-17 10:48:56 +00:00
|
|
|
MARS_IO("at restart\n");
|
2011-07-22 10:43:40 +00:00
|
|
|
found_dir = false;
|
|
|
|
|
|
|
|
/* First, get all the inode information in a separate pass
|
|
|
|
* before starting work.
|
|
|
|
* The separate pass is necessary because some dents may
|
|
|
|
* forward-reference other dents, and it would be a pity if
|
|
|
|
* some inodes were not available or were outdated.
|
|
|
|
*/
|
|
|
|
for (tmp = global->dent_anchor.next; tmp != &global->dent_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
// treat any member only once during this invocation
|
|
|
|
if (dent->d_version == version)
|
|
|
|
continue;
|
|
|
|
dent->d_version = version;
|
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(dent, &say_channel);
|
|
|
|
|
2012-01-17 10:48:56 +00:00
|
|
|
//MARS_IO("reading inode '%s'\n", dent->d_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
status = get_inode(dent->d_path, dent);
|
|
|
|
total_status |= status;
|
|
|
|
|
2015-01-19 12:33:00 +00:00
|
|
|
// mark gone dents for removal
|
|
|
|
if (unlikely(status < 0) && list_empty(&dent->brick_list))
|
|
|
|
dent->d_killme = true;
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
// recurse into subdirectories by inserting into the flat list
|
|
|
|
if (S_ISDIR(dent->new_stat.mode) && dent->d_depth <= maxdepth) {
|
|
|
|
struct mars_cookie sub_cookie = {
|
|
|
|
.global = global,
|
|
|
|
.checker = checker,
|
|
|
|
.path = dent->d_path,
|
|
|
|
.allocsize = allocsize,
|
|
|
|
.parent = dent,
|
|
|
|
.depth = dent->d_depth + 1,
|
|
|
|
};
|
|
|
|
found_dir = true;
|
|
|
|
status = _mars_readdir(&sub_cookie);
|
|
|
|
total_status |= status;
|
|
|
|
if (status < 0) {
|
2012-07-17 15:51:40 +00:00
|
|
|
MARS_INF("forward: status %d on '%s'\n", status, dent->d_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(NULL, &say_channel);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
if (found_dir && ++rounds < 10) {
|
2012-12-14 11:44:54 +00:00
|
|
|
brick_yield();
|
2011-07-22 10:43:40 +00:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
2014-03-13 14:54:52 +00:00
|
|
|
up_write(&global->dent_mutex);
|
|
|
|
|
2011-10-24 10:54:15 +00:00
|
|
|
/* Preparation pass.
|
|
|
|
* Here is a chance to mark some dents for removal
|
|
|
|
* (or other types of non-destructive operations)
|
|
|
|
*/
|
2014-03-13 14:54:52 +00:00
|
|
|
down_read(&global->dent_mutex);
|
2012-01-17 10:48:56 +00:00
|
|
|
MARS_IO("prep pass\n");
|
2011-10-24 10:54:15 +00:00
|
|
|
for (tmp = global->dent_anchor.next, next = tmp->next; tmp != &global->dent_anchor; tmp = next, next = next->next) {
|
|
|
|
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2014-03-13 14:54:52 +00:00
|
|
|
up_read(&global->dent_mutex);
|
|
|
|
|
2012-12-14 11:44:54 +00:00
|
|
|
brick_yield();
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(dent, &say_channel);
|
|
|
|
|
2012-01-17 10:48:56 +00:00
|
|
|
//MARS_IO("forward prepare '%s'\n", dent->d_path);
|
2011-10-24 10:54:15 +00:00
|
|
|
status = worker(buf, dent, true, false);
|
2012-01-17 10:48:56 +00:00
|
|
|
if (status) {
|
|
|
|
//MARS_IO("forward treat '%s' status = %d\n", dent->d_path, status);
|
|
|
|
}
|
2014-03-13 14:54:52 +00:00
|
|
|
down_read(&global->dent_mutex);
|
2011-10-24 10:54:15 +00:00
|
|
|
total_status |= status;
|
|
|
|
}
|
2014-03-13 14:54:52 +00:00
|
|
|
up_read(&global->dent_mutex);
|
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(NULL, &say_channel);
|
2011-10-24 10:54:15 +00:00
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
/* Remove all dents marked for removal.
|
|
|
|
*/
|
2014-03-13 14:54:52 +00:00
|
|
|
down_write(&global->dent_mutex);
|
2012-01-17 10:48:56 +00:00
|
|
|
MARS_IO("removal pass\n");
|
2011-07-22 10:43:40 +00:00
|
|
|
for (tmp = global->dent_anchor.next, next = tmp->next; tmp != &global->dent_anchor; tmp = next, next = next->next) {
|
|
|
|
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
if (!dent->d_killme)
|
|
|
|
continue;
|
2012-11-14 09:46:36 +00:00
|
|
|
|
|
|
|
bind_to_dent(dent, &say_channel);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
MARS_DBG("killing dent '%s'\n", dent->d_path);
|
|
|
|
list_del_init(tmp);
|
2011-09-27 08:46:14 +00:00
|
|
|
mars_free_dent(dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
up_write(&global->dent_mutex);
|
|
|
|
|
2014-03-13 14:54:52 +00:00
|
|
|
bind_to_dent(NULL, &say_channel);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
/* Forward pass.
|
|
|
|
*/
|
|
|
|
down_read(&global->dent_mutex);
|
2012-01-17 10:48:56 +00:00
|
|
|
MARS_IO("forward pass\n");
|
2011-07-22 10:43:40 +00:00
|
|
|
for (tmp = global->dent_anchor.next, next = tmp->next; tmp != &global->dent_anchor; tmp = next, next = next->next) {
|
|
|
|
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
|
2011-09-27 08:46:14 +00:00
|
|
|
up_read(&global->dent_mutex);
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2012-12-14 11:44:54 +00:00
|
|
|
brick_yield();
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(dent, &say_channel);
|
|
|
|
|
2012-01-17 10:48:56 +00:00
|
|
|
//MARS_IO("forward treat '%s'\n", dent->d_path);
|
2011-10-24 10:54:15 +00:00
|
|
|
status = worker(buf, dent, false, false);
|
2012-01-17 10:48:56 +00:00
|
|
|
if (status) {
|
|
|
|
//MARS_IO("forward treat '%s' status = %d\n", dent->d_path, status);
|
|
|
|
}
|
2011-09-27 08:46:14 +00:00
|
|
|
down_read(&global->dent_mutex);
|
2011-07-22 10:43:40 +00:00
|
|
|
total_status |= status;
|
|
|
|
}
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(NULL, &say_channel);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
/* Backward pass.
|
|
|
|
*/
|
2014-03-13 14:54:52 +00:00
|
|
|
MARS_IO("backward pass\n");
|
2011-09-27 08:46:14 +00:00
|
|
|
for (tmp = global->dent_anchor.prev, next = tmp->prev; tmp != &global->dent_anchor; tmp = next, next = next->prev) {
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
|
2011-09-27 08:46:14 +00:00
|
|
|
up_read(&global->dent_mutex);
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2012-12-14 11:44:54 +00:00
|
|
|
brick_yield();
|
2012-11-13 11:50:48 +00:00
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(dent, &say_channel);
|
|
|
|
|
2012-01-17 10:48:56 +00:00
|
|
|
//MARS_IO("backward treat '%s'\n", dent->d_path);
|
2011-10-24 10:54:15 +00:00
|
|
|
status = worker(buf, dent, false, true);
|
2012-01-17 10:48:56 +00:00
|
|
|
if (status) {
|
|
|
|
//MARS_IO("backward treat '%s' status = %d\n", dent->d_path, status);
|
|
|
|
}
|
2011-09-27 08:46:14 +00:00
|
|
|
down_read(&global->dent_mutex);
|
2011-07-22 10:43:40 +00:00
|
|
|
total_status |= status;
|
|
|
|
if (status < 0) {
|
2012-07-17 15:51:40 +00:00
|
|
|
MARS_INF("backwards: status %d on '%s'\n", status, dent->d_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
up_read(&global->dent_mutex);
|
2014-03-13 14:54:52 +00:00
|
|
|
|
2012-11-14 09:46:36 +00:00
|
|
|
bind_to_dent(NULL, &say_channel);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
done:
|
2011-09-27 08:46:14 +00:00
|
|
|
MARS_IO("total_status = %d\n", total_status);
|
2011-07-22 10:43:40 +00:00
|
|
|
return total_status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_dent_work);
|
|
|
|
|
|
|
|
struct mars_dent *_mars_find_dent(struct mars_global *global, const char *path)
|
|
|
|
{
|
|
|
|
struct mars_dent *res = NULL;
|
|
|
|
struct list_head *tmp;
|
|
|
|
|
|
|
|
if (!rwsem_is_locked(&global->dent_mutex)) {
|
|
|
|
MARS_ERR("dent_mutex not held!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (tmp = global->dent_anchor.next; tmp != &global->dent_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_dent *tmp_dent = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
if (!strcmp(tmp_dent->d_path, path)) {
|
|
|
|
res = tmp_dent;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2011-10-24 10:54:15 +00:00
|
|
|
EXPORT_SYMBOL_GPL(_mars_find_dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
struct mars_dent *mars_find_dent(struct mars_global *global, const char *path)
|
|
|
|
{
|
|
|
|
struct mars_dent *res;
|
2011-09-23 14:08:17 +00:00
|
|
|
if (!global)
|
|
|
|
return NULL;
|
|
|
|
down_read(&global->dent_mutex);
|
2011-07-22 10:43:40 +00:00
|
|
|
res = _mars_find_dent(global, path);
|
2011-09-23 14:08:17 +00:00
|
|
|
up_read(&global->dent_mutex);
|
2011-07-22 10:43:40 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_find_dent);
|
|
|
|
|
2011-09-27 08:46:14 +00:00
|
|
|
int mars_find_dent_all(struct mars_global *global, char *prefix, struct mars_dent ***table)
|
|
|
|
{
|
|
|
|
int max = 1024; // provisionary
|
|
|
|
int count = 0;
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct mars_dent **res = brick_zmem_alloc(max * sizeof(void*));
|
|
|
|
int prefix_len = strlen(prefix);
|
|
|
|
|
|
|
|
*table = res;
|
|
|
|
if (unlikely(!res || !global))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
down_read(&global->dent_mutex);
|
|
|
|
for (tmp = global->dent_anchor.next; tmp != &global->dent_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_dent *tmp_dent = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
int this_len;
|
|
|
|
if (!tmp_dent->d_path) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
this_len = strlen(tmp_dent->d_path);
|
|
|
|
if (this_len < prefix_len || strncmp(tmp_dent->d_path, prefix, prefix_len)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
res[count++] = tmp_dent;
|
|
|
|
if (count >= max)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
up_read(&global->dent_mutex);
|
|
|
|
|
|
|
|
done:
|
|
|
|
return count;
|
|
|
|
}
|
2011-10-04 11:45:09 +00:00
|
|
|
EXPORT_SYMBOL_GPL(mars_find_dent_all);
|
2011-09-27 08:46:14 +00:00
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
void mars_kill_dent(struct mars_dent *dent)
|
|
|
|
{
|
|
|
|
dent->d_killme = true;
|
2011-08-31 11:42:04 +00:00
|
|
|
mars_kill_brick_all(NULL, &dent->brick_list, true);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_kill_dent);
|
|
|
|
|
|
|
|
void mars_free_dent(struct mars_dent *dent)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-10-10 13:03:10 +00:00
|
|
|
MARS_IO("%p path='%s'\n", dent, dent->d_path);
|
2011-09-27 08:46:14 +00:00
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
mars_kill_dent(dent);
|
|
|
|
|
|
|
|
CHECK_HEAD_EMPTY(&dent->dent_link);
|
|
|
|
CHECK_HEAD_EMPTY(&dent->brick_list);
|
|
|
|
|
|
|
|
for (i = 0; i < MARS_ARGV_MAX; i++) {
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(dent->d_argv[i]);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(dent->d_args);
|
2011-08-19 10:49:16 +00:00
|
|
|
brick_string_free(dent->d_name);
|
2011-08-25 10:16:32 +00:00
|
|
|
brick_string_free(dent->d_rest);
|
2011-08-19 10:49:16 +00:00
|
|
|
brick_string_free(dent->d_path);
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(dent->old_link);
|
|
|
|
brick_string_free(dent->new_link);
|
2014-02-20 15:59:37 +00:00
|
|
|
if (likely(dent->d_parent)) {
|
|
|
|
dent->d_parent->d_child_count--;
|
|
|
|
}
|
2012-11-19 10:55:38 +00:00
|
|
|
if (dent->d_private_destruct) {
|
|
|
|
dent->d_private_destruct(dent->d_private);
|
|
|
|
}
|
2011-08-31 11:42:04 +00:00
|
|
|
brick_mem_free(dent->d_private);
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_mem_free(dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_free_dent);
|
|
|
|
|
2011-09-27 08:46:14 +00:00
|
|
|
void mars_free_dent_all(struct mars_global *global, struct list_head *anchor)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
2011-09-27 08:46:14 +00:00
|
|
|
LIST_HEAD(tmp_list);
|
|
|
|
if (global)
|
|
|
|
down_write(&global->dent_mutex);
|
|
|
|
list_replace_init(anchor, &tmp_list);
|
|
|
|
if (global)
|
|
|
|
up_write(&global->dent_mutex);
|
|
|
|
MARS_DBG("is_empty=%d\n", list_empty(&tmp_list));
|
|
|
|
while (!list_empty(&tmp_list)) {
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_dent *dent;
|
2011-09-27 08:46:14 +00:00
|
|
|
dent = container_of(tmp_list.prev, struct mars_dent, dent_link);
|
2011-08-19 10:49:16 +00:00
|
|
|
list_del_init(&dent->dent_link);
|
2011-09-27 08:46:14 +00:00
|
|
|
MARS_IO("freeing dent %p\n", dent);
|
2011-07-22 10:43:40 +00:00
|
|
|
mars_free_dent(dent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_free_dent_all);
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// low-level brick instantiation
|
|
|
|
|
2016-12-31 21:57:07 +00:00
|
|
|
int mars_connect(struct mars_input *a, struct mars_output *b)
|
|
|
|
{
|
|
|
|
struct mars_brick *a_brick = a->brick;
|
|
|
|
struct mars_global *a_global = a_brick->global;
|
|
|
|
struct mars_brick *b_brick = b->brick;
|
|
|
|
struct mars_global *b_global = b_brick->global;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (a_global)
|
|
|
|
down_write(&a_global->brick_mutex);
|
|
|
|
if (b_global && b_global != a_global)
|
|
|
|
down_write(&b_global->brick_mutex);
|
|
|
|
|
|
|
|
status = generic_connect((void*)a, (void*)b);
|
|
|
|
|
|
|
|
if (b_global && b_global != a_global)
|
|
|
|
up_write(&b_global->brick_mutex);
|
|
|
|
if (a_global)
|
|
|
|
up_write(&a_global->brick_mutex);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mars_disconnect(struct mars_input *a)
|
|
|
|
{
|
|
|
|
struct mars_brick *a_brick = a->brick;
|
|
|
|
struct mars_global *a_global = a_brick->global;
|
|
|
|
struct mars_output *b;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (a_global)
|
|
|
|
down_write(&a_global->brick_mutex);
|
|
|
|
|
|
|
|
b = a->connect;
|
|
|
|
if (b) {
|
|
|
|
struct mars_brick *b_brick = b->brick;
|
|
|
|
struct mars_global *b_global = b_brick->global;
|
|
|
|
|
|
|
|
if (b_global && b_global != a_global)
|
|
|
|
down_write(&b_global->brick_mutex);
|
|
|
|
|
|
|
|
status = generic_disconnect((void*)a);
|
|
|
|
|
|
|
|
if (b_global && b_global != a_global)
|
|
|
|
up_write(&b_global->brick_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a_global)
|
|
|
|
up_write(&a_global->brick_mutex);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_brick *mars_find_brick(struct mars_global *global, const void *brick_type, const char *path)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
|
|
|
|
if (!global || !path)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
down_read(&global->brick_mutex);
|
|
|
|
|
|
|
|
for (tmp = global->brick_anchor.next; tmp != &global->brick_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_brick *test = container_of(tmp, struct mars_brick, global_brick_link);
|
|
|
|
if (!strcmp(test->brick_path, path)) {
|
|
|
|
up_read(&global->brick_mutex);
|
|
|
|
if (brick_type && test->type != brick_type) {
|
|
|
|
MARS_ERR("bad brick type\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return test;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
up_read(&global->brick_mutex);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_find_brick);
|
|
|
|
|
|
|
|
int mars_free_brick(struct mars_brick *brick)
|
|
|
|
{
|
|
|
|
struct mars_global *global;
|
|
|
|
int i;
|
2012-02-02 15:25:43 +00:00
|
|
|
int count;
|
2011-07-22 10:43:40 +00:00
|
|
|
int status;
|
2013-09-16 10:53:22 +00:00
|
|
|
int sleeptime;
|
|
|
|
int maxsleep;
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
if (!brick) {
|
|
|
|
MARS_ERR("bad brick parameter\n");
|
|
|
|
status = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!brick->power.force_off || !brick->power.led_off) {
|
2012-02-02 15:25:43 +00:00
|
|
|
MARS_WRN("brick '%s' is not freeable\n", brick->brick_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
status = -ETXTBSY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
// first check whether the brick is in use somewhere
|
2011-10-05 14:59:29 +00:00
|
|
|
for (i = 0; i < brick->type->max_outputs; i++) {
|
|
|
|
struct mars_output *output = brick->outputs[i];
|
|
|
|
if (output && output->nr_connected > 0) {
|
2012-02-02 15:25:43 +00:00
|
|
|
MARS_WRN("brick '%s' not freeable, output %i is used\n", brick->brick_path, i);
|
2011-07-22 10:43:40 +00:00
|
|
|
status = -EEXIST;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 10:53:22 +00:00
|
|
|
// Should not happen, but workaround: wait until flying IO has vanished
|
|
|
|
maxsleep = 20000;
|
|
|
|
sleeptime = 1000;
|
|
|
|
for (;;) {
|
|
|
|
count = atomic_read(&brick->mref_object_layout.alloc_count);
|
|
|
|
if (likely(!count)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (maxsleep > 0) {
|
|
|
|
MARS_WRN("MEMLEAK: brick '%s' has %d mrefs allocated (total = %d, maxsleep = %d)\n", brick->brick_path, count, atomic_read(&brick->mref_object_layout.total_alloc_count), maxsleep);
|
|
|
|
} else {
|
|
|
|
MARS_ERR("MEMLEAK: brick '%s' has %d mrefs allocated (total = %d)\n", brick->brick_path, count, atomic_read(&brick->mref_object_layout.total_alloc_count));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
brick_msleep(sleeptime);
|
|
|
|
maxsleep -= sleeptime;
|
2012-02-02 15:25:43 +00:00
|
|
|
}
|
|
|
|
|
2011-08-25 10:16:32 +00:00
|
|
|
MARS_DBG("===> freeing brick name = '%s' path = '%s'\n", brick->brick_name, brick->brick_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
global = brick->global;
|
|
|
|
if (global) {
|
|
|
|
down_write(&global->brick_mutex);
|
|
|
|
list_del_init(&brick->global_brick_link);
|
|
|
|
list_del_init(&brick->dent_brick_link);
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
|
2011-10-05 14:59:29 +00:00
|
|
|
for (i = 0; i < brick->type->max_inputs; i++) {
|
2011-08-25 10:16:32 +00:00
|
|
|
struct mars_input *input = brick->inputs[i];
|
|
|
|
if (input) {
|
|
|
|
MARS_DBG("disconnecting input %i\n", i);
|
2016-12-31 22:14:34 +00:00
|
|
|
mars_disconnect(input);
|
2011-08-25 10:16:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-31 11:42:04 +00:00
|
|
|
MARS_DBG("deallocate name = '%s' path = '%s'\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
|
|
|
brick_string_free(brick->brick_name);
|
|
|
|
brick_string_free(brick->brick_path);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
status = generic_brick_exit_full((void*)brick);
|
|
|
|
|
|
|
|
if (status >= 0) {
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_mem_free(brick);
|
2011-07-22 10:43:40 +00:00
|
|
|
mars_trigger();
|
|
|
|
} else {
|
|
|
|
MARS_ERR("error freeing brick, status = %d\n", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_free_brick);
|
|
|
|
|
2013-05-09 16:59:09 +00:00
|
|
|
struct mars_brick *mars_make_brick(struct mars_global *global, struct mars_dent *belongs, const void *_brick_type, const char *path, const char *_name)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
2011-08-12 11:09:48 +00:00
|
|
|
const char *name = brick_strdup(_name);
|
2011-08-31 11:42:04 +00:00
|
|
|
const char *names[] = { name, NULL };
|
2011-07-22 10:43:40 +00:00
|
|
|
const struct generic_brick_type *brick_type = _brick_type;
|
|
|
|
const struct generic_input_type **input_types;
|
|
|
|
const struct generic_output_type **output_types;
|
|
|
|
struct mars_brick *res;
|
|
|
|
int size;
|
|
|
|
int i;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
MARS_ERR("cannot allocate space for name\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = brick_type->brick_size +
|
|
|
|
(brick_type->max_inputs + brick_type->max_outputs) * sizeof(void*);
|
|
|
|
input_types = brick_type->default_input_types;
|
|
|
|
for (i = 0; i < brick_type->max_inputs; i++) {
|
|
|
|
const struct generic_input_type *type = *input_types++;
|
|
|
|
if (unlikely(!type)) {
|
|
|
|
MARS_ERR("input_type %d is missing\n", i);
|
|
|
|
goto err_name;
|
|
|
|
}
|
|
|
|
if (unlikely(type->input_size <= 0)) {
|
|
|
|
MARS_ERR("bad input_size at %d\n", i);
|
|
|
|
goto err_name;
|
|
|
|
}
|
|
|
|
size += type->input_size;
|
|
|
|
}
|
|
|
|
output_types = brick_type->default_output_types;
|
|
|
|
for (i = 0; i < brick_type->max_outputs; i++) {
|
|
|
|
const struct generic_output_type *type = *output_types++;
|
|
|
|
if (unlikely(!type)) {
|
|
|
|
MARS_ERR("output_type %d is missing\n", i);
|
|
|
|
goto err_name;
|
|
|
|
}
|
|
|
|
if (unlikely(type->output_size <= 0)) {
|
|
|
|
MARS_ERR("bad output_size at %d\n", i);
|
|
|
|
goto err_name;
|
|
|
|
}
|
|
|
|
size += type->output_size;
|
|
|
|
}
|
|
|
|
|
2011-08-12 11:09:48 +00:00
|
|
|
res = brick_zmem_alloc(size);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (!res) {
|
|
|
|
MARS_ERR("cannot grab %d bytes for brick type '%s'\n", size, brick_type->type_name);
|
|
|
|
goto err_name;
|
|
|
|
}
|
2017-01-01 08:24:38 +00:00
|
|
|
get_lamport(&res->create_stamp);
|
2011-07-22 10:43:40 +00:00
|
|
|
res->global = global;
|
|
|
|
INIT_LIST_HEAD(&res->dent_brick_link);
|
2011-08-12 11:09:48 +00:00
|
|
|
res->brick_path = brick_strdup(path);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (!res->brick_path) {
|
|
|
|
MARS_ERR("cannot grab memory for path '%s'\n", path);
|
|
|
|
goto err_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = generic_brick_init_full(res, size, brick_type, NULL, NULL, names);
|
|
|
|
MARS_DBG("brick '%s' init '%s' '%s' (status=%d)\n", brick_type->type_name, path, name, status);
|
|
|
|
if (status < 0) {
|
|
|
|
MARS_ERR("cannot init brick %s\n", brick_type->type_name);
|
|
|
|
goto err_path;
|
|
|
|
}
|
|
|
|
res->free = mars_free_brick;
|
|
|
|
|
|
|
|
/* Immediately make it visible, regardless of internal state.
|
|
|
|
* Switching on / etc must be done separately.
|
|
|
|
*/
|
|
|
|
down_write(&global->brick_mutex);
|
2013-01-22 11:13:09 +00:00
|
|
|
list_add(&res->global_brick_link, &global->brick_anchor);
|
|
|
|
if (belongs) {
|
|
|
|
list_add_tail(&res->dent_brick_link, &belongs->brick_list);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
err_path:
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(res->brick_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
err_res:
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_mem_free(res);
|
2011-07-22 10:43:40 +00:00
|
|
|
err_name:
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(name);
|
2011-07-22 10:43:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_make_brick);
|
|
|
|
|
|
|
|
int mars_kill_brick(struct mars_brick *brick)
|
|
|
|
{
|
|
|
|
struct mars_global *global;
|
|
|
|
int status = -EINVAL;
|
|
|
|
|
|
|
|
CHECK_PTR(brick, done);
|
|
|
|
global = brick->global;
|
|
|
|
|
2012-01-30 12:47:45 +00:00
|
|
|
MARS_DBG("===> killing brick %s path = '%s' name = '%s'\n", brick->type ? SAFE_STR(brick->type->type_name) : "undef", SAFE_STR(brick->brick_path), SAFE_STR(brick->brick_name));
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2013-01-05 05:11:07 +00:00
|
|
|
if (unlikely(brick->nr_outputs > 0 && brick->outputs[0] && brick->outputs[0]->nr_connected)) {
|
|
|
|
MARS_ERR("sorry, output is in use '%s'\n", SAFE_STR(brick->brick_path));
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2011-09-27 11:57:04 +00:00
|
|
|
if (brick->show_status) {
|
|
|
|
brick->show_status(brick, true);
|
|
|
|
}
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
// start shutdown
|
2013-05-09 09:43:28 +00:00
|
|
|
set_button_wait((void*)brick, false, true, 0);
|
|
|
|
|
2017-01-08 07:49:30 +00:00
|
|
|
if (brick->power.led_off) {
|
2013-05-09 09:43:28 +00:00
|
|
|
int max_inputs = 0;
|
2017-01-04 06:06:19 +00:00
|
|
|
bool failed = false;
|
2013-05-09 09:43:28 +00:00
|
|
|
int i;
|
|
|
|
|
2017-01-04 06:06:19 +00:00
|
|
|
if (global) {
|
|
|
|
down_write(&global->brick_mutex);
|
|
|
|
list_del_init(&brick->global_brick_link);
|
|
|
|
list_del_init(&brick->dent_brick_link);
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
|
2013-05-09 09:43:28 +00:00
|
|
|
if (likely(brick->type)) {
|
|
|
|
max_inputs = brick->type->max_inputs;
|
|
|
|
} else {
|
|
|
|
MARS_ERR("uninitialized brick '%s' '%s'\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
MARS_DBG("---> freeing '%s' '%s'\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
|
|
|
|
2014-02-03 08:19:14 +00:00
|
|
|
if (brick->kill_ptr)
|
|
|
|
*brick->kill_ptr = NULL;
|
|
|
|
|
2013-05-09 09:43:28 +00:00
|
|
|
for (i = 0; i < max_inputs; i++) {
|
2016-12-31 22:14:34 +00:00
|
|
|
struct mars_input *input = brick->inputs[i];
|
2013-05-09 09:43:28 +00:00
|
|
|
if (!input)
|
|
|
|
continue;
|
2016-12-31 22:14:34 +00:00
|
|
|
status = mars_disconnect(input);
|
2013-05-09 09:43:28 +00:00
|
|
|
if (unlikely(status < 0)) {
|
2017-01-04 06:06:19 +00:00
|
|
|
failed = true;
|
2013-05-09 09:43:28 +00:00
|
|
|
MARS_ERR("brick '%s' '%s' disconnect %d failed, status = %d\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path), i, status);
|
|
|
|
}
|
|
|
|
}
|
2017-01-04 06:06:19 +00:00
|
|
|
if (failed)
|
|
|
|
goto done;
|
2013-05-09 09:43:28 +00:00
|
|
|
if (likely(brick->free)) {
|
|
|
|
status = brick->free(brick);
|
|
|
|
if (unlikely(status < 0)) {
|
|
|
|
MARS_ERR("freeing '%s' '%s' failed, status = %d\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path), status);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MARS_ERR("brick '%s' '%s' has no destructor\n", SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
|
|
|
}
|
|
|
|
status = 0;
|
|
|
|
} else {
|
2017-01-08 07:49:30 +00:00
|
|
|
/* This may happen regularly when bricks are shut down in parallel */
|
|
|
|
MARS_INF("brick '%s' '%s' is not off\n",
|
|
|
|
SAFE_STR(brick->brick_name), SAFE_STR(brick->brick_path));
|
2015-01-15 17:21:29 +00:00
|
|
|
status = -EUCLEAN;
|
2013-05-09 09:43:28 +00:00
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_kill_brick);
|
|
|
|
|
2011-08-31 11:42:04 +00:00
|
|
|
int mars_kill_brick_all(struct mars_global *global, struct list_head *anchor, bool use_dent_link)
|
|
|
|
{
|
|
|
|
int status = 0;
|
2012-11-29 14:35:41 +00:00
|
|
|
if (!anchor || !anchor->next)
|
|
|
|
goto done;
|
2011-08-31 11:42:04 +00:00
|
|
|
if (global) {
|
|
|
|
down_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
while (!list_empty(anchor)) {
|
|
|
|
struct list_head *tmp = anchor->next;
|
|
|
|
struct mars_brick *brick;
|
|
|
|
if (use_dent_link) {
|
|
|
|
brick = container_of(tmp, struct mars_brick, dent_brick_link);
|
|
|
|
} else {
|
|
|
|
brick = container_of(tmp, struct mars_brick, global_brick_link);
|
|
|
|
}
|
|
|
|
list_del_init(tmp);
|
|
|
|
if (global) {
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
status |= mars_kill_brick(brick);
|
|
|
|
if (global) {
|
|
|
|
down_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (global) {
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
2012-11-29 14:35:41 +00:00
|
|
|
done:
|
2011-08-31 11:42:04 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_kill_brick_all);
|
|
|
|
|
2013-05-09 14:34:17 +00:00
|
|
|
int mars_kill_brick_when_possible(struct mars_global *global, struct list_head *anchor, bool use_dent_link, const struct mars_brick_type *type, bool even_on)
|
2011-10-10 11:38:55 +00:00
|
|
|
{
|
|
|
|
int return_status = 0;
|
|
|
|
struct list_head *tmp;
|
2012-02-14 13:14:58 +00:00
|
|
|
|
|
|
|
restart:
|
2011-10-10 11:38:55 +00:00
|
|
|
if (global) {
|
|
|
|
down_write(&global->brick_mutex);
|
|
|
|
}
|
2012-02-14 13:14:58 +00:00
|
|
|
for (tmp = anchor->next; tmp != anchor; tmp = tmp->next) {
|
2011-10-10 11:38:55 +00:00
|
|
|
struct mars_brick *brick;
|
2015-01-19 09:09:33 +00:00
|
|
|
int count;
|
2011-10-10 11:38:55 +00:00
|
|
|
int status;
|
2015-01-19 09:09:33 +00:00
|
|
|
|
2011-10-10 11:38:55 +00:00
|
|
|
if (use_dent_link) {
|
|
|
|
brick = container_of(tmp, struct mars_brick, dent_brick_link);
|
|
|
|
} else {
|
|
|
|
brick = container_of(tmp, struct mars_brick, global_brick_link);
|
|
|
|
}
|
2011-10-10 13:03:10 +00:00
|
|
|
// only kill the right brick types
|
2013-01-22 11:10:12 +00:00
|
|
|
if (type && brick->type != type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// only kill marked bricks
|
|
|
|
if (!brick->killme) {
|
2011-10-10 11:38:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
2011-10-10 13:03:10 +00:00
|
|
|
// only kill unconnected bricks
|
2013-01-22 13:54:01 +00:00
|
|
|
if (brick->nr_outputs > 0 && brick->outputs[0] && brick->outputs[0]->nr_connected > 0) {
|
2011-10-10 11:38:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-09 14:34:17 +00:00
|
|
|
if (!even_on && (brick->power.button || !brick->power.led_off)) {
|
|
|
|
continue;
|
2011-11-04 15:43:23 +00:00
|
|
|
}
|
2015-01-19 09:09:33 +00:00
|
|
|
// only kill bricks which have no resources allocated
|
|
|
|
count = atomic_read(&brick->mref_object_layout.alloc_count);
|
|
|
|
if (count > 0)
|
|
|
|
continue;
|
2013-05-09 19:55:23 +00:00
|
|
|
/* Workaround FIXME:
|
2011-10-10 13:03:10 +00:00
|
|
|
* only kill bricks which have not been touched during the current mars_dent_work() round.
|
2013-05-09 19:55:23 +00:00
|
|
|
* some bricks like aio seem to have races between startup and termination of threads.
|
2011-10-10 13:03:10 +00:00
|
|
|
* disable this for stress-testing the allocation/deallocation logic.
|
2013-05-09 19:55:23 +00:00
|
|
|
* OTOH, frequently doing useless starts/stops is no good idea.
|
|
|
|
* CHECK: how to avoid too frequent switching by other means?
|
2011-10-10 13:03:10 +00:00
|
|
|
*/
|
2013-05-09 19:55:23 +00:00
|
|
|
if (brick->kill_round++ < 1) {
|
2011-10-10 13:03:10 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-02-14 13:14:58 +00:00
|
|
|
|
2017-01-08 07:49:30 +00:00
|
|
|
/* start shutdown */
|
|
|
|
mars_power_button(brick, false, true);
|
|
|
|
|
|
|
|
/* wait until actually off */
|
|
|
|
if (!brick->power.led_off)
|
|
|
|
continue;
|
|
|
|
|
2011-10-10 11:38:55 +00:00
|
|
|
if (global) {
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
2012-02-14 13:14:58 +00:00
|
|
|
|
2017-01-08 07:49:30 +00:00
|
|
|
MARS_DBG("KILLING '%s' '%s'\n",
|
|
|
|
brick->brick_path, brick->brick_name);
|
2011-10-10 11:38:55 +00:00
|
|
|
status = mars_kill_brick(brick);
|
2012-02-14 13:14:58 +00:00
|
|
|
|
2017-01-08 07:49:30 +00:00
|
|
|
if (status >= 0)
|
2011-10-10 11:38:55 +00:00
|
|
|
return_status++;
|
2017-01-08 07:49:30 +00:00
|
|
|
|
2012-02-14 13:14:58 +00:00
|
|
|
/* The list may have changed during the open lock
|
|
|
|
* in unpredictable ways.
|
|
|
|
*/
|
|
|
|
goto restart;
|
2011-10-10 11:38:55 +00:00
|
|
|
}
|
|
|
|
if (global) {
|
|
|
|
up_write(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
return return_status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mars_kill_brick_when_possible);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// mid-level brick instantiation (identity is based on path strings)
|
|
|
|
|
2012-11-19 10:31:55 +00:00
|
|
|
char *_vpath_make(int line, const char *fmt, va_list *args)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
2014-03-24 07:03:41 +00:00
|
|
|
va_list copy_args;
|
|
|
|
char dummy[2];
|
|
|
|
int len;
|
|
|
|
char *res;
|
|
|
|
|
|
|
|
memcpy(©_args, args, sizeof(copy_args));
|
|
|
|
len = vsnprintf(dummy, sizeof(dummy), fmt, copy_args);
|
|
|
|
len = _length_paranoia(len, line);
|
|
|
|
res = _brick_string_alloc(len + 2, line);
|
|
|
|
|
|
|
|
vsnprintf(res, len + 1, fmt, *args);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2012-11-19 10:31:55 +00:00
|
|
|
EXPORT_SYMBOL_GPL(_vpath_make);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2012-11-19 10:31:55 +00:00
|
|
|
char *_path_make(int line, const char *fmt, ...)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
char *res;
|
|
|
|
va_start(args, fmt);
|
2012-11-19 10:31:55 +00:00
|
|
|
res = _vpath_make(line, fmt, &args);
|
2011-07-22 10:43:40 +00:00
|
|
|
va_end(args);
|
|
|
|
return res;
|
|
|
|
}
|
2012-11-19 10:31:55 +00:00
|
|
|
EXPORT_SYMBOL_GPL(_path_make);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2012-11-19 10:31:55 +00:00
|
|
|
char *_backskip_replace(int line, const char *path, char delim, bool insert, const char *fmt, ...)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
|
|
|
int path_len = strlen(path);
|
2014-03-24 07:03:41 +00:00
|
|
|
int fmt_len;
|
|
|
|
int total_len;
|
|
|
|
char *res;
|
|
|
|
va_list args;
|
|
|
|
int pos = path_len;
|
|
|
|
int plus;
|
|
|
|
char dummy[2];
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
fmt_len = vsnprintf(dummy, sizeof(dummy), fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
fmt_len = _length_paranoia(fmt_len, line);
|
|
|
|
|
|
|
|
total_len = fmt_len + path_len;
|
|
|
|
total_len = _length_paranoia(total_len, line);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2014-03-24 07:03:41 +00:00
|
|
|
res = _brick_string_alloc(total_len + 2, line);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
2014-03-24 07:03:41 +00:00
|
|
|
while (pos > 0 && path[pos] != '/') {
|
|
|
|
pos--;
|
|
|
|
}
|
|
|
|
if (delim != '/') {
|
|
|
|
while (pos < path_len && path[pos] != delim) {
|
|
|
|
pos++;
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-24 07:03:41 +00:00
|
|
|
memcpy(res, path, pos);
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
plus = vscnprintf(res + pos, total_len - pos, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (insert) {
|
|
|
|
strncpy(res + pos + plus, path + pos + 1, total_len - pos - plus);
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
return res;
|
|
|
|
}
|
2012-11-19 10:31:55 +00:00
|
|
|
EXPORT_SYMBOL_GPL(_backskip_replace);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
struct mars_brick *path_find_brick(struct mars_global *global, const void *brick_type, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
char *fullpath;
|
|
|
|
struct mars_brick *res;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
fullpath = vpath_make(fmt, &args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (!fullpath) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
res = mars_find_brick(global, brick_type, fullpath);
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(fullpath);
|
2011-07-22 10:43:40 +00:00
|
|
|
MARS_IO("search for '%s' found = %p\n", fullpath, res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(path_find_brick);
|
|
|
|
|
|
|
|
const struct generic_brick_type *_client_brick_type = NULL;
|
|
|
|
EXPORT_SYMBOL_GPL(_client_brick_type);
|
|
|
|
const struct generic_brick_type *_bio_brick_type = NULL;
|
|
|
|
EXPORT_SYMBOL_GPL(_bio_brick_type);
|
|
|
|
const struct generic_brick_type *_aio_brick_type = NULL;
|
|
|
|
EXPORT_SYMBOL_GPL(_aio_brick_type);
|
2011-11-14 13:12:33 +00:00
|
|
|
const struct generic_brick_type *_sio_brick_type = NULL;
|
|
|
|
EXPORT_SYMBOL_GPL(_sio_brick_type);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
struct mars_brick *make_brick_all(
|
|
|
|
struct mars_global *global,
|
|
|
|
struct mars_dent *belongs,
|
2011-08-25 10:16:32 +00:00
|
|
|
int (*setup_fn)(struct mars_brick *brick, void *private),
|
2011-07-22 10:43:40 +00:00
|
|
|
void *private,
|
|
|
|
const char *new_name,
|
|
|
|
const struct generic_brick_type *new_brick_type,
|
|
|
|
const struct generic_brick_type *prev_brick_type[],
|
2013-05-10 06:15:00 +00:00
|
|
|
int switch_override, // -1 = off, 0 = leave in current state, +1 = create when necessary, +2 = create + switch on
|
2011-07-22 10:43:40 +00:00
|
|
|
const char *new_fmt,
|
|
|
|
const char *prev_fmt[],
|
|
|
|
int prev_count,
|
|
|
|
...
|
|
|
|
)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
const char *new_path;
|
2011-08-12 11:09:48 +00:00
|
|
|
char *_new_path = NULL;
|
2011-07-22 10:43:40 +00:00
|
|
|
struct mars_brick *brick = NULL;
|
|
|
|
char *paths[prev_count];
|
|
|
|
struct mars_brick *prev[prev_count];
|
2013-05-10 06:15:00 +00:00
|
|
|
bool switch_state;
|
2011-07-22 10:43:40 +00:00
|
|
|
int i;
|
2013-05-09 08:52:19 +00:00
|
|
|
int status;
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
// treat variable arguments
|
|
|
|
va_start(args, prev_count);
|
|
|
|
if (new_fmt) {
|
|
|
|
new_path = _new_path = vpath_make(new_fmt, &args);
|
|
|
|
} else {
|
|
|
|
new_path = new_name;
|
|
|
|
}
|
|
|
|
for (i = 0; i < prev_count; i++) {
|
|
|
|
paths[i] = vpath_make(prev_fmt[i], &args);
|
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (!new_path) {
|
|
|
|
MARS_ERR("could not create new path\n");
|
|
|
|
goto err;
|
|
|
|
}
|
2013-05-10 06:15:00 +00:00
|
|
|
|
|
|
|
// get old switch state
|
|
|
|
brick = mars_find_brick(global, NULL, new_path);
|
|
|
|
switch_state = false;
|
|
|
|
if (brick) {
|
|
|
|
switch_state = brick->power.button;
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
2013-05-09 14:15:04 +00:00
|
|
|
// override?
|
2013-05-10 06:15:00 +00:00
|
|
|
if (switch_override > 1)
|
2011-11-03 11:31:47 +00:00
|
|
|
switch_state = true;
|
|
|
|
else if (switch_override < 0)
|
|
|
|
switch_state = false;
|
2013-05-09 14:15:04 +00:00
|
|
|
// even higher override
|
2011-09-02 12:17:40 +00:00
|
|
|
if (global && !global->global_power.button) {
|
|
|
|
switch_state = false;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
// brick already existing?
|
|
|
|
if (brick) {
|
|
|
|
// just switch the power state
|
|
|
|
MARS_DBG("found existing brick '%s'\n", new_path);
|
2013-05-09 14:15:04 +00:00
|
|
|
// highest general override
|
|
|
|
if (mars_check_outputs(brick)) {
|
|
|
|
if (!switch_state)
|
|
|
|
MARS_DBG("brick '%s' override 0 -> 1\n", new_path);
|
|
|
|
switch_state = true;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
goto do_switch;
|
|
|
|
}
|
2013-05-10 06:15:00 +00:00
|
|
|
|
|
|
|
// brick not existing => check whether to create it
|
|
|
|
if (switch_override < 1) { // don't create
|
2011-07-22 10:43:40 +00:00
|
|
|
MARS_DBG("no need for brick '%s'\n", new_path);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
MARS_DBG("make new brick '%s'\n", new_path);
|
|
|
|
if (!new_name)
|
|
|
|
new_name = new_path;
|
|
|
|
|
|
|
|
MARS_DBG("----> new brick type = '%s' path = '%s' name = '%s'\n", new_brick_type->type_name, new_path, new_name);
|
|
|
|
|
|
|
|
// get all predecessor bricks
|
|
|
|
for (i = 0; i < prev_count; i++) {
|
|
|
|
char *path = paths[i];
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
MARS_ERR("could not build path %d\n", i);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-09-02 12:17:40 +00:00
|
|
|
prev[i] = mars_find_brick(global, NULL, path);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
if (!prev[i]) {
|
2011-08-31 11:42:04 +00:00
|
|
|
MARS_WRN("prev brick '%s' does not exist\n", path);
|
2011-07-22 10:43:40 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
MARS_DBG("------> predecessor %d path = '%s'\n", i, path);
|
2011-11-03 11:31:47 +00:00
|
|
|
if (!prev[i]->power.led_on) {
|
|
|
|
switch_state = false;
|
|
|
|
MARS_DBG("predecessor power is not on\n");
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// some generic brick replacements (better performance / network functionality)
|
|
|
|
brick = NULL;
|
|
|
|
if ((new_brick_type == _bio_brick_type || new_brick_type == _aio_brick_type)
|
|
|
|
&& _client_brick_type != NULL) {
|
|
|
|
char *remote = strchr(new_name, '@');
|
|
|
|
if (remote) {
|
|
|
|
remote++;
|
|
|
|
MARS_DBG("substitute by remote brick '%s' on peer '%s'\n", new_name, remote);
|
|
|
|
|
2013-05-09 16:59:09 +00:00
|
|
|
brick = mars_make_brick(global, belongs, _client_brick_type, new_path, new_name);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (brick) {
|
|
|
|
struct client_brick *_brick = (void*)brick;
|
|
|
|
_brick->max_flying = 10000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!brick && new_brick_type == _bio_brick_type && _aio_brick_type) {
|
|
|
|
struct kstat test = {};
|
|
|
|
int status = mars_stat(new_path, &test, false);
|
|
|
|
if (SKIP_BIO || status < 0 || !S_ISBLK(test.mode)) {
|
|
|
|
new_brick_type = _aio_brick_type;
|
|
|
|
MARS_DBG("substitute bio by aio\n");
|
|
|
|
}
|
|
|
|
}
|
2011-11-14 13:12:33 +00:00
|
|
|
#ifdef CONFIG_MARS_PREFER_SIO
|
|
|
|
if (!brick && new_brick_type == _aio_brick_type && _sio_brick_type) {
|
|
|
|
new_brick_type = _sio_brick_type;
|
|
|
|
MARS_DBG("substitute aio by sio\n");
|
|
|
|
}
|
|
|
|
#endif
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
// create it...
|
|
|
|
if (!brick)
|
2013-05-09 16:59:09 +00:00
|
|
|
brick = mars_make_brick(global, belongs, new_brick_type, new_path, new_name);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (unlikely(!brick)) {
|
|
|
|
MARS_ERR("creation failed '%s' '%s'\n", new_path, new_name);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (unlikely(brick->nr_inputs < prev_count)) {
|
2011-08-19 10:49:16 +00:00
|
|
|
MARS_ERR("'%s' wrong number of arguments: %d < %d\n", new_path, brick->nr_inputs, prev_count);
|
2011-07-22 10:43:40 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// connect the wires
|
|
|
|
for (i = 0; i < prev_count; i++) {
|
|
|
|
int status;
|
2016-12-31 22:14:34 +00:00
|
|
|
status = mars_connect(brick->inputs[i], prev[i]->outputs[0]);
|
2011-07-22 10:43:40 +00:00
|
|
|
if (unlikely(status < 0)) {
|
|
|
|
MARS_ERR("'%s' '%s' cannot connect input %d\n", new_path, new_name, i);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 10:16:32 +00:00
|
|
|
do_switch:
|
2011-07-22 10:43:40 +00:00
|
|
|
// call setup function
|
|
|
|
if (setup_fn) {
|
2011-08-25 10:16:32 +00:00
|
|
|
int setup_status = setup_fn(brick, private);
|
|
|
|
if (setup_status <= 0) {
|
|
|
|
switch_state = 0;
|
|
|
|
}
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// switch on/off (may fail silently, but responsibility is at the workers)
|
2013-05-09 09:43:28 +00:00
|
|
|
status = mars_power_button((void*)brick, switch_state, false);
|
2013-05-09 08:52:19 +00:00
|
|
|
MARS_DBG("switch '%s' to %d status = %d\n", new_path, switch_state, status);
|
2011-07-22 10:43:40 +00:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
err:
|
|
|
|
if (brick) {
|
|
|
|
mars_kill_brick(brick);
|
|
|
|
}
|
|
|
|
brick = NULL;
|
|
|
|
done:
|
|
|
|
for (i = 0; i < prev_count; i++) {
|
|
|
|
if (paths[i]) {
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(paths[i]);
|
2011-07-22 10:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_new_path)
|
2011-08-12 11:09:48 +00:00
|
|
|
brick_string_free(_new_path);
|
2011-07-22 10:43:40 +00:00
|
|
|
|
|
|
|
return brick;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(make_brick_all);
|
|
|
|
|
2013-01-22 10:30:52 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-04-11 12:58:36 +00:00
|
|
|
// generic symlink updates
|
|
|
|
|
|
|
|
void update_client_links(struct client_brick *brick)
|
|
|
|
{
|
|
|
|
char val[8];
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
name = backskip_replace(brick->brick_path, '/', true, "/local-%s/connection-", my_id());
|
|
|
|
if (unlikely(!name))
|
|
|
|
return; // silently
|
|
|
|
|
|
|
|
sprintf(val, "%d", brick->connection_state - 1);
|
|
|
|
mars_symlink(val, name, NULL, 0);
|
|
|
|
|
|
|
|
brick_string_free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-01-22 10:30:52 +00:00
|
|
|
// statistics
|
|
|
|
|
|
|
|
int global_show_statist =
|
|
|
|
#ifdef CONFIG_MARS_DEBUG_DEFAULT
|
|
|
|
1;
|
|
|
|
#else
|
|
|
|
0;
|
|
|
|
#endif
|
|
|
|
EXPORT_SYMBOL_GPL(global_show_statist);
|
|
|
|
|
2013-04-11 12:58:36 +00:00
|
|
|
int global_show_connections =
|
|
|
|
#ifdef CONFIG_MARS_SHOW_CONNECTIONS
|
|
|
|
1;
|
|
|
|
#else
|
|
|
|
0;
|
|
|
|
#endif
|
|
|
|
EXPORT_SYMBOL_GPL(global_show_connections);
|
|
|
|
|
2013-01-22 10:30:52 +00:00
|
|
|
static
|
|
|
|
void _show_one(struct mars_brick *test, int *brick_count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (*brick_count) {
|
|
|
|
MARS_STAT("---------\n");
|
|
|
|
}
|
|
|
|
MARS_STAT("BRICK type = %s path = '%s' name = '%s' "
|
2017-01-01 08:24:38 +00:00
|
|
|
"create_stamp = %ld.%09ld "
|
2013-01-22 10:30:52 +00:00
|
|
|
"size_hint=%d "
|
|
|
|
"mrefs_alloc = %d "
|
|
|
|
"mrefs_apsect_alloc = %d "
|
|
|
|
"total_mrefs_alloc = %d "
|
|
|
|
"total_mrefs_aspects = %d "
|
2016-12-21 15:58:41 +00:00
|
|
|
"killme = %d "
|
2013-01-22 10:30:52 +00:00
|
|
|
"button = %d off = %d on = %d\n",
|
|
|
|
SAFE_STR(test->type->type_name),
|
|
|
|
SAFE_STR(test->brick_path),
|
|
|
|
SAFE_STR(test->brick_name),
|
2017-01-01 08:24:38 +00:00
|
|
|
test->create_stamp.tv_sec, test->create_stamp.tv_nsec,
|
2013-01-22 10:30:52 +00:00
|
|
|
test->mref_object_layout.size_hint,
|
|
|
|
atomic_read(&test->mref_object_layout.alloc_count),
|
|
|
|
atomic_read(&test->mref_object_layout.aspect_count),
|
|
|
|
atomic_read(&test->mref_object_layout.total_alloc_count),
|
|
|
|
atomic_read(&test->mref_object_layout.total_aspect_count),
|
2016-12-21 15:58:41 +00:00
|
|
|
test->killme,
|
2013-01-22 10:30:52 +00:00
|
|
|
test->power.button,
|
|
|
|
test->power.led_off,
|
|
|
|
test->power.led_on);
|
|
|
|
(*brick_count)++;
|
|
|
|
if (test->ops && test->ops->brick_statistics) {
|
|
|
|
char *info = test->ops->brick_statistics(test, 0);
|
|
|
|
if (info) {
|
|
|
|
MARS_STAT(" %s", info);
|
|
|
|
brick_string_free(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < test->type->max_inputs; i++) {
|
|
|
|
struct mars_input *input = test->inputs[i];
|
|
|
|
struct mars_output *output = input ? input->connect : NULL;
|
|
|
|
if (output) {
|
|
|
|
MARS_STAT(" input %d connected with %s path = '%s' name = '%s'\n", i, SAFE_STR(output->brick->type->type_name), SAFE_STR(output->brick->brick_path), SAFE_STR(output->brick->brick_name));
|
|
|
|
} else {
|
|
|
|
MARS_STAT(" input %d not connected\n", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < test->type->max_outputs; i++) {
|
|
|
|
struct mars_output *output = test->outputs[i];
|
|
|
|
if (output) {
|
|
|
|
MARS_STAT(" output %d nr_connected = %d\n", i, output->nr_connected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void show_statistics(struct mars_global *global, const char *class)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
int dent_count = 0;
|
|
|
|
int brick_count = 0;
|
|
|
|
|
2013-04-11 12:58:36 +00:00
|
|
|
// update all connection state symlinks
|
|
|
|
if (global_show_connections) {
|
|
|
|
down_read(&global->brick_mutex);
|
|
|
|
for (tmp = global->brick_anchor.next; tmp != &global->brick_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_brick *test;
|
|
|
|
test = container_of(tmp, struct mars_brick, global_brick_link);
|
|
|
|
if (test->type == (void*)&client_brick_type) {
|
|
|
|
update_client_links((void*)test);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
up_read(&global->brick_mutex);
|
|
|
|
}
|
|
|
|
|
2013-01-22 10:30:52 +00:00
|
|
|
if (!global_show_statist)
|
|
|
|
return; // silently
|
|
|
|
|
2014-03-21 08:38:43 +00:00
|
|
|
brick_mem_statistics(false);
|
2013-01-22 10:30:52 +00:00
|
|
|
|
|
|
|
down_read(&global->brick_mutex);
|
|
|
|
MARS_STAT("================================== %s bricks:\n", class);
|
|
|
|
for (tmp = global->brick_anchor.next; tmp != &global->brick_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_brick *test;
|
|
|
|
test = container_of(tmp, struct mars_brick, global_brick_link);
|
|
|
|
_show_one(test, &brick_count);
|
|
|
|
}
|
|
|
|
up_read(&global->brick_mutex);
|
|
|
|
|
|
|
|
MARS_STAT("================================== %s dents:\n", class);
|
|
|
|
down_read(&global->dent_mutex);
|
|
|
|
for (tmp = global->dent_anchor.next; tmp != &global->dent_anchor; tmp = tmp->next) {
|
|
|
|
struct mars_dent *dent;
|
|
|
|
struct list_head *sub;
|
|
|
|
dent = container_of(tmp, struct mars_dent, dent_link);
|
|
|
|
MARS_STAT("dent %d '%s' '%s' stamp=%ld.%09ld\n", dent->d_class, SAFE_STR(dent->d_path), SAFE_STR(dent->new_link), dent->new_stat.mtime.tv_sec, dent->new_stat.mtime.tv_nsec);
|
|
|
|
dent_count++;
|
|
|
|
for (sub = dent->brick_list.next; sub != &dent->brick_list; sub = sub->next) {
|
|
|
|
struct mars_brick *test;
|
|
|
|
test = container_of(sub, struct mars_brick, dent_brick_link);
|
|
|
|
MARS_STAT(" owner of brick '%s'\n", SAFE_STR(test->brick_path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
up_read(&global->dent_mutex);
|
|
|
|
|
2013-04-05 11:04:55 +00:00
|
|
|
MARS_STAT("==================== %s STATISTICS: %d dents, %d bricks, %lld KB free\n", class, dent_count, brick_count, global_remaining_space);
|
2013-01-22 10:30:52 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(show_statistics);
|
|
|
|
|
2011-07-22 10:43:40 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// init stuff
|
|
|
|
|
2011-08-25 10:16:32 +00:00
|
|
|
int __init init_sy(void)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
|
|
|
MARS_INF("init_sy()\n");
|
|
|
|
|
|
|
|
_mars_trigger = __mars_trigger;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-23 11:16:26 +00:00
|
|
|
void exit_sy(void)
|
2011-07-22 10:43:40 +00:00
|
|
|
{
|
|
|
|
MARS_INF("exit_sy()\n");
|
|
|
|
}
|