2010-04-07 02:14:15 +04:00
# include <linux/ceph/ceph_debug.h>
2010-08-03 02:34:23 +04:00
# include <linux/file.h>
# include <linux/namei.h>
2014-03-09 19:16:40 +04:00
# include <linux/random.h>
2010-08-03 02:34:23 +04:00
# include "super.h"
# include "mds_client.h"
2010-04-07 02:14:15 +04:00
# include <linux/ceph/pagelist.h>
2010-08-03 02:34:23 +04:00
2014-03-09 19:16:40 +04:00
static u64 lock_secret ;
static inline u64 secure_addr ( void * addr )
{
u64 v = lock_secret ^ ( u64 ) ( unsigned long ) addr ;
/*
* Set the most significant bit , so that MDS knows the ' owner '
* is sufficient to identify the owner of lock . ( old code uses
* both ' owner ' and ' pid ' )
*/
v | = ( 1ULL < < 63 ) ;
return v ;
}
void __init ceph_flock_init ( void )
{
get_random_bytes ( & lock_secret , sizeof ( lock_secret ) ) ;
}
2010-08-03 02:34:23 +04:00
/**
* Implement fcntl and flock locking functions .
*/
static int ceph_lock_message ( u8 lock_type , u16 operation , struct file * file ,
2010-11-24 00:42:23 +03:00
int cmd , u8 wait , struct file_lock * fl )
2010-08-03 02:34:23 +04:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( file ) ;
2014-03-09 19:16:40 +04:00
struct ceph_mds_client * mdsc = ceph_sb_to_client ( inode - > i_sb ) - > mdsc ;
2010-08-03 02:34:23 +04:00
struct ceph_mds_request * req ;
int err ;
2010-11-24 00:42:23 +03:00
u64 length = 0 ;
2014-03-09 19:16:40 +04:00
u64 owner ;
2010-08-03 02:34:23 +04:00
req = ceph_mdsc_create_request ( mdsc , operation , USE_AUTH_MDS ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
2011-05-27 20:24:26 +04:00
req - > r_inode = inode ;
ihold ( inode ) ;
2014-04-27 05:17:45 +04:00
req - > r_num_caps = 1 ;
2010-08-03 02:34:23 +04:00
2010-11-24 00:42:23 +03:00
/* mds requires start and length rather than start and end */
if ( LLONG_MAX = = fl - > fl_end )
length = 0 ;
else
length = fl - > fl_end - fl - > fl_start + 1 ;
2014-03-09 19:16:40 +04:00
if ( lock_type = = CEPH_LOCK_FCNTL )
owner = secure_addr ( fl - > fl_owner ) ;
else
owner = secure_addr ( fl - > fl_file ) ;
dout ( " ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, "
" start: %llu, length: %llu, wait: %d, type: %d " , ( int ) lock_type ,
( int ) operation , owner , ( u64 ) fl - > fl_pid , fl - > fl_start , length ,
wait , fl - > fl_type ) ;
2010-11-24 00:42:23 +03:00
2010-08-03 02:34:23 +04:00
req - > r_args . filelock_change . rule = lock_type ;
req - > r_args . filelock_change . type = cmd ;
2014-03-09 19:16:40 +04:00
req - > r_args . filelock_change . owner = cpu_to_le64 ( owner ) ;
2010-11-24 00:42:23 +03:00
req - > r_args . filelock_change . pid = cpu_to_le64 ( ( u64 ) fl - > fl_pid ) ;
req - > r_args . filelock_change . start = cpu_to_le64 ( fl - > fl_start ) ;
2010-08-03 02:34:23 +04:00
req - > r_args . filelock_change . length = cpu_to_le64 ( length ) ;
req - > r_args . filelock_change . wait = wait ;
err = ceph_mdsc_do_request ( mdsc , inode , req ) ;
2010-11-24 00:58:29 +03:00
2014-03-09 19:16:40 +04:00
if ( operation = = CEPH_MDS_OP_GETFILELOCK ) {
2010-11-24 00:58:29 +03:00
fl - > fl_pid = le64_to_cpu ( req - > r_reply_info . filelock_reply - > pid ) ;
if ( CEPH_LOCK_SHARED = = req - > r_reply_info . filelock_reply - > type )
fl - > fl_type = F_RDLCK ;
else if ( CEPH_LOCK_EXCL = = req - > r_reply_info . filelock_reply - > type )
fl - > fl_type = F_WRLCK ;
else
fl - > fl_type = F_UNLCK ;
fl - > fl_start = le64_to_cpu ( req - > r_reply_info . filelock_reply - > start ) ;
length = le64_to_cpu ( req - > r_reply_info . filelock_reply - > start ) +
le64_to_cpu ( req - > r_reply_info . filelock_reply - > length ) ;
if ( length > = 1 )
fl - > fl_end = length - 1 ;
else
fl - > fl_end = 0 ;
}
2010-08-03 02:34:23 +04:00
ceph_mdsc_put_request ( req ) ;
dout ( " ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
2011-05-26 01:56:12 +04:00
" length: %llu, wait: %d, type: %d, err code %d " , ( int ) lock_type ,
2010-11-24 00:42:23 +03:00
( int ) operation , ( u64 ) fl - > fl_pid , fl - > fl_start ,
length , wait , fl - > fl_type , err ) ;
2010-08-03 02:34:23 +04:00
return err ;
}
/**
* Attempt to set an fcntl lock .
* For now , this just goes away to the server . Later it may be more awesome .
*/
int ceph_lock ( struct file * file , int cmd , struct file_lock * fl )
{
u8 lock_cmd ;
int err ;
u8 wait = 0 ;
u16 op = CEPH_MDS_OP_SETFILELOCK ;
2014-03-04 11:50:06 +04:00
if ( ! ( fl - > fl_flags & FL_POSIX ) )
return - ENOLCK ;
/* No mandatory locks */
if ( __mandatory_lock ( file - > f_mapping - > host ) & & fl - > fl_type ! = F_UNLCK )
return - ENOLCK ;
2014-03-09 19:16:40 +04:00
dout ( " ceph_lock, fl_owner: %p " , fl - > fl_owner ) ;
2010-08-03 02:34:23 +04:00
/* set wait bit as appropriate, then make command as Ceph expects it*/
2014-03-04 11:42:24 +04:00
if ( IS_GETLK ( cmd ) )
2010-08-03 02:34:23 +04:00
op = CEPH_MDS_OP_GETFILELOCK ;
2014-03-04 11:42:24 +04:00
else if ( IS_SETLKW ( cmd ) )
wait = 1 ;
2010-08-03 02:34:23 +04:00
if ( F_RDLCK = = fl - > fl_type )
lock_cmd = CEPH_LOCK_SHARED ;
else if ( F_WRLCK = = fl - > fl_type )
lock_cmd = CEPH_LOCK_EXCL ;
else
lock_cmd = CEPH_LOCK_UNLOCK ;
2010-11-24 00:42:23 +03:00
err = ceph_lock_message ( CEPH_LOCK_FCNTL , op , file , lock_cmd , wait , fl ) ;
2010-08-03 02:34:23 +04:00
if ( ! err ) {
2014-03-09 19:16:40 +04:00
if ( op ! = CEPH_MDS_OP_GETFILELOCK ) {
2010-11-24 00:58:29 +03:00
dout ( " mds locked, locking locally " ) ;
err = posix_lock_file ( file , fl , NULL ) ;
if ( err & & ( CEPH_MDS_OP_SETFILELOCK = = op ) ) {
2011-05-26 01:56:12 +04:00
/* undo! This should only happen if
* the kernel detects local
* deadlock . */
2010-11-24 00:58:29 +03:00
ceph_lock_message ( CEPH_LOCK_FCNTL , op , file ,
CEPH_LOCK_UNLOCK , 0 , fl ) ;
2011-05-26 01:56:12 +04:00
dout ( " got %d on posix_lock_file, undid lock " ,
err ) ;
2010-11-24 00:58:29 +03:00
}
2010-08-03 02:34:23 +04:00
}
2010-11-24 00:58:29 +03:00
2011-05-26 01:56:12 +04:00
} else if ( err = = - ERESTARTSYS ) {
dout ( " undoing lock \n " ) ;
ceph_lock_message ( CEPH_LOCK_FCNTL , op , file ,
CEPH_LOCK_UNLOCK , 0 , fl ) ;
2010-08-03 02:34:23 +04:00
}
return err ;
}
int ceph_flock ( struct file * file , int cmd , struct file_lock * fl )
{
u8 lock_cmd ;
int err ;
2014-03-04 11:42:24 +04:00
u8 wait = 0 ;
2010-08-03 02:34:23 +04:00
2014-03-04 11:50:06 +04:00
if ( ! ( fl - > fl_flags & FL_FLOCK ) )
return - ENOLCK ;
/* No mandatory locks */
if ( __mandatory_lock ( file - > f_mapping - > host ) & & fl - > fl_type ! = F_UNLCK )
return - ENOLCK ;
2014-03-09 19:16:40 +04:00
dout ( " ceph_flock, fl_file: %p " , fl - > fl_file ) ;
2010-08-03 02:34:23 +04:00
2014-03-04 11:42:24 +04:00
if ( IS_SETLKW ( cmd ) )
wait = 1 ;
if ( F_RDLCK = = fl - > fl_type )
2010-08-03 02:34:23 +04:00
lock_cmd = CEPH_LOCK_SHARED ;
2014-03-04 11:42:24 +04:00
else if ( F_WRLCK = = fl - > fl_type )
2010-08-03 02:34:23 +04:00
lock_cmd = CEPH_LOCK_EXCL ;
else
lock_cmd = CEPH_LOCK_UNLOCK ;
err = ceph_lock_message ( CEPH_LOCK_FLOCK , CEPH_MDS_OP_SETFILELOCK ,
2010-11-24 00:42:23 +03:00
file , lock_cmd , wait , fl ) ;
2010-08-03 02:34:23 +04:00
if ( ! err ) {
err = flock_lock_file_wait ( file , fl ) ;
if ( err ) {
ceph_lock_message ( CEPH_LOCK_FLOCK ,
CEPH_MDS_OP_SETFILELOCK ,
2010-11-24 00:42:23 +03:00
file , CEPH_LOCK_UNLOCK , 0 , fl ) ;
2010-08-03 02:34:23 +04:00
dout ( " got %d on flock_lock_file_wait, undid lock " , err ) ;
}
2011-05-26 01:56:12 +04:00
} else if ( err = = - ERESTARTSYS ) {
dout ( " undoing lock \n " ) ;
ceph_lock_message ( CEPH_LOCK_FLOCK ,
CEPH_MDS_OP_SETFILELOCK ,
file , CEPH_LOCK_UNLOCK , 0 , fl ) ;
2010-08-03 02:34:23 +04:00
}
return err ;
}
/**
2013-05-15 20:38:12 +04:00
* Must be called with lock_flocks ( ) already held . Fills in the passed
2010-08-03 02:34:23 +04:00
* counter variables , so you can prepare pagelist metadata before calling
* ceph_encode_locks .
*/
void ceph_count_locks ( struct inode * inode , int * fcntl_count , int * flock_count )
{
struct file_lock * lock ;
* fcntl_count = 0 ;
* flock_count = 0 ;
for ( lock = inode - > i_flock ; lock ! = NULL ; lock = lock - > fl_next ) {
if ( lock - > fl_flags & FL_POSIX )
+ + ( * fcntl_count ) ;
else if ( lock - > fl_flags & FL_FLOCK )
+ + ( * flock_count ) ;
}
dout ( " counted %d flock locks and %d fcntl locks " ,
* flock_count , * fcntl_count ) ;
}
/**
2013-05-15 22:03:35 +04:00
* Encode the flock and fcntl locks for the given inode into the ceph_filelock
2013-06-21 16:58:15 +04:00
* array . Must be called with inode - > i_lock already held .
2013-05-15 22:03:35 +04:00
* If we encounter more of a specific lock type than expected , return - ENOSPC .
2010-08-03 02:34:23 +04:00
*/
2013-05-15 22:03:35 +04:00
int ceph_encode_locks_to_buffer ( struct inode * inode ,
struct ceph_filelock * flocks ,
int num_fcntl_locks , int num_flock_locks )
2010-08-03 02:34:23 +04:00
{
struct file_lock * lock ;
int err = 0 ;
2010-09-17 21:24:02 +04:00
int seen_fcntl = 0 ;
int seen_flock = 0 ;
2013-05-15 22:03:35 +04:00
int l = 0 ;
2010-08-03 02:34:23 +04:00
dout ( " encoding %d flock and %d fcntl locks " , num_flock_locks ,
num_fcntl_locks ) ;
2013-05-15 22:03:35 +04:00
2010-08-03 02:34:23 +04:00
for ( lock = inode - > i_flock ; lock ! = NULL ; lock = lock - > fl_next ) {
if ( lock - > fl_flags & FL_POSIX ) {
2010-09-17 21:24:02 +04:00
+ + seen_fcntl ;
if ( seen_fcntl > num_fcntl_locks ) {
err = - ENOSPC ;
goto fail ;
}
2013-05-15 22:03:35 +04:00
err = lock_to_ceph_filelock ( lock , & flocks [ l ] ) ;
2010-08-03 02:34:23 +04:00
if ( err )
goto fail ;
2013-05-15 22:03:35 +04:00
+ + l ;
2010-08-03 02:34:23 +04:00
}
}
for ( lock = inode - > i_flock ; lock ! = NULL ; lock = lock - > fl_next ) {
if ( lock - > fl_flags & FL_FLOCK ) {
2010-09-17 21:24:02 +04:00
+ + seen_flock ;
if ( seen_flock > num_flock_locks ) {
err = - ENOSPC ;
goto fail ;
}
2013-05-15 22:03:35 +04:00
err = lock_to_ceph_filelock ( lock , & flocks [ l ] ) ;
2010-08-03 02:34:23 +04:00
if ( err )
goto fail ;
2013-05-15 22:03:35 +04:00
+ + l ;
2010-08-03 02:34:23 +04:00
}
}
fail :
return err ;
}
2013-05-15 22:03:35 +04:00
/**
* Copy the encoded flock and fcntl locks into the pagelist .
* Format is : # fcntl locks , sequential fcntl locks , # flock locks ,
* sequential flock locks .
* Returns zero on success .
*/
int ceph_locks_to_pagelist ( struct ceph_filelock * flocks ,
struct ceph_pagelist * pagelist ,
int num_fcntl_locks , int num_flock_locks )
{
int err = 0 ;
__le32 nlocks ;
nlocks = cpu_to_le32 ( num_fcntl_locks ) ;
err = ceph_pagelist_append ( pagelist , & nlocks , sizeof ( nlocks ) ) ;
if ( err )
goto out_fail ;
err = ceph_pagelist_append ( pagelist , flocks ,
num_fcntl_locks * sizeof ( * flocks ) ) ;
if ( err )
goto out_fail ;
nlocks = cpu_to_le32 ( num_flock_locks ) ;
err = ceph_pagelist_append ( pagelist , & nlocks , sizeof ( nlocks ) ) ;
if ( err )
goto out_fail ;
err = ceph_pagelist_append ( pagelist ,
& flocks [ num_fcntl_locks ] ,
num_flock_locks * sizeof ( * flocks ) ) ;
out_fail :
return err ;
}
2010-08-03 02:34:23 +04:00
/*
* Given a pointer to a lock , convert it to a ceph filelock
*/
int lock_to_ceph_filelock ( struct file_lock * lock ,
struct ceph_filelock * cephlock )
{
int err = 0 ;
cephlock - > start = cpu_to_le64 ( lock - > fl_start ) ;
cephlock - > length = cpu_to_le64 ( lock - > fl_end - lock - > fl_start + 1 ) ;
cephlock - > client = cpu_to_le64 ( 0 ) ;
2014-03-09 19:16:40 +04:00
cephlock - > pid = cpu_to_le64 ( ( u64 ) lock - > fl_pid ) ;
if ( lock - > fl_flags & FL_POSIX )
cephlock - > owner = cpu_to_le64 ( secure_addr ( lock - > fl_owner ) ) ;
else
cephlock - > owner = cpu_to_le64 ( secure_addr ( lock - > fl_file ) ) ;
2010-08-03 02:34:23 +04:00
switch ( lock - > fl_type ) {
case F_RDLCK :
cephlock - > type = CEPH_LOCK_SHARED ;
break ;
case F_WRLCK :
cephlock - > type = CEPH_LOCK_EXCL ;
break ;
case F_UNLCK :
cephlock - > type = CEPH_LOCK_UNLOCK ;
break ;
default :
dout ( " Have unknown lock type %d " , lock - > fl_type ) ;
err = - EINVAL ;
}
return err ;
}