📄 xpc_channel.c
字号:
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (c) 2004-2006 Silicon Graphics, Inc. All Rights Reserved. *//* * Cross Partition Communication (XPC) channel support. * * This is the part of XPC that manages the channels and * sends/receives messages across them to/from other partitions. * */#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/cache.h>#include <linux/interrupt.h>#include <linux/mutex.h>#include <linux/completion.h>#include <asm/sn/bte.h>#include <asm/sn/sn_sal.h>#include <asm/sn/xpc.h>/* * Guarantee that the kzalloc'd memory is cacheline aligned. */static void *xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base){ /* see if kzalloc will give us cachline aligned memory by default */ *base = kzalloc(size, flags); if (*base == NULL) { return NULL; } if ((u64) *base == L1_CACHE_ALIGN((u64) *base)) { return *base; } kfree(*base); /* nope, we'll have to do it ourselves */ *base = kzalloc(size + L1_CACHE_BYTES, flags); if (*base == NULL) { return NULL; } return (void *) L1_CACHE_ALIGN((u64) *base);}/* * Set up the initial values for the XPartition Communication channels. */static voidxpc_initialize_channels(struct xpc_partition *part, partid_t partid){ int ch_number; struct xpc_channel *ch; for (ch_number = 0; ch_number < part->nchannels; ch_number++) { ch = &part->channels[ch_number]; ch->partid = partid; ch->number = ch_number; ch->flags = XPC_C_DISCONNECTED; ch->local_GP = &part->local_GPs[ch_number]; ch->local_openclose_args = &part->local_openclose_args[ch_number]; atomic_set(&ch->kthreads_assigned, 0); atomic_set(&ch->kthreads_idle, 0); atomic_set(&ch->kthreads_active, 0); atomic_set(&ch->references, 0); atomic_set(&ch->n_to_notify, 0); spin_lock_init(&ch->lock); mutex_init(&ch->msg_to_pull_mutex); init_completion(&ch->wdisconnect_wait); atomic_set(&ch->n_on_msg_allocate_wq, 0); init_waitqueue_head(&ch->msg_allocate_wq); init_waitqueue_head(&ch->idle_wq); }}/* * Setup the infrastructure necessary to support XPartition Communication * between the specified remote partition and the local one. */enum xpc_retvalxpc_setup_infrastructure(struct xpc_partition *part){ int ret, cpuid; struct timer_list *timer; partid_t partid = XPC_PARTID(part); /* * Zero out MOST of the entry for this partition. Only the fields * starting with `nchannels' will be zeroed. The preceding fields must * remain `viable' across partition ups and downs, since they may be * referenced during this memset() operation. */ memset(&part->nchannels, 0, sizeof(struct xpc_partition) - offsetof(struct xpc_partition, nchannels)); /* * Allocate all of the channel structures as a contiguous chunk of * memory. */ part->channels = kzalloc(sizeof(struct xpc_channel) * XPC_NCHANNELS, GFP_KERNEL); if (part->channels == NULL) { dev_err(xpc_chan, "can't get memory for channels\n"); return xpcNoMemory; } part->nchannels = XPC_NCHANNELS; /* allocate all the required GET/PUT values */ part->local_GPs = xpc_kzalloc_cacheline_aligned(XPC_GP_SIZE, GFP_KERNEL, &part->local_GPs_base); if (part->local_GPs == NULL) { kfree(part->channels); part->channels = NULL; dev_err(xpc_chan, "can't get memory for local get/put " "values\n"); return xpcNoMemory; } part->remote_GPs = xpc_kzalloc_cacheline_aligned(XPC_GP_SIZE, GFP_KERNEL, &part->remote_GPs_base); if (part->remote_GPs == NULL) { dev_err(xpc_chan, "can't get memory for remote get/put " "values\n"); kfree(part->local_GPs_base); part->local_GPs = NULL; kfree(part->channels); part->channels = NULL; return xpcNoMemory; } /* allocate all the required open and close args */ part->local_openclose_args = xpc_kzalloc_cacheline_aligned( XPC_OPENCLOSE_ARGS_SIZE, GFP_KERNEL, &part->local_openclose_args_base); if (part->local_openclose_args == NULL) { dev_err(xpc_chan, "can't get memory for local connect args\n"); kfree(part->remote_GPs_base); part->remote_GPs = NULL; kfree(part->local_GPs_base); part->local_GPs = NULL; kfree(part->channels); part->channels = NULL; return xpcNoMemory; } part->remote_openclose_args = xpc_kzalloc_cacheline_aligned( XPC_OPENCLOSE_ARGS_SIZE, GFP_KERNEL, &part->remote_openclose_args_base); if (part->remote_openclose_args == NULL) { dev_err(xpc_chan, "can't get memory for remote connect args\n"); kfree(part->local_openclose_args_base); part->local_openclose_args = NULL; kfree(part->remote_GPs_base); part->remote_GPs = NULL; kfree(part->local_GPs_base); part->local_GPs = NULL; kfree(part->channels); part->channels = NULL; return xpcNoMemory; } xpc_initialize_channels(part, partid); atomic_set(&part->nchannels_active, 0); atomic_set(&part->nchannels_engaged, 0); /* local_IPI_amo were set to 0 by an earlier memset() */ /* Initialize this partitions AMO_t structure */ part->local_IPI_amo_va = xpc_IPI_init(partid); spin_lock_init(&part->IPI_lock); atomic_set(&part->channel_mgr_requests, 1); init_waitqueue_head(&part->channel_mgr_wq); sprintf(part->IPI_owner, "xpc%02d", partid); ret = request_irq(SGI_XPC_NOTIFY, xpc_notify_IRQ_handler, IRQF_SHARED, part->IPI_owner, (void *) (u64) partid); if (ret != 0) { dev_err(xpc_chan, "can't register NOTIFY IRQ handler, " "errno=%d\n", -ret); kfree(part->remote_openclose_args_base); part->remote_openclose_args = NULL; kfree(part->local_openclose_args_base); part->local_openclose_args = NULL; kfree(part->remote_GPs_base); part->remote_GPs = NULL; kfree(part->local_GPs_base); part->local_GPs = NULL; kfree(part->channels); part->channels = NULL; return xpcLackOfResources; } /* Setup a timer to check for dropped IPIs */ timer = &part->dropped_IPI_timer; init_timer(timer); timer->function = (void (*)(unsigned long)) xpc_dropped_IPI_check; timer->data = (unsigned long) part; timer->expires = jiffies + XPC_P_DROPPED_IPI_WAIT; add_timer(timer); /* * With the setting of the partition setup_state to XPC_P_SETUP, we're * declaring that this partition is ready to go. */ part->setup_state = XPC_P_SETUP; /* * Setup the per partition specific variables required by the * remote partition to establish channel connections with us. * * The setting of the magic # indicates that these per partition * specific variables are ready to be used. */ xpc_vars_part[partid].GPs_pa = __pa(part->local_GPs); xpc_vars_part[partid].openclose_args_pa = __pa(part->local_openclose_args); xpc_vars_part[partid].IPI_amo_pa = __pa(part->local_IPI_amo_va); cpuid = raw_smp_processor_id(); /* any CPU in this partition will do */ xpc_vars_part[partid].IPI_nasid = cpuid_to_nasid(cpuid); xpc_vars_part[partid].IPI_phys_cpuid = cpu_physical_id(cpuid); xpc_vars_part[partid].nchannels = part->nchannels; xpc_vars_part[partid].magic = XPC_VP_MAGIC1; return xpcSuccess;}/* * Create a wrapper that hides the underlying mechanism for pulling a cacheline * (or multiple cachelines) from a remote partition. * * src must be a cacheline aligned physical address on the remote partition. * dst must be a cacheline aligned virtual address on this partition. * cnt must be an cacheline sized */static enum xpc_retvalxpc_pull_remote_cachelines(struct xpc_partition *part, void *dst, const void *src, size_t cnt){ bte_result_t bte_ret; DBUG_ON((u64) src != L1_CACHE_ALIGN((u64) src)); DBUG_ON((u64) dst != L1_CACHE_ALIGN((u64) dst)); DBUG_ON(cnt != L1_CACHE_ALIGN(cnt)); if (part->act_state == XPC_P_DEACTIVATING) { return part->reason; } bte_ret = xp_bte_copy((u64) src, (u64) dst, (u64) cnt, (BTE_NORMAL | BTE_WACQUIRE), NULL); if (bte_ret == BTE_SUCCESS) { return xpcSuccess; } dev_dbg(xpc_chan, "xp_bte_copy() from partition %d failed, ret=%d\n", XPC_PARTID(part), bte_ret); return xpc_map_bte_errors(bte_ret);}/* * Pull the remote per partition specific variables from the specified * partition. */enum xpc_retvalxpc_pull_remote_vars_part(struct xpc_partition *part){ u8 buffer[L1_CACHE_BYTES * 2]; struct xpc_vars_part *pulled_entry_cacheline = (struct xpc_vars_part *) L1_CACHE_ALIGN((u64) buffer); struct xpc_vars_part *pulled_entry; u64 remote_entry_cacheline_pa, remote_entry_pa; partid_t partid = XPC_PARTID(part); enum xpc_retval ret; /* pull the cacheline that contains the variables we're interested in */ DBUG_ON(part->remote_vars_part_pa != L1_CACHE_ALIGN(part->remote_vars_part_pa)); DBUG_ON(sizeof(struct xpc_vars_part) != L1_CACHE_BYTES / 2); remote_entry_pa = part->remote_vars_part_pa + sn_partition_id * sizeof(struct xpc_vars_part); remote_entry_cacheline_pa = (remote_entry_pa & ~(L1_CACHE_BYTES - 1)); pulled_entry = (struct xpc_vars_part *) ((u64) pulled_entry_cacheline + (remote_entry_pa & (L1_CACHE_BYTES - 1))); ret = xpc_pull_remote_cachelines(part, pulled_entry_cacheline, (void *) remote_entry_cacheline_pa, L1_CACHE_BYTES); if (ret != xpcSuccess) { dev_dbg(xpc_chan, "failed to pull XPC vars_part from " "partition %d, ret=%d\n", partid, ret); return ret; } /* see if they've been set up yet */ if (pulled_entry->magic != XPC_VP_MAGIC1 && pulled_entry->magic != XPC_VP_MAGIC2) { if (pulled_entry->magic != 0) { dev_dbg(xpc_chan, "partition %d's XPC vars_part for " "partition %d has bad magic value (=0x%lx)\n", partid, sn_partition_id, pulled_entry->magic); return xpcBadMagic; } /* they've not been initialized yet */ return xpcRetry; } if (xpc_vars_part[partid].magic == XPC_VP_MAGIC1) { /* validate the variables */ if (pulled_entry->GPs_pa == 0 || pulled_entry->openclose_args_pa == 0 || pulled_entry->IPI_amo_pa == 0) { dev_err(xpc_chan, "partition %d's XPC vars_part for " "partition %d are not valid\n", partid, sn_partition_id); return xpcInvalidAddress; } /* the variables we imported look to be valid */ part->remote_GPs_pa = pulled_entry->GPs_pa; part->remote_openclose_args_pa = pulled_entry->openclose_args_pa; part->remote_IPI_amo_va = (AMO_t *) __va(pulled_entry->IPI_amo_pa); part->remote_IPI_nasid = pulled_entry->IPI_nasid; part->remote_IPI_phys_cpuid = pulled_entry->IPI_phys_cpuid; if (part->nchannels > pulled_entry->nchannels) { part->nchannels = pulled_entry->nchannels; } /* let the other side know that we've pulled their variables */ xpc_vars_part[partid].magic = XPC_VP_MAGIC2; } if (pulled_entry->magic == XPC_VP_MAGIC1) { return xpcRetry; } return xpcSuccess;}/* * Get the IPI flags and pull the openclose args and/or remote GPs as needed. */static u64xpc_get_IPI_flags(struct xpc_partition *part){ unsigned long irq_flags; u64 IPI_amo; enum xpc_retval ret; /* * See if there are any IPI flags to be handled. */ spin_lock_irqsave(&part->IPI_lock, irq_flags); if ((IPI_amo = part->local_IPI_amo) != 0) { part->local_IPI_amo = 0; } spin_unlock_irqrestore(&part->IPI_lock, irq_flags); if (XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(IPI_amo)) { ret = xpc_pull_remote_cachelines(part, part->remote_openclose_args, (void *) part->remote_openclose_args_pa, XPC_OPENCLOSE_ARGS_SIZE); if (ret != xpcSuccess) { XPC_DEACTIVATE_PARTITION(part, ret); dev_dbg(xpc_chan, "failed to pull openclose args from " "partition %d, ret=%d\n", XPC_PARTID(part), ret); /* don't bother processing IPIs anymore */ IPI_amo = 0; } } if (XPC_ANY_MSG_IPI_FLAGS_SET(IPI_amo)) { ret = xpc_pull_remote_cachelines(part, part->remote_GPs, (void *) part->remote_GPs_pa, XPC_GP_SIZE); if (ret != xpcSuccess) { XPC_DEACTIVATE_PARTITION(part, ret); dev_dbg(xpc_chan, "failed to pull GPs from partition " "%d, ret=%d\n", XPC_PARTID(part), ret); /* don't bother processing IPIs anymore */ IPI_amo = 0; } } return IPI_amo;}/* * Allocate the local message queue and the notify queue. */static enum xpc_retvalxpc_allocate_local_msgqueue(struct xpc_channel *ch){ unsigned long irq_flags; int nentries; size_t nbytes; // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between // >>> iterations of the for-loop, bail if set? // >>> should we impose a minimum #of entries? like 4 or 8? for (nentries = ch->local_nentries; nentries > 0; nentries--) { nbytes = nentries * ch->msg_size; ch->local_msgqueue = xpc_kzalloc_cacheline_aligned(nbytes, GFP_KERNEL, &ch->local_msgqueue_base); if (ch->local_msgqueue == NULL) { continue; } nbytes = nentries * sizeof(struct xpc_notify); ch->notify_queue = kzalloc(nbytes, GFP_KERNEL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -