2006-01-17 08:47:12 +00:00
/*
2008-03-14 15:09:15 -05:00
* Copyright ( C ) 2005 - 2008 Red Hat , Inc . All rights reserved .
2006-01-17 08:47:12 +00:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 11:05:15 -04:00
* of the GNU General Public License version 2.
2006-01-17 08:47:12 +00:00
*/
2008-03-14 15:09:15 -05:00
# include <linux/fs.h>
2006-01-17 08:47:12 +00:00
# include <linux/miscdevice.h>
2006-10-19 17:23:57 -04:00
# include <linux/poll.h>
2008-03-14 15:09:15 -05:00
# include <linux/dlm.h>
# include <linux/dlm_plock.h>
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
# include "dlm_internal.h"
# include "lockspace.h"
2006-01-17 08:47:12 +00:00
static spinlock_t ops_lock ;
static struct list_head send_list ;
static struct list_head recv_list ;
static wait_queue_head_t send_wq ;
static wait_queue_head_t recv_wq ;
struct plock_op {
struct list_head list ;
int done ;
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info ;
2006-01-17 08:47:12 +00:00
} ;
2006-11-14 16:37:25 -05:00
struct plock_xop {
struct plock_op xop ;
void * callback ;
void * fl ;
void * file ;
struct file_lock flc ;
} ;
2008-03-14 15:09:15 -05:00
static inline void set_version ( struct dlm_plock_info * info )
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
info - > version [ 0 ] = DLM_PLOCK_VERSION_MAJOR ;
info - > version [ 1 ] = DLM_PLOCK_VERSION_MINOR ;
info - > version [ 2 ] = DLM_PLOCK_VERSION_PATCH ;
2006-01-17 08:47:12 +00:00
}
2008-03-14 15:09:15 -05:00
static int check_version ( struct dlm_plock_info * info )
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
if ( ( DLM_PLOCK_VERSION_MAJOR ! = info - > version [ 0 ] ) | |
( DLM_PLOCK_VERSION_MINOR < info - > version [ 1 ] ) ) {
log_print ( " plock device version mismatch: "
2006-01-17 08:47:12 +00:00
" kernel (%u.%u.%u), user (%u.%u.%u) " ,
2008-03-14 15:09:15 -05:00
DLM_PLOCK_VERSION_MAJOR ,
DLM_PLOCK_VERSION_MINOR ,
DLM_PLOCK_VERSION_PATCH ,
2006-01-17 08:47:12 +00:00
info - > version [ 0 ] ,
info - > version [ 1 ] ,
info - > version [ 2 ] ) ;
return - EINVAL ;
}
return 0 ;
}
static void send_op ( struct plock_op * op )
{
set_version ( & op - > info ) ;
INIT_LIST_HEAD ( & op - > list ) ;
spin_lock ( & ops_lock ) ;
list_add_tail ( & op - > list , & send_list ) ;
spin_unlock ( & ops_lock ) ;
wake_up ( & send_wq ) ;
}
2008-03-14 15:09:15 -05:00
int dlm_posix_lock ( dlm_lockspace_t * lockspace , u64 number , struct file * file ,
int cmd , struct file_lock * fl )
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls * ls ;
2006-01-17 08:47:12 +00:00
struct plock_op * op ;
2006-11-14 16:37:25 -05:00
struct plock_xop * xop ;
2006-01-17 08:47:12 +00:00
int rv ;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local ( lockspace ) ;
if ( ! ls )
return - EINVAL ;
2006-11-14 16:37:25 -05:00
xop = kzalloc ( sizeof ( * xop ) , GFP_KERNEL ) ;
2008-03-14 15:09:15 -05:00
if ( ! xop ) {
rv = - ENOMEM ;
goto out ;
}
2006-01-17 08:47:12 +00:00
2006-11-14 16:37:25 -05:00
op = & xop - > xop ;
2008-03-14 15:09:15 -05:00
op - > info . optype = DLM_PLOCK_OP_LOCK ;
2006-04-25 15:45:51 -04:00
op - > info . pid = fl - > fl_pid ;
2006-01-17 08:47:12 +00:00
op - > info . ex = ( fl - > fl_type = = F_WRLCK ) ;
op - > info . wait = IS_SETLKW ( cmd ) ;
2008-03-14 15:09:15 -05:00
op - > info . fsid = ls - > ls_global_id ;
op - > info . number = number ;
2006-01-17 08:47:12 +00:00
op - > info . start = fl - > fl_start ;
op - > info . end = fl - > fl_end ;
2006-11-14 16:37:25 -05:00
if ( fl - > fl_lmops & & fl - > fl_lmops - > fl_grant ) {
2007-12-06 09:35:25 -06:00
/* fl_owner is lockd which doesn't distinguish
processes on the nfs client */
op - > info . owner = ( __u64 ) fl - > fl_pid ;
2006-11-14 16:37:25 -05:00
xop - > callback = fl - > fl_lmops - > fl_grant ;
locks_init_lock ( & xop - > flc ) ;
locks_copy_lock ( & xop - > flc , fl ) ;
xop - > fl = fl ;
xop - > file = file ;
2007-12-06 09:35:25 -06:00
} else {
op - > info . owner = ( __u64 ) ( long ) fl - > fl_owner ;
2006-11-14 16:37:25 -05:00
xop - > callback = NULL ;
2007-12-06 09:35:25 -06:00
}
2006-01-17 08:47:12 +00:00
send_op ( op ) ;
2006-11-14 16:37:25 -05:00
if ( xop - > callback = = NULL )
wait_event ( recv_wq , ( op - > done ! = 0 ) ) ;
2008-03-14 15:09:15 -05:00
else {
2008-07-25 01:48:57 -07:00
rv = FILE_LOCK_DEFERRED ;
2008-03-14 15:09:15 -05:00
goto out ;
}
2006-01-17 08:47:12 +00:00
spin_lock ( & ops_lock ) ;
if ( ! list_empty ( & op - > list ) ) {
2008-03-14 15:09:15 -05:00
log_error ( ls , " dlm_posix_lock: op on list %llx " ,
( unsigned long long ) number ) ;
2006-01-17 08:47:12 +00:00
list_del ( & op - > list ) ;
}
spin_unlock ( & ops_lock ) ;
rv = op - > info . rv ;
if ( ! rv ) {
if ( posix_lock_file_wait ( file , fl ) < 0 )
2008-03-14 15:09:15 -05:00
log_error ( ls , " dlm_posix_lock: vfs lock error %llx " ,
( unsigned long long ) number ) ;
2006-01-17 08:47:12 +00:00
}
2006-11-14 16:37:25 -05:00
kfree ( xop ) ;
2008-03-14 15:09:15 -05:00
out :
dlm_put_lockspace ( ls ) ;
2006-11-14 16:37:25 -05:00
return rv ;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL ( dlm_posix_lock ) ;
2006-11-14 16:37:25 -05:00
/* Returns failure iff a succesful lock operation should be canceled */
2008-03-14 15:09:15 -05:00
static int dlm_plock_callback ( struct plock_op * op )
2006-11-14 16:37:25 -05:00
{
struct file * file ;
struct file_lock * fl ;
struct file_lock * flc ;
int ( * notify ) ( void * , void * , int ) = NULL ;
struct plock_xop * xop = ( struct plock_xop * ) op ;
int rv = 0 ;
spin_lock ( & ops_lock ) ;
if ( ! list_empty ( & op - > list ) ) {
2008-03-14 15:09:15 -05:00
log_print ( " dlm_plock_callback: op on list %llx " ,
( unsigned long long ) op - > info . number ) ;
2006-11-14 16:37:25 -05:00
list_del ( & op - > list ) ;
}
spin_unlock ( & ops_lock ) ;
/* check if the following 2 are still valid or make a copy */
file = xop - > file ;
flc = & xop - > flc ;
fl = xop - > fl ;
notify = xop - > callback ;
if ( op - > info . rv ) {
2009-01-19 13:13:33 -06:00
notify ( fl , NULL , op - > info . rv ) ;
2006-11-14 16:37:25 -05:00
goto out ;
}
/* got fs lock; bookkeep locally as well: */
flc - > fl_flags & = ~ FL_SLEEP ;
if ( posix_lock_file ( file , flc , NULL ) ) {
/*
* This can only happen in the case of kmalloc ( ) failure .
* The filesystem ' s own lock is the authoritative lock ,
* so a failure to get the lock locally is not a disaster .
2008-03-14 15:09:15 -05:00
* As long as the fs cannot reliably cancel locks ( especially
2006-11-14 16:37:25 -05:00
* in a low - memory situation ) , we ' re better off ignoring
* this failure than trying to recover .
*/
2008-03-14 15:09:15 -05:00
log_print ( " dlm_plock_callback: vfs lock error %llx file %p fl %p " ,
( unsigned long long ) op - > info . number , file , fl ) ;
2006-11-14 16:37:25 -05:00
}
2009-01-19 13:13:33 -06:00
rv = notify ( fl , NULL , 0 ) ;
2006-11-14 16:37:25 -05:00
if ( rv ) {
/* XXX: We need to cancel the fs lock here: */
2008-03-14 15:09:15 -05:00
log_print ( " dlm_plock_callback: lock granted after lock request "
" failed; dangling lock! \n " ) ;
2006-11-14 16:37:25 -05:00
goto out ;
}
out :
kfree ( xop ) ;
2006-01-17 08:47:12 +00:00
return rv ;
}
2008-03-14 15:09:15 -05:00
int dlm_posix_unlock ( dlm_lockspace_t * lockspace , u64 number , struct file * file ,
struct file_lock * fl )
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls * ls ;
2006-01-17 08:47:12 +00:00
struct plock_op * op ;
int rv ;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local ( lockspace ) ;
if ( ! ls )
return - EINVAL ;
2006-01-17 08:47:12 +00:00
op = kzalloc ( sizeof ( * op ) , GFP_KERNEL ) ;
2008-03-14 15:09:15 -05:00
if ( ! op ) {
rv = - ENOMEM ;
goto out ;
}
2006-01-17 08:47:12 +00:00
if ( posix_lock_file_wait ( file , fl ) < 0 )
2008-03-14 15:09:15 -05:00
log_error ( ls , " dlm_posix_unlock: vfs unlock error %llx " ,
( unsigned long long ) number ) ;
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
op - > info . optype = DLM_PLOCK_OP_UNLOCK ;
2006-04-25 15:45:51 -04:00
op - > info . pid = fl - > fl_pid ;
2008-03-14 15:09:15 -05:00
op - > info . fsid = ls - > ls_global_id ;
op - > info . number = number ;
2006-01-17 08:47:12 +00:00
op - > info . start = fl - > fl_start ;
op - > info . end = fl - > fl_end ;
2007-12-06 09:35:25 -06:00
if ( fl - > fl_lmops & & fl - > fl_lmops - > fl_grant )
op - > info . owner = ( __u64 ) fl - > fl_pid ;
else
op - > info . owner = ( __u64 ) ( long ) fl - > fl_owner ;
2006-01-17 08:47:12 +00:00
send_op ( op ) ;
wait_event ( recv_wq , ( op - > done ! = 0 ) ) ;
spin_lock ( & ops_lock ) ;
if ( ! list_empty ( & op - > list ) ) {
2008-03-14 15:09:15 -05:00
log_error ( ls , " dlm_posix_unlock: op on list %llx " ,
( unsigned long long ) number ) ;
2006-01-17 08:47:12 +00:00
list_del ( & op - > list ) ;
}
spin_unlock ( & ops_lock ) ;
rv = op - > info . rv ;
2006-11-14 16:37:25 -05:00
if ( rv = = - ENOENT )
rv = 0 ;
2006-01-17 08:47:12 +00:00
kfree ( op ) ;
2008-03-14 15:09:15 -05:00
out :
dlm_put_lockspace ( ls ) ;
2006-01-17 08:47:12 +00:00
return rv ;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL ( dlm_posix_unlock ) ;
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
int dlm_posix_get ( dlm_lockspace_t * lockspace , u64 number , struct file * file ,
struct file_lock * fl )
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls * ls ;
2006-01-17 08:47:12 +00:00
struct plock_op * op ;
int rv ;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local ( lockspace ) ;
if ( ! ls )
return - EINVAL ;
2006-01-17 08:47:12 +00:00
op = kzalloc ( sizeof ( * op ) , GFP_KERNEL ) ;
2008-03-14 15:09:15 -05:00
if ( ! op ) {
rv = - ENOMEM ;
goto out ;
}
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
op - > info . optype = DLM_PLOCK_OP_GET ;
2006-04-25 15:45:51 -04:00
op - > info . pid = fl - > fl_pid ;
2006-01-17 08:47:12 +00:00
op - > info . ex = ( fl - > fl_type = = F_WRLCK ) ;
2008-03-14 15:09:15 -05:00
op - > info . fsid = ls - > ls_global_id ;
op - > info . number = number ;
2006-01-17 08:47:12 +00:00
op - > info . start = fl - > fl_start ;
op - > info . end = fl - > fl_end ;
2007-12-06 09:35:25 -06:00
if ( fl - > fl_lmops & & fl - > fl_lmops - > fl_grant )
op - > info . owner = ( __u64 ) fl - > fl_pid ;
else
op - > info . owner = ( __u64 ) ( long ) fl - > fl_owner ;
2006-11-14 16:37:25 -05:00
2006-01-17 08:47:12 +00:00
send_op ( op ) ;
wait_event ( recv_wq , ( op - > done ! = 0 ) ) ;
spin_lock ( & ops_lock ) ;
if ( ! list_empty ( & op - > list ) ) {
2008-03-14 15:09:15 -05:00
log_error ( ls , " dlm_posix_get: op on list %llx " ,
( unsigned long long ) number ) ;
2006-01-17 08:47:12 +00:00
list_del ( & op - > list ) ;
}
spin_unlock ( & ops_lock ) ;
2007-06-08 17:01:40 -05:00
/* info.rv from userspace is 1 for conflict, 0 for no-conflict,
- ENOENT if there are no locks on the file */
2006-01-17 08:47:12 +00:00
rv = op - > info . rv ;
2006-11-14 16:37:25 -05:00
fl - > fl_type = F_UNLCK ;
if ( rv = = - ENOENT )
rv = 0 ;
2007-06-08 17:01:40 -05:00
else if ( rv > 0 ) {
2009-01-21 11:34:50 -05:00
locks_init_lock ( fl ) ;
2006-01-17 08:47:12 +00:00
fl - > fl_type = ( op - > info . ex ) ? F_WRLCK : F_RDLCK ;
2009-01-21 11:34:50 -05:00
fl - > fl_flags = FL_POSIX ;
2006-01-17 08:47:12 +00:00
fl - > fl_pid = op - > info . pid ;
fl - > fl_start = op - > info . start ;
fl - > fl_end = op - > info . end ;
2007-06-08 17:01:40 -05:00
rv = 0 ;
2006-01-17 08:47:12 +00:00
}
kfree ( op ) ;
2008-03-14 15:09:15 -05:00
out :
dlm_put_lockspace ( ls ) ;
2006-01-17 08:47:12 +00:00
return rv ;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL ( dlm_posix_get ) ;
2006-01-17 08:47:12 +00:00
/* a read copies out one plock request from the send list */
static ssize_t dev_read ( struct file * file , char __user * u , size_t count ,
loff_t * ppos )
{
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info ;
2006-01-17 08:47:12 +00:00
struct plock_op * op = NULL ;
if ( count < sizeof ( info ) )
return - EINVAL ;
spin_lock ( & ops_lock ) ;
if ( ! list_empty ( & send_list ) ) {
op = list_entry ( send_list . next , struct plock_op , list ) ;
list_move ( & op - > list , & recv_list ) ;
memcpy ( & info , & op - > info , sizeof ( info ) ) ;
}
spin_unlock ( & ops_lock ) ;
if ( ! op )
return - EAGAIN ;
if ( copy_to_user ( u , & info , sizeof ( info ) ) )
return - EFAULT ;
return sizeof ( info ) ;
}
/* a write copies in one plock result that should match a plock_op
on the recv list */
static ssize_t dev_write ( struct file * file , const char __user * u , size_t count ,
loff_t * ppos )
{
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info ;
2006-01-17 08:47:12 +00:00
struct plock_op * op ;
int found = 0 ;
if ( count ! = sizeof ( info ) )
return - EINVAL ;
if ( copy_from_user ( & info , u , sizeof ( info ) ) )
return - EFAULT ;
if ( check_version ( & info ) )
return - EINVAL ;
spin_lock ( & ops_lock ) ;
list_for_each_entry ( op , & recv_list , list ) {
2006-09-08 10:17:58 -04:00
if ( op - > info . fsid = = info . fsid & & op - > info . number = = info . number & &
2006-08-04 16:19:20 -05:00
op - > info . owner = = info . owner ) {
2006-01-17 08:47:12 +00:00
list_del_init ( & op - > list ) ;
found = 1 ;
op - > done = 1 ;
memcpy ( & op - > info , & info , sizeof ( info ) ) ;
break ;
}
}
spin_unlock ( & ops_lock ) ;
2006-11-14 16:37:25 -05:00
if ( found ) {
struct plock_xop * xop ;
xop = ( struct plock_xop * ) op ;
if ( xop - > callback )
2008-05-13 14:28:26 -05:00
dlm_plock_callback ( op ) ;
2006-11-14 16:37:25 -05:00
else
wake_up ( & recv_wq ) ;
} else
2008-03-14 15:09:15 -05:00
log_print ( " dev_write no op %x %llx " , info . fsid ,
( unsigned long long ) info . number ) ;
2006-01-17 08:47:12 +00:00
return count ;
}
static unsigned int dev_poll ( struct file * file , poll_table * wait )
{
2007-07-25 17:53:58 +08:00
unsigned int mask = 0 ;
2006-01-17 08:47:12 +00:00
poll_wait ( file , & send_wq , wait ) ;
spin_lock ( & ops_lock ) ;
2007-07-25 17:53:58 +08:00
if ( ! list_empty ( & send_list ) )
mask = POLLIN | POLLRDNORM ;
2006-01-17 08:47:12 +00:00
spin_unlock ( & ops_lock ) ;
2007-07-25 17:53:58 +08:00
return mask ;
2006-01-17 08:47:12 +00:00
}
2007-02-12 00:55:34 -08:00
static const struct file_operations dev_fops = {
2006-01-17 08:47:12 +00:00
. read = dev_read ,
. write = dev_write ,
. poll = dev_poll ,
. owner = THIS_MODULE
} ;
static struct miscdevice plock_dev_misc = {
. minor = MISC_DYNAMIC_MINOR ,
2008-03-14 15:09:15 -05:00
. name = DLM_PLOCK_MISC_NAME ,
2006-01-17 08:47:12 +00:00
. fops = & dev_fops
} ;
2008-03-14 15:09:15 -05:00
int dlm_plock_init ( void )
2006-01-17 08:47:12 +00:00
{
int rv ;
spin_lock_init ( & ops_lock ) ;
INIT_LIST_HEAD ( & send_list ) ;
INIT_LIST_HEAD ( & recv_list ) ;
init_waitqueue_head ( & send_wq ) ;
init_waitqueue_head ( & recv_wq ) ;
rv = misc_register ( & plock_dev_misc ) ;
if ( rv )
2008-03-14 15:09:15 -05:00
log_print ( " dlm_plock_init: misc_register failed %d " , rv ) ;
2006-01-17 08:47:12 +00:00
return rv ;
}
2008-03-14 15:09:15 -05:00
void dlm_plock_exit ( void )
2006-01-17 08:47:12 +00:00
{
if ( misc_deregister ( & plock_dev_misc ) < 0 )
2008-03-14 15:09:15 -05:00
log_print ( " dlm_plock_exit: misc_deregister failed " ) ;
2006-01-17 08:47:12 +00:00
}