1221431Sjonathan/* 2221431Sjonathan * CDDL HEADER START 3224651Sjonathan * 4221431Sjonathan * The contents of this file are subject to the terms 5221431Sjonathan * of the Common Development and Distribution License 6221431Sjonathan * (the "License"). You may not use this file except 7221431Sjonathan * in compliance with the License. 8221431Sjonathan * 9221431Sjonathan * You can obtain a copy of the license at 10221431Sjonathan * src/OPENSOLARIS.LICENSE 11221431Sjonathan * or http://www.opensolaris.org/os/licensing. 12221431Sjonathan * See the License for the specific language governing 13221431Sjonathan * permissions and limitations under the License. 14221431Sjonathan * 15221431Sjonathan * When distributing Covered Code, include this CDDL 16221431Sjonathan * HEADER in each file and include the License file at 17221431Sjonathan * usr/src/OPENSOLARIS.LICENSE. If applicable, 18221431Sjonathan * add the following below this CDDL HEADER, with the 19221431Sjonathan * fields enclosed by brackets "[]" replaced with your 20221431Sjonathan * own identifying information: Portions Copyright [yyyy] 21221431Sjonathan * [name of copyright owner] 22221431Sjonathan * 23221431Sjonathan * CDDL HEADER END 24221431Sjonathan */ 25221431Sjonathan 26221431Sjonathan/* 27221431Sjonathan * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28221431Sjonathan * Use is subject to license terms. 29221431Sjonathan */ 30221431Sjonathan 31221431Sjonathan/* 32221431Sjonathan * The "cascade" test case is a multiprocess/multithread batten-passing model 33224651Sjonathan * using lock primitives alone for synchronisation. Threads are arranged in a 34221431Sjonathan * ring. Each thread has two locks of its own on which it blocks, and is able 35224651Sjonathan * to manipulate the two locks belonging to the thread which follows it in the 36224651Sjonathan * ring. 37224651Sjonathan * 38224651Sjonathan * The number of threads (nthreads) is specified by the generic libMicro -P/-T 39224651Sjonathan * options. With nthreads == 1 (the default) the uncontended case can be timed. 40224651Sjonathan * 41224651Sjonathan * The main logic is generic and allows any simple blocking API to be tested. 42224651Sjonathan * The API-specific component is clearly indicated. 43224651Sjonathan */ 44224651Sjonathan 45224651Sjonathan#include <unistd.h> 46224651Sjonathan#include <stdlib.h> 47224651Sjonathan#include <stdio.h> 48224651Sjonathan#include <fcntl.h> 49224651Sjonathan 50224651Sjonathan#include "libmicro.h" 51224651Sjonathan 52224651Sjonathantypedef struct { 53224651Sjonathan int ts_once; 54224651Sjonathan int ts_id; 55224651Sjonathan int ts_us0; /* our lock indices */ 56224651Sjonathan int ts_us1; 57224651Sjonathan int ts_them0; /* their lock indices */ 58224651Sjonathan int ts_them1; 59224651Sjonathan} tsd_t; 60224651Sjonathan 61224651Sjonathanstatic int nthreads; 62224651Sjonathan 63224651Sjonathan/* 64224651Sjonathan * API-specific code BEGINS here 65224651Sjonathan */ 66224651Sjonathan 67224651Sjonathan#define DEFD "/tmp" 68224651Sjonathan 69224651Sjonathanstatic char *optd = DEFD; 70224651Sjonathanstatic int file; 71224651Sjonathanstatic int nlocks; 72224651Sjonathan 73224651Sjonathanint 74224651Sjonathanbenchmark_init() 75224651Sjonathan{ 76224651Sjonathan lm_tsdsize = sizeof (tsd_t); 77224651Sjonathan 78224651Sjonathan (void) sprintf(lm_optstr, "d:"); 79224651Sjonathan 80224651Sjonathan lm_defN = "cscd_fcntl"; 81224651Sjonathan 82224651Sjonathan (void) sprintf(lm_usage, 83224651Sjonathan " [-d directory for temp file (default %s)]\n" 84224651Sjonathan "notes: thread cascade using fcntl region locking\n", 85224651Sjonathan DEFD); 86224651Sjonathan 87224651Sjonathan return (0); 88224651Sjonathan} 89224651Sjonathan 90224651Sjonathanint 91224651Sjonathanbenchmark_optswitch(int opt, char *optarg) 92224651Sjonathan{ 93224651Sjonathan switch (opt) { 94224651Sjonathan case 'd': 95224651Sjonathan optd = optarg; 96224651Sjonathan break; 97224651Sjonathan default: 98224651Sjonathan return (-1); 99224651Sjonathan } 100224651Sjonathan return (0); 101224651Sjonathan} 102224651Sjonathan 103224651Sjonathanint 104224651Sjonathanbenchmark_initrun() 105224651Sjonathan{ 106224651Sjonathan int errors = 0; 107224651Sjonathan char fname[1024]; 108224651Sjonathan 109224651Sjonathan nthreads = lm_optP * lm_optT; 110224651Sjonathan nlocks = nthreads * 2; 111224651Sjonathan 112224651Sjonathan (void) sprintf(fname, "%s/cascade.%ld", optd, getpid()); 113224651Sjonathan 114224651Sjonathan file = open(fname, O_CREAT | O_TRUNC | O_RDWR, 0600); 115224651Sjonathan if (file == -1) { 116224651Sjonathan errors++; 117224651Sjonathan } 118224651Sjonathan 119224651Sjonathan if (unlink(fname)) { 120224651Sjonathan errors++; 121224651Sjonathan } 122224651Sjonathan 123224651Sjonathan if (ftruncate(file, nlocks * 3) == -1) { 124224651Sjonathan errors++; 125224651Sjonathan } 126224651Sjonathan 127224651Sjonathan return (errors); 128224651Sjonathan} 129224651Sjonathan 130224651Sjonathanint 131224651Sjonathanblock(int index) 132224651Sjonathan{ 133224651Sjonathan struct flock fl; 134224651Sjonathan 135224651Sjonathan fl.l_type = F_WRLCK; 136224651Sjonathan fl.l_whence = SEEK_SET; 137224651Sjonathan fl.l_start = index; 138224651Sjonathan fl.l_len = 1; 139224651Sjonathan return (fcntl(file, F_SETLKW, &fl) == -1); 140224651Sjonathan} 141224651Sjonathan 142224651Sjonathanint 143224651Sjonathanunblock(int index) 144224651Sjonathan{ 145224651Sjonathan struct flock fl; 146224651Sjonathan 147224651Sjonathan fl.l_type = F_UNLCK; 148224651Sjonathan fl.l_whence = SEEK_SET; 149224651Sjonathan fl.l_start = index; 150224651Sjonathan fl.l_len = 1; 151224793Sjonathan return (fcntl(file, F_SETLK, &fl) == -1); 152224793Sjonathan} 153224651Sjonathan 154224651Sjonathan/* 155221431Sjonathan * API-specific code ENDS here 156 */ 157 158int 159benchmark_initbatch(void *tsd) 160{ 161 tsd_t *ts = (tsd_t *)tsd; 162 int e = 0; 163 164 if (ts->ts_once == 0) { 165 int us, them; 166 167 us = (getpindex() * lm_optT) + gettindex(); 168 them = (us + 1) % (lm_optP * lm_optT); 169 170 ts->ts_id = us; 171 172 /* lock index asignment for us and them */ 173 ts->ts_us0 = (us * 4); 174 ts->ts_us1 = (us * 4) + 2; 175 if (us < nthreads - 1) { 176 /* straight-thru connection to them */ 177 ts->ts_them0 = (them * 4); 178 ts->ts_them1 = (them * 4) + 2; 179 } else { 180 /* cross-over connection to them */ 181 ts->ts_them0 = (them * 4) + 2; 182 ts->ts_them1 = (them * 4); 183 } 184 185 ts->ts_once = 1; 186 } 187 188 /* block their first move */ 189 e += block(ts->ts_them0); 190 191 return (e); 192} 193 194int 195benchmark(void *tsd, result_t *res) 196{ 197 tsd_t *ts = (tsd_t *)tsd; 198 int i; 199 int e = 0; 200 201 /* wait to be unblocked (id == 0 will not block) */ 202 e += block(ts->ts_us0); 203 204 for (i = 0; i < lm_optB; i += 2) { 205 /* allow them to block us again */ 206 e += unblock(ts->ts_us0); 207 208 /* block their next + 1 move */ 209 e += block(ts->ts_them1); 210 211 /* unblock their next move */ 212 e += unblock(ts->ts_them0); 213 214 /* wait for them to unblock us */ 215 e += block(ts->ts_us1); 216 217 /* repeat with locks reversed */ 218 e += unblock(ts->ts_us1); 219 e += block(ts->ts_them0); 220 e += unblock(ts->ts_them1); 221 e += block(ts->ts_us0); 222 } 223 224 /* finish batch with nothing blocked */ 225 e += unblock(ts->ts_them0); 226 e += unblock(ts->ts_us0); 227 228 res->re_count = i; 229 res->re_errors = e; 230 231 return (0); 232} 233