1
0
mirror of https://github.com/schoebel/mars synced 2025-03-11 07:47:41 +00:00
mars/kernel/sy_old/sy_generic.c

3549 lines
84 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>
2020-09-17 16:02:15 +00:00
#include <linux/crc32c.h>
2011-07-22 10:43:40 +00:00
#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
// 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),
2020-07-08 20:14:03 +00:00
META_INI(d_proto, struct mars_dent, FIELD_INT),
2020-09-17 16:06:09 +00:00
META_INI(d_unordered, 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
2020-11-30 05:22:02 +00:00
inode_lock_nested(parent->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
2020-11-30 05:22:02 +00:00
inode_unlock(parent->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;
}
2020-07-31 06:07:08 +00:00
bool mars_is_mountpoint(const char *pathname)
{
struct path path = {};
mm_segment_t oldfs;
int status;
bool res = false;
oldfs = get_fs();
set_fs(KERNEL_DS);
2020-07-31 06:07:08 +00:00
status = user_path_at(AT_FDCWD, pathname, 0, &path);
if (unlikely(status < 0)) {
MARS_WRN("pathname '%s' does not exist, status = %d\n",
pathname, status);
goto done_fs;
}
if (unlikely(!path.dentry)) {
MARS_WRN("path '%s' has invalid dentry\n", pathname);
goto done_put;
}
if (unlikely(!follow_up(&path))) {
MARS_WRN("path '%s' has no vfsmnt\n", pathname);
goto done_put;
}
/* the second one may fail when we already are at the root mount */
if (!follow_up(&path)) {
goto done_put;
}
res = d_mountpoint(path.dentry);
done_put:
path_put(&path);
done_fs:
set_fs(oldfs);
return res;
}
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(KERNEL_DS);
2011-07-22 10:43:40 +00:00
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(KERNEL_DS);
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
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(KERNEL_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(KERNEL_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(KERNEL_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;
}
2020-03-01 14:51:00 +00:00
static
int mars_symlink(const char *oldpath, const char *newpath,
const struct lamport_time *stamp,
bool ordered)
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(KERNEL_DS);
2020-03-01 14:51:00 +00:00
status = vfs_lstat((char*)newpath, &stat);
2020-03-01 14:51:00 +00:00
/* When ordered, obey the Lamport condition.
*/
if (ordered && status >= 0 && stamp &&
lamport_time_compare(&stat.mtime, stamp) > 0) {
struct lamport_time real_now;
/* Illegal old link stamps are disobeyed, when
* they are too far in the future.
* Although this leads to a backskip violating
* the Lamport condition, it is a beneficial
* exceptional error correction.
* Such errors have been observed at ShaHoLin
* after fatal hardware crashes, where MARS
* was run for a _short_ time with an illegal
* CMOS hardware clock value, until ntpd
* corrected the system clock, fortunately.
*/
get_real_lamport(&real_now);
status = 1;
if (likely(stat.mtime.tv_sec <
real_now.tv_sec + max_lamport_future))
goto done_fs;
/* Continue, overriding the illegal old
* link stamp by exceptionally disobeying
* the Lamport condition.
*/
}
2020-03-01 14:51:00 +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.
*/
if (status >= 0 &&
!stamp &&
!stat.mtime.tv_nsec &&
times[0].tv_sec == stat.mtime.tv_sec) {
2021-01-27 11:46:26 +00:00
MARS_DBG("workaround timestamp tv_sec=%lld\n",
(s64)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) {
2020-03-24 07:20:39 +00:00
sys_lchown(tmp, 0, 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);
}
2020-03-01 14:51:00 +00:00
done_fs:
2011-07-22 10:43:40 +00:00
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;
}
2021-01-30 16:27:48 +00:00
/* adapt to ce6595a28a15c874aee374757dcd08f537d7b24d
* detected via 84a2bd39405ffd5fa6d6d77e408c5b9210da98de
*/
#ifdef LOOKUP_ROOT_GRABBED
#define user_lpath(name,path) \
user_path_at_empty(AT_FDCWD, (name), 0, (path), NULL)
#endif
char *mars_readlink(const char *newpath, struct lamport_time *stamp)
2012-12-12 14:10:26 +00:00
{
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(KERNEL_DS);
2012-12-12 14:10:26 +00:00
2020-10-27 08:05:18 +00:00
status = user_lpath(newpath, &path);
2012-12-12 14:10:26 +00:00
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
2020-03-02 13:36:06 +00:00
if (unlikely(!inode)) {
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
}
2020-03-02 13:36:06 +00:00
if (S_ISDIR(inode->i_mode)) {
/* fail silently: this can happen during
* deletions of directories
*/
status = -EINVAL;
goto done_put;
}
if (!S_ISLNK(inode->i_mode)) {
MARS_WRN("'%s' is no symlink\n", newpath);
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 if (stamp) {
set_get_lamport(&inode->i_mtime, NULL, stamp);
} 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(KERNEL_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(KERNEL_DS);
2011-07-22 10:43:40 +00:00
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);
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(KERNEL_DS);
2013-04-08 09:20:56 +00:00
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 */
2020-03-01 15:06:24 +00:00
/* Description of the OLD odering method:
*/
2018-03-12 12:06:31 +00:00
/* 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".
*/
2020-03-01 15:06:24 +00:00
int compat_deletions = 1;
2018-03-12 12:06:31 +00:00
static DEFINE_MUTEX(ordered_lock);
2020-03-01 15:06:24 +00:00
static
int compat_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) {
2021-01-27 11:46:26 +00:00
MARS_DBG("creating / updating marker '%s' mtime=%lld.%09ld\n",
2018-03-12 12:06:31 +00:00
marker_path,
2021-01-27 11:46:26 +00:00
(s64)stamp->tv_sec, stamp->tv_nsec);
2020-03-24 07:20:39 +00:00
status = mars_symlink(serial_str, marker_path, stamp, false);
2018-03-12 12:06:31 +00:00
}
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;
}
2020-03-01 15:06:24 +00:00
static
int compat_ordered_symlink(const char *oldpath,
const char *newpath,
const struct lamport_time *stamp)
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);
2020-03-24 07:20:39 +00:00
status = mars_symlink(oldpath, newpath, stamp, false);
2018-03-12 12:06:31 +00:00
done:
mutex_unlock(&ordered_lock);
brick_string_free(marker_path);
return status;
}
2020-03-01 15:06:24 +00:00
/* NEW timestamp ordering method.
* 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
* deleted?
* The new method simply uses a special value MARS_DELETED_STR
* as a marker for symlinks.
* In order to prevent long-term accumulation of suchalike
* "zombie" symlinks, some cleanup via unlink() is necessary.
* We offload cleanup to "marsadm cron".
*/
2020-03-01 13:55:44 +00:00
char *ordered_readlink(const char *path, struct lamport_time *stamp)
2020-03-01 13:55:44 +00:00
{
char *res = mars_readlink(path, stamp);
2020-03-01 13:55:44 +00:00
if (!strcmp(res, MARS_DELETED_STR)) {
*res = '\0';
}
return res;
}
2020-03-01 15:06:24 +00:00
int ordered_unlink(const char *path,
const struct lamport_time *stamp,
int serial, int mode)
{
if (compat_deletions)
return compat_ordered_unlink(path, stamp, serial, mode);
return ordered_symlink(MARS_DELETED_STR, path, stamp);
}
int ordered_symlink(const char *oldpath,
const char *newpath,
const struct lamport_time *stamp)
{
char *dir_path = NULL;
int dir_len;
int nr_retry = 0;
2020-03-01 15:06:24 +00:00
int status;
retry:
2020-03-01 15:06:24 +00:00
if (compat_deletions)
status = compat_ordered_symlink(oldpath, newpath, stamp);
else
status = mars_symlink(oldpath, newpath, stamp, true);
/* Automatically create any missing path dirs */
while (unlikely(status < 0)) {
int old_len;
int check;
2020-03-01 15:06:24 +00:00
if (!dir_path) {
dir_path = brick_strdup(newpath);
dir_len = strlen(dir_path);
}
old_len = dir_len;
while (dir_len > 0 && dir_path[dir_len] != '/')
dir_len--;
dir_path[dir_len] = '\0';
if (dir_len <= 0 || dir_len >= old_len)
break;
check = mars_mkdir(dir_path);
if (check >= 0) {
brick_string_free(dir_path);
dir_path = NULL;
if (nr_retry++ < 3)
goto retry;
break;
}
}
brick_string_free(dir_path);
2020-03-01 15:06:24 +00:00
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;
2020-09-12 20:29:40 +00:00
for (;;) {
if (!test) {
dent->d_say_channel = default_channel;
break;
}
2020-09-12 20:29:40 +00:00
CHECK_PTR(test, fatal);
if (test->d_use_channel && test->d_path) {
2020-03-07 18:49:18 +00:00
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);
}
2020-09-12 20:29:40 +00:00
fatal: ;
}
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);
2019-08-13 10:14:14 +00:00
void _init_mars_global(struct mars_global *global)
{
int i;
memset(global, 0, sizeof(struct mars_global));
INIT_LIST_HEAD(&global->dent_anchor);
INIT_LIST_HEAD(&global->brick_anchor);
global->global_power.button = true;
init_waitqueue_head(&global->main_event);
2020-08-20 08:11:32 +00:00
for (i = 0; i < MARS_GLOBAL_HASH_BASE; i++) {
struct list_head *table = brick_mem_alloc(PAGE_SIZE);
int j;
global->dent_hash_table[i] = table;
for (j = 0; j < MARS_GLOBAL_HASH_TABLE; j++)
INIT_LIST_HEAD(&table[j]);
}
}
void exit_mars_global(struct mars_global *global)
{
int i;
#ifdef CONFIG_MARS_DEBUG
CHECK_HEAD_EMPTY(&global->dent_anchor);
CHECK_HEAD_EMPTY(&global->brick_anchor);
#endif
for (i = 0; i < MARS_GLOBAL_HASH_BASE; i++) {
struct list_head *table = global->dent_hash_table[i];
#ifdef CONFIG_MARS_DEBUG
int j;
for (j = 0; j < MARS_GLOBAL_HASH_TABLE; j++)
CHECK_HEAD_EMPTY(&table[j]);
#endif
brick_mem_free(table);
}
2019-08-13 10:14:14 +00:00
}
void __mars_trigger(int mode)
2011-07-22 10:43:40 +00:00
{
2020-09-15 15:42:30 +00:00
struct mars_global *global = mars_global;
if (global) {
global->trigger_mode |= mode;
global->main_trigger = true;
wake_up_interruptible_all(&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 *check_parent;
2011-07-22 10:43:40 +00:00
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;
bool some_ordered;
2014-06-18 04:56:00 +00:00
bool hit;
2011-07-22 10:43:40 +00:00
};
static
2020-03-04 11:37:07 +00:00
int get_inode(char *newpath, struct mars_dent *dent, bool get_deleted)
2011-07-22 10:43:40 +00:00
{
mm_segment_t oldfs;
int status;
struct kstat tmp = {};
oldfs = get_fs();
set_fs(KERNEL_DS);
2011-07-22 10:43:40 +00:00
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, NULL);
if (val) {
2020-03-24 07:20:39 +00:00
mars_symlink(val, newpath, &tmp.mtime, false);
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
}
2020-03-04 11:37:07 +00:00
/* Treat deleted dents as non-existing.
* Killing will be done later via ->killme.
*/
if (!get_deleted &&
status >= 0 &&
dent->new_link &&
!strcmp(dent->new_link, MARS_DELETED_STR)) {
status = -ENOENT;
}
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;
}
2020-09-17 16:02:15 +00:00
unsigned int dent_hash(const char *str, int len)
{
u32 raw_hash = crc32c(0, str, len);
u32 mask = (u32)-1;
unsigned int hash = 0;
/* catch all possible bits */
while (mask) {
hash ^= raw_hash;
raw_hash /= MARS_GLOBAL_HASH;
mask /= MARS_GLOBAL_HASH;
}
hash %= MARS_GLOBAL_HASH;
return hash;
}
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
#ifdef MARS_HAS_ITERATE_DIR
// end_remove_this
struct mars_dir_context {
struct dir_context ctx;
struct mars_cookie *cookie;
};
// remove_this
#endif
// end_remove_this
2020-10-19 14:10:47 +00:00
/* Skip any names / directories used for backup etc */
#define SKIP_ENTRY(str) \
{ str, strlen(str) }
struct skip_info {
const char *name;
int len;
};
const struct skip_info skips[] = {
SKIP_ENTRY("backup"),
SKIP_ENTRY("cache"),
SKIP_ENTRY("local"),
SKIP_ENTRY("probe"),
{ NULL, 0 }
};
/* 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 MARS_HAS_ITERATE_DIR
2015-07-06 10:41:40 +00:00
struct mars_dir_context *buf = (void *)__buf;
struct mars_cookie *cookie = buf->cookie;
#else
struct mars_cookie *cookie = __buf;
#endif
// end_remove_this
2011-07-22 10:43:40 +00:00
struct mars_dent *dent;
char *newpath;
2020-09-17 16:02:15 +00:00
unsigned int hash;
2011-07-22 10:43:40 +00:00
int prefix = 0;
int pathlen;
int class;
int serial = 0;
2020-10-19 14:10:47 +00:00
#if 0
int i;
#endif
bool use_channel = false;
2011-07-22 10:43:40 +00:00
2014-06-18 04:56:00 +00:00
cookie->hit = true;
2020-09-11 12:50:02 +00:00
cookie->global->nr_readitem++;
2014-06-18 04:56:00 +00:00
2020-03-04 11:37:07 +00:00
if (!name || !*name || name[0] == '.') {
2011-07-22 10:43:40 +00:00
return 0;
}
class = cookie->checker(cookie->check_parent,
name, namlen,
d_type,
&prefix,
&serial,
&use_channel);
/* For some_ordered network transfers, always
* accept symlinks.
*/
if (class < 0 &&
(!cookie->some_ordered ||
d_type != DT_LNK))
2011-07-22 10:43:40 +00:00
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';
2020-09-17 16:02:15 +00:00
hash = dent_hash(newpath, pathlen);
2020-08-20 06:50:49 +00:00
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;
2020-03-31 04:40:05 +00:00
newpath = NULL;
2014-04-08 08:03:06 +00:00
2019-03-12 20:07:29 +00:00
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);
INIT_LIST_HEAD(&dent->dent_link);
INIT_LIST_HEAD(&dent->dent_hash_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;
return 0;
}
2020-08-19 07:58:53 +00:00
static
2020-09-12 20:29:40 +00:00
void _reconnect_dent(struct mars_dent *parent, struct mars_dent *dent)
2020-08-19 07:58:53 +00:00
{
2020-09-12 16:58:40 +00:00
CHECK_PTR(dent, fatal);
2020-09-12 20:29:40 +00:00
if (dent->d_parent == parent)
2020-08-19 07:58:53 +00:00
return;
2020-09-12 16:58:40 +00:00
if (dent->d_parent) {
CHECK_PTR(dent->d_parent, fatal);
2020-08-19 07:58:53 +00:00
dent->d_parent->d_child_count--;
2020-09-12 16:58:40 +00:00
}
2020-09-12 20:29:40 +00:00
dent->d_parent = parent;
if (parent) {
CHECK_PTR(parent, fatal);
parent->d_child_count++;
2020-09-12 16:58:40 +00:00
}
fatal: ;
2020-08-19 07:58:53 +00:00
}
2020-03-03 10:28:09 +00:00
static
void _mars_order(struct mars_cookie *cookie, struct mars_dent *dent)
{
struct mars_global *global = cookie->global;
struct list_head *sorted_anchor;
struct list_head *sorted_prev;
2020-03-03 10:28:09 +00:00
struct list_head *hash_anchor;
struct list_head *hash_try;
struct list_head *hash_prev;
2020-03-03 10:28:09 +00:00
struct list_head *tmp;
2020-09-12 16:58:40 +00:00
CHECK_PTR(dent, fatal);
/* Fuzzy hashing algorithm.
* Although based on external hashing, it produces an additional
* sorted list and allows for neighbour search.
+ A description and an evaluation can be found at the
* University of Stattgart, Institut fuer Informatik
* (sorry, much is in German, produced in the 1990s)
*/
sorted_anchor = &global->dent_anchor;
2020-09-12 16:58:40 +00:00
CHECK_PTR(sorted_anchor, fatal);
2020-09-17 16:02:15 +00:00
hash_anchor = DENT_HASH_ANCHOR(global, dent->d_hash);
2020-09-12 16:58:40 +00:00
CHECK_PTR(hash_anchor, fatal);
2019-03-12 20:07:29 +00:00
sorted_prev = sorted_anchor;
tmp = sorted_anchor->next;
for (hash_try = hash_anchor->next; hash_try != hash_anchor; hash_try = hash_try->next) {
struct mars_dent *hash_test = container_of(hash_try, struct mars_dent, dent_hash_link);
int cmp;
2020-09-12 16:58:40 +00:00
CHECK_PTR(hash_test, fatal);
if (hash_test->d_unordered)
break;
#ifdef CONFIG_MARS_DEBUG
if (unlikely(hash_test->d_hash != dent->d_hash)) {
MARS_ERR("bad target hash index %d\n", hash_test->d_hash);
break;
}
#endif
cmp = dent_compare(hash_test, dent);
if (!cmp) {
mars_free_dent(global, dent);
dent = hash_test;
goto found;
}
if (cmp >= 0)
break;
tmp = &hash_test->dent_link;
sorted_prev = tmp->prev;
2020-09-12 16:58:40 +00:00
CHECK_PTR(sorted_prev, fatal);
}
while (tmp != sorted_anchor) {
2014-04-08 08:03:06 +00:00
struct mars_dent *test = container_of(tmp, struct mars_dent, dent_link);
int cmp;
2020-09-12 16:58:40 +00:00
CHECK_PTR(test, fatal);
if (test->d_unordered)
break;
2019-03-12 20:07:29 +00:00
cmp = dent_compare(test, dent);
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;
2021-03-01 11:16:34 +00:00
unsigned int hash_try_index = test->d_hash;
2019-03-12 20:07:29 +00:00
#ifdef CONFIG_MARS_DEBUG
2021-03-01 11:16:34 +00:00
if (unlikely(hash_try_index >= MARS_GLOBAL_HASH)) {
2019-03-12 20:07:29 +00:00
MARS_ERR("bad hash index %d\n", hash_try_index);
break;
}
#endif
2020-09-17 16:02:15 +00:00
hash_try_anchor = DENT_HASH_ANCHOR(global, hash_try_index);
2020-09-12 16:58:40 +00:00
CHECK_PTR(hash_try_anchor, fatal);
2019-03-12 20:07:29 +00:00
hash_try = test->dent_hash_link.next;
2020-09-12 16:58:40 +00:00
CHECK_PTR(hash_try, fatal);
2019-03-12 20:07:29 +00:00
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);
2020-09-12 16:58:40 +00:00
CHECK_PTR(test, fatal);
if (test->d_unordered)
break;
#ifdef CONFIG_MARS_DEBUG
2019-03-12 20:07:29 +00:00
if (unlikely(test->d_hash != hash_try_index)) {
MARS_ERR("bad target hash index %d\n", test->d_hash);
break;
}
#endif
2019-03-12 20:07:29 +00:00
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;
2020-09-12 16:58:40 +00:00
CHECK_PTR(tmp, fatal);
2019-03-12 20:07:29 +00:00
}
sorted_prev = tmp;
2019-03-12 20:07:29 +00:00
tmp = tmp->next;
2020-09-12 16:58:40 +00:00
CHECK_PTR(tmp, fatal);
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
dent->d_unordered = false;
list_add(&dent->dent_link, sorted_prev);
2011-07-22 10:43:40 +00:00
hash_prev = hash_anchor;
2019-03-12 20:07:29 +00:00
for (tmp = hash_anchor->next; tmp != hash_anchor; tmp = tmp->next) {
struct mars_dent *test = container_of(tmp, struct mars_dent, dent_hash_link);
2020-09-12 16:58:40 +00:00
int cmp;
2019-03-12 20:07:29 +00:00
2020-09-12 16:58:40 +00:00
CHECK_PTR(test, fatal);
cmp = dent_compare(test, dent);
2019-03-12 20:07:29 +00:00
if (cmp >= 0)
break;
hash_prev = tmp;
2019-03-12 20:07:29 +00:00
}
list_add(&dent->dent_hash_link, hash_prev);
2019-03-12 20:07:29 +00:00
2011-07-22 10:43:40 +00:00
found:
2020-09-12 20:29:40 +00:00
_reconnect_dent(cookie->parent, dent);
2020-09-12 16:58:40 +00:00
fatal: ;
2020-03-03 10:28:09 +00:00
}
2011-07-22 10:43:40 +00:00
static inline
void _list_connect(struct list_head *a, struct list_head *b)
{
a->next = b;
b->prev = a;
}
2020-03-03 10:28:09 +00:00
static
void _mars_order_all(struct mars_cookie *cookie)
{
LIST_HEAD(later_anchor);
2020-03-03 10:28:09 +00:00
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);
2020-09-12 16:58:40 +00:00
CHECK_PTR(dent, fatal);
2020-03-03 10:28:09 +00:00
list_del_init(tmp);
/* When some_ordered: only sort links spawning
* a .cl_forward action (with some unimportant exceptions).
* In addition, the alive and time links need to
* come last, for race avoidance.
* The rest needs not to be sorted, saving CPU.
*/
if (cookie->some_ordered &&
2020-09-17 11:35:56 +00:00
dent->d_class < 0 &&
!S_ISDIR(dent->new_stat.mode) &&
/* further sorted items */
strncmp(dent->d_name, "alive-", 6) &&
true) {
get_inode(dent->d_path, dent, true);
dent->d_unordered = true;
2020-09-11 12:50:02 +00:00
cookie->global->nr_unordered++;
/* time-* must be the very last items */
if (strncmp(dent->d_name, "time-", 5))
list_add(&dent->dent_link, &later_anchor);
else
list_add_tail(&dent->dent_link, &later_anchor);
2020-09-12 20:29:40 +00:00
_reconnect_dent(NULL, dent);
continue;
}
if (!cookie->some_ordered) {
2020-03-04 11:37:07 +00:00
char *check;
/* Do not add _new_ dents when deleted.
* Existing ones are treated in get_inode().
*/
check = mars_readlink(dent->d_path, NULL);
2020-03-04 11:37:07 +00:00
if (check && !strcmp(check, MARS_DELETED_STR)) {
brick_string_free(check);
mars_free_dent(cookie->global, dent);
continue;
}
brick_string_free(check);
}
2020-09-11 12:50:02 +00:00
cookie->global->nr_ordered++;
2020-07-16 04:13:50 +00:00
get_inode(dent->d_path, dent, true);
_mars_order(cookie, dent);
}
/* Append the whole unordered list.
* This is done on return from recursion, so searching
* remains possible.
*/
if (!list_empty(&later_anchor)) {
struct list_head *a = cookie->global->dent_anchor.prev;
struct list_head *b = later_anchor.next;
struct list_head *c = later_anchor.prev;
struct list_head *d = &cookie->global->dent_anchor;
_list_connect(a, b);
_list_connect(c, d);
2020-03-03 10:28:09 +00:00
}
2020-09-12 16:58:40 +00:00
fatal: ;
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;
2020-10-19 14:10:47 +00:00
int i;
2011-07-22 10:43:40 +00:00
int status = 0;
2020-10-19 14:10:47 +00:00
/* check for names to skip */
for (i = 0; ; ) {
const struct skip_info *check;
check = &skips[i++];
if (!check->name)
break;
if (!strncmp(cookie->path, check->name, check->len))
goto done;
}
2020-07-29 10:04:08 +00:00
/* Performance optimization.
* Skip readdir() when not activated and no device-$host exists
*/
if (cookie->parent &&
cookie->parent->d_skip_fn) {
cookie->parent->d_skip_fn(cookie->parent);
if (cookie->parent->d_no_scan &&
!cookie->parent->d_running) {
MARS_DBG("scan_skip '%s'\n", cookie->parent->d_path);
goto done;
}
}
2011-07-22 10:43:40 +00:00
oldfs = get_fs();
set_fs(KERNEL_DS);
2011-07-22 10:43:40 +00:00
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
2020-09-11 12:50:02 +00:00
cookie->global->nr_readdir++;
2011-07-22 10:43:40 +00:00
for (;;) {
// remove_this
#ifdef MARS_HAS_ITERATE_DIR
struct mars_dir_context buf = {
.ctx.actor = mars_filler,
.cookie = cookie,
};
cookie->hit = false;
status = iterate_dir(f, &buf.ctx);
#else
cookie->hit = false;
status = vfs_readdir(f, mars_filler, cookie);
#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);
2020-07-29 10:04:08 +00:00
done:
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-22 21:17:49 +00:00
#define MARS_PREFIX_RESOURCE "/mars/resource-"
static
bool has_subtree_prefix(const char *path)
{
bool res;
res = !strncmp(path,
MARS_PREFIX_RESOURCE,
sizeof(MARS_PREFIX_RESOURCE) - 1);
return res;
}
static
const char *subtree_prefix(const char *path)
{
char *copy = brick_strdup(path);
char *tmp = copy;
int count = 3;
while (*tmp) {
if (*tmp++ != '/')
continue;
if (--count > 0)
continue;
tmp[-1] = '\0';
return copy;
}
brick_string_free(copy);
return NULL;
}
2020-02-23 18:03:59 +00:00
static
int _op_scan(struct say_channel **say_channel,
struct mars_global *global,
2020-07-17 07:40:34 +00:00
const char *path_list,
2020-02-23 18:03:59 +00:00
int allocsize,
mars_dent_checker_fn checker,
int maxdepth,
bool use_subtree,
2020-02-23 18:03:59 +00:00
int version,
bool *found_dir,
bool has_dir_list,
bool some_ordered)
2020-02-23 18:03:59 +00:00
{
struct list_head *tmp;
int total_status = 0;
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);
int status;
// treat any member only once during this invocation
if (dent->d_version == version)
continue;
dent->d_version = version;
if (say_channel)
bind_to_dent(dent, say_channel);
2020-02-23 18:03:59 +00:00
//MARS_IO("reading inode '%s'\n", dent->d_path);
2020-03-04 11:37:07 +00:00
status = get_inode(dent->d_path, dent, some_ordered);
2020-02-23 18:03:59 +00:00
total_status |= status;
// mark gone dents for removal
if (unlikely(status < 0) && list_empty(&dent->brick_list))
dent->d_killme = true;
/* Recurse into subdirectories.
* Insert either into the flat list, or create
* a new subtree.
*/
2020-02-23 18:03:59 +00:00
if (S_ISDIR(dent->new_stat.mode) &&
(maxdepth <= 0 ||
dent->d_depth <= maxdepth) &&
(!dent->d_no_scan ||
dent->d_running ||
dent->d_depth <= 1) &&
2020-02-23 18:03:59 +00:00
(!has_dir_list ||
dent->d_depth > 0 ||
dir_path_is_in(dent->d_path, path_list))) {
struct mars_cookie sub_cookie = {
.global = global,
.checker = checker,
.path = dent->d_path,
.allocsize = allocsize,
.check_parent = dent,
.parent = some_ordered ? NULL : dent,
2020-02-23 18:03:59 +00:00
.tmp_anchor = LIST_HEAD_INIT(sub_cookie.tmp_anchor),
.depth = dent->d_depth + 1,
.some_ordered = some_ordered,
2020-02-23 18:03:59 +00:00
};
if (say_channel && use_subtree &&
has_subtree_prefix(dent->d_path)) {
if (!dent->d_subtree) {
dent->d_subtree = alloc_mars_global();
}
sub_cookie.global = dent->d_subtree;
2020-09-11 12:50:02 +00:00
sub_cookie.global->nr_readdir = 0;
sub_cookie.global->nr_readitem = 0;
sub_cookie.global->nr_ordered = 0;
sub_cookie.global->nr_unordered = 0;
}
global->has_subtrees = true;
2020-02-23 18:03:59 +00:00
*found_dir = true;
status = _mars_readdir(&sub_cookie);
total_status |= status;
if (status < 0) {
MARS_INF("forward: status %d on '%s'\n", status, dent->d_path);
}
if (dent->d_subtree)
total_status |=
_op_scan(NULL,
dent->d_subtree,
path_list,
allocsize, checker,
maxdepth, use_subtree,
version,
found_dir, has_dir_list,
false);
2020-02-23 18:03:59 +00:00
}
}
return total_status;
}
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;
if (dent->d_no_scan && !dent->d_running) {
MARS_DBG("scan_skip '%s'\n", dent->d_path);
continue;
}
2020-02-23 15:31:23 +00:00
up_read(&global->dent_mutex);
brick_yield();
2020-02-23 16:13:10 +00:00
if (say_channel)
bind_to_dent(dent, say_channel);
2020-02-23 15:31:23 +00:00
2020-02-23 16:13:10 +00:00
/* Caution: the order is important.
* First visit the parent, then descend into
* the subtree (when present).
*/
2020-02-23 15:31:23 +00:00
status = worker(buf, dent, w_fl1, w_fl2);
total_status |= status;
2020-02-23 16:13:10 +00:00
if (dent->d_subtree)
total_status |=
_op_forward(NULL,
dent->d_subtree,
worker, buf,
w_fl1, w_fl2);
down_read(&global->dent_mutex);
2020-02-23 15:31:23 +00:00
}
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;
if (dent->d_no_scan && !dent->d_running) {
MARS_DBG("scan_skip '%s'\n", dent->d_path);
continue;
}
2020-02-23 15:50:46 +00:00
up_read(&global->dent_mutex);
brick_yield();
if (say_channel)
bind_to_dent(dent, say_channel);
/* Caution: the order is important.
* When running backwards, we first need to
* descend into the subtree (when present)
* before visiting the parent.
*/
if (dent->d_subtree)
total_status |=
_op_backward(NULL,
dent->d_subtree,
worker, buf);
2020-02-23 15:50:46 +00:00
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_subtree)
_op_remove(NULL, dent->d_subtree);
2020-02-23 15:36:41 +00:00
if (!dent->d_killme)
continue;
2020-03-05 17:55:12 +00:00
if (dent->d_child_count)
continue;
2020-09-15 08:11:26 +00:00
if (atomic_read(&dent->d_count))
continue;
2020-03-05 17:55:12 +00:00
if (!list_empty(&dent->brick_list))
continue;
2020-02-23 15:36:41 +00:00
if (say_channel)
bind_to_dent(dent, say_channel);
2020-02-23 15:36:41 +00:00
MARS_DBG("killing dent '%s'\n", dent->d_path);
list_del_init(&dent->dent_link);
list_del_init(&dent->dent_hash_link);
mars_free_dent(NULL, dent);
}
up_write(&global->dent_mutex);
}
/* Stripped-down version for peer metadata exchange
*/
int mars_get_dent_list(struct mars_global *global,
2020-07-17 07:40:34 +00:00
const char *path_list,
int allocsize,
mars_dent_checker_fn checker,
int maxdepth)
{
static int version = 0;
char *startname = brick_strdup(path_list);
struct mars_cookie cookie = {
.global = global,
.checker = checker,
.path = startname,
.tmp_anchor = LIST_HEAD_INIT(cookie.tmp_anchor),
.allocsize = allocsize,
.depth = 0,
.some_ordered = true,
};
struct say_channel *say_channel = NULL;
char *ptr;
int total_status;
bool found_dir = false;
bool has_dir_list = false;
ptr = strchr(startname, '|');
if (ptr) {
*ptr = '\0';
has_dir_list = true;
}
global->global_version = ++version;
down_write(&global->dent_mutex);
2020-09-11 12:50:02 +00:00
global->nr_readdir = 0;
global->nr_readitem = 0;
global->nr_ordered = 0;
global->nr_unordered = 0;
total_status = _mars_readdir(&cookie);
total_status =
_op_scan(&say_channel,
global,
path_list,
allocsize,
checker,
maxdepth,
false,
version,
&found_dir,
has_dir_list,
true);
up_write(&global->dent_mutex);
2020-05-07 09:44:34 +00:00
brick_string_free(startname);
return total_status;
}
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,
2020-02-23 14:38:55 +00:00
int maxdepth,
bool use_subtree)
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,
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
int rounds = 0;
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);
2020-09-11 12:50:02 +00:00
global->nr_readdir = 0;
global->nr_readitem = 0;
global->nr_ordered = 0;
global->nr_unordered = 0;
2011-07-22 10:43:40 +00:00
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.
*/
2020-02-23 18:03:59 +00:00
total_status |=
_op_scan(&say_channel,
global, path_list,
allocsize, checker,
maxdepth, use_subtree,
2020-02-23 18:03:59 +00:00
version,
&found_dir,
has_dir_list,
false);
2015-01-19 12:33:00 +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
2020-09-14 08:03:16 +00:00
struct mars_dent *_mars_find_dent(struct list_head *anchor, const char *path)
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;
tmp_dent = container_of(tmp, struct mars_dent, dent_hash_link);
2020-09-12 16:58:40 +00:00
CHECK_PTR(tmp_dent, done);
2011-07-22 10:43:40 +00:00
2020-09-12 16:58:40 +00:00
if (unlikely(!tmp_dent->d_path)) {
MARS_ERR("dent %p has empty path\n", tmp_dent);
2020-07-01 11:56:38 +00:00
continue;
2020-09-12 16:58:40 +00:00
}
2011-07-22 10:43:40 +00:00
if (!strcmp(tmp_dent->d_path, path)) {
2020-09-12 16:58:40 +00:00
if (unlikely(tmp_dent->d_unordered))
MARS_ERR("found unordered '%s'\n",
tmp_dent->d_path);
2011-07-22 10:43:40 +00:00
res = tmp_dent;
break;
}
}
2020-09-12 16:58:40 +00:00
done:
2011-07-22 10:43:40 +00:00
return res;
}
2020-09-14 08:03:16 +00:00
struct mars_dent *mars_find_dent(struct mars_global *global,
const char *path)
2011-07-22 10:43:40 +00:00
{
struct list_head *hash_anchor;
2020-02-22 21:17:49 +00:00
struct mars_dent *res = NULL;
unsigned int hash;
2019-03-12 13:36:25 +00:00
2020-09-14 08:03:16 +00:00
if (!global || !path)
return NULL;
2019-03-12 13:36:25 +00:00
if (global->has_subtrees && has_subtree_prefix(path)) {
2020-02-22 21:17:49 +00:00
const char *prefix = subtree_prefix(path);
if (prefix) {
struct list_head *sub_hash_anchor;
2020-02-22 21:17:49 +00:00
struct mars_dent *sub;
unsigned int sub_hash;
2020-02-22 21:17:49 +00:00
sub_hash = dent_hash(prefix, strlen(prefix));
sub_hash_anchor = DENT_HASH_ANCHOR(global, sub_hash);
2020-09-14 08:03:16 +00:00
down_read(&global->dent_mutex);
sub = _mars_find_dent(sub_hash_anchor, prefix);
2020-09-14 08:03:16 +00:00
if (sub && sub->d_subtree) {
hash = dent_hash(path, strlen(path));
hash_anchor = DENT_HASH_ANCHOR(sub->d_subtree, hash);
2020-09-14 08:03:16 +00:00
down_read(&sub->d_subtree->dent_mutex);
up_read(&global->dent_mutex);
res = _mars_find_dent(hash_anchor, path);
2020-09-14 08:03:16 +00:00
up_read(&sub->d_subtree->dent_mutex);
} else {
up_read(&global->dent_mutex);
res = sub;
2020-02-22 21:17:49 +00:00
}
2020-09-14 08:03:16 +00:00
brick_string_free(prefix);
2020-02-22 21:17:49 +00:00
return res;
2020-09-14 08:03:16 +00:00
}
2020-02-22 21:17:49 +00:00
}
hash = dent_hash(path, strlen(path));
hash_anchor = DENT_HASH_ANCHOR(global, hash);
down_read(&global->dent_mutex);
res = _mars_find_dent(hash_anchor, path);
up_read(&global->dent_mutex);
2011-07-22 10:43:40 +00:00
return res;
}
2020-02-22 21:17:49 +00:00
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;
2011-09-27 08:46:14 +00:00
2020-09-12 16:58:40 +00:00
CHECK_PTR(dent, fatal);
MARS_IO("%p path='%s'\n", dent, dent->d_path);
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);
2011-07-22 10:43:40 +00:00
CHECK_HEAD_EMPTY(&dent->brick_list);
2020-02-22 21:17:49 +00:00
if (dent->d_subtree) {
2020-09-16 16:21:22 +00:00
mars_free_dent_all(dent->d_subtree);
2020-08-20 08:11:32 +00:00
free_mars_global(dent->d_subtree);
2020-02-22 21:17:49 +00:00
}
2020-09-15 08:33:37 +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-07-22 10:43:40 +00:00
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);
2020-09-12 16:58:40 +00:00
if (dent->d_parent) {
CHECK_PTR(dent->d_parent, fatal);
dent->d_parent->d_child_count--;
2020-09-12 16:58:40 +00:00
dent->d_parent = NULL;
}
if (unlikely(dent->d_child_count)) {
MARS_ERR("dent '%s' child_count=%d\n",
dent->d_path,
dent->d_child_count);
goto fatal;
}
if (unlikely(atomic_read(&dent->d_count))) {
MARS_ERR("dent '%s' d_count=%d\n",
dent->d_path,
atomic_read(&dent->d_count));
goto fatal;
}
2011-08-12 11:09:48 +00:00
brick_mem_free(dent);
2020-09-12 16:58:40 +00:00
fatal: ;
2011-07-22 10:43:40 +00:00
}
EXPORT_SYMBOL_GPL(mars_free_dent);
2020-09-16 16:21:22 +00:00
void mars_free_dent_all(struct mars_global *global)
2011-07-22 10:43:40 +00:00
{
2020-09-16 16:21:22 +00:00
struct list_head *anchor = &global->dent_anchor;
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
}
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);
2020-09-12 16:58:40 +00:00
CHECK_PTR(dent, fatal);
2011-08-19 10:49:16 +00:00
list_del_init(&dent->dent_link);
list_del_init(&dent->dent_hash_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
}
2020-09-12 16:58:40 +00:00
fatal: ;
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-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);
int mars_kill_brick_when_possible(struct mars_global *global,
2020-03-07 06:07:52 +00:00
const struct mars_brick_type *type_list[],
bool even_on)
{
struct list_head *anchor = &global->brick_anchor;
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;
2020-03-07 05:31:35 +00:00
brick = container_of(tmp, struct mars_brick, global_brick_link);
// 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;
}
2020-03-07 06:07:52 +00:00
/* Only kill the right brick types
*/
if (type_list) {
int index = 0;
const struct mars_brick_type *this_type = type_list[index++];
bool matches = false;
while (this_type) {
if (this_type == brick->type) {
matches = true;
break;
}
this_type = type_list[index++];
}
/* also kill any non-working bricks */
if (!matches &&
(brick->power.button || !brick->power.led_off))
2020-03-07 06:07:52 +00:00
continue;
}
// only kill bricks which have no resources allocated
count = atomic_read(&brick->mref_object_layout.alloc_count);
if (count > 0) {
mars_trigger();
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) {
get_real_lamport(&brick->kill_stamp);
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) {
get_real_lamport(&brick->kill_stamp);
brick->kill_stamp.tv_sec += 3;
}
get_real_lamport(&now);
if (lamport_time_compare(&now, &brick->kill_stamp) <= 0 &&
global &&
global->global_power.button) {
mars_trigger();
continue;
}
if (brick->kill_round++ < 1) {
mars_trigger();
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) {
mars_trigger();
2017-01-08 07:49:30 +00:00
continue;
}
2017-01-08 07:49:30 +00:00
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
#define MAX_PREV_COUNT 8
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;
char *paths[MAX_PREV_COUNT];
struct mars_brick *prev[MAX_PREV_COUNT];
bool switch_state;
2011-07-22 10:43:40 +00:00
int i;
int status;
2011-07-22 10:43:40 +00:00
if (prev_count >= MAX_PREV_COUNT) {
MARS_ERR("internal: prev_count=%d too high!\n",
prev_count);
goto err;
}
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
2020-06-10 05:31:59 +00:00
if (!switch_state &&
brick->power.button &&
mars_check_outputs(brick)) {
MARS_DBG("KEEP '%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) {
MARS_DBG("predecessor power is not on\n");
if (!brick || !brick->power.button)
switch_state = false;
}
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)
2020-07-28 16:45:28 +00:00
status = mars_power_button((void *)brick, switch_state, false);
/* retry when a stray symlink during O_NOFOLLOW was the reason */
if (status == -ELOOP && switch_state) {
mars_unlink(new_path);
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++) {
brick_string_free(paths[i]);
2011-07-22 10:43:40 +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);
2020-03-24 07:20:39 +00:00
ordered_symlink(val, name, NULL);
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' "
2021-01-27 11:46:26 +00:00
"create_stamp = %lld.%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),
2021-01-27 11:46:26 +00:00
(s64)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);
}
}
}
2020-08-25 08:30:58 +00:00
static
void _show_dent_list(struct mars_global *global, int *count)
{
struct list_head *tmp;
2020-09-11 11:28:45 +00:00
int flat_count = 0;
int sub_count = 0;
2020-08-25 08:30:58 +00:00
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);
2021-01-27 11:46:26 +00:00
MARS_STAT("dent %d%d %d '%s' '%s' stamp=%lld.%09ld\n",
2020-08-25 08:30:58 +00:00
dent->d_no_scan,
dent->d_running,
dent->d_class,
SAFE_STR(dent->d_path),
SAFE_STR(dent->new_link),
2021-01-27 11:46:26 +00:00
(s64)dent->new_stat.mtime.tv_sec, dent->new_stat.mtime.tv_nsec);
2020-08-25 08:30:58 +00:00
(*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));
}
2020-09-11 11:28:45 +00:00
if (dent->d_subtree) {
2020-08-25 08:30:58 +00:00
_show_dent_list(dent->d_subtree, count);
2020-09-11 11:28:45 +00:00
sub_count++;
} else {
flat_count++;
}
2020-08-25 08:30:58 +00:00
}
2020-09-11 11:28:45 +00:00
MARS_STAT("flat_count=%d sub_count=%d\n", flat_count, sub_count);
2020-09-11 12:50:02 +00:00
MARS_STAT("nr_readdir=%d nr_readitem=%d nr_ordered=%d nr_unordered=%d\n",
global->nr_readdir,
global->nr_readitem,
global->nr_ordered,
global->nr_unordered);
2020-08-25 08:30:58 +00:00
up_read(&global->dent_mutex);
}
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;
2020-08-25 08:30:58 +00:00
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);
2020-08-25 08:30:58 +00:00
_show_dent_list(global, &dent_count);
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");
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");
}