📄 iucv.c
字号:
parm->db.ipbfadr1 = (u32)(addr_t) buffer; parm->db.ipbfln1f = (u32) size; parm->db.ippathid = path->pathid; parm->db.ipflags1 = flags | IUCV_IPNORPY; parm->db.iptrgcls = msg->class; parm->db.ipsrccls = srccls; parm->db.ipmsgtag = msg->tag; } rc = iucv_call_b2f0(IUCV_SEND, parm); if (!rc) msg->id = parm->db.ipmsgid; local_bh_enable(); return rc;}EXPORT_SYMBOL(iucv_message_send);/** * iucv_message_send2way * @path: address of iucv path structure * @msg: address of iucv msg structure * @flags: how the message is sent and the reply is received * (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST) * @srccls: source class of message * @buffer: address of send buffer or address of struct iucv_array * @size: length of send buffer * @ansbuf: address of answer buffer or address of struct iucv_array * @asize: size of reply buffer * * This function transmits data to another application. Data to be * transmitted is in a buffer. The receiver of the send is expected to * reply to the message and a buffer is provided into which IUCV moves * the reply to this message. * * Returns the result from the CP IUCV call. */int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, u8 flags, u32 srccls, void *buffer, size_t size, void *answer, size_t asize, size_t *residual){ union iucv_param *parm; int rc; local_bh_disable(); parm = iucv_param[smp_processor_id()]; memset(parm, 0, sizeof(union iucv_param)); if (flags & IUCV_IPRMDATA) { parm->dpl.ippathid = path->pathid; parm->dpl.ipflags1 = path->flags; /* priority message */ parm->dpl.iptrgcls = msg->class; parm->dpl.ipsrccls = srccls; parm->dpl.ipmsgtag = msg->tag; parm->dpl.ipbfadr2 = (u32)(addr_t) answer; parm->dpl.ipbfln2f = (u32) asize; memcpy(parm->dpl.iprmmsg, buffer, 8); } else { parm->db.ippathid = path->pathid; parm->db.ipflags1 = path->flags; /* priority message */ parm->db.iptrgcls = msg->class; parm->db.ipsrccls = srccls; parm->db.ipmsgtag = msg->tag; parm->db.ipbfadr1 = (u32)(addr_t) buffer; parm->db.ipbfln1f = (u32) size; parm->db.ipbfadr2 = (u32)(addr_t) answer; parm->db.ipbfln2f = (u32) asize; } rc = iucv_call_b2f0(IUCV_SEND, parm); if (!rc) msg->id = parm->db.ipmsgid; local_bh_enable(); return rc;}EXPORT_SYMBOL(iucv_message_send2way);/** * iucv_path_pending * @data: Pointer to external interrupt buffer * * Process connection pending work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_path_pending { u16 ippathid; u8 ipflags1; u8 iptype; u16 ipmsglim; u16 res1; u8 ipvmid[8]; u8 ipuser[16]; u32 res3; u8 ippollfg; u8 res4[3];} __attribute__ ((packed));static void iucv_path_pending(struct iucv_irq_data *data){ struct iucv_path_pending *ipp = (void *) data; struct iucv_handler *handler; struct iucv_path *path; char *error; BUG_ON(iucv_path_table[ipp->ippathid]); /* New pathid, handler found. Create a new path struct. */ error = iucv_error_no_memory; path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC); if (!path) goto out_sever; path->pathid = ipp->ippathid; iucv_path_table[path->pathid] = path; EBCASC(ipp->ipvmid, 8); /* Call registered handler until one is found that wants the path. */ list_for_each_entry(handler, &iucv_handler_list, list) { if (!handler->path_pending) continue; /* * Add path to handler to allow a call to iucv_path_sever * inside the path_pending function. If the handler returns * an error remove the path from the handler again. */ list_add(&path->list, &handler->paths); path->handler = handler; if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser)) return; list_del(&path->list); path->handler = NULL; } /* No handler wanted the path. */ iucv_path_table[path->pathid] = NULL; iucv_path_free(path); error = iucv_error_no_listener;out_sever: iucv_sever_pathid(ipp->ippathid, error);}/** * iucv_path_complete * @data: Pointer to external interrupt buffer * * Process connection complete work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_path_complete { u16 ippathid; u8 ipflags1; u8 iptype; u16 ipmsglim; u16 res1; u8 res2[8]; u8 ipuser[16]; u32 res3; u8 ippollfg; u8 res4[3];} __attribute__ ((packed));static void iucv_path_complete(struct iucv_irq_data *data){ struct iucv_path_complete *ipc = (void *) data; struct iucv_path *path = iucv_path_table[ipc->ippathid]; if (path && path->handler && path->handler->path_complete) path->handler->path_complete(path, ipc->ipuser);}/** * iucv_path_severed * @data: Pointer to external interrupt buffer * * Process connection severed work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_path_severed { u16 ippathid; u8 res1; u8 iptype; u32 res2; u8 res3[8]; u8 ipuser[16]; u32 res4; u8 ippollfg; u8 res5[3];} __attribute__ ((packed));static void iucv_path_severed(struct iucv_irq_data *data){ struct iucv_path_severed *ips = (void *) data; struct iucv_path *path = iucv_path_table[ips->ippathid]; if (!path || !path->handler) /* Already severed */ return; if (path->handler->path_severed) path->handler->path_severed(path, ips->ipuser); else { iucv_sever_pathid(path->pathid, NULL); iucv_path_table[path->pathid] = NULL; list_del_init(&path->list); iucv_path_free(path); }}/** * iucv_path_quiesced * @data: Pointer to external interrupt buffer * * Process connection quiesced work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_path_quiesced { u16 ippathid; u8 res1; u8 iptype; u32 res2; u8 res3[8]; u8 ipuser[16]; u32 res4; u8 ippollfg; u8 res5[3];} __attribute__ ((packed));static void iucv_path_quiesced(struct iucv_irq_data *data){ struct iucv_path_quiesced *ipq = (void *) data; struct iucv_path *path = iucv_path_table[ipq->ippathid]; if (path && path->handler && path->handler->path_quiesced) path->handler->path_quiesced(path, ipq->ipuser);}/** * iucv_path_resumed * @data: Pointer to external interrupt buffer * * Process connection resumed work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_path_resumed { u16 ippathid; u8 res1; u8 iptype; u32 res2; u8 res3[8]; u8 ipuser[16]; u32 res4; u8 ippollfg; u8 res5[3];} __attribute__ ((packed));static void iucv_path_resumed(struct iucv_irq_data *data){ struct iucv_path_resumed *ipr = (void *) data; struct iucv_path *path = iucv_path_table[ipr->ippathid]; if (path && path->handler && path->handler->path_resumed) path->handler->path_resumed(path, ipr->ipuser);}/** * iucv_message_complete * @data: Pointer to external interrupt buffer * * Process message complete work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_message_complete { u16 ippathid; u8 ipflags1; u8 iptype; u32 ipmsgid; u32 ipaudit; u8 iprmmsg[8]; u32 ipsrccls; u32 ipmsgtag; u32 res; u32 ipbfln2f; u8 ippollfg; u8 res2[3];} __attribute__ ((packed));static void iucv_message_complete(struct iucv_irq_data *data){ struct iucv_message_complete *imc = (void *) data; struct iucv_path *path = iucv_path_table[imc->ippathid]; struct iucv_message msg; if (path && path->handler && path->handler->message_complete) { msg.flags = imc->ipflags1; msg.id = imc->ipmsgid; msg.audit = imc->ipaudit; memcpy(msg.rmmsg, imc->iprmmsg, 8); msg.class = imc->ipsrccls; msg.tag = imc->ipmsgtag; msg.length = imc->ipbfln2f; path->handler->message_complete(path, &msg); }}/** * iucv_message_pending * @data: Pointer to external interrupt buffer * * Process message pending work item. Called from tasklet while holding * iucv_table_lock. */struct iucv_message_pending { u16 ippathid; u8 ipflags1; u8 iptype; u32 ipmsgid; u32 iptrgcls; union { u32 iprmmsg1_u32; u8 iprmmsg1[4]; } ln1msg1; union { u32 ipbfln1f; u8 iprmmsg2[4]; } ln1msg2; u32 res1[3]; u32 ipbfln2f; u8 ippollfg; u8 res2[3];} __attribute__ ((packed));static void iucv_message_pending(struct iucv_irq_data *data){ struct iucv_message_pending *imp = (void *) data; struct iucv_path *path = iucv_path_table[imp->ippathid]; struct iucv_message msg; if (path && path->handler && path->handler->message_pending) { msg.flags = imp->ipflags1; msg.id = imp->ipmsgid; msg.class = imp->iptrgcls; if (imp->ipflags1 & IUCV_IPRMDATA) { memcpy(msg.rmmsg, imp->ln1msg1.iprmmsg1, 8); msg.length = 8; } else msg.length = imp->ln1msg2.ipbfln1f; msg.reply_size = imp->ipbfln2f; path->handler->message_pending(path, &msg); }}/** * iucv_tasklet_fn: * * This tasklet loops over the queue of irq buffers created by * iucv_external_interrupt, calls the appropriate action handler * and then frees the buffer. */static void iucv_tasklet_fn(unsigned long ignored){ typedef void iucv_irq_fn(struct iucv_irq_data *); static iucv_irq_fn *irq_fn[] = { [0x02] = iucv_path_complete, [0x03] = iucv_path_severed, [0x04] = iucv_path_quiesced, [0x05] = iucv_path_resumed, [0x06] = iucv_message_complete, [0x07] = iucv_message_complete, [0x08] = iucv_message_pending, [0x09] = iucv_message_pending, }; struct list_head task_queue = LIST_HEAD_INIT(task_queue); struct iucv_irq_list *p, *n; /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ if (!spin_trylock(&iucv_table_lock)) { tasklet_schedule(&iucv_tasklet); return; } iucv_active_cpu = smp_processor_id(); spin_lock_irq(&iucv_queue_lock); list_splice_init(&iucv_task_queue, &task_queue); spin_unlock_irq(&iucv_queue_lock); list_for_each_entry_safe(p, n, &task_queue, list) { list_del_init(&p->list); irq_fn[p->data.iptype](&p->data); kfree(p); } iucv_active_cpu = -1; spin_unlock(&iucv_table_lock);}/** * iucv_work_fn: * * This work function loops over the queue of path pending irq blocks * created by iucv_external_interrupt, calls the appropriate action * handler and then frees the buffer. */static void iucv_work_fn(struct work_struct *work){ typedef void iucv_irq_fn(struct iucv_irq_data *); struct list_head work_queue = LIST_HEAD_INIT(work_queue); struct iucv_irq_list *p, *n; /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ spin_lock_bh(&iucv_table_lock); iucv_active_cpu = smp_processor_id(); spin_lock_irq(&iucv_queue_lock); list_splice_init(&iucv_work_queue, &work_queue); spin_unlock_irq(&iucv_queue_lock); iucv_cleanup_queue(); list_for_each_entry_safe(p, n, &work_queue, list) { list_del_init(&p->list); iucv_path_pending(&p->data); kfree(p); } iucv_active_cpu = -1; spin_unlock_bh(&iucv_table_lock);}/** * iucv_external_interrupt * @code: irq code * * Handles external interrupts coming in from CP. * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn(). */static void iucv_external_interrupt(u16 code){ struct iucv_irq_data *p; struct iucv_irq_list *work; p = iucv_irq_data[smp_processor_id()]; if (p->ippathid >= iucv_max_pathid) { printk(KERN_WARNING "iucv_do_int: Got interrupt with " "pathid %d > max_connections (%ld)\n", p->ippathid, iucv_max_pathid - 1); iucv_sever_pathid(p->ippathid, iucv_error_no_listener); return; } if (p->iptype < 0x01 || p->iptype > 0x09) { printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n"); return; } work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC); if (!work) { printk(KERN_WARNING "iucv_external_interrupt: out of memory\n"); return; } memcpy(&work->data, p, sizeof(work->data)); spin_lock(&iucv_queue_lock); if (p->iptype == 0x01) { /* Path pending interrupt. */ list_add_tail(&work->list, &iucv_work_queue); schedule_work(&iucv_work); } else { /* The other interrupts. */ list_add_tail(&work->list, &iucv_task_queue); tasklet_schedule(&iucv_tasklet); } spin_unlock(&iucv_queue_lock);}/** * iucv_init * * Allocates and initializes various data structures. */static int __init iucv_init(void){ int rc; int cpu; if (!MACHINE_IS_VM) { rc = -EPROTONOSUPPORT; goto out; } rc = iucv_query_maxconn(); if (rc) goto out; rc = register_external_interrupt(0x4000, iucv_external_interrupt); if (rc) goto out; rc = bus_register(&iucv_bus); if (rc) goto out_int; iucv_root = s390_root_dev_register("iucv"); if (IS_ERR(iucv_root)) { rc = PTR_ERR(iucv_root); goto out_bus; } for_each_online_cpu(cpu) { /* Note: GFP_DMA used to get memory below 2G */ iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data), GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); if (!iucv_irq_data[cpu]) { rc = -ENOMEM; goto out_free; } /* Allocate parameter blocks. */ iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param), GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); if (!iucv_param[cpu]) { rc = -ENOMEM; goto out_free; } } register_hotcpu_notifier(&iucv_cpu_notifier); ASCEBC(iucv_error_no_listener, 16); ASCEBC(iucv_error_no_memory, 16); ASCEBC(iucv_error_pathid, 16); iucv_available = 1; return 0;out_free: for_each_possible_cpu(cpu) { kfree(iucv_param[cpu]); iucv_param[cpu] = NULL; kfree(iucv_irq_data[cpu]); iucv_irq_data[cpu] = NULL; } s390_root_dev_unregister(iucv_root);out_bus: bus_unregister(&iucv_bus);out_int: unregister_external_interrupt(0x4000, iucv_external_interrupt);out: return rc;}/** * iucv_exit * * Frees everything allocated from iucv_init. */static void __exit iucv_exit(void){ struct iucv_irq_list *p, *n; int cpu; spin_lock_irq(&iucv_queue_lock); list_for_each_entry_safe(p, n, &iucv_task_queue, list) kfree(p); list_for_each_entry_safe(p, n, &iucv_work_queue, list) kfree(p); spin_unlock_irq(&iucv_queue_lock); unregister_hotcpu_notifier(&iucv_cpu_notifier); for_each_possible_cpu(cpu) { kfree(iucv_param[cpu]); iucv_param[cpu] = NULL; kfree(iucv_irq_data[cpu]); iucv_irq_data[cpu] = NULL; } s390_root_dev_unregister(iucv_root); bus_unregister(&iucv_bus); unregister_external_interrupt(0x4000, iucv_external_interrupt);}subsys_initcall(iucv_init);module_exit(iucv_exit);MODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com)");MODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -