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