📄 ap_bus.c
字号:
if (ap_dev->queue_count > 0) *flags |= 1; break; case AP_RESPONSE_NO_PENDING_REPLY: if (status.queue_empty) { /* The card shouldn't forget requests but who knows. */ atomic_sub(ap_dev->queue_count, &ap_poll_requests); ap_dev->queue_count = 0; list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); ap_dev->requestq_count += ap_dev->pendingq_count; ap_dev->pendingq_count = 0; } else *flags |= 2; break; default: return -ENODEV; } return 0;}/** * Send messages from the request queue to an AP device. * @ap_dev: pointer to the AP device * @flags: pointer to control flags, bit 2^0 is set if another poll is * required, bit 2^1 is set if the poll timer needs to get armed * Returns 0 if the device is still present, -ENODEV if not. */static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags){ struct ap_queue_status status; struct ap_message *ap_msg; if (ap_dev->requestq_count <= 0 || ap_dev->queue_count >= ap_dev->queue_depth) return 0; /* Start the next request on the queue. */ ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); status = __ap_send(ap_dev->qid, ap_msg->psmid, ap_msg->message, ap_msg->length); switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_inc(&ap_poll_requests); ap_increase_queue_count(ap_dev); list_move_tail(&ap_msg->list, &ap_dev->pendingq); ap_dev->requestq_count--; ap_dev->pendingq_count++; if (ap_dev->queue_count < ap_dev->queue_depth && ap_dev->requestq_count > 0) *flags |= 1; *flags |= 2; break; case AP_RESPONSE_Q_FULL: case AP_RESPONSE_RESET_IN_PROGRESS: *flags |= 2; break; case AP_RESPONSE_MESSAGE_TOO_BIG: return -EINVAL; default: return -ENODEV; } return 0;}/** * Poll AP device for pending replies and send new messages. If either * ap_poll_read or ap_poll_write returns -ENODEV unregister the device. * @ap_dev: pointer to the bus device * @flags: pointer to control flags, bit 2^0 is set if another poll is * required, bit 2^1 is set if the poll timer needs to get armed * Returns 0. */static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags){ int rc; rc = ap_poll_read(ap_dev, flags); if (rc) return rc; return ap_poll_write(ap_dev, flags);}/** * Queue a message to a device. * @ap_dev: pointer to the AP device * @ap_msg: the message to be queued */static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg){ struct ap_queue_status status; if (list_empty(&ap_dev->requestq) && ap_dev->queue_count < ap_dev->queue_depth) { status = __ap_send(ap_dev->qid, ap_msg->psmid, ap_msg->message, ap_msg->length); switch (status.response_code) { case AP_RESPONSE_NORMAL: list_add_tail(&ap_msg->list, &ap_dev->pendingq); atomic_inc(&ap_poll_requests); ap_dev->pendingq_count++; ap_increase_queue_count(ap_dev); ap_dev->total_request_count++; break; case AP_RESPONSE_Q_FULL: case AP_RESPONSE_RESET_IN_PROGRESS: list_add_tail(&ap_msg->list, &ap_dev->requestq); ap_dev->requestq_count++; ap_dev->total_request_count++; return -EBUSY; case AP_RESPONSE_MESSAGE_TOO_BIG: ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); return -EINVAL; default: /* Device is gone. */ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); return -ENODEV; } } else { list_add_tail(&ap_msg->list, &ap_dev->requestq); ap_dev->requestq_count++; ap_dev->total_request_count++; return -EBUSY; } ap_schedule_poll_timer(); return 0;}void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg){ unsigned long flags; int rc; spin_lock_bh(&ap_dev->lock); if (!ap_dev->unregistered) { /* Make room on the queue by polling for finished requests. */ rc = ap_poll_queue(ap_dev, &flags); if (!rc) rc = __ap_queue_message(ap_dev, ap_msg); if (!rc) wake_up(&ap_poll_wait); if (rc == -ENODEV) ap_dev->unregistered = 1; } else { ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); rc = -ENODEV; } spin_unlock_bh(&ap_dev->lock); if (rc == -ENODEV) device_unregister(&ap_dev->device);}EXPORT_SYMBOL(ap_queue_message);/** * Cancel a crypto request. This is done by removing the request * from the devive pendingq or requestq queue. Note that the * request stays on the AP queue. When it finishes the message * reply will be discarded because the psmid can't be found. * @ap_dev: AP device that has the message queued * @ap_msg: the message that is to be removed */void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg){ struct ap_message *tmp; spin_lock_bh(&ap_dev->lock); if (!list_empty(&ap_msg->list)) { list_for_each_entry(tmp, &ap_dev->pendingq, list) if (tmp->psmid == ap_msg->psmid) { ap_dev->pendingq_count--; goto found; } ap_dev->requestq_count--; found: list_del_init(&ap_msg->list); } spin_unlock_bh(&ap_dev->lock);}EXPORT_SYMBOL(ap_cancel_message);/** * AP receive polling for finished AP requests */static void ap_poll_timeout(unsigned long unused){ tasklet_schedule(&ap_tasklet);}/** * Reset a not responding AP device and move all requests from the * pending queue to the request queue. */static void ap_reset(struct ap_device *ap_dev){ int rc; ap_dev->reset = AP_RESET_IGNORE; atomic_sub(ap_dev->queue_count, &ap_poll_requests); ap_dev->queue_count = 0; list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); ap_dev->requestq_count += ap_dev->pendingq_count; ap_dev->pendingq_count = 0; rc = ap_init_queue(ap_dev->qid); if (rc == -ENODEV) ap_dev->unregistered = 1;}/** * Poll all AP devices on the bus in a round robin fashion. Continue * polling until bit 2^0 of the control flags is not set. If bit 2^1 * of the control flags has been set arm the poll timer. */static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags){ spin_lock(&ap_dev->lock); if (!ap_dev->unregistered) { if (ap_poll_queue(ap_dev, flags)) ap_dev->unregistered = 1; if (ap_dev->reset == AP_RESET_DO) ap_reset(ap_dev); } spin_unlock(&ap_dev->lock); return 0;}static void ap_poll_all(unsigned long dummy){ unsigned long flags; struct ap_device *ap_dev; do { flags = 0; spin_lock(&ap_device_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { __ap_poll_all(ap_dev, &flags); } spin_unlock(&ap_device_lock); } while (flags & 1); if (flags & 2) ap_schedule_poll_timer();}/** * AP bus poll thread. The purpose of this thread is to poll for * finished requests in a loop if there is a "free" cpu - that is * a cpu that doesn't have anything better to do. The polling stops * as soon as there is another task or if all messages have been * delivered. */static int ap_poll_thread(void *data){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; int requests; struct ap_device *ap_dev; set_user_nice(current, 19); while (1) { if (need_resched()) { schedule(); continue; } add_wait_queue(&ap_poll_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; requests = atomic_read(&ap_poll_requests); if (requests <= 0) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); flags = 0; spin_lock_bh(&ap_device_lock); list_for_each_entry(ap_dev, &ap_device_list, list) { __ap_poll_all(ap_dev, &flags); } spin_unlock_bh(&ap_device_lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); return 0;}static int ap_poll_thread_start(void){ int rc; mutex_lock(&ap_poll_thread_mutex); if (!ap_poll_kthread) { ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; if (rc) ap_poll_kthread = NULL; } else rc = 0; mutex_unlock(&ap_poll_thread_mutex); return rc;}static void ap_poll_thread_stop(void){ mutex_lock(&ap_poll_thread_mutex); if (ap_poll_kthread) { kthread_stop(ap_poll_kthread); ap_poll_kthread = NULL; } mutex_unlock(&ap_poll_thread_mutex);}/** * Handling of request timeouts */static void ap_request_timeout(unsigned long data){ struct ap_device *ap_dev = (struct ap_device *) data; if (ap_dev->reset == AP_RESET_ARMED) ap_dev->reset = AP_RESET_DO;}static void ap_reset_domain(void){ int i; if (ap_domain_index != -1) for (i = 0; i < AP_DEVICES; i++) ap_reset_queue(AP_MKQID(i, ap_domain_index));}static void ap_reset_all(void){ int i, j; for (i = 0; i < AP_DOMAINS; i++) for (j = 0; j < AP_DEVICES; j++) ap_reset_queue(AP_MKQID(j, i));}static struct reset_call ap_reset_call = { .fn = ap_reset_all,};/** * The module initialization code. */int __init ap_module_init(void){ int rc, i; if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) { printk(KERN_WARNING "Invalid param: domain = %d. " " Not loading.\n", ap_domain_index); return -EINVAL; } if (ap_instructions_available() != 0) { printk(KERN_WARNING "AP instructions not installed.\n"); return -ENODEV; } register_reset_call(&ap_reset_call); /* Create /sys/bus/ap. */ rc = bus_register(&ap_bus_type); if (rc) goto out; for (i = 0; ap_bus_attrs[i]; i++) { rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]); if (rc) goto out_bus; } /* Create /sys/devices/ap. */ ap_root_device = s390_root_dev_register("ap"); rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; if (rc) goto out_bus; ap_work_queue = create_singlethread_workqueue("kapwork"); if (!ap_work_queue) { rc = -ENOMEM; goto out_root; } if (ap_select_domain() == 0) ap_scan_bus(NULL); /* Setup the ap bus rescan timer. */ init_timer(&ap_config_timer); ap_config_timer.function = ap_config_timeout; ap_config_timer.data = 0; ap_config_timer.expires = jiffies + ap_config_time * HZ; add_timer(&ap_config_timer); /* Start the low priority AP bus poll thread. */ if (ap_thread_flag) { rc = ap_poll_thread_start(); if (rc) goto out_work; } return 0;out_work: del_timer_sync(&ap_config_timer); del_timer_sync(&ap_poll_timer); destroy_workqueue(ap_work_queue);out_root: s390_root_dev_unregister(ap_root_device);out_bus: while (i--) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type);out: unregister_reset_call(&ap_reset_call); return rc;}static int __ap_match_all(struct device *dev, void *data){ return 1;}/** * The module termination code */void ap_module_exit(void){ int i; struct device *dev; ap_reset_domain(); ap_poll_thread_stop(); del_timer_sync(&ap_config_timer); del_timer_sync(&ap_poll_timer); destroy_workqueue(ap_work_queue); tasklet_kill(&ap_tasklet); s390_root_dev_unregister(ap_root_device); while ((dev = bus_find_device(&ap_bus_type, NULL, NULL, __ap_match_all))) { device_unregister(dev); put_device(dev); } for (i = 0; ap_bus_attrs[i]; i++) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); unregister_reset_call(&ap_reset_call);}#ifndef CONFIG_ZCRYPT_MONOLITHICmodule_init(ap_module_init);module_exit(ap_module_exit);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -