2007-11-28 14:38:40 -08:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* stack_user . c
*
* Code which interfaces ocfs2 with fs / dlm and a userspace stack .
*
* 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 , version 2.
*
* 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 .
*/
# include <linux/module.h>
2008-02-18 19:23:28 -08:00
# include <linux/fs.h>
# include <linux/miscdevice.h>
# include <linux/mutex.h>
# include <linux/reboot.h>
2008-02-18 19:40:12 -08:00
# include <asm/uaccess.h>
2007-11-28 14:38:40 -08:00
2008-02-20 14:29:27 -08:00
# include "ocfs2.h" /* For struct ocfs2_lock_res */
2007-11-28 14:38:40 -08:00
# include "stackglue.h"
2008-02-18 19:23:28 -08:00
/*
* The control protocol starts with a handshake . Until the handshake
* is complete , the control device will fail all write ( 2 ) s .
*
* The handshake is simple . First , the client reads until EOF . Each line
* of output is a supported protocol tag . All protocol tags are a single
* character followed by a two hex digit version number . Currently the
* only things supported is T01 , for " Text-base version 0x01 " . Next , the
2008-02-18 17:07:09 -08:00
* client writes the version they would like to use , including the newline .
* Thus , the protocol tag is ' T01 \ n ' . If the version tag written is
* unknown , - EINVAL is returned . Once the negotiation is complete , the
* client can start sending messages .
*
2008-02-20 15:39:44 -08:00
* The T01 protocol has three messages . First is the " SETN " message .
2008-02-20 14:44:34 -08:00
* It has the following syntax :
*
* SETN < space > < 8 - char - hex - nodenum > < newline >
*
* This is 14 characters .
*
* The " SETN " message must be the first message following the protocol .
* It tells ocfs2_control the local node number .
*
2008-02-20 15:39:44 -08:00
* Next comes the " SETV " message . It has the following syntax :
*
* SETV < space > < 2 - char - hex - major > < space > < 2 - char - hex - minor > < newline >
*
* This is 11 characters .
*
* The " SETV " message sets the filesystem locking protocol version as
* negotiated by the client . The client negotiates based on the maximum
* version advertised in / sys / fs / ocfs2 / max_locking_protocol . The major
* number from the " SETV " message must match
* user_stack . sp_proto - > lp_max_version . pv_major , and the minor number
* must be less than or equal to . . . - > lp_max_version . pv_minor .
*
* Once this information has been set , mounts will be allowed . From this
* point on , the " DOWN " message can be sent for node down notification .
* It has the following syntax :
2008-02-18 17:07:09 -08:00
*
* DOWN < space > < 32 - char - cap - hex - uuid > < space > < 8 - char - hex - nodenum > < newline >
*
* eg :
*
* DOWN 632 A924FDD844190BDA93C0DF6B94899 00000001 \ n
*
* This is 47 characters .
2008-02-18 19:23:28 -08:00
*/
2008-02-18 19:40:12 -08:00
/*
* Whether or not the client has done the handshake .
* For now , we have just one protocol version .
*/
# define OCFS2_CONTROL_PROTO "T01\n"
# define OCFS2_CONTROL_PROTO_LEN 4
2008-02-20 14:44:34 -08:00
/* Handshake states */
2008-02-18 19:40:12 -08:00
# define OCFS2_CONTROL_HANDSHAKE_INVALID (0)
# define OCFS2_CONTROL_HANDSHAKE_READ (1)
2008-02-20 14:44:34 -08:00
# define OCFS2_CONTROL_HANDSHAKE_PROTOCOL (2)
# define OCFS2_CONTROL_HANDSHAKE_VALID (3)
/* Messages */
# define OCFS2_CONTROL_MESSAGE_OP_LEN 4
# define OCFS2_CONTROL_MESSAGE_SETNODE_OP "SETN"
# define OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN 14
2008-02-20 15:39:44 -08:00
# define OCFS2_CONTROL_MESSAGE_SETVERSION_OP "SETV"
# define OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN 11
2008-02-20 14:44:34 -08:00
# define OCFS2_CONTROL_MESSAGE_DOWN_OP "DOWN"
2008-02-18 17:07:09 -08:00
# define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN 47
# define OCFS2_TEXT_UUID_LEN 32
2008-02-20 15:39:44 -08:00
# define OCFS2_CONTROL_MESSAGE_VERNUM_LEN 2
2008-02-18 17:07:09 -08:00
# define OCFS2_CONTROL_MESSAGE_NODENUM_LEN 8
2008-02-18 19:40:12 -08:00
2008-02-18 19:23:28 -08:00
/*
* ocfs2_live_connection is refcounted because the filesystem and
* miscdevice sides can detach in different order . Let ' s just be safe .
*/
struct ocfs2_live_connection {
struct list_head oc_list ;
struct ocfs2_cluster_connection * oc_conn ;
} ;
2008-02-18 19:40:12 -08:00
struct ocfs2_control_private {
struct list_head op_list ;
int op_state ;
2008-02-20 14:44:34 -08:00
int op_this_node ;
2008-02-20 15:39:44 -08:00
struct ocfs2_protocol_version op_proto ;
2008-02-20 14:44:34 -08:00
} ;
/* SETN<space><8-char-hex-nodenum><newline> */
struct ocfs2_control_message_setn {
char tag [ OCFS2_CONTROL_MESSAGE_OP_LEN ] ;
char space ;
char nodestr [ OCFS2_CONTROL_MESSAGE_NODENUM_LEN ] ;
char newline ;
} ;
2008-02-20 15:39:44 -08:00
/* SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> */
struct ocfs2_control_message_setv {
char tag [ OCFS2_CONTROL_MESSAGE_OP_LEN ] ;
char space1 ;
char major [ OCFS2_CONTROL_MESSAGE_VERNUM_LEN ] ;
char space2 ;
char minor [ OCFS2_CONTROL_MESSAGE_VERNUM_LEN ] ;
char newline ;
} ;
2008-02-20 14:44:34 -08:00
/* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */
struct ocfs2_control_message_down {
char tag [ OCFS2_CONTROL_MESSAGE_OP_LEN ] ;
char space1 ;
char uuid [ OCFS2_TEXT_UUID_LEN ] ;
char space2 ;
char nodestr [ OCFS2_CONTROL_MESSAGE_NODENUM_LEN ] ;
char newline ;
} ;
union ocfs2_control_message {
char tag [ OCFS2_CONTROL_MESSAGE_OP_LEN ] ;
struct ocfs2_control_message_setn u_setn ;
2008-02-20 15:39:44 -08:00
struct ocfs2_control_message_setv u_setv ;
2008-02-20 14:44:34 -08:00
struct ocfs2_control_message_down u_down ;
2008-02-18 19:40:12 -08:00
} ;
2008-02-20 14:29:27 -08:00
static struct ocfs2_stack_plugin user_stack ;
2008-02-18 19:23:28 -08:00
static atomic_t ocfs2_control_opened ;
2008-02-20 14:44:34 -08:00
static int ocfs2_control_this_node = - 1 ;
2008-02-20 15:39:44 -08:00
static struct ocfs2_protocol_version running_proto ;
2008-02-18 19:23:28 -08:00
static LIST_HEAD ( ocfs2_live_connection_list ) ;
2008-02-18 19:40:12 -08:00
static LIST_HEAD ( ocfs2_control_private_list ) ;
2008-02-18 19:23:28 -08:00
static DEFINE_MUTEX ( ocfs2_control_lock ) ;
2008-02-18 19:40:12 -08:00
static inline void ocfs2_control_set_handshake_state ( struct file * file ,
int state )
{
struct ocfs2_control_private * p = file - > private_data ;
p - > op_state = state ;
}
static inline int ocfs2_control_get_handshake_state ( struct file * file )
{
struct ocfs2_control_private * p = file - > private_data ;
return p - > op_state ;
}
2008-02-18 19:23:28 -08:00
static struct ocfs2_live_connection * ocfs2_connection_find ( const char * name )
{
size_t len = strlen ( name ) ;
struct ocfs2_live_connection * c ;
BUG_ON ( ! mutex_is_locked ( & ocfs2_control_lock ) ) ;
list_for_each_entry ( c , & ocfs2_live_connection_list , oc_list ) {
if ( ( c - > oc_conn - > cc_namelen = = len ) & &
! strncmp ( c - > oc_conn - > cc_name , name , len ) )
return c ;
}
return c ;
}
/*
* ocfs2_live_connection structures are created underneath the ocfs2
* mount path . Since the VFS prevents multiple calls to
* fill_super ( ) , we can ' t get dupes here .
*/
static int ocfs2_live_connection_new ( struct ocfs2_cluster_connection * conn ,
struct ocfs2_live_connection * * c_ret )
{
int rc = 0 ;
struct ocfs2_live_connection * c ;
c = kzalloc ( sizeof ( struct ocfs2_live_connection ) , GFP_KERNEL ) ;
if ( ! c )
return - ENOMEM ;
mutex_lock ( & ocfs2_control_lock ) ;
c - > oc_conn = conn ;
if ( atomic_read ( & ocfs2_control_opened ) )
list_add ( & c - > oc_list , & ocfs2_live_connection_list ) ;
else {
printk ( KERN_ERR
" ocfs2: Userspace control daemon is not present \n " ) ;
rc = - ESRCH ;
}
mutex_unlock ( & ocfs2_control_lock ) ;
if ( ! rc )
* c_ret = c ;
else
kfree ( c ) ;
return rc ;
}
/*
* This function disconnects the cluster connection from ocfs2_control .
* Afterwards , userspace can ' t affect the cluster connection .
*/
static void ocfs2_live_connection_drop ( struct ocfs2_live_connection * c )
{
mutex_lock ( & ocfs2_control_lock ) ;
list_del_init ( & c - > oc_list ) ;
c - > oc_conn = NULL ;
mutex_unlock ( & ocfs2_control_lock ) ;
kfree ( c ) ;
}
2008-02-20 14:44:34 -08:00
static int ocfs2_control_cfu ( void * target , size_t target_len ,
const char __user * buf , size_t count )
2008-02-18 19:40:12 -08:00
{
/* The T01 expects write(2) calls to have exactly one command */
2008-02-20 14:44:34 -08:00
if ( ( count ! = target_len ) | |
( count > sizeof ( union ocfs2_control_message ) ) )
2008-02-18 19:40:12 -08:00
return - EINVAL ;
if ( copy_from_user ( target , buf , target_len ) )
return - EFAULT ;
2008-02-20 14:44:34 -08:00
return 0 ;
2008-02-18 19:40:12 -08:00
}
2008-02-20 14:44:34 -08:00
static ssize_t ocfs2_control_validate_protocol ( struct file * file ,
const char __user * buf ,
size_t count )
2008-02-18 19:40:12 -08:00
{
ssize_t ret ;
char kbuf [ OCFS2_CONTROL_PROTO_LEN ] ;
ret = ocfs2_control_cfu ( kbuf , OCFS2_CONTROL_PROTO_LEN ,
buf , count ) ;
2008-02-20 14:44:34 -08:00
if ( ret )
2008-02-18 19:40:12 -08:00
return ret ;
if ( strncmp ( kbuf , OCFS2_CONTROL_PROTO , OCFS2_CONTROL_PROTO_LEN ) )
return - EINVAL ;
ocfs2_control_set_handshake_state ( file ,
2008-02-20 14:44:34 -08:00
OCFS2_CONTROL_HANDSHAKE_PROTOCOL ) ;
2008-02-18 19:40:12 -08:00
return count ;
}
2008-02-18 17:07:09 -08:00
static void ocfs2_control_send_down ( const char * uuid ,
int nodenum )
{
struct ocfs2_live_connection * c ;
mutex_lock ( & ocfs2_control_lock ) ;
c = ocfs2_connection_find ( uuid ) ;
if ( c ) {
BUG_ON ( c - > oc_conn = = NULL ) ;
c - > oc_conn - > cc_recovery_handler ( nodenum ,
c - > oc_conn - > cc_recovery_data ) ;
}
mutex_unlock ( & ocfs2_control_lock ) ;
}
2008-02-20 14:44:34 -08:00
/*
* Called whenever configuration elements are sent to / dev / ocfs2_control .
* If all configuration elements are present , try to set the global
2008-02-20 15:39:44 -08:00
* values . If there is a problem , return an error . Skip any missing
* elements , and only bump ocfs2_control_opened when we have all elements
* and are successful .
2008-02-20 14:44:34 -08:00
*/
static int ocfs2_control_install_private ( struct file * file )
{
int rc = 0 ;
int set_p = 1 ;
struct ocfs2_control_private * p = file - > private_data ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
BUG_ON ( p - > op_state ! = OCFS2_CONTROL_HANDSHAKE_PROTOCOL ) ;
2008-02-20 15:39:44 -08:00
mutex_lock ( & ocfs2_control_lock ) ;
if ( p - > op_this_node < 0 ) {
2008-02-20 14:44:34 -08:00
set_p = 0 ;
2008-02-20 15:39:44 -08:00
} else if ( ( ocfs2_control_this_node > = 0 ) & &
( ocfs2_control_this_node ! = p - > op_this_node ) ) {
rc = - EINVAL ;
goto out_unlock ;
}
2008-02-20 14:44:34 -08:00
2008-02-20 15:39:44 -08:00
if ( ! p - > op_proto . pv_major ) {
set_p = 0 ;
} else if ( ! list_empty ( & ocfs2_live_connection_list ) & &
( ( running_proto . pv_major ! = p - > op_proto . pv_major ) | |
( running_proto . pv_minor ! = p - > op_proto . pv_minor ) ) ) {
2008-02-20 14:44:34 -08:00
rc = - EINVAL ;
2008-02-20 15:39:44 -08:00
goto out_unlock ;
}
if ( set_p ) {
ocfs2_control_this_node = p - > op_this_node ;
running_proto . pv_major = p - > op_proto . pv_major ;
running_proto . pv_minor = p - > op_proto . pv_minor ;
}
out_unlock :
2008-02-20 14:44:34 -08:00
mutex_unlock ( & ocfs2_control_lock ) ;
if ( ! rc & & set_p ) {
/* We set the global values successfully */
atomic_inc ( & ocfs2_control_opened ) ;
ocfs2_control_set_handshake_state ( file ,
OCFS2_CONTROL_HANDSHAKE_VALID ) ;
}
return rc ;
}
2008-02-20 14:29:27 -08:00
static int ocfs2_control_get_this_node ( void )
{
int rc ;
mutex_lock ( & ocfs2_control_lock ) ;
if ( ocfs2_control_this_node < 0 )
rc = - EINVAL ;
else
rc = ocfs2_control_this_node ;
mutex_unlock ( & ocfs2_control_lock ) ;
return rc ;
}
2008-02-20 14:44:34 -08:00
static int ocfs2_control_do_setnode_msg ( struct file * file ,
struct ocfs2_control_message_setn * msg )
2008-02-18 17:07:09 -08:00
{
long nodenum ;
2008-02-20 14:44:34 -08:00
char * ptr = NULL ;
struct ocfs2_control_private * p = file - > private_data ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
if ( ocfs2_control_get_handshake_state ( file ) ! =
OCFS2_CONTROL_HANDSHAKE_PROTOCOL )
return - EINVAL ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
if ( strncmp ( msg - > tag , OCFS2_CONTROL_MESSAGE_SETNODE_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
return - EINVAL ;
if ( ( msg - > space ! = ' ' ) | | ( msg - > newline ! = ' \n ' ) )
return - EINVAL ;
msg - > space = msg - > newline = ' \0 ' ;
nodenum = simple_strtol ( msg - > nodestr , & ptr , 16 ) ;
if ( ! ptr | | * ptr )
return - EINVAL ;
if ( ( nodenum = = LONG_MIN ) | | ( nodenum = = LONG_MAX ) | |
( nodenum > INT_MAX ) | | ( nodenum < 0 ) )
return - ERANGE ;
p - > op_this_node = nodenum ;
return ocfs2_control_install_private ( file ) ;
}
2008-02-20 15:39:44 -08:00
static int ocfs2_control_do_setversion_msg ( struct file * file ,
struct ocfs2_control_message_setv * msg )
{
long major , minor ;
char * ptr = NULL ;
struct ocfs2_control_private * p = file - > private_data ;
struct ocfs2_protocol_version * max =
& user_stack . sp_proto - > lp_max_version ;
if ( ocfs2_control_get_handshake_state ( file ) ! =
OCFS2_CONTROL_HANDSHAKE_PROTOCOL )
return - EINVAL ;
if ( strncmp ( msg - > tag , OCFS2_CONTROL_MESSAGE_SETVERSION_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
return - EINVAL ;
if ( ( msg - > space1 ! = ' ' ) | | ( msg - > space2 ! = ' ' ) | |
( msg - > newline ! = ' \n ' ) )
return - EINVAL ;
msg - > space1 = msg - > space2 = msg - > newline = ' \0 ' ;
major = simple_strtol ( msg - > major , & ptr , 16 ) ;
if ( ! ptr | | * ptr )
return - EINVAL ;
minor = simple_strtol ( msg - > minor , & ptr , 16 ) ;
if ( ! ptr | | * ptr )
return - EINVAL ;
/*
* The major must be between 1 and 255 , inclusive . The minor
* must be between 0 and 255 , inclusive . The version passed in
* must be within the maximum version supported by the filesystem .
*/
if ( ( major = = LONG_MIN ) | | ( major = = LONG_MAX ) | |
( major > ( u8 ) - 1 ) | | ( major < 1 ) )
return - ERANGE ;
if ( ( minor = = LONG_MIN ) | | ( minor = = LONG_MAX ) | |
( minor > ( u8 ) - 1 ) | | ( minor < 0 ) )
return - ERANGE ;
if ( ( major ! = max - > pv_major ) | |
( minor > max - > pv_minor ) )
return - EINVAL ;
p - > op_proto . pv_major = major ;
p - > op_proto . pv_minor = minor ;
return ocfs2_control_install_private ( file ) ;
}
2008-02-20 14:44:34 -08:00
static int ocfs2_control_do_down_msg ( struct file * file ,
struct ocfs2_control_message_down * msg )
{
long nodenum ;
char * p = NULL ;
if ( ocfs2_control_get_handshake_state ( file ) ! =
OCFS2_CONTROL_HANDSHAKE_VALID )
return - EINVAL ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
if ( strncmp ( msg - > tag , OCFS2_CONTROL_MESSAGE_DOWN_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
2008-02-18 17:07:09 -08:00
return - EINVAL ;
2008-02-20 14:44:34 -08:00
if ( ( msg - > space1 ! = ' ' ) | | ( msg - > space2 ! = ' ' ) | |
( msg - > newline ! = ' \n ' ) )
2008-02-18 17:07:09 -08:00
return - EINVAL ;
2008-02-20 14:44:34 -08:00
msg - > space1 = msg - > space2 = msg - > newline = ' \0 ' ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
nodenum = simple_strtol ( msg - > nodestr , & p , 16 ) ;
2008-02-18 17:07:09 -08:00
if ( ! p | | * p )
return - EINVAL ;
if ( ( nodenum = = LONG_MIN ) | | ( nodenum = = LONG_MAX ) | |
( nodenum > INT_MAX ) | | ( nodenum < 0 ) )
return - ERANGE ;
2008-02-20 14:44:34 -08:00
ocfs2_control_send_down ( msg - > uuid , nodenum ) ;
2008-02-18 17:07:09 -08:00
2008-02-20 14:44:34 -08:00
return 0 ;
}
static ssize_t ocfs2_control_message ( struct file * file ,
const char __user * buf ,
size_t count )
{
ssize_t ret ;
union ocfs2_control_message msg ;
/* Try to catch padding issues */
WARN_ON ( offsetof ( struct ocfs2_control_message_down , uuid ) ! =
( sizeof ( msg . u_down . tag ) + sizeof ( msg . u_down . space1 ) ) ) ;
memset ( & msg , 0 , sizeof ( union ocfs2_control_message ) ) ;
ret = ocfs2_control_cfu ( & msg , count , buf , count ) ;
if ( ret )
goto out ;
if ( ( count = = OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN ) & &
! strncmp ( msg . tag , OCFS2_CONTROL_MESSAGE_SETNODE_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
ret = ocfs2_control_do_setnode_msg ( file , & msg . u_setn ) ;
2008-02-20 15:39:44 -08:00
else if ( ( count = = OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN ) & &
! strncmp ( msg . tag , OCFS2_CONTROL_MESSAGE_SETVERSION_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
ret = ocfs2_control_do_setversion_msg ( file , & msg . u_setv ) ;
2008-02-20 14:44:34 -08:00
else if ( ( count = = OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN ) & &
! strncmp ( msg . tag , OCFS2_CONTROL_MESSAGE_DOWN_OP ,
OCFS2_CONTROL_MESSAGE_OP_LEN ) )
ret = ocfs2_control_do_down_msg ( file , & msg . u_down ) ;
else
ret = - EINVAL ;
out :
return ret ? ret : count ;
2008-02-18 17:07:09 -08:00
}
2008-02-18 19:23:28 -08:00
static ssize_t ocfs2_control_write ( struct file * file ,
const char __user * buf ,
size_t count ,
loff_t * ppos )
2007-11-28 14:38:40 -08:00
{
2008-02-18 19:40:12 -08:00
ssize_t ret ;
switch ( ocfs2_control_get_handshake_state ( file ) ) {
case OCFS2_CONTROL_HANDSHAKE_INVALID :
ret = - EINVAL ;
break ;
case OCFS2_CONTROL_HANDSHAKE_READ :
2008-02-20 14:44:34 -08:00
ret = ocfs2_control_validate_protocol ( file , buf ,
count ) ;
2008-02-18 19:40:12 -08:00
break ;
2008-02-20 14:44:34 -08:00
case OCFS2_CONTROL_HANDSHAKE_PROTOCOL :
2008-02-18 19:40:12 -08:00
case OCFS2_CONTROL_HANDSHAKE_VALID :
2008-02-18 17:07:09 -08:00
ret = ocfs2_control_message ( file , buf , count ) ;
2008-02-18 19:40:12 -08:00
break ;
default :
BUG ( ) ;
ret = - EIO ;
break ;
}
return ret ;
2007-11-28 14:38:40 -08:00
}
2008-02-18 19:40:12 -08:00
/*
* This is a naive version . If we ever have a new protocol , we ' ll expand
* it . Probably using seq_file .
*/
2008-02-18 19:23:28 -08:00
static ssize_t ocfs2_control_read ( struct file * file ,
char __user * buf ,
size_t count ,
loff_t * ppos )
{
2008-02-18 19:40:12 -08:00
char * proto_string = OCFS2_CONTROL_PROTO ;
size_t to_write = 0 ;
if ( * ppos > = OCFS2_CONTROL_PROTO_LEN )
return 0 ;
to_write = OCFS2_CONTROL_PROTO_LEN - * ppos ;
if ( to_write > count )
to_write = count ;
if ( copy_to_user ( buf , proto_string + * ppos , to_write ) )
return - EFAULT ;
* ppos + = to_write ;
/* Have we read the whole protocol list? */
if ( * ppos > = OCFS2_CONTROL_PROTO_LEN )
ocfs2_control_set_handshake_state ( file ,
OCFS2_CONTROL_HANDSHAKE_READ ) ;
return to_write ;
2008-02-18 19:23:28 -08:00
}
static int ocfs2_control_release ( struct inode * inode , struct file * file )
{
2008-02-18 19:40:12 -08:00
struct ocfs2_control_private * p = file - > private_data ;
mutex_lock ( & ocfs2_control_lock ) ;
if ( ocfs2_control_get_handshake_state ( file ) ! =
OCFS2_CONTROL_HANDSHAKE_VALID )
goto out ;
2008-02-18 19:23:28 -08:00
if ( atomic_dec_and_test ( & ocfs2_control_opened ) ) {
if ( ! list_empty ( & ocfs2_live_connection_list ) ) {
/* XXX: Do bad things! */
printk ( KERN_ERR
" ocfs2: Unexpected release of ocfs2_control! \n "
" Loss of cluster connection requires "
" an emergency restart! \n " ) ;
emergency_restart ( ) ;
}
2008-02-20 15:39:44 -08:00
/*
* Last valid close clears the node number and resets
* the locking protocol version
*/
2008-02-20 14:44:34 -08:00
ocfs2_control_this_node = - 1 ;
2008-02-20 15:39:44 -08:00
running_proto . pv_major = 0 ;
running_proto . pv_major = 0 ;
2008-02-18 19:23:28 -08:00
}
2008-02-18 19:40:12 -08:00
out :
list_del_init ( & p - > op_list ) ;
file - > private_data = NULL ;
mutex_unlock ( & ocfs2_control_lock ) ;
kfree ( p ) ;
2008-02-18 19:23:28 -08:00
return 0 ;
}
static int ocfs2_control_open ( struct inode * inode , struct file * file )
{
2008-02-18 19:40:12 -08:00
struct ocfs2_control_private * p ;
p = kzalloc ( sizeof ( struct ocfs2_control_private ) , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
2008-02-20 14:44:34 -08:00
p - > op_this_node = - 1 ;
2008-02-18 19:40:12 -08:00
mutex_lock ( & ocfs2_control_lock ) ;
file - > private_data = p ;
list_add ( & p - > op_list , & ocfs2_control_private_list ) ;
mutex_unlock ( & ocfs2_control_lock ) ;
2008-02-18 19:23:28 -08:00
return 0 ;
}
static const struct file_operations ocfs2_control_fops = {
. open = ocfs2_control_open ,
. release = ocfs2_control_release ,
. read = ocfs2_control_read ,
. write = ocfs2_control_write ,
. owner = THIS_MODULE ,
} ;
2008-04-21 11:49:26 +03:00
static struct miscdevice ocfs2_control_device = {
2008-02-18 19:23:28 -08:00
. minor = MISC_DYNAMIC_MINOR ,
. name = " ocfs2_control " ,
. fops = & ocfs2_control_fops ,
} ;
static int ocfs2_control_init ( void )
{
int rc ;
atomic_set ( & ocfs2_control_opened , 0 ) ;
rc = misc_register ( & ocfs2_control_device ) ;
if ( rc )
printk ( KERN_ERR
" ocfs2: Unable to register ocfs2_control device "
" (errno %d) \n " ,
- rc ) ;
return rc ;
}
static void ocfs2_control_exit ( void )
{
int rc ;
rc = misc_deregister ( & ocfs2_control_device ) ;
if ( rc )
printk ( KERN_ERR
" ocfs2: Unable to deregister ocfs2_control device "
" (errno %d) \n " ,
- rc ) ;
}
2008-02-20 14:29:27 -08:00
static struct dlm_lksb * fsdlm_astarg_to_lksb ( void * astarg )
{
struct ocfs2_lock_res * res = astarg ;
return & res - > l_lksb . lksb_fsdlm ;
}
static void fsdlm_lock_ast_wrapper ( void * astarg )
{
struct dlm_lksb * lksb = fsdlm_astarg_to_lksb ( astarg ) ;
int status = lksb - > sb_status ;
BUG_ON ( user_stack . sp_proto = = NULL ) ;
/*
* For now we ' re punting on the issue of other non - standard errors
* where we can ' t tell if the unlock_ast or lock_ast should be called .
* The main " other error " that ' s possible is EINVAL which means the
* function was called with invalid args , which shouldn ' t be possible
* since the caller here is under our control . Other non - standard
* errors probably fall into the same category , or otherwise are fatal
* which means we can ' t carry on anyway .
*/
if ( status = = - DLM_EUNLOCK | | status = = - DLM_ECANCEL )
user_stack . sp_proto - > lp_unlock_ast ( astarg , 0 ) ;
else
user_stack . sp_proto - > lp_lock_ast ( astarg ) ;
}
static void fsdlm_blocking_ast_wrapper ( void * astarg , int level )
{
BUG_ON ( user_stack . sp_proto = = NULL ) ;
user_stack . sp_proto - > lp_blocking_ast ( astarg , level ) ;
}
static int user_dlm_lock ( struct ocfs2_cluster_connection * conn ,
int mode ,
union ocfs2_dlm_lksb * lksb ,
u32 flags ,
void * name ,
unsigned int namelen ,
void * astarg )
{
int ret ;
if ( ! lksb - > lksb_fsdlm . sb_lvbptr )
lksb - > lksb_fsdlm . sb_lvbptr = ( char * ) lksb +
sizeof ( struct dlm_lksb ) ;
ret = dlm_lock ( conn - > cc_lockspace , mode , & lksb - > lksb_fsdlm ,
flags | DLM_LKF_NODLCKWT , name , namelen , 0 ,
fsdlm_lock_ast_wrapper , astarg ,
fsdlm_blocking_ast_wrapper ) ;
return ret ;
}
static int user_dlm_unlock ( struct ocfs2_cluster_connection * conn ,
union ocfs2_dlm_lksb * lksb ,
u32 flags ,
void * astarg )
{
int ret ;
ret = dlm_unlock ( conn - > cc_lockspace , lksb - > lksb_fsdlm . sb_lkid ,
flags , & lksb - > lksb_fsdlm , astarg ) ;
return ret ;
}
static int user_dlm_lock_status ( union ocfs2_dlm_lksb * lksb )
{
return lksb - > lksb_fsdlm . sb_status ;
}
static void * user_dlm_lvb ( union ocfs2_dlm_lksb * lksb )
{
return ( void * ) ( lksb - > lksb_fsdlm . sb_lvbptr ) ;
}
static void user_dlm_dump_lksb ( union ocfs2_dlm_lksb * lksb )
{
}
/*
* Compare a requested locking protocol version against the current one .
*
* If the major numbers are different , they are incompatible .
* If the current minor is greater than the request , they are incompatible .
* If the current minor is less than or equal to the request , they are
* compatible , and the requester should run at the current minor version .
*/
static int fs_protocol_compare ( struct ocfs2_protocol_version * existing ,
struct ocfs2_protocol_version * request )
{
if ( existing - > pv_major ! = request - > pv_major )
return 1 ;
if ( existing - > pv_minor > request - > pv_minor )
return 1 ;
if ( existing - > pv_minor < request - > pv_minor )
request - > pv_minor = existing - > pv_minor ;
return 0 ;
}
static int user_cluster_connect ( struct ocfs2_cluster_connection * conn )
{
dlm_lockspace_t * fsdlm ;
struct ocfs2_live_connection * control ;
int rc = 0 ;
BUG_ON ( conn = = NULL ) ;
rc = ocfs2_live_connection_new ( conn , & control ) ;
if ( rc )
goto out ;
/*
* running_proto must have been set before we allowed any mounts
* to proceed .
*/
if ( fs_protocol_compare ( & running_proto , & conn - > cc_version ) ) {
printk ( KERN_ERR
" Unable to mount with fs locking protocol version "
" %u.%u because the userspace control daemon has "
" negotiated %u.%u \n " ,
conn - > cc_version . pv_major , conn - > cc_version . pv_minor ,
running_proto . pv_major , running_proto . pv_minor ) ;
rc = - EPROTO ;
ocfs2_live_connection_drop ( control ) ;
goto out ;
}
rc = dlm_new_lockspace ( conn - > cc_name , strlen ( conn - > cc_name ) ,
& fsdlm , DLM_LSFL_FS , DLM_LVB_LEN ) ;
if ( rc ) {
ocfs2_live_connection_drop ( control ) ;
goto out ;
}
conn - > cc_private = control ;
conn - > cc_lockspace = fsdlm ;
out :
return rc ;
}
static int user_cluster_disconnect ( struct ocfs2_cluster_connection * conn ,
int hangup_pending )
{
dlm_release_lockspace ( conn - > cc_lockspace , 2 ) ;
conn - > cc_lockspace = NULL ;
ocfs2_live_connection_drop ( conn - > cc_private ) ;
conn - > cc_private = NULL ;
return 0 ;
}
static int user_cluster_this_node ( unsigned int * this_node )
{
int rc ;
rc = ocfs2_control_get_this_node ( ) ;
if ( rc < 0 )
return rc ;
* this_node = rc ;
return 0 ;
}
static struct ocfs2_stack_operations user_stack_ops = {
. connect = user_cluster_connect ,
. disconnect = user_cluster_disconnect ,
. this_node = user_cluster_this_node ,
. dlm_lock = user_dlm_lock ,
. dlm_unlock = user_dlm_unlock ,
. lock_status = user_dlm_lock_status ,
. lock_lvb = user_dlm_lvb ,
. dump_lksb = user_dlm_dump_lksb ,
} ;
static struct ocfs2_stack_plugin user_stack = {
. sp_name = " user " ,
. sp_ops = & user_stack_ops ,
. sp_owner = THIS_MODULE ,
} ;
2008-02-18 19:23:28 -08:00
static int __init user_stack_init ( void )
{
2008-02-20 14:29:27 -08:00
int rc ;
rc = ocfs2_control_init ( ) ;
if ( ! rc ) {
rc = ocfs2_stack_glue_register ( & user_stack ) ;
if ( rc )
ocfs2_control_exit ( ) ;
}
return rc ;
2008-02-18 19:23:28 -08:00
}
2007-11-28 14:38:40 -08:00
static void __exit user_stack_exit ( void )
{
2008-02-20 14:29:27 -08:00
ocfs2_stack_glue_unregister ( & user_stack ) ;
2008-02-18 19:23:28 -08:00
ocfs2_control_exit ( ) ;
2007-11-28 14:38:40 -08:00
}
MODULE_AUTHOR ( " Oracle " ) ;
MODULE_DESCRIPTION ( " ocfs2 driver for userspace cluster stacks " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( user_stack_init ) ;
module_exit ( user_stack_exit ) ;