mars/kernel/sy_old/sy_generic.c

2816 lines
68 KiB
C
Raw Normal View History

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
#define MARS_DEBUGGING
2011-09-27 08:46:14 +00:00
//#define IO_DEBUGGING
#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>
#include <linux/blkdev.h>
2011-07-22 10:43:40 +00:00
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/mount.h>
2011-07-22 10:43:40 +00:00
#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 "../compat.h"
2011-07-22 10:43:40 +00:00
#include <linux/namei.h>
#include <linux/kthread.h>
#include <linux/statfs.h>
2011-07-22 10:43:40 +00:00
#define SKIP_BIO false
// remove_this
#include <linux/wait.h>
2014-07-14 15:27:11 +00:00
#include <linux/version.h>
#ifndef DCACHE_MISS_TYPE /* define accessors compatible to b18825a7c8e37a7cf6abb97a12a6ad71af160de7 */
#define d_is_negative(dentry) ((dentry)->d_inode == NULL)
#define d_backing_inode(dentry) ((dentry)->d_inode)
#endif
2014-07-14 15:27:11 +00:00
/* 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)
#define HAS_VFS_READDIR
2015-07-06 10:41:40 +00:00
#elif !defined(f_dentry)
#define __HAS_NEW_FILLDIR_T
#endif
#ifdef RENAME_NOREPLACE
#define __HAS_RENAME2
#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),
2019-02-19 09:18:29 +00:00
META_INI_SUB(atime, struct kstat, mars_lamport_time_meta),
META_INI_SUB(mtime, struct kstat, mars_lamport_time_meta),
META_INI_SUB(ctime, struct kstat, mars_lamport_time_meta),
2011-07-22 10:43:40 +00:00
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);
// remove_this
2019-08-06 06:34:41 +00:00
#ifndef MARS_HAS_PREPATCH
/////////////////////////////////////////////////////////////////////
/* The _compat_*() functions are needed for the out-of-tree version
* of MARS for adapdation to different kernel version.
*/
#ifdef SB_FREEZE_LEVELS
/* since kernel 3.6 */
/* see a8104a9fcdeb82e22d7acd55fca20746581067d3 */
/* locking order changes in c30dabfe5d10c5fd70d882e5afb8f59f2942b194, we need to adapt */
#define __NEW_PATH_CREATE
#endif
2015-10-28 14:20:52 +00:00
#include <linux/fscache-cache.h>
#ifndef FSCACHE_OP_DEAD
/* since kernel 3.8 */
/* see b9d6ba94b875192ef5e2dab92d72beea33b83c3d */
#define __HAS_RETRY_ESTALE
#endif
/* 5955102c9984fa081b2d570cfac75c97eecf8f3b
*/
#ifndef FLOCK_VERIFY_READ /* detect kernel 4.5-rc1 via acc15575e */
#define HAS_INODE_LOCK_WRAPPERS
#endif
/* Hack because of 8bcb77fabd7cbabcad49f58750be8683febee92b
*/
static int __path_parent(const char *name, struct path *path, unsigned flags)
{
#ifdef user_path
return kern_path(name, flags | LOOKUP_PARENT | LOOKUP_DIRECTORY | LOOKUP_FOLLOW, path);
#else
char *tmp;
int len;
int error;
len = strlen(name);
while (len > 0 && name[len] != '/')
len--;
if (unlikely(!len))
return -EINVAL;
tmp = brick_string_alloc(len + 1);
strncpy(tmp, name, len);
tmp[len] = '\0';
error = kern_path(tmp, flags | LOOKUP_DIRECTORY | LOOKUP_FOLLOW, path);
brick_string_free(tmp);
return error;
#endif
}
/* code is blindly stolen from symlinkat()
* and later adapted to various kernels
*/
int _compat_symlink(const char __user *oldname,
2015-05-27 10:03:47 +00:00
const char __user *newname,
2019-02-19 09:18:29 +00:00
struct lamport_time *mtime)
{
const int newdfd = AT_FDCWD;
int error;
char *from;
struct dentry *dentry;
struct path path;
unsigned int lookup_flags = 0;
from = (char *)oldname;
#ifdef __HAS_RETRY_ESTALE
retry:
#endif
dentry = user_path_create(newdfd, newname, &path, lookup_flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_putname;
#ifndef __NEW_PATH_CREATE
error = mnt_want_write(path.mnt);
if (error)
goto out_dput;
#endif
error = vfs_symlink(path.dentry->d_inode, dentry, from);
2015-05-27 10:03:47 +00:00
if (error >= 0 && mtime) {
struct iattr iattr = {
.ia_valid = ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET,
.ia_mtime.tv_sec = mtime->tv_sec,
.ia_mtime.tv_nsec = mtime->tv_nsec,
};
#ifdef HAS_INODE_LOCK_WRAPPERS
inode_lock(dentry->d_inode);
#else
2015-05-27 10:03:47 +00:00
mutex_lock(&dentry->d_inode->i_mutex);
#endif
2015-05-27 10:03:47 +00:00
#ifdef FL_DELEG
error = notify_change(dentry, &iattr, NULL);
#else
error = notify_change(dentry, &iattr);
#endif
#ifdef HAS_INODE_LOCK_WRAPPERS
inode_unlock(dentry->d_inode);
#else
2015-05-27 10:03:47 +00:00
mutex_unlock(&dentry->d_inode->i_mutex);
#endif
2015-05-27 10:03:47 +00:00
}
#ifdef __NEW_PATH_CREATE
done_path_create(&path, dentry);
#else
mnt_drop_write(path.mnt);
out_dput:
dput(dentry);
mutex_unlock(&path.dentry->d_inode->i_mutex);
path_put(&path);
#endif
#ifdef __HAS_RETRY_ESTALE
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
#endif
out_putname:
return error;
}
/* code is stolen from mkdirat()
*/
int _compat_mkdir(const char __user *pathname,
int mode)
{
const int dfd = AT_FDCWD;
struct dentry *dentry;
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_DIRECTORY;
#ifdef __HAS_RETRY_ESTALE
retry:
#endif
dentry = user_path_create(dfd, pathname, &path, lookup_flags);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
if (!IS_POSIXACL(path.dentry->d_inode))
mode &= ~current_umask();
#ifndef __NEW_PATH_CREATE
error = mnt_want_write(path.mnt);
if (error)
goto out_dput;
#endif
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
#ifdef __NEW_PATH_CREATE
done_path_create(&path, dentry);
#else
mnt_drop_write(path.mnt);
out_dput:
dput(dentry);
mutex_unlock(&path.dentry->d_inode->i_mutex);
path_put(&path);
#endif
#ifdef __HAS_RETRY_ESTALE
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
#endif
return error;
}
/* This has some restrictions:
* - oldname and newname must reside in the same directory
* - standard case, no mountpoints inbetween
* - no security checks (we are anyway called from kernel code)
*/
int _compat_rename(const char *oldname,
const char *newname)
{
struct path oldpath;
struct path newpath;
struct dentry *old_dir;
struct dentry *new_dir;
struct dentry *old_dentry;
struct dentry *new_dentry;
struct dentry *trap;
const char *old_one;
const char *new_one;
const char *tmp;
unsigned int lookup_flags = 0;
#ifdef __HAS_RETRY_ESTALE
bool should_retry = false;
#endif
int error;
#ifdef __HAS_RETRY_ESTALE
retry:
#endif
error = __path_parent(oldname, &oldpath, lookup_flags);
if (unlikely(error))
goto exit;
old_dir = oldpath.dentry;
error = __path_parent(newname, &newpath, lookup_flags);
if (unlikely(error))
goto exit1;
new_dir = newpath.dentry;
old_one = oldname;
for (;;) {
for (tmp = old_one; *tmp && *tmp != '/'; tmp++)
/* empty */;
if (!*tmp)
break;
old_one = tmp + 1;
}
new_one = newname;
for (;;) {
for (tmp = new_one; *tmp && *tmp != '/'; tmp++)
/* empty */;
if (!*tmp)
break;
new_one = tmp + 1;
}
#ifdef __NEW_PATH_CREATE
error = mnt_want_write(oldpath.mnt);
if (unlikely(error))
goto exit2;
#endif
trap = lock_rename(new_dir, old_dir);
old_dentry = lookup_one_len(old_one, old_dir, strlen(old_one));
error = PTR_ERR(old_dentry);
if (unlikely(IS_ERR(old_dentry)))
goto out_unlock_rename;
error = -ENOENT;
if (unlikely(d_is_negative(old_dentry)))
goto out_dput_old;
error = -EINVAL;
if (unlikely(old_dentry == trap))
goto out_dput_old;
new_dentry = lookup_one_len(new_one, new_dir, strlen(new_one));
error = PTR_ERR(new_dentry);
if (unlikely(IS_ERR(new_dentry)))
goto out_dput_old;
error = -ENOTEMPTY;
if (unlikely(new_dentry == trap))
goto out_dput_new;
#ifndef __NEW_PATH_CREATE
error = mnt_want_write(oldpath.mnt);
if (unlikely(error))
goto out_dput_new;
#endif
#ifdef __HAS_RENAME2
error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry, NULL, 0);
#elif defined(FL_DELEG)
error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry, NULL);
#else
error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
#endif
#ifndef __NEW_PATH_CREATE
mnt_drop_write(oldpath.mnt);
#endif
out_dput_new:
dput(new_dentry);
out_dput_old:
dput(old_dentry);
out_unlock_rename:
unlock_rename(new_dir, old_dir);
#ifdef __NEW_PATH_CREATE
mnt_drop_write(oldpath.mnt);
exit2:
#endif
#ifdef __HAS_RETRY_ESTALE
if (retry_estale(error, lookup_flags))
should_retry = true;
#endif
path_put(&newpath);
exit1:
path_put(&oldpath);
#ifdef __HAS_RETRY_ESTALE
if (should_retry) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
#endif
exit:
return error;
}
/* This has some restrictions:
* - standard case, no mountpoints inbetween
* - no security checks (we are anyway called from kernel code)
*/
int _compat_unlink(const char *pathname)
{
struct path path;
struct dentry *parent;
struct dentry *dentry;
struct inode *inode = NULL;
const char *one;
const char *tmp;
int error;
unsigned int lookup_flags = 0;
#ifdef __HAS_RETRY_ESTALE
retry:
#endif
error = __path_parent(pathname, &path, lookup_flags);
if (unlikely(error))
goto exit;
parent = path.dentry;
if (unlikely(d_is_negative(parent)))
goto exit1;
one = pathname;
for (;;) {
for (tmp = one; *tmp && *tmp != '/'; tmp++)
/* empty */;
if (!*tmp)
break;
one = tmp + 1;
}
#ifdef __NEW_PATH_CREATE
error = mnt_want_write(path.mnt);
if (error)
goto exit1;
#endif
#ifdef HAS_INODE_LOCK_WRAPPERS
inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT);
#else
mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT);
#endif
dentry = lookup_one_len(one, parent, strlen(one));
error = PTR_ERR(dentry);
if (unlikely(IS_ERR(dentry)))
goto exit2;
error = -ENOENT;
if (unlikely(d_is_negative(dentry)))
goto exit3;
inode = dentry->d_inode;
ihold(inode);
#ifndef __NEW_PATH_CREATE
error = mnt_want_write(path.mnt);
if (error)
goto exit3;
#endif
#ifdef FL_DELEG
error = vfs_unlink(parent->d_inode, dentry, NULL);
#else
error = vfs_unlink(parent->d_inode, dentry);
#endif
#ifndef __NEW_PATH_CREATE
mnt_drop_write(path.mnt);
#endif
exit3:
dput(dentry);
exit2:
#ifdef HAS_INODE_LOCK_WRAPPERS
inode_unlock(dentry->d_inode);
#else
mutex_unlock(&parent->d_inode->i_mutex);
#endif
if (inode)
iput(inode);
#ifdef __NEW_PATH_CREATE
mnt_drop_write(path.mnt);
#endif
exit1:
path_put(&path);
exit:
#ifdef __HAS_RETRY_ESTALE
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
inode = NULL;
goto retry;
}
#endif
return error;
}
#endif
// end_remove_this
2011-07-22 10:43:40 +00:00
/////////////////////////////////////////////////////////////////////
// some helpers
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);
shrink_dcache_sb(sb);
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
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());
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
2011-07-22 10:43:40 +00:00
status = sys_mkdir(path, 0700);
#else
status = _compat_mkdir(path, 0700);
#endif
2011-07-22 10:43:40 +00:00
set_fs(oldfs);
return status;
}
EXPORT_SYMBOL_GPL(mars_mkdir);
int mars_rmdir(const char *path)
{
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
mm_segment_t oldfs;
int status;
oldfs = get_fs();
set_fs(get_ds());
status = sys_rmdir(path);
set_fs(oldfs);
return status;
2015-05-26 07:07:18 +00:00
#else
return -ENOSYS;
#endif
}
EXPORT_SYMBOL_GPL(mars_rmdir);
int mars_unlink(const char *path)
{
mm_segment_t oldfs;
int status;
oldfs = get_fs();
set_fs(get_ds());
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
status = sys_unlink(path);
#else
status = _compat_unlink(path);
#endif
set_fs(oldfs);
return status;
}
EXPORT_SYMBOL_GPL(mars_unlink);
2019-02-19 09:18:29 +00:00
int mars_symlink(const char *oldpath, const char *newpath, const struct lamport_time *stamp, uid_t uid)
2011-07-22 10:43:40 +00:00
{
char *tmp = backskip_replace(newpath, '/', true, "/.tmp-");
mm_segment_t oldfs;
struct kstat stat = {};
2019-02-19 09:18:29 +00:00
struct lamport_time times[2];
2011-07-22 10:43:40 +00:00
int status = -ENOMEM;
if (unlikely(!tmp))
goto done;
if (stamp)
memcpy(&times[0], stamp, sizeof(times[0]));
else
2017-04-15 06:21:21 +00:00
get_lamport(NULL, &times[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());
/* 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.
*/
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;
/* 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;
}
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
2011-07-22 10:43:40 +00:00
(void)sys_unlink(tmp);
status = sys_symlink(oldpath, tmp);
if (status >= 0) {
2011-07-22 10:43:40 +00:00
sys_lchown(tmp, uid, 0);
2019-02-19 09:18:29 +00:00
memcpy(&times[1], &times[0], sizeof(struct lamport_time));
2011-07-22 10:43:40 +00:00
status = do_utimes(AT_FDCWD, tmp, times, AT_SYMLINK_NOFOLLOW);
}
2015-05-27 10:03:47 +00:00
#else
(void)_compat_unlink(tmp);
status = _compat_symlink(oldpath, tmp, &times[0]);
#endif
2011-07-22 10:43:40 +00:00
if (status >= 0) {
set_lamport(&times[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)
{
char *res = NULL;
2012-12-12 14:10:26 +00:00
struct path path = {};
mm_segment_t oldfs;
struct inode *inode;
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
}
2019-02-25 12:03:58 +00:00
if (unlikely(!path.dentry)) {
MARS_WRN("path '%s' has invalid dentry\n", newpath);
goto done_put;
}
2012-12-12 14:10:26 +00:00
2019-02-25 12:03:58 +00:00
#ifdef MARS_HAS_VFS_READLINK
inode = d_inode(path.dentry);
#else
2012-12-12 14:10:26 +00:00
inode = path.dentry->d_inode;
2019-02-25 12:03:58 +00:00
#endif
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
}
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);
2019-02-25 12:03:58 +00:00
#ifdef MARS_HAS_VFS_READLINK
status = vfs_readlink(path.dentry, res, len + 1);
#else
status = inode->i_op->readlink(path.dentry, res, len + 1);
2019-02-25 12:03:58 +00:00
#endif
2012-12-12 14:10:26 +00:00
if (unlikely(status < 0)) {
MARS_ERR("cannot read link '%s', status = %d\n", newpath, status);
} 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)) {
if (unlikely(!res)) {
res = brick_string_alloc(1);
}
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());
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
2011-07-22 10:43:40 +00:00
status = sys_rename(oldpath, newpath);
#else
status = _compat_rename(oldpath, newpath);
#endif
2011-07-22 10:43:40 +00:00
set_fs(oldfs);
return status;
}
EXPORT_SYMBOL_GPL(mars_rename);
int mars_chmod(const char *path, mode_t mode)
{
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
2011-07-22 10:43:40 +00:00
mm_segment_t oldfs;
int status;
oldfs = get_fs();
set_fs(get_ds());
status = sys_chmod(path, mode);
set_fs(oldfs);
return status;
2015-05-26 06:58:55 +00:00
#else
return -ENOSYS;
#endif
2011-07-22 10:43:40 +00:00
}
EXPORT_SYMBOL_GPL(mars_chmod);
int mars_lchown(const char *path, uid_t uid)
{
2019-08-06 06:34:41 +00:00
#ifdef MARS_HAS_PREPATCH
2011-07-22 10:43:40 +00:00
mm_segment_t oldfs;
int status;
oldfs = get_fs();
set_fs(get_ds());
status = sys_lchown(path, uid, 0);
set_fs(oldfs);
return status;
2015-05-26 06:58:55 +00:00
#else
return -ENOSYS;
#endif
2011-07-22 10:43:40 +00:00
}
EXPORT_SYMBOL_GPL(mars_lchown);
loff_t _compute_space(struct kstatfs *kstatfs, loff_t raw_val)
{
u64 fsize = kstatfs->f_frsize;
if (fsize <= 0)
fsize = kstatfs->f_bsize;
MARS_DBG("fsize = %llu raw_val = %lld\n", fsize, raw_val);
/* Implausible value:
* Guess, use safe side from old Unix...
*/
if (fsize <= 0)
fsize = 512;
// 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)
{
struct path path = {};
struct kstatfs kstatfs = {};
2013-04-08 09:20:56 +00:00
mm_segment_t oldfs;
int res;
*total = *remaining = 0;
2013-04-08 09:20:56 +00:00
oldfs = get_fs();
set_fs(get_ds());
res = user_path_at(AT_FDCWD, fspath, 0, &path);
2013-04-08 09:20:56 +00:00
set_fs(oldfs);
if (unlikely(res < 0)) {
2013-04-08 09:20:56 +00:00
MARS_ERR("cannot get fspath '%s', err = %d\n\n", fspath, res);
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
res = vfs_statfs(path.dentry, &kstatfs);
2012-09-18 06:13:33 +00:00
#endif
if (unlikely(res < 0)) {
goto done;
}
*total = _compute_space(&kstatfs, kstatfs.f_blocks);
2014-03-11 09:53:09 +00:00
*remaining = _compute_space(&kstatfs, kstatfs.f_bavail);
done:
path_put(&path);
err: ;
}
EXPORT_SYMBOL_GPL(mars_remaining_space);
2018-03-12 12:06:31 +00:00
/*************************************************************/
/* Timestamp Ordering */
/* Timestamp ordering (e.g. via Lamport Clock) is easy when
* the object exists.
* When unlink() comes into play, it becomes more complex:
* where to store the timestamp of the object when it is
* currently deleted?
* This is necessary to allow permutations between _all_ operations
* on the object, including unlink().
* Idea: use a substitute object ".deleted-$object".
*/
static DEFINE_MUTEX(ordered_lock);
2019-02-19 09:18:29 +00:00
int ordered_unlink(const char *path, const struct lamport_time *stamp, int serial, int mode)
2018-03-12 12:06:31 +00:00
{
struct kstat stat;
char serial_str[32];
2019-02-19 09:18:29 +00:00
struct lamport_time now;
2018-03-12 12:06:31 +00:00
const char *marker_path;
int marker_status;
int status = 0;
snprintf(serial_str, sizeof(serial_str), "%d,%d", serial, mode);
if (!stamp) {
get_lamport(NULL, &now);
2018-03-12 12:06:31 +00:00
stamp = &now;
}
mutex_lock(&ordered_lock);
marker_path = backskip_replace(path, '/', true, "/.deleted-");
marker_status = mars_stat(marker_path, &stat, true);
if (marker_status < 0 ||
2019-02-19 09:18:29 +00:00
lamport_time_compare(stamp, &stat.mtime) >= 0) {
2018-03-12 12:06:31 +00:00
MARS_DBG("creating / updating marker '%s' mtime=%lu.%09lu\n",
marker_path,
stamp->tv_sec, stamp->tv_nsec);
status = mars_symlink(serial_str, marker_path, stamp, 0);
}
if (marker_status < 0 ||
2019-02-19 09:18:29 +00:00
lamport_time_compare(stamp, &stat.mtime) >= 0) {
2018-03-12 12:06:31 +00:00
status = mars_unlink(path);
}
mutex_unlock(&ordered_lock);
brick_string_free(marker_path);
return status;
}
2019-02-19 09:18:29 +00:00
int ordered_symlink(const char *oldpath, const char *newpath, const struct lamport_time *stamp, uid_t uid)
2018-03-12 12:06:31 +00:00
{
struct kstat stat;
2019-02-19 09:18:29 +00:00
struct lamport_time now;
2018-03-12 12:06:31 +00:00
const char *marker_path;
2018-01-03 05:47:16 +00:00
int status = 1;
2018-03-12 12:06:31 +00:00
if (!stamp) {
get_lamport(NULL, &now);
2018-03-12 12:06:31 +00:00
stamp = &now;
}
mutex_lock(&ordered_lock);
marker_path = backskip_replace(newpath, '/', true, "/.deleted-");
if (mars_stat(marker_path, &stat, true) >= 0 &&
2019-02-19 09:18:29 +00:00
lamport_time_compare(&stat.mtime, stamp) > 0) {
2018-03-12 12:06:31 +00:00
goto done;
}
if (mars_stat(newpath, &stat, true) >= 0 &&
2019-02-19 09:18:29 +00:00
lamport_time_compare(&stat.mtime, stamp) > 0) {
2018-03-12 12:06:31 +00:00
goto done;
}
(void)mars_unlink(marker_path);
status = mars_symlink(oldpath, newpath, stamp, uid);
done:
mutex_unlock(&ordered_lock);
brick_string_free(marker_path);
return status;
}
2011-07-22 10:43:40 +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) {
dent->d_say_channel = make_channel(test->d_path, true);
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(int mode)
2011-07-22 10:43:40 +00:00
{
if (mars_global) {
mars_global->trigger_mode |= mode;
2011-07-22 10:43:40 +00:00
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
}
}
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) {
// 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);
}
2011-07-22 10:43:40 +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
/* 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 || status < 0) {
2011-07-22 10:43:40 +00:00
mars_trigger();
}
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;
mars_dent_checker_fn checker;
2011-07-22 10:43:40 +00:00
char *path;
struct mars_dent *parent;
2020-03-03 10:28:09 +00:00
struct list_head tmp_anchor;
2011-07-22 10:43:40 +00:00
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;
}
/* Correct illegal timestamps */
2019-02-19 09:18:29 +00:00
if (unlikely(protect_lamport_time(&tmp.mtime)) &&
S_ISLNK(dent->new_stat.mode)) {
char *val = mars_readlink(newpath);
if (val) {
mars_symlink(val, newpath, &tmp.mtime, 0);
brick_string_free(val);
}
}
2011-07-22 10:43:40 +00:00
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;
}
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;
}
2019-02-25 12:03:58 +00:00
if (unlikely(!path.dentry)) {
MARS_WRN("path '%s' has invalid dentry\n", newpath);
goto done;
}
2011-07-22 10:43:40 +00:00
2019-02-25 12:03:58 +00:00
#ifdef MARS_HAS_VFS_READLINK
inode = d_inode(path.dentry);
#else
2011-07-22 10:43:40 +00:00
inode = path.dentry->d_inode;
2019-02-25 12:03:58 +00:00
#endif
if (unlikely(!inode || !S_ISLNK(inode->i_mode))) {
MARS_ERR("link '%s' has invalid inode\n", newpath);
status = -EINVAL;
goto done_put;
}
2011-07-22 10:43:40 +00:00
status = -ENOMEM;
link = brick_string_alloc(len + 2);
MARS_IO("len = %d\n", len);
2019-02-25 12:03:58 +00:00
#ifdef MARS_HAS_VFS_READLINK
status = vfs_readlink(path.dentry, link, len + 1);
#else
status = inode->i_op->readlink(path.dentry, link, len + 1);
2019-02-25 12:03:58 +00:00
#endif
link[len] = '\0';
if (status < 0 ||
(dent->new_link && !strcmp(dent->new_link, link))) {
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
}
2019-02-25 12:03:58 +00:00
done_put:
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)) {
2017-12-10 10:30:21 +00:00
loff_t min;
2014-03-30 06:18:47 +00:00
dent->d_corr_A = 0;
dent->d_corr_B = 0;
2017-12-10 10:30:21 +00:00
min = mf_get_any_dirty(newpath, DIRTY_COMPLETED);
2014-03-30 06:18:47 +00:00
if (min < dent->new_stat.size) {
2017-12-10 10:30:21 +00:00
MARS_DBG("file '%s' A size=%lld min=%lld\n", newpath, dent->new_stat.size, min);
2014-03-30 06:18:47 +00:00
dent->d_corr_A = min;
}
2017-12-10 10:30:21 +00:00
min = mf_get_any_dirty(newpath, DIRTY_FINISHED);
2014-03-30 06:18:47 +00:00
if (min < dent->new_stat.size) {
2017-12-10 10:30:21 +00:00
MARS_DBG("file '%s' B size=%lld min=%lld\n", newpath, dent->new_stat.size, min);
2014-03-30 06:18:47 +00:00
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);
}
// 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
/* Caution: this is called as a callback from iterate_dir() and friends.
* Don't deadlock by producing any filesystem output within 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
{
// remove_this
#ifdef HAS_VFS_READDIR
2011-07-22 10:43:40 +00:00
struct mars_cookie *cookie = __buf;
#else
// end_remove_this
2015-07-06 10:41:40 +00:00
struct mars_dir_context *buf = (void *)__buf;
struct mars_cookie *cookie = buf->cookie;
// remove_this
#endif
// end_remove_this
2011-07-22 10:43:40 +00:00
struct mars_dent *dent;
char *newpath;
2019-03-12 20:07:29 +00:00
int hash;
2011-07-22 10:43:40 +00:00
int prefix = 0;
int pathlen;
int class;
int serial = 0;
bool use_channel = false;
2011-07-22 10:43:40 +00:00
2014-06-18 04:56:00 +00:00
cookie->hit = true;
2011-07-22 10:43:40 +00:00
if (name[0] == '.') {
return 0;
}
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;
pathlen = strlen(cookie->path);
newpath = brick_string_alloc(pathlen + namlen + 2);
2011-07-22 10:43:40 +00:00
memcpy(newpath, cookie->path, pathlen);
newpath[pathlen++] = '/';
memcpy(newpath + pathlen, name, namlen);
pathlen += namlen;
newpath[pathlen] = '\0';
2014-04-08 08:03:06 +00:00
dent = brick_zmem_alloc(cookie->allocsize);
dent->d_class = class;
dent->d_serial = serial;
dent->d_path = newpath;
2019-03-12 20:07:29 +00:00
hash = (class + serial + d_type + pathlen * 7 + namlen * 13) % MARS_GLOBAL_HASH;
dent->d_hash = hash;
2020-03-03 10:28:09 +00:00
dent->d_name = brick_string_alloc(namlen + 1);
memcpy(dent->d_name, name, namlen);
dent->d_name[namlen] = '\0';
dent->d_rest = brick_strdup(dent->d_name + prefix);
newpath = NULL;
INIT_LIST_HEAD(&dent->dent_link);
INIT_LIST_HEAD(&dent->dent_hash_link);
INIT_LIST_HEAD(&dent->dent_quick_link);
INIT_LIST_HEAD(&dent->brick_list);
list_add(&dent->dent_link, &cookie->tmp_anchor);
dent->d_type = d_type;
dent->d_class = class;
dent->d_serial = serial;
dent->d_killme = false;
dent->d_use_channel = use_channel;
dent->d_depth = cookie->depth;
dent->d_global = cookie->global;
brick_string_free(newpath);
return 0;
}
static
void _mars_order(struct mars_cookie *cookie, struct mars_dent *dent)
{
struct mars_global *global = cookie->global;
struct list_head *anchor = &global->dent_anchor;
struct list_head *start = anchor;
struct list_head *hash_anchor;
struct list_head *tmp;
hash_anchor = &global->dent_hash_anchor[dent->d_hash];
2019-03-12 20:07:29 +00:00
tmp = anchor->next;
while (tmp != anchor) {
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);
2019-03-12 20:07:29 +00:00
2011-07-22 10:43:40 +00:00
if (!cmp) {
2020-03-03 10:28:09 +00:00
mars_free_dent(global, dent);
2014-04-08 08:03:06 +00:00
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;
2019-03-12 20:07:29 +00:00
for (;;) {
struct list_head *hash_try;
struct list_head *hash_try_anchor;
int hash_try_index = test->d_hash;
if (unlikely(hash_try_index < 0 || hash_try_index >= MARS_GLOBAL_HASH)) {
MARS_ERR("bad hash index %d\n", hash_try_index);
break;
}
hash_try_anchor = &global->dent_hash_anchor[hash_try_index];
hash_try = test->dent_hash_link.next;
if (hash_try == hash_try_anchor ||
hash_try == tmp->next ||
hash_try == &test->dent_hash_link)
break;
test = container_of(hash_try,
struct mars_dent,
dent_hash_link);
if (unlikely(test->d_hash != hash_try_index)) {
MARS_ERR("bad target hash index %d\n", test->d_hash);
break;
}
cmp = dent_compare(test, dent);
if (!cmp) {
2020-03-03 10:28:09 +00:00
mars_free_dent(global, dent);
2019-03-12 20:07:29 +00:00
dent = test;
goto found;
}
if (cmp >= 0)
break;
tmp = &test->dent_link;
}
2014-04-08 08:03:06 +00:00
start = tmp;
2019-03-12 20:07:29 +00:00
tmp = tmp->next;
2011-07-22 10:43:40 +00:00
}
2019-03-12 20:07:29 +00:00
/* not found: finish dent and insert into data stuctures */
2011-07-22 10:43:40 +00:00
2014-04-08 08:03:06 +00:00
list_add(&dent->dent_link, start);
2011-07-22 10:43:40 +00:00
2019-03-12 20:07:29 +00:00
start = hash_anchor;
for (tmp = hash_anchor->next; tmp != hash_anchor; tmp = tmp->next) {
struct mars_dent *test = container_of(tmp, struct mars_dent, dent_hash_link);
int cmp = dent_compare(test, dent);
if (cmp >= 0)
break;
start = tmp;
}
list_add(&dent->dent_hash_link, start);
2011-07-22 10:43:40 +00:00
found:
if (dent->d_parent)
dent->d_parent->d_child_count--;
2011-07-22 10:43:40 +00:00
dent->d_parent = cookie->parent;
if (dent->d_parent)
dent->d_parent->d_child_count++;
2020-03-03 10:28:09 +00:00
}
2011-07-22 10:43:40 +00:00
2020-03-03 10:28:09 +00:00
static
void _mars_order_all(struct mars_cookie *cookie)
{
while (!list_empty(&cookie->tmp_anchor)) {
struct list_head *tmp = cookie->tmp_anchor.next;
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
list_del_init(tmp);
_mars_order(cookie, dent);
}
2011-07-22 10:43:40 +00:00
}
static int _mars_readdir(struct mars_cookie *cookie)
{
struct file *f;
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);
}
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 (;;) {
// 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);
#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);
2020-03-03 10:28:09 +00:00
_mars_order_all(cookie);
2011-07-22 10:43:40 +00:00
return status;
}
2017-01-31 08:18:38 +00:00
static
bool dir_path_is_in(const char *path, const char *list)
{
const char *pattern = path_make("|%s/", path);
bool res;
res = strstr(list, pattern) != NULL;
brick_string_free(pattern);
return res;
}
2020-02-23 15:31:23 +00:00
static
int _op_forward(struct say_channel **say_channel,
struct mars_global *global,
mars_dent_worker_fn worker,
void *buf,
bool w_fl1, bool w_fl2)
{
struct list_head *tmp;
struct list_head *next;
int total_status = 0;
down_read(&global->dent_mutex);
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);
int status;
up_read(&global->dent_mutex);
brick_yield();
bind_to_dent(dent, say_channel);
status = worker(buf, dent, w_fl1, w_fl2);
down_read(&global->dent_mutex);
total_status |= status;
}
up_read(&global->dent_mutex);
return total_status;
}
2020-02-23 15:50:46 +00:00
static
int _op_backward(struct say_channel **say_channel,
struct mars_global *global,
mars_dent_worker_fn worker,
void *buf)
{
struct list_head *tmp;
struct list_head *next;
int total_status = 0;
down_read(&global->dent_mutex);
for (tmp = global->dent_anchor.prev, next = tmp->prev; tmp != &global->dent_anchor; tmp = next, next = next->prev) {
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
int status;
up_read(&global->dent_mutex);
brick_yield();
bind_to_dent(dent, say_channel);
status = worker(buf, dent, false, true);
down_read(&global->dent_mutex);
total_status |= status;
if (status < 0) {
MARS_INF("backwards: status %d on '%s'\n", status, dent->d_path);
}
}
up_read(&global->dent_mutex);
return total_status;
}
2020-02-23 15:36:41 +00:00
static
void _op_remove(struct say_channel **say_channel,
struct mars_global *global)
{
struct list_head *tmp;
struct list_head *prev;
down_write(&global->dent_mutex);
for (tmp = global->dent_anchor.prev, prev = tmp->prev; tmp != &global->dent_anchor; tmp = prev, prev = prev->prev) {
struct mars_dent *dent = container_of(tmp, struct mars_dent, dent_link);
if (!dent->d_killme)
continue;
bind_to_dent(dent, say_channel);
MARS_DBG("killing dent '%s'\n", dent->d_path);
list_del_init(&dent->dent_link);
list_del_init(&dent->dent_hash_link);
list_del_init(&dent->dent_quick_link);
mars_free_dent(NULL, dent);
}
up_write(&global->dent_mutex);
}
2017-01-31 08:18:38 +00:00
int mars_dent_work(struct mars_global *global,
char *path_list,
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;
2017-01-31 08:18:38 +00:00
char *startname = brick_strdup(path_list);
char *ptr;
2011-07-22 10:43:40 +00:00
struct mars_cookie cookie = {
.global = global,
.checker = checker,
2017-01-31 08:18:38 +00:00
.path = startname,
.parent = NULL,
2020-03-03 10:28:09 +00:00
.tmp_anchor = LIST_HEAD_INIT(cookie.tmp_anchor),
2011-07-22 10:43:40 +00:00
.allocsize = allocsize,
.depth = 0,
};
struct say_channel *say_channel = NULL;
2011-07-22 10:43:40 +00:00
struct list_head *tmp;
int rounds = 0;
int status;
int total_status = 0;
bool found_dir;
2017-01-31 08:18:38 +00:00
bool has_dir_list = false;
ptr = strchr(startname, '|');
if (ptr) {
*ptr = '\0';
has_dir_list = true;
}
2011-07-22 10:43:40 +00:00
/* Initialize the flat dent list
*/
version++;
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;
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
2017-01-31 08:18:38 +00:00
if (S_ISDIR(dent->new_stat.mode) &&
dent->d_depth <= maxdepth &&
(!has_dir_list ||
dent->d_depth > 0 ||
dir_path_is_in(dent->d_path, path_list))) {
2011-07-22 10:43:40 +00:00
struct mars_cookie sub_cookie = {
.global = global,
.checker = checker,
.path = dent->d_path,
.allocsize = allocsize,
.parent = dent,
2020-03-03 10:28:09 +00:00
.tmp_anchor = LIST_HEAD_INIT(sub_cookie.tmp_anchor),
2011-07-22 10:43:40 +00:00
.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
}
}
}
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);
/* Preparation pass.
* Here is a chance to mark some dents for removal
* (or other types of non-destructive operations)
*/
2020-02-23 15:31:23 +00:00
total_status |=
_op_forward(&say_channel, global, worker, buf, true, false);
2014-03-13 14:54:52 +00:00
bind_to_dent(NULL, &say_channel);
2011-07-22 10:43:40 +00:00
/* Remove all dents marked for removal.
2018-11-26 06:16:05 +00:00
* Needs to be done in reverse order because later d_parent pointers may
* reference earlier list members.
2011-07-22 10:43:40 +00:00
*/
2020-02-23 15:36:41 +00:00
_op_remove(&say_channel, global);
2011-07-22 10:43:40 +00:00
2014-03-13 14:54:52 +00:00
bind_to_dent(NULL, &say_channel);
2011-07-22 10:43:40 +00:00
/* Forward pass.
*/
2020-02-23 15:43:27 +00:00
total_status |=
_op_forward(&say_channel, global, worker, buf, false, false);
2011-07-22 10:43:40 +00:00
/* Backward pass.
*/
2020-02-23 15:50:46 +00:00
total_status |=
_op_backward(&say_channel, global, worker, buf);
2014-03-13 14:54:52 +00:00
bind_to_dent(NULL, &say_channel);
2011-07-22 10:43:40 +00:00
done:
2017-01-31 08:18:38 +00:00
brick_string_free(startname);
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);
2019-03-12 13:36:25 +00:00
static inline
struct mars_dent *_mars_find_dent(struct list_head *anchor, const char *path, bool use_dent_link)
2011-07-22 10:43:40 +00:00
{
struct mars_dent *res = NULL;
struct list_head *tmp;
2019-03-12 13:36:25 +00:00
for (tmp = anchor->next; tmp != anchor; tmp = tmp->next) {
struct mars_dent *tmp_dent;
if (use_dent_link)
tmp_dent = container_of(tmp, struct mars_dent, dent_link);
else
tmp_dent = container_of(tmp, struct mars_dent, dent_quick_link);
2011-07-22 10:43:40 +00:00
if (!strcmp(tmp_dent->d_path, path)) {
res = tmp_dent;
break;
}
}
return res;
}
struct mars_dent *mars_find_dent(struct mars_global *global, const char *path)
{
struct mars_dent *res;
2019-03-12 13:36:25 +00:00
if (!global)
return NULL;
2019-03-12 13:36:25 +00:00
down_read(&global->dent_mutex);
2019-03-12 13:36:25 +00:00
res = _mars_find_dent(&global->dent_quick_anchor, path, false);
up_read(&global->dent_mutex);
2019-03-12 13:36:25 +00:00
if (res)
return res;
down_write(&global->dent_mutex);
res = _mars_find_dent(&global->dent_quick_anchor, path, false);
if (!res) {
res = _mars_find_dent(&global->dent_anchor, path, true);
if (res) {
list_del_init(&res->dent_quick_link);
list_add(&res->dent_quick_link, &global->dent_quick_anchor);
}
}
up_write(&global->dent_mutex);
2011-07-22 10:43:40 +00:00
return res;
}
EXPORT_SYMBOL_GPL(mars_find_dent);
2019-03-12 13:36:25 +00:00
void mars_kill_dent(struct mars_global *global, struct mars_dent *dent)
2011-07-22 10:43:40 +00:00
{
2019-11-08 16:59:40 +00:00
/* Only mark as killable.
* Removal from the lists is done at mars_free_dent_all().
*/
2011-07-22 10:43:40 +00:00
dent->d_killme = true;
2019-03-12 13:36:25 +00:00
mars_kill_brick_all(global, &dent->brick_list, true);
2011-07-22 10:43:40 +00:00
}
EXPORT_SYMBOL_GPL(mars_kill_dent);
2019-03-12 13:36:25 +00:00
void mars_free_dent(struct mars_global *global, struct mars_dent *dent)
2011-07-22 10:43:40 +00:00
{
int i;
MARS_IO("%p path='%s'\n", dent, dent->d_path);
2011-09-27 08:46:14 +00:00
2019-03-12 13:36:25 +00:00
mars_kill_dent(global, dent);
2011-07-22 10:43:40 +00:00
CHECK_HEAD_EMPTY(&dent->dent_link);
CHECK_HEAD_EMPTY(&dent->dent_hash_link);
2019-03-12 13:25:04 +00:00
CHECK_HEAD_EMPTY(&dent->dent_quick_link);
2011-07-22 10:43:40 +00:00
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);
if (likely(dent->d_parent)) {
dent->d_parent->d_child_count--;
}
2017-01-31 06:57:10 +00:00
if (dent->d_private) {
if (dent->d_private_destruct) {
dent->d_private_destruct(dent->d_private);
}
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);
2018-11-26 06:16:05 +00:00
/* Needs to be done in reverse order because later d_parent pointers may
* reference earlier list members.
*/
2019-03-12 13:36:25 +00:00
if (global) {
2011-09-27 08:46:14 +00:00
down_write(&global->dent_mutex);
2019-03-12 13:36:25 +00:00
list_del_init(&global->dent_quick_anchor);
}
2011-09-27 08:46:14 +00:00
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;
2019-03-12 13:25:04 +00:00
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);
list_del_init(&dent->dent_hash_link);
2019-03-12 13:25:04 +00:00
list_del_init(&dent->dent_quick_link);
2011-09-27 08:46:14 +00:00
MARS_IO("freeing dent %p\n", dent);
2019-03-12 13:36:25 +00:00
mars_free_dent(global, dent);
2011-07-22 10:43:40 +00:00
}
}
EXPORT_SYMBOL_GPL(mars_free_dent_all);
/////////////////////////////////////////////////////////////////////
// low-level brick instantiation
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;
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;
}
}
// 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));
#ifdef CONFIG_MARS_DEBUG
dump_stack();
#endif
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);
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-04-15 06:21:21 +00:00
get_lamport(NULL, &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);
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;
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
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
set_button_wait((void*)brick, false, true, 0);
2017-01-08 07:49:30 +00:00
if (brick->power.led_off) {
int max_inputs = 0;
2017-01-04 06:06:19 +00:00
bool failed = false;
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);
}
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));
if (brick->kill_ptr)
*brick->kill_ptr = NULL;
for (i = 0; i < max_inputs; i++) {
2016-12-31 22:14:34 +00:00
struct mars_input *input = brick->inputs[i];
if (!input)
continue;
2016-12-31 22:14:34 +00:00
status = mars_disconnect(input);
if (unlikely(status < 0)) {
2017-01-04 06:06:19 +00:00
failed = true;
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;
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));
status = -EUCLEAN;
}
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;
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);
}
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)
{
int return_status = 0;
struct list_head *tmp;
restart:
if (global) {
down_write(&global->brick_mutex);
}
for (tmp = anchor->next; tmp != anchor; tmp = tmp->next) {
struct mars_brick *brick;
struct lamport_time now;
int count;
int status;
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);
}
// only kill the right brick types
if (type && brick->type != type) {
continue;
}
// only kill marked bricks
if (!brick->killme) {
continue;
}
// only kill unconnected bricks
if (brick->nr_outputs > 0 && brick->outputs[0] && brick->outputs[0]->nr_connected > 0) {
continue;
}
2013-05-09 14:34:17 +00:00
if (!even_on && (brick->power.button || !brick->power.led_off)) {
continue;
}
// only kill bricks which have no resources allocated
count = atomic_read(&brick->mref_object_layout.alloc_count);
if (count > 0)
continue;
/* only kill non-transient bricks */
if (brick->power.button != brick->power.led_on ||
brick->power.button == brick->power.led_off) {
if (!brick->kill_stamp.tv_sec) {
brick->kill_stamp = get_real_lamport();
brick->kill_stamp.tv_sec += 10;
}
}
/* Workaround FIXME:
* only kill bricks which have not been touched during the current mars_dent_work() round.
* some bricks like aio seem to have races between startup and termination of threads.
* disable this for stress-testing the allocation/deallocation logic.
* OTOH, frequently doing useless starts/stops is no good idea.
* CHECK: how to avoid too frequent switching by other means?
*/
if (!brick->kill_stamp.tv_sec) {
brick->kill_stamp = get_real_lamport();
brick->kill_stamp.tv_sec += 3;
}
now = get_real_lamport();
if (lamport_time_compare(&now, &brick->kill_stamp) <= 0 &&
global &&
global->global_power.button) {
continue;
}
if (brick->kill_round++ < 1) {
continue;
}
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;
if (global) {
up_write(&global->brick_mutex);
}
2017-01-08 07:49:30 +00:00
MARS_DBG("KILLING '%s' '%s'\n",
brick->brick_path, brick->brick_name);
status = mars_kill_brick(brick);
2017-01-08 07:49:30 +00:00
if (status >= 0)
return_status++;
2017-01-08 07:49:30 +00:00
/* The list may have changed during the open lock
* in unpredictable ways.
*/
goto restart;
}
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)
char *_vpath_make(int line, const char *fmt, va_list *args)
2011-07-22 10:43:40 +00:00
{
va_list copy_args;
char dummy[2];
int len;
char *res;
memcpy(&copy_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;
}
EXPORT_SYMBOL_GPL(_vpath_make);
2011-07-22 10:43:40 +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);
res = _vpath_make(line, fmt, &args);
2011-07-22 10:43:40 +00:00
va_end(args);
return res;
}
EXPORT_SYMBOL_GPL(_path_make);
2011-07-22 10:43:40 +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);
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
res = _brick_string_alloc(total_len + 2, line);
2011-07-22 10:43:40 +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
}
}
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;
}
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);
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[],
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;
2019-02-19 06:02:49 +00:00
char *paths[prev_count + 1];
struct mars_brick *prev[prev_count + 1];
bool switch_state;
2011-07-22 10:43:40 +00:00
int i;
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;
}
// 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
}
// override?
if (switch_override > 1)
switch_state = true;
else if (switch_override < 0)
switch_state = false;
// 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);
// 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;
}
// 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);
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);
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");
}
}
2016-08-26 07:04:02 +00:00
#ifndef ENABLE_MARS_AIO
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)
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)
status = mars_power_button((void*)brick, switch_state, false);
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);
/////////////////////////////////////////////////////////////////////////
// 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);
2018-03-12 12:58:26 +00:00
ordered_symlink(val, name, NULL, 0);
brick_string_free(name);
}
/////////////////////////////////////////////////////////////////////////
// statistics
int global_show_statist =
#ifdef CONFIG_MARS_DEBUG_DEFAULT
1;
#else
0;
#endif
EXPORT_SYMBOL_GPL(global_show_statist);
int global_show_connections =
#ifdef CONFIG_MARS_SHOW_CONNECTIONS
1;
#else
0;
#endif
EXPORT_SYMBOL_GPL(global_show_connections);
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' "
"create_stamp = %ld.%09ld "
"size_hint=%d "
"mrefs_alloc = %d "
"mrefs_apsect_alloc = %d "
"total_mrefs_alloc = %d "
"total_mrefs_aspects = %d "
"killme = %d "
2017-05-16 09:25:58 +00:00
"button = %d force_off = %d off = %d on = %d\n",
SAFE_STR(test->type->type_name),
SAFE_STR(test->brick_path),
SAFE_STR(test->brick_name),
test->create_stamp.tv_sec, test->create_stamp.tv_nsec,
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),
test->killme,
test->power.button,
2017-05-16 09:25:58 +00:00
test->power.force_off,
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;
// 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);
}
if (!global_show_statist)
return; // silently
2014-03-21 08:38:43 +00:00
brick_mem_statistics(false);
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);
MARS_STAT("==================== %s STATISTICS: %d dents, %d bricks, %lld KB free\n", class, dent_count, brick_count, global_remaining_space);
}
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");
}