Jimi Xenidis | 9d67028 | 2011-09-29 10:55:12 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * ICSWX and ACOP Management |
| 3 | * |
| 4 | * Copyright (C) 2011 Anton Blanchard, IBM Corp. <anton@samba.org> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License |
| 8 | * as published by the Free Software Foundation; either version |
| 9 | * 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #include <linux/sched.h> |
| 14 | #include <linux/kernel.h> |
| 15 | #include <linux/errno.h> |
| 16 | #include <linux/types.h> |
| 17 | #include <linux/mm.h> |
| 18 | #include <linux/spinlock.h> |
| 19 | #include <linux/module.h> |
| 20 | #include "icswx.h" |
| 21 | |
| 22 | /* |
| 23 | * The processor and its L2 cache cause the icswx instruction to |
| 24 | * generate a COP_REQ transaction on PowerBus. The transaction has no |
| 25 | * address, and the processor does not perform an MMU access to |
| 26 | * authenticate the transaction. The command portion of the PowerBus |
| 27 | * COP_REQ transaction includes the LPAR_ID (LPID) and the coprocessor |
| 28 | * Process ID (PID), which the coprocessor compares to the authorized |
| 29 | * LPID and PID held in the coprocessor, to determine if the process |
| 30 | * is authorized to generate the transaction. The data of the COP_REQ |
| 31 | * transaction is 128-byte or less in size and is placed in cacheable |
| 32 | * memory on a 128-byte cache line boundary. |
| 33 | * |
| 34 | * The task to use a coprocessor should use use_cop() to mark the use |
| 35 | * of the Coprocessor Type (CT) and context switching. On a server |
| 36 | * class processor, the PID register is used only for coprocessor |
| 37 | * management + * and so a coprocessor PID is allocated before |
| 38 | * executing icswx + * instruction. Drop_cop() is used to free the |
| 39 | * coprocessor PID. |
| 40 | * |
| 41 | * Example: |
| 42 | * Host Fabric Interface (HFI) is a PowerPC network coprocessor. |
| 43 | * Each HFI have multiple windows. Each HFI window serves as a |
| 44 | * network device sending to and receiving from HFI network. |
| 45 | * HFI immediate send function uses icswx instruction. The immediate |
| 46 | * send function allows small (single cache-line) packets be sent |
| 47 | * without using the regular HFI send FIFO and doorbell, which are |
| 48 | * much slower than immediate send. |
| 49 | * |
| 50 | * For each task intending to use HFI immediate send, the HFI driver |
| 51 | * calls use_cop() to obtain a coprocessor PID for the task. |
| 52 | * The HFI driver then allocate a free HFI window and save the |
| 53 | * coprocessor PID to the HFI window to allow the task to use the |
| 54 | * HFI window. |
| 55 | * |
| 56 | * The HFI driver repeatedly creates immediate send packets and |
| 57 | * issues icswx instruction to send data through the HFI window. |
| 58 | * The HFI compares the coprocessor PID in the CPU PID register |
| 59 | * to the PID held in the HFI window to determine if the transaction |
| 60 | * is allowed. |
| 61 | * |
| 62 | * When the task to release the HFI window, the HFI driver calls |
| 63 | * drop_cop() to release the coprocessor PID. |
| 64 | */ |
| 65 | |
| 66 | void switch_cop(struct mm_struct *next) |
| 67 | { |
| 68 | #ifdef CONFIG_ICSWX_PID |
| 69 | mtspr(SPRN_PID, next->context.cop_pid); |
| 70 | #endif |
| 71 | mtspr(SPRN_ACOP, next->context.acop); |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Start using a coprocessor. |
| 76 | * @acop: mask of coprocessor to be used. |
| 77 | * @mm: The mm the coprocessor to associate with. Most likely current mm. |
| 78 | * |
| 79 | * Return a positive PID if successful. Negative errno otherwise. |
| 80 | * The returned PID will be fed to the coprocessor to determine if an |
| 81 | * icswx transaction is authenticated. |
| 82 | */ |
| 83 | int use_cop(unsigned long acop, struct mm_struct *mm) |
| 84 | { |
| 85 | int ret; |
| 86 | |
| 87 | if (!cpu_has_feature(CPU_FTR_ICSWX)) |
| 88 | return -ENODEV; |
| 89 | |
| 90 | if (!mm || !acop) |
| 91 | return -EINVAL; |
| 92 | |
| 93 | /* The page_table_lock ensures mm_users won't change under us */ |
| 94 | spin_lock(&mm->page_table_lock); |
| 95 | spin_lock(mm->context.cop_lockp); |
| 96 | |
| 97 | ret = get_cop_pid(mm); |
| 98 | if (ret < 0) |
| 99 | goto out; |
| 100 | |
| 101 | /* update acop */ |
| 102 | mm->context.acop |= acop; |
| 103 | |
| 104 | sync_cop(mm); |
| 105 | |
| 106 | /* |
| 107 | * If this is a threaded process then there might be other threads |
| 108 | * running. We need to send an IPI to force them to pick up any |
| 109 | * change in PID and ACOP. |
| 110 | */ |
| 111 | if (atomic_read(&mm->mm_users) > 1) |
| 112 | smp_call_function(sync_cop, mm, 1); |
| 113 | |
| 114 | out: |
| 115 | spin_unlock(mm->context.cop_lockp); |
| 116 | spin_unlock(&mm->page_table_lock); |
| 117 | |
| 118 | return ret; |
| 119 | } |
| 120 | EXPORT_SYMBOL_GPL(use_cop); |
| 121 | |
| 122 | /** |
| 123 | * Stop using a coprocessor. |
| 124 | * @acop: mask of coprocessor to be stopped. |
| 125 | * @mm: The mm the coprocessor associated with. |
| 126 | */ |
| 127 | void drop_cop(unsigned long acop, struct mm_struct *mm) |
| 128 | { |
| 129 | int free_pid; |
| 130 | |
| 131 | if (!cpu_has_feature(CPU_FTR_ICSWX)) |
| 132 | return; |
| 133 | |
| 134 | if (WARN_ON_ONCE(!mm)) |
| 135 | return; |
| 136 | |
| 137 | /* The page_table_lock ensures mm_users won't change under us */ |
| 138 | spin_lock(&mm->page_table_lock); |
| 139 | spin_lock(mm->context.cop_lockp); |
| 140 | |
| 141 | mm->context.acop &= ~acop; |
| 142 | |
| 143 | free_pid = disable_cop_pid(mm); |
| 144 | sync_cop(mm); |
| 145 | |
| 146 | /* |
| 147 | * If this is a threaded process then there might be other threads |
| 148 | * running. We need to send an IPI to force them to pick up any |
| 149 | * change in PID and ACOP. |
| 150 | */ |
| 151 | if (atomic_read(&mm->mm_users) > 1) |
| 152 | smp_call_function(sync_cop, mm, 1); |
| 153 | |
| 154 | if (free_pid != COP_PID_NONE) |
| 155 | free_cop_pid(free_pid); |
| 156 | |
| 157 | spin_unlock(mm->context.cop_lockp); |
| 158 | spin_unlock(&mm->page_table_lock); |
| 159 | } |
| 160 | EXPORT_SYMBOL_GPL(drop_cop); |