[PATCH 2/2] ocfs2: cluster aware flock()
Hook up ocfs2_flock(), using the new flock lock type in dlmglue.c. A new mount option, "localflocks" is added so that users can revert to old functionality as need be. Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
This commit is contained in:
parent
cf8e06f1a8
commit
53fc622b9e
7 changed files with 237 additions and 1 deletions
|
@ -75,3 +75,4 @@ commit=nrsec (*) Ocfs2 can be told to sync all its data and metadata
|
|||
localalloc=8(*) Allows custom localalloc size in MB. If the value is too
|
||||
large, the fs will silently revert it to the default.
|
||||
Localalloc is not enabled for local mounts.
|
||||
localflocks This disables cluster aware flock.
|
||||
|
|
|
@ -19,6 +19,7 @@ ocfs2-objs := \
|
|||
ioctl.o \
|
||||
journal.o \
|
||||
localalloc.o \
|
||||
locks.o \
|
||||
mmap.o \
|
||||
namei.o \
|
||||
resize.o \
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "inode.h"
|
||||
#include "ioctl.h"
|
||||
#include "journal.h"
|
||||
#include "locks.h"
|
||||
#include "mmap.h"
|
||||
#include "suballoc.h"
|
||||
#include "super.h"
|
||||
|
@ -63,6 +64,35 @@ static int ocfs2_sync_inode(struct inode *inode)
|
|||
return sync_mapping_buffers(inode->i_mapping);
|
||||
}
|
||||
|
||||
static int ocfs2_init_file_private(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ocfs2_file_private *fp;
|
||||
|
||||
fp = kzalloc(sizeof(struct ocfs2_file_private), GFP_KERNEL);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
|
||||
fp->fp_file = file;
|
||||
mutex_init(&fp->fp_mutex);
|
||||
ocfs2_file_lock_res_init(&fp->fp_flock, fp);
|
||||
file->private_data = fp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ocfs2_free_file_private(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ocfs2_file_private *fp = file->private_data;
|
||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||
|
||||
if (fp) {
|
||||
ocfs2_simple_drop_lockres(osb, &fp->fp_flock);
|
||||
ocfs2_lock_res_free(&fp->fp_flock);
|
||||
kfree(fp);
|
||||
file->private_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ocfs2_file_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int status;
|
||||
|
@ -89,7 +119,18 @@ static int ocfs2_file_open(struct inode *inode, struct file *file)
|
|||
|
||||
oi->ip_open_count++;
|
||||
spin_unlock(&oi->ip_lock);
|
||||
status = 0;
|
||||
|
||||
status = ocfs2_init_file_private(inode, file);
|
||||
if (status) {
|
||||
/*
|
||||
* We want to set open count back if we're failing the
|
||||
* open.
|
||||
*/
|
||||
spin_lock(&oi->ip_lock);
|
||||
oi->ip_open_count--;
|
||||
spin_unlock(&oi->ip_lock);
|
||||
}
|
||||
|
||||
leave:
|
||||
mlog_exit(status);
|
||||
return status;
|
||||
|
@ -108,11 +149,24 @@ static int ocfs2_file_release(struct inode *inode, struct file *file)
|
|||
oi->ip_flags &= ~OCFS2_INODE_OPEN_DIRECT;
|
||||
spin_unlock(&oi->ip_lock);
|
||||
|
||||
ocfs2_free_file_private(inode, file);
|
||||
|
||||
mlog_exit(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocfs2_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return ocfs2_init_file_private(inode, file);
|
||||
}
|
||||
|
||||
static int ocfs2_dir_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
ocfs2_free_file_private(inode, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocfs2_sync_file(struct file *file,
|
||||
struct dentry *dentry,
|
||||
int datasync)
|
||||
|
@ -2191,6 +2245,7 @@ const struct file_operations ocfs2_fops = {
|
|||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ocfs2_compat_ioctl,
|
||||
#endif
|
||||
.flock = ocfs2_flock,
|
||||
.splice_read = ocfs2_file_splice_read,
|
||||
.splice_write = ocfs2_file_splice_write,
|
||||
};
|
||||
|
@ -2199,8 +2254,11 @@ const struct file_operations ocfs2_dops = {
|
|||
.read = generic_read_dir,
|
||||
.readdir = ocfs2_readdir,
|
||||
.fsync = ocfs2_sync_file,
|
||||
.release = ocfs2_dir_release,
|
||||
.open = ocfs2_dir_open,
|
||||
.ioctl = ocfs2_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ocfs2_compat_ioctl,
|
||||
#endif
|
||||
.flock = ocfs2_flock,
|
||||
};
|
||||
|
|
125
fs/ocfs2/locks.c
Normal file
125
fs/ocfs2/locks.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/* -*- mode: c; c-basic-offset: 8; -*-
|
||||
* vim: noexpandtab sw=8 ts=8 sts=0:
|
||||
*
|
||||
* locks.c
|
||||
*
|
||||
* Userspace file locking support
|
||||
*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define MLOG_MASK_PREFIX ML_INODE
|
||||
#include <cluster/masklog.h>
|
||||
|
||||
#include "ocfs2.h"
|
||||
|
||||
#include "dlmglue.h"
|
||||
#include "file.h"
|
||||
#include "locks.h"
|
||||
|
||||
static int ocfs2_do_flock(struct file *file, struct inode *inode,
|
||||
int cmd, struct file_lock *fl)
|
||||
{
|
||||
int ret = 0, level = 0, trylock = 0;
|
||||
struct ocfs2_file_private *fp = file->private_data;
|
||||
struct ocfs2_lock_res *lockres = &fp->fp_flock;
|
||||
|
||||
if (fl->fl_type == F_WRLCK)
|
||||
level = 1;
|
||||
if (!IS_SETLKW(cmd))
|
||||
trylock = 1;
|
||||
|
||||
mutex_lock(&fp->fp_mutex);
|
||||
|
||||
if (lockres->l_flags & OCFS2_LOCK_ATTACHED &&
|
||||
lockres->l_level > LKM_NLMODE) {
|
||||
int old_level = 0;
|
||||
|
||||
if (lockres->l_level == LKM_EXMODE)
|
||||
old_level = 1;
|
||||
|
||||
if (level == old_level)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Converting an existing lock is not guaranteed to be
|
||||
* atomic, so we can get away with simply unlocking
|
||||
* here and allowing the lock code to try at the new
|
||||
* level.
|
||||
*/
|
||||
|
||||
flock_lock_file_wait(file,
|
||||
&(struct file_lock){.fl_type = F_UNLCK});
|
||||
|
||||
ocfs2_file_unlock(file);
|
||||
}
|
||||
|
||||
ret = ocfs2_file_lock(file, level, trylock);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN && trylock)
|
||||
ret = -EWOULDBLOCK;
|
||||
else
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = flock_lock_file_wait(file, fl);
|
||||
|
||||
out:
|
||||
mutex_unlock(&fp->fp_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocfs2_do_funlock(struct file *file, int cmd, struct file_lock *fl)
|
||||
{
|
||||
int ret;
|
||||
struct ocfs2_file_private *fp = file->private_data;
|
||||
|
||||
mutex_lock(&fp->fp_mutex);
|
||||
ocfs2_file_unlock(file);
|
||||
ret = flock_lock_file_wait(file, fl);
|
||||
mutex_unlock(&fp->fp_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overall flow of ocfs2_flock() was influenced by gfs2_flock().
|
||||
*/
|
||||
int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||
|
||||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
return -ENOLCK;
|
||||
if (__mandatory_lock(inode))
|
||||
return -ENOLCK;
|
||||
|
||||
if ((osb->s_mount_opt & OCFS2_MOUNT_LOCALFLOCKS) ||
|
||||
ocfs2_mount_local(osb))
|
||||
return flock_lock_file_wait(file, fl);
|
||||
|
||||
if (fl->fl_type == F_UNLCK)
|
||||
return ocfs2_do_funlock(file, cmd, fl);
|
||||
else
|
||||
return ocfs2_do_flock(file, inode, cmd, fl);
|
||||
}
|
31
fs/ocfs2/locks.h
Normal file
31
fs/ocfs2/locks.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* -*- mode: c; c-basic-offset: 8; -*-
|
||||
* vim: noexpandtab sw=8 ts=8 sts=0:
|
||||
*
|
||||
* locks.h
|
||||
*
|
||||
* Function prototypes for Userspace file locking support
|
||||
*
|
||||
* Copyright (C) 2002, 2004 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 OCFS2_LOCKS_H
|
||||
#define OCFS2_LOCKS_H
|
||||
|
||||
int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl);
|
||||
|
||||
#endif /* OCFS2_LOCKS_H */
|
|
@ -171,6 +171,7 @@ enum ocfs2_mount_options
|
|||
OCFS2_MOUNT_NOINTR = 1 << 2, /* Don't catch signals */
|
||||
OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */
|
||||
OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */
|
||||
OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */
|
||||
};
|
||||
|
||||
#define OCFS2_OSB_SOFT_RO 0x0001
|
||||
|
|
|
@ -153,6 +153,7 @@ enum {
|
|||
Opt_slot,
|
||||
Opt_commit,
|
||||
Opt_localalloc,
|
||||
Opt_localflocks,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
|
@ -170,6 +171,7 @@ static match_table_t tokens = {
|
|||
{Opt_slot, "preferred_slot=%u"},
|
||||
{Opt_commit, "commit=%u"},
|
||||
{Opt_localalloc, "localalloc=%d"},
|
||||
{Opt_localflocks, "localflocks"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
|
@ -848,6 +850,20 @@ static int ocfs2_parse_options(struct super_block *sb,
|
|||
if (option >= 0 && (option <= ocfs2_local_alloc_size(sb) * 8))
|
||||
mopt->localalloc_opt = option;
|
||||
break;
|
||||
case Opt_localflocks:
|
||||
/*
|
||||
* Changing this during remount could race
|
||||
* flock() requests, or "unbalance" existing
|
||||
* ones (e.g., a lock is taken in one mode but
|
||||
* dropped in the other). If users care enough
|
||||
* to flip locking modes during remount, we
|
||||
* could add a "local" flag to individual
|
||||
* flock structures for proper tracking of
|
||||
* state.
|
||||
*/
|
||||
if (!is_remount)
|
||||
mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS;
|
||||
break;
|
||||
default:
|
||||
mlog(ML_ERROR,
|
||||
"Unrecognized mount option \"%s\" "
|
||||
|
@ -903,6 +919,9 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
|
|||
if (osb->local_alloc_size != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE)
|
||||
seq_printf(s, ",localalloc=%d", osb->local_alloc_size);
|
||||
|
||||
if (opts & OCFS2_MOUNT_LOCALFLOCKS)
|
||||
seq_printf(s, ",localflocks,");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue