Commit e675349e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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: (64 commits)
  ocfs2/net: Add debug interface to o2net
  ocfs2: Only build ocfs2/dlm with the o2cb stack module
  ocfs2/cluster: Get rid of arguments to the timeout routines
  ocfs2: Put tree in MAINTAINERS
  ocfs2: Use BUG_ON
  ocfs2: Convert ocfs2 over to unlocked_ioctl
  ocfs2: Improve rename locking
  fs/ocfs2/aops.c: test for IS_ERR rather than 0
  ocfs2: Add inode stealing for ocfs2_reserve_new_inode
  ocfs2: Add ac_alloc_slot in ocfs2_alloc_context
  ocfs2: Add a new parameter for ocfs2_reserve_suballoc_bits
  ocfs2: Enable cross extent block merge.
  ocfs2: Add support for cross extent block
  ocfs2: Move /sys/o2cb to /sys/fs/o2cb
  sysfs: Allow removal of symlinks in the sysfs root
  ocfs2:  Reconnect after idle time out.
  ocfs2/dlm: Cleanup lockres print
  ocfs2/dlm: Fix lockname in lockres print function
  ocfs2/dlm: Move dlm_print_one_mle() from dlmmaster.c to dlmdebug.c
  ocfs2/dlm: Dumps the purgelist into a debugfs file
  ...
parents ef38ff9d 2309e9e0
What: /sys/o2cb symlink
Date: Dec 2005
KernelVersion: 2.6.16
Contact: ocfs2-devel@oss.oracle.com
Description: This is a symlink: /sys/o2cb to /sys/fs/o2cb. The symlink will
be removed when new versions of ocfs2-tools which know to look
in /sys/fs/o2cb are sufficiently prevalent. Don't code new
software to look here, it should try /sys/fs/o2cb instead.
See Documentation/ABI/stable/o2cb for more information on usage.
Users: ocfs2-tools. It's sufficient to mail proposed changes to
ocfs2-devel@oss.oracle.com.
What: /sys/fs/o2cb/ (was /sys/o2cb)
Date: Dec 2005
KernelVersion: 2.6.16
Contact: ocfs2-devel@oss.oracle.com
Description: Ocfs2-tools looks at 'interface-revision' for versioning
information. Each logmask/ file controls a set of debug prints
and can be written into with the strings "allow", "deny", or
"off". Reading the file returns the current state.
Users: ocfs2-tools. It's sufficient to mail proposed changes to
ocfs2-devel@oss.oracle.com.
What: /sys/fs/ocfs2/
Date: April 2008
Contact: ocfs2-devel@oss.oracle.com
Description:
The /sys/fs/ocfs2 directory contains knobs used by the
ocfs2-tools to interact with the filesystem.
What: /sys/fs/ocfs2/max_locking_protocol
Date: April 2008
Contact: ocfs2-devel@oss.oracle.com
Description:
The /sys/fs/ocfs2/max_locking_protocol file displays version
of ocfs2 locking supported by the filesystem. This version
covers how ocfs2 uses distributed locking between cluster
nodes.
The protocol version has a major and minor number. Two
cluster nodes can interoperate if they have an identical
major number and an overlapping minor number - thus,
a node with version 1.10 can interoperate with a node
sporting version 1.8, as long as both use the 1.8 protocol.
Reading from this file returns a single line, the major
number and minor number joined by a period, eg "1.10".
This file is read-only. The value is compiled into the
driver.
What: /sys/fs/ocfs2/loaded_cluster_plugins
Date: April 2008
Contact: ocfs2-devel@oss.oracle.com
Description:
The /sys/fs/ocfs2/loaded_cluster_plugins file describes
the available plugins to support ocfs2 cluster operation.
A cluster plugin is required to use ocfs2 in a cluster.
There are currently two available plugins:
* 'o2cb' - The classic o2cb cluster stack that ocfs2 has
used since its inception.
* 'user' - A plugin supporting userspace cluster software
in conjunction with fs/dlm.
Reading from this file returns the names of all loaded
plugins, one per line.
This file is read-only. Its contents may change as
plugins are loaded or removed.
What: /sys/fs/ocfs2/active_cluster_plugin
Date: April 2008
Contact: ocfs2-devel@oss.oracle.com
Description:
The /sys/fs/ocfs2/active_cluster_plugin displays which
cluster plugin is currently in use by the filesystem.
The active plugin will appear in the loaded_cluster_plugins
file as well. Only one plugin can be used at a time.
Reading from this file returns the name of the active plugin
on a single line.
This file is read-only. Which plugin is active depends on
the cluster stack in use. The contents may change
when all filesystems are unmounted and the cluster stack
is changed.
What: /sys/fs/ocfs2/cluster_stack
Date: April 2008
Contact: ocfs2-devel@oss.oracle.com
Description:
The /sys/fs/ocfs2/cluster_stack file contains the name
of current ocfs2 cluster stack. This value is set by
userspace tools when bringing the cluster stack online.
Cluster stack names are 4 characters in length.
When the 'o2cb' cluster stack is used, the 'o2cb' cluster
plugin is active. All other cluster stacks use the 'user'
cluster plugin.
Reading from this file returns the name of the current
cluster stack on a single line.
Writing a new stack name to this file changes the current
cluster stack unless there are mounted ocfs2 filesystems.
If there are mounted filesystems, attempts to change the
stack return an error.
Users:
ocfs2-tools <ocfs2-tools-devel@oss.oracle.com>
......@@ -318,3 +318,13 @@ Why: Not used in-tree. The current out-of-tree users used it to
code / infrastructure should be in the kernel and not in some
out-of-tree driver.
Who: Thomas Gleixner <tglx@linutronix.de>
---------------------------
What: /sys/o2cb symlink
When: January 2010
Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb
exists as a symlink for backwards compatibility for old versions of
ocfs2-tools. 2 years should be sufficient time to phase in new versions
which know to look in /sys/fs/o2cb.
Who: ocfs2-devel@oss.oracle.com
......@@ -2952,6 +2952,7 @@ P: Joel Becker
M: joel.becker@oracle.com
L: ocfs2-devel@oss.oracle.com
W: http://oss.oracle.com/projects/ocfs2/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git
S: Supported
OMNIKEY CARDMAN 4000 DRIVER
......
......@@ -444,6 +444,32 @@ config OCFS2_FS
For more information on OCFS2, see the file
<file:Documentation/filesystems/ocfs2.txt>.
config OCFS2_FS_O2CB
tristate "O2CB Kernelspace Clustering"
depends on OCFS2_FS
default y
help
OCFS2 includes a simple kernelspace clustering package, the OCFS2
Cluster Base. It only requires a very small userspace component
to configure it. This comes with the standard ocfs2-tools package.
O2CB is limited to maintaining a cluster for OCFS2 file systems.
It cannot manage any other cluster applications.
It is always safe to say Y here, as the clustering method is
run-time selectable.
config OCFS2_FS_USERSPACE_CLUSTER
tristate "OCFS2 Userspace Clustering"
depends on OCFS2_FS && DLM
default y
help
This option will allow OCFS2 to use userspace clustering services
in conjunction with the DLM in fs/dlm. If you are using a
userspace cluster manager, say Y here.
It is safe to say Y, as the clustering method is run-time
selectable.
config OCFS2_DEBUG_MASKLOG
bool "OCFS2 logging support"
depends on OCFS2_FS
......
......@@ -2,7 +2,12 @@ EXTRA_CFLAGS += -Ifs/ocfs2
EXTRA_CFLAGS += -DCATCH_BH_JBD_RACES
obj-$(CONFIG_OCFS2_FS) += ocfs2.o
obj-$(CONFIG_OCFS2_FS) += \
ocfs2.o \
ocfs2_stackglue.o
obj-$(CONFIG_OCFS2_FS_O2CB) += ocfs2_stack_o2cb.o
obj-$(CONFIG_OCFS2_FS_USERSPACE_CLUSTER) += ocfs2_stack_user.o
ocfs2-objs := \
alloc.o \
......@@ -31,5 +36,10 @@ ocfs2-objs := \
uptodate.o \
ver.o
ocfs2_stackglue-objs := stackglue.o
ocfs2_stack_o2cb-objs := stack_o2cb.o
ocfs2_stack_user-objs := stack_user.o
# cluster/ is always needed when OCFS2_FS for masklog support
obj-$(CONFIG_OCFS2_FS) += cluster/
obj-$(CONFIG_OCFS2_FS) += dlm/
obj-$(CONFIG_OCFS2_FS_O2CB) += dlm/
......@@ -1029,8 +1029,7 @@ static void ocfs2_rotate_leaf(struct ocfs2_extent_list *el,
BUG_ON(!next_free);
/* The tree code before us didn't allow enough room in the leaf. */
if (el->l_next_free_rec == el->l_count && !has_empty)
BUG();
BUG_ON(el->l_next_free_rec == el->l_count && !has_empty);
/*
* The easiest way to approach this is to just remove the
......@@ -1450,6 +1449,8 @@ static void ocfs2_adjust_root_records(struct ocfs2_extent_list *root_el,
* - When our insert into the right path leaf is at the leftmost edge
* and requires an update of the path immediately to it's left. This
* can occur at the end of some types of rotation and appending inserts.
* - When we've adjusted the last extent record in the left path leaf and the
* 1st extent record in the right path leaf during cross extent block merge.
*/
static void ocfs2_complete_edge_insert(struct inode *inode, handle_t *handle,
struct ocfs2_path *left_path,
......@@ -2712,24 +2713,147 @@ static void ocfs2_cleanup_merge(struct ocfs2_extent_list *el,
}
}
static int ocfs2_get_right_path(struct inode *inode,
struct ocfs2_path *left_path,
struct ocfs2_path **ret_right_path)
{
int ret;
u32 right_cpos;
struct ocfs2_path *right_path = NULL;
struct ocfs2_extent_list *left_el;
*ret_right_path = NULL;
/* This function shouldn't be called for non-trees. */
BUG_ON(left_path->p_tree_depth == 0);
left_el = path_leaf_el(left_path);
BUG_ON(left_el->l_next_free_rec != left_el->l_count);
ret = ocfs2_find_cpos_for_right_leaf(inode->i_sb, left_path,
&right_cpos);
if (ret) {
mlog_errno(ret);
goto out;
}
/* This function shouldn't be called for the rightmost leaf. */
BUG_ON(right_cpos == 0);
right_path = ocfs2_new_path(path_root_bh(left_path),
path_root_el(left_path));
if (!right_path) {
ret = -ENOMEM;
mlog_errno(ret);
goto out;
}
ret = ocfs2_find_path(inode, right_path, right_cpos);
if (ret) {
mlog_errno(ret);
goto out;
}
*ret_right_path = right_path;
out:
if (ret)
ocfs2_free_path(right_path);
return ret;
}
/*
* Remove split_rec clusters from the record at index and merge them
* onto the beginning of the record at index + 1.
* onto the beginning of the record "next" to it.
* For index < l_count - 1, the next means the extent rec at index + 1.
* For index == l_count - 1, the "next" means the 1st extent rec of the
* next extent block.
*/
static int ocfs2_merge_rec_right(struct inode *inode, struct buffer_head *bh,
handle_t *handle,
struct ocfs2_extent_rec *split_rec,
struct ocfs2_extent_list *el, int index)
static int ocfs2_merge_rec_right(struct inode *inode,
struct ocfs2_path *left_path,
handle_t *handle,
struct ocfs2_extent_rec *split_rec,
int index)
{
int ret;
int ret, next_free, i;
unsigned int split_clusters = le16_to_cpu(split_rec->e_leaf_clusters);
struct ocfs2_extent_rec *left_rec;
struct ocfs2_extent_rec *right_rec;
struct ocfs2_extent_list *right_el;
struct ocfs2_path *right_path = NULL;
int subtree_index = 0;
struct ocfs2_extent_list *el = path_leaf_el(left_path);
struct buffer_head *bh = path_leaf_bh(left_path);
struct buffer_head *root_bh = NULL;
BUG_ON(index >= le16_to_cpu(el->l_next_free_rec));
left_rec = &el->l_recs[index];
right_rec = &el->l_recs[index + 1];
if (index == le16_to_cpu(el->l_next_free_rec - 1) &&
le16_to_cpu(el->l_next_free_rec) == le16_to_cpu(el->l_count)) {
/* we meet with a cross extent block merge. */
ret = ocfs2_get_right_path(inode, left_path, &right_path);
if (ret) {
mlog_errno(ret);
goto out;
}
right_el = path_leaf_el(right_path);
next_free = le16_to_cpu(right_el->l_next_free_rec);
BUG_ON(next_free <= 0);
right_rec = &right_el->l_recs[0];
if (ocfs2_is_empty_extent(right_rec)) {
BUG_ON(le16_to_cpu(next_free) <= 1);
right_rec = &right_el->l_recs[1];
}
BUG_ON(le32_to_cpu(left_rec->e_cpos) +
le16_to_cpu(left_rec->e_leaf_clusters) !=
le32_to_cpu(right_rec->e_cpos));
subtree_index = ocfs2_find_subtree_root(inode,
left_path, right_path);
ret = ocfs2_extend_rotate_transaction(handle, subtree_index,
handle->h_buffer_credits,
right_path);
if (ret) {
mlog_errno(ret);
goto out;
}
root_bh = left_path->p_node[subtree_index].bh;
BUG_ON(root_bh != right_path->p_node[subtree_index].bh);
ret = ocfs2_journal_access(handle, inode, root_bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
for (i = subtree_index + 1;
i < path_num_items(right_path); i++) {
ret = ocfs2_journal_access(handle, inode,
right_path->p_node[i].bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
ret = ocfs2_journal_access(handle, inode,
left_path->p_node[i].bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
}
} else {
BUG_ON(index == le16_to_cpu(el->l_next_free_rec) - 1);
right_rec = &el->l_recs[index + 1];
}
ret = ocfs2_journal_access(handle, inode, bh,
OCFS2_JOURNAL_ACCESS_WRITE);
......@@ -2751,30 +2875,156 @@ static int ocfs2_merge_rec_right(struct inode *inode, struct buffer_head *bh,
if (ret)
mlog_errno(ret);
if (right_path) {
ret = ocfs2_journal_dirty(handle, path_leaf_bh(right_path));
if (ret)
mlog_errno(ret);
ocfs2_complete_edge_insert(inode, handle, left_path,
right_path, subtree_index);
}
out:
if (right_path)
ocfs2_free_path(right_path);
return ret;
}
static int ocfs2_get_left_path(struct inode *inode,
struct ocfs2_path *right_path,
struct ocfs2_path **ret_left_path)
{
int ret;
u32 left_cpos;
struct ocfs2_path *left_path = NULL;
*ret_left_path = NULL;
/* This function shouldn't be called for non-trees. */
BUG_ON(right_path->p_tree_depth == 0);
ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb,
right_path, &left_cpos);
if (ret) {
mlog_errno(ret);
goto out;
}
/* This function shouldn't be called for the leftmost leaf. */
BUG_ON(left_cpos == 0);
left_path = ocfs2_new_path(path_root_bh(right_path),
path_root_el(right_path));
if (!left_path) {
ret = -ENOMEM;
mlog_errno(ret);
goto out;
}
ret = ocfs2_find_path(inode, left_path, left_cpos);
if (ret) {
mlog_errno(ret);
goto out;
}
*ret_left_path = left_path;
out:
if (ret)
ocfs2_free_path(left_path);
return ret;
}
/*
* Remove split_rec clusters from the record at index and merge them
* onto the tail of the record at index - 1.
* onto the tail of the record "before" it.
* For index > 0, the "before" means the extent rec at index - 1.
*
* For index == 0, the "before" means the last record of the previous
* extent block. And there is also a situation that we may need to
* remove the rightmost leaf extent block in the right_path and change
* the right path to indicate the new rightmost path.
*/
static int ocfs2_merge_rec_left(struct inode *inode, struct buffer_head *bh,
static int ocfs2_merge_rec_left(struct inode *inode,
struct ocfs2_path *right_path,
handle_t *handle,
struct ocfs2_extent_rec *split_rec,
struct ocfs2_extent_list *el, int index)
struct ocfs2_cached_dealloc_ctxt *dealloc,
int index)
{
int ret, has_empty_extent = 0;
int ret, i, subtree_index = 0, has_empty_extent = 0;
unsigned int split_clusters = le16_to_cpu(split_rec->e_leaf_clusters);
struct ocfs2_extent_rec *left_rec;
struct ocfs2_extent_rec *right_rec;
struct ocfs2_extent_list *el = path_leaf_el(right_path);
struct buffer_head *bh = path_leaf_bh(right_path);
struct buffer_head *root_bh = NULL;
struct ocfs2_path *left_path = NULL;
struct ocfs2_extent_list *left_el;
BUG_ON(index <= 0);
BUG_ON(index < 0);
left_rec = &el->l_recs[index - 1];
right_rec = &el->l_recs[index];
if (ocfs2_is_empty_extent(&el->l_recs[0]))
has_empty_extent = 1;
if (index == 0) {
/* we meet with a cross extent block merge. */
ret = ocfs2_get_left_path(inode, right_path, &left_path);
if (ret) {
mlog_errno(ret);
goto out;
}
left_el = path_leaf_el(left_path);
BUG_ON(le16_to_cpu(left_el->l_next_free_rec) !=
le16_to_cpu(left_el->l_count));
left_rec = &left_el->l_recs[
le16_to_cpu(left_el->l_next_free_rec) - 1];
BUG_ON(le32_to_cpu(left_rec->e_cpos) +
le16_to_cpu(left_rec->e_leaf_clusters) !=
le32_to_cpu(split_rec->e_cpos));
subtree_index = ocfs2_find_subtree_root(inode,
left_path, right_path);
ret = ocfs2_extend_rotate_transaction(handle, subtree_index,
handle->h_buffer_credits,
left_path);
if (ret) {
mlog_errno(ret);
goto out;
}
root_bh = left_path->p_node[subtree_index].bh;
BUG_ON(root_bh != right_path->p_node[subtree_index].bh);
ret = ocfs2_journal_access(handle, inode, root_bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
for (i = subtree_index + 1;
i < path_num_items(right_path); i++) {
ret = ocfs2_journal_access(handle, inode,
right_path->p_node[i].bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
ret = ocfs2_journal_access(handle, inode,
left_path->p_node[i].bh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
}
}
} else {
left_rec = &el->l_recs[index - 1];
if (ocfs2_is_empty_extent(&el->l_recs[0]))
has_empty_extent = 1;
}
ret = ocfs2_journal_access(handle, inode, bh,
OCFS2_JOURNAL_ACCESS_WRITE);
......@@ -2790,9 +3040,8 @@ static int ocfs2_merge_rec_left(struct inode *inode, struct buffer_head *bh,
*left_rec = *split_rec;
has_empty_extent = 0;
} else {
} else
le16_add_cpu(&left_rec->e_leaf_clusters, split_clusters);
}
le32_add_cpu(&right_rec->e_cpos, split_clusters);
le64_add_cpu(&right_rec->e_blkno,
......@@ -2805,13 +3054,44 @@ static int ocfs2_merge_rec_left(struct inode *inode, struct buffer_head *bh,
if (ret)
mlog_errno(ret);
if (left_path) {
ret = ocfs2_journal_dirty(handle, path_leaf_bh(left_path));
if (ret)
mlog_errno(ret);
/*
* In the situation that the right_rec is empty and the extent
* block is empty also, ocfs2_complete_edge_insert can't handle
* it and we need to delete the right extent block.
*/
if (le16_to_cpu(right_rec->e_leaf_clusters) == 0 &&
le16_to_cpu(el->l_next_free_rec) == 1) {
ret = ocfs2_remove_rightmost_path(inode, handle,
right_path, dealloc);
if (ret) {
mlog_errno(ret);
goto out;
}
/* Now the rightmost extent block has been deleted.
* So we use the new rightmost path.
*/
ocfs2_mv_path(right_path, left_path);
left_path = NULL;
} else
ocfs2_complete_edge_insert(inode, handle, left_path,
right_path, subtree_index);
}
out:
if (left_path)
ocfs2_free_path(left_path);
return ret;
}
static int ocfs2_try_to_merge_extent(struct inode *inode,
handle_t *handle,
struct ocfs2_path *left_path,
struct ocfs2_path *path,
int split_index,
struct ocfs2_extent_rec *split_rec,
struct ocfs2_cached_dealloc_ctxt *dealloc,
......@@ -2819,7 +3099,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
{
int ret = 0;
struct ocfs2_extent_list *el = path_leaf_el(left_path);
struct ocfs2_extent_list *el = path_leaf_el(path);
struct ocfs2_extent_rec *rec = &el->l_recs[split_index];
BUG_ON(ctxt->c_contig_type == CONTIG_NONE);
......@@ -2832,7 +3112,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
* extents - having more than one in a leaf is
* illegal.
*/
ret = ocfs2_rotate_tree_left(inode, handle, left_path,
ret = ocfs2_rotate_tree_left(inode, handle, path,
dealloc);
if (ret) {
mlog_errno(ret);
......@@ -2847,7 +3127,6 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
* Left-right contig implies this.
*/
BUG_ON(!ctxt->c_split_covers_rec);
BUG_ON(split_index == 0);
/*
* Since the leftright insert always covers the entire
......@@ -2858,9 +3137,14 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
* Since the adding of an empty extent shifts
* everything back to the right, there's no need to
* update split_index here.
*
* When the split_index is zero, we need to merge it to the
* prevoius extent block. It is more efficient and easier
* if we do merge_right first and merge_left later.
*/
ret = ocfs2_merge_rec_left(inode, path_leaf_bh(left_path),
handle, split_rec, el, split_index);
ret = ocfs2_merge_rec_right(inode, path,
handle, split_rec,
split_index);
if (ret) {
mlog_errno(ret);
goto out;
......@@ -2871,32 +3155,30 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
*/
BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0]));
/*
* The left merge left us with an empty extent, remove
* it.
*/
ret = ocfs2_rotate_tree_left(inode, handle, left_path, dealloc);
/* The merge left us with an empty extent, remove it. */
ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
if (ret) {
mlog_errno(ret);
goto out;
}
split_index--;
rec = &el->l_recs[split_index];
/*
* Note that we don't pass split_rec here on purpose -
* we've merged it into the left side.
* we've merged it into the rec already.
*/
ret = ocfs2_merge_rec_right(inode, path_leaf_bh(left_path),
handle, rec, el, split_index);
ret = ocfs2_merge_rec_left(inode, path,
handle, rec,
dealloc,
split_index);
if (ret) {
mlog_errno(ret);
goto out;
}
BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0]));
ret = ocfs2_rotate_tree_left(inode, handle, left_path,
ret = ocfs2_rotate_tree_left(inode, handle, path,
dealloc);
/*
* Error from this last rotate is not critical, so
......@@ -2915,8 +3197,9 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
*/
if (ctxt->c_contig_type == CONTIG_RIGHT) {
ret = ocfs2_merge_rec_left(inode,
path_leaf_bh(left_path),
handle, split_rec, el,
path,
handle, split_rec,
dealloc,
split_index);
if (ret) {
mlog_errno(ret);
......@@ -2924,8 +3207,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
}
} else {
ret = ocfs2_merge_rec_right(inode,
path_leaf_bh(left_path),
handle, split_rec, el,
path,
handle, split_rec,
split_index);
if (ret) {
mlog_errno(ret);
......@@ -2938,7 +3221,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
* The merge may have left an empty extent in
* our leaf. Try to rotate it away.
*/
ret = ocfs2_rotate_tree_left(inode, handle, left_path,
ret = ocfs2_rotate_tree_left(inode, handle, path,
dealloc);
if (ret)
mlog_errno(ret);
......@@ -3498,20 +3781,57 @@ out:
}
static enum ocfs2_contig_type
ocfs2_figure_merge_contig_type(struct inode *inode,
ocfs2_figure_merge_contig_type(struct inode *inode, struct ocfs2_path *path,
struct ocfs2_extent_list *el, int index,
struct ocfs2_extent_rec *split_rec)
{
struct ocfs2_extent_rec *rec;
int status;
enum ocfs2_contig_type ret = CONTIG_NONE;
u32 left_cpos, right_cpos;
struct ocfs2_extent_rec *rec = NULL;
struct ocfs2_extent_list *new_el;
struct ocfs2_path *left_path = NULL, *right_path = NULL;
struct buffer_head *bh;
struct ocfs2_extent_block *eb;
if (index > 0) {
rec = &el->l_recs[index - 1];
} else if (path->p_tree_depth > 0) {
status = ocfs2_find_cpos_for_left_leaf(inode->i_sb,
path, &left_cpos);
if (status)
goto out;
if (left_cpos != 0) {
left_path = ocfs2_new_path(path_root_bh(path),
path_root_el(path));
if (!left_path)
goto out;
status = ocfs2_find_path(inode, left_path, left_cpos);
if (status)
goto out;
new_el = path_leaf_el(left_path);
if (le16_to_cpu(new_el->l_next_free_rec) !=
le16_to_cpu(new_el->l_count)) {
bh = path_leaf_bh(left_path);
eb = (struct ocfs2_extent_block *)bh->b_data;
OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb,
eb);
goto out;
}
rec = &new_el->l_recs[
le16_to_cpu(new_el->l_next_free_rec) - 1];
}
}
/*
* We're careful to check for an empty extent record here -
* the merge code will know what to do if it sees one.
*/
if (index > 0) {
rec = &el->l_recs[index - 1];
if (rec) {
if (index == 1 && ocfs2_is_empty_extent(rec)) {
if (split_rec->e_cpos == el->l_recs[index].e_cpos)
ret = CONTIG_RIGHT;
......@@ -3520,10 +3840,45 @@ ocfs2_figure_merge_contig_type(struct inode *inode,
}
}
if (index < (le16_to_cpu(el->l_next_free_rec) - 1)) {
rec = NULL;
if (index < (le16_to_cpu(el->l_next_free_rec) - 1))
rec = &el->l_recs[index + 1];
else if (le16_to_cpu(el->l_next_free_rec) == le16_to_cpu(el->l_count) &&
path->p_tree_depth > 0) {
status = ocfs2_find_cpos_for_right_leaf(inode->i_sb,
path, &right_cpos);
if (status)
goto out;
if (right_cpos == 0)
goto out;
right_path = ocfs2_new_path(path_root_bh(path),
path_root_el(path));
if (!right_path)
goto out;
status = ocfs2_find_path(inode, right_path, right_cpos);
if (status)
goto out;
new_el = path_leaf_el(right_path);
rec = &new_el->l_recs[0];
if (ocfs2_is_empty_extent(rec)) {
if (le16_to_cpu(new_el->l_next_free_rec) <= 1) {
bh = path_leaf_bh(right_path);
eb = (struct ocfs2_extent_block *)bh->b_data;
OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb,
eb);
goto out;
}
rec = &new_el->l_recs[1];
}
}
if (rec) {
enum ocfs2_contig_type contig_type;
rec = &el->l_recs[index + 1];
contig_type = ocfs2_extent_contig(inode, rec, split_rec);
if (contig_type == CONTIG_LEFT && ret == CONTIG_RIGHT)
......@@ -3532,6 +3887,12 @@ ocfs2_figure_merge_contig_type(struct inode *inode,
ret = contig_type;
}
out:
if (left_path)
ocfs2_free_path(left_path);
if (right_path)
ocfs2_free_path(right_path);
return ret;
}
......@@ -3994,7 +4355,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode,
goto out;
}
ctxt.c_contig_type = ocfs2_figure_merge_contig_type(inode, el,
ctxt.c_contig_type = ocfs2_figure_merge_contig_type(inode, path, el,
split_index,
split_rec);
......@@ -4788,6 +5149,8 @@ static void ocfs2_truncate_log_worker(struct work_struct *work)
status = ocfs2_flush_truncate_log(osb);
if (status < 0)
mlog_errno(status);
else
ocfs2_init_inode_steal_slot(osb);
mlog_exit(status);
}
......
......@@ -467,11 +467,11 @@ handle_t *ocfs2_start_walk_page_trans(struct inode *inode,
unsigned to)
{
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
handle_t *handle = NULL;
handle_t *handle;
int ret = 0;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (!handle) {
if (IS_ERR(handle)) {
ret = -ENOMEM;
mlog_errno(ret);
goto out;
......@@ -487,7 +487,7 @@ handle_t *ocfs2_start_walk_page_trans(struct inode *inode,
}
out:
if (ret) {
if (handle)
if (!IS_ERR(handle))
ocfs2_commit_trans(osb, handle);
handle = ERR_PTR(ret);
}
......
obj-$(CONFIG_OCFS2_FS) += ocfs2_nodemanager.o
ocfs2_nodemanager-objs := heartbeat.o masklog.o sys.o nodemanager.o \
quorum.o tcp.o ver.o
quorum.o tcp.o netdebug.o ver.o
/* -*- mode: c; c-basic-offset: 8; -*-
* vim: noexpandtab sw=8 ts=8 sts=0:
*
* netdebug.c
*
* debug functionality for o2net
*
* Copyright (C) 2005, 2008 Oracle. All rights reserved.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*
*/
#ifdef CONFIG_DEBUG_FS
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/kref.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include "tcp.h"
#include "nodemanager.h"
#define MLOG_MASK_PREFIX ML_TCP
#include "masklog.h"
#include "tcp_internal.h"
#define O2NET_DEBUG_DIR "o2net"
#define SC_DEBUG_NAME "sock_containers"
#define NST_DEBUG_NAME "send_tracking"
static struct dentry *o2net_dentry;
static struct dentry *sc_dentry;
static struct dentry *nst_dentry;
static DEFINE_SPINLOCK(o2net_debug_lock);
static LIST_HEAD(sock_containers);
static LIST_HEAD(send_tracking);
void o2net_debug_add_nst(struct o2net_send_tracking *nst)
{
spin_lock(&o2net_debug_lock);
list_add(&nst->st_net_debug_item, &send_tracking);
spin_unlock(&o2net_debug_lock);
}
void o2net_debug_del_nst(struct o2net_send_tracking *nst)
{
spin_lock(&o2net_debug_lock);
if (!list_empty(&nst->st_net_debug_item))
list_del_init(&nst->st_net_debug_item);
spin_unlock(&o2net_debug_lock);
}
static struct o2net_send_tracking
*next_nst(struct o2net_send_tracking *nst_start)
{
struct o2net_send_tracking *nst, *ret = NULL;
assert_spin_locked(&o2net_debug_lock);
list_for_each_entry(nst, &nst_start->st_net_debug_item,
st_net_debug_item) {
/* discover the head of the list */
if (&nst->st_net_debug_item == &send_tracking)
break;
/* use st_task to detect real nsts in the list */
if (nst->st_task != NULL) {
ret = nst;
break;
}
}
return ret;
}
static void *nst_seq_start(struct seq_file *seq, loff_t *pos)
{
struct o2net_send_tracking *nst, *dummy_nst = seq->private;
spin_lock(&o2net_debug_lock);
nst = next_nst(dummy_nst);
spin_unlock(&o2net_debug_lock);
return nst;
}
static void *nst_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct o2net_send_tracking *nst, *dummy_nst = seq->private;
spin_lock(&o2net_debug_lock);
nst = next_nst(dummy_nst);
list_del_init(&dummy_nst->st_net_debug_item);
if (nst)
list_add(&dummy_nst->st_net_debug_item,
&nst->st_net_debug_item);
spin_unlock(&o2net_debug_lock);
return nst; /* unused, just needs to be null when done */
}
static int nst_seq_show(struct seq_file *seq, void *v)
{
struct o2net_send_tracking *nst, *dummy_nst = seq->private;
spin_lock(&o2net_debug_lock);
nst = next_nst(dummy_nst);
if (nst != NULL) {
/* get_task_comm isn't exported. oh well. */
seq_printf(seq, "%p:\n"
" pid: %lu\n"
" tgid: %lu\n"
" process name: %s\n"
" node: %u\n"
" sc: %p\n"
" message id: %d\n"
" message type: %u\n"
" message key: 0x%08x\n"
" sock acquiry: %lu.%lu\n"
" send start: %lu.%lu\n"
" wait start: %lu.%lu\n",
nst, (unsigned long)nst->st_task->pid,
(unsigned long)nst->st_task->tgid,
nst->st_task->comm, nst->st_node,
nst->st_sc, nst->st_id, nst->st_msg_type,
nst->st_msg_key,
nst->st_sock_time.tv_sec, nst->st_sock_time.tv_usec,
nst->st_send_time.tv_sec, nst->st_send_time.tv_usec,
nst->st_status_time.tv_sec,
nst->st_status_time.tv_usec);
}
spin_unlock(&o2net_debug_lock);
return 0;
}
static void nst_seq_stop(struct seq_file *seq, void *v)
{
}
static struct seq_operations nst_seq_ops = {
.start = nst_seq_start,
.next = nst_seq_next,
.stop = nst_seq_stop,
.show = nst_seq_show,
};
static int nst_fop_open(struct inode *inode, struct file *file)
{
struct o2net_send_tracking *dummy_nst;
struct seq_file *seq;
int ret;
dummy_nst = kmalloc(sizeof(struct o2net_send_tracking), GFP_KERNEL);
if (dummy_nst == NULL) {
ret = -ENOMEM;
goto out;
}
dummy_nst->st_task = NULL;
ret = seq_open(file, &nst_seq_ops);
if (ret)
goto out;
seq = file->private_data;
seq->private = dummy_nst;
o2net_debug_add_nst(dummy_nst);
dummy_nst = NULL;
out:
kfree(dummy_nst);
return ret;
}
static int nst_fop_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = file->private_data;
struct o2net_send_tracking *dummy_nst = seq->private;
o2net_debug_del_nst(dummy_nst);
return seq_release_private(inode, file);
}
static struct file_operations nst_seq_fops = {
.open = nst_fop_open,
.read = seq_read,
.llseek = seq_lseek,
.release = nst_fop_release,
};
void o2net_debug_add_sc(struct o2net_sock_container *sc)
{
spin_lock(&o2net_debug_lock);
list_add(&sc->sc_net_debug_item, &sock_containers);
spin_unlock(&o2net_debug_lock);
}
void o2net_debug_del_sc(struct o2net_sock_container *sc)
{
spin_lock(&o2net_debug_lock);
list_del_init(&sc->sc_net_debug_item);
spin_unlock(&o2net_debug_lock);
}
static struct o2net_sock_container
*next_sc(struct o2net_sock_container *sc_start)
{
struct o2net_sock_container *sc, *ret = NULL;
assert_spin_locked(&o2net_debug_lock);
list_for_each_entry(sc, &sc_start->sc_net_debug_item,
sc_net_debug_item) {
/* discover the head of the list miscast as a sc */
if (&sc->sc_net_debug_item == &sock_containers)
break;
/* use sc_page to detect real scs in the list */
if (sc->sc_page != NULL) {
ret = sc;
break;
}
}
return ret;
}
static void *sc_seq_start(struct seq_file *seq, loff_t *pos)
{
struct o2net_sock_container *sc, *dummy_sc = seq->private;
spin_lock(&o2net_debug_lock);
sc = next_sc(dummy_sc);
spin_unlock(&o2net_debug_lock);
return sc;
}
static void *sc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct o2net_sock_container *sc, *dummy_sc = seq->private;
spin_lock(&o2net_debug_lock);
sc = next_sc(dummy_sc);
list_del_init(&dummy_sc->sc_net_debug_item);
if (sc)
list_add(&dummy_sc->sc_net_debug_item, &sc->sc_net_debug_item);
spin_unlock(&o2net_debug_lock);
return sc; /* unused, just needs to be null when done */
}
#define TV_SEC_USEC(TV) TV.tv_sec, TV.tv_usec
static int sc_seq_show(struct seq_file *seq, void *v)
{
struct o2net_sock_container *sc, *dummy_sc = seq->private;
spin_lock(&o2net_debug_lock);
sc = next_sc(dummy_sc);
if (sc != NULL) {
struct inet_sock *inet = NULL;
__be32 saddr = 0, daddr = 0;
__be16 sport = 0, dport = 0;
if (sc->sc_sock) {
inet = inet_sk(sc->sc_sock->sk);
/* the stack's structs aren't sparse endian clean */
saddr = (__force __be32)inet->saddr;
daddr = (__force __be32)inet->daddr;
sport = (__force __be16)inet->sport;
dport = (__force __be16)inet->dport;
}
/* XXX sigh, inet-> doesn't have sparse annotation so any
* use of it here generates a warning with -Wbitwise */
seq_printf(seq, "%p:\n"
" krefs: %d\n"
" sock: %u.%u.%u.%u:%u -> "
"%u.%u.%u.%u:%u\n"
" remote node: %s\n"
" page off: %zu\n"
" handshake ok: %u\n"
" timer: %lu.%lu\n"
" data ready: %lu.%lu\n"
" advance start: %lu.%lu\n"
" advance stop: %lu.%lu\n"
" func start: %lu.%lu\n"
" func stop: %lu.%lu\n"
" func key: %u\n"
" func type: %u\n",
sc,
atomic_read(&sc->sc_kref.refcount),
NIPQUAD(saddr), inet ? ntohs(sport) : 0,
NIPQUAD(daddr), inet ? ntohs(dport) : 0,
sc->sc_node->nd_name,
sc->sc_page_off,
sc->sc_handshake_ok,
TV_SEC_USEC(sc->sc_tv_timer),
TV_SEC_USEC(sc->sc_tv_data_ready),
TV_SEC_USEC(sc->sc_tv_advance_start),
TV_SEC_USEC(sc->sc_tv_advance_stop),
TV_SEC_USEC(sc->sc_tv_func_start),
TV_SEC_USEC(sc->sc_tv_func_stop),
sc->sc_msg_key,
sc->sc_msg_type);
}
spin_unlock(&o2net_debug_lock);
return 0;
}
static void sc_seq_stop(struct seq_file *seq, void *v)
{
}
static struct seq_operations sc_seq_ops = {
.start = sc_seq_start,
.next = sc_seq_next,
.stop = sc_seq_stop,
.show = sc_seq_show,
};
static int sc_fop_open(struct inode *inode, struct file *file)
{
struct o2net_sock_container *dummy_sc;
struct seq_file *seq;
int ret;
dummy_sc = kmalloc(sizeof(struct o2net_sock_container), GFP_KERNEL);
if (dummy_sc == NULL) {
ret = -ENOMEM;
goto out;
}
dummy_sc->sc_page = NULL;
ret = seq_open(file, &sc_seq_ops);
if (ret)
goto out;
seq = file->private_data;
seq->private = dummy_sc;
o2net_debug_add_sc(dummy_sc);
dummy_sc = NULL;
out:
kfree(dummy_sc);
return ret;
}
static int sc_fop_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = file->private_data;
struct o2net_sock_container *dummy_sc = seq->private;
o2net_debug_del_sc(dummy_sc);
return seq_release_private(inode, file);
}
static struct file_operations sc_seq_fops = {
.open = sc_fop_open,
.read = seq_read,
.llseek = seq_lseek,
.release = sc_fop_release,
};
int o2net_debugfs_init(void)
{
o2net_dentry = debugfs_create_dir(O2NET_DEBUG_DIR, NULL);
if (!o2net_dentry) {
mlog_errno(-ENOMEM);
goto bail;
}
nst_dentry = debugfs_create_file(NST_DEBUG_NAME, S_IFREG|S_IRUSR,
o2net_dentry, NULL,
&nst_seq_fops);
if (!nst_dentry) {
mlog_errno(-ENOMEM);
goto bail;
}
sc_dentry = debugfs_create_file(SC_DEBUG_NAME, S_IFREG|S_IRUSR,
o2net_dentry, NULL,
&sc_seq_fops);
if (!sc_dentry) {
mlog_errno(-ENOMEM);
goto bail;
}
return 0;
bail:
if (sc_dentry)
debugfs_remove(sc_dentry);
if (nst_dentry)
debugfs_remove(nst_dentry);
if (o2net_dentry)
debugfs_remove(o2net_dentry);
return -ENOMEM;
}
void o2net_debugfs_exit(void)
{
if (sc_dentry)
debugfs_remove(sc_dentry);
if (nst_dentry)
debugfs_remove(nst_dentry);
if (o2net_dentry)
debugfs_remove(o2net_dentry);
}
#endif /* CONFIG_DEBUG_FS */
......@@ -959,7 +959,10 @@ static int __init init_o2nm(void)
cluster_print_version();
o2hb_init();
o2net_init();
ret = o2net_init();
if (ret)
goto out;
ocfs2_table_header = register_sysctl_table(ocfs2_root_table);
if (!ocfs2_table_header) {
......
......@@ -57,6 +57,7 @@ static struct kset *o2cb_kset;
void o2cb_sys_shutdown(void)
{
mlog_sys_shutdown();
sysfs_remove_link(NULL, "o2cb");
kset_unregister(o2cb_kset);
}
......@@ -68,6 +69,14 @@ int o2cb_sys_init(void)
if (!o2cb_kset)
return -ENOMEM;
/*
* Create this symlink for backwards compatibility with old
* versions of ocfs2-tools which look for things in /sys/o2cb.
*/
ret = sysfs_create_link(NULL, &o2cb_kset->kobj, "o2cb");
if (ret)
goto error;
ret = sysfs_create_group(&o2cb_kset->kobj, &o2cb_attr_group);
if (ret)
goto error;
......
......@@ -142,23 +142,65 @@ static void o2net_idle_timer(unsigned long data);
static void o2net_sc_postpone_idle(struct o2net_sock_container *sc);
static void o2net_sc_reset_idle_timer(struct o2net_sock_container *sc);
/*
* FIXME: These should use to_o2nm_cluster_from_node(), but we end up
* losing our parent link to the cluster during shutdown. This can be
* solved by adding a pre-removal callback to configfs, or passing
* around the cluster with the node. -jeffm
*/
static inline int o2net_reconnect_delay(struct o2nm_node *node)
static void o2net_init_nst(struct o2net_send_tracking *nst, u32 msgtype,
u32 msgkey, struct task_struct *task, u8 node)
{
#ifdef CONFIG_DEBUG_FS
INIT_LIST_HEAD(&nst->st_net_debug_item);
nst->st_task = task;
nst->st_msg_type = msgtype;
nst->st_msg_key = msgkey;
nst->st_node = node;
#endif
}
static void o2net_set_nst_sock_time(struct o2net_send_tracking *nst)
{
#ifdef CONFIG_DEBUG_FS
do_gettimeofday(&nst->st_sock_time);
#endif
}
static void o2net_set_nst_send_time(struct o2net_send_tracking *nst)
{
#ifdef CONFIG_DEBUG_FS
do_gettimeofday(&nst->st_send_time);
#endif
}
static void o2net_set_nst_status_time(struct o2net_send_tracking *nst)
{
#ifdef CONFIG_DEBUG_FS
do_gettimeofday(&nst->st_status_time);
#endif
}
static void o2net_set_nst_sock_container(struct o2net_send_tracking *nst,
struct o2net_sock_container *sc)
{
#ifdef CONFIG_DEBUG_FS
nst->st_sc = sc;
#endif
}
static void o2net_set_nst_msg_id(struct o2net_send_tracking *nst, u32 msg_id)
{
#ifdef CONFIG_DEBUG_FS
nst->st_id = msg_id;
#endif
}
static inline int o2net_reconnect_delay(void)
{
return o2nm_single_cluster->cl_reconnect_delay_ms;
}
static inline int o2net_keepalive_delay(struct o2nm_node *node)
static inline int o2net_keepalive_delay(void)
{
return o2nm_single_cluster->cl_keepalive_delay_ms;
}
static inline int o2net_idle_timeout(struct o2nm_node *node)
static inline int o2net_idle_timeout(void)
{
return o2nm_single_cluster->cl_idle_timeout_ms;
}
......@@ -296,6 +338,7 @@ static void sc_kref_release(struct kref *kref)
o2nm_node_put(sc->sc_node);
sc->sc_node = NULL;
o2net_debug_del_sc(sc);
kfree(sc);
}
......@@ -336,6 +379,7 @@ static struct o2net_sock_container *sc_alloc(struct o2nm_node *node)
ret = sc;
sc->sc_page = page;
o2net_debug_add_sc(sc);
sc = NULL;
page = NULL;
......@@ -399,8 +443,6 @@ static void o2net_set_nn_state(struct o2net_node *nn,
mlog_bug_on_msg(err && valid, "err %d valid %u\n", err, valid);
mlog_bug_on_msg(valid && !sc, "valid %u sc %p\n", valid, sc);
/* we won't reconnect after our valid conn goes away for
* this hb iteration.. here so it shows up in the logs */
if (was_valid && !valid && err == 0)
err = -ENOTCONN;
......@@ -430,11 +472,6 @@ static void o2net_set_nn_state(struct o2net_node *nn,
if (!was_valid && valid) {
o2quo_conn_up(o2net_num_from_nn(nn));
/* this is a bit of a hack. we only try reconnecting
* when heartbeating starts until we get a connection.
* if that connection then dies we don't try reconnecting.
* the only way to start connecting again is to down
* heartbeat and bring it back up. */
cancel_delayed_work(&nn->nn_connect_expired);
printk(KERN_INFO "o2net: %s " SC_NODEF_FMT "\n",
o2nm_this_node() > sc->sc_node->nd_num ?
......@@ -451,12 +488,24 @@ static void o2net_set_nn_state(struct o2net_node *nn,
/* delay if we're withing a RECONNECT_DELAY of the
* last attempt */
delay = (nn->nn_last_connect_attempt +
msecs_to_jiffies(o2net_reconnect_delay(NULL)))
msecs_to_jiffies(o2net_reconnect_delay()))
- jiffies;
if (delay > msecs_to_jiffies(o2net_reconnect_delay(NULL)))
if (delay > msecs_to_jiffies(o2net_reconnect_delay()))
delay = 0;
mlog(ML_CONN, "queueing conn attempt in %lu jiffies\n", delay);
queue_delayed_work(o2net_wq, &nn->nn_connect_work, delay);
/*
* Delay the expired work after idle timeout.
*
* We might have lots of failed connection attempts that run
* through here but we only cancel the connect_expired work when
* a connection attempt succeeds. So only the first enqueue of
* the connect_expired work will do anything. The rest will see
* that it's already queued and do nothing.
*/
delay += msecs_to_jiffies(o2net_idle_timeout());
queue_delayed_work(o2net_wq, &nn->nn_connect_expired, delay);
}
/* keep track of the nn's sc ref for the caller */
......@@ -914,6 +963,9 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
struct o2net_status_wait nsw = {
.ns_node_item = LIST_HEAD_INIT(nsw.ns_node_item),
};
struct o2net_send_tracking nst;
o2net_init_nst(&nst, msg_type, key, current, target_node);
if (o2net_wq == NULL) {
mlog(0, "attempt to tx without o2netd running\n");
......@@ -939,6 +991,10 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
goto out;
}
o2net_debug_add_nst(&nst);
o2net_set_nst_sock_time(&nst);
ret = wait_event_interruptible(nn->nn_sc_wq,
o2net_tx_can_proceed(nn, &sc, &error));
if (!ret && error)
......@@ -946,6 +1002,8 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
if (ret)
goto out;
o2net_set_nst_sock_container(&nst, sc);
veclen = caller_veclen + 1;
vec = kmalloc(sizeof(struct kvec) * veclen, GFP_ATOMIC);
if (vec == NULL) {
......@@ -972,6 +1030,9 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
goto out;
msg->msg_num = cpu_to_be32(nsw.ns_id);
o2net_set_nst_msg_id(&nst, nsw.ns_id);
o2net_set_nst_send_time(&nst);
/* finally, convert the message header to network byte-order
* and send */
......@@ -986,6 +1047,7 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
}
/* wait on other node's handler */
o2net_set_nst_status_time(&nst);
wait_event(nsw.ns_wq, o2net_nsw_completed(nn, &nsw));
/* Note that we avoid overwriting the callers status return
......@@ -998,6 +1060,7 @@ int o2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
mlog(0, "woken, returning system status %d, user status %d\n",
ret, nsw.ns_status);
out:
o2net_debug_del_nst(&nst); /* must be before dropping sc and node */
if (sc)
sc_put(sc);
if (vec)
......@@ -1154,23 +1217,23 @@ static int o2net_check_handshake(struct o2net_sock_container *sc)
* but isn't. This can ultimately cause corruption.
*/
if (be32_to_cpu(hand->o2net_idle_timeout_ms) !=
o2net_idle_timeout(sc->sc_node)) {
o2net_idle_timeout()) {
mlog(ML_NOTICE, SC_NODEF_FMT " uses a network idle timeout of "
"%u ms, but we use %u ms locally. disconnecting\n",
SC_NODEF_ARGS(sc),
be32_to_cpu(hand->o2net_idle_timeout_ms),
o2net_idle_timeout(sc->sc_node));
o2net_idle_timeout());
o2net_ensure_shutdown(nn, sc, -ENOTCONN);
return -1;
}
if (be32_to_cpu(hand->o2net_keepalive_delay_ms) !=
o2net_keepalive_delay(sc->sc_node)) {
o2net_keepalive_delay()) {
mlog(ML_NOTICE, SC_NODEF_FMT " uses a keepalive delay of "
"%u ms, but we use %u ms locally. disconnecting\n",
SC_NODEF_ARGS(sc),
be32_to_cpu(hand->o2net_keepalive_delay_ms),
o2net_keepalive_delay(sc->sc_node));
o2net_keepalive_delay());
o2net_ensure_shutdown(nn, sc, -ENOTCONN);
return -1;
}
......@@ -1193,6 +1256,7 @@ static int o2net_check_handshake(struct o2net_sock_container *sc)
* shut down already */
if (nn->nn_sc == sc) {
o2net_sc_reset_idle_timer(sc);
atomic_set(&nn->nn_timeout, 0);
o2net_set_nn_state(nn, sc, 1, 0);
}
spin_unlock(&nn->nn_lock);
......@@ -1347,12 +1411,11 @@ static void o2net_initialize_handshake(void)
{
o2net_hand->o2hb_heartbeat_timeout_ms = cpu_to_be32(
O2HB_MAX_WRITE_TIMEOUT_MS);
o2net_hand->o2net_idle_timeout_ms = cpu_to_be32(
o2net_idle_timeout(NULL));
o2net_hand->o2net_idle_timeout_ms = cpu_to_be32(o2net_idle_timeout());
o2net_hand->o2net_keepalive_delay_ms = cpu_to_be32(
o2net_keepalive_delay(NULL));
o2net_keepalive_delay());
o2net_hand->o2net_reconnect_delay_ms = cpu_to_be32(
o2net_reconnect_delay(NULL));
o2net_reconnect_delay());
}
/* ------------------------------------------------------------ */
......@@ -1391,14 +1454,15 @@ static void o2net_sc_send_keep_req(struct work_struct *work)
static void o2net_idle_timer(unsigned long data)
{
struct o2net_sock_container *sc = (struct o2net_sock_container *)data;
struct o2net_node *nn = o2net_nn_from_num(sc->sc_node->nd_num);
struct timeval now;
do_gettimeofday(&now);
printk(KERN_INFO "o2net: connection to " SC_NODEF_FMT " has been idle for %u.%u "
"seconds, shutting it down.\n", SC_NODEF_ARGS(sc),
o2net_idle_timeout(sc->sc_node) / 1000,
o2net_idle_timeout(sc->sc_node) % 1000);
o2net_idle_timeout() / 1000,
o2net_idle_timeout() % 1000);
mlog(ML_NOTICE, "here are some times that might help debug the "
"situation: (tmr %ld.%ld now %ld.%ld dr %ld.%ld adv "
"%ld.%ld:%ld.%ld func (%08x:%u) %ld.%ld:%ld.%ld)\n",
......@@ -1413,6 +1477,12 @@ static void o2net_idle_timer(unsigned long data)
sc->sc_tv_func_start.tv_sec, (long) sc->sc_tv_func_start.tv_usec,
sc->sc_tv_func_stop.tv_sec, (long) sc->sc_tv_func_stop.tv_usec);
/*
* Initialize the nn_timeout so that the next connection attempt
* will continue in o2net_start_connect.
*/
atomic_set(&nn->nn_timeout, 1);
o2net_sc_queue_work(sc, &sc->sc_shutdown_work);
}
......@@ -1420,10 +1490,10 @@ static void o2net_sc_reset_idle_timer(struct o2net_sock_container *sc)
{
o2net_sc_cancel_delayed_work(sc, &sc->sc_keepalive_work);
o2net_sc_queue_delayed_work(sc, &sc->sc_keepalive_work,
msecs_to_jiffies(o2net_keepalive_delay(sc->sc_node)));
msecs_to_jiffies(o2net_keepalive_delay()));
do_gettimeofday(&sc->sc_tv_timer);
mod_timer(&sc->sc_idle_timeout,
jiffies + msecs_to_jiffies(o2net_idle_timeout(sc->sc_node)));
jiffies + msecs_to_jiffies(o2net_idle_timeout()));
}
static void o2net_sc_postpone_idle(struct o2net_sock_container *sc)
......@@ -1447,6 +1517,7 @@ static void o2net_start_connect(struct work_struct *work)
struct socket *sock = NULL;
struct sockaddr_in myaddr = {0, }, remoteaddr = {0, };
int ret = 0, stop;
unsigned int timeout;
/* if we're greater we initiate tx, otherwise we accept */
if (o2nm_this_node() <= o2net_num_from_nn(nn))
......@@ -1466,8 +1537,17 @@ static void o2net_start_connect(struct work_struct *work)
}
spin_lock(&nn->nn_lock);
/* see if we already have one pending or have given up */
stop = (nn->nn_sc || nn->nn_persistent_error);
/*
* see if we already have one pending or have given up.
* For nn_timeout, it is set when we close the connection
* because of the idle time out. So it means that we have
* at least connected to that node successfully once,
* now try to connect to it again.
*/
timeout = atomic_read(&nn->nn_timeout);
stop = (nn->nn_sc ||
(nn->nn_persistent_error &&
(nn->nn_persistent_error != -ENOTCONN || timeout == 0)));
spin_unlock(&nn->nn_lock);
if (stop)
goto out;
......@@ -1555,8 +1635,8 @@ static void o2net_connect_expired(struct work_struct *work)
mlog(ML_ERROR, "no connection established with node %u after "
"%u.%u seconds, giving up and returning errors.\n",
o2net_num_from_nn(nn),
o2net_idle_timeout(NULL) / 1000,
o2net_idle_timeout(NULL) % 1000);
o2net_idle_timeout() / 1000,
o2net_idle_timeout() % 1000);
o2net_set_nn_state(nn, NULL, 0, -ENOTCONN);
}
......@@ -1579,6 +1659,7 @@ void o2net_disconnect_node(struct o2nm_node *node)
/* don't reconnect until it's heartbeating again */
spin_lock(&nn->nn_lock);
atomic_set(&nn->nn_timeout, 0);
o2net_set_nn_state(nn, NULL, 0, -ENOTCONN);
spin_unlock(&nn->nn_lock);
......@@ -1610,20 +1691,15 @@ static void o2net_hb_node_up_cb(struct o2nm_node *node, int node_num,
/* ensure an immediate connect attempt */
nn->nn_last_connect_attempt = jiffies -
(msecs_to_jiffies(o2net_reconnect_delay(node)) + 1);
(msecs_to_jiffies(o2net_reconnect_delay()) + 1);
if (node_num != o2nm_this_node()) {
/* heartbeat doesn't work unless a local node number is
* configured and doing so brings up the o2net_wq, so we can
* use it.. */
queue_delayed_work(o2net_wq, &nn->nn_connect_expired,
msecs_to_jiffies(o2net_idle_timeout(node)));
/* believe it or not, accept and node hearbeating testing
* can succeed for this node before we got here.. so
* only use set_nn_state to clear the persistent error
* if that hasn't already happened */
spin_lock(&nn->nn_lock);
atomic_set(&nn->nn_timeout, 0);
if (nn->nn_persistent_error)
o2net_set_nn_state(nn, NULL, 0, 0);
spin_unlock(&nn->nn_lock);
......@@ -1747,6 +1823,7 @@ static int o2net_accept_one(struct socket *sock)
new_sock = NULL;
spin_lock(&nn->nn_lock);
atomic_set(&nn->nn_timeout, 0);
o2net_set_nn_state(nn, sc, 0, 0);
spin_unlock(&nn->nn_lock);
......@@ -1922,6 +1999,9 @@ int o2net_init(void)
o2quo_init();
if (o2net_debugfs_init())
return -ENOMEM;
o2net_hand = kzalloc(sizeof(struct o2net_handshake), GFP_KERNEL);
o2net_keep_req = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL);
o2net_keep_resp = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL);
......@@ -1941,6 +2021,7 @@ int o2net_init(void)
for (i = 0; i < ARRAY_SIZE(o2net_nodes); i++) {
struct o2net_node *nn = o2net_nn_from_num(i);
atomic_set(&nn->nn_timeout, 0);
spin_lock_init(&nn->nn_lock);
INIT_DELAYED_WORK(&nn->nn_connect_work, o2net_start_connect);
INIT_DELAYED_WORK(&nn->nn_connect_expired,
......@@ -1962,4 +2043,5 @@ void o2net_exit(void)
kfree(o2net_hand);
kfree(o2net_keep_req);
kfree(o2net_keep_resp);
o2net_debugfs_exit();
}
......@@ -117,4 +117,36 @@ int o2net_num_connected_peers(void);
int o2net_init(void);
void o2net_exit(void);
struct o2net_send_tracking;
struct o2net_sock_container;
#ifdef CONFIG_DEBUG_FS
int o2net_debugfs_init(void);
void o2net_debugfs_exit(void);
void o2net_debug_add_nst(struct o2net_send_tracking *nst);
void o2net_debug_del_nst(struct o2net_send_tracking *nst);
void o2net_debug_add_sc(struct o2net_sock_container *sc);
void o2net_debug_del_sc(struct o2net_sock_container *sc);
#else
static int o2net_debugfs_init(void)
{
return 0;
}
static void o2net_debugfs_exit(void)
{
}
static void o2net_debug_add_nst(struct o2net_send_tracking *nst)
{
}
static void o2net_debug_del_nst(struct o2net_send_tracking *nst)
{
}
static void o2net_debug_add_sc(struct o2net_sock_container *sc)
{
}
static void o2net_debug_del_sc(struct o2net_sock_container *sc)
{
}
#endif /* CONFIG_DEBUG_FS */
#endif /* O2CLUSTER_TCP_H */
......@@ -95,6 +95,8 @@ struct o2net_node {
unsigned nn_sc_valid:1;
/* if this is set tx just returns it */
int nn_persistent_error;
/* It is only set to 1 after the idle time out. */
atomic_t nn_timeout;
/* threads waiting for an sc to arrive wait on the wq for generation
* to increase. it is increased when a connecting socket succeeds
......@@ -164,7 +166,9 @@ struct o2net_sock_container {
/* original handlers for the sockets */
void (*sc_state_change)(struct sock *sk);
void (*sc_data_ready)(struct sock *sk, int bytes);
#ifdef CONFIG_DEBUG_FS
struct list_head sc_net_debug_item;
#endif
struct timeval sc_tv_timer;
struct timeval sc_tv_data_ready;
struct timeval sc_tv_advance_start;
......@@ -206,4 +210,24 @@ struct o2net_status_wait {
struct list_head ns_node_item;
};
#ifdef CONFIG_DEBUG_FS
/* just for state dumps */
struct o2net_send_tracking {
struct list_head st_net_debug_item;
struct task_struct *st_task;
struct o2net_sock_container *st_sc;
u32 st_id;
u32 st_msg_type;
u32 st_msg_key;
u8 st_node;
struct timeval st_sock_time;
struct timeval st_send_time;
struct timeval st_status_time;
};
#else
struct o2net_send_tracking {
u32 dummy;
};
#endif /* CONFIG_DEBUG_FS */
#endif /* O2CLUSTER_TCP_INTERNAL_H */
EXTRA_CFLAGS += -Ifs/ocfs2
obj-$(CONFIG_OCFS2_FS) += ocfs2_dlm.o ocfs2_dlmfs.o
obj-$(CONFIG_OCFS2_FS_O2CB) += ocfs2_dlm.o ocfs2_dlmfs.o
ocfs2_dlm-objs := dlmdomain.o dlmdebug.o dlmthread.o dlmrecovery.o \
dlmmaster.o dlmast.o dlmconvert.o dlmlock.o dlmunlock.o dlmver.o
......
......@@ -49,6 +49,41 @@
/* Intended to make it easier for us to switch out hash functions */
#define dlm_lockid_hash(_n, _l) full_name_hash(_n, _l)
enum dlm_mle_type {
DLM_MLE_BLOCK,
DLM_MLE_MASTER,
DLM_MLE_MIGRATION
};
struct dlm_lock_name {
u8 len;
u8 name[DLM_LOCKID_NAME_MAX];
};
struct dlm_master_list_entry {
struct list_head list;
struct list_head hb_events;
struct dlm_ctxt *dlm;
spinlock_t spinlock;
wait_queue_head_t wq;
atomic_t woken;
struct kref mle_refs;
int inuse;
unsigned long maybe_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
unsigned long vote_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
unsigned long response_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
unsigned long node_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
u8 master;
u8 new_master;
enum dlm_mle_type type;
struct o2hb_callback_func mle_hb_up;
struct o2hb_callback_func mle_hb_down;
union {
struct dlm_lock_resource *res;
struct dlm_lock_name name;
} u;
};
enum dlm_ast_type {
DLM_AST = 0,
DLM_BAST,
......@@ -101,6 +136,7 @@ struct dlm_ctxt
struct list_head purge_list;
struct list_head pending_asts;
struct list_head pending_basts;
struct list_head tracking_list;
unsigned int purge_count;
spinlock_t spinlock;
spinlock_t ast_lock;
......@@ -122,6 +158,9 @@ struct dlm_ctxt
atomic_t remote_resources;
atomic_t unknown_resources;
struct dlm_debug_ctxt *dlm_debug_ctxt;
struct dentry *dlm_debugfs_subroot;
/* NOTE: Next three are protected by dlm_domain_lock */
struct kref dlm_refs;
enum dlm_ctxt_state dlm_state;
......@@ -270,6 +309,9 @@ struct dlm_lock_resource
struct list_head dirty;
struct list_head recovering; // dlm_recovery_ctxt.resources list
/* Added during init and removed during release */
struct list_head tracking; /* dlm->tracking_list */
/* unused lock resources have their last_used stamped and are
* put on a list for the dlm thread to run. */
unsigned long last_used;
......@@ -963,9 +1005,16 @@ static inline void __dlm_wait_on_lockres(struct dlm_lock_resource *res)
DLM_LOCK_RES_MIGRATING));
}
/* create/destroy slab caches */
int dlm_init_master_caches(void);
void dlm_destroy_master_caches(void);
int dlm_init_lock_cache(void);
void dlm_destroy_lock_cache(void);
int dlm_init_mle_cache(void);
void dlm_destroy_mle_cache(void);
void dlm_hb_event_notify_attached(struct dlm_ctxt *dlm, int idx, int node_up);
int dlm_drop_lockres_ref(struct dlm_ctxt *dlm,
struct dlm_lock_resource *res);
......
This diff is collapsed.
/* -*- mode: c; c-basic-offset: 8; -*-
* vim: noexpandtab sw=8 ts=8 sts=0:
*
* dlmdebug.h
*
* Copyright (C) 2008 Oracle. All rights reserved.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*
*/
#ifndef DLMDEBUG_H
#define DLMDEBUG_H
void dlm_print_one_mle(struct dlm_master_list_entry *mle);
#ifdef CONFIG_DEBUG_FS
struct dlm_debug_ctxt {
struct kref debug_refcnt;
struct dentry *debug_state_dentry;
struct dentry *debug_lockres_dentry;
struct dentry *debug_mle_dentry;
struct dentry *debug_purgelist_dentry;
};
struct debug_buffer {
int len;
char *buf;
};
struct debug_lockres {
int dl_len;
char *dl_buf;
struct dlm_ctxt *dl_ctxt;
struct dlm_lock_resource *dl_res;
};
int dlm_debug_init(struct dlm_ctxt *dlm);
void dlm_debug_shutdown(struct dlm_ctxt *dlm);
int dlm_create_debugfs_subroot(struct dlm_ctxt *dlm);
void dlm_destroy_debugfs_subroot(struct dlm_ctxt *dlm);
int dlm_create_debugfs_root(void);
void dlm_destroy_debugfs_root(void);
#else
static int dlm_debug_init(struct dlm_ctxt *dlm)
{
return 0;
}
static void dlm_debug_shutdown(struct dlm_ctxt *dlm)
{
}
static int dlm_create_debugfs_subroot(struct dlm_ctxt *dlm)
{
return 0;
}
static void dlm_destroy_debugfs_subroot(struct dlm_ctxt *dlm)
{
}
static int dlm_create_debugfs_root(void)
{
return 0;
}
static void dlm_destroy_debugfs_root(void)
{
}
#endif /* CONFIG_DEBUG_FS */
#endif /* DLMDEBUG_H */
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