1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1994-2002 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Methods of the cfsd_maptbl classes.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <stddef.h>
36#include <string.h>
37#include <synch.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <sys/utsname.h>
42#include <sys/vfs.h>
43#include <sys/cred.h>
44#include <sys/param.h>
45#include <sys/types.h>
46#include <sys/stat.h>
47#include <sys/mman.h>
48#include <sys/fs/cachefs_fs.h>
49#include <sys/fs/cachefs_dlog.h>
50#include <mdbug/mdbug.h>
51#include "cfsd.h"
52#include "cfsd_logfile.h"
53
54/*
55 *			cfsd_logfile_create
56 *
57 * Description:
58 * Arguments:
59 * Returns:
60 * Preconditions:
61 */
62cfsd_logfile_object_t *
63cfsd_logfile_create(void)
64{
65	cfsd_logfile_object_t *logfile_object_p;
66
67	dbug_enter("cfsd_logfile_create");
68
69	logfile_object_p = cfsd_calloc(sizeof (cfsd_logfile_object_t));
70	logfile_object_p->i_fid = -1;
71	logfile_object_p->i_map_entry.i_pa = NULL;
72	logfile_object_p->i_map_entry.i_paoff = 0;
73	logfile_object_p->i_map_entry.i_paend = 0;
74	logfile_object_p->i_map_entry.i_palen = 0;
75	logfile_object_p->i_map_offset.i_pa = NULL;
76	logfile_object_p->i_map_offset.i_paoff = 0;
77	logfile_object_p->i_map_offset.i_paend = 0;
78	logfile_object_p->i_map_offset.i_palen = 0;
79	logfile_object_p->i_cur_offset = 0;
80	logfile_object_p->i_cur_entry = NULL;
81	dbug_leave("cfsd_logfile_create");
82	return (logfile_object_p);
83}
84
85/*
86 *			cfsd_logfile_destroy
87 *
88 * Description:
89 * Arguments:
90 * Returns:
91 * Preconditions:
92 */
93void
94cfsd_logfile_destroy(cfsd_logfile_object_t *logfile_object_p)
95{
96	dbug_enter("cfsd_logfile_destroy");
97	logfile_sync(logfile_object_p);
98	logfile_teardown(logfile_object_p);
99	cfsd_free(logfile_object_p);
100	dbug_leave("cfsd_logfile_destroy");
101}
102
103/*
104 *			logfile_domap
105 *
106 * Description:
107 *	Maps in the specified section of the file.
108 * Arguments:
109 *	off	The offset to map in.  Must be i_pagesize aligned.
110 *	map	0 means use map_entry, 1 means use map_offset
111 * Returns:
112 *	Returns 0 for success or an errno value on failure.
113 * Preconditions:
114 */
115int
116logfile_domap(cfsd_logfile_object_t *logfile_object_p, off_t off, int map)
117{
118	int xx;
119	int len;
120	mmap_info_t *mmp;
121
122	dbug_enter("logfile_domap");
123	dbug_precond(logfile_object_p->i_fid >= 0);
124
125	len = logfile_object_p->i_maplen;
126	mmp = (map == 0) ?
127		&logfile_object_p->i_map_entry :
128		&logfile_object_p->i_map_offset;
129
130	logfile_object_p->i_stat_mapmove++;
131
132	/* destroy old mapping if it exists */
133	if (mmp->i_pa) {
134		/* determine how far we have to move the map */
135		logfile_object_p->i_stat_mapdist += abs(mmp->i_paoff - off);
136
137		/* remove the map */
138		xx = munmap(mmp->i_pa, mmp->i_palen);
139		if (xx == -1) {
140			xx = errno;
141			dbug_print(("error", "Could not unmap %s, %d, %p, %d",
142			    logfile_object_p->i_name, xx, mmp->i_pa,
143			    mmp->i_palen));
144		}
145		mmp->i_pa = NULL;
146		mmp->i_palen = 0;
147		mmp->i_paoff = 0;
148		mmp->i_paend = 0;
149	}
150
151	/* do the mapping */
152	mmp->i_pa = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED,
153	    logfile_object_p->i_fid, off);
154	if (mmp->i_pa == MAP_FAILED) {
155		xx = errno;
156		dbug_print(("error",
157		    "Could not map %s, error %d, off %d, len %d",
158		    logfile_object_p->i_name, xx, off, len));
159		mmp->i_pa = NULL;
160		dbug_leave("logfile_domap");
161		return (xx);
162	}
163
164	mmp->i_palen = len;
165	mmp->i_paoff = off;
166	mmp->i_paend = off + len - 1;
167	dbug_leave("logfile_domap");
168	return (0);
169}
170
171/*
172 *			logfile_getaddr
173 *
174 * Description:
175 *	Returns an address of a particular offset in the file.
176 *	The size of the item to map is i_maxmap
177 *	This routine assumes that if we have to remap that i_maxmap
178 *	will fit inside the default mapping size.
179 * Arguments:
180 *	start	offset in the file to map
181 *	map	0 means use map_entry, 1 means use map_offset
182 * Returns:
183 *	Returns NULL for a failure with the mapping file.
184 * Preconditions:
185 */
186caddr_t
187logfile_getaddr(cfsd_logfile_object_t *logfile_object_p, off_t start, int map)
188{
189	mmap_info_t *mmp;
190	caddr_t pa;
191	off_t end;
192
193	dbug_enter("logfile_getaddr");
194
195	mmp = (map == 0) ?
196	    &logfile_object_p->i_map_entry :
197	    &logfile_object_p->i_map_offset;
198
199	/* determine the end of the item */
200	end = start + logfile_object_p->i_maxmap - 1;
201
202	/* map the entry in if necessary */
203	if ((start < mmp->i_paoff) || (mmp->i_paend < end)) {
204		if (logfile_domap(logfile_object_p,
205		    start & logfile_object_p->i_pagemask, map)) {
206			dbug_leave("logfile_getaddr");
207			return (NULL);
208		}
209		dbug_assert((mmp->i_paoff <= start) && (end <= mmp->i_paend));
210	}
211
212	/* make an address and return it */
213	pa = mmp->i_pa + (start - mmp->i_paoff);
214	dbug_leave("logfile_getaddr");
215	return (pa);
216}
217
218/*
219 *			logfile_setup
220 *
221 * Description:
222 *	Sets up to use the specified file.
223 *	Call this routine before using any of the other routines.
224 * Arguments:
225 *	filename	file to use
226 *	maxmap		max amount needed after a map
227 * Returns:
228 *	Returns 0 for success or an errno value.
229 * Preconditions:
230 *	precond(filename)
231 */
232int
233logfile_setup(cfsd_logfile_object_t *logfile_object_p,
234	const char *filename, int maxmap)
235{
236	int xx;
237	struct stat sinfo;
238	long *versionp;
239
240	dbug_enter("logfile_setup");
241	dbug_precond(filename);
242
243	/* clean up from a previous setup */
244	logfile_teardown(logfile_object_p);
245
246	strlcpy(logfile_object_p->i_name, filename,
247	    sizeof (logfile_object_p->i_name));
248	dbug_print(("info", "filename %s", logfile_object_p->i_name));
249	logfile_object_p->i_maxmap = maxmap;
250
251	/* get the page info */
252	logfile_object_p->i_pagesize = PAGESIZE;
253	logfile_object_p->i_pagemask = PAGEMASK;
254	logfile_object_p->i_maplen = logfile_object_p->i_pagesize * 100;
255
256	/* open the file */
257	logfile_object_p->i_fid = open(logfile_object_p->i_name,
258	    O_RDWR | O_NONBLOCK);
259	if (logfile_object_p->i_fid == -1) {
260		xx = errno;
261		dbug_print(("error", "Could not open %s, %d",
262		    logfile_object_p->i_name, xx));
263		dbug_leave("logfile_setup");
264		return (xx);
265	}
266
267	/* get the size and type of file */
268	xx = fstat(logfile_object_p->i_fid, &sinfo);
269	if (xx) {
270		xx = errno;
271		if (xx == ENOENT) {
272			dbug_print(("info", "No log file to roll"));
273		} else {
274			dbug_print(("error", "Could not stat %s, %d",
275			    logfile_object_p->i_name, xx));
276		}
277		dbug_leave("logfile_setup");
278		return (xx);
279	}
280	logfile_object_p->i_size = sinfo.st_size;
281
282	/* sanity check, better be a regular file */
283	if (!S_ISREG(sinfo.st_mode)) {
284		xx = ENOTSUP;
285		dbug_print(("error", "%s Not a regular file.",
286		    logfile_object_p->i_name));
287		dbug_leave("logfile_setup");
288		return (xx);
289	}
290
291	/* better not be too small */
292	if (logfile_object_p->i_size < LOGFILE_ENTRY_START) {
293		dbug_print(("error", "File %s is too small %d.",
294		    logfile_object_p->i_name, logfile_object_p->i_size));
295		dbug_leave("logfile_setup");
296		return (EINVAL);
297	}
298
299	/* initialize statistic gathering */
300	logfile_object_p->i_stat_mapmove = 0;
301	logfile_object_p->i_stat_mapdist = 0;
302
303	/* check the version number */
304	versionp = (long *)logfile_getaddr(logfile_object_p, 0, 1);
305	if (versionp == NULL) {
306		dbug_leave("logfile_setup");
307		return (EIO);
308	}
309	if (*versionp != CFS_DLOG_VERSION) {
310		dbug_print(("error", "Log file version mismatch %d != %d",
311		    *versionp, CFS_DLOG_VERSION));
312		dbug_leave("logfile_setup");
313		return (EINVAL);
314	}
315
316	/* return success */
317	dbug_leave("logfile_setup");
318	return (0);
319}
320
321/*
322 *			logfile_teardown
323 *
324 * Description:
325 *	Uninitializes the object.
326 *	Call logfile_setup before using this object again.
327 * Arguments:
328 * Returns:
329 * Preconditions:
330 */
331void
332logfile_teardown(cfsd_logfile_object_t *logfile_object_p)
333{
334	int xx;
335
336	dbug_enter("logfile_teardown");
337
338	if (logfile_object_p->i_map_entry.i_pa) {
339		xx = munmap(logfile_object_p->i_map_entry.i_pa,
340		    logfile_object_p->i_map_entry.i_palen);
341		if (xx == -1) {
342			xx = errno;
343			dbug_print(("error", "Could not unmap %s, %d, %p, %d",
344			    logfile_object_p->i_name, xx,
345			    logfile_object_p->i_map_entry.i_pa,
346			    logfile_object_p->i_map_entry.i_palen));
347		}
348		logfile_object_p->i_map_entry.i_pa = NULL;
349	}
350	logfile_object_p->i_map_entry.i_paoff = 0;
351	logfile_object_p->i_map_entry.i_paend = 0;
352	logfile_object_p->i_map_entry.i_palen = 0;
353
354	if (logfile_object_p->i_map_offset.i_pa) {
355		xx = munmap(logfile_object_p->i_map_offset.i_pa,
356		    logfile_object_p->i_map_offset.i_palen);
357		if (xx == -1) {
358			xx = errno;
359			dbug_print(("error", "Could not unmap %s, %d, %p, %d",
360			    logfile_object_p->i_name, xx,
361			    logfile_object_p->i_map_offset.i_pa,
362			    logfile_object_p->i_map_offset.i_palen));
363		}
364		logfile_object_p->i_map_offset.i_pa = NULL;
365	}
366	logfile_object_p->i_map_offset.i_paoff = 0;
367	logfile_object_p->i_map_offset.i_paend = 0;
368	logfile_object_p->i_map_offset.i_palen = 0;
369
370	if (logfile_object_p->i_fid != -1) {
371		if (close(logfile_object_p->i_fid))
372			dbug_print(("error", "Could not close %s, %d",
373			    logfile_object_p->i_name, errno));
374		logfile_object_p->i_fid = -1;
375	}
376	logfile_object_p->i_cur_offset = 0;
377	logfile_object_p->i_cur_entry = NULL;
378	dbug_leave("logfile_teardown");
379}
380
381/*
382 *			logfile_entry
383 *
384 * Description:
385 *	Sets addrp to the address of the log entry at offset
386 *	The mapping remains in effect until:
387 *		a) this routine is called again
388 *		b) logfile_teardown is called
389 *		c) this object is destroyed
390 * Arguments:
391 *	offset	offset to start of entry
392 *	entpp	place to store address
393 * Returns:
394 *	Returns 0 for success, 1 for EOF, -1 if a fatal error occurs.
395 * Preconditions:
396 *	precond(addrp)
397 */
398int
399logfile_entry(cfsd_logfile_object_t *logfile_object_p,
400	off_t offset,
401	cfs_dlog_entry_t **entpp)
402{
403	cfs_dlog_entry_t *entp;
404
405	dbug_enter("logfile_entry");
406	dbug_precond(entpp);
407	dbug_precond(offset >= sizeof (long));
408
409
410	logfile_object_p->i_stat_nextcnt++;
411
412	/* check for eof */
413	if (offset >= logfile_object_p->i_size) {
414		dbug_leave("logfile_entry");
415		return (1);
416	}
417	dbug_assert((offset & 3) == 0);
418
419	/* get the address of the entry */
420	entp = (cfs_dlog_entry_t *)logfile_getaddr(logfile_object_p, offset, 0);
421	if (entp == NULL) {
422		dbug_leave("logfile_entry");
423		return (-1);
424	}
425	/* sanity check, record should be alligned */
426	if (entp->dl_len & 3) {
427		dbug_print(("error",
428		    "Record at offset %d length is not alligned %d",
429		    offset, entp->dl_len));
430		dbug_leave("logfile_entry");
431		return (-1);
432	}
433
434	/* sanity check record should a reasonable size */
435	if ((entp->dl_len < CFS_DLOG_ENTRY_MINSIZE) ||
436	    (entp->dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
437		dbug_print(("error",
438		    "Record at offset %d has an invalid size %d", offset,
439		    entp->dl_len));
440		dbug_leave("logfile_entry");
441		return (-1);
442	}
443
444	/* preserve offset and pointer */
445	logfile_object_p->i_cur_offset = offset;
446	logfile_object_p->i_cur_entry = entp;
447
448	/* return success */
449	*entpp = entp;
450	dbug_leave("logfile_entry");
451	return (0);
452}
453
454/*
455 *			logfile_offset
456 *
457 * Description:
458 *	Sets addrp to the address of the specified offset.
459 *	The mapping remains in effect until:
460 *		a) this routine is called again
461 *		b) logfile_teardown is called
462 *		c) this object is destroyed
463 * Arguments:
464 *	offset	offset into file, must be 0 <= offset < i_size
465 *	addrp	returns mapped address
466 * Returns:
467 *	Returns 0 for success, -1 if a fatal error occurs.
468 * Preconditions:
469 *	precond(addrp)
470 */
471int
472logfile_offset(cfsd_logfile_object_t *logfile_object_p,
473	off_t offset,
474	caddr_t *addrp)
475{
476	caddr_t pa;
477
478	dbug_enter("logfile_offset");
479	dbug_precond(addrp);
480	dbug_precond((0 <= offset) && (offset < logfile_object_p->i_size));
481
482	logfile_object_p->i_stat_offcnt++;
483
484	/* get the address for the offset */
485	pa = logfile_getaddr(logfile_object_p, offset, 1);
486	if (pa == NULL) {
487		dbug_leave("logfile_offset");
488		return (-1);
489	}
490	/* return success */
491	*addrp = pa;
492	dbug_leave("logfile_offset");
493	return (0);
494}
495
496/*
497 *			logfile_sync
498 *
499 * Description:
500 *	Performs an fsync on the log file.
501 * Arguments:
502 * Returns:
503 *	Returns 0 for success or an errno value on failure.
504 * Preconditions:
505 */
506int
507logfile_sync(cfsd_logfile_object_t *logfile_object_p)
508{
509	int xx;
510
511	dbug_enter("logfile_sync");
512
513	if (logfile_object_p->i_fid == -1) {
514		dbug_leave("logfile_sync");
515		return (0);
516	}
517	xx = fsync(logfile_object_p->i_fid);
518	if (xx) {
519		xx = errno;
520		dbug_print(("error", "fsync failed %d", xx));
521	}
522	dbug_leave("logfile_sync");
523	return (xx);
524}
525
526/*
527 *			logfile_dumpstats
528 *
529 * Description:
530 *	Prints out various stats about the hashing.
531 * Arguments:
532 * Returns:
533 * Preconditions:
534 */
535void
536logfile_dumpstats(cfsd_logfile_object_t *logfile_object_p)
537{
538	int xx;
539	double dd;
540
541	dbug_enter("logfile_dumpstats");
542
543	dbug_print(("dump", "Request - next %d",
544	    logfile_object_p->i_stat_nextcnt));
545	dbug_print(("dump", "Request - offset %d",
546	    logfile_object_p->i_stat_offcnt));
547	dbug_print(("dump", "Map Moves %d", logfile_object_p->i_stat_mapmove));
548	dbug_print(("dump", "Mapping Size %d", logfile_object_p->i_maplen));
549	dbug_print(("dump", "Item Size %d", logfile_object_p->i_maxmap));
550	dbug_print(("dump", "File Size %d", logfile_object_p->i_size));
551	if (logfile_object_p->i_stat_mapmove == 0) {
552		dbug_leave("logfile_dumpstats");
553		return;
554	}
555
556	dd = (double)logfile_object_p->i_stat_mapmove /
557	    (logfile_object_p->i_stat_nextcnt +
558	    logfile_object_p->i_stat_offcnt);
559	dbug_print(("dump", "Mmap moves per Request %.2f", dd));
560
561	xx = logfile_object_p->i_stat_mapdist /
562	    logfile_object_p->i_stat_mapmove;
563	dbug_print(("dump", "Average distance per mmap moves %d", xx));
564	dbug_leave("logfile_dumpstats");
565}
566