xref: /titanic_44/usr/src/cmd/fs.d/cachefs/cfsd/cfsd_logfile.c (revision 1cb6af97c6f66f456d4f726ef056e1ebc0f73305)
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  */
62 cfsd_logfile_object_t *
63 cfsd_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  */
93 void
94 cfsd_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  */
115 int
116 logfile_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  */
186 caddr_t
187 logfile_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  */
232 int
233 logfile_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  */
331 void
332 logfile_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  */
398 int
399 logfile_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  */
471 int
472 logfile_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  */
506 int
507 logfile_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  */
535 void
536 logfile_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