1 /*
2 * SN Platform FetchOp Support
3 *
4 * This driver exports the SN fetchop facility to user processes.
5 * Fetchops are atomic memory operations that are implemented in the
6 * memory controller on SGI SN hardware.
7 */
8
9 /*
10 * Copyright (C) 1999,2001-2003 Silicon Graphics, Inc. All rights
11 * reserved.
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of version 2 of the GNU General Public License
15 * as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it would be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * Further, this software is distributed without any warranty that it is
22 * free of the rightful claim of any third person regarding infringement
23 * or the like. Any license provided herein, whether implied or
24 * otherwise, applies only to this software file. Patent licenses, if
25 * any, provided herein do not apply to combinations of this program with
26 * other software, or any other product whatsoever.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA
31 * 02111-1307, USA.
32 *
33 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
34 * Mountain View, CA 94043, or:
35 *
36 * http://www.sgi.com
37 *
38 * For further information regarding this notice, see:
39 *
40 * http://oss.sgi.com/projects/GenInfo/NoticeExplan
41 */
42
43
44 #include <linux/types.h>
45 #include <linux/kernel.h>
46 #include <linux/module.h>
47 #include <linux/init.h>
48 #include <linux/errno.h>
49 #include <linux/devfs_fs_kernel.h>
50 #include <linux/miscdevice.h>
51 #include <linux/spinlock.h>
52 #include <linux/mm.h>
53 #include <linux/proc_fs.h>
54 #include <linux/vmalloc.h>
55 #include <linux/bitops.h>
56 #include <linux/efi.h>
57 #include <asm/system.h>
58 #include <asm/pgtable.h>
59 #include <asm/machvec.h>
60 #include <asm/sn/sgi.h>
61 #include <asm/sn/addrs.h>
62 #include <asm/sn/arch.h>
63 #include <asm/sn/fetchop.h>
64 #include <asm/sn/sn_cpuid.h>
65
66
67 #define DRIVER_ID_STR "SGI Fetchop Device Driver"
68 #define REVISION "1.03"
69
70
71 #define MSPEC_TO_NID(maddr) nasid_to_cnodeid(NASID_GET(maddr))
72
73
74 static int fetchop_mmap(struct file *file, struct vm_area_struct *vma);
75 static void fetchop_open(struct vm_area_struct *vma);
76 static void fetchop_close(struct vm_area_struct *vma);
77
78 static struct file_operations fetchop_fops = {
79 owner: THIS_MODULE,
80 mmap: fetchop_mmap,
81 };
82
83 static struct miscdevice fetchop_miscdev = {
84 MISC_DYNAMIC_MINOR,
85 "fetchop",
86 &fetchop_fops
87 };
88
89 static struct vm_operations_struct fetchop_vm_ops = {
90 open: fetchop_open,
91 close: fetchop_close,
92 };
93
94 /*
95 * There is one of these structs per node. It is used to manage the fetchop
96 * space that is available on the node. Current assumption is that there is
97 * only 1 fetchop block of memory per node.
98 */
99 struct node_fetchops {
100 long maddr; /* MSPEC address of start of fetchops. */
101 int count; /* Total number of fetchop pages. */
102 atomic_t free; /* Number of pages currently free. */
103 unsigned long bits[1]; /* Bitmap for managing pages. */
104 };
105
106
107 /*
108 * One of these structures is allocated when a fetchop region is mmaped. The
109 * structure is pointed to by the vma->vm_private_data field in the vma struct.
110 * This structure is used to record the addresses of the fetchop pages.
111 */
112 struct vma_data {
113 int count; /* Number of pages allocated. */
114 atomic_t refcnt; /* Number of vmas sharing the data. */
115 unsigned long maddr[1]; /* Array of MSPEC addresses. */
116 };
117
118
119 /*
120 * Fetchop statistics.
121 */
122 struct fetchop_stats {
123 unsigned long map_count; /* Number of active mmap's */
124 unsigned long pages_in_use; /* Number of fetchop pages in use */
125 unsigned long pages_total; /* Total number of fetchop pages */
126 };
127
128 static struct fetchop_stats fetchop_stats;
129 static struct node_fetchops *node_fetchops[MAX_COMPACT_NODES];
130 static spinlock_t fetchop_lock = SPIN_LOCK_UNLOCKED;
131
132 /*
133 * NOTE: This is included here simply because the kernel doesn't have
134 * a generally acceptable UC memory allocator. See PV: 896479 for
135 * more details. --cw
136 *
137 * efi_memmap_walk_uc
138 *
139 * This function walks the EFI memory map and calls 'callback' once
140 * for each EFI memory descriptor that has memory that marked as only
141 * EFI_MEMORY_UC.
142 */
143 static void
efi_memmap_walk_uc(efi_freemem_callback_t callback,void * arg)144 efi_memmap_walk_uc (efi_freemem_callback_t callback, void *arg)
145 {
146 void *efi_map_start, *efi_map_end, *p;
147 efi_memory_desc_t *md;
148 u64 efi_desc_size, start, end;
149
150 efi_map_start = __va(ia64_boot_param->efi_memmap);
151 efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size;
152 efi_desc_size = ia64_boot_param->efi_memdesc_size;
153
154 for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
155 md = p;
156 if (md->attribute == EFI_MEMORY_UC) {
157 start = PAGE_ALIGN(md->phys_addr);
158 end = PAGE_ALIGN((md->phys_addr+(md->num_pages << EFI_PAGE_SHIFT)) & PAGE_MASK);
159 if ((*callback)(start, end, arg) < 0)
160 return;
161 }
162 }
163 }
164
165
166 /*
167 * fetchop_initialize_page
168 *
169 * Initial a page that is about to be used for fetchops.
170 * All fetchop variables in the page are set to 0.
171 *
172 */
173 static void
fetchop_initialize_page(unsigned long maddr)174 fetchop_initialize_page(unsigned long maddr)
175 {
176 unsigned long p, pe;
177
178 for (p=FETCHOP_KADDR_TO_MSPEC_ADDR(maddr), pe=p+PAGE_SIZE; p<pe; p+=FETCHOP_VAR_SIZE)
179 FETCHOP_STORE_OP(p,FETCHOP_STORE, 0);
180 }
181
182
183 /*
184 * fetchop_alloc_page
185 *
186 * Allocate 1 fetchop page. Allocates on the requested node. If no
187 * fetchops are available on the requested node, roundrobin starting
188 * with higher nodes,
189 */
190 static unsigned long
fetchop_alloc_page(int nid)191 fetchop_alloc_page(int nid)
192 {
193 int i, bit;
194 struct node_fetchops *fops;
195 unsigned long maddr;
196
197 if (nid < 0 || nid >= numnodes)
198 nid = numa_node_id();
199 for (i=0; i<numnodes; i++) {
200 fops = node_fetchops[nid];
201 while (fops && (bit = find_first_zero_bit(fops->bits, fops->count)) < fops->count) {
202 if (test_and_set_bit(bit, fops->bits) == 0) {
203 atomic_dec(&node_fetchops[nid]->free);
204 maddr = fops->maddr + (bit<<PAGE_SHIFT);
205 fetchop_initialize_page(maddr);
206 return maddr;
207 }
208 }
209 nid = (nid+1 < numnodes) ? nid+1 : 0;
210 }
211 return 0;
212
213 }
214
215
216 /*
217 * fetchop_free_pages
218 *
219 * Free all fetchop pages that are linked to a vma struct.
220 */
221 static void
fetchop_free_page(unsigned long maddr)222 fetchop_free_page(unsigned long maddr)
223 {
224 int nid, bit;
225
226 nid = MSPEC_TO_NID(maddr);
227 bit = (maddr - node_fetchops[nid]->maddr) >> PAGE_SHIFT;
228 clear_bit(bit, node_fetchops[nid]->bits);
229 atomic_inc(&node_fetchops[nid]->free);
230 }
231
232 static void
fetchop_free_pages(struct vma_data * vdata)233 fetchop_free_pages(struct vma_data *vdata)
234 {
235 int i;
236
237 for (i=0; i<vdata->count; i++)
238 fetchop_free_page(vdata->maddr[i]);
239 }
240
241
242 /*
243 * fetchop_update_stats
244 *
245 * Update statistics of the number of fetchop mappings & pages.
246 * If creating a new mapping, ensure that we don't exceed the maximum allowed
247 * number of fetchop pages.
248 */
249 static int
fetchop_update_stats(int mmap,long count)250 fetchop_update_stats(int mmap, long count)
251 {
252 int ret = 0;
253
254 spin_lock(&fetchop_lock);
255 if (count > 0 && fetchop_stats.pages_in_use + count > fetchop_stats.pages_total) {
256 ret = -1;
257 } else {
258 fetchop_stats.map_count += mmap;
259 fetchop_stats.pages_in_use += count;
260 }
261 spin_unlock(&fetchop_lock);
262
263 return ret;
264 }
265
266
267 /*
268 * fetchop_mmap
269 *
270 * Called when mmaping the device. Creates fetchop pages and map them
271 * to user space.
272 */
273 static int
fetchop_mmap(struct file * file,struct vm_area_struct * vma)274 fetchop_mmap(struct file *file, struct vm_area_struct *vma)
275 {
276 unsigned long vm_start;
277 unsigned long maddr;
278 int pages;
279 struct vma_data *vdata;
280
281 if (vma->vm_pgoff != 0)
282 return -EINVAL;
283
284 if ((vma->vm_flags&VM_WRITE) == 0)
285 return -EPERM;
286
287 pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
288 if (fetchop_update_stats(1, pages) < 0)
289 return -ENOSPC;
290
291 if (!(vdata=vmalloc(sizeof(struct vma_data)+(pages-1)*sizeof(long)))) {
292 fetchop_update_stats(-1, -pages);
293 return -ENOMEM;
294 }
295
296 vdata->count = 0;
297 vdata->refcnt = ATOMIC_INIT(1);
298 vma->vm_private_data = vdata;
299
300 vma->vm_flags |= (VM_IO | VM_SHM | VM_LOCKED | VM_NONCACHED);
301 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
302 vma->vm_ops = &fetchop_vm_ops;
303 vm_start = vma->vm_start;
304
305 while (vm_start < vma->vm_end) {
306 maddr = fetchop_alloc_page(numa_node_id());
307 if (maddr == 0)
308 BUG();
309 vdata->maddr[vdata->count++] = maddr;
310
311
312 if (remap_page_range(vm_start, __pa(maddr), PAGE_SIZE, vma->vm_page_prot)) {
313 fetchop_free_pages(vma->vm_private_data);
314 vfree(vdata);
315 fetchop_update_stats(-1, -pages);
316 return -EAGAIN;
317 }
318 vm_start += PAGE_SIZE;
319 }
320
321 return 0;
322 }
323
324 /*
325 * fetchop_open
326 *
327 * Called when a device mapping is created by a means other than mmap
328 * (via fork, etc.). Increments the reference count on the underlying
329 * fetchop data so it is not freed prematurely.
330 */
331 static void
fetchop_open(struct vm_area_struct * vma)332 fetchop_open(struct vm_area_struct *vma)
333 {
334 struct vma_data *vdata;
335
336 vdata = vma->vm_private_data;
337 if (vdata && vdata->count) {
338 atomic_inc(&vdata->refcnt);
339 }
340 }
341
342 /*
343 * fetchop_close
344 *
345 * Called when unmapping a device mapping. Frees all fetchop pages
346 * belonging to the vma.
347 */
348 static void
fetchop_close(struct vm_area_struct * vma)349 fetchop_close(struct vm_area_struct *vma)
350 {
351 struct vma_data *vdata;
352
353 vdata = vma->vm_private_data;
354 if (vdata && vdata->count && !atomic_dec(&vdata->refcnt)) {
355 fetchop_free_pages(vdata);
356 fetchop_update_stats(-1, -vdata->count);
357 vfree(vdata);
358 }
359 }
360
361 #ifdef CONFIG_PROC_FS
362
363 static struct proc_dir_entry *proc_fetchop;
364
365 /*
366 * fetchop_read_proc
367 *
368 * Implements /proc/fetchop. Return statistics about fetchops.
369 */
370 static int
fetchop_read_proc(char * page,char ** start,off_t off,int count,int * eof,void * data)371 fetchop_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
372 {
373 struct node_fetchops *fops;
374 int len = 0, nid;
375
376 len += sprintf(page + len, "mappings : %lu\n", fetchop_stats.map_count);
377 len += sprintf(page + len, "current fetchop pages : %lu\n", fetchop_stats.pages_in_use);
378 len += sprintf(page + len, "maximum fetchop pages : %lu\n", fetchop_stats.pages_total);
379
380 len += sprintf(page + len, "%4s %7s %7s\n", "node", "total", "free");
381 for (nid = 0; nid < numnodes; nid++) {
382 fops = node_fetchops[nid];
383 len += sprintf(page + len, "%4d %7d %7d\n", nid, fops ? fops->count : 0, fops ? atomic_read(&fops->free) : 0);
384 }
385
386 if (len <= off+count) *eof = 1;
387 *start = page + off;
388 len -= off;
389 if (len>count) len = count;
390 if (len<0) len = 0;
391 return len;
392 }
393
394 static int
fetchop_write_proc(struct file * file,const char * userbuf,unsigned long count,void * data)395 fetchop_write_proc (struct file *file, const char *userbuf, unsigned long count, void *data)
396 {
397 char buf[80];
398
399 if (copy_from_user(buf, userbuf, count < sizeof(buf) ? count : sizeof(buf)))
400 return -EFAULT;
401
402 return count;
403 }
404 #endif /* CONFIG_PROC_FS */
405
406 /*
407 * fetchop_build_memmap,
408 *
409 * Called at boot time to build a map of pages that can be used for
410 * fetchops.
411 */
412 static int __init
fetchop_build_memmap(unsigned long start,unsigned long end,void * arg)413 fetchop_build_memmap(unsigned long start, unsigned long end, void *arg)
414 {
415 struct node_fetchops *fops;
416 long count, bytes;
417
418 count = (end - start) >> PAGE_SHIFT;
419 bytes = sizeof(struct node_fetchops) + count/8;
420 fops = vmalloc(bytes);
421 memset(fops, 0, bytes);
422 fops->maddr = FETCHOP_KADDR_TO_MSPEC_ADDR(start);
423 fops->count = count;
424 atomic_add(count, &fops->free);
425 fetchop_stats.pages_total += count;
426 node_fetchops[MSPEC_TO_NID(start)] = fops;
427
428 sn_flush_all_caches((long)__va(start), end - start);
429
430 return 0;
431 }
432
433
434
435 /*
436 * fetchop_init
437 *
438 * Called at boot time to initialize the fetchop facility.
439 */
440 static int __init
fetchop_init(void)441 fetchop_init(void)
442 {
443 int ret;
444 devfs_handle_t hnd;
445
446 if (!ia64_platform_is("sn2"))
447 return -ENODEV;
448
449 #ifdef CONFIG_DEVFS_FS
450 if (!devfs_register(NULL, FETCHOP_BASENAME, DEVFS_FL_AUTO_DEVNUM,
451 0, 0, S_IFCHR | S_IRUGO | S_IWUGO, &fetchop_fops, NULL)) {
452 printk(KERN_ERR "%s: failed to register device\n", DRIVER_ID_STR);
453 return -ENODEV;
454 }
455 #endif
456
457 if ((ret = misc_register(&fetchop_miscdev))) {
458 printk(KERN_ERR "%s: failed to register device\n", DRIVER_ID_STR);
459 return ret;
460 }
461 printk(KERN_DEBUG "%s: registered misc-device with minor %d\n", DRIVER_ID_STR, fetchop_miscdev.minor);
462
463 if ((proc_fetchop = create_proc_entry(FETCHOP_BASENAME, 0644, NULL)) == NULL) {
464 printk(KERN_ERR "%s: unable to create proc entry", DRIVER_ID_STR);
465 devfs_unregister(hnd);
466 return -EINVAL;
467 }
468
469 #ifdef CONFIG_PROC_FS
470 if ((proc_fetchop = create_proc_entry(FETCHOP_BASENAME, 0644, NULL)) == NULL) {
471 printk(KERN_ERR "%s: unable to create proc entry", DRIVER_ID_STR);
472 devfs_unregister(hnd);
473 return -EINVAL;
474 }
475 proc_fetchop->read_proc = fetchop_read_proc;
476 proc_fetchop->write_proc = fetchop_write_proc;
477 #endif /* CONFIG_PROC_FS */
478
479 efi_memmap_walk_uc(fetchop_build_memmap, 0);
480 printk(KERN_INFO "%s: v%s\n", DRIVER_ID_STR, REVISION);
481
482 return 0;
483 }
484
485
486
487 /*-----------------------------------------------------------------------------
488 * KERNEL APIs
489 * Note: right now, these APIs return a full page of fetchops. If these
490 * interfaces are used often for tasks which do not require a full page of
491 * fetchops, new APIs should be added to suballocate out of a single page.
492 */
493
494 unsigned long
fetchop_kalloc_page(int nid)495 fetchop_kalloc_page(int nid)
496 {
497 if (fetchop_update_stats(1, 1) < 0)
498 return 0;
499 return fetchop_alloc_page(nid);
500 }
501 EXPORT_SYMBOL(fetchop_kalloc_page);
502
503
504 void
fetchop_kfree_page(unsigned long maddr)505 fetchop_kfree_page(unsigned long maddr)
506 {
507 fetchop_free_page(maddr);
508 fetchop_update_stats(-1, -1);
509 }
510 EXPORT_SYMBOL(fetchop_kfree_page);
511
512 module_init(fetchop_init);
513
514 MODULE_AUTHOR("Silicon Graphics Inc.");
515 MODULE_DESCRIPTION("Driver for SGI SN 'fetchop' atomic memory operations");
516 MODULE_LICENSE("GPL");
517