OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [fetchop.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.