diff options
Diffstat (limited to 'core/opal-msg.c')
-rw-r--r-- | core/opal-msg.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/core/opal-msg.c b/core/opal-msg.c new file mode 100644 index 0000000..f033b76 --- /dev/null +++ b/core/opal-msg.c @@ -0,0 +1,167 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <skiboot.h> +#include <opal-msg.h> +#include <lock.h> + +#define OPAL_MAX_MSGS (OPAL_MSG_TYPE_MAX + OPAL_MAX_ASYNC_COMP - 1) +#define OPAL_MSG_PREFIX "opalmsg: " + + +struct opal_msg_entry { + struct list_node link; + void (*consumed)(void *data); + void *data; + struct opal_msg msg; +}; + +static LIST_HEAD(msg_free_list); +static LIST_HEAD(msg_pending_list); + +static struct lock opal_msg_lock = LOCK_UNLOCKED; + +int _opal_queue_msg(enum OpalMessageType msg_type, void *data, + void (*consumed)(void *data), size_t num_params, + const u64 *params) +{ + struct opal_msg_entry *entry; + + lock(&opal_msg_lock); + + entry = list_pop(&msg_free_list, struct opal_msg_entry, link); + if (!entry) { + prerror(OPAL_MSG_PREFIX "No available node in the free list, allocating\n"); + entry = zalloc(sizeof(struct opal_msg_entry)); + if (!entry) { + prerror(OPAL_MSG_PREFIX "Allocation failed\n"); + unlock(&opal_msg_lock); + return OPAL_RESOURCE; + } + } + + entry->consumed = consumed; + entry->data = data; + entry->msg.msg_type = msg_type; + + if (num_params > ARRAY_SIZE(entry->msg.params)) { + prerror(OPAL_MSG_PREFIX "Discarding extra parameters\n"); + num_params = ARRAY_SIZE(entry->msg.params); + } + memcpy(entry->msg.params, params, num_params*sizeof(u64)); + + list_add_tail(&msg_pending_list, &entry->link); + opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, + OPAL_EVENT_MSG_PENDING); + + unlock(&opal_msg_lock); + + return 0; +} + +static int64_t opal_get_msg(uint64_t *buffer, uint64_t size) +{ + struct opal_msg_entry *entry; + void (*callback)(void *data); + void *data; + + if (size < sizeof(struct opal_msg) || !buffer) + return OPAL_PARAMETER; + + lock(&opal_msg_lock); + + entry = list_pop(&msg_pending_list, struct opal_msg_entry, link); + if (!entry) { + unlock(&opal_msg_lock); + return OPAL_RESOURCE; + } + + memcpy(buffer, &entry->msg, sizeof(entry->msg)); + callback = entry->consumed; + data = entry->data; + + list_add(&msg_free_list, &entry->link); + if (list_empty(&msg_pending_list)) + opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0); + + unlock(&opal_msg_lock); + + if (callback) + callback(data); + + return OPAL_SUCCESS; +} +opal_call(OPAL_GET_MSG, opal_get_msg, 2); + +static int64_t opal_check_completion(uint64_t *buffer, uint64_t size, + uint64_t token) +{ + struct opal_msg_entry *entry, *next_entry; + void (*callback)(void *data) = NULL; + int rc = OPAL_BUSY; + void *data = NULL; + + lock(&opal_msg_lock); + list_for_each_safe(&msg_pending_list, entry, next_entry, link) { + if (entry->msg.msg_type == OPAL_MSG_ASYNC_COMP && + entry->msg.params[0] == token) { + list_del(&entry->link); + callback = entry->consumed; + data = entry->data; + list_add(&msg_free_list, &entry->link); + if (list_empty(&msg_pending_list)) + opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, + 0); + rc = OPAL_SUCCESS; + break; + } + } + + if (rc == OPAL_SUCCESS && size >= sizeof(struct opal_msg)) + memcpy(buffer, &entry->msg, sizeof(entry->msg)); + + unlock(&opal_msg_lock); + + if (callback) + callback(data); + + return rc; + +} +opal_call(OPAL_CHECK_ASYNC_COMPLETION, opal_check_completion, 3); + +void opal_init_msg(void) +{ + struct opal_msg_entry *entry; + int i; + + for (i = 0; i < OPAL_MAX_MSGS; i++, entry++) { + entry = zalloc(sizeof(*entry)); + if (!entry) + goto err; + list_add_tail(&msg_free_list, &entry->link); + } + return; + +err: + for (; i > 0; i--) { + entry = list_pop(&msg_free_list, struct opal_msg_entry, link); + if (entry) + free(entry); + } +} + |