Commit 6cab1ea3 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: IUCV network driver.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

IUCV interface fixes:
 - VM workaround: redirect setmask call to cpu 0.
 - Disable message interrupts during connection setup.
 - Honor incoming connection severed during connection setup.
 - Add connect retry when remote severed connection.
 - Add symlinks between net device and iucv device.
 - More fixes for proper net device allocation/deallocation.
parent dd0d869f
/* /*
* $Id: iucv.c,v 1.19 2003/12/18 15:28:49 braunu Exp $ * $Id: iucv.c,v 1.24 2004/02/05 14:16:01 braunu Exp $
* *
* IUCV network driver * IUCV network driver
* *
...@@ -29,10 +29,12 @@ ...@@ -29,10 +29,12 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
* RELEASE-TAG: IUCV lowlevel driver $Revision: 1.19 $ * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.24 $
* *
*/ */
/* #define DEBUG */
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/config.h> #include <linux/config.h>
...@@ -53,8 +55,6 @@ ...@@ -53,8 +55,6 @@
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/ccwdev.h> //for root device stuff #include <asm/ccwdev.h> //for root device stuff
#define DEBUG
/* FLAGS: /* FLAGS:
* All flags are defined in the field IPFLAGS1 of each function * All flags are defined in the field IPFLAGS1 of each function
* and can be found in CP Programming Services. * and can be found in CP Programming Services.
...@@ -104,6 +104,8 @@ static iucv_GeneralInterrupt *iucv_external_int_buffer; ...@@ -104,6 +104,8 @@ static iucv_GeneralInterrupt *iucv_external_int_buffer;
static spinlock_t iucv_lock = SPIN_LOCK_UNLOCKED; static spinlock_t iucv_lock = SPIN_LOCK_UNLOCKED;
static int messagesDisabled = 0;
/***************INTERRUPT HANDLING ***************/ /***************INTERRUPT HANDLING ***************/
typedef struct { typedef struct {
...@@ -349,7 +351,7 @@ do { \ ...@@ -349,7 +351,7 @@ do { \
static void static void
iucv_banner(void) iucv_banner(void)
{ {
char vbuf[] = "$Revision: 1.19 $"; char vbuf[] = "$Revision: 1.24 $";
char *version = vbuf; char *version = vbuf;
if ((version = strchr(version, ':'))) { if ((version = strchr(version, ':'))) {
...@@ -433,10 +435,13 @@ iucv_init(void) ...@@ -433,10 +435,13 @@ iucv_init(void)
* *
* Frees everything allocated from iucv_init. * Frees everything allocated from iucv_init.
*/ */
static int iucv_retrieve_buffer (void);
static void static void
iucv_exit(void) iucv_exit(void)
{ {
if (iucv_external_int_buffer) iucv_retrieve_buffer();
if (iucv_external_int_buffer)
kfree(iucv_external_int_buffer); kfree(iucv_external_int_buffer);
if (iucv_param_pool) if (iucv_param_pool)
kfree(iucv_param_pool); kfree(iucv_param_pool);
...@@ -716,7 +721,6 @@ iucv_remove_handler(handler *handler) ...@@ -716,7 +721,6 @@ iucv_remove_handler(handler *handler)
spin_lock_irqsave (&iucv_lock, flags); spin_lock_irqsave (&iucv_lock, flags);
list_del(&handler->list); list_del(&handler->list);
if (list_empty(&iucv_handler_table)) { if (list_empty(&iucv_handler_table)) {
iucv_retrieve_buffer();
if (register_flag) { if (register_flag) {
unregister_external_interrupt(0x4000, iucv_irq_handler); unregister_external_interrupt(0x4000, iucv_irq_handler);
register_flag = 0; register_flag = 0;
...@@ -1028,6 +1032,8 @@ iucv_accept(__u16 pathid, __u16 msglim_reqstd, ...@@ -1028,6 +1032,8 @@ iucv_accept(__u16 pathid, __u16 msglim_reqstd,
b2f0_result = b2f0(ACCEPT, parm); b2f0_result = b2f0(ACCEPT, parm);
if (b2f0_result == 0) { if (b2f0_result == 0) {
if (msglim)
*msglim = parm->ipmsglim;
if (pgm_data) if (pgm_data)
h->pgm_data = pgm_data; h->pgm_data = pgm_data;
if (flags1_out) if (flags1_out)
...@@ -1083,6 +1089,7 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd, ...@@ -1083,6 +1089,7 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd,
iucv_handle_t handle, void *pgm_data) iucv_handle_t handle, void *pgm_data)
{ {
iparml_control *parm; iparml_control *parm;
iparml_control local_parm;
struct list_head *lh; struct list_head *lh;
ulong b2f0_result = 0; ulong b2f0_result = 0;
ulong flags; ulong flags;
...@@ -1139,27 +1146,53 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd, ...@@ -1139,27 +1146,53 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd,
EBC_TOUPPER(parm->iptarget, sizeof(parm->iptarget)); EBC_TOUPPER(parm->iptarget, sizeof(parm->iptarget));
} }
/* In order to establish an IUCV connection, the procedure is:
*
* b2f0(CONNECT)
* take the ippathid from the b2f0 call
* register the handler to the ippathid
*
* Unfortunately, the ConnectionEstablished message gets sent after the
* b2f0(CONNECT) call but before the register is handled.
*
* In order for this race condition to be eliminated, the IUCV Control
* Interrupts must be disabled for the above procedure.
*
* David Kennedy <dkennedy@linuxcare.com>
*/
/* Enable everything but IUCV Control messages */
iucv_setmask(~(AllInterrupts));
messagesDisabled = 1;
spin_lock_irqsave (&iucv_lock, flags); spin_lock_irqsave (&iucv_lock, flags);
parm->ipflags1 = (__u8)flags1; parm->ipflags1 = (__u8)flags1;
b2f0_result = b2f0(CONNECT, parm); b2f0_result = b2f0(CONNECT, parm);
memcpy(&local_parm, parm, sizeof(local_parm));
release_param(parm);
parm = &local_parm;
if (b2f0_result == 0) if (b2f0_result == 0)
add_pathid_result = __iucv_add_pathid(parm->ippathid, h); add_pathid_result = __iucv_add_pathid(parm->ippathid, h);
spin_unlock_irqrestore (&iucv_lock, flags); spin_unlock_irqrestore (&iucv_lock, flags);
if (b2f0_result) { if (b2f0_result) {
release_param(parm); iucv_setmask(~0);
messagesDisabled = 0;
return b2f0_result; return b2f0_result;
} }
*pathid = parm->ippathid; *pathid = parm->ippathid;
/* Enable everything again */
iucv_setmask(IUCVControlInterruptsFlag);
if (msglim) if (msglim)
*msglim = parm->ipmsglim; *msglim = parm->ipmsglim;
if (flags1_out) if (flags1_out)
*flags1_out = (parm->ipflags1 & IPPRTY) ? IPPRTY : 0; *flags1_out = (parm->ipflags1 & IPPRTY) ? IPPRTY : 0;
if (add_pathid_result) { if (add_pathid_result) {
iucv_sever(parm->ippathid, no_memory); iucv_sever(*pathid, no_memory);
printk(KERN_WARNING "%s: add_pathid failed with rc =" printk(KERN_WARNING "%s: add_pathid failed with rc ="
" %d\n", __FUNCTION__, add_pathid_result); " %d\n", __FUNCTION__, add_pathid_result);
return(add_pathid_result); return(add_pathid_result);
...@@ -2142,6 +2175,24 @@ iucv_send2way_prmmsg_array (__u16 pathid, ...@@ -2142,6 +2175,24 @@ iucv_send2way_prmmsg_array (__u16 pathid,
return b2f0_result; return b2f0_result;
} }
void
iucv_setmask_cpu0 (void *result)
{
iparml_set_mask *parm;
if (smp_processor_id() != 0)
return;
iucv_debug(1, "entering");
parm = (iparml_set_mask *)grab_param();
parm->ipmask = *((__u8*)result);
*((ulong *)result) = b2f0(SETMASK, parm);
release_param(parm);
iucv_debug(1, "b2f0_result = %ld", *((ulong *)result));
iucv_debug(1, "exiting");
}
/* /*
* Name: iucv_setmask * Name: iucv_setmask
* Purpose: This function enables or disables the following IUCV * Purpose: This function enables or disables the following IUCV
...@@ -2152,28 +2203,25 @@ iucv_send2way_prmmsg_array (__u16 pathid, ...@@ -2152,28 +2203,25 @@ iucv_send2way_prmmsg_array (__u16 pathid,
* 0x40 - Priority_MessagePendingInterruptsFlag * 0x40 - Priority_MessagePendingInterruptsFlag
* 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag
* 0x10 - Priority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag
* 0x08 - IUCVControlInterruptsFlag
* Output: NA * Output: NA
* Return: b2f0_result - return code from CP * Return: b2f0_result - return code from CP
*/ */
int int
iucv_setmask (int SetMaskFlag) iucv_setmask (int SetMaskFlag)
{ {
iparml_set_mask *parm; union {
ulong b2f0_result = 0; ulong result;
__u8 param;
iucv_debug(1, "entering"); } u;
parm = (iparml_set_mask *)grab_param();
parm->ipmask = (__u8)SetMaskFlag;
b2f0_result = b2f0(SETMASK, parm);
release_param(parm);
iucv_debug(1, "b2f0_result = %ld", b2f0_result); u.param = SetMaskFlag;
iucv_debug(1, "exiting"); if (smp_processor_id() == 0)
iucv_setmask_cpu0(&u);
else
smp_call_function(iucv_setmask_cpu0, &u, 0, 1);
return b2f0_result; return u.result;
} }
/** /**
...@@ -2280,6 +2328,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) ...@@ -2280,6 +2328,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf)
/* end of if statement */ /* end of if statement */
switch (int_buf->iptype) { switch (int_buf->iptype) {
case 0x01: /* connection pending */ case 0x01: /* connection pending */
if (messagesDisabled) {
iucv_setmask(~0);
messagesDisabled = 0;
}
spin_lock_irqsave(&iucv_lock, flags); spin_lock_irqsave(&iucv_lock, flags);
list_for_each(lh, &iucv_handler_table) { list_for_each(lh, &iucv_handler_table) {
h = list_entry(lh, handler, list); h = list_entry(lh, handler, list);
...@@ -2328,11 +2380,17 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) ...@@ -2328,11 +2380,17 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf)
break; break;
case 0x02: /*connection complete */ case 0x02: /*connection complete */
if (messagesDisabled) {
iucv_setmask(~0);
messagesDisabled = 0;
}
if (h) { if (h) {
if (interrupt->ConnectionComplete) if (interrupt->ConnectionComplete)
{
interrupt->ConnectionComplete( interrupt->ConnectionComplete(
(iucv_ConnectionComplete *)int_buf, (iucv_ConnectionComplete *)int_buf,
h->pgm_data); h->pgm_data);
}
else else
iucv_debug(1, iucv_debug(1,
"ConnectionComplete not called"); "ConnectionComplete not called");
...@@ -2341,6 +2399,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) ...@@ -2341,6 +2399,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf)
break; break;
case 0x03: /* connection severed */ case 0x03: /* connection severed */
if (messagesDisabled) {
iucv_setmask(~0);
messagesDisabled = 0;
}
if (h) { if (h) {
if (interrupt->ConnectionSevered) if (interrupt->ConnectionSevered)
interrupt->ConnectionSevered( interrupt->ConnectionSevered(
...@@ -2354,6 +2416,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) ...@@ -2354,6 +2416,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf)
break; break;
case 0x04: /* connection quiesced */ case 0x04: /* connection quiesced */
if (messagesDisabled) {
iucv_setmask(~0);
messagesDisabled = 0;
}
if (h) { if (h) {
if (interrupt->ConnectionQuiesced) if (interrupt->ConnectionQuiesced)
interrupt->ConnectionQuiesced( interrupt->ConnectionQuiesced(
...@@ -2366,6 +2432,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) ...@@ -2366,6 +2432,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf)
break; break;
case 0x05: /* connection resumed */ case 0x05: /* connection resumed */
if (messagesDisabled) {
iucv_setmask(~0);
messagesDisabled = 0;
}
if (h) { if (h) {
if (interrupt->ConnectionResumed) if (interrupt->ConnectionResumed)
interrupt->ConnectionResumed( interrupt->ConnectionResumed(
...@@ -2467,7 +2537,9 @@ EXPORT_SYMBOL (iucv_quiesce); ...@@ -2467,7 +2537,9 @@ EXPORT_SYMBOL (iucv_quiesce);
EXPORT_SYMBOL (iucv_receive); EXPORT_SYMBOL (iucv_receive);
#if 0 #if 0
EXPORT_SYMBOL (iucv_receive_array); EXPORT_SYMBOL (iucv_receive_array);
#endif
EXPORT_SYMBOL (iucv_reject); EXPORT_SYMBOL (iucv_reject);
#if 0
EXPORT_SYMBOL (iucv_reply); EXPORT_SYMBOL (iucv_reply);
EXPORT_SYMBOL (iucv_reply_array); EXPORT_SYMBOL (iucv_reply_array);
EXPORT_SYMBOL (iucv_reply_prmmsg); EXPORT_SYMBOL (iucv_reply_prmmsg);
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
#define Priority_MessagePendingInterruptsFlag 0x40 #define Priority_MessagePendingInterruptsFlag 0x40
#define Nonpriority_MessageCompletionInterruptsFlag 0x20 #define Nonpriority_MessageCompletionInterruptsFlag 0x20
#define Priority_MessageCompletionInterruptsFlag 0x10 #define Priority_MessageCompletionInterruptsFlag 0x10
#define IUCVControlInterruptsFlag 0x08
#define AllInterrupts 0xf8
/* /*
* Mapping of external interrupt buffers should be used with the corresponding * Mapping of external interrupt buffers should be used with the corresponding
* interrupt types. * interrupt types.
...@@ -738,6 +740,7 @@ int iucv_send2way_prmmsg_array (u16 pathid, ...@@ -738,6 +740,7 @@ int iucv_send2way_prmmsg_array (u16 pathid,
* 0x40 - Priority_MessagePendingInterruptsFlag * 0x40 - Priority_MessagePendingInterruptsFlag
* 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag
* 0x10 - Priority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag
* 0x08 - IUCVControlInterruptsFlag
* Output: NA * Output: NA
* Return: Return code from CP IUCV call. * Return: Return code from CP IUCV call.
*/ */
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment