2008-12-25 15:38:57 +03:00
/*
2009-01-09 14:14:59 +03:00
* hvc_iucv . c - z / VM IUCV hypervisor console ( HVC ) device driver
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:59 +03:00
* This HVC device driver provides terminal access using
2008-12-25 15:38:57 +03:00
* z / VM IUCV communication paths .
*
2009-01-09 14:14:59 +03:00
* Copyright IBM Corp . 2008
2008-12-25 15:38:57 +03:00
*
* Author ( s ) : Hendrik Brueckner < brueckner @ linux . vnet . ibm . com >
*/
# define KMSG_COMPONENT "hvc_iucv"
2009-01-09 14:14:59 +03:00
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2008-12-25 15:38:57 +03:00
# include <linux/types.h>
# include <asm/ebcdic.h>
2009-03-26 17:23:55 +03:00
# include <linux/ctype.h>
2009-01-09 14:14:58 +03:00
# include <linux/delay.h>
2009-01-09 14:15:00 +03:00
# include <linux/init.h>
2008-12-25 15:38:57 +03:00
# include <linux/mempool.h>
2009-03-26 17:23:55 +03:00
# include <linux/moduleparam.h>
2008-12-25 15:38:57 +03:00
# include <linux/tty.h>
2009-01-09 14:14:58 +03:00
# include <linux/wait.h>
2008-12-25 15:38:57 +03:00
# include <net/iucv/iucv.h>
# include "hvc_console.h"
2009-01-09 14:14:59 +03:00
/* General device driver settings */
2008-12-25 15:38:57 +03:00
# define HVC_IUCV_MAGIC 0xc9e4c3e5
# define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS
# define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer) / 4)
/* IUCV TTY message */
# define MSG_VERSION 0x02 /* Message version */
# define MSG_TYPE_ERROR 0x01 /* Error message */
# define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */
# define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */
# define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */
# define MSG_TYPE_DATA 0x10 /* Terminal data */
struct iucv_tty_msg {
u8 version ; /* Message version */
u8 type ; /* Message type */
2009-01-09 14:14:58 +03:00
# define MSG_MAX_DATALEN ((u16)(~0))
2008-12-25 15:38:57 +03:00
u16 datalen ; /* Payload length */
u8 data [ ] ; /* Payload buffer */
} __attribute__ ( ( packed ) ) ;
2009-01-09 14:14:59 +03:00
# define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data))
2008-12-25 15:38:57 +03:00
enum iucv_state_t {
IUCV_DISCONN = 0 ,
IUCV_CONNECTED = 1 ,
IUCV_SEVERED = 2 ,
} ;
enum tty_state_t {
TTY_CLOSED = 0 ,
TTY_OPENED = 1 ,
} ;
struct hvc_iucv_private {
2009-01-09 14:14:59 +03:00
struct hvc_struct * hvc ; /* HVC struct reference */
2008-12-25 15:38:57 +03:00
u8 srv_name [ 8 ] ; /* IUCV service name (ebcdic) */
2009-01-09 14:15:01 +03:00
unsigned char is_console ; /* Linux console usage flag */
2008-12-25 15:38:57 +03:00
enum iucv_state_t iucv_state ; /* IUCV connection status */
enum tty_state_t tty_state ; /* TTY status */
struct iucv_path * path ; /* IUCV path pointer */
spinlock_t lock ; /* hvc_iucv_private lock */
2009-01-09 14:14:58 +03:00
# define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */
void * sndbuf ; /* send buffer */
size_t sndbuf_len ; /* length of send buffer */
# define QUEUE_SNDBUF_DELAY (HZ / 25)
struct delayed_work sndbuf_work ; /* work: send iucv msg(s) */
wait_queue_head_t sndbuf_waitq ; /* wait for send completion */
2008-12-25 15:38:57 +03:00
struct list_head tty_outqueue ; /* outgoing IUCV messages */
struct list_head tty_inqueue ; /* incoming IUCV messages */
} ;
struct iucv_tty_buffer {
struct list_head list ; /* list pointer */
2009-01-09 14:14:59 +03:00
struct iucv_message msg ; /* store an IUCV message */
2008-12-25 15:38:57 +03:00
size_t offset ; /* data buffer offset */
struct iucv_tty_msg * mbuf ; /* buffer to store input/output data */
} ;
/* IUCV callback handler */
static int hvc_iucv_path_pending ( struct iucv_path * , u8 [ 8 ] , u8 [ 16 ] ) ;
static void hvc_iucv_path_severed ( struct iucv_path * , u8 [ 16 ] ) ;
static void hvc_iucv_msg_pending ( struct iucv_path * , struct iucv_message * ) ;
static void hvc_iucv_msg_complete ( struct iucv_path * , struct iucv_message * ) ;
2009-01-09 14:14:57 +03:00
/* Kernel module parameter: use one terminal device as default */
static unsigned long hvc_iucv_devices = 1 ;
2008-12-25 15:38:57 +03:00
/* Array of allocated hvc iucv tty lines... */
static struct hvc_iucv_private * hvc_iucv_table [ MAX_HVC_IUCV_LINES ] ;
2009-01-09 14:15:01 +03:00
# define IUCV_HVC_CON_IDX (0)
2009-03-26 17:23:55 +03:00
/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */
# define MAX_VMID_FILTER (500)
static size_t hvc_iucv_filter_size ;
static void * hvc_iucv_filter ;
static const char * hvc_iucv_filter_string ;
static DEFINE_RWLOCK ( hvc_iucv_filter_lock ) ;
2008-12-25 15:38:57 +03:00
/* Kmem cache and mempool for iucv_tty_buffer elements */
static struct kmem_cache * hvc_iucv_buffer_cache ;
static mempool_t * hvc_iucv_mempool ;
/* IUCV handler callback functions */
static struct iucv_handler hvc_iucv_handler = {
. path_pending = hvc_iucv_path_pending ,
. path_severed = hvc_iucv_path_severed ,
. message_complete = hvc_iucv_msg_complete ,
. message_pending = hvc_iucv_msg_pending ,
} ;
/**
* hvc_iucv_get_private ( ) - Return a struct hvc_iucv_private instance .
* @ num : The HVC virtual terminal number ( vtermno )
*
* This function returns the struct hvc_iucv_private instance that corresponds
* to the HVC virtual terminal number specified as parameter @ num .
*/
struct hvc_iucv_private * hvc_iucv_get_private ( uint32_t num )
{
if ( ( num < HVC_IUCV_MAGIC ) | | ( num - HVC_IUCV_MAGIC > hvc_iucv_devices ) )
return NULL ;
return hvc_iucv_table [ num - HVC_IUCV_MAGIC ] ;
}
/**
2009-01-09 14:14:59 +03:00
* alloc_tty_buffer ( ) - Return a new struct iucv_tty_buffer element .
2008-12-25 15:38:57 +03:00
* @ size : Size of the internal buffer used to store data .
* @ flags : Memory allocation flags passed to mempool .
*
* This function allocates a new struct iucv_tty_buffer element and , optionally ,
* allocates an internal data buffer with the specified size @ size .
* Note : The total message size arises from the internal buffer size and the
* members of the iucv_tty_msg structure .
* The function returns NULL if memory allocation has failed .
*/
static struct iucv_tty_buffer * alloc_tty_buffer ( size_t size , gfp_t flags )
{
struct iucv_tty_buffer * bufp ;
bufp = mempool_alloc ( hvc_iucv_mempool , flags ) ;
if ( ! bufp )
return NULL ;
2009-01-09 14:15:01 +03:00
memset ( bufp , 0 , sizeof ( * bufp ) ) ;
2008-12-25 15:38:57 +03:00
if ( size > 0 ) {
bufp - > msg . length = MSG_SIZE ( size ) ;
bufp - > mbuf = kmalloc ( bufp - > msg . length , flags ) ;
if ( ! bufp - > mbuf ) {
mempool_free ( bufp , hvc_iucv_mempool ) ;
return NULL ;
}
bufp - > mbuf - > version = MSG_VERSION ;
bufp - > mbuf - > type = MSG_TYPE_DATA ;
bufp - > mbuf - > datalen = ( u16 ) size ;
}
return bufp ;
}
/**
* destroy_tty_buffer ( ) - destroy struct iucv_tty_buffer element .
* @ bufp : Pointer to a struct iucv_tty_buffer element , SHALL NOT be NULL .
*/
static void destroy_tty_buffer ( struct iucv_tty_buffer * bufp )
{
kfree ( bufp - > mbuf ) ;
mempool_free ( bufp , hvc_iucv_mempool ) ;
}
/**
* destroy_tty_buffer_list ( ) - call destroy_tty_buffer ( ) for each list element .
2009-01-09 14:14:59 +03:00
* @ list : List containing struct iucv_tty_buffer elements .
2008-12-25 15:38:57 +03:00
*/
static void destroy_tty_buffer_list ( struct list_head * list )
{
struct iucv_tty_buffer * ent , * next ;
list_for_each_entry_safe ( ent , next , list , list ) {
list_del ( & ent - > list ) ;
destroy_tty_buffer ( ent ) ;
}
}
/**
2009-01-09 14:14:59 +03:00
* hvc_iucv_write ( ) - Receive IUCV message & write data to HVC buffer .
* @ priv : Pointer to struct hvc_iucv_private
* @ buf : HVC buffer for writing received terminal data .
* @ count : HVC buffer size .
2008-12-25 15:38:57 +03:00
* @ has_more_data : Pointer to an int variable .
*
* The function picks up pending messages from the input queue and receives
* the message data that is then written to the specified buffer @ buf .
2009-01-09 14:14:59 +03:00
* If the buffer size @ count is less than the data message size , the
2008-12-25 15:38:57 +03:00
* message is kept on the input queue and @ has_more_data is set to 1.
2009-01-09 14:14:59 +03:00
* If all message data has been written , the message is removed from
2008-12-25 15:38:57 +03:00
* the input queue .
*
* The function returns the number of bytes written to the terminal , zero if
* there are no pending data messages available or if there is no established
* IUCV path .
* If the IUCV path has been severed , then - EPIPE is returned to cause a
2009-01-09 14:14:59 +03:00
* hang up ( that is issued by the HVC layer ) .
2008-12-25 15:38:57 +03:00
*/
static int hvc_iucv_write ( struct hvc_iucv_private * priv ,
char * buf , int count , int * has_more_data )
{
struct iucv_tty_buffer * rb ;
int written ;
int rc ;
2009-01-09 14:14:59 +03:00
/* immediately return if there is no IUCV connection */
2008-12-25 15:38:57 +03:00
if ( priv - > iucv_state = = IUCV_DISCONN )
return 0 ;
2009-01-09 14:14:59 +03:00
/* if the IUCV path has been severed, return -EPIPE to inform the
* HVC layer to hang up the tty device . */
2008-12-25 15:38:57 +03:00
if ( priv - > iucv_state = = IUCV_SEVERED )
return - EPIPE ;
/* check if there are pending messages */
if ( list_empty ( & priv - > tty_inqueue ) )
return 0 ;
2009-01-09 14:14:59 +03:00
/* receive an iucv message and flip data to the tty (ldisc) */
2008-12-25 15:38:57 +03:00
rb = list_first_entry ( & priv - > tty_inqueue , struct iucv_tty_buffer , list ) ;
written = 0 ;
if ( ! rb - > mbuf ) { /* message not yet received ... */
/* allocate mem to store msg data; if no memory is available
* then leave the buffer on the list and re - try later */
rb - > mbuf = kmalloc ( rb - > msg . length , GFP_ATOMIC ) ;
if ( ! rb - > mbuf )
return - ENOMEM ;
rc = __iucv_message_receive ( priv - > path , & rb - > msg , 0 ,
rb - > mbuf , rb - > msg . length , NULL ) ;
switch ( rc ) {
case 0 : /* Successful */
break ;
case 2 : /* No message found */
case 9 : /* Message purged */
break ;
default :
written = - EIO ;
}
/* remove buffer if an error has occured or received data
* is not correct */
if ( rc | | ( rb - > mbuf - > version ! = MSG_VERSION ) | |
( rb - > msg . length ! = MSG_SIZE ( rb - > mbuf - > datalen ) ) )
goto out_remove_buffer ;
}
switch ( rb - > mbuf - > type ) {
case MSG_TYPE_DATA :
written = min_t ( int , rb - > mbuf - > datalen - rb - > offset , count ) ;
memcpy ( buf , rb - > mbuf - > data + rb - > offset , written ) ;
if ( written < ( rb - > mbuf - > datalen - rb - > offset ) ) {
rb - > offset + = written ;
* has_more_data = 1 ;
goto out_written ;
}
break ;
case MSG_TYPE_WINSIZE :
if ( rb - > mbuf - > datalen ! = sizeof ( struct winsize ) )
break ;
2009-01-09 14:14:59 +03:00
hvc_resize ( priv - > hvc , * ( ( struct winsize * ) rb - > mbuf - > data ) ) ;
2008-12-25 15:38:57 +03:00
break ;
case MSG_TYPE_ERROR : /* ignored ... */
case MSG_TYPE_TERMENV : /* ignored ... */
case MSG_TYPE_TERMIOS : /* ignored ... */
break ;
}
out_remove_buffer :
list_del ( & rb - > list ) ;
destroy_tty_buffer ( rb ) ;
* has_more_data = ! list_empty ( & priv - > tty_inqueue ) ;
out_written :
return written ;
}
/**
* hvc_iucv_get_chars ( ) - HVC get_chars operation .
* @ vtermno : HVC virtual terminal number .
* @ buf : Pointer to a buffer to store data
* @ count : Size of buffer available for writing
*
2009-01-09 14:14:59 +03:00
* The HVC thread calls this method to read characters from the back - end .
* If an IUCV communication path has been established , pending IUCV messages
* are received and data is copied into buffer @ buf up to @ count bytes .
2008-12-25 15:38:57 +03:00
*
* Locking : The routine gets called under an irqsave ( ) spinlock ; and
* the routine locks the struct hvc_iucv_private - > lock to call
* helper functions .
*/
static int hvc_iucv_get_chars ( uint32_t vtermno , char * buf , int count )
{
struct hvc_iucv_private * priv = hvc_iucv_get_private ( vtermno ) ;
int written ;
int has_more_data ;
if ( count < = 0 )
return 0 ;
if ( ! priv )
return - ENODEV ;
spin_lock ( & priv - > lock ) ;
has_more_data = 0 ;
written = hvc_iucv_write ( priv , buf , count , & has_more_data ) ;
spin_unlock ( & priv - > lock ) ;
/* if there are still messages on the queue... schedule another run */
if ( has_more_data )
hvc_kick ( ) ;
return written ;
}
/**
2009-01-09 14:14:58 +03:00
* hvc_iucv_queue ( ) - Buffer terminal data for sending .
2008-12-25 15:38:57 +03:00
* @ priv : Pointer to struct hvc_iucv_private instance .
* @ buf : Buffer containing data to send .
2009-01-09 14:14:58 +03:00
* @ count : Size of buffer and amount of data to send .
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:58 +03:00
* The function queues data for sending . To actually send the buffered data ,
2009-01-09 14:14:59 +03:00
* a work queue function is scheduled ( with QUEUE_SNDBUF_DELAY ) .
2009-01-09 14:14:58 +03:00
* The function returns the number of data bytes that has been buffered .
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:58 +03:00
* If the device is not connected , data is ignored and the function returns
* @ count .
* If the buffer is full , the function returns 0.
2009-01-09 14:14:59 +03:00
* If an existing IUCV communicaton path has been severed , - EPIPE is returned
* ( that can be passed to HVC layer to cause a tty hangup ) .
2008-12-25 15:38:57 +03:00
*/
2009-01-09 14:14:58 +03:00
static int hvc_iucv_queue ( struct hvc_iucv_private * priv , const char * buf ,
2009-01-09 14:14:59 +03:00
int count )
2009-01-09 14:14:58 +03:00
{
size_t len ;
if ( priv - > iucv_state = = IUCV_DISCONN )
return count ; /* ignore data */
if ( priv - > iucv_state = = IUCV_SEVERED )
return - EPIPE ;
len = min_t ( size_t , count , SNDBUF_SIZE - priv - > sndbuf_len ) ;
if ( ! len )
return 0 ;
memcpy ( priv - > sndbuf + priv - > sndbuf_len , buf , len ) ;
priv - > sndbuf_len + = len ;
if ( priv - > iucv_state = = IUCV_CONNECTED )
schedule_delayed_work ( & priv - > sndbuf_work , QUEUE_SNDBUF_DELAY ) ;
return len ;
}
/**
* hvc_iucv_send ( ) - Send an IUCV message containing terminal data .
* @ priv : Pointer to struct hvc_iucv_private instance .
*
2009-01-09 14:14:59 +03:00
* If an IUCV communication path has been established , the buffered output data
* is sent via an IUCV message and the number of bytes sent is returned .
* Returns 0 if there is no established IUCV communication path or
* - EPIPE if an existing IUCV communicaton path has been severed .
2009-01-09 14:14:58 +03:00
*/
static int hvc_iucv_send ( struct hvc_iucv_private * priv )
2008-12-25 15:38:57 +03:00
{
struct iucv_tty_buffer * sb ;
2009-01-09 14:14:58 +03:00
int rc , len ;
2008-12-25 15:38:57 +03:00
if ( priv - > iucv_state = = IUCV_SEVERED )
return - EPIPE ;
if ( priv - > iucv_state = = IUCV_DISCONN )
2009-01-09 14:14:58 +03:00
return - EIO ;
2008-12-25 15:38:57 +03:00
2009-01-09 14:14:58 +03:00
if ( ! priv - > sndbuf_len )
return 0 ;
2008-12-25 15:38:57 +03:00
/* allocate internal buffer to store msg data and also compute total
* message length */
2009-01-09 14:14:58 +03:00
sb = alloc_tty_buffer ( priv - > sndbuf_len , GFP_ATOMIC ) ;
2008-12-25 15:38:57 +03:00
if ( ! sb )
return - ENOMEM ;
2009-01-09 14:14:58 +03:00
memcpy ( sb - > mbuf - > data , priv - > sndbuf , priv - > sndbuf_len ) ;
sb - > mbuf - > datalen = ( u16 ) priv - > sndbuf_len ;
sb - > msg . length = MSG_SIZE ( sb - > mbuf - > datalen ) ;
2008-12-25 15:38:57 +03:00
list_add_tail ( & sb - > list , & priv - > tty_outqueue ) ;
rc = __iucv_message_send ( priv - > path , & sb - > msg , 0 , 0 ,
( void * ) sb - > mbuf , sb - > msg . length ) ;
if ( rc ) {
2009-01-09 14:14:58 +03:00
/* drop the message here; however we might want to handle
* 0x03 ( msg limit reached ) by trying again . . . */
2008-12-25 15:38:57 +03:00
list_del ( & sb - > list ) ;
destroy_tty_buffer ( sb ) ;
}
2009-01-09 14:14:58 +03:00
len = priv - > sndbuf_len ;
priv - > sndbuf_len = 0 ;
2008-12-25 15:38:57 +03:00
return len ;
}
2009-01-09 14:14:58 +03:00
/**
* hvc_iucv_sndbuf_work ( ) - Send buffered data over IUCV
* @ work : Work structure .
*
2009-01-09 14:14:59 +03:00
* This work queue function sends buffered output data over IUCV and ,
* if not all buffered data could be sent , reschedules itself .
2009-01-09 14:14:58 +03:00
*/
static void hvc_iucv_sndbuf_work ( struct work_struct * work )
{
struct hvc_iucv_private * priv ;
priv = container_of ( work , struct hvc_iucv_private , sndbuf_work . work ) ;
if ( ! priv )
return ;
spin_lock_bh ( & priv - > lock ) ;
hvc_iucv_send ( priv ) ;
spin_unlock_bh ( & priv - > lock ) ;
}
2008-12-25 15:38:57 +03:00
/**
* hvc_iucv_put_chars ( ) - HVC put_chars operation .
* @ vtermno : HVC virtual terminal number .
* @ buf : Pointer to an buffer to read data from
* @ count : Size of buffer available for reading
*
2009-01-09 14:14:59 +03:00
* The HVC thread calls this method to write characters to the back - end .
* The function calls hvc_iucv_queue ( ) to queue terminal data for sending .
2008-12-25 15:38:57 +03:00
*
* Locking : The method gets called under an irqsave ( ) spinlock ; and
* locks struct hvc_iucv_private - > lock .
*/
static int hvc_iucv_put_chars ( uint32_t vtermno , const char * buf , int count )
{
struct hvc_iucv_private * priv = hvc_iucv_get_private ( vtermno ) ;
2009-01-09 14:14:58 +03:00
int queued ;
2008-12-25 15:38:57 +03:00
if ( count < = 0 )
return 0 ;
if ( ! priv )
return - ENODEV ;
spin_lock ( & priv - > lock ) ;
2009-01-09 14:14:58 +03:00
queued = hvc_iucv_queue ( priv , buf , count ) ;
2008-12-25 15:38:57 +03:00
spin_unlock ( & priv - > lock ) ;
2009-01-09 14:14:58 +03:00
return queued ;
2008-12-25 15:38:57 +03:00
}
/**
* hvc_iucv_notifier_add ( ) - HVC notifier for opening a TTY for the first time .
* @ hp : Pointer to the HVC device ( struct hvc_struct )
* @ id : Additional data ( originally passed to hvc_alloc ) : the index of an struct
* hvc_iucv_private instance .
*
2009-01-09 14:15:01 +03:00
* The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private
2008-12-25 15:38:57 +03:00
* instance that is derived from @ id . Always returns 0.
*
* Locking : struct hvc_iucv_private - > lock , spin_lock_bh
*/
static int hvc_iucv_notifier_add ( struct hvc_struct * hp , int id )
{
struct hvc_iucv_private * priv ;
priv = hvc_iucv_get_private ( id ) ;
if ( ! priv )
return 0 ;
spin_lock_bh ( & priv - > lock ) ;
priv - > tty_state = TTY_OPENED ;
spin_unlock_bh ( & priv - > lock ) ;
return 0 ;
}
/**
2009-01-09 14:14:59 +03:00
* hvc_iucv_cleanup ( ) - Clean up and reset a z / VM IUCV HVC instance .
2008-12-25 15:38:57 +03:00
* @ priv : Pointer to the struct hvc_iucv_private instance .
*/
static void hvc_iucv_cleanup ( struct hvc_iucv_private * priv )
{
destroy_tty_buffer_list ( & priv - > tty_outqueue ) ;
destroy_tty_buffer_list ( & priv - > tty_inqueue ) ;
priv - > tty_state = TTY_CLOSED ;
priv - > iucv_state = IUCV_DISCONN ;
2009-01-09 14:14:58 +03:00
priv - > sndbuf_len = 0 ;
}
/**
* tty_outqueue_empty ( ) - Test if the tty outq is empty
* @ priv : Pointer to struct hvc_iucv_private instance .
*/
static inline int tty_outqueue_empty ( struct hvc_iucv_private * priv )
{
int rc ;
spin_lock_bh ( & priv - > lock ) ;
rc = list_empty ( & priv - > tty_outqueue ) ;
spin_unlock_bh ( & priv - > lock ) ;
return rc ;
}
/**
* flush_sndbuf_sync ( ) - Flush send buffer and wait for completion
* @ priv : Pointer to struct hvc_iucv_private instance .
*
* The routine cancels a pending sndbuf work , calls hvc_iucv_send ( )
* to flush any buffered terminal output data and waits for completion .
*/
static void flush_sndbuf_sync ( struct hvc_iucv_private * priv )
{
int sync_wait ;
cancel_delayed_work_sync ( & priv - > sndbuf_work ) ;
spin_lock_bh ( & priv - > lock ) ;
hvc_iucv_send ( priv ) ; /* force sending buffered data */
sync_wait = ! list_empty ( & priv - > tty_outqueue ) ; /* anything queued ? */
spin_unlock_bh ( & priv - > lock ) ;
if ( sync_wait )
wait_event_timeout ( priv - > sndbuf_waitq ,
tty_outqueue_empty ( priv ) , HZ ) ;
2008-12-25 15:38:57 +03:00
}
/**
2009-01-09 14:14:59 +03:00
* hvc_iucv_notifier_hangup ( ) - HVC notifier for TTY hangups .
* @ hp : Pointer to the HVC device ( struct hvc_struct )
* @ id : Additional data ( originally passed to hvc_alloc ) :
* the index of an struct hvc_iucv_private instance .
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:59 +03:00
* This routine notifies the HVC back - end that a tty hangup ( carrier loss ,
2008-12-25 15:38:57 +03:00
* virtual or otherwise ) has occured .
2009-01-09 14:14:59 +03:00
* The z / VM IUCV HVC device driver ignores virtual hangups ( vhangup ( ) )
* to keep an existing IUCV communication path established .
2008-12-25 15:38:57 +03:00
* ( Background : vhangup ( ) is called from user space ( by getty or login ) to
* disable writing to the tty by other applications ) .
2009-01-09 14:14:59 +03:00
* If the tty has been opened and an established IUCV path has been severed
* ( we caused the tty hangup ) , the function calls hvc_iucv_cleanup ( ) .
2008-12-25 15:38:57 +03:00
*
* Locking : struct hvc_iucv_private - > lock
*/
static void hvc_iucv_notifier_hangup ( struct hvc_struct * hp , int id )
{
struct hvc_iucv_private * priv ;
priv = hvc_iucv_get_private ( id ) ;
if ( ! priv )
return ;
2009-01-09 14:14:58 +03:00
flush_sndbuf_sync ( priv ) ;
2008-12-25 15:38:57 +03:00
spin_lock_bh ( & priv - > lock ) ;
/* NOTE: If the hangup was scheduled by ourself (from the iucv
2009-01-09 14:14:59 +03:00
* path_servered callback [ IUCV_SEVERED ] ) , we have to clean up
* our structure and to set state to TTY_CLOSED .
2008-12-25 15:38:57 +03:00
* If the tty was hung up otherwise ( e . g . vhangup ( ) ) , then we
* ignore this hangup and keep an established IUCV path open . . .
* ( . . . the reason is that we are not able to connect back to the
* client if we disconnect on hang up ) */
priv - > tty_state = TTY_CLOSED ;
if ( priv - > iucv_state = = IUCV_SEVERED )
hvc_iucv_cleanup ( priv ) ;
spin_unlock_bh ( & priv - > lock ) ;
}
/**
* hvc_iucv_notifier_del ( ) - HVC notifier for closing a TTY for the last time .
* @ hp : Pointer to the HVC device ( struct hvc_struct )
* @ id : Additional data ( originally passed to hvc_alloc ) :
* the index of an struct hvc_iucv_private instance .
*
2009-01-09 14:14:59 +03:00
* This routine notifies the HVC back - end that the last tty device fd has been
* closed . The function calls hvc_iucv_cleanup ( ) to clean up the struct
* hvc_iucv_private instance .
2008-12-25 15:38:57 +03:00
*
* Locking : struct hvc_iucv_private - > lock
*/
static void hvc_iucv_notifier_del ( struct hvc_struct * hp , int id )
{
struct hvc_iucv_private * priv ;
struct iucv_path * path ;
priv = hvc_iucv_get_private ( id ) ;
if ( ! priv )
return ;
2009-01-09 14:14:58 +03:00
flush_sndbuf_sync ( priv ) ;
2008-12-25 15:38:57 +03:00
spin_lock_bh ( & priv - > lock ) ;
path = priv - > path ; /* save reference to IUCV path */
priv - > path = NULL ;
hvc_iucv_cleanup ( priv ) ;
spin_unlock_bh ( & priv - > lock ) ;
/* sever IUCV path outside of priv->lock due to lock ordering of:
* priv - > lock < - - > iucv_table_lock */
if ( path ) {
iucv_path_sever ( path , NULL ) ;
iucv_path_free ( path ) ;
}
}
2009-03-26 17:23:55 +03:00
/**
* hvc_iucv_filter_connreq ( ) - Filter connection request based on z / VM user ID
* @ ipvmid : Originating z / VM user ID ( right padded with blanks )
*
* Returns 0 if the z / VM user ID @ ipvmid is allowed to connection , otherwise
* non - zero .
*/
static int hvc_iucv_filter_connreq ( u8 ipvmid [ 8 ] )
{
size_t i ;
/* Note: default policy is ACCEPT if no filter is set */
if ( ! hvc_iucv_filter_size )
return 0 ;
for ( i = 0 ; i < hvc_iucv_filter_size ; i + + )
if ( 0 = = memcmp ( ipvmid , hvc_iucv_filter + ( 8 * i ) , 8 ) )
return 0 ;
return 1 ;
}
2008-12-25 15:38:57 +03:00
/**
* hvc_iucv_path_pending ( ) - IUCV handler to process a connection request .
* @ path : Pending path ( struct iucv_path )
2009-01-09 14:14:59 +03:00
* @ ipvmid : z / VM system identifier of originator
2008-12-25 15:38:57 +03:00
* @ ipuser : User specified data for this path
* ( AF_IUCV : port / service name and originator port )
*
2009-01-09 14:14:59 +03:00
* The function uses the @ ipuser data to determine if the pending path belongs
* to a terminal managed by this device driver .
* If the path belongs to this driver , ensure that the terminal is not accessed
* multiple times ( only one connection to a terminal is allowed ) .
* If the terminal is not yet connected , the pending path is accepted and is
* associated to the appropriate struct hvc_iucv_private instance .
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:59 +03:00
* Returns 0 if @ path belongs to a terminal managed by the this device driver ;
2008-12-25 15:38:57 +03:00
* otherwise returns - ENODEV in order to dispatch this path to other handlers .
*
* Locking : struct hvc_iucv_private - > lock
*/
static int hvc_iucv_path_pending ( struct iucv_path * path ,
u8 ipvmid [ 8 ] , u8 ipuser [ 16 ] )
{
struct hvc_iucv_private * priv ;
u8 nuser_data [ 16 ] ;
2009-03-26 17:23:55 +03:00
u8 vm_user_id [ 9 ] ;
2008-12-25 15:38:57 +03:00
int i , rc ;
priv = NULL ;
for ( i = 0 ; i < hvc_iucv_devices ; i + + )
if ( hvc_iucv_table [ i ] & &
( 0 = = memcmp ( hvc_iucv_table [ i ] - > srv_name , ipuser , 8 ) ) ) {
priv = hvc_iucv_table [ i ] ;
break ;
}
if ( ! priv )
return - ENODEV ;
2009-03-26 17:23:55 +03:00
/* Enforce that ipvmid is allowed to connect to us */
read_lock ( & hvc_iucv_filter_lock ) ;
rc = hvc_iucv_filter_connreq ( ipvmid ) ;
read_unlock ( & hvc_iucv_filter_lock ) ;
if ( rc ) {
iucv_path_sever ( path , ipuser ) ;
iucv_path_free ( path ) ;
memcpy ( vm_user_id , ipvmid , 8 ) ;
vm_user_id [ 8 ] = 0 ;
pr_info ( " A connection request from z/VM user ID %s "
" was refused \n " , vm_user_id ) ;
return 0 ;
}
2008-12-25 15:38:57 +03:00
spin_lock ( & priv - > lock ) ;
/* If the terminal is already connected or being severed, then sever
* this path to enforce that there is only ONE established communication
* path per terminal . */
if ( priv - > iucv_state ! = IUCV_DISCONN ) {
iucv_path_sever ( path , ipuser ) ;
iucv_path_free ( path ) ;
goto out_path_handled ;
}
/* accept path */
memcpy ( nuser_data , ipuser + 8 , 8 ) ; /* remote service (for af_iucv) */
memcpy ( nuser_data + 8 , ipuser , 8 ) ; /* local service (for af_iucv) */
path - > msglim = 0xffff ; /* IUCV MSGLIMIT */
path - > flags & = ~ IUCV_IPRMDATA ; /* TODO: use IUCV_IPRMDATA */
rc = iucv_path_accept ( path , & hvc_iucv_handler , nuser_data , priv ) ;
if ( rc ) {
iucv_path_sever ( path , ipuser ) ;
iucv_path_free ( path ) ;
goto out_path_handled ;
}
priv - > path = path ;
priv - > iucv_state = IUCV_CONNECTED ;
2009-01-09 14:14:58 +03:00
/* flush buffered output data... */
schedule_delayed_work ( & priv - > sndbuf_work , 5 ) ;
2008-12-25 15:38:57 +03:00
out_path_handled :
spin_unlock ( & priv - > lock ) ;
return 0 ;
}
/**
* hvc_iucv_path_severed ( ) - IUCV handler to process a path sever .
* @ path : Pending path ( struct iucv_path )
* @ ipuser : User specified data for this path
* ( AF_IUCV : port / service name and originator port )
*
* The function also severs the path ( as required by the IUCV protocol ) and
* sets the iucv state to IUCV_SEVERED for the associated struct
* hvc_iucv_private instance . Later , the IUCV_SEVERED state triggers a tty
* hangup ( hvc_iucv_get_chars ( ) / hvc_iucv_write ( ) ) .
2009-01-09 14:14:59 +03:00
* If tty portion of the HVC is closed , clean up the outqueue .
2008-12-25 15:38:57 +03:00
*
* Locking : struct hvc_iucv_private - > lock
*/
static void hvc_iucv_path_severed ( struct iucv_path * path , u8 ipuser [ 16 ] )
{
struct hvc_iucv_private * priv = path - > private ;
spin_lock ( & priv - > lock ) ;
priv - > iucv_state = IUCV_SEVERED ;
2009-01-09 14:14:58 +03:00
/* If the tty has not yet been opened, clean up the hvc_iucv_private
* structure to allow re - connects .
2009-01-09 14:15:01 +03:00
* This is also done for our console device because console hangups
* are handled specially and no notifier is called by HVC .
* The tty session is active ( TTY_OPEN ) and ready for re - connects . . .
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:58 +03:00
* If it has been opened , let get_chars ( ) return - EPIPE to signal the
* HVC layer to hang up the tty .
* If so , we need to wake up the HVC thread to call get_chars ( ) . . .
*/
2008-12-25 15:38:57 +03:00
priv - > path = NULL ;
if ( priv - > tty_state = = TTY_CLOSED )
hvc_iucv_cleanup ( priv ) ;
2009-01-09 14:14:58 +03:00
else
2009-01-09 14:15:01 +03:00
if ( priv - > is_console ) {
hvc_iucv_cleanup ( priv ) ;
priv - > tty_state = TTY_OPENED ;
} else
hvc_kick ( ) ;
2008-12-25 15:38:57 +03:00
spin_unlock ( & priv - > lock ) ;
/* finally sever path (outside of priv->lock due to lock ordering) */
iucv_path_sever ( path , ipuser ) ;
iucv_path_free ( path ) ;
}
/**
* hvc_iucv_msg_pending ( ) - IUCV handler to process an incoming IUCV message .
* @ path : Pending path ( struct iucv_path )
* @ msg : Pointer to the IUCV message
*
2009-01-09 14:14:59 +03:00
* The function puts an incoming message on the input queue for later
2008-12-25 15:38:57 +03:00
* processing ( by hvc_iucv_get_chars ( ) / hvc_iucv_write ( ) ) .
2009-01-09 14:14:59 +03:00
* If the tty has not yet been opened , the message is rejected .
2008-12-25 15:38:57 +03:00
*
* Locking : struct hvc_iucv_private - > lock
*/
static void hvc_iucv_msg_pending ( struct iucv_path * path ,
struct iucv_message * msg )
{
struct hvc_iucv_private * priv = path - > private ;
struct iucv_tty_buffer * rb ;
2009-01-09 14:14:58 +03:00
/* reject messages that exceed max size of iucv_tty_msg->datalen */
if ( msg - > length > MSG_SIZE ( MSG_MAX_DATALEN ) ) {
iucv_message_reject ( path , msg ) ;
return ;
}
2008-12-25 15:38:57 +03:00
spin_lock ( & priv - > lock ) ;
/* reject messages if tty has not yet been opened */
if ( priv - > tty_state = = TTY_CLOSED ) {
iucv_message_reject ( path , msg ) ;
goto unlock_return ;
}
2009-01-09 14:14:58 +03:00
/* allocate tty buffer to save iucv msg only */
2008-12-25 15:38:57 +03:00
rb = alloc_tty_buffer ( 0 , GFP_ATOMIC ) ;
if ( ! rb ) {
iucv_message_reject ( path , msg ) ;
goto unlock_return ; /* -ENOMEM */
}
rb - > msg = * msg ;
list_add_tail ( & rb - > list , & priv - > tty_inqueue ) ;
2009-01-09 14:14:59 +03:00
hvc_kick ( ) ; /* wake up hvc thread */
2008-12-25 15:38:57 +03:00
unlock_return :
spin_unlock ( & priv - > lock ) ;
}
/**
* hvc_iucv_msg_complete ( ) - IUCV handler to process message completion
* @ path : Pending path ( struct iucv_path )
* @ msg : Pointer to the IUCV message
*
2009-01-09 14:14:59 +03:00
* The function is called upon completion of message delivery to remove the
* message from the outqueue . Additional delivery information can be found
* msg - > audit : rejected messages ( 0x040000 ( IPADRJCT ) ) , and
* purged messages ( 0x010000 ( IPADPGNR ) ) .
2008-12-25 15:38:57 +03:00
*
* Locking : struct hvc_iucv_private - > lock
*/
static void hvc_iucv_msg_complete ( struct iucv_path * path ,
struct iucv_message * msg )
{
struct hvc_iucv_private * priv = path - > private ;
struct iucv_tty_buffer * ent , * next ;
LIST_HEAD ( list_remove ) ;
spin_lock ( & priv - > lock ) ;
list_for_each_entry_safe ( ent , next , & priv - > tty_outqueue , list )
if ( ent - > msg . id = = msg - > id ) {
list_move ( & ent - > list , & list_remove ) ;
break ;
}
2009-01-09 14:14:58 +03:00
wake_up ( & priv - > sndbuf_waitq ) ;
2008-12-25 15:38:57 +03:00
spin_unlock ( & priv - > lock ) ;
destroy_tty_buffer_list ( & list_remove ) ;
}
/* HVC operations */
static struct hv_ops hvc_iucv_ops = {
. get_chars = hvc_iucv_get_chars ,
. put_chars = hvc_iucv_put_chars ,
. notifier_add = hvc_iucv_notifier_add ,
. notifier_del = hvc_iucv_notifier_del ,
. notifier_hangup = hvc_iucv_notifier_hangup ,
} ;
/**
* hvc_iucv_alloc ( ) - Allocates a new struct hvc_iucv_private instance
2009-01-09 14:15:01 +03:00
* @ id : hvc_iucv_table index
* @ is_console : Flag if the instance is used as Linux console
2008-12-25 15:38:57 +03:00
*
2009-01-09 14:14:59 +03:00
* This function allocates a new hvc_iucv_private structure and stores
* the instance in hvc_iucv_table at index @ id .
2008-12-25 15:38:57 +03:00
* Returns 0 on success ; otherwise non - zero .
*/
2009-01-09 14:15:01 +03:00
static int __init hvc_iucv_alloc ( int id , unsigned int is_console )
2008-12-25 15:38:57 +03:00
{
struct hvc_iucv_private * priv ;
char name [ 9 ] ;
int rc ;
priv = kzalloc ( sizeof ( struct hvc_iucv_private ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
spin_lock_init ( & priv - > lock ) ;
INIT_LIST_HEAD ( & priv - > tty_outqueue ) ;
INIT_LIST_HEAD ( & priv - > tty_inqueue ) ;
2009-01-09 14:14:58 +03:00
INIT_DELAYED_WORK ( & priv - > sndbuf_work , hvc_iucv_sndbuf_work ) ;
init_waitqueue_head ( & priv - > sndbuf_waitq ) ;
priv - > sndbuf = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! priv - > sndbuf ) {
kfree ( priv ) ;
return - ENOMEM ;
}
2008-12-25 15:38:57 +03:00
2009-01-09 14:15:01 +03:00
/* set console flag */
priv - > is_console = is_console ;
2009-01-09 14:14:59 +03:00
/* finally allocate hvc */
2009-01-09 14:14:58 +03:00
priv - > hvc = hvc_alloc ( HVC_IUCV_MAGIC + id , /* PAGE_SIZE */
HVC_IUCV_MAGIC + id , & hvc_iucv_ops , 256 ) ;
2008-12-25 15:38:57 +03:00
if ( IS_ERR ( priv - > hvc ) ) {
rc = PTR_ERR ( priv - > hvc ) ;
2009-01-09 14:14:58 +03:00
free_page ( ( unsigned long ) priv - > sndbuf ) ;
2008-12-25 15:38:57 +03:00
kfree ( priv ) ;
return rc ;
}
2009-01-09 14:14:59 +03:00
/* notify HVC thread instead of using polling */
2009-01-09 14:14:58 +03:00
priv - > hvc - > irq_requested = 1 ;
2008-12-25 15:38:57 +03:00
/* setup iucv related information */
2009-01-09 14:14:57 +03:00
snprintf ( name , 9 , " lnxhvc%-2d " , id ) ;
2008-12-25 15:38:57 +03:00
memcpy ( priv - > srv_name , name , 8 ) ;
ASCEBC ( priv - > srv_name , 8 ) ;
hvc_iucv_table [ id ] = priv ;
return 0 ;
}
2009-03-26 17:23:55 +03:00
/**
* hvc_iucv_parse_filter ( ) - Parse filter for a single z / VM user ID
* @ filter : String containing a comma - separated list of z / VM user IDs
*/
static const char * hvc_iucv_parse_filter ( const char * filter , char * dest )
{
const char * nextdelim , * residual ;
size_t len ;
nextdelim = strchr ( filter , ' , ' ) ;
if ( nextdelim ) {
len = nextdelim - filter ;
residual = nextdelim + 1 ;
} else {
len = strlen ( filter ) ;
residual = filter + len ;
}
if ( len = = 0 )
return ERR_PTR ( - EINVAL ) ;
/* check for '\n' (if called from sysfs) */
if ( filter [ len - 1 ] = = ' \n ' )
len - - ;
if ( len > 8 )
return ERR_PTR ( - EINVAL ) ;
/* pad with blanks and save upper case version of user ID */
memset ( dest , ' ' , 8 ) ;
while ( len - - )
dest [ len ] = toupper ( filter [ len ] ) ;
return residual ;
}
/**
* hvc_iucv_setup_filter ( ) - Set up z / VM user ID filter
* @ filter : String consisting of a comma - separated list of z / VM user IDs
*
* The function parses the @ filter string and creates an array containing
* the list of z / VM user ID filter entries .
* Return code 0 means success , - EINVAL if the filter is syntactically
* incorrect , - ENOMEM if there was not enough memory to allocate the
* filter list array , or - ENOSPC if too many z / VM user IDs have been specified .
*/
static int hvc_iucv_setup_filter ( const char * val )
{
const char * residual ;
int err ;
size_t size , count ;
void * array , * old_filter ;
count = strlen ( val ) ;
if ( count = = 0 | | ( count = = 1 & & val [ 0 ] = = ' \n ' ) ) {
size = 0 ;
array = NULL ;
goto out_replace_filter ; /* clear filter */
}
/* count user IDs in order to allocate sufficient memory */
size = 1 ;
residual = val ;
while ( ( residual = strchr ( residual , ' , ' ) ) ! = NULL ) {
residual + + ;
size + + ;
}
/* check if the specified list exceeds the filter limit */
if ( size > MAX_VMID_FILTER )
return - ENOSPC ;
array = kzalloc ( size * 8 , GFP_KERNEL ) ;
if ( ! array )
return - ENOMEM ;
count = size ;
residual = val ;
while ( * residual & & count ) {
residual = hvc_iucv_parse_filter ( residual ,
array + ( ( size - count ) * 8 ) ) ;
if ( IS_ERR ( residual ) ) {
err = PTR_ERR ( residual ) ;
kfree ( array ) ;
goto out_err ;
}
count - - ;
}
out_replace_filter :
write_lock_bh ( & hvc_iucv_filter_lock ) ;
old_filter = hvc_iucv_filter ;
hvc_iucv_filter_size = size ;
hvc_iucv_filter = array ;
write_unlock_bh ( & hvc_iucv_filter_lock ) ;
kfree ( old_filter ) ;
err = 0 ;
out_err :
return err ;
}
/**
* param_set_vmidfilter ( ) - Set z / VM user ID filter parameter
* @ val : String consisting of a comma - separated list of z / VM user IDs
* @ kp : Kernel parameter pointing to hvc_iucv_filter array
*
* The function sets up the z / VM user ID filter specified as comma - separated
* list of user IDs in @ val .
* Note : If it is called early in the boot process , @ val is stored and
* parsed later in hvc_iucv_init ( ) .
*/
static int param_set_vmidfilter ( const char * val , struct kernel_param * kp )
{
int rc ;
if ( ! MACHINE_IS_VM | | ! hvc_iucv_devices )
return - ENODEV ;
if ( ! val )
return - EINVAL ;
rc = 0 ;
if ( slab_is_available ( ) )
rc = hvc_iucv_setup_filter ( val ) ;
else
hvc_iucv_filter_string = val ; /* defer... */
return rc ;
}
/**
* param_get_vmidfilter ( ) - Get z / VM user ID filter
* @ buffer : Buffer to store z / VM user ID filter ,
* ( buffer size assumption PAGE_SIZE )
* @ kp : Kernel parameter pointing to the hvc_iucv_filter array
*
* The function stores the filter as a comma - separated list of z / VM user IDs
* in @ buffer . Typically , sysfs routines call this function for attr show .
*/
static int param_get_vmidfilter ( char * buffer , struct kernel_param * kp )
{
int rc ;
size_t index , len ;
void * start , * end ;
if ( ! MACHINE_IS_VM | | ! hvc_iucv_devices )
return - ENODEV ;
rc = 0 ;
read_lock_bh ( & hvc_iucv_filter_lock ) ;
for ( index = 0 ; index < hvc_iucv_filter_size ; index + + ) {
start = hvc_iucv_filter + ( 8 * index ) ;
end = memchr ( start , ' ' , 8 ) ;
len = ( end ) ? end - start : 8 ;
memcpy ( buffer + rc , start , len ) ;
rc + = len ;
buffer [ rc + + ] = ' , ' ;
}
read_unlock_bh ( & hvc_iucv_filter_lock ) ;
if ( rc )
buffer [ - - rc ] = ' \0 ' ; /* replace last comma and update rc */
return rc ;
}
# define param_check_vmidfilter(name, p) __param_check(name, p, void)
2008-12-25 15:38:57 +03:00
/**
2009-01-09 14:14:59 +03:00
* hvc_iucv_init ( ) - z / VM IUCV HVC device driver initialization
2008-12-25 15:38:57 +03:00
*/
static int __init hvc_iucv_init ( void )
{
2009-01-09 14:15:01 +03:00
int rc ;
unsigned int i ;
2008-12-25 15:38:57 +03:00
2009-03-26 17:23:55 +03:00
if ( ! hvc_iucv_devices )
return - ENODEV ;
2008-12-25 15:38:57 +03:00
if ( ! MACHINE_IS_VM ) {
2009-03-26 17:23:54 +03:00
pr_notice ( " The z/VM IUCV HVC device driver cannot "
2009-01-09 14:14:58 +03:00
" be used without z/VM \n " ) ;
2009-03-26 17:23:55 +03:00
rc = - ENODEV ;
goto out_error ;
2008-12-25 15:38:57 +03:00
}
2009-03-26 17:23:54 +03:00
if ( hvc_iucv_devices > MAX_HVC_IUCV_LINES ) {
pr_err ( " %lu is not a valid value for the hvc_iucv= "
" kernel parameter \n " , hvc_iucv_devices ) ;
2009-03-26 17:23:55 +03:00
rc = - EINVAL ;
goto out_error ;
}
/* parse hvc_iucv_allow string and create z/VM user ID filter list */
if ( hvc_iucv_filter_string ) {
rc = hvc_iucv_setup_filter ( hvc_iucv_filter_string ) ;
switch ( rc ) {
case 0 :
break ;
case - ENOMEM :
pr_err ( " Allocating memory failed with "
" reason code=%d \n " , 3 ) ;
goto out_error ;
case - EINVAL :
pr_err ( " hvc_iucv_allow= does not specify a valid "
" z/VM user ID list \n " ) ;
goto out_error ;
case - ENOSPC :
pr_err ( " hvc_iucv_allow= specifies too many "
" z/VM user IDs \n " ) ;
goto out_error ;
default :
goto out_error ;
}
2009-03-26 17:23:54 +03:00
}
2008-12-25 15:38:57 +03:00
hvc_iucv_buffer_cache = kmem_cache_create ( KMSG_COMPONENT ,
sizeof ( struct iucv_tty_buffer ) ,
0 , 0 , NULL ) ;
if ( ! hvc_iucv_buffer_cache ) {
2009-01-09 14:14:58 +03:00
pr_err ( " Allocating memory failed with reason code=%d \n " , 1 ) ;
2009-03-26 17:23:55 +03:00
rc = - ENOMEM ;
goto out_error ;
2008-12-25 15:38:57 +03:00
}
hvc_iucv_mempool = mempool_create_slab_pool ( MEMPOOL_MIN_NR ,
hvc_iucv_buffer_cache ) ;
if ( ! hvc_iucv_mempool ) {
2009-01-09 14:14:58 +03:00
pr_err ( " Allocating memory failed with reason code=%d \n " , 2 ) ;
2008-12-25 15:38:57 +03:00
kmem_cache_destroy ( hvc_iucv_buffer_cache ) ;
2009-03-26 17:23:55 +03:00
rc = - ENOMEM ;
goto out_error ;
2008-12-25 15:38:57 +03:00
}
2009-01-09 14:15:00 +03:00
/* register the first terminal device as console
* ( must be done before allocating hvc terminal devices ) */
2009-01-09 14:15:01 +03:00
rc = hvc_instantiate ( HVC_IUCV_MAGIC , IUCV_HVC_CON_IDX , & hvc_iucv_ops ) ;
if ( rc ) {
pr_err ( " Registering HVC terminal device as "
" Linux console failed \n " ) ;
goto out_error_memory ;
}
2009-01-09 14:15:00 +03:00
2008-12-25 15:38:57 +03:00
/* allocate hvc_iucv_private structs */
for ( i = 0 ; i < hvc_iucv_devices ; i + + ) {
2009-01-09 14:15:01 +03:00
rc = hvc_iucv_alloc ( i , ( i = = IUCV_HVC_CON_IDX ) ? 1 : 0 ) ;
2008-12-25 15:38:57 +03:00
if ( rc ) {
2009-01-09 14:14:58 +03:00
pr_err ( " Creating a new HVC terminal device "
2009-01-09 14:14:59 +03:00
" failed with error code=%d \n " , rc ) ;
2008-12-25 15:38:57 +03:00
goto out_error_hvc ;
}
}
/* register IUCV callback handler */
rc = iucv_register ( & hvc_iucv_handler , 0 ) ;
if ( rc ) {
2009-01-09 14:14:58 +03:00
pr_err ( " Registering IUCV handlers failed with error code=%d \n " ,
rc ) ;
2008-12-25 15:38:57 +03:00
goto out_error_iucv ;
}
return 0 ;
out_error_iucv :
iucv_unregister ( & hvc_iucv_handler , 0 ) ;
out_error_hvc :
for ( i = 0 ; i < hvc_iucv_devices ; i + + )
if ( hvc_iucv_table [ i ] ) {
if ( hvc_iucv_table [ i ] - > hvc )
hvc_remove ( hvc_iucv_table [ i ] - > hvc ) ;
kfree ( hvc_iucv_table [ i ] ) ;
}
2009-01-09 14:15:01 +03:00
out_error_memory :
2008-12-25 15:38:57 +03:00
mempool_destroy ( hvc_iucv_mempool ) ;
kmem_cache_destroy ( hvc_iucv_buffer_cache ) ;
2009-03-26 17:23:55 +03:00
out_error :
hvc_iucv_devices = 0 ; /* ensure that we do not provide any device */
2008-12-25 15:38:57 +03:00
return rc ;
}
/**
* hvc_iucv_config ( ) - Parsing of hvc_iucv = kernel command line parameter
* @ val : Parameter value ( numeric )
*/
static int __init hvc_iucv_config ( char * val )
{
return strict_strtoul ( val , 10 , & hvc_iucv_devices ) ;
}
2009-01-09 14:15:00 +03:00
device_initcall ( hvc_iucv_init ) ;
2008-12-25 15:38:57 +03:00
__setup ( " hvc_iucv= " , hvc_iucv_config ) ;
2009-03-26 17:23:55 +03:00
core_param ( hvc_iucv_allow , hvc_iucv_filter , vmidfilter , 0640 ) ;