Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

DCA: Add Direct Cache Access driver

Direct Cache Access (DCA) is a method for warming the CPU cache before data
is used, with the intent of lessening the impact of cache misses. This
patch adds a manager and interface for matching up client requests for DCA
services with devices that offer DCA services.

In order to use DCA, a module must do bus writes with the appropriate tag
bits set to trigger a cache read for a specific CPU. However, different
CPUs and chipsets can require different sets of tag bits, and the methods
for determining the correct bits may be simple hardcoding or may be a
hardware specific magic incantation. This interface is a way for DCA
clients to find the correct tag bits for the targeted CPU without needing
to know the specifics.

[Dave Miller] use DEFINE_SPINLOCK()

Signed-off-by: Shannon Nelson <shannon.nelson@intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Shannon Nelson and committed by
Linus Torvalds
7589670f 3e037454

+351
+2
drivers/Kconfig
··· 84 84 85 85 source "drivers/dma/Kconfig" 86 86 87 + source "drivers/dca/Kconfig" 88 + 87 89 source "drivers/auxdisplay/Kconfig" 88 90 89 91 source "drivers/kvm/Kconfig"
+1
drivers/Makefile
··· 85 85 obj-$(CONFIG_SUPERH) += sh/ 86 86 obj-$(CONFIG_GENERIC_TIME) += clocksource/ 87 87 obj-$(CONFIG_DMA_ENGINE) += dma/ 88 + obj-$(CONFIG_DCA) += dca/ 88 89 obj-$(CONFIG_HID) += hid/ 89 90 obj-$(CONFIG_PPC_PS3) += ps3/ 90 91 obj-$(CONFIG_OF) += of/
+11
drivers/dca/Kconfig
··· 1 + # 2 + # DCA server configuration 3 + # 4 + 5 + config DCA 6 + tristate "DCA support for clients and providers" 7 + default m 8 + help 9 + This is a server to help modules that want to use Direct Cache 10 + Access to find DCA providers that will supply correct CPU tags. 11 +
+2
drivers/dca/Makefile
··· 1 + obj-$(CONFIG_DCA) += dca.o 2 + dca-objs := dca-core.o dca-sysfs.o
+200
drivers/dca/dca-core.c
··· 1 + /* 2 + * Copyright(c) 2007 Intel Corporation. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify it 5 + * under the terms of the GNU General Public License as published by the Free 6 + * Software Foundation; either version 2 of the License, or (at your option) 7 + * any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, but WITHOUT 10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 + * more details. 13 + * 14 + * You should have received a copy of the GNU General Public License along with 15 + * this program; if not, write to the Free Software Foundation, Inc., 59 16 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 + * 18 + * The full GNU General Public License is included in this distribution in the 19 + * file called COPYING. 20 + */ 21 + 22 + /* 23 + * This driver supports an interface for DCA clients and providers to meet. 24 + */ 25 + 26 + #include <linux/kernel.h> 27 + #include <linux/notifier.h> 28 + #include <linux/device.h> 29 + #include <linux/dca.h> 30 + 31 + MODULE_LICENSE("GPL"); 32 + 33 + /* For now we're assuming a single, global, DCA provider for the system. */ 34 + 35 + static DEFINE_SPINLOCK(dca_lock); 36 + 37 + static struct dca_provider *global_dca = NULL; 38 + 39 + /** 40 + * dca_add_requester - add a dca client to the list 41 + * @dev - the device that wants dca service 42 + */ 43 + int dca_add_requester(struct device *dev) 44 + { 45 + int err, slot; 46 + 47 + if (!global_dca) 48 + return -ENODEV; 49 + 50 + spin_lock(&dca_lock); 51 + slot = global_dca->ops->add_requester(global_dca, dev); 52 + spin_unlock(&dca_lock); 53 + if (slot < 0) 54 + return slot; 55 + 56 + err = dca_sysfs_add_req(global_dca, dev, slot); 57 + if (err) { 58 + spin_lock(&dca_lock); 59 + global_dca->ops->remove_requester(global_dca, dev); 60 + spin_unlock(&dca_lock); 61 + return err; 62 + } 63 + 64 + return 0; 65 + } 66 + EXPORT_SYMBOL_GPL(dca_add_requester); 67 + 68 + /** 69 + * dca_remove_requester - remove a dca client from the list 70 + * @dev - the device that wants dca service 71 + */ 72 + int dca_remove_requester(struct device *dev) 73 + { 74 + int slot; 75 + if (!global_dca) 76 + return -ENODEV; 77 + 78 + spin_lock(&dca_lock); 79 + slot = global_dca->ops->remove_requester(global_dca, dev); 80 + spin_unlock(&dca_lock); 81 + if (slot < 0) 82 + return slot; 83 + 84 + dca_sysfs_remove_req(global_dca, slot); 85 + return 0; 86 + } 87 + EXPORT_SYMBOL_GPL(dca_remove_requester); 88 + 89 + /** 90 + * dca_get_tag - return the dca tag for the given cpu 91 + * @cpu - the cpuid as returned by get_cpu() 92 + */ 93 + u8 dca_get_tag(int cpu) 94 + { 95 + if (!global_dca) 96 + return -ENODEV; 97 + return global_dca->ops->get_tag(global_dca, cpu); 98 + } 99 + EXPORT_SYMBOL_GPL(dca_get_tag); 100 + 101 + /** 102 + * alloc_dca_provider - get data struct for describing a dca provider 103 + * @ops - pointer to struct of dca operation function pointers 104 + * @priv_size - size of extra mem to be added for provider's needs 105 + */ 106 + struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size) 107 + { 108 + struct dca_provider *dca; 109 + int alloc_size; 110 + 111 + alloc_size = (sizeof(*dca) + priv_size); 112 + dca = kzalloc(alloc_size, GFP_KERNEL); 113 + if (!dca) 114 + return NULL; 115 + dca->ops = ops; 116 + 117 + return dca; 118 + } 119 + EXPORT_SYMBOL_GPL(alloc_dca_provider); 120 + 121 + /** 122 + * free_dca_provider - release the dca provider data struct 123 + * @ops - pointer to struct of dca operation function pointers 124 + * @priv_size - size of extra mem to be added for provider's needs 125 + */ 126 + void free_dca_provider(struct dca_provider *dca) 127 + { 128 + kfree(dca); 129 + } 130 + EXPORT_SYMBOL_GPL(free_dca_provider); 131 + 132 + static BLOCKING_NOTIFIER_HEAD(dca_provider_chain); 133 + 134 + /** 135 + * register_dca_provider - register a dca provider 136 + * @dca - struct created by alloc_dca_provider() 137 + * @dev - device providing dca services 138 + */ 139 + int register_dca_provider(struct dca_provider *dca, struct device *dev) 140 + { 141 + int err; 142 + 143 + if (global_dca) 144 + return -EEXIST; 145 + err = dca_sysfs_add_provider(dca, dev); 146 + if (err) 147 + return err; 148 + global_dca = dca; 149 + blocking_notifier_call_chain(&dca_provider_chain, 150 + DCA_PROVIDER_ADD, NULL); 151 + return 0; 152 + } 153 + EXPORT_SYMBOL_GPL(register_dca_provider); 154 + 155 + /** 156 + * unregister_dca_provider - remove a dca provider 157 + * @dca - struct created by alloc_dca_provider() 158 + */ 159 + void unregister_dca_provider(struct dca_provider *dca) 160 + { 161 + if (!global_dca) 162 + return; 163 + blocking_notifier_call_chain(&dca_provider_chain, 164 + DCA_PROVIDER_REMOVE, NULL); 165 + global_dca = NULL; 166 + dca_sysfs_remove_provider(dca); 167 + } 168 + EXPORT_SYMBOL_GPL(unregister_dca_provider); 169 + 170 + /** 171 + * dca_register_notify - register a client's notifier callback 172 + */ 173 + void dca_register_notify(struct notifier_block *nb) 174 + { 175 + blocking_notifier_chain_register(&dca_provider_chain, nb); 176 + } 177 + EXPORT_SYMBOL_GPL(dca_register_notify); 178 + 179 + /** 180 + * dca_unregister_notify - remove a client's notifier callback 181 + */ 182 + void dca_unregister_notify(struct notifier_block *nb) 183 + { 184 + blocking_notifier_chain_unregister(&dca_provider_chain, nb); 185 + } 186 + EXPORT_SYMBOL_GPL(dca_unregister_notify); 187 + 188 + static int __init dca_init(void) 189 + { 190 + return dca_sysfs_init(); 191 + } 192 + 193 + static void __exit dca_exit(void) 194 + { 195 + dca_sysfs_exit(); 196 + } 197 + 198 + module_init(dca_init); 199 + module_exit(dca_exit); 200 +
+88
drivers/dca/dca-sysfs.c
··· 1 + #include <linux/kernel.h> 2 + #include <linux/spinlock.h> 3 + #include <linux/device.h> 4 + #include <linux/idr.h> 5 + #include <linux/kdev_t.h> 6 + #include <linux/err.h> 7 + #include <linux/dca.h> 8 + 9 + static struct class *dca_class; 10 + static struct idr dca_idr; 11 + static spinlock_t dca_idr_lock; 12 + 13 + int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot) 14 + { 15 + struct class_device *cd; 16 + 17 + cd = class_device_create(dca_class, dca->cd, MKDEV(0, slot + 1), 18 + dev, "requester%d", slot); 19 + if (IS_ERR(cd)) 20 + return PTR_ERR(cd); 21 + return 0; 22 + } 23 + 24 + void dca_sysfs_remove_req(struct dca_provider *dca, int slot) 25 + { 26 + class_device_destroy(dca_class, MKDEV(0, slot + 1)); 27 + } 28 + 29 + int dca_sysfs_add_provider(struct dca_provider *dca, struct device *dev) 30 + { 31 + struct class_device *cd; 32 + int err = 0; 33 + 34 + idr_try_again: 35 + if (!idr_pre_get(&dca_idr, GFP_KERNEL)) 36 + return -ENOMEM; 37 + spin_lock(&dca_idr_lock); 38 + err = idr_get_new(&dca_idr, dca, &dca->id); 39 + spin_unlock(&dca_idr_lock); 40 + switch (err) { 41 + case 0: 42 + break; 43 + case -EAGAIN: 44 + goto idr_try_again; 45 + default: 46 + return err; 47 + } 48 + 49 + cd = class_device_create(dca_class, NULL, MKDEV(0, 0), 50 + dev, "dca%d", dca->id); 51 + if (IS_ERR(cd)) { 52 + spin_lock(&dca_idr_lock); 53 + idr_remove(&dca_idr, dca->id); 54 + spin_unlock(&dca_idr_lock); 55 + return PTR_ERR(cd); 56 + } 57 + dca->cd = cd; 58 + return 0; 59 + } 60 + 61 + void dca_sysfs_remove_provider(struct dca_provider *dca) 62 + { 63 + class_device_unregister(dca->cd); 64 + dca->cd = NULL; 65 + spin_lock(&dca_idr_lock); 66 + idr_remove(&dca_idr, dca->id); 67 + spin_unlock(&dca_idr_lock); 68 + } 69 + 70 + int __init dca_sysfs_init(void) 71 + { 72 + idr_init(&dca_idr); 73 + spin_lock_init(&dca_idr_lock); 74 + 75 + dca_class = class_create(THIS_MODULE, "dca"); 76 + if (IS_ERR(dca_class)) { 77 + idr_destroy(&dca_idr); 78 + return PTR_ERR(dca_class); 79 + } 80 + return 0; 81 + } 82 + 83 + void __exit dca_sysfs_exit(void) 84 + { 85 + class_destroy(dca_class); 86 + idr_destroy(&dca_idr); 87 + } 88 +
+47
include/linux/dca.h
··· 1 + #ifndef DCA_H 2 + #define DCA_H 3 + /* DCA Provider API */ 4 + 5 + /* DCA Notifier Interface */ 6 + void dca_register_notify(struct notifier_block *nb); 7 + void dca_unregister_notify(struct notifier_block *nb); 8 + 9 + #define DCA_PROVIDER_ADD 0x0001 10 + #define DCA_PROVIDER_REMOVE 0x0002 11 + 12 + struct dca_provider { 13 + struct dca_ops *ops; 14 + struct class_device *cd; 15 + int id; 16 + }; 17 + 18 + struct dca_ops { 19 + int (*add_requester) (struct dca_provider *, struct device *); 20 + int (*remove_requester) (struct dca_provider *, struct device *); 21 + u8 (*get_tag) (struct dca_provider *, int cpu); 22 + }; 23 + 24 + struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size); 25 + void free_dca_provider(struct dca_provider *dca); 26 + int register_dca_provider(struct dca_provider *dca, struct device *dev); 27 + void unregister_dca_provider(struct dca_provider *dca); 28 + 29 + static inline void *dca_priv(struct dca_provider *dca) 30 + { 31 + return (void *)dca + sizeof(struct dca_provider); 32 + } 33 + 34 + /* Requester API */ 35 + int dca_add_requester(struct device *dev); 36 + int dca_remove_requester(struct device *dev); 37 + u8 dca_get_tag(int cpu); 38 + 39 + /* internal stuff */ 40 + int __init dca_sysfs_init(void); 41 + void __exit dca_sysfs_exit(void); 42 + int dca_sysfs_add_provider(struct dca_provider *dca, struct device *dev); 43 + void dca_sysfs_remove_provider(struct dca_provider *dca); 44 + int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot); 45 + void dca_sysfs_remove_req(struct dca_provider *dca, int slot); 46 + 47 + #endif /* DCA_H */