146 lines
		
	
	
	
		
			3.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			146 lines
		
	
	
	
		
			3.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright 2011 Tilera Corporation. All Rights Reserved.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *   This program is free software; you can redistribute it and/or
							 | 
						||
| 
								 | 
							
								 *   modify it under the terms of the GNU General Public License
							 | 
						||
| 
								 | 
							
								 *   as published by the Free Software Foundation, version 2.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *   This program is distributed in the hope that it will be useful, but
							 | 
						||
| 
								 | 
							
								 *   WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
							 | 
						||
| 
								 | 
							
								 *   NON INFRINGEMENT.  See the GNU General Public License for
							 | 
						||
| 
								 | 
							
								 *   more details.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <arch/chip.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <linux/types.h>
							 | 
						||
| 
								 | 
							
								#include <linux/string.h>
							 | 
						||
| 
								 | 
							
								#include <linux/module.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#undef memset
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void *memset(void *s, int c, size_t n)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									uint64_t *out64;
							 | 
						||
| 
								 | 
							
									int n64, to_align64;
							 | 
						||
| 
								 | 
							
									uint64_t v64;
							 | 
						||
| 
								 | 
							
									uint8_t *out8 = s;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Experimentation shows that a trivial tight loop is a win up until
							 | 
						||
| 
								 | 
							
									 * around a size of 20, where writing a word at a time starts to win.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
								#define BYTE_CUTOFF 20
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if BYTE_CUTOFF < 7
							 | 
						||
| 
								 | 
							
									/* This must be at least at least this big, or some code later
							 | 
						||
| 
								 | 
							
									 * on doesn't work.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
								#error "BYTE_CUTOFF is too small"
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (n < BYTE_CUTOFF) {
							 | 
						||
| 
								 | 
							
										/* Strangely, this turns out to be the tightest way to
							 | 
						||
| 
								 | 
							
										 * write this loop.
							 | 
						||
| 
								 | 
							
										 */
							 | 
						||
| 
								 | 
							
										if (n != 0) {
							 | 
						||
| 
								 | 
							
											do {
							 | 
						||
| 
								 | 
							
												/* Strangely, combining these into one line
							 | 
						||
| 
								 | 
							
												 * performs worse.
							 | 
						||
| 
								 | 
							
												 */
							 | 
						||
| 
								 | 
							
												*out8 = c;
							 | 
						||
| 
								 | 
							
												out8++;
							 | 
						||
| 
								 | 
							
											} while (--n != 0);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return s;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Align 'out8'. We know n >= 7 so this won't write past the end. */
							 | 
						||
| 
								 | 
							
									while (((uintptr_t) out8 & 7) != 0) {
							 | 
						||
| 
								 | 
							
										*out8++ = c;
							 | 
						||
| 
								 | 
							
										--n;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Align 'n'. */
							 | 
						||
| 
								 | 
							
									while (n & 7)
							 | 
						||
| 
								 | 
							
										out8[--n] = c;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									out64 = (uint64_t *) out8;
							 | 
						||
| 
								 | 
							
									n64 = n >> 3;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Tile input byte out to 64 bits. */
							 | 
						||
| 
								 | 
							
									/* KLUDGE */
							 | 
						||
| 
								 | 
							
									v64 = 0x0101010101010101ULL * (uint8_t)c;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* This must be at least 8 or the following loop doesn't work. */
							 | 
						||
| 
								 | 
							
								#define CACHE_LINE_SIZE_IN_DOUBLEWORDS (CHIP_L2_LINE_SIZE() / 8)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Determine how many words we need to emit before the 'out32'
							 | 
						||
| 
								 | 
							
									 * pointer becomes aligned modulo the cache line size.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									to_align64 = (-((uintptr_t)out64 >> 3)) &
							 | 
						||
| 
								 | 
							
										(CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Only bother aligning and using wh64 if there is at least
							 | 
						||
| 
								 | 
							
									 * one full cache line to process.  This check also prevents
							 | 
						||
| 
								 | 
							
									 * overrunning the end of the buffer with alignment words.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									if (to_align64 <= n64 - CACHE_LINE_SIZE_IN_DOUBLEWORDS) {
							 | 
						||
| 
								 | 
							
										int lines_left;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* Align out64 mod the cache line size so we can use wh64. */
							 | 
						||
| 
								 | 
							
										n64 -= to_align64;
							 | 
						||
| 
								 | 
							
										for (; to_align64 != 0; to_align64--) {
							 | 
						||
| 
								 | 
							
											*out64 = v64;
							 | 
						||
| 
								 | 
							
											out64++;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* Use unsigned divide to turn this into a right shift. */
							 | 
						||
| 
								 | 
							
										lines_left = (unsigned)n64 / CACHE_LINE_SIZE_IN_DOUBLEWORDS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										do {
							 | 
						||
| 
								 | 
							
											/* Only wh64 a few lines at a time, so we don't
							 | 
						||
| 
								 | 
							
											 * exceed the maximum number of victim lines.
							 | 
						||
| 
								 | 
							
											 */
							 | 
						||
| 
								 | 
							
											int x = ((lines_left < CHIP_MAX_OUTSTANDING_VICTIMS())
							 | 
						||
| 
								 | 
							
												  ? lines_left
							 | 
						||
| 
								 | 
							
												  : CHIP_MAX_OUTSTANDING_VICTIMS());
							 | 
						||
| 
								 | 
							
											uint64_t *wh = out64;
							 | 
						||
| 
								 | 
							
											int i = x;
							 | 
						||
| 
								 | 
							
											int j;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											lines_left -= x;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											do {
							 | 
						||
| 
								 | 
							
												__insn_wh64(wh);
							 | 
						||
| 
								 | 
							
												wh += CACHE_LINE_SIZE_IN_DOUBLEWORDS;
							 | 
						||
| 
								 | 
							
											} while (--i);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for (j = x * (CACHE_LINE_SIZE_IN_DOUBLEWORDS / 4);
							 | 
						||
| 
								 | 
							
											     j != 0; j--) {
							 | 
						||
| 
								 | 
							
												*out64++ = v64;
							 | 
						||
| 
								 | 
							
												*out64++ = v64;
							 | 
						||
| 
								 | 
							
												*out64++ = v64;
							 | 
						||
| 
								 | 
							
												*out64++ = v64;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} while (lines_left != 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* We processed all full lines above, so only this many
							 | 
						||
| 
								 | 
							
										 * words remain to be processed.
							 | 
						||
| 
								 | 
							
										 */
							 | 
						||
| 
								 | 
							
										n64 &= CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Now handle any leftover values. */
							 | 
						||
| 
								 | 
							
									if (n64 != 0) {
							 | 
						||
| 
								 | 
							
										do {
							 | 
						||
| 
								 | 
							
											*out64 = v64;
							 | 
						||
| 
								 | 
							
											out64++;
							 | 
						||
| 
								 | 
							
										} while (--n64 != 0);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return s;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								EXPORT_SYMBOL(memset);
							 |