2009-01-28 03:13:35 +03:00
/*
* Unix SMB / CIFS implementation .
* Support for OneFS kernel oplocks
*
* Copyright ( C ) Volker Lendecke 2007
* Copyright ( C ) Tim Prouty , 2009
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# define DBGC_CLASS DBGC_LOCKING
# include "includes.h"
# if HAVE_ONEFS
# include "oplock_onefs.h"
# include "smbd/globals.h"
# include <ifs/ifs_syscalls.h>
# include <isi_ecs/isi_ecs_oplocks.h>
# include <sys/proc.h>
struct onefs_oplocks_context {
struct kernel_oplocks * ctx ;
const struct oplocks_event_ops * onefs_ops ;
int onefs_event_fd ;
struct fd_event * read_fde ;
} ;
enum onefs_callback_state {
ONEFS_OPEN_FILE ,
ONEFS_WAITING_FOR_OPLOCK
} ;
struct onefs_callback_record {
struct onefs_callback_record * prev , * next ;
uint64_t id ;
enum onefs_callback_state state ;
union {
files_struct * fsp ; /* ONEFS_OPEN_FILE */
uint16_t mid ; /* ONEFS_WAITING_FOR_OPLOCK */
} data ;
} ;
/**
* Internal list of files ( along with additional state ) that have outstanding
* oplocks or requests for oplocks .
*/
struct onefs_callback_record * callback_recs ;
/**
2009-07-10 22:50:30 +04:00
* Convert a onefs_callback_record to a debug string using the dbg_ctx ( ) .
2009-01-28 03:13:35 +03:00
*/
2009-07-10 22:50:30 +04:00
const char * onefs_cb_record_str_dbg ( const struct onefs_callback_record * r )
2009-01-28 03:13:35 +03:00
{
2009-07-10 22:50:30 +04:00
char * result ;
2009-01-28 03:13:35 +03:00
if ( r = = NULL ) {
2009-11-03 12:59:18 +03:00
result = talloc_strdup ( talloc_tos ( ) , " NULL callback record " ) ;
2009-01-28 03:13:35 +03:00
return result ;
}
switch ( r - > state ) {
case ONEFS_OPEN_FILE :
2009-11-03 12:59:18 +03:00
result = talloc_asprintf ( talloc_tos ( ) , " cb record %llu for "
2009-07-25 04:09:42 +04:00
" file %s " , r - > id ,
2009-07-11 01:50:37 +04:00
fsp_str_dbg ( r - > data . fsp ) ) ;
2009-01-28 03:13:35 +03:00
case ONEFS_WAITING_FOR_OPLOCK :
2009-11-03 12:59:18 +03:00
result = talloc_asprintf ( talloc_tos ( ) , " cb record %llu for "
2009-07-10 22:50:30 +04:00
" pending mid %d " , r - > id ,
( int ) r - > data . mid ) ;
2009-01-28 03:13:35 +03:00
break ;
default :
2009-11-03 12:59:18 +03:00
result = talloc_asprintf ( talloc_tos ( ) , " cb record %llu unknown "
2009-07-10 22:50:30 +04:00
" state %d " , r - > id , r - > state ) ;
2009-01-28 03:13:35 +03:00
break ;
}
return result ;
}
/**
* Traverse the list of onefs_callback_records and print all entries .
*/
static void debug_cb_records ( const char * fn )
{
struct onefs_callback_record * rec ;
if ( DEBUGLEVEL < 10 )
return ;
DEBUG ( 10 , ( " cb records (%s): \n " , fn ) ) ;
for ( rec = callback_recs ; rec ; rec = rec - > next ) {
2009-07-25 04:09:42 +04:00
DEBUGADD ( 10 , ( " %s \n " , onefs_cb_record_str_dbg ( rec ) ) ) ;
2009-01-28 03:13:35 +03:00
}
}
/**
* Find a callback record in the list of outstanding oplock operations .
*
* Once n ifs_createfile requests an oplock on a file , the kernel communicates
* with samba via the oplock event channel by sending events that reference an
* id . This function maps that id to the onefs_callback_record that was
* created for it during the initial setup on open ( onefs_oplock_wait_record ) .
* When a matching id is found in the onefs_callback_record list , the
* callback_type is checked to make sure the record is in in the correct
* state .
*/
static struct onefs_callback_record * onefs_find_cb ( uint64_t id ,
enum onefs_callback_state expected_state )
{
struct onefs_callback_record * rec ;
debug_cb_records ( " onefs_find_cb " ) ;
for ( rec = callback_recs ; rec ; rec = rec - > next ) {
if ( rec - > id = = id ) {
DEBUG ( 10 , ( " found %s \n " ,
2009-07-25 04:09:42 +04:00
onefs_cb_record_str_dbg ( rec ) ) ) ;
2009-01-28 03:13:35 +03:00
break ;
}
}
if ( rec = = NULL ) {
DEBUG ( 5 , ( " Could not find callback record for id %llu \n " , id ) ) ;
return NULL ;
}
if ( rec - > state ! = expected_state ) {
DEBUG ( 0 , ( " Expected cb type %d, got %s " , expected_state ,
2009-07-25 04:09:42 +04:00
onefs_cb_record_str_dbg ( rec ) ) ) ;
2009-01-28 03:13:35 +03:00
SMB_ASSERT ( 0 ) ;
return NULL ;
}
return rec ;
}
/**
* Remove and free a callback record from the callback record list .
*/
void destroy_onefs_callback_record ( uint64_t id )
{
struct onefs_callback_record * rec ;
debug_cb_records ( " destroy_onefs_callback_record " ) ;
if ( id = = 0 ) {
DEBUG ( 10 , ( " destroy_onefs_callback_record: Nothing to "
" destroy \n " ) ) ;
return ;
}
for ( rec = callback_recs ; rec ; rec = rec - > next ) {
if ( rec - > id = = id ) {
DLIST_REMOVE ( callback_recs , rec ) ;
SAFE_FREE ( rec ) ;
DEBUG ( 10 , ( " removed cb rec %llu \n " , id ) ) ;
return ;
}
}
DEBUG ( 0 , ( " Could not find cb rec %llu to delete " , id ) ) ;
SMB_ASSERT ( 0 ) ;
}
/**
* Initialize a callback record and add it to the list of outstanding callback
* records .
*
* This is called in the open path before ifs_createfile so an id can be
* passed in . Each callback record can be in one of two states :
*
* 1. WAITING_FOR_OPLOCK : This is the initial state for all callback
* records . If ifs_createfile can be completed syncronously without needing
* to break any level I oplocks , the state is transitioned to OPEN_FILE .
* Otherwise ifs_createfile will finish asynchronously and the open is
* deferred . When the necessary level I opocks have been broken , and the
* open can be done , an event is sent by the kernel on the oplock event
* channel , which is handled by semlock_available_handler . At this point
* the deferred open is retried . Unless a level I oplock was acquired by
* another client , ifs_createfile will now complete synchronously .
*
* 2. OPEN_FILE : Once ifs_createfile completes , the callback record is
* transitioned to this state via onefs_set_oplock_callback .
*/
uint64_t onefs_oplock_wait_record ( uint16_t mid )
{
struct onefs_callback_record * result ;
static uint64_t id_generator = 0 ;
if ( ! ( result = SMB_MALLOC_P ( struct onefs_callback_record ) ) ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return 0 ;
}
memset ( result , ' \0 ' , sizeof ( result ) ) ;
id_generator + = 1 ;
if ( id_generator = = 0 ) {
/* Wow, that's a long-running smbd... */
id_generator + = 1 ;
}
result - > id = id_generator ;
result - > state = ONEFS_WAITING_FOR_OPLOCK ;
result - > data . mid = mid ;
DLIST_ADD ( callback_recs , result ) ;
DEBUG ( 10 , ( " New cb rec %llu created \n " , result - > id ) ) ;
return result - > id ;
}
/**
* Transition the callback record state to OPEN_FILE .
*
* This is called after the file is opened and an fsp struct has been
* allocated . The mid is dropped in favor of storing the fsp .
*/
void onefs_set_oplock_callback ( uint64_t id , files_struct * fsp )
{
struct onefs_callback_record * cb ;
char * msg ;
DEBUG ( 10 , ( " onefs_set_oplock_callback called for cb rec %llu \n " , id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_WAITING_FOR_OPLOCK ) ) ) {
if ( asprintf ( & msg , " Got invalid callback %lld \n " , id ) ! = - 1 ) {
smb_panic ( msg ) ;
}
smb_panic ( " Got invalid callback id \n " ) ;
}
/*
* Paranoia check
*/
if ( open_was_deferred ( cb - > data . mid ) ) {
if ( asprintf ( & msg , " Trying to upgrade callback for deferred "
" open mid=%d \n " , cb - > data . mid ) ! = - 1 ) {
smb_panic ( msg ) ;
}
smb_panic ( " Trying to upgrade callback for deferred open "
" mid \n " ) ;
}
cb - > state = ONEFS_OPEN_FILE ;
cb - > data . fsp = fsp ;
}
/**
* Using a callback record , initialize a share mode entry to pass to
* share_mode_entry_to_message to send samba IPC messages .
*/
static void init_share_mode_entry ( struct share_mode_entry * sme ,
struct onefs_callback_record * cb ,
int op_type )
{
ZERO_STRUCT ( * sme ) ;
sme - > pid = procid_self ( ) ;
sme - > op_type = op_type ;
sme - > id = cb - > data . fsp - > file_id ;
sme - > share_file_id = cb - > data . fsp - > fh - > gen_id ;
}
/**
* Callback when a break - to - none event is received from the kernel .
*
* On OneFS level 1 oplocks are always broken to level 2 first , therefore an
* async level 2 break message is always sent when breaking to none . The
* downside of this is that OneFS currently has no way to express breaking
* directly from level 1 to none .
*/
static void oplock_break_to_none_handler ( uint64_t id )
{
struct onefs_callback_record * cb ;
struct share_mode_entry sme ;
char msg [ MSG_SMB_SHARE_MODE_ENTRY_SIZE ] ;
DEBUG ( 10 , ( " oplock_break_to_none_handler called for id %llu \n " , id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_OPEN_FILE ) ) ) {
DEBUG ( 3 , ( " oplock_break_to_none_handler: could not find "
" callback id %llu \n " , id ) ) ;
return ;
}
DEBUG ( 10 , ( " oplock_break_to_none_handler called for file %s \n " ,
2009-07-25 04:09:42 +04:00
fsp_str_dbg ( cb - > data . fsp ) ) ) ;
2009-01-28 03:13:35 +03:00
init_share_mode_entry ( & sme , cb , FORCE_OPLOCK_BREAK_TO_NONE ) ;
share_mode_entry_to_message ( msg , & sme ) ;
messaging_send_buf ( smbd_messaging_context ( ) ,
sme . pid ,
MSG_SMB_ASYNC_LEVEL2_BREAK ,
( uint8_t * ) msg ,
MSG_SMB_SHARE_MODE_ENTRY_SIZE ) ;
/*
* We could still receive an OPLOCK_REVOKED message , so keep the
* oplock_callback_id around .
*/
}
/**
* Callback when a break - to - level2 event is received from the kernel .
*
* Breaks from level 1 to level 2.
*/
static void oplock_break_to_level_two_handler ( uint64_t id )
{
struct onefs_callback_record * cb ;
struct share_mode_entry sme ;
char msg [ MSG_SMB_SHARE_MODE_ENTRY_SIZE ] ;
DEBUG ( 10 , ( " oplock_break_to_level_two_handler called for id %llu \n " ,
id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_OPEN_FILE ) ) ) {
DEBUG ( 3 , ( " oplock_break_to_level_two_handler: could not find "
" callback id %llu \n " , id ) ) ;
return ;
}
DEBUG ( 10 , ( " oplock_break_to_level_two_handler called for file %s \n " ,
2009-07-25 04:09:42 +04:00
fsp_str_dbg ( cb - > data . fsp ) ) ) ;
2009-01-28 03:13:35 +03:00
init_share_mode_entry ( & sme , cb , LEVEL_II_OPLOCK ) ;
share_mode_entry_to_message ( msg , & sme ) ;
messaging_send_buf ( smbd_messaging_context ( ) ,
sme . pid ,
MSG_SMB_BREAK_REQUEST ,
( uint8_t * ) msg ,
MSG_SMB_SHARE_MODE_ENTRY_SIZE ) ;
/*
* We could still receive an OPLOCK_REVOKED or OPLOCK_BREAK_TO_NONE
* message , so keep the oplock_callback_id around .
*/
}
/**
* Revoke an oplock from an unresponsive client .
*
* The kernel will send this message when it times out waiting for a level 1
* oplock break to be acknowledged by the client . The oplock is then
* immediately removed .
*/
static void oplock_revoked_handler ( uint64_t id )
{
struct onefs_callback_record * cb ;
files_struct * fsp = NULL ;
DEBUG ( 10 , ( " oplock_revoked_handler called for id %llu \n " , id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_OPEN_FILE ) ) ) {
DEBUG ( 3 , ( " oplock_revoked_handler: could not find "
" callback id %llu \n " , id ) ) ;
return ;
}
fsp = cb - > data . fsp ;
SMB_ASSERT ( fsp - > oplock_timeout = = NULL ) ;
DEBUG ( 0 , ( " Level 1 oplock break failed for file %s. Forcefully "
2009-07-11 01:50:37 +04:00
" revoking oplock \n " , fsp_str_dbg ( fsp ) ) ) ;
2009-01-28 03:13:35 +03:00
global_client_failed_oplock_break = True ;
remove_oplock ( fsp ) ;
/*
* cb record is cleaned up in fsp ext data destructor on close , so
* leave it in the list .
*/
}
/**
* Asynchronous ifs_createfile callback
*
* If ifs_createfile had to asynchronously break any oplocks , this function is
* called when the kernel sends an event that the open can be retried .
*/
static void semlock_available_handler ( uint64_t id )
{
struct onefs_callback_record * cb ;
DEBUG ( 10 , ( " semlock_available_handler called: %llu \n " , id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_WAITING_FOR_OPLOCK ) ) ) {
DEBUG ( 5 , ( " semlock_available_handler: Did not find callback "
" %llu \n " , id ) ) ;
return ;
}
DEBUG ( 10 , ( " Got semlock available for mid %d \n " , cb - > data . mid ) ) ;
/* Paranoia check */
if ( ! ( open_was_deferred ( cb - > data . mid ) ) ) {
char * msg ;
if ( asprintf ( & msg , " Semlock available on an open that wasn't "
" deferred: %s \n " ,
2009-07-25 04:09:42 +04:00
onefs_cb_record_str_dbg ( cb ) ) ! = - 1 ) {
2009-01-28 03:13:35 +03:00
smb_panic ( msg ) ;
}
smb_panic ( " Semlock available on an open that wasn't "
" deferred \n " ) ;
}
schedule_deferred_open_smb_message ( cb - > data . mid ) ;
/* Cleanup the callback record since the open will be retried. */
destroy_onefs_callback_record ( id ) ;
return ;
}
/**
* Asynchronous ifs_createfile failure callback
*
* If ifs_createfile had to asynchronously break any oplocks , but an error was
* encountered in the kernel , the open will be retried with the state - > failed
* set to true . This will prompt the open path to send an INTERNAL_ERROR
* error message to the client .
*/
static void semlock_async_failure_handler ( uint64_t id )
{
struct onefs_callback_record * cb ;
struct pending_message_list * pml ;
struct deferred_open_record * state ;
DEBUG ( 1 , ( " semlock_async_failure_handler called: %llu \n " , id ) ) ;
if ( ! ( cb = onefs_find_cb ( id , ONEFS_WAITING_FOR_OPLOCK ) ) ) {
DEBUG ( 5 , ( " semlock_async_failure_handler: Did not find callback "
" %llu \n " , id ) ) ;
return ;
}
DEBUG ( 1 , ( " Got semlock_async_failure message for mid %d \n " , cb - > data . mid ) ) ;
/* Paranoia check */
if ( ! ( open_was_deferred ( cb - > data . mid ) ) ) {
char * msg ;
if ( asprintf ( & msg , " Semlock failure on an open that wasn't "
" deferred: %s \n " ,
2009-07-25 04:09:42 +04:00
onefs_cb_record_str_dbg ( cb ) ) ! = - 1 ) {
2009-01-28 03:13:35 +03:00
smb_panic ( msg ) ;
}
smb_panic ( " Semlock failure on an open that wasn't deferred \n " ) ;
}
/* Find the actual deferred open record. */
if ( ! ( pml = get_open_deferred_message ( cb - > data . mid ) ) ) {
DEBUG ( 0 , ( " Could not find deferred request for "
" mid %d \n " , cb - > data . mid ) ) ;
destroy_onefs_callback_record ( id ) ;
return ;
}
state = ( struct deferred_open_record * ) pml - > private_data . data ;
/* Update to failed so the client can be notified on retried open. */
state - > failed = true ;
/* Schedule deferred open for immediate retry. */
schedule_deferred_open_smb_message ( cb - > data . mid ) ;
/* Cleanup the callback record here since the open will be retried. */
destroy_onefs_callback_record ( id ) ;
return ;
}
/**
* OneFS acquires all oplocks via ifs_createfile , so this is a no - op .
*/
static bool onefs_set_kernel_oplock ( struct kernel_oplocks * _ctx ,
files_struct * fsp , int oplock_type ) {
return true ;
}
/**
* Release the kernel oplock .
*/
static void onefs_release_kernel_oplock ( struct kernel_oplocks * _ctx ,
files_struct * fsp , int oplock_type )
{
enum oplock_type oplock = onefs_samba_oplock_to_oplock ( oplock_type ) ;
DEBUG ( 10 , ( " onefs_release_kernel_oplock: Releasing %s to type %s \n " ,
2009-07-11 01:50:37 +04:00
fsp_str_dbg ( fsp ) , onefs_oplock_str ( oplock ) ) ) ;
2009-01-28 03:13:35 +03:00
if ( fsp - > fh - > fd = = - 1 ) {
DEBUG ( 1 , ( " no fd \n " ) ) ;
return ;
}
/* Downgrade oplock to either SHARED or NONE. */
if ( ifs_oplock_downgrade ( fsp - > fh - > fd , oplock ) ) {
DEBUG ( 1 , ( " ifs_oplock_downgrade failed: %s \n " ,
strerror ( errno ) ) ) ;
}
}
/**
* Wrap ifs_semlock_write so it is only called on operations that aren ' t
* already contended in the kernel .
*/
static void onefs_semlock_write ( int fd , enum level2_contention_type type ,
enum semlock_operation semlock_op )
{
int ret ;
switch ( type ) {
case LEVEL2_CONTEND_ALLOC_GROW :
case LEVEL2_CONTEND_POSIX_BRL :
DEBUG ( 10 , ( " Taking %d write semlock for cmd %d on fd: %d \n " ,
semlock_op , type , fd ) ) ;
ret = ifs_semlock_write ( fd , semlock_op ) ;
if ( ret ) {
DEBUG ( 0 , ( " ifs_semlock_write failed taking %d write "
" semlock for cmd %d on fd: %d: %s " ,
semlock_op , type , fd , strerror ( errno ) ) ) ;
}
break ;
default :
DEBUG ( 10 , ( " Skipping write semlock for cmd %d on fd: %d \n " ,
type , fd ) ) ;
}
}
/**
* Contend level 2 oplocks in the kernel and smbd .
*
* Taking a write semlock will contend all level 2 oplocks in all smbds across
* the cluster except the fsp ' s own level 2 oplock . This lack of
* self - contention is a limitation of the current OneFS kernel oplocks
* implementation . Luckily it is easy to contend our own level 2 oplock by
2009-04-03 05:24:43 +04:00
* checking the the fsp ' s oplock_type . If it ' s a level2 , send a break message
* to the client and remove the oplock .
2009-01-28 03:13:35 +03:00
*/
static void onefs_contend_level2_oplocks_begin ( files_struct * fsp ,
enum level2_contention_type type )
{
/* Take care of level 2 kernel contention. */
onefs_semlock_write ( fsp - > fh - > fd , type , SEMLOCK_LOCK ) ;
2009-04-03 05:24:43 +04:00
/* Take care of level 2 self contention. */
if ( LEVEL_II_OPLOCK_TYPE ( fsp - > oplock_type ) )
break_level2_to_none_async ( fsp ) ;
2009-01-28 03:13:35 +03:00
}
/**
* Unlock the write semlock when the level 2 contending operation ends .
*/
static void onefs_contend_level2_oplocks_end ( files_struct * fsp ,
enum level2_contention_type type )
{
/* Take care of level 2 kernel contention. */
onefs_semlock_write ( fsp - > fh - > fd , type , SEMLOCK_UNLOCK ) ;
}
/**
* Return string value of onefs oplock types .
*/
const char * onefs_oplock_str ( enum oplock_type onefs_oplock_type )
{
switch ( onefs_oplock_type ) {
case OPLOCK_NONE :
return " OPLOCK_NONE " ;
case OPLOCK_EXCLUSIVE :
return " OPLOCK_EXCLUSIVE " ;
case OPLOCK_BATCH :
return " OPLOCK_BATCH " ;
case OPLOCK_SHARED :
return " OPLOCK_SHARED " ;
default :
break ;
}
return " UNKNOWN " ;
}
/**
* Convert from onefs to samba oplock .
*/
int onefs_oplock_to_samba_oplock ( enum oplock_type onefs_oplock )
{
switch ( onefs_oplock ) {
case OPLOCK_NONE :
return NO_OPLOCK ;
case OPLOCK_EXCLUSIVE :
return EXCLUSIVE_OPLOCK ;
case OPLOCK_BATCH :
return BATCH_OPLOCK ;
case OPLOCK_SHARED :
return LEVEL_II_OPLOCK ;
default :
DEBUG ( 0 , ( " unknown oplock type %d found \n " , onefs_oplock ) ) ;
break ;
}
return NO_OPLOCK ;
}
/**
* Convert from samba to onefs oplock .
*/
enum oplock_type onefs_samba_oplock_to_oplock ( int samba_oplock_type )
{
if ( BATCH_OPLOCK_TYPE ( samba_oplock_type ) ) return OPLOCK_BATCH ;
if ( EXCLUSIVE_OPLOCK_TYPE ( samba_oplock_type ) ) return OPLOCK_EXCLUSIVE ;
if ( LEVEL_II_OPLOCK_TYPE ( samba_oplock_type ) ) return OPLOCK_SHARED ;
return OPLOCK_NONE ;
}
/**
* Oplock event handler .
*
* Call into the event system dispatcher to handle each event .
*/
static void onefs_oplocks_read_fde_handler ( struct event_context * ev ,
struct fd_event * fde ,
uint16_t flags ,
void * private_data )
{
struct onefs_oplocks_context * ctx =
talloc_get_type ( private_data , struct onefs_oplocks_context ) ;
if ( oplocks_event_dispatcher ( ctx - > onefs_ops ) ) {
DEBUG ( 0 , ( " oplocks_event_dispatcher failed: %s \n " ,
strerror ( errno ) ) ) ;
}
}
/**
* Setup kernel oplocks
*/
static const struct kernel_oplocks_ops onefs_koplocks_ops = {
. set_oplock = onefs_set_kernel_oplock ,
. release_oplock = onefs_release_kernel_oplock ,
. contend_level2_oplocks_begin = onefs_contend_level2_oplocks_begin ,
. contend_level2_oplocks_end = onefs_contend_level2_oplocks_end ,
} ;
static const struct oplocks_event_ops onefs_dispatch_ops = {
. oplock_break_to_none = oplock_break_to_none_handler ,
. oplock_break_to_level_two = oplock_break_to_level_two_handler ,
. oplock_revoked = oplock_revoked_handler ,
. semlock_available = semlock_available_handler ,
. semlock_async_failure = semlock_async_failure_handler ,
} ;
struct kernel_oplocks * onefs_init_kernel_oplocks ( TALLOC_CTX * mem_ctx )
{
struct kernel_oplocks * _ctx = NULL ;
struct onefs_oplocks_context * ctx = NULL ;
struct procoptions po = PROCOPTIONS_INIT ;
DEBUG ( 10 , ( " onefs_init_kernel_oplocks called \n " ) ) ;
/* Set the non-blocking proc flag */
po . po_flags_on | = P_NON_BLOCKING_SEMLOCK ;
if ( setprocoptions ( & po ) ! = 0 ) {
DEBUG ( 0 , ( " setprocoptions failed: %s. \n " , strerror ( errno ) ) ) ;
2009-02-24 04:43:47 +03:00
return NULL ;
2009-01-28 03:13:35 +03:00
}
/* Setup the oplock contexts */
_ctx = talloc_zero ( mem_ctx , struct kernel_oplocks ) ;
if ( ! _ctx ) {
2009-02-24 04:43:47 +03:00
return NULL ;
2009-01-28 03:13:35 +03:00
}
ctx = talloc_zero ( _ctx , struct onefs_oplocks_context ) ;
if ( ! ctx ) {
goto err_out ;
}
_ctx - > ops = & onefs_koplocks_ops ;
_ctx - > flags = ( KOPLOCKS_LEVEL2_SUPPORTED |
KOPLOCKS_DEFERRED_OPEN_NOTIFICATION |
KOPLOCKS_TIMEOUT_NOTIFICATION |
KOPLOCKS_OPLOCK_BROKEN_NOTIFICATION ) ;
_ctx - > private_data = ctx ;
ctx - > ctx = _ctx ;
ctx - > onefs_ops = & onefs_dispatch_ops ;
/* Register an kernel event channel for oplocks */
ctx - > onefs_event_fd = oplocks_event_register ( ) ;
if ( ctx - > onefs_event_fd = = - 1 ) {
DEBUG ( 0 , ( " oplocks_event_register failed: %s \n " ,
strerror ( errno ) ) ) ;
goto err_out ;
}
DEBUG ( 10 , ( " oplock event_fd = %d \n " , ctx - > onefs_event_fd ) ) ;
/* Register the oplock event_fd with samba's event system */
ctx - > read_fde = event_add_fd ( smbd_event_context ( ) ,
ctx ,
ctx - > onefs_event_fd ,
EVENT_FD_READ ,
onefs_oplocks_read_fde_handler ,
ctx ) ;
return _ctx ;
err_out :
talloc_free ( _ctx ) ;
return NULL ;
}
# else
void oplock_onefs_dummy ( void ) ;
void oplock_onefs_dummy ( void ) { }
# endif /* HAVE_ONEFS */