-/*
- * $Id: iucv.c,v 1.32 2004/05/18 09:28:43 braunu Exp $
- *
+/*
* IUCV network driver
*
* Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.32 $
- *
*/
-\f
+
/* #define DEBUG */
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/s390_ext.h>
#include <asm/ebcdic.h>
-#include <asm/ccwdev.h> //for root device stuff
+#include <asm/smp.h>
+#include <asm/s390_rdev.h>
/* FLAGS:
* All flags are defined in the field IPFLAGS1 of each function
struct bus_type iucv_bus = {
.name = "iucv",
.match = iucv_bus_match,
-};
+};
struct device *iucv_root;
/* Spin Lock declaration */
-static spinlock_t iucv_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(iucv_lock);
static int messagesDisabled = 0;
} iucv_irqdata;
static struct list_head iucv_irq_queue;
-static spinlock_t iucv_irq_queue_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(iucv_irq_queue_lock);
/*
*Internal function prototypes
*/
static void iucv_tasklet_handler(unsigned long);
-static void iucv_irq_handler(struct pt_regs *, __u16);
+static void iucv_irq_handler(__u16);
static DECLARE_TASKLET(iucv_tasklet,iucv_tasklet_handler,0);
static unsigned long max_connections;
/**
- * declare_flag: is 0 when iucv_declare_buffer has not been called
+ * iucv_cpuid: contains the logical cpu number of the cpu which
+ * has declared the iucv buffer by issuing DECLARE_BUFFER.
+ * If no cpu has done the initialization iucv_cpuid contains -1.
*/
-static int declare_flag;
+static int iucv_cpuid = -1;
/**
* register_flag: is 0 when external interrupt has not been registered
*/
/*
* Debugging stuff
*******************************************************************************/
-\f
+
#ifdef DEBUG
static int debuglevel = 0;
#else
-#define iucv_debug(lvl, fmt, args...)
-#define iucv_dumpit(title, buf, len)
+#define iucv_debug(lvl, fmt, args...) do { } while (0)
+#define iucv_dumpit(title, buf, len) do { } while (0)
#endif
/*
* Internal functions
*******************************************************************************/
-\f
+
/**
* print start banner
*/
static void
iucv_banner(void)
{
- char vbuf[] = "$Revision: 1.32 $";
- char *version = vbuf;
-
- if ((version = strchr(version, ':'))) {
- char *p = strchr(version + 1, '$');
- if (p)
- *p = '\0';
- } else
- version = " ??? ";
- printk(KERN_INFO
- "IUCV lowlevel driver Version%s initialized\n", version);
+ printk(KERN_INFO "IUCV lowlevel driver initialized\n");
}
/**
}
ret = bus_register(&iucv_bus);
- if (ret != 0) {
+ if (ret) {
printk(KERN_ERR "IUCV: failed to register bus.\n");
return ret;
}
}
/* Note: GFP_DMA used used to get memory below 2G */
- iucv_external_int_buffer = kmalloc(sizeof(iucv_GeneralInterrupt),
+ iucv_external_int_buffer = kzalloc(sizeof(iucv_GeneralInterrupt),
GFP_KERNEL|GFP_DMA);
if (!iucv_external_int_buffer) {
printk(KERN_WARNING
bus_unregister(&iucv_bus);
return -ENOMEM;
}
- memset(iucv_external_int_buffer, 0, sizeof(iucv_GeneralInterrupt));
/* Initialize parameter pool */
- iucv_param_pool = kmalloc(sizeof(iucv_param) * PARAM_POOL_SIZE,
+ iucv_param_pool = kzalloc(sizeof(iucv_param) * PARAM_POOL_SIZE,
GFP_KERNEL|GFP_DMA);
if (!iucv_param_pool) {
printk(KERN_WARNING "%s: Could not allocate param pool\n",
bus_unregister(&iucv_bus);
return -ENOMEM;
}
- memset(iucv_param_pool, 0, sizeof(iucv_param) * PARAM_POOL_SIZE);
/* Initialize irq queue */
INIT_LIST_HEAD(&iucv_irq_queue);
iucv_exit(void)
{
iucv_retrieve_buffer();
- if (iucv_external_int_buffer) {
- kfree(iucv_external_int_buffer);
- iucv_external_int_buffer = NULL;
- }
- if (iucv_param_pool) {
- kfree(iucv_param_pool);
- iucv_param_pool = NULL;
- }
+ kfree(iucv_external_int_buffer);
+ iucv_external_int_buffer = NULL;
+ kfree(iucv_param_pool);
+ iucv_param_pool = NULL;
s390_root_dev_unregister(iucv_root);
bus_unregister(&iucv_bus);
printk(KERN_INFO "IUCV lowlevel driver unloaded\n");
ptr++;
if (ptr >= iucv_param_pool + PARAM_POOL_SIZE)
ptr = iucv_param_pool;
- } while (atomic_compare_and_swap(0, 1, &ptr->in_use));
+ } while (atomic_cmpxchg(&ptr->in_use, 0, 1) != 0);
hint = ptr - iucv_param_pool;
memset(&ptr->param, 0, sizeof(ptr->param));
*/
list_for_each(lh, &iucv_handler_table) {
handler *h = list_entry(lh, handler, list);
- if (memcmp(&new->id, &h->id, sizeof(h->id)) == 0) {
+ if (!memcmp(&new->id, &h->id, sizeof(h->id))) {
iucv_debug(1, "ret 1");
spin_unlock_irqrestore (&iucv_lock, flags);
return 1;
*
* Returns: return code from CP's IUCV call
*/
-static __inline__ ulong
-b2f0(__u32 code, void *parm)
+static inline ulong b2f0(__u32 code, void *parm)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iucv_dumpit("iparml before b2f0 call:", parm, sizeof(iucv_param));
- asm volatile (
- "LRA 1,0(%1)\n\t"
- "LR 0,%0\n\t"
- ".long 0xb2f01000"
- :
- : "d" (code), "a" (parm)
- : "0", "1"
- );
+ reg0 = code;
+ reg1 = virt_to_phys(parm);
+ asm volatile(".long 0xb2f01000" : : "d" (reg0), "a" (reg1));
iucv_dumpit("iparml after b2f0 call:", parm, sizeof(iucv_param));
}
/**
- * iucv_declare_buffer_cpu0
- * Register at VM for subsequent IUCV operations. This is always
- * executed on CPU 0. Called from iucv_declare_buffer().
+ * iucv_declare_buffer_cpuid
+ * Register at VM for subsequent IUCV operations. This is executed
+ * on the reserved CPU iucv_cpuid. Called from iucv_declare_buffer().
*/
static void
-iucv_declare_buffer_cpu0 (void *result)
+iucv_declare_buffer_cpuid (void *result)
{
iparml_db *parm;
- if (!(result && (smp_processor_id() == 0)))
- return;
parm = (iparml_db *)grab_param();
parm->ipbfadr1 = virt_to_phys(iucv_external_int_buffer);
if ((*((ulong *)result) = b2f0(DECLARE_BUFFER, parm)) == 1)
}
/**
- * iucv_retrieve_buffer_cpu0:
- * Unregister IUCV usage at VM. This is always executed on CPU 0.
+ * iucv_retrieve_buffer_cpuid:
+ * Unregister IUCV usage at VM. This is always executed on the same
+ * cpu that registered the buffer to VM.
* Called from iucv_retrieve_buffer().
*/
static void
-iucv_retrieve_buffer_cpu0 (void *result)
+iucv_retrieve_buffer_cpuid (void *cpu)
{
iparml_control *parm;
- if (smp_processor_id() != 0)
- return;
parm = (iparml_control *)grab_param();
b2f0(RETRIEVE_BUFFER, parm);
release_param(parm);
static int
iucv_declare_buffer (void)
{
- ulong b2f0_result = 0x0deadbeef;
+ unsigned long flags;
+ ulong b2f0_result;
iucv_debug(1, "entering");
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_declare_buffer_cpu0(&b2f0_result);
- else
- smp_call_function(iucv_declare_buffer_cpu0, &b2f0_result, 0, 1);
- preempt_enable();
- iucv_debug(1, "Address of EIB = %p", iucv_external_int_buffer);
- if (b2f0_result == 0x0deadbeef)
- b2f0_result = 0xaa;
+ b2f0_result = -ENODEV;
+ spin_lock_irqsave (&iucv_lock, flags);
+ if (iucv_cpuid == -1) {
+ /* Reserve any cpu for use by iucv. */
+ iucv_cpuid = smp_get_cpu(CPU_MASK_ALL);
+ spin_unlock_irqrestore (&iucv_lock, flags);
+ smp_call_function_on(iucv_declare_buffer_cpuid,
+ &b2f0_result, 0, 1, iucv_cpuid);
+ if (b2f0_result) {
+ smp_put_cpu(iucv_cpuid);
+ iucv_cpuid = -1;
+ }
+ iucv_debug(1, "Address of EIB = %p", iucv_external_int_buffer);
+ } else {
+ spin_unlock_irqrestore (&iucv_lock, flags);
+ b2f0_result = 0;
+ }
iucv_debug(1, "exiting");
return b2f0_result;
}
iucv_retrieve_buffer (void)
{
iucv_debug(1, "entering");
- if (declare_flag) {
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_retrieve_buffer_cpu0(0);
- else
- smp_call_function(iucv_retrieve_buffer_cpu0, 0, 0, 1);
- declare_flag = 0;
- preempt_enable();
+ if (iucv_cpuid != -1) {
+ smp_call_function_on(iucv_retrieve_buffer_cpuid,
+ NULL, 0, 1, iucv_cpuid);
+ /* Release the cpu reserved by iucv_declare_buffer. */
+ smp_put_cpu(iucv_cpuid);
+ iucv_cpuid = -1;
}
iucv_debug(1, "exiting");
return 0;
}
/* Allocate handler entry */
- new_handler = (handler *)kmalloc(sizeof(handler), GFP_ATOMIC);
+ new_handler = kmalloc(sizeof(handler), GFP_ATOMIC);
if (new_handler == NULL) {
printk(KERN_WARNING "%s: storage allocation for new handler "
"failed.\n", __FUNCTION__);
}
max_connections = iucv_query_maxconn();
- iucv_pathid_table = kmalloc(max_connections * sizeof(handler *),
- GFP_ATOMIC);
+ iucv_pathid_table = kcalloc(max_connections, sizeof(handler *),
+ GFP_ATOMIC);
if (iucv_pathid_table == NULL) {
printk(KERN_WARNING "%s: iucv_pathid_table storage "
"allocation failed\n", __FUNCTION__);
kfree(new_handler);
return NULL;
}
- memset (iucv_pathid_table, 0, max_connections * sizeof(handler *));
}
memset(new_handler, 0, sizeof (handler));
memcpy(new_handler->id.user_data, pgmname,
sizeof (new_handler->id.userid));
EBC_TOUPPER (new_handler->id.userid,
sizeof (new_handler->id.userid));
-
+
if (pgmmask) {
memcpy (new_handler->id.mask, pgmmask,
sizeof (new_handler->id.mask));
return NULL;
}
- if (declare_flag == 0) {
- rc = iucv_declare_buffer();
- if (rc) {
- char *err = "Unknown";
- iucv_remove_handler(new_handler);
- kfree(new_handler);
- switch(rc) {
- case 0x03:
- err = "Directory error";
- break;
- case 0x0a:
- err = "Invalid length";
- break;
- case 0x13:
- err = "Buffer already exists";
- break;
- case 0x3e:
- err = "Buffer overlap";
- break;
- case 0x5c:
- err = "Paging or storage error";
- break;
- case 0xaa:
- err = "Function not called";
- break;
- }
- printk(KERN_WARNING "%s: iucv_declare_buffer "
- "returned error 0x%02lx (%s)\n", __FUNCTION__, rc,
- err);
- return NULL;
+ rc = iucv_declare_buffer();
+ if (rc) {
+ char *err = "Unknown";
+ iucv_remove_handler(new_handler);
+ kfree(new_handler);
+ switch(rc) {
+ case 0x03:
+ err = "Directory error";
+ break;
+ case 0x0a:
+ err = "Invalid length";
+ break;
+ case 0x13:
+ err = "Buffer already exists";
+ break;
+ case 0x3e:
+ err = "Buffer overlap";
+ break;
+ case 0x5c:
+ err = "Paging or storage error";
+ break;
}
- declare_flag = 1;
+ printk(KERN_WARNING "%s: iucv_declare_buffer "
+ "returned error 0x%02lx (%s)\n", __FUNCTION__, rc, err);
+ return NULL;
}
- if (register_flag == 0) {
+ if (!register_flag) {
/* request the 0x4000 external interrupt */
rc = register_external_interrupt (0x4000, iucv_irq_handler);
if (rc) {
parm->ipflags1 = (__u8)flags1;
b2f0_result = b2f0(ACCEPT, parm);
- if (b2f0_result == 0) {
+ if (!b2f0_result) {
if (msglim)
*msglim = parm->ipmsglim;
if (pgm_data)
memcpy(&local_parm, parm, sizeof(local_parm));
release_param(parm);
parm = &local_parm;
- if (b2f0_result == 0)
+ if (!b2f0_result)
add_pathid_result = __iucv_add_pathid(parm->ippathid, h);
spin_unlock_irqrestore (&iucv_lock, flags);
parm->ipflags1 |= (IPSRCCLS | IPFGMID | IPFGPID);
b2f0_result = b2f0(PURGE, parm);
- if ((b2f0_result == 0) && audit) {
+ if (!b2f0_result && audit) {
memcpy(audit, parm->ipaudit, sizeof(parm->ipaudit));
/* parm->ipaudit has only 3 bytes */
*audit >>= 8;
}
-
+
release_param(parm);
iucv_debug(1, "b2f0_result = %ld", b2f0_result);
static int
iucv_query_generic(int want_maxconn)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iparml_purge *parm = (iparml_purge *)grab_param();
int bufsize, maxconn;
int ccode;
* Call b2f0 and store R0 (max buffer size),
* R1 (max connections) and CC.
*/
- asm volatile (
- "LRA 1,0(%4)\n\t"
- "LR 0,%3\n\t"
- ".long 0xb2f01000\n\t"
- "IPM %0\n\t"
- "SRL %0,28\n\t"
- "ST 0,%1\n\t"
- "ST 1,%2\n\t"
- : "=d" (ccode), "=m" (bufsize), "=m" (maxconn)
- : "d" (QUERY), "a" (parm)
- : "0", "1", "cc"
- );
+ reg0 = QUERY;
+ reg1 = virt_to_phys(parm);
+ asm volatile(
+ " .long 0xb2f01000\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
+ bufsize = reg0;
+ maxconn = reg1;
release_param(parm);
if (ccode)
b2f0_result = b2f0(RECEIVE, parm);
- if (b2f0_result == 0 || b2f0_result == 5) {
+ if (!b2f0_result || b2f0_result == 5) {
if (flags1_out) {
iucv_debug(2, "*flags1_out = %d", *flags1_out);
*flags1_out = (parm->ipflags1 & (~0x07));
b2f0_result = b2f0(RECEIVE, parm);
- if (b2f0_result == 0 || b2f0_result == 5) {
+ if (!b2f0_result || b2f0_result == 5) {
if (flags1_out) {
iucv_debug(2, "*flags1_out = %d", *flags1_out);
*residual_length = abs (buflen - 8);
if (residual_buffer) {
- if (moved == 0)
+ if (!moved)
*residual_buffer = (ulong) buffer;
else
*residual_buffer =
b2f0_result = b2f0(REPLY, parm);
- if ((b2f0_result == 0) || (b2f0_result == 5)) {
+ if ((!b2f0_result) || (b2f0_result == 5)) {
if (ipbfadr2)
*ipbfadr2 = parm->ipbfadr2;
if (ipbfln2f)
b2f0_result = b2f0(REPLY, parm);
- if ((b2f0_result == 0) || (b2f0_result == 5)) {
+ if ((!b2f0_result) || (b2f0_result == 5)) {
if (ipbfadr2)
*ipbfadr2 = parm->ipbfadr2;
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
parm->ipflags1 = (IPNORPY | IPBUFLST | flags1);
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
parm->ipmsgtag = msgtag;
parm->ipflags1 = (IPBUFLST | IPANSLST | flags1);
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
parm->ipflags1 = (IPRMDATA | IPANSLST | flags1);
memcpy(parm->iprmmsg, prmmsg, sizeof(parm->iprmmsg));
b2f0_result = b2f0(SEND, parm);
- if ((b2f0_result == 0) && (msgid))
+ if ((!b2f0_result) && (msgid))
*msgid = parm->ipmsgid;
release_param(parm);
}
void
-iucv_setmask_cpu0 (void *result)
+iucv_setmask_cpuid (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;
__u8 param;
} u;
+ int cpu;
u.param = SetMaskFlag;
- preempt_disable();
- if (smp_processor_id() == 0)
- iucv_setmask_cpu0(&u);
- else
- smp_call_function(iucv_setmask_cpu0, &u, 0, 1);
- preempt_enable();
+ cpu = get_cpu();
+ smp_call_function_on(iucv_setmask_cpuid, &u, 0, 1, iucv_cpuid);
+ put_cpu();
return u.result;
}
* Places the interrupt buffer on a queue and schedules iucv_tasklet_handler().
*/
static void
-iucv_irq_handler(struct pt_regs *regs, __u16 code)
+iucv_irq_handler(__u16 code)
{
iucv_irqdata *irqdata;
irqdata = kmalloc(sizeof(iucv_irqdata), GFP_ATOMIC);
if (!irqdata) {
printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
- irq_exit();
return;
}
temp_buff1[j] &= (h->id.mask)[j];
temp_buff2[j] &= (h->id.mask)[j];
}
-
+
iucv_dumpit("temp_buff1:",
temp_buff1, sizeof(temp_buff1));
iucv_dumpit("temp_buff2",
temp_buff2, sizeof(temp_buff2));
-
- if (memcmp (temp_buff1, temp_buff2, 24) == 0) {
-
+
+ if (!memcmp (temp_buff1, temp_buff2, 24)) {
+
iucv_debug(2,
"found a matching handler");
break;
- }
+ } else
+ h = NULL;
}
spin_unlock_irqrestore (&iucv_lock, flags);
if (h) {
} else
iucv_sever(int_buf->ippathid, no_listener);
break;
-
+
case 0x02: /*connection complete */
if (messagesDisabled) {
iucv_setmask(~0);
} else
iucv_sever(int_buf->ippathid, no_listener);
break;
-
+
case 0x03: /* connection severed */
if (messagesDisabled) {
iucv_setmask(~0);
interrupt->ConnectionSevered(
(iucv_ConnectionSevered *)int_buf,
h->pgm_data);
-
+
else
iucv_sever (int_buf->ippathid, no_listener);
} else
iucv_sever(int_buf->ippathid, no_listener);
break;
-
+
case 0x04: /* connection quiesced */
if (messagesDisabled) {
iucv_setmask(~0);
"ConnectionQuiesced not called");
}
break;
-
+
case 0x05: /* connection resumed */
if (messagesDisabled) {
iucv_setmask(~0);
"ConnectionResumed not called");
}
break;
-
+
case 0x06: /* priority message complete */
case 0x07: /* nonpriority message complete */
if (h) {
"MessageComplete not called");
}
break;
-
+
case 0x08: /* priority message pending */
case 0x09: /* nonpriority message pending */
if (h) {
__FUNCTION__);
break;
} /* end switch */
-
+
iucv_debug(2, "exiting pathid %d, type %02X",
int_buf->ippathid, int_buf->iptype);
return;
}
-module_init(iucv_init);
+subsys_initcall(iucv_init);
module_exit(iucv_exit);
/**
#if 0
EXPORT_SYMBOL (iucv_reply);
EXPORT_SYMBOL (iucv_reply_array);
-EXPORT_SYMBOL (iucv_reply_prmmsg);
EXPORT_SYMBOL (iucv_resume);
#endif
+EXPORT_SYMBOL (iucv_reply_prmmsg);
EXPORT_SYMBOL (iucv_send);
-#if 0
EXPORT_SYMBOL (iucv_send2way);
EXPORT_SYMBOL (iucv_send2way_array);
-EXPORT_SYMBOL (iucv_send_array);
EXPORT_SYMBOL (iucv_send2way_prmmsg);
EXPORT_SYMBOL (iucv_send2way_prmmsg_array);
+#if 0
+EXPORT_SYMBOL (iucv_send_array);
EXPORT_SYMBOL (iucv_send_prmmsg);
EXPORT_SYMBOL (iucv_setmask);
#endif