Commit 63a16f90 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2

* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2:
  [PATCH] ocfs2: Release mutex in error handling code
  [PATCH] ocfs2: Fix oops when racing files truncates with writes into an mmap region
  [PATCH 2/2] ocfs2: Fix race between mount and recovery
  [PATCH 1/2] ocfs2: Add counter in struct ocfs2_dinode to track journal replays
  [PATCH] configfs: Convenience macros for attribute definition.
  [PATCH] configfs: Pin configfs subsystems separately from new config_items.
  [PATCH] configfs: Fix open directory making rmdir() fail
  [PATCH] configfs: Lock new directory inodes before removing on cleanup after failure
  [PATCH] configfs: Prevent userspace from creating new entries under attaching directories
  [PATCH] configfs: Fix failing symlink() making rmdir() fail
  [PATCH] configfs: Fix symlink() to a removing item
  [PATCH] configfs: Include linux/err.h in linux/configfs.h
parents 5adf2b03 c259ae52
...@@ -311,9 +311,20 @@ the subsystem must be ready for it. ...@@ -311,9 +311,20 @@ the subsystem must be ready for it.
[An Example] [An Example]
The best example of these basic concepts is the simple_children The best example of these basic concepts is the simple_children
subsystem/group and the simple_child item in configfs_example.c It subsystem/group and the simple_child item in configfs_example_explicit.c
shows a trivial object displaying and storing an attribute, and a simple and configfs_example_macros.c. It shows a trivial object displaying and
group creating and destroying these children. storing an attribute, and a simple group creating and destroying these
children.
The only difference between configfs_example_explicit.c and
configfs_example_macros.c is how the attributes of the childless item
are defined. The childless item has extended attributes, each with
their own show()/store() operation. This follows a convention commonly
used in sysfs. configfs_example_explicit.c creates these attributes
by explicitly defining the structures involved. Conversely
configfs_example_macros.c uses some convenience macros from configfs.h
to define the attributes. These macros are similar to their sysfs
counterparts.
[Hierarchy Navigation and the Subsystem Mutex] [Hierarchy Navigation and the Subsystem Mutex]
......
/* /*
* vim: noexpandtab ts=8 sts=0 sw=8: * vim: noexpandtab ts=8 sts=0 sw=8:
* *
* configfs_example.c - This file is a demonstration module containing * configfs_example_explicit.c - This file is a demonstration module
* a number of configfs subsystems. * containing a number of configfs subsystems. It explicitly defines
* each structure without using the helper macros defined in
* configfs.h.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public * modify it under the terms of the GNU General Public
...@@ -281,7 +283,6 @@ static struct config_item *simple_children_make_item(struct config_group *group, ...@@ -281,7 +283,6 @@ static struct config_item *simple_children_make_item(struct config_group *group,
if (!simple_child) if (!simple_child)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
config_item_init_type_name(&simple_child->item, name, config_item_init_type_name(&simple_child->item, name,
&simple_child_type); &simple_child_type);
...@@ -302,8 +303,8 @@ static struct configfs_attribute *simple_children_attrs[] = { ...@@ -302,8 +303,8 @@ static struct configfs_attribute *simple_children_attrs[] = {
}; };
static ssize_t simple_children_attr_show(struct config_item *item, static ssize_t simple_children_attr_show(struct config_item *item,
struct configfs_attribute *attr, struct configfs_attribute *attr,
char *page) char *page)
{ {
return sprintf(page, return sprintf(page,
"[02-simple-children]\n" "[02-simple-children]\n"
...@@ -318,7 +319,7 @@ static void simple_children_release(struct config_item *item) ...@@ -318,7 +319,7 @@ static void simple_children_release(struct config_item *item)
} }
static struct configfs_item_operations simple_children_item_ops = { static struct configfs_item_operations simple_children_item_ops = {
.release = simple_children_release, .release = simple_children_release,
.show_attribute = simple_children_attr_show, .show_attribute = simple_children_attr_show,
}; };
...@@ -368,7 +369,6 @@ static struct config_group *group_children_make_group(struct config_group *group ...@@ -368,7 +369,6 @@ static struct config_group *group_children_make_group(struct config_group *group
if (!simple_children) if (!simple_children)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
config_group_init_type_name(&simple_children->group, name, config_group_init_type_name(&simple_children->group, name,
&simple_children_type); &simple_children_type);
...@@ -387,8 +387,8 @@ static struct configfs_attribute *group_children_attrs[] = { ...@@ -387,8 +387,8 @@ static struct configfs_attribute *group_children_attrs[] = {
}; };
static ssize_t group_children_attr_show(struct config_item *item, static ssize_t group_children_attr_show(struct config_item *item,
struct configfs_attribute *attr, struct configfs_attribute *attr,
char *page) char *page)
{ {
return sprintf(page, return sprintf(page,
"[03-group-children]\n" "[03-group-children]\n"
......
This diff is collapsed.
...@@ -49,8 +49,10 @@ struct configfs_dirent { ...@@ -49,8 +49,10 @@ struct configfs_dirent {
#define CONFIGFS_USET_DEFAULT 0x0080 #define CONFIGFS_USET_DEFAULT 0x0080
#define CONFIGFS_USET_DROPPING 0x0100 #define CONFIGFS_USET_DROPPING 0x0100
#define CONFIGFS_USET_IN_MKDIR 0x0200 #define CONFIGFS_USET_IN_MKDIR 0x0200
#define CONFIGFS_USET_CREATING 0x0400
#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
extern struct mutex configfs_symlink_mutex;
extern spinlock_t configfs_dirent_lock; extern spinlock_t configfs_dirent_lock;
extern struct vfsmount * configfs_mount; extern struct vfsmount * configfs_mount;
...@@ -66,6 +68,7 @@ extern void configfs_inode_exit(void); ...@@ -66,6 +68,7 @@ extern void configfs_inode_exit(void);
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
extern int configfs_make_dirent(struct configfs_dirent *, extern int configfs_make_dirent(struct configfs_dirent *,
struct dentry *, void *, umode_t, int); struct dentry *, void *, umode_t, int);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int); extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int);
extern void configfs_hash_and_remove(struct dentry * dir, const char * name); extern void configfs_hash_and_remove(struct dentry * dir, const char * name);
......
This diff is collapsed.
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
#include <linux/configfs.h> #include <linux/configfs.h>
#include "configfs_internal.h" #include "configfs_internal.h"
/* Protects attachments of new symlinks */
DEFINE_MUTEX(configfs_symlink_mutex);
static int item_depth(struct config_item * item) static int item_depth(struct config_item * item)
{ {
struct config_item * p = item; struct config_item * p = item;
...@@ -73,11 +76,20 @@ static int create_link(struct config_item *parent_item, ...@@ -73,11 +76,20 @@ static int create_link(struct config_item *parent_item,
struct configfs_symlink *sl; struct configfs_symlink *sl;
int ret; int ret;
ret = -ENOENT;
if (!configfs_dirent_is_ready(target_sd))
goto out;
ret = -ENOMEM; ret = -ENOMEM;
sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL);
if (sl) { if (sl) {
sl->sl_target = config_item_get(item); sl->sl_target = config_item_get(item);
spin_lock(&configfs_dirent_lock); spin_lock(&configfs_dirent_lock);
if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
spin_unlock(&configfs_dirent_lock);
config_item_put(item);
kfree(sl);
return -ENOENT;
}
list_add(&sl->sl_list, &target_sd->s_links); list_add(&sl->sl_list, &target_sd->s_links);
spin_unlock(&configfs_dirent_lock); spin_unlock(&configfs_dirent_lock);
ret = configfs_create_link(sl, parent_item->ci_dentry, ret = configfs_create_link(sl, parent_item->ci_dentry,
...@@ -91,6 +103,7 @@ static int create_link(struct config_item *parent_item, ...@@ -91,6 +103,7 @@ static int create_link(struct config_item *parent_item,
} }
} }
out:
return ret; return ret;
} }
...@@ -120,6 +133,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna ...@@ -120,6 +133,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
{ {
int ret; int ret;
struct nameidata nd; struct nameidata nd;
struct configfs_dirent *sd;
struct config_item *parent_item; struct config_item *parent_item;
struct config_item *target_item; struct config_item *target_item;
struct config_item_type *type; struct config_item_type *type;
...@@ -128,9 +142,19 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna ...@@ -128,9 +142,19 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
if (dentry->d_parent == configfs_sb->s_root) if (dentry->d_parent == configfs_sb->s_root)
goto out; goto out;
sd = dentry->d_parent->d_fsdata;
/*
* Fake invisibility if dir belongs to a group/default groups hierarchy
* being attached
*/
ret = -ENOENT;
if (!configfs_dirent_is_ready(sd))
goto out;
parent_item = configfs_get_config_item(dentry->d_parent); parent_item = configfs_get_config_item(dentry->d_parent);
type = parent_item->ci_type; type = parent_item->ci_type;
ret = -EPERM;
if (!type || !type->ct_item_ops || if (!type || !type->ct_item_ops ||
!type->ct_item_ops->allow_link) !type->ct_item_ops->allow_link)
goto out_put; goto out_put;
...@@ -141,7 +165,9 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna ...@@ -141,7 +165,9 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
ret = type->ct_item_ops->allow_link(parent_item, target_item); ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) { if (!ret) {
mutex_lock(&configfs_symlink_mutex);
ret = create_link(parent_item, target_item, dentry); ret = create_link(parent_item, target_item, dentry);
mutex_unlock(&configfs_symlink_mutex);
if (ret && type->ct_item_ops->drop_link) if (ret && type->ct_item_ops->drop_link)
type->ct_item_ops->drop_link(parent_item, type->ct_item_ops->drop_link(parent_item,
target_item); target_item);
......
...@@ -1073,12 +1073,15 @@ static void ocfs2_write_failure(struct inode *inode, ...@@ -1073,12 +1073,15 @@ static void ocfs2_write_failure(struct inode *inode,
for(i = 0; i < wc->w_num_pages; i++) { for(i = 0; i < wc->w_num_pages; i++) {
tmppage = wc->w_pages[i]; tmppage = wc->w_pages[i];
if (ocfs2_should_order_data(inode)) if (page_has_buffers(tmppage)) {
walk_page_buffers(wc->w_handle, page_buffers(tmppage), if (ocfs2_should_order_data(inode))
from, to, NULL, walk_page_buffers(wc->w_handle,
ocfs2_journal_dirty_data); page_buffers(tmppage),
from, to, NULL,
block_commit_write(tmppage, from, to); ocfs2_journal_dirty_data);
block_commit_write(tmppage, from, to);
}
} }
} }
...@@ -1901,12 +1904,14 @@ int ocfs2_write_end_nolock(struct address_space *mapping, ...@@ -1901,12 +1904,14 @@ int ocfs2_write_end_nolock(struct address_space *mapping,
to = PAGE_CACHE_SIZE; to = PAGE_CACHE_SIZE;
} }
if (ocfs2_should_order_data(inode)) if (page_has_buffers(tmppage)) {
walk_page_buffers(wc->w_handle, page_buffers(tmppage), if (ocfs2_should_order_data(inode))
from, to, NULL, walk_page_buffers(wc->w_handle,
ocfs2_journal_dirty_data); page_buffers(tmppage),
from, to, NULL,
block_commit_write(tmppage, from, to); ocfs2_journal_dirty_data);
block_commit_write(tmppage, from, to);
}
} }
out_write_size: out_write_size:
......
...@@ -1766,8 +1766,8 @@ static int __ocfs2_change_file_space(struct file *file, struct inode *inode, ...@@ -1766,8 +1766,8 @@ static int __ocfs2_change_file_space(struct file *file, struct inode *inode,
out_rw_unlock: out_rw_unlock:
ocfs2_rw_unlock(inode, 1); ocfs2_rw_unlock(inode, 1);
mutex_unlock(&inode->i_mutex);
out: out:
mutex_unlock(&inode->i_mutex);
return ret; return ret;
} }
......
...@@ -57,7 +57,7 @@ static int __ocfs2_recovery_thread(void *arg); ...@@ -57,7 +57,7 @@ static int __ocfs2_recovery_thread(void *arg);
static int ocfs2_commit_cache(struct ocfs2_super *osb); static int ocfs2_commit_cache(struct ocfs2_super *osb);
static int ocfs2_wait_on_mount(struct ocfs2_super *osb); static int ocfs2_wait_on_mount(struct ocfs2_super *osb);
static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
int dirty); int dirty, int replayed);
static int ocfs2_trylock_journal(struct ocfs2_super *osb, static int ocfs2_trylock_journal(struct ocfs2_super *osb,
int slot_num); int slot_num);
static int ocfs2_recover_orphans(struct ocfs2_super *osb, static int ocfs2_recover_orphans(struct ocfs2_super *osb,
...@@ -562,8 +562,18 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, int *dirty) ...@@ -562,8 +562,18 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, int *dirty)
return status; return status;
} }
static void ocfs2_bump_recovery_generation(struct ocfs2_dinode *di)
{
le32_add_cpu(&(di->id1.journal1.ij_recovery_generation), 1);
}
static u32 ocfs2_get_recovery_generation(struct ocfs2_dinode *di)
{
return le32_to_cpu(di->id1.journal1.ij_recovery_generation);
}
static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
int dirty) int dirty, int replayed)
{ {
int status; int status;
unsigned int flags; unsigned int flags;
...@@ -593,6 +603,9 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, ...@@ -593,6 +603,9 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
flags &= ~OCFS2_JOURNAL_DIRTY_FL; flags &= ~OCFS2_JOURNAL_DIRTY_FL;
fe->id1.journal1.ij_flags = cpu_to_le32(flags); fe->id1.journal1.ij_flags = cpu_to_le32(flags);
if (replayed)
ocfs2_bump_recovery_generation(fe);
status = ocfs2_write_block(osb, bh, journal->j_inode); status = ocfs2_write_block(osb, bh, journal->j_inode);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
...@@ -667,7 +680,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb) ...@@ -667,7 +680,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
* Do not toggle if flush was unsuccessful otherwise * Do not toggle if flush was unsuccessful otherwise
* will leave dirty metadata in a "clean" journal * will leave dirty metadata in a "clean" journal
*/ */
status = ocfs2_journal_toggle_dirty(osb, 0); status = ocfs2_journal_toggle_dirty(osb, 0, 0);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
} }
...@@ -710,7 +723,7 @@ static void ocfs2_clear_journal_error(struct super_block *sb, ...@@ -710,7 +723,7 @@ static void ocfs2_clear_journal_error(struct super_block *sb,
} }
} }
int ocfs2_journal_load(struct ocfs2_journal *journal, int local) int ocfs2_journal_load(struct ocfs2_journal *journal, int local, int replayed)
{ {
int status = 0; int status = 0;
struct ocfs2_super *osb; struct ocfs2_super *osb;
...@@ -729,7 +742,7 @@ int ocfs2_journal_load(struct ocfs2_journal *journal, int local) ...@@ -729,7 +742,7 @@ int ocfs2_journal_load(struct ocfs2_journal *journal, int local)
ocfs2_clear_journal_error(osb->sb, journal->j_journal, osb->slot_num); ocfs2_clear_journal_error(osb->sb, journal->j_journal, osb->slot_num);
status = ocfs2_journal_toggle_dirty(osb, 1); status = ocfs2_journal_toggle_dirty(osb, 1, replayed);
if (status < 0) { if (status < 0) {
mlog_errno(status); mlog_errno(status);
goto done; goto done;
...@@ -771,7 +784,7 @@ int ocfs2_journal_wipe(struct ocfs2_journal *journal, int full) ...@@ -771,7 +784,7 @@ int ocfs2_journal_wipe(struct ocfs2_journal *journal, int full)
goto bail; goto bail;
} }
status = ocfs2_journal_toggle_dirty(journal->j_osb, 0); status = ocfs2_journal_toggle_dirty(journal->j_osb, 0, 0);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
...@@ -1034,6 +1047,12 @@ static int __ocfs2_recovery_thread(void *arg) ...@@ -1034,6 +1047,12 @@ static int __ocfs2_recovery_thread(void *arg)
spin_unlock(&osb->osb_lock); spin_unlock(&osb->osb_lock);
mlog(0, "All nodes recovered\n"); mlog(0, "All nodes recovered\n");
/* Refresh all journal recovery generations from disk */
status = ocfs2_check_journals_nolocks(osb);
status = (status == -EROFS) ? 0 : status;
if (status < 0)
mlog_errno(status);
ocfs2_super_unlock(osb, 1); ocfs2_super_unlock(osb, 1);
/* We always run recovery on our own orphan dir - the dead /* We always run recovery on our own orphan dir - the dead
...@@ -1096,6 +1115,42 @@ void ocfs2_recovery_thread(struct ocfs2_super *osb, int node_num) ...@@ -1096,6 +1115,42 @@ void ocfs2_recovery_thread(struct ocfs2_super *osb, int node_num)
mlog_exit_void(); mlog_exit_void();
} }
static int ocfs2_read_journal_inode(struct ocfs2_super *osb,
int slot_num,
struct buffer_head **bh,
struct inode **ret_inode)
{
int status = -EACCES;
struct inode *inode = NULL;
BUG_ON(slot_num >= osb->max_slots);
inode = ocfs2_get_system_file_inode(osb, JOURNAL_SYSTEM_INODE,
slot_num);
if (!inode || is_bad_inode(inode)) {
mlog_errno(status);
goto bail;
}
SET_INODE_JOURNAL(inode);
status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, bh, 0, inode);
if (status < 0) {
mlog_errno(status);
goto bail;
}
status = 0;
bail:
if (inode) {
if (status || !ret_inode)
iput(inode);
else
*ret_inode = inode;
}
return status;
}
/* Does the actual journal replay and marks the journal inode as /* Does the actual journal replay and marks the journal inode as
* clean. Will only replay if the journal inode is marked dirty. */ * clean. Will only replay if the journal inode is marked dirty. */
static int ocfs2_replay_journal(struct ocfs2_super *osb, static int ocfs2_replay_journal(struct ocfs2_super *osb,
...@@ -1109,22 +1164,36 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, ...@@ -1109,22 +1164,36 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
struct ocfs2_dinode *fe; struct ocfs2_dinode *fe;
journal_t *journal = NULL; journal_t *journal = NULL;
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
u32 slot_reco_gen;
inode = ocfs2_get_system_file_inode(osb, JOURNAL_SYSTEM_INODE, status = ocfs2_read_journal_inode(osb, slot_num, &bh, &inode);
slot_num); if (status) {
if (inode == NULL) {
status = -EACCES;
mlog_errno(status); mlog_errno(status);
goto done; goto done;
} }
if (is_bad_inode(inode)) {
status = -EACCES; fe = (struct ocfs2_dinode *)bh->b_data;
iput(inode); slot_reco_gen = ocfs2_get_recovery_generation(fe);
inode = NULL; brelse(bh);
mlog_errno(status); bh = NULL;
/*
* As the fs recovery is asynchronous, there is a small chance that
* another node mounted (and recovered) the slot before the recovery
* thread could get the lock. To handle that, we dirty read the journal
* inode for that slot to get the recovery generation. If it is
* different than what we expected, the slot has been recovered.
* If not, it needs recovery.
*/
if (osb->slot_recovery_generations[slot_num] != slot_reco_gen) {
mlog(0, "Slot %u already recovered (old/new=%u/%u)\n", slot_num,
osb->slot_recovery_generations[slot_num], slot_reco_gen);
osb->slot_recovery_generations[slot_num] = slot_reco_gen;
status = -EBUSY;
goto done; goto done;
} }
SET_INODE_JOURNAL(inode);
/* Continue with recovery as the journal has not yet been recovered */
status = ocfs2_inode_lock_full(inode, &bh, 1, OCFS2_META_LOCK_RECOVERY); status = ocfs2_inode_lock_full(inode, &bh, 1, OCFS2_META_LOCK_RECOVERY);
if (status < 0) { if (status < 0) {
...@@ -1138,9 +1207,12 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, ...@@ -1138,9 +1207,12 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
fe = (struct ocfs2_dinode *) bh->b_data; fe = (struct ocfs2_dinode *) bh->b_data;
flags = le32_to_cpu(fe->id1.journal1.ij_flags); flags = le32_to_cpu(fe->id1.journal1.ij_flags);
slot_reco_gen = ocfs2_get_recovery_generation(fe);
if (!(flags & OCFS2_JOURNAL_DIRTY_FL)) { if (!(flags & OCFS2_JOURNAL_DIRTY_FL)) {
mlog(0, "No recovery required for node %d\n", node_num); mlog(0, "No recovery required for node %d\n", node_num);
/* Refresh recovery generation for the slot */
osb->slot_recovery_generations[slot_num] = slot_reco_gen;
goto done; goto done;
} }
...@@ -1188,6 +1260,11 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, ...@@ -1188,6 +1260,11 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
flags &= ~OCFS2_JOURNAL_DIRTY_FL; flags &= ~OCFS2_JOURNAL_DIRTY_FL;
fe->id1.journal1.ij_flags = cpu_to_le32(flags); fe->id1.journal1.ij_flags = cpu_to_le32(flags);
/* Increment recovery generation to indicate successful recovery */
ocfs2_bump_recovery_generation(fe);
osb->slot_recovery_generations[slot_num] =
ocfs2_get_recovery_generation(fe);
status = ocfs2_write_block(osb, bh, inode); status = ocfs2_write_block(osb, bh, inode);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
...@@ -1252,6 +1329,13 @@ static int ocfs2_recover_node(struct ocfs2_super *osb, ...@@ -1252,6 +1329,13 @@ static int ocfs2_recover_node(struct ocfs2_super *osb,
status = ocfs2_replay_journal(osb, node_num, slot_num); status = ocfs2_replay_journal(osb, node_num, slot_num);
if (status < 0) { if (status < 0) {
if (status == -EBUSY) {
mlog(0, "Skipping recovery for slot %u (node %u) "
"as another node has recovered it\n", slot_num,
node_num);
status = 0;
goto done;
}
mlog_errno(status); mlog_errno(status);
goto done; goto done;
} }
...@@ -1334,12 +1418,29 @@ int ocfs2_mark_dead_nodes(struct ocfs2_super *osb) ...@@ -1334,12 +1418,29 @@ int ocfs2_mark_dead_nodes(struct ocfs2_super *osb)
{ {
unsigned int node_num; unsigned int node_num;
int status, i; int status, i;
struct buffer_head *bh = NULL;
struct ocfs2_dinode *di;
/* This is called with the super block cluster lock, so we /* This is called with the super block cluster lock, so we
* know that the slot map can't change underneath us. */ * know that the slot map can't change underneath us. */
spin_lock(&osb->osb_lock); spin_lock(&osb->osb_lock);
for (i = 0; i < osb->max_slots; i++) { for (i = 0; i < osb->max_slots; i++) {
/* Read journal inode to get the recovery generation */
status = ocfs2_read_journal_inode(osb, i, &bh, NULL);
if (status) {
mlog_errno(status);
goto bail;
}
di = (struct ocfs2_dinode *)bh->b_data;
osb->slot_recovery_generations[i] =
ocfs2_get_recovery_generation(di);
brelse(bh);
bh = NULL;
mlog(0, "Slot %u recovery generation is %u\n", i,
osb->slot_recovery_generations[i]);
if (i == osb->slot_num) if (i == osb->slot_num)
continue; continue;
...@@ -1603,49 +1704,41 @@ static int ocfs2_commit_thread(void *arg) ...@@ -1603,49 +1704,41 @@ static int ocfs2_commit_thread(void *arg)
return 0; return 0;
} }
/* Look for a dirty journal without taking any cluster locks. Used for /* Reads all the journal inodes without taking any cluster locks. Used
* hard readonly access to determine whether the file system journals * for hard readonly access to determine whether any journal requires
* require recovery. */ * recovery. Also used to refresh the recovery generation numbers after
* a journal has been recovered by another node.
*/
int ocfs2_check_journals_nolocks(struct ocfs2_super *osb) int ocfs2_check_journals_nolocks(struct ocfs2_super *osb)
{ {
int ret = 0; int ret = 0;
unsigned int slot; unsigned int slot;
struct buffer_head *di_bh; struct buffer_head *di_bh = NULL;
struct ocfs2_dinode *di; struct ocfs2_dinode *di;
struct inode *journal = NULL; int journal_dirty = 0;
for(slot = 0; slot < osb->max_slots; slot++) { for(slot = 0; slot < osb->max_slots; slot++) {
journal = ocfs2_get_system_file_inode(osb, ret = ocfs2_read_journal_inode(osb, slot, &di_bh, NULL);
JOURNAL_SYSTEM_INODE, if (ret) {
slot);
if (!journal || is_bad_inode(journal)) {
ret = -EACCES;
mlog_errno(ret);
goto out;
}
di_bh = NULL;
ret = ocfs2_read_block(osb, OCFS2_I(journal)->ip_blkno, &di_bh,
0, journal);
if (ret < 0) {
mlog_errno(ret); mlog_errno(ret);
goto out; goto out;
} }
di = (struct ocfs2_dinode *) di_bh->b_data; di = (struct ocfs2_dinode *) di_bh->b_data;
osb->slot_recovery_generations[slot] =
ocfs2_get_recovery_generation(di);
if (le32_to_cpu(di->id1.journal1.ij_flags) & if (le32_to_cpu(di->id1.journal1.ij_flags) &
OCFS2_JOURNAL_DIRTY_FL) OCFS2_JOURNAL_DIRTY_FL)
ret = -EROFS; journal_dirty = 1;
brelse(di_bh); brelse(di_bh);
if (ret) di_bh = NULL;
break;
} }
out: out:
if (journal) if (journal_dirty)
iput(journal); ret = -EROFS;
return ret; return ret;
} }
...@@ -161,7 +161,8 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, ...@@ -161,7 +161,8 @@ int ocfs2_journal_init(struct ocfs2_journal *journal,
void ocfs2_journal_shutdown(struct ocfs2_super *osb); void ocfs2_journal_shutdown(struct ocfs2_super *osb);
int ocfs2_journal_wipe(struct ocfs2_journal *journal, int ocfs2_journal_wipe(struct ocfs2_journal *journal,
int full); int full);
int ocfs2_journal_load(struct ocfs2_journal *journal, int local); int ocfs2_journal_load(struct ocfs2_journal *journal, int local,
int replayed);
int ocfs2_check_journals_nolocks(struct ocfs2_super *osb); int ocfs2_check_journals_nolocks(struct ocfs2_super *osb);
void ocfs2_recovery_thread(struct ocfs2_super *osb, void ocfs2_recovery_thread(struct ocfs2_super *osb,
int node_num); int node_num);
......
...@@ -204,6 +204,8 @@ struct ocfs2_super ...@@ -204,6 +204,8 @@ struct ocfs2_super
struct ocfs2_slot_info *slot_info; struct ocfs2_slot_info *slot_info;
u32 *slot_recovery_generations;
spinlock_t node_map_lock; spinlock_t node_map_lock;
u64 root_blkno; u64 root_blkno;
......
...@@ -660,7 +660,10 @@ struct ocfs2_dinode { ...@@ -660,7 +660,10 @@ struct ocfs2_dinode {
struct { /* Info for journal system struct { /* Info for journal system
inodes */ inodes */
__le32 ij_flags; /* Mounted, version, etc. */ __le32 ij_flags; /* Mounted, version, etc. */
__le32 ij_pad; __le32 ij_recovery_generation; /* Incremented when the
journal is recovered
after an unclean
shutdown */
} journal1; } journal1;
} id1; /* Inode type dependant 1 */ } id1; /* Inode type dependant 1 */
/*C0*/ union { /*C0*/ union {
......
...@@ -1442,6 +1442,15 @@ static int ocfs2_initialize_super(struct super_block *sb, ...@@ -1442,6 +1442,15 @@ static int ocfs2_initialize_super(struct super_block *sb,
} }
mlog(0, "max_slots for this device: %u\n", osb->max_slots); mlog(0, "max_slots for this device: %u\n", osb->max_slots);
osb->slot_recovery_generations =
kcalloc(osb->max_slots, sizeof(*osb->slot_recovery_generations),
GFP_KERNEL);
if (!osb->slot_recovery_generations) {
status = -ENOMEM;
mlog_errno(status);
goto bail;
}
init_waitqueue_head(&osb->osb_wipe_event); init_waitqueue_head(&osb->osb_wipe_event);
osb->osb_orphan_wipes = kcalloc(osb->max_slots, osb->osb_orphan_wipes = kcalloc(osb->max_slots,
sizeof(*osb->osb_orphan_wipes), sizeof(*osb->osb_orphan_wipes),
...@@ -1703,7 +1712,7 @@ static int ocfs2_check_volume(struct ocfs2_super *osb) ...@@ -1703,7 +1712,7 @@ static int ocfs2_check_volume(struct ocfs2_super *osb)
local = ocfs2_mount_local(osb); local = ocfs2_mount_local(osb);
/* will play back anything left in the journal. */ /* will play back anything left in the journal. */
status = ocfs2_journal_load(osb->journal, local); status = ocfs2_journal_load(osb->journal, local, dirty);
if (status < 0) { if (status < 0) {
mlog(ML_ERROR, "ocfs2 journal load failed! %d\n", status); mlog(ML_ERROR, "ocfs2 journal load failed! %d\n", status);
goto finally; goto finally;
...@@ -1768,6 +1777,7 @@ static void ocfs2_delete_osb(struct ocfs2_super *osb) ...@@ -1768,6 +1777,7 @@ static void ocfs2_delete_osb(struct ocfs2_super *osb)
ocfs2_free_slot_info(osb); ocfs2_free_slot_info(osb);
kfree(osb->osb_orphan_wipes); kfree(osb->osb_orphan_wipes);
kfree(osb->slot_recovery_generations);
/* FIXME /* FIXME
* This belongs in journal shutdown, but because we have to * This belongs in journal shutdown, but because we have to
* allocate osb->journal at the start of ocfs2_initalize_osb(), * allocate osb->journal at the start of ocfs2_initalize_osb(),
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/err.h>
#include <asm/atomic.h> #include <asm/atomic.h>
...@@ -129,8 +130,25 @@ struct configfs_attribute { ...@@ -129,8 +130,25 @@ struct configfs_attribute {
/* /*
* Users often need to create attribute structures for their configurable * Users often need to create attribute structures for their configurable
* attributes, containing a configfs_attribute member and function pointers * attributes, containing a configfs_attribute member and function pointers
* for the show() and store() operations on that attribute. They can use * for the show() and store() operations on that attribute. If they don't
* this macro (similar to sysfs' __ATTR) to make defining attributes easier. * need anything else on the extended attribute structure, they can use
* this macro to define it The argument _item is the name of the
* config_item structure.
*/
#define CONFIGFS_ATTR_STRUCT(_item) \
struct _item##_attribute { \
struct configfs_attribute attr; \
ssize_t (*show)(struct _item *, char *); \
ssize_t (*store)(struct _item *, const char *, size_t); \
}
/*
* With the extended attribute structure, users can use this macro
* (similar to sysfs' __ATTR) to make defining attributes easier.
* An example:
* #define MYITEM_ATTR(_name, _mode, _show, _store) \
* struct myitem_attribute childless_attr_##_name = \
* __CONFIGFS_ATTR(_name, _mode, _show, _store)
*/ */
#define __CONFIGFS_ATTR(_name, _mode, _show, _store) \ #define __CONFIGFS_ATTR(_name, _mode, _show, _store) \
{ \ { \
...@@ -142,6 +160,52 @@ struct configfs_attribute { ...@@ -142,6 +160,52 @@ struct configfs_attribute {
.show = _show, \ .show = _show, \
.store = _store, \ .store = _store, \
} }
/* Here is a readonly version, only requiring a show() operation */
#define __CONFIGFS_ATTR_RO(_name, _show) \
{ \
.attr = { \
.ca_name = __stringify(_name), \
.ca_mode = 0444, \
.ca_owner = THIS_MODULE, \
}, \
.show = _show, \
}
/*
* With these extended attributes, the simple show_attribute() and
* store_attribute() operations need to call the show() and store() of the
* attributes. This is a common pattern, so we provide a macro to define
* them. The argument _item is the name of the config_item structure.
* This macro expects the attributes to be named "struct <name>_attribute"
* and the function to_<name>() to exist;
*/
#define CONFIGFS_ATTR_OPS(_item) \
static ssize_t _item##_attr_show(struct config_item *item, \
struct configfs_attribute *attr, \
char *page) \
{ \
struct _item *_item = to_##_item(item); \
struct _item##_attribute *_item##_attr = \
container_of(attr, struct _item##_attribute, attr); \
ssize_t ret = 0; \
\
if (_item##_attr->show) \
ret = _item##_attr->show(_item, page); \
return ret; \
} \
static ssize_t _item##_attr_store(struct config_item *item, \
struct configfs_attribute *attr, \
const char *page, size_t count) \
{ \
struct _item *_item = to_##_item(item); \
struct _item##_attribute *_item##_attr = \
container_of(attr, struct _item##_attribute, attr); \
ssize_t ret = -EINVAL; \
\
if (_item##_attr->store) \
ret = _item##_attr->store(_item, page, count); \
return ret; \
}
/* /*
* If allow_link() exists, the item can symlink(2) out to other * If allow_link() exists, the item can symlink(2) out to other
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment