Index: head/bin/ed/Makefile =================================================================== --- head/bin/ed/Makefile (revision 312926) +++ head/bin/ed/Makefile (revision 312927) @@ -1,16 +1,16 @@ # $FreeBSD$ .include PACKAGE=runtime PROG= ed SRCS= buf.c cbc.c glbl.c io.c main.c re.c sub.c undo.c LINKS= ${BINDIR}/ed ${BINDIR}/red MLINKS= ed.1 red.1 .if ${MK_OPENSSL} != "no" && ${MK_ED_CRYPTO} != "no" -CFLAGS+=-DDES -I/usr/include/private/openssl +CFLAGS+=-DDES LIBADD= crypto .endif .include Index: head/contrib/dma/mail.c =================================================================== --- head/contrib/dma/mail.c (revision 312926) +++ head/contrib/dma/mail.c (revision 312927) @@ -1,483 +1,460 @@ /* * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>. * Copyright (c) 2008 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Simon Schubert <2@0x2c.org>. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include -#include #include "dma.h" void bounce(struct qitem *it, const char *reason) { struct queue bounceq; char line[1000]; size_t pos; int error; /* Don't bounce bounced mails */ if (it->sender[0] == 0) { syslog(LOG_INFO, "can not bounce a bounce message, discarding"); exit(EX_SOFTWARE); } bzero(&bounceq, sizeof(bounceq)); LIST_INIT(&bounceq.queue); bounceq.sender = ""; if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0) goto fail; if (newspoolf(&bounceq) != 0) goto fail; syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id); setlogident("%s", bounceq.id); error = fprintf(bounceq.mailf, "Received: from MAILER-DAEMON\n" "\tid %s\n" "\tby %s (%s);\n" "\t%s\n" "X-Original-To: <%s>\n" "From: MAILER-DAEMON <>\n" "To: %s\n" "Subject: Mail delivery failed\n" "Message-Id: <%s@%s>\n" "Date: %s\n" "\n" "This is the %s at %s.\n" "\n" "There was an error delivering your mail to <%s>.\n" "\n" "%s\n" "\n" "%s\n" "\n", bounceq.id, hostname(), VERSION, rfc822date(), it->addr, it->sender, bounceq.id, hostname(), rfc822date(), VERSION, hostname(), it->addr, reason, config.features & FULLBOUNCE ? "Original message follows." : "Message headers follow."); if (error < 0) goto fail; if (fseek(it->mailf, 0, SEEK_SET) != 0) goto fail; if (config.features & FULLBOUNCE) { while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) { if (fwrite(line, 1, pos, bounceq.mailf) != pos) goto fail; } } else { while (!feof(it->mailf)) { if (fgets(line, sizeof(line), it->mailf) == NULL) break; if (line[0] == '\n') break; if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1) goto fail; } } if (linkspool(&bounceq) != 0) goto fail; /* bounce is safe */ delqueue(it); run_queue(&bounceq); /* NOTREACHED */ fail: syslog(LOG_CRIT, "error creating bounce: %m"); delqueue(it); exit(EX_IOERR); } struct parse_state { char addr[1000]; int pos; enum { NONE = 0, START, MAIN, EOL, QUIT } state; int comment; int quote; int brackets; int esc; }; /* * Simplified RFC2822 header/address parsing. * We copy escapes and quoted strings directly, since * we have to pass them like this to the mail server anyways. * XXX local addresses will need treatment */ static int parse_addrs(struct parse_state *ps, char *s, struct queue *queue) { char *addr; again: switch (ps->state) { case NONE: return (-1); case START: /* init our data */ bzero(ps, sizeof(*ps)); /* skip over header name */ while (*s != ':') s++; s++; ps->state = MAIN; break; case MAIN: /* all fine */ break; case EOL: switch (*s) { case ' ': case '\t': s++; /* continue */ break; default: ps->state = QUIT; if (ps->pos != 0) goto newaddr; return (0); } case QUIT: return (0); } for (; *s != 0; s++) { if (ps->esc) { ps->esc = 0; switch (*s) { case '\r': case '\n': goto err; default: goto copy; } } if (ps->quote) { switch (*s) { case '"': ps->quote = 0; goto copy; case '\\': ps->esc = 1; goto copy; case '\r': case '\n': goto eol; default: goto copy; } } switch (*s) { case '(': ps->comment++; break; case ')': if (ps->comment) ps->comment--; else goto err; goto skip; case '"': ps->quote = 1; goto copy; case '\\': ps->esc = 1; goto copy; case '\r': case '\n': goto eol; } if (ps->comment) goto skip; switch (*s) { case ' ': case '\t': /* ignore whitespace */ goto skip; case '<': /* this is the real address now */ ps->brackets = 1; ps->pos = 0; goto skip; case '>': if (!ps->brackets) goto err; ps->brackets = 0; s++; goto newaddr; case ':': /* group - ignore */ ps->pos = 0; goto skip; case ',': case ';': /* * Next address, copy previous one. * However, we might be directly after * a
, or have two consecutive * commas. * Skip the comma unless there is * really something to copy. */ if (ps->pos == 0) goto skip; s++; goto newaddr; default: goto copy; } copy: if (ps->comment) goto skip; if (ps->pos + 1 == sizeof(ps->addr)) goto err; ps->addr[ps->pos++] = *s; skip: ; } eol: ps->state = EOL; return (0); err: ps->state = QUIT; return (-1); newaddr: ps->addr[ps->pos] = 0; ps->pos = 0; addr = strdup(ps->addr); if (addr == NULL) errlog(EX_SOFTWARE, "strdup"); if (add_recp(queue, addr, EXPAND_WILDCARD) != 0) errlogx(EX_DATAERR, "invalid recipient `%s'", addr); goto again; } -static int -writeline(struct queue *queue, const char *line, ssize_t linelen) -{ - ssize_t len; - - while (linelen > 0) { - len = linelen; - if (linelen > 1000) { - len = 990; - } - if (fwrite(line, len, 1, queue->mailf) != 1) - return (-1); - if (linelen <= 1000) - break; - if (fwrite("\n", 1, 1, queue->mailf) != 1) - return (-1); - line += 990; - linelen = strlen(line); - } - return (0); -} - int readmail(struct queue *queue, int nodot, int recp_from_header) { struct parse_state parse_state; - char *line = NULL; - ssize_t linelen; - size_t linecap = 0; - char newline[1000]; + char line[1000]; /* by RFC2822 */ + size_t linelen; size_t error; int had_headers = 0; int had_from = 0; int had_messagid = 0; int had_date = 0; + int had_last_line = 0; int nocopy = 0; parse_state.state = NONE; error = fprintf(queue->mailf, "Received: from %s (uid %d)\n" "\t(envelope-from %s)\n" "\tid %s\n" "\tby %s (%s);\n" "\t%s\n", username, useruid, queue->sender, queue->id, hostname(), VERSION, rfc822date()); if ((ssize_t)error < 0) return (-1); while (!feof(stdin)) { - newline[0] = '\0'; - if ((linelen = getline(&line, &linecap, stdin)) <= 0) + if (fgets(line, sizeof(line) - 1, stdin) == NULL) break; + if (had_last_line) + errlogx(EX_DATAERR, "bad mail input format:" + " from %s (uid %d) (envelope-from %s)", + username, useruid, queue->sender); + linelen = strlen(line); + if (linelen == 0 || line[linelen - 1] != '\n') { + /* + * This line did not end with a newline character. + * If we fix it, it better be the last line of + * the file. + */ + line[linelen] = '\n'; + line[linelen + 1] = 0; + had_last_line = 1; + } if (!had_headers) { - if (linelen > 1000) - errlogx(EX_DATAERR, "bad mail input format:" - " from %s (uid %d) (envelope-from %s)", - username, useruid, queue->sender); - /* * Unless this is a continuation, switch of * the Bcc: nocopy flag. */ if (!(line[0] == ' ' || line[0] == '\t')) nocopy = 0; if (strprefixcmp(line, "Date:") == 0) had_date = 1; else if (strprefixcmp(line, "Message-Id:") == 0) had_messagid = 1; else if (strprefixcmp(line, "From:") == 0) had_from = 1; else if (strprefixcmp(line, "Bcc:") == 0) nocopy = 1; if (parse_state.state != NONE) { if (parse_addrs(&parse_state, line, queue) < 0) { errlogx(EX_DATAERR, "invalid address in header\n"); /* NOTREACHED */ } } if (recp_from_header && ( strprefixcmp(line, "To:") == 0 || strprefixcmp(line, "Cc:") == 0 || strprefixcmp(line, "Bcc:") == 0)) { parse_state.state = START; if (parse_addrs(&parse_state, line, queue) < 0) { errlogx(EX_DATAERR, "invalid address in header\n"); /* NOTREACHED */ } } } - if (line[0] == '\n' && !had_headers) { + if (strcmp(line, "\n") == 0 && !had_headers) { had_headers = 1; while (!had_date || !had_messagid || !had_from) { if (!had_date) { had_date = 1; - snprintf(newline, sizeof(newline), "Date: %s\n", rfc822date()); + snprintf(line, sizeof(line), "Date: %s\n", rfc822date()); } else if (!had_messagid) { /* XXX msgid, assign earlier and log? */ had_messagid = 1; - snprintf(newline, sizeof(newline), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n", + snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n", (uintmax_t)time(NULL), queue->id, (uintmax_t)random(), hostname()); } else if (!had_from) { had_from = 1; - snprintf(newline, sizeof(newline), "From: <%s>\n", queue->sender); + snprintf(line, sizeof(line), "From: <%s>\n", queue->sender); } - if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1) - goto fail; + if (fwrite(line, strlen(line), 1, queue->mailf) != 1) + return (-1); } - strlcpy(newline, "\n", sizeof(newline)); + strcpy(line, "\n"); } if (!nodot && linelen == 2 && line[0] == '.') break; if (!nocopy) { - if (newline[0] != '\0') { - if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1) - goto fail; - } else { - if (writeline(queue, line, linelen) != 0) - goto fail; - } + if (fwrite(line, strlen(line), 1, queue->mailf) != 1) + return (-1); } } return (0); -fail: - free(line); - return (-1); } Index: head/share/mk/bsd.doc.mk =================================================================== --- head/share/mk/bsd.doc.mk (revision 312926) +++ head/share/mk/bsd.doc.mk (revision 312927) @@ -1,202 +1,202 @@ # from: @(#)bsd.doc.mk 5.3 (Berkeley) 1/2/91 # $FreeBSD$ # # The include file handles installing BSD troff documents. # # # +++ variables +++ # # DCOMPRESS_CMD Program to compress troff documents. Output is to stdout. # [${COMPRESS_CMD}] # # DESTDIR Change the tree where the documents get installed. [not set] # # DOC Document name. [paper] # # EXTRA Extra files (not SRCS) that make up the document. [not set] # # LPR Printer command. [lpr] # # MACROS Macro packages used to build the document. [not set] # # WITHOUT_DOCCOMPRESS If you do not want formatted troff documents to be # compressed when they are installed. [not set] # # PRINTERDEVICE Indicates which output formats will be generated # (ascii, ps, html). [ascii] # # SRCDIR Directory where source files live. [${.CURDIR}] # # SRCS List of source files. [not set] # # TRFLAGS Additional flags to groff(1). [not set] # # USE_EQN If set, preprocess with eqn(1). [not set] # # USE_PIC If set, preprocess with pic(1). [not set] # # USE_REFER If set, preprocess with refer(1). [not set] # # USE_SOELIM If set, preprocess with soelim(1). [not set] # # USE_TBL If set, preprocess with tbl(1). [not set] # # VOLUME Volume the document belongs to. [not set] .include PRINTERDEVICE?= ascii BIB?= bib GREMLIN?= grn GRIND?= vgrind -f INDXBIB?= indxbib PIC?= pic REFER?= refer .for _dev in ${PRINTERDEVICE:Mascii} -ROFF.ascii?= nroff -Tascii -P-c ${TRFLAGS} -mtty-char ${MACROS} ${PAGES:C/^/-o/1} +ROFF.ascii?= groff -Tascii -P-c ${TRFLAGS} -mtty-char ${MACROS} ${PAGES:C/^/-o/1} .endfor .for _dev in ${PRINTERDEVICE:Nascii} -ROFF.${_dev}?= nroff -T${_dev} ${TRFLAGS} ${MACROS} ${PAGES:C/^/-o/1} +ROFF.${_dev}?= groff -T${_dev} ${TRFLAGS} ${MACROS} ${PAGES:C/^/-o/1} .endfor SOELIM?= soelim TBL?= tbl DOC?= paper LPR?= lpr .if defined(USE_EQN) TRFLAGS+= -e .endif .if defined(USE_PIC) TRFLAGS+= -p .endif .if defined(USE_REFER) TRFLAGS+= -R .endif .if defined(USE_SOELIM) TRFLAGS+= -I${.CURDIR} .endif .if defined(USE_TBL) TRFLAGS+= -t .endif .if defined(NO_ROOT) .if !defined(TAGS) || ! ${TAGS:Mpackage=*} TAGS+= package=${PACKAGE:Uruntime} .endif TAG_ARGS= -T ${TAGS:[*]:S/ /,/g} .endif DCOMPRESS_EXT?= ${COMPRESS_EXT} DCOMPRESS_CMD?= ${COMPRESS_CMD} .for _dev in ${PRINTERDEVICE:Mhtml} DFILE.html= ${DOC}.html .endfor .for _dev in ${PRINTERDEVICE:Nhtml} .if ${MK_DOCCOMPRESS} == "no" DFILE.${_dev}= ${DOC}.${_dev} .else DFILE.${_dev}= ${DOC}.${_dev}${DCOMPRESS_EXT} .endif .endfor UNROFF?= unroff HTML_SPLIT?= yes UNROFFFLAGS?= -fhtml .if ${HTML_SPLIT} == "yes" UNROFFFLAGS+= split=1 .endif # Compatibility mode flag for groff. Use this when formatting documents with # Berkeley me macros (orig_me(7)). COMPAT?= -C .PATH: ${.CURDIR} ${SRCDIR} .if !defined(_SKIP_BUILD) .for _dev in ${PRINTERDEVICE} all: ${DFILE.${_dev}} .endfor .endif .if !target(print) .for _dev in ${PRINTERDEVICE} print: ${DFILE.${_dev}} .endfor print: .for _dev in ${PRINTERDEVICE} .if ${MK_DOCCOMPRESS} == "no" ${LPR} ${DFILE.${_dev}} .else ${DCOMPRESS_CMD} -d ${DFILE.${_dev}} | ${LPR} .endif .endfor .endif .for _dev in ${PRINTERDEVICE:Nascii:Nps:Nhtml} CLEANFILES+= ${DOC}.${_dev} ${DOC}.${_dev}${DCOMPRESS_EXT} .endfor CLEANFILES+= ${DOC}.ascii ${DOC}.ascii${DCOMPRESS_EXT} \ ${DOC}.ps ${DOC}.ps${DCOMPRESS_EXT} \ ${DOC}.html ${DOC}-*.html realinstall: .if ${PRINTERDEVICE:Mhtml} cd ${SRCDIR}; \ ${INSTALL} ${TAG_ARGS:D${TAG_ARGS},docs} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${DOC}*.html ${DESTDIR}${BINDIR}/${VOLUME}/ .endif .for _dev in ${PRINTERDEVICE:Nhtml} ${INSTALL} ${TAG_ARGS:D${TAG_ARGS},docs} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${DFILE.${_dev}} ${DESTDIR}${BINDIR}/${VOLUME}/ .endfor spell: ${SRCS} (cd ${.CURDIR}; spell ${SRCS} ) | sort | \ comm -23 - ${.CURDIR}/spell.ok > ${DOC}.spell BINDIR?= /usr/share/doc BINMODE= 444 SRCDIR?= ${.CURDIR} .if defined(EXTRA) && !empty(EXTRA) _stamp.extra: ${EXTRA} touch ${.TARGET} .endif CLEANFILES+= _stamp.extra .for _dev in ${PRINTERDEVICE:Nhtml} .if !target(${DFILE.${_dev}}) .if target(_stamp.extra) ${DFILE.${_dev}}: _stamp.extra .endif ${DFILE.${_dev}}: ${SRCS} .if ${MK_DOCCOMPRESS} == "no" ${ROFF.${_dev}} ${.ALLSRC:N_stamp.extra} > ${.TARGET} .else ${ROFF.${_dev}} ${.ALLSRC:N_stamp.extra} | ${DCOMPRESS_CMD} > ${.TARGET} .endif .endif .endfor .for _dev in ${PRINTERDEVICE:Mhtml} .if !target(${DFILE.html}) .if target(_stamp.extra) ${DFILE.html}: _stamp.extra .endif ${DFILE.html}: ${SRCS} .if defined(MACROS) && !empty(MACROS) cd ${SRCDIR}; ${UNROFF} ${MACROS} ${UNROFFFLAGS} \ document=${DOC} ${SRCS} .else # unroff(1) requires a macro package as an argument cd ${SRCDIR}; ${UNROFF} -ms ${UNROFFFLAGS} \ document=${DOC} ${SRCS} .endif .endif .endfor DISTRIBUTION?= doc .include Index: head/sys/amd64/amd64/db_trace.c =================================================================== --- head/sys/amd64/amd64/db_trace.c (revision 312926) +++ head/sys/amd64/amd64/db_trace.c (revision 312927) @@ -1,639 +1,624 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; static db_varfcn_t db_frame_seg; CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg)); /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame_seg }, { "ds", DB_OFFSET(tf_ds), db_frame_seg }, { "es", DB_OFFSET(tf_es), db_frame_seg }, { "fs", DB_OFFSET(tf_fs), db_frame_seg }, { "gs", DB_OFFSET(tf_gs), db_frame_seg }, { "ss", DB_OFFSET(tf_ss), db_frame_seg }, { "rax", DB_OFFSET(tf_rax), db_frame }, { "rcx", DB_OFFSET(tf_rcx), db_frame }, { "rdx", DB_OFFSET(tf_rdx), db_frame }, { "rbx", DB_OFFSET(tf_rbx), db_frame }, { "rsp", DB_OFFSET(tf_rsp), db_frame }, { "rbp", DB_OFFSET(tf_rbp), db_frame }, { "rsi", DB_OFFSET(tf_rsi), db_frame }, { "rdi", DB_OFFSET(tf_rdi), db_frame }, { "r8", DB_OFFSET(tf_r8), db_frame }, { "r9", DB_OFFSET(tf_r9), db_frame }, { "r10", DB_OFFSET(tf_r10), db_frame }, { "r11", DB_OFFSET(tf_r11), db_frame }, { "r12", DB_OFFSET(tf_r12), db_frame }, { "r13", DB_OFFSET(tf_r13), db_frame }, { "r14", DB_OFFSET(tf_r14), db_frame }, { "r15", DB_OFFSET(tf_r15), db_frame }, { "rip", DB_OFFSET(tf_rip), db_frame }, { "rflags", DB_OFFSET(tf_rflags), db_frame }, }; struct db_variable *db_eregs = db_regs + nitems(db_regs); static int db_frame_seg(struct db_variable *vp, db_expr_t *valuep, int op) { uint16_t *reg; if (kdb_frame == NULL) return (0); reg = (uint16_t *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { long *reg; if (kdb_frame == NULL) return (0); reg = (long *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 #define TRAP_INTERRUPT 5 static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *); static void db_print_stack_entry(const char *, db_addr_t, void *); static void decode_syscall(int, struct thread *); static const char * watchtype_str(int type); int amd64_set_watch(int watchnum, unsigned long watchaddr, int size, int access, struct dbreg *d); int amd64_clr_watch(int watchnum, struct dbreg *d); static void db_print_stack_entry(const char *name, db_addr_t callpc, void *frame) { db_printf("%s() at ", name != NULL ? name : "??"); db_printsym(callpc, DB_STGY_PROC); if (frame != NULL) db_printf("/frame 0x%lx", (register_t)frame); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) { f = p->p_sysent->sv_table[number].sy_call; sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; long rip, rsp, rbp; db_expr_t offset; c_db_sym_t sym; const char *name; rip = db_get_value((long) &(*fp)->f_retaddr, 8, FALSE); rbp = db_get_value((long) &(*fp)->f_frame, 8, FALSE); /* * Figure out frame type. We look at the address just before * the saved instruction pointer as the saved EIP is after the * call function, and if the function being called is marked as * dead (such as panic() at the end of dblfault_handler()), then * the instruction at the saved EIP will be part of a different * function (syscall() in this example) rather than the one that * actually made the call. */ frame_type = NORMAL; sym = db_search_symbol(rip - 1, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0 || strcmp(name, "nmi_calltrap") == 0 || strcmp(name, "Xdblfault") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0 || strcmp(name, "Xtimerint") == 0 || strcmp(name, "Xipi_intr_bitmap_handler") == 0 || strcmp(name, "Xcpustop") == 0 || strcmp(name, "Xcpususpend") == 0 || strcmp(name, "Xrendezvous") == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xfast_syscall") == 0) frame_type = SYSCALL; #ifdef COMPAT_FREEBSD32 else if (strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; #endif /* XXX: These are interrupts with trap frames. */ else if (strcmp(name, "Xtimerint") == 0 || strcmp(name, "Xcpustop") == 0 || strcmp(name, "Xcpususpend") == 0 || strcmp(name, "Xrendezvous") == 0 || strcmp(name, "Xipi_intr_bitmap_handler") == 0) frame_type = TRAP_INTERRUPT; } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; return; } db_print_stack_entry(name, rip, &(*fp)->f_frame); /* * Point to base of trapframe which is just above the * current frame. */ tf = (struct trapframe *)((long)*fp + 16); if (INKERNEL((long) tf)) { rsp = tf->tf_rsp; rip = tf->tf_rip; rbp = tf->tf_rbp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); decode_syscall(tf->tf_rax, td); break; case TRAP_INTERRUPT: case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", rip = %#lr, rsp = %#lr, rbp = %#lr ---\n", rip, rsp, rbp); } *ip = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; } - static int db_backtrace(struct thread *td, struct trapframe *tf, struct amd64_frame *frame, db_addr_t pc, register_t sp, int count) { struct amd64_frame *actframe; const char *name; db_expr_t offset; c_db_sym_t sym; boolean_t first; if (count == -1) count = 1024; first = TRUE; while (count-- && !db_pager_quit) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { first = FALSE; if (sym == C_DB_SYM_NULL && sp != 0) { /* * If a symbol couldn't be found, we've probably * jumped to a bogus location, so try and use * the return address to find our caller. */ db_print_stack_entry(name, pc, NULL); pc = db_get_value(sp, 8, FALSE); if (db_search_symbol(pc, DB_STGY_PROC, &offset) == C_DB_SYM_NULL) break; continue; } else if (tf != NULL) { int instr; instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffffff) == 0xe5894855) { /* pushq %rbp; movq %rsp, %rbp */ actframe = (void *)(tf->tf_rsp - 8); } else if ((instr & 0xffffff) == 0xe58948) { /* movq %rsp, %rbp */ actframe = (void *)tf->tf_rsp; if (tf->tf_rbp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0xc3) { /* ret */ actframe = (void *)(tf->tf_rsp - 8); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(tf->tf_rsp - 8); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ db_print_stack_entry(name, pc, actframe); break; } } db_print_stack_entry(name, pc, actframe); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((long)&actframe->f_retaddr, 8, FALSE); continue; } db_nextframe(&frame, &pc, td); if (INKERNEL((long)pc) && !INKERNEL((long)frame)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, pc, frame); break; } if (!INKERNEL((long) frame)) { break; } } return (0); } void db_trace_self(void) { struct amd64_frame *frame; db_addr_t callpc; register_t rbp; __asm __volatile("movq %%rbp,%0" : "=r" (rbp)); frame = (struct amd64_frame *)rbp; callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE); frame = frame->f_frame; db_backtrace(curthread, NULL, frame, callpc, 0, -1); -} - -void -db_trace_self_depth(int depth) -{ - struct amd64_frame *frame; - db_addr_t callpc; - register_t rbp; - - __asm __volatile("movq %%rbp,%0" : "=r" (rbp)); - frame = (struct amd64_frame *)rbp; - callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE); - frame = frame->f_frame; - db_backtrace(curthread, NULL, frame, callpc, 0, depth); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; struct trapframe *tf; ctx = kdb_thr_ctx(thr); tf = thr == kdb_thread ? kdb_frame : NULL; return (db_backtrace(thr, tf, (struct amd64_frame *)ctx->pcb_rbp, ctx->pcb_rip, ctx->pcb_rsp, count)); } int amd64_set_watch(watchnum, watchaddr, size, access, d) int watchnum; unsigned long watchaddr; int size; int access; struct dbreg *d; { int i, len; if (watchnum == -1) { for (i = 0; i < 4; i++) if (!DBREG_DR7_ENABLED(d->dr[7], i)) break; if (i < 4) watchnum = i; else return (-1); } switch (access) { case DBREG_DR7_EXEC: size = 1; /* size must be 1 for an execution breakpoint */ /* fall through */ case DBREG_DR7_WRONLY: case DBREG_DR7_RDWR: break; default: return (-1); } /* * we can watch a 1, 2, 4, or 8 byte sized location */ switch (size) { case 1: len = DBREG_DR7_LEN_1; break; case 2: len = DBREG_DR7_LEN_2; break; case 4: len = DBREG_DR7_LEN_4; break; case 8: len = DBREG_DR7_LEN_8; break; default: return (-1); } /* clear the bits we are about to affect */ d->dr[7] &= ~DBREG_DR7_MASK(watchnum); /* set drN register to the address, N=watchnum */ DBREG_DRX(d, watchnum) = watchaddr; /* enable the watchpoint */ d->dr[7] |= DBREG_DR7_SET(watchnum, len, access, DBREG_DR7_GLOBAL_ENABLE); return (watchnum); } int amd64_clr_watch(watchnum, d) int watchnum; struct dbreg *d; { if (watchnum < 0 || watchnum >= 4) return (-1); d->dr[7] &= ~DBREG_DR7_MASK(watchnum); DBREG_DRX(d, watchnum) = 0; return (0); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { struct dbreg *d; struct pcpu *pc; int avail, c, cpu, i, wsize; d = (struct dbreg *)PCPU_PTR(dbreg); cpu = PCPU_GET(cpuid); fill_dbregs(NULL, d); avail = 0; for (i = 0; i < 4; i++) { if (!DBREG_DR7_ENABLED(d->dr[7], i)) avail++; } if (avail * 8 < size) return (-1); for (i = 0; i < 4 && size > 0; i++) { if (!DBREG_DR7_ENABLED(d->dr[7], i)) { if (size >= 8 || (avail == 1 && size > 4)) wsize = 8; else if (size > 2) wsize = 4; else wsize = size; amd64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, d); addr += wsize; size -= wsize; avail--; } } set_dbregs(NULL, d); CPU_FOREACH(c) { if (c == cpu) continue; pc = pcpu_find(c); memcpy(pc->pc_dbreg, d, sizeof(*d)); pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; } return (0); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { struct dbreg *d; struct pcpu *pc; int i, c, cpu; d = (struct dbreg *)PCPU_PTR(dbreg); cpu = PCPU_GET(cpuid); fill_dbregs(NULL, d); for (i = 0; i < 4; i++) { if (DBREG_DR7_ENABLED(d->dr[7], i)) { if (DBREG_DRX((d), i) >= addr && DBREG_DRX((d), i) < addr + size) amd64_clr_watch(i, d); } } set_dbregs(NULL, d); CPU_FOREACH(c) { if (c == cpu) continue; pc = pcpu_find(c); memcpy(pc->pc_dbreg, d, sizeof(*d)); pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; } return (0); } static const char * watchtype_str(type) int type; { switch (type) { case DBREG_DR7_EXEC : return "execute"; break; case DBREG_DR7_RDWR : return "read/write"; break; case DBREG_DR7_WRONLY : return "write"; break; default : return "invalid"; break; } } void db_md_list_watchpoints() { struct dbreg d; int i, len, type; fill_dbregs(NULL, &d); db_printf("\nhardware watchpoints:\n"); db_printf(" watch status type len address\n"); db_printf(" ----- -------- ---------- --- ------------------\n"); for (i = 0; i < 4; i++) { if (DBREG_DR7_ENABLED(d.dr[7], i)) { type = DBREG_DR7_ACCESS(d.dr[7], i); len = DBREG_DR7_LEN(d.dr[7], i); if (len == DBREG_DR7_LEN_8) len = 8; else len++; db_printf(" %-5d %-8s %10s %3d ", i, "enabled", watchtype_str(type), len); db_printsym((db_addr_t)DBREG_DRX((&d), i), DB_STGY_ANY); db_printf("\n"); } else { db_printf(" %-5d disabled\n", i); } } db_printf("\ndebug register values:\n"); for (i = 0; i < 8; i++) { db_printf(" dr%d 0x%016lx\n", i, DBREG_DRX((&d), i)); } db_printf("\n"); } void amd64_db_resume_dbreg(void) { struct dbreg *d; switch (PCPU_GET(dbreg_cmd)) { case PC_DBREG_CMD_LOAD: d = (struct dbreg *)PCPU_PTR(dbreg); set_dbregs(NULL, d); PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE); break; } } Index: head/sys/amd64/cloudabi64/cloudabi64_sysvec.c =================================================================== --- head/sys/amd64/cloudabi64/cloudabi64_sysvec.c (revision 312926) +++ head/sys/amd64/cloudabi64/cloudabi64_sysvec.c (revision 312927) @@ -1,219 +1,217 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include #include #include #include #include -#include #include #include #include extern const char *cloudabi64_syscallnames[]; extern struct sysent cloudabi64_sysent[]; static int cloudabi64_fixup_tcb(register_t **stack_base, struct image_params *imgp) { int error; register_t tcbptr; /* Place auxiliary vector and TCB on the stack. */ error = cloudabi64_fixup(stack_base, imgp); if (error != 0) return (error); /* * On x86-64, the TCB is referred to by %fs:0. Take some space * from the top of the stack to store a single element array, * containing a pointer to the TCB. %fs base will point to this. */ tcbptr = (register_t)*stack_base; return (copyout(&tcbptr, --*stack_base, sizeof(tcbptr))); } static void cloudabi64_proc_setregs(struct thread *td, struct image_params *imgp, unsigned long stack) { struct trapframe *regs; exec_setregs(td, imgp, stack); /* * The stack now contains a pointer to the TCB, the TCB itself, * and the auxiliary vector. Let %rdx point to the auxiliary * vector, and set %fs base to the address of the TCB. */ regs = td->td_frame; regs->tf_rdi = stack + sizeof(register_t) + roundup(sizeof(cloudabi64_tcb_t), sizeof(register_t)); (void)cpu_set_user_tls(td, (void *)stack); } static int cloudabi64_fetch_syscall_args(struct thread *td, struct syscall_args *sa) { struct trapframe *frame = td->td_frame; /* Obtain system call number. */ sa->code = frame->tf_rax; if (sa->code >= CLOUDABI64_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi64_sysent[sa->code]; sa->narg = sa->callp->sy_narg; /* Fetch system call arguments. */ sa->args[0] = frame->tf_rdi; sa->args[1] = frame->tf_rsi; sa->args[2] = frame->tf_rdx; sa->args[3] = frame->tf_rcx; /* Actually %r10. */ sa->args[4] = frame->tf_r8; sa->args[5] = frame->tf_r9; /* Default system call return values. */ td->td_retval[0] = 0; td->td_retval[1] = frame->tf_rdx; return (0); } static void cloudabi64_set_syscall_retval(struct thread *td, int error) { struct trapframe *frame = td->td_frame; switch (error) { case 0: /* System call succeeded. */ frame->tf_rax = td->td_retval[0]; frame->tf_rdx = td->td_retval[1]; frame->tf_rflags &= ~PSL_C; break; case ERESTART: /* Restart system call. */ frame->tf_rip -= frame->tf_err; frame->tf_r10 = frame->tf_rcx; set_pcb_flags(td->td_pcb, PCB_FULL_IRET); break; case EJUSTRETURN: break; default: /* System call returned an error. */ frame->tf_rax = cloudabi_convert_errno(error); frame->tf_rflags |= PSL_C; break; } } static void cloudabi64_schedtail(struct thread *td) { struct trapframe *frame = td->td_frame; /* Initial register values for processes returning from fork. */ frame->tf_rax = CLOUDABI_PROCESS_CHILD; frame->tf_rdx = td->td_tid; } int cloudabi64_thread_setregs(struct thread *td, const cloudabi64_threadattr_t *attr, uint64_t tcb) { struct trapframe *frame; stack_t stack; uint64_t tcbptr; int error; /* * On x86-64, the TCB is referred to by %fs:0. Take some space * from the top of the stack to store a single element array, * containing a pointer to the TCB. %fs base will point to this. */ tcbptr = rounddown(attr->stack + attr->stack_len - sizeof(tcbptr), _Alignof(tcbptr)); error = copyout(&tcb, (void *)tcbptr, sizeof(tcb)); if (error != 0) return (error); /* Perform standard register initialization. */ stack.ss_sp = TO_PTR(attr->stack); stack.ss_size = tcbptr - attr->stack; cpu_set_upcall(td, TO_PTR(attr->entry_point), NULL, &stack); /* * Pass in the thread ID of the new thread and the argument * pointer provided by the parent thread in as arguments to the * entry point. */ frame = td->td_frame; frame->tf_rdi = td->td_tid; frame->tf_rsi = attr->argument; return (cpu_set_user_tls(td, (void *)tcbptr)); } static struct sysentvec cloudabi64_elf_sysvec = { .sv_size = CLOUDABI64_SYS_MAXSYSCALL, .sv_table = cloudabi64_sysent, .sv_fixup = cloudabi64_fixup_tcb, .sv_name = "CloudABI ELF64", .sv_coredump = elf64_coredump, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_strings = cloudabi64_copyout_strings, .sv_setregs = cloudabi64_proc_setregs, .sv_flags = SV_ABI_CLOUDABI | SV_CAPSICUM | SV_LP64, .sv_set_syscall_retval = cloudabi64_set_syscall_retval, .sv_fetch_syscall_args = cloudabi64_fetch_syscall_args, .sv_syscallnames = cloudabi64_syscallnames, .sv_schedtail = cloudabi64_schedtail, }; INIT_SYSENTVEC(elf_sysvec, &cloudabi64_elf_sysvec); Elf64_Brandinfo cloudabi64_brand = { .brand = ELFOSABI_CLOUDABI, .machine = EM_X86_64, .sysvec = &cloudabi64_elf_sysvec, .flags = BI_CAN_EXEC_DYN, .compat_3_brand = "CloudABI", }; Index: head/sys/cam/ctl/ctl_ioctl.h =================================================================== --- head/sys/cam/ctl/ctl_ioctl.h (revision 312926) +++ head/sys/cam/ctl/ctl_ioctl.h (revision 312927) @@ -1,856 +1,856 @@ /*- * Copyright (c) 2003 Silicon Graphics International Corp. * Copyright (c) 2011 Spectra Logic Corporation * Copyright (c) 2014-2017 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_ioctl.h#4 $ * $FreeBSD$ */ /* * CAM Target Layer ioctl interface. * * Author: Ken Merry */ #ifndef _CTL_IOCTL_H_ #define _CTL_IOCTL_H_ #ifdef ICL_KERNEL_PROXY #include #endif #include #define CTL_DEFAULT_DEV "/dev/cam/ctl" /* * Maximum number of targets we support. */ #define CTL_MAX_TARGETS 1 /* * Maximum target ID we support. */ #define CTL_MAX_TARGID 15 /* * Maximum number of LUNs we support at the moment. MUST be a power of 2. */ #define CTL_MAX_LUNS 1024 /* * Maximum number of initiators per port. */ #define CTL_MAX_INIT_PER_PORT 2048 /* * Maximum number of ports registered at one time. */ #define CTL_MAX_PORTS 256 /* * Maximum number of initiators we support. */ #define CTL_MAX_INITIATORS (CTL_MAX_INIT_PER_PORT * CTL_MAX_PORTS) /* Hopefully this won't conflict with new misc devices that pop up */ #define CTL_MINOR 225 /* Legacy statistics accumulated for every port for every LU. */ //#define CTL_LEGACY_STATS 1 typedef enum { CTL_DELAY_TYPE_NONE, CTL_DELAY_TYPE_CONT, CTL_DELAY_TYPE_ONESHOT } ctl_delay_type; typedef enum { CTL_DELAY_LOC_NONE, CTL_DELAY_LOC_DATAMOVE, CTL_DELAY_LOC_DONE, } ctl_delay_location; typedef enum { CTL_DELAY_STATUS_NONE, CTL_DELAY_STATUS_OK, CTL_DELAY_STATUS_INVALID_LUN, CTL_DELAY_STATUS_INVALID_TYPE, CTL_DELAY_STATUS_INVALID_LOC, CTL_DELAY_STATUS_NOT_IMPLEMENTED } ctl_delay_status; struct ctl_io_delay_info { uint32_t lun_id; ctl_delay_type delay_type; ctl_delay_location delay_loc; uint32_t delay_secs; ctl_delay_status status; }; typedef enum { CTL_STATS_NO_IO, CTL_STATS_READ, CTL_STATS_WRITE } ctl_stat_types; #define CTL_STATS_NUM_TYPES 3 typedef enum { CTL_SS_OK, CTL_SS_NEED_MORE_SPACE, CTL_SS_ERROR } ctl_stats_status; typedef enum { CTL_STATS_FLAG_NONE = 0x00, CTL_STATS_FLAG_TIME_VALID = 0x01 } ctl_stats_flags; #ifdef CTL_LEGACY_STATS typedef enum { CTL_LUN_STATS_NO_BLOCKSIZE = 0x01 } ctl_lun_stats_flags; struct ctl_lun_io_port_stats { uint32_t targ_port; uint64_t bytes[CTL_STATS_NUM_TYPES]; uint64_t operations[CTL_STATS_NUM_TYPES]; struct bintime time[CTL_STATS_NUM_TYPES]; uint64_t num_dmas[CTL_STATS_NUM_TYPES]; struct bintime dma_time[CTL_STATS_NUM_TYPES]; }; struct ctl_lun_io_stats { uint8_t device_type; uint64_t lun_number; uint32_t blocksize; ctl_lun_stats_flags flags; - struct ctl_lun_io_port_stats *ports; + struct ctl_lun_io_port_stats ports[CTL_MAX_PORTS]; }; struct ctl_stats { int alloc_len; /* passed to kernel */ struct ctl_lun_io_stats *lun_stats; /* passed to/from kernel */ int fill_len; /* passed to userland */ int num_luns; /* passed to userland */ ctl_stats_status status; /* passed to userland */ ctl_stats_flags flags; /* passed to userland */ struct timespec timestamp; /* passed to userland */ }; #endif /* CTL_LEGACY_STATS */ struct ctl_io_stats { uint32_t item; uint64_t bytes[CTL_STATS_NUM_TYPES]; uint64_t operations[CTL_STATS_NUM_TYPES]; uint64_t dmas[CTL_STATS_NUM_TYPES]; struct bintime time[CTL_STATS_NUM_TYPES]; struct bintime dma_time[CTL_STATS_NUM_TYPES]; }; struct ctl_get_io_stats { struct ctl_io_stats *stats; /* passed to/from kernel */ size_t alloc_len; /* passed to kernel */ size_t fill_len; /* passed to userland */ int first_item; /* passed to kernel */ int num_items; /* passed to userland */ ctl_stats_status status; /* passed to userland */ ctl_stats_flags flags; /* passed to userland */ struct timespec timestamp; /* passed to userland */ }; /* * The types of errors that can be injected: * * NONE: No error specified. * ABORTED: SSD_KEY_ABORTED_COMMAND, 0x45, 0x00 * MEDIUM_ERR: Medium error, different asc/ascq depending on read/write. * UA: Unit attention. * CUSTOM: User specifies the sense data. * TYPE: Mask to use with error types. * * Flags that affect injection behavior: * CONTINUOUS: This error will stay around until explicitly cleared. * DESCRIPTOR: Use descriptor sense instead of fixed sense. */ typedef enum { CTL_LUN_INJ_NONE = 0x000, CTL_LUN_INJ_ABORTED = 0x001, CTL_LUN_INJ_MEDIUM_ERR = 0x002, CTL_LUN_INJ_UA = 0x003, CTL_LUN_INJ_CUSTOM = 0x004, CTL_LUN_INJ_TYPE = 0x0ff, CTL_LUN_INJ_CONTINUOUS = 0x100, CTL_LUN_INJ_DESCRIPTOR = 0x200 } ctl_lun_error; /* * Flags to specify what type of command the given error pattern will * execute on. The first group of types can be ORed together. * * READ: Any read command. * WRITE: Any write command. * READWRITE: Any read or write command. * READCAP: Any read capacity command. * TUR: Test Unit Ready. * ANY: Any command. * MASK: Mask for basic command patterns. * * Special types: * * CMD: The CDB to act on is specified in struct ctl_error_desc_cmd. * RANGE: For read/write commands, act when the LBA is in the * specified range. */ typedef enum { CTL_LUN_PAT_NONE = 0x000, CTL_LUN_PAT_READ = 0x001, CTL_LUN_PAT_WRITE = 0x002, CTL_LUN_PAT_READWRITE = CTL_LUN_PAT_READ | CTL_LUN_PAT_WRITE, CTL_LUN_PAT_READCAP = 0x004, CTL_LUN_PAT_TUR = 0x008, CTL_LUN_PAT_ANY = 0x0ff, CTL_LUN_PAT_MASK = 0x0ff, CTL_LUN_PAT_CMD = 0x100, CTL_LUN_PAT_RANGE = 0x200 } ctl_lun_error_pattern; /* * This structure allows the user to specify a particular CDB pattern to * look for. * * cdb_pattern: Fill in the relevant bytes to look for in the CDB. * cdb_valid_bytes: Bitmask specifying valid bytes in the cdb_pattern. * flags: Specify any command flags (see ctl_io_flags) that * should be set. */ struct ctl_error_desc_cmd { uint8_t cdb_pattern[CTL_MAX_CDBLEN]; uint32_t cdb_valid_bytes; uint32_t flags; }; /* * Error injection descriptor. * * lun_id LUN to act on. * lun_error: The type of error to inject. See above for descriptions. * error_pattern: What kind of command to act on. See above. * cmd_desc: For CTL_LUN_PAT_CMD only. * lba_range: For CTL_LUN_PAT_RANGE only. * custom_sense: Specify sense. For CTL_LUN_INJ_CUSTOM only. * serial: Serial number returned by the kernel. Use for deletion. * links: Kernel use only. */ struct ctl_error_desc { uint32_t lun_id; /* To kernel */ ctl_lun_error lun_error; /* To kernel */ ctl_lun_error_pattern error_pattern; /* To kernel */ struct ctl_error_desc_cmd cmd_desc; /* To kernel */ struct ctl_lba_len lba_range; /* To kernel */ struct scsi_sense_data custom_sense; /* To kernel */ uint64_t serial; /* From kernel */ STAILQ_ENTRY(ctl_error_desc) links; /* Kernel use only */ }; typedef enum { CTL_OOA_FLAG_NONE = 0x00, CTL_OOA_FLAG_ALL_LUNS = 0x01 } ctl_ooa_flags; typedef enum { CTL_OOA_OK, CTL_OOA_NEED_MORE_SPACE, CTL_OOA_ERROR } ctl_get_ooa_status; typedef enum { CTL_OOACMD_FLAG_NONE = 0x00, CTL_OOACMD_FLAG_DMA = 0x01, CTL_OOACMD_FLAG_BLOCKED = 0x02, CTL_OOACMD_FLAG_ABORT = 0x04, CTL_OOACMD_FLAG_RTR = 0x08, CTL_OOACMD_FLAG_DMA_QUEUED = 0x10 } ctl_ooa_cmd_flags; struct ctl_ooa_entry { ctl_ooa_cmd_flags cmd_flags; uint8_t cdb[CTL_MAX_CDBLEN]; uint8_t cdb_len; uint32_t tag_num; uint32_t lun_num; struct bintime start_bt; }; struct ctl_ooa { ctl_ooa_flags flags; /* passed to kernel */ uint64_t lun_num; /* passed to kernel */ uint32_t alloc_len; /* passed to kernel */ uint32_t alloc_num; /* passed to kernel */ struct ctl_ooa_entry *entries; /* filled in kernel */ uint32_t fill_len; /* passed to userland */ uint32_t fill_num; /* passed to userland */ uint32_t dropped_num; /* passed to userland */ struct bintime cur_bt; /* passed to userland */ ctl_get_ooa_status status; /* passed to userland */ }; typedef enum { CTL_LUN_NOSTATUS, CTL_LUN_OK, CTL_LUN_ERROR, CTL_LUN_WARNING } ctl_lun_status; #define CTL_ERROR_STR_LEN 160 #define CTL_BEARG_RD 0x01 #define CTL_BEARG_WR 0x02 #define CTL_BEARG_RW (CTL_BEARG_RD|CTL_BEARG_WR) #define CTL_BEARG_ASCII 0x04 /* * Backend Argument: * * namelen: Length of the name field, including the terminating NUL. * * name: Name of the parameter. This must be NUL-terminated. * * flags: Flags for the parameter, see above for values. * * vallen: Length of the value in bytes, including the terminating NUL. * * value: Value to be set/fetched. This must be NUL-terminated. * * kname: For kernel use only. * * kvalue: For kernel use only. */ struct ctl_be_arg { unsigned int namelen; char *name; int flags; unsigned int vallen; void *value; char *kname; void *kvalue; }; typedef enum { CTL_LUNREQ_CREATE, CTL_LUNREQ_RM, CTL_LUNREQ_MODIFY, } ctl_lunreq_type; /* * The ID_REQ flag is used to say that the caller has requested a * particular LUN ID in the req_lun_id field. If we cannot allocate that * LUN ID, the ctl_add_lun() call will fail. * * The STOPPED flag tells us that the LUN should default to the powered * off state. It will return 0x04,0x02 until it is powered up. ("Logical * unit not ready, initializing command required.") * * The NO_MEDIA flag tells us that the LUN has no media inserted. * * The PRIMARY flag tells us that this LUN is registered as a Primary LUN * which is accessible via the Master shelf controller in an HA. This flag * being set indicates a Primary LUN. This flag being reset represents a * Secondary LUN controlled by the Secondary controller in an HA * configuration. Flag is applicable at this time to T_DIRECT types. * * The SERIAL_NUM flag tells us that the serial_num field is filled in and * valid for use in SCSI INQUIRY VPD page 0x80. * * The DEVID flag tells us that the device_id field is filled in and * valid for use in SCSI INQUIRY VPD page 0x83. * * The DEV_TYPE flag tells us that the device_type field is filled in. * * The EJECTED flag tells us that the removable LUN has tray open. * * The UNMAP flag tells us that this LUN supports UNMAP. * * The OFFLINE flag tells us that this LUN can not access backing store. */ typedef enum { CTL_LUN_FLAG_ID_REQ = 0x01, CTL_LUN_FLAG_STOPPED = 0x02, CTL_LUN_FLAG_NO_MEDIA = 0x04, CTL_LUN_FLAG_PRIMARY = 0x08, CTL_LUN_FLAG_SERIAL_NUM = 0x10, CTL_LUN_FLAG_DEVID = 0x20, CTL_LUN_FLAG_DEV_TYPE = 0x40, CTL_LUN_FLAG_UNMAP = 0x80, CTL_LUN_FLAG_EJECTED = 0x100, CTL_LUN_FLAG_READONLY = 0x200 } ctl_backend_lun_flags; /* * LUN creation parameters: * * flags: Various LUN flags, see above. * * device_type: The SCSI device type. e.g. 0 for Direct Access, * 3 for Processor, etc. Only certain backends may * support setting this field. The CTL_LUN_FLAG_DEV_TYPE * flag should be set in the flags field if the device * type is set. * * lun_size_bytes: The size of the LUN in bytes. For some backends * this is relevant (e.g. ramdisk), for others, it may * be ignored in favor of using the properties of the * backing store. If specified, this should be a * multiple of the blocksize. * * The actual size of the LUN is returned in this * field. * * blocksize_bytes: The LUN blocksize in bytes. For some backends this * is relevant, for others it may be ignored in * favor of using the properties of the backing store. * * The actual blocksize of the LUN is returned in this * field. * * req_lun_id: The requested LUN ID. The CTL_LUN_FLAG_ID_REQ flag * should be set if this is set. The request will be * granted if the LUN number is available, otherwise * the LUN addition request will fail. * * The allocated LUN number is returned in this field. * * serial_num: This is the value returned in SCSI INQUIRY VPD page * 0x80. If it is specified, the CTL_LUN_FLAG_SERIAL_NUM * flag should be set. * * The serial number value used is returned in this * field. * * device_id: This is the value returned in the T10 vendor ID * based DESIGNATOR field in the SCSI INQUIRY VPD page * 0x83 data. If it is specified, the CTL_LUN_FLAG_DEVID * flag should be set. * * The device id value used is returned in this field. */ struct ctl_lun_create_params { ctl_backend_lun_flags flags; uint8_t device_type; uint64_t lun_size_bytes; uint32_t blocksize_bytes; uint32_t req_lun_id; uint8_t serial_num[CTL_SN_LEN]; uint8_t device_id[CTL_DEVID_LEN]; }; /* * LUN removal parameters: * * lun_id: The number of the LUN to delete. This must be set. * The LUN must be backed by the given backend. */ struct ctl_lun_rm_params { uint32_t lun_id; }; /* * LUN modification parameters: * * lun_id: The number of the LUN to modify. This must be set. * The LUN must be backed by the given backend. * * lun_size_bytes: The size of the LUN in bytes. If zero, update * the size using the backing file size, if possible. */ struct ctl_lun_modify_params { uint32_t lun_id; uint64_t lun_size_bytes; }; /* * Union of request type data. Fill in the appropriate union member for * the request type. */ union ctl_lunreq_data { struct ctl_lun_create_params create; struct ctl_lun_rm_params rm; struct ctl_lun_modify_params modify; }; /* * LUN request interface: * * backend: This is required, and is NUL-terminated a string * that is the name of the backend, like "ramdisk" or * "block". * * reqtype: The type of request, CTL_LUNREQ_CREATE to create a * LUN, CTL_LUNREQ_RM to delete a LUN. * * reqdata: Request type-specific information. See the * description of individual the union members above * for more information. * * num_be_args: This is the number of backend-specific arguments * in the be_args array. * * be_args: This is an array of backend-specific arguments. * See above for a description of the fields in this * structure. * * status: Status of the LUN request. * * error_str: If the status is CTL_LUN_ERROR, this will * contain a string describing the error. * * kern_be_args: For kernel use only. */ struct ctl_lun_req { #define CTL_BE_NAME_LEN 32 char backend[CTL_BE_NAME_LEN]; ctl_lunreq_type reqtype; union ctl_lunreq_data reqdata; int num_be_args; struct ctl_be_arg *be_args; ctl_lun_status status; char error_str[CTL_ERROR_STR_LEN]; struct ctl_be_arg *kern_be_args; }; /* * LUN list status: * * NONE: No status. * * OK: Request completed successfully. * * NEED_MORE_SPACE: The allocated length of the entries field is too * small for the available data. * * ERROR: An error occurred, look at the error string for a * description of the error. */ typedef enum { CTL_LUN_LIST_NONE, CTL_LUN_LIST_OK, CTL_LUN_LIST_NEED_MORE_SPACE, CTL_LUN_LIST_ERROR } ctl_lun_list_status; /* * LUN list interface * * backend_name: This is a NUL-terminated string. If the string * length is 0, then all LUNs on all backends will * be enumerated. Otherwise this is the name of the * backend to be enumerated, like "ramdisk" or "block". * * alloc_len: The length of the data buffer allocated for entries. * In order to properly size the buffer, make one call * with alloc_len set to 0, and then use the returned * dropped_len as the buffer length to allocate and * pass in on a subsequent call. * * lun_xml: XML-formatted information on the requested LUNs. * * fill_len: The amount of data filled in the storage for entries. * * status: The status of the request. See above for the * description of the values of this field. * * error_str: If the status indicates an error, this string will * be filled in to describe the error. */ struct ctl_lun_list { char backend[CTL_BE_NAME_LEN]; /* passed to kernel*/ uint32_t alloc_len; /* passed to kernel */ char *lun_xml; /* filled in kernel */ uint32_t fill_len; /* passed to userland */ ctl_lun_list_status status; /* passed to userland */ char error_str[CTL_ERROR_STR_LEN]; /* passed to userland */ }; /* * Port request interface: * * driver: This is required, and is NUL-terminated a string * that is the name of the frontend, like "iscsi" . * * reqtype: The type of request, CTL_REQ_CREATE to create a * port, CTL_REQ_REMOVE to delete a port. * * num_be_args: This is the number of frontend-specific arguments * in the be_args array. * * be_args: This is an array of frontend-specific arguments. * See above for a description of the fields in this * structure. * * status: Status of the request. * * error_str: If the status is CTL_LUN_ERROR, this will * contain a string describing the error. * * kern_be_args: For kernel use only. */ typedef enum { CTL_REQ_CREATE, CTL_REQ_REMOVE, CTL_REQ_MODIFY, } ctl_req_type; struct ctl_req { char driver[CTL_DRIVER_NAME_LEN]; ctl_req_type reqtype; int num_args; struct ctl_be_arg *args; ctl_lun_status status; char error_str[CTL_ERROR_STR_LEN]; struct ctl_be_arg *kern_args; }; /* * iSCSI status * * OK: Request completed successfully. * * ERROR: An error occurred, look at the error string for a * description of the error. * * CTL_ISCSI_LIST_NEED_MORE_SPACE: * User has to pass larger buffer for CTL_ISCSI_LIST ioctl. */ typedef enum { CTL_ISCSI_OK, CTL_ISCSI_ERROR, CTL_ISCSI_LIST_NEED_MORE_SPACE, CTL_ISCSI_SESSION_NOT_FOUND } ctl_iscsi_status; typedef enum { CTL_ISCSI_HANDOFF, CTL_ISCSI_LIST, CTL_ISCSI_LOGOUT, CTL_ISCSI_TERMINATE, CTL_ISCSI_LIMITS, #if defined(ICL_KERNEL_PROXY) || 1 /* * We actually need those in all cases, but leave the ICL_KERNEL_PROXY, * to remember to remove them along with rest of proxy code, eventually. */ CTL_ISCSI_LISTEN, CTL_ISCSI_ACCEPT, CTL_ISCSI_SEND, CTL_ISCSI_RECEIVE, #endif } ctl_iscsi_type; typedef enum { CTL_ISCSI_DIGEST_NONE, CTL_ISCSI_DIGEST_CRC32C } ctl_iscsi_digest; #define CTL_ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */ #define CTL_ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */ #define CTL_ISCSI_ALIAS_LEN 128 /* Arbitrary. */ #define CTL_ISCSI_OFFLOAD_LEN 8 /* Arbitrary. */ struct ctl_iscsi_handoff_params { char initiator_name[CTL_ISCSI_NAME_LEN]; char initiator_addr[CTL_ISCSI_ADDR_LEN]; char initiator_alias[CTL_ISCSI_ALIAS_LEN]; uint8_t initiator_isid[6]; char target_name[CTL_ISCSI_NAME_LEN]; int socket; int portal_group_tag; /* * Connection parameters negotiated by ctld(8). */ ctl_iscsi_digest header_digest; ctl_iscsi_digest data_digest; uint32_t cmdsn; uint32_t statsn; int max_recv_data_segment_length; int max_burst_length; int first_burst_length; uint32_t immediate_data; char offload[CTL_ISCSI_OFFLOAD_LEN]; #ifdef ICL_KERNEL_PROXY int connection_id; #else int spare; #endif int max_send_data_segment_length; }; struct ctl_iscsi_list_params { uint32_t alloc_len; /* passed to kernel */ char *conn_xml; /* filled in kernel */ uint32_t fill_len; /* passed to userland */ int spare[4]; }; struct ctl_iscsi_logout_params { int connection_id; /* passed to kernel */ char initiator_name[CTL_ISCSI_NAME_LEN]; /* passed to kernel */ char initiator_addr[CTL_ISCSI_ADDR_LEN]; /* passed to kernel */ int all; /* passed to kernel */ int spare[4]; }; struct ctl_iscsi_terminate_params { int connection_id; /* passed to kernel */ char initiator_name[CTL_ISCSI_NAME_LEN]; /* passed to kernel */ char initiator_addr[CTL_ISCSI_NAME_LEN]; /* passed to kernel */ int all; /* passed to kernel */ int spare[4]; }; struct ctl_iscsi_limits_params { /* passed to kernel */ char offload[CTL_ISCSI_OFFLOAD_LEN]; /* passed to userland */ size_t spare; int max_recv_data_segment_length; int max_send_data_segment_length; int max_burst_length; int first_burst_length; }; #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params { int iser; int domain; int socktype; int protocol; struct sockaddr *addr; socklen_t addrlen; int portal_id; int spare[4]; }; struct ctl_iscsi_accept_params { int connection_id; int portal_id; struct sockaddr *initiator_addr; socklen_t initiator_addrlen; int spare[4]; }; struct ctl_iscsi_send_params { int connection_id; void *bhs; size_t spare; void *spare2; size_t data_segment_len; void *data_segment; int spare3[4]; }; struct ctl_iscsi_receive_params { int connection_id; void *bhs; size_t spare; void *spare2; size_t data_segment_len; void *data_segment; int spare3[4]; }; #endif /* ICL_KERNEL_PROXY */ union ctl_iscsi_data { struct ctl_iscsi_handoff_params handoff; struct ctl_iscsi_list_params list; struct ctl_iscsi_logout_params logout; struct ctl_iscsi_terminate_params terminate; struct ctl_iscsi_limits_params limits; #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params listen; struct ctl_iscsi_accept_params accept; struct ctl_iscsi_send_params send; struct ctl_iscsi_receive_params receive; #endif }; /* * iSCSI interface * * status: The status of the request. See above for the * description of the values of this field. * * error_str: If the status indicates an error, this string will * be filled in to describe the error. */ struct ctl_iscsi { ctl_iscsi_type type; /* passed to kernel */ union ctl_iscsi_data data; /* passed to kernel */ ctl_iscsi_status status; /* passed to userland */ char error_str[CTL_ERROR_STR_LEN]; /* passed to userland */ }; struct ctl_lun_map { uint32_t port; uint32_t plun; uint32_t lun; }; #define CTL_IO _IOWR(CTL_MINOR, 0x00, union ctl_io) #define CTL_ENABLE_PORT _IOW(CTL_MINOR, 0x04, struct ctl_port_entry) #define CTL_DISABLE_PORT _IOW(CTL_MINOR, 0x05, struct ctl_port_entry) #define CTL_DELAY_IO _IOWR(CTL_MINOR, 0x10, struct ctl_io_delay_info) #define CTL_GETSTATS _IOWR(CTL_MINOR, 0x15, struct ctl_stats) #define CTL_ERROR_INJECT _IOWR(CTL_MINOR, 0x16, struct ctl_error_desc) #define CTL_GET_OOA _IOWR(CTL_MINOR, 0x18, struct ctl_ooa) #define CTL_DUMP_STRUCTS _IO(CTL_MINOR, 0x19) #define CTL_LUN_REQ _IOWR(CTL_MINOR, 0x21, struct ctl_lun_req) #define CTL_LUN_LIST _IOWR(CTL_MINOR, 0x22, struct ctl_lun_list) #define CTL_ERROR_INJECT_DELETE _IOW(CTL_MINOR, 0x23, struct ctl_error_desc) #define CTL_SET_PORT_WWNS _IOW(CTL_MINOR, 0x24, struct ctl_port_entry) #define CTL_ISCSI _IOWR(CTL_MINOR, 0x25, struct ctl_iscsi) #define CTL_PORT_REQ _IOWR(CTL_MINOR, 0x26, struct ctl_req) #define CTL_PORT_LIST _IOWR(CTL_MINOR, 0x27, struct ctl_lun_list) #define CTL_LUN_MAP _IOW(CTL_MINOR, 0x28, struct ctl_lun_map) #define CTL_GET_LUN_STATS _IOWR(CTL_MINOR, 0x29, struct ctl_get_io_stats) #define CTL_GET_PORT_STATS _IOWR(CTL_MINOR, 0x2a, struct ctl_get_io_stats) #endif /* _CTL_IOCTL_H_ */ /* * vim: ts=8 */ Index: head/sys/cddl/dev/fbt/x86/fbt_isa.c =================================================================== --- head/sys/cddl/dev/fbt/x86/fbt_isa.c (revision 312926) +++ head/sys/cddl/dev/fbt/x86/fbt_isa.c (revision 312927) @@ -1,333 +1,331 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Portions Copyright 2006-2008 John Birrell jb@freebsd.org * * $FreeBSD$ * */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include -#include - #include "fbt.h" #define FBT_PUSHL_EBP 0x55 #define FBT_MOVL_ESP_EBP0_V0 0x8b #define FBT_MOVL_ESP_EBP1_V0 0xec #define FBT_MOVL_ESP_EBP0_V1 0x89 #define FBT_MOVL_ESP_EBP1_V1 0xe5 #define FBT_REX_RSP_RBP 0x48 #define FBT_POPL_EBP 0x5d #define FBT_RET 0xc3 #define FBT_RET_IMM16 0xc2 #define FBT_LEAVE 0xc9 #ifdef __amd64__ #define FBT_PATCHVAL 0xcc #else #define FBT_PATCHVAL 0xf0 #endif #define FBT_ENTRY "entry" #define FBT_RETURN "return" int fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) { solaris_cpu_t *cpu; uintptr_t *stack; uintptr_t arg0, arg1, arg2, arg3, arg4; fbt_probe_t *fbt; #ifdef __amd64__ stack = (uintptr_t *)frame->tf_rsp; #else /* Skip hardware-saved registers. */ stack = (uintptr_t *)frame->tf_isp + 3; #endif cpu = &solaris_cpu[curcpu]; fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { if ((uintptr_t)fbt->fbtp_patchpoint == addr) { if (fbt->fbtp_roffset == 0) { #ifdef __amd64__ /* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); cpu->cpu_dtrace_caller = stack[0]; DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); arg0 = frame->tf_rdi; arg1 = frame->tf_rsi; arg2 = frame->tf_rdx; arg3 = frame->tf_rcx; arg4 = frame->tf_r8; #else int i = 0; /* * When accessing the arguments on the stack, * we must protect against accessing beyond * the stack. We can safely set NOFAULT here * -- we know that interrupts are already * disabled. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); cpu->cpu_dtrace_caller = stack[i++]; arg0 = stack[i++]; arg1 = stack[i++]; arg2 = stack[i++]; arg3 = stack[i++]; arg4 = stack[i++]; DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); #endif dtrace_probe(fbt->fbtp_id, arg0, arg1, arg2, arg3, arg4); cpu->cpu_dtrace_caller = 0; } else { #ifdef __amd64__ /* * On amd64, we instrument the ret, not the * leave. We therefore need to set the caller * to ensure that the top frame of a stack() * action is correct. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); cpu->cpu_dtrace_caller = stack[0]; DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); #endif dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, rval, 0, 0, 0); cpu->cpu_dtrace_caller = 0; } return (fbt->fbtp_rval); } } return (0); } void fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) { *fbt->fbtp_patchpoint = val; } int fbt_provide_module_function(linker_file_t lf, int symindx, linker_symval_t *symval, void *opaque) { char *modname = opaque; const char *name = symval->name; fbt_probe_t *fbt, *retfbt; int j; int size; uint8_t *instr, *limit; if (fbt_excluded(name)) return (0); /* * trap_check() is a wrapper for DTrace's fault handler, so we don't * want to be able to instrument it. */ if (strcmp(name, "trap_check") == 0) return (0); size = symval->size; instr = (uint8_t *) symval->value; limit = (uint8_t *) symval->value + symval->size; #ifdef __amd64__ while (instr < limit) { if (*instr == FBT_PUSHL_EBP) break; if ((size = dtrace_instr_size(instr)) <= 0) break; instr += size; } if (instr >= limit || *instr != FBT_PUSHL_EBP) { /* * We either don't save the frame pointer in this * function, or we ran into some disassembly * screw-up. Either way, we bail. */ return (0); } #else if (instr[0] != FBT_PUSHL_EBP) return (0); if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 && instr[2] == FBT_MOVL_ESP_EBP1_V0) && !(instr[1] == FBT_MOVL_ESP_EBP0_V1 && instr[2] == FBT_MOVL_ESP_EBP1_V1)) return (0); #endif fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); fbt->fbtp_name = name; fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, name, FBT_ENTRY, 3, fbt); fbt->fbtp_patchpoint = instr; fbt->fbtp_ctl = lf; fbt->fbtp_loadcnt = lf->loadcnt; fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP; fbt->fbtp_savedval = *instr; fbt->fbtp_patchval = FBT_PATCHVAL; fbt->fbtp_symindx = symindx; fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; lf->fbt_nentries++; retfbt = NULL; again: if (instr >= limit) return (0); /* * If this disassembly fails, then we've likely walked off into * a jump table or some other unsuitable area. Bail out of the * disassembly now. */ if ((size = dtrace_instr_size(instr)) <= 0) return (0); #ifdef __amd64__ /* * We only instrument "ret" on amd64 -- we don't yet instrument * ret imm16, largely because the compiler doesn't seem to * (yet) emit them in the kernel... */ if (*instr != FBT_RET) { instr += size; goto again; } #else if (!(size == 1 && (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) && (*(instr + 1) == FBT_RET || *(instr + 1) == FBT_RET_IMM16))) { instr += size; goto again; } #endif /* * We (desperately) want to avoid erroneously instrumenting a * jump table, especially given that our markers are pretty * short: two bytes on x86, and just one byte on amd64. To * determine if we're looking at a true instruction sequence * or an inline jump table that happens to contain the same * byte sequences, we resort to some heuristic sleeze: we * treat this instruction as being contained within a pointer, * and see if that pointer points to within the body of the * function. If it does, we refuse to instrument it. */ for (j = 0; j < sizeof (uintptr_t); j++) { caddr_t check = (caddr_t) instr - j; uint8_t *ptr; if (check < symval->value) break; if (check + sizeof (caddr_t) > (caddr_t)limit) continue; ptr = *(uint8_t **)check; if (ptr >= (uint8_t *) symval->value && ptr < limit) { instr += size; goto again; } } /* * We have a winner! */ fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); fbt->fbtp_name = name; if (retfbt == NULL) { fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, name, FBT_RETURN, 3, fbt); } else { retfbt->fbtp_next = fbt; fbt->fbtp_id = retfbt->fbtp_id; } retfbt = fbt; fbt->fbtp_patchpoint = instr; fbt->fbtp_ctl = lf; fbt->fbtp_loadcnt = lf->loadcnt; fbt->fbtp_symindx = symindx; #ifndef __amd64__ if (*instr == FBT_POPL_EBP) { fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP; } else { ASSERT(*instr == FBT_LEAVE); fbt->fbtp_rval = DTRACE_INVOP_LEAVE; } fbt->fbtp_roffset = (uintptr_t)(instr - (uint8_t *) symval->value) + 1; #else ASSERT(*instr == FBT_RET); fbt->fbtp_rval = DTRACE_INVOP_RET; fbt->fbtp_roffset = (uintptr_t)(instr - (uint8_t *) symval->value); #endif fbt->fbtp_savedval = *instr; fbt->fbtp_patchval = FBT_PATCHVAL; fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; lf->fbt_nentries++; instr += size; goto again; } Index: head/sys/compat/cloudabi/cloudabi_fd.c =================================================================== --- head/sys/compat/cloudabi/cloudabi_fd.c (revision 312926) +++ head/sys/compat/cloudabi/cloudabi_fd.c (revision 312927) @@ -1,547 +1,546 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include /* Translation between CloudABI and Capsicum rights. */ #define RIGHTS_MAPPINGS \ MAPPING(CLOUDABI_RIGHT_FD_DATASYNC, CAP_FSYNC) \ MAPPING(CLOUDABI_RIGHT_FD_READ, CAP_READ) \ MAPPING(CLOUDABI_RIGHT_FD_SEEK, CAP_SEEK) \ MAPPING(CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS, CAP_FCNTL) \ MAPPING(CLOUDABI_RIGHT_FD_SYNC, CAP_FSYNC) \ MAPPING(CLOUDABI_RIGHT_FD_TELL, CAP_SEEK_TELL) \ MAPPING(CLOUDABI_RIGHT_FD_WRITE, CAP_WRITE) \ MAPPING(CLOUDABI_RIGHT_FILE_ADVISE) \ MAPPING(CLOUDABI_RIGHT_FILE_ALLOCATE, CAP_WRITE) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FIFO, CAP_MKFIFOAT) \ MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LINKAT_SOURCE) \ MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT_TARGET) \ MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP) \ MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ) \ MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP) \ MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT_SOURCE) \ MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_RENAMEAT_TARGET) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES, CAP_FUTIMES) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_GET, CAP_FSTATAT) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES, CAP_FUTIMESAT) \ MAPPING(CLOUDABI_RIGHT_FILE_SYMLINK, CAP_SYMLINKAT) \ MAPPING(CLOUDABI_RIGHT_FILE_UNLINK, CAP_UNLINKAT) \ MAPPING(CLOUDABI_RIGHT_MEM_MAP, CAP_MMAP) \ MAPPING(CLOUDABI_RIGHT_MEM_MAP_EXEC, CAP_MMAP_X) \ MAPPING(CLOUDABI_RIGHT_POLL_FD_READWRITE, CAP_EVENT) \ MAPPING(CLOUDABI_RIGHT_POLL_MODIFY, CAP_KQUEUE_CHANGE) \ MAPPING(CLOUDABI_RIGHT_POLL_PROC_TERMINATE, CAP_EVENT) \ MAPPING(CLOUDABI_RIGHT_POLL_WAIT, CAP_KQUEUE_EVENT) \ MAPPING(CLOUDABI_RIGHT_PROC_EXEC, CAP_FEXECVE) \ MAPPING(CLOUDABI_RIGHT_SOCK_ACCEPT, CAP_ACCEPT) \ MAPPING(CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY, CAP_BINDAT) \ MAPPING(CLOUDABI_RIGHT_SOCK_BIND_SOCKET, CAP_BIND) \ MAPPING(CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY, CAP_CONNECTAT) \ MAPPING(CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET, CAP_CONNECT) \ MAPPING(CLOUDABI_RIGHT_SOCK_LISTEN, CAP_LISTEN) \ MAPPING(CLOUDABI_RIGHT_SOCK_SHUTDOWN, CAP_SHUTDOWN) \ MAPPING(CLOUDABI_RIGHT_SOCK_STAT_GET, CAP_GETPEERNAME, \ CAP_GETSOCKNAME, CAP_GETSOCKOPT) int cloudabi_sys_fd_close(struct thread *td, struct cloudabi_sys_fd_close_args *uap) { return (kern_close(td, uap->fd)); } int cloudabi_sys_fd_create1(struct thread *td, struct cloudabi_sys_fd_create1_args *uap) { struct filecaps fcaps = {}; struct socket_args socket_args = { .domain = AF_UNIX, }; switch (uap->type) { case CLOUDABI_FILETYPE_POLL: cap_rights_init(&fcaps.fc_rights, CAP_FSTAT, CAP_KQUEUE); return (kern_kqueue(td, 0, &fcaps)); case CLOUDABI_FILETYPE_SHARED_MEMORY: cap_rights_init(&fcaps.fc_rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_MMAP_RWX); return (kern_shm_open(td, SHM_ANON, O_RDWR, 0, &fcaps)); case CLOUDABI_FILETYPE_SOCKET_DGRAM: socket_args.type = SOCK_DGRAM; return (sys_socket(td, &socket_args)); case CLOUDABI_FILETYPE_SOCKET_SEQPACKET: socket_args.type = SOCK_SEQPACKET; return (sys_socket(td, &socket_args)); case CLOUDABI_FILETYPE_SOCKET_STREAM: socket_args.type = SOCK_STREAM; return (sys_socket(td, &socket_args)); default: return (EINVAL); } } int cloudabi_sys_fd_create2(struct thread *td, struct cloudabi_sys_fd_create2_args *uap) { struct filecaps fcaps1 = {}, fcaps2 = {}; int fds[2]; int error; switch (uap->type) { case CLOUDABI_FILETYPE_FIFO: /* * CloudABI pipes are unidirectional. Restrict rights on * the pipe to simulate this. */ cap_rights_init(&fcaps1.fc_rights, CAP_EVENT, CAP_FCNTL, CAP_FSTAT, CAP_READ); fcaps1.fc_fcntls = CAP_FCNTL_SETFL; cap_rights_init(&fcaps2.fc_rights, CAP_EVENT, CAP_FCNTL, CAP_FSTAT, CAP_WRITE); fcaps2.fc_fcntls = CAP_FCNTL_SETFL; error = kern_pipe(td, fds, 0, &fcaps1, &fcaps2); break; case CLOUDABI_FILETYPE_SOCKET_DGRAM: error = kern_socketpair(td, AF_UNIX, SOCK_DGRAM, 0, fds); break; case CLOUDABI_FILETYPE_SOCKET_SEQPACKET: error = kern_socketpair(td, AF_UNIX, SOCK_SEQPACKET, 0, fds); break; case CLOUDABI_FILETYPE_SOCKET_STREAM: error = kern_socketpair(td, AF_UNIX, SOCK_STREAM, 0, fds); break; default: return (EINVAL); } if (error == 0) { td->td_retval[0] = fds[0]; td->td_retval[1] = fds[1]; } return (0); } int cloudabi_sys_fd_datasync(struct thread *td, struct cloudabi_sys_fd_datasync_args *uap) { return (kern_fsync(td, uap->fd, false)); } int cloudabi_sys_fd_dup(struct thread *td, struct cloudabi_sys_fd_dup_args *uap) { return (kern_dup(td, FDDUP_NORMAL, 0, uap->from, 0)); } int cloudabi_sys_fd_replace(struct thread *td, struct cloudabi_sys_fd_replace_args *uap) { int error; /* * CloudABI's equivalent to dup2(). CloudABI processes should * not depend on hardcoded file descriptor layouts, but simply * use the file descriptor numbers that are allocated by the * kernel. Duplicating file descriptors to arbitrary numbers * should not be done. * * Invoke kern_dup() with FDDUP_MUSTREPLACE, so that we return * EBADF when duplicating to a nonexistent file descriptor. Also * clear the return value, as this system call yields no return * value. */ error = kern_dup(td, FDDUP_MUSTREPLACE, 0, uap->from, uap->to); td->td_retval[0] = 0; return (error); } int cloudabi_sys_fd_seek(struct thread *td, struct cloudabi_sys_fd_seek_args *uap) { struct lseek_args lseek_args = { .fd = uap->fd, .offset = uap->offset }; switch (uap->whence) { case CLOUDABI_WHENCE_CUR: lseek_args.whence = SEEK_CUR; break; case CLOUDABI_WHENCE_END: lseek_args.whence = SEEK_END; break; case CLOUDABI_WHENCE_SET: lseek_args.whence = SEEK_SET; break; default: return (EINVAL); } return (sys_lseek(td, &lseek_args)); } /* Converts a file descriptor to a CloudABI file descriptor type. */ cloudabi_filetype_t cloudabi_convert_filetype(const struct file *fp) { struct socket *so; struct vnode *vp; switch (fp->f_type) { case DTYPE_FIFO: return (CLOUDABI_FILETYPE_FIFO); case DTYPE_KQUEUE: return (CLOUDABI_FILETYPE_POLL); case DTYPE_PIPE: return (CLOUDABI_FILETYPE_FIFO); case DTYPE_PROCDESC: return (CLOUDABI_FILETYPE_PROCESS); case DTYPE_SHM: return (CLOUDABI_FILETYPE_SHARED_MEMORY); case DTYPE_SOCKET: so = fp->f_data; switch (so->so_type) { case SOCK_DGRAM: return (CLOUDABI_FILETYPE_SOCKET_DGRAM); case SOCK_SEQPACKET: return (CLOUDABI_FILETYPE_SOCKET_SEQPACKET); case SOCK_STREAM: return (CLOUDABI_FILETYPE_SOCKET_STREAM); default: return (CLOUDABI_FILETYPE_UNKNOWN); } case DTYPE_VNODE: vp = fp->f_vnode; switch (vp->v_type) { case VBLK: return (CLOUDABI_FILETYPE_BLOCK_DEVICE); case VCHR: return (CLOUDABI_FILETYPE_CHARACTER_DEVICE); case VDIR: return (CLOUDABI_FILETYPE_DIRECTORY); case VFIFO: return (CLOUDABI_FILETYPE_FIFO); case VLNK: return (CLOUDABI_FILETYPE_SYMBOLIC_LINK); case VREG: return (CLOUDABI_FILETYPE_REGULAR_FILE); case VSOCK: return (CLOUDABI_FILETYPE_SOCKET_STREAM); default: return (CLOUDABI_FILETYPE_UNKNOWN); } default: return (CLOUDABI_FILETYPE_UNKNOWN); } } /* Removes rights that conflict with the file descriptor type. */ void cloudabi_remove_conflicting_rights(cloudabi_filetype_t filetype, cloudabi_rights_t *base, cloudabi_rights_t *inheriting) { /* * CloudABI has a small number of additional rights bits to * disambiguate between multiple purposes. Remove the bits that * don't apply to the type of the file descriptor. * * As file descriptor access modes (O_ACCMODE) has been fully * replaced by rights bits, CloudABI distinguishes between * rights that apply to the file descriptor itself (base) versus * rights of new file descriptors derived from them * (inheriting). The code below approximates the pair by * decomposing depending on the file descriptor type. * * We need to be somewhat accurate about which actions can * actually be performed on the file descriptor, as functions * like fcntl(fd, F_GETFL) are emulated on top of this. */ switch (filetype) { case CLOUDABI_FILETYPE_DIRECTORY: *base &= CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | CLOUDABI_RIGHT_FD_SYNC | CLOUDABI_RIGHT_FILE_ADVISE | CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY | CLOUDABI_RIGHT_FILE_CREATE_FILE | CLOUDABI_RIGHT_FILE_CREATE_FIFO | CLOUDABI_RIGHT_FILE_LINK_SOURCE | CLOUDABI_RIGHT_FILE_LINK_TARGET | CLOUDABI_RIGHT_FILE_OPEN | CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_FILE_READLINK | CLOUDABI_RIGHT_FILE_RENAME_SOURCE | CLOUDABI_RIGHT_FILE_RENAME_TARGET | CLOUDABI_RIGHT_FILE_STAT_FGET | CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES | CLOUDABI_RIGHT_FILE_STAT_GET | CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES | CLOUDABI_RIGHT_FILE_SYMLINK | CLOUDABI_RIGHT_FILE_UNLINK | CLOUDABI_RIGHT_POLL_FD_READWRITE | CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY | CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY; *inheriting &= CLOUDABI_RIGHT_FD_DATASYNC | CLOUDABI_RIGHT_FD_READ | CLOUDABI_RIGHT_FD_SEEK | CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | CLOUDABI_RIGHT_FD_SYNC | CLOUDABI_RIGHT_FD_TELL | CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ADVISE | CLOUDABI_RIGHT_FILE_ALLOCATE | CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY | CLOUDABI_RIGHT_FILE_CREATE_FILE | CLOUDABI_RIGHT_FILE_CREATE_FIFO | CLOUDABI_RIGHT_FILE_LINK_SOURCE | CLOUDABI_RIGHT_FILE_LINK_TARGET | CLOUDABI_RIGHT_FILE_OPEN | CLOUDABI_RIGHT_FILE_READDIR | CLOUDABI_RIGHT_FILE_READLINK | CLOUDABI_RIGHT_FILE_RENAME_SOURCE | CLOUDABI_RIGHT_FILE_RENAME_TARGET | CLOUDABI_RIGHT_FILE_STAT_FGET | CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE | CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES | CLOUDABI_RIGHT_FILE_STAT_GET | CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES | CLOUDABI_RIGHT_FILE_SYMLINK | CLOUDABI_RIGHT_FILE_UNLINK | CLOUDABI_RIGHT_MEM_MAP | CLOUDABI_RIGHT_MEM_MAP_EXEC | CLOUDABI_RIGHT_POLL_FD_READWRITE | CLOUDABI_RIGHT_PROC_EXEC | CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY | CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY; break; case CLOUDABI_FILETYPE_FIFO: *base &= CLOUDABI_RIGHT_FD_READ | CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_STAT_FGET | CLOUDABI_RIGHT_POLL_FD_READWRITE; *inheriting = 0; break; case CLOUDABI_FILETYPE_POLL: *base &= ~CLOUDABI_RIGHT_FILE_ADVISE; *inheriting = 0; break; case CLOUDABI_FILETYPE_PROCESS: *base &= ~(CLOUDABI_RIGHT_FILE_ADVISE | CLOUDABI_RIGHT_POLL_FD_READWRITE); *inheriting = 0; break; case CLOUDABI_FILETYPE_REGULAR_FILE: *base &= CLOUDABI_RIGHT_FD_DATASYNC | CLOUDABI_RIGHT_FD_READ | CLOUDABI_RIGHT_FD_SEEK | CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | CLOUDABI_RIGHT_FD_SYNC | CLOUDABI_RIGHT_FD_TELL | CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_ADVISE | CLOUDABI_RIGHT_FILE_ALLOCATE | CLOUDABI_RIGHT_FILE_STAT_FGET | CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE | CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES | CLOUDABI_RIGHT_MEM_MAP | CLOUDABI_RIGHT_MEM_MAP_EXEC | CLOUDABI_RIGHT_POLL_FD_READWRITE | CLOUDABI_RIGHT_PROC_EXEC; *inheriting = 0; break; case CLOUDABI_FILETYPE_SHARED_MEMORY: *base &= ~(CLOUDABI_RIGHT_FD_SEEK | CLOUDABI_RIGHT_FD_TELL | CLOUDABI_RIGHT_FILE_ADVISE | CLOUDABI_RIGHT_FILE_ALLOCATE | CLOUDABI_RIGHT_FILE_READDIR); *inheriting = 0; break; case CLOUDABI_FILETYPE_SOCKET_DGRAM: case CLOUDABI_FILETYPE_SOCKET_SEQPACKET: case CLOUDABI_FILETYPE_SOCKET_STREAM: *base &= CLOUDABI_RIGHT_FD_READ | CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS | CLOUDABI_RIGHT_FD_WRITE | CLOUDABI_RIGHT_FILE_STAT_FGET | CLOUDABI_RIGHT_POLL_FD_READWRITE | CLOUDABI_RIGHT_SOCK_ACCEPT | CLOUDABI_RIGHT_SOCK_BIND_SOCKET | CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET | CLOUDABI_RIGHT_SOCK_LISTEN | CLOUDABI_RIGHT_SOCK_SHUTDOWN | CLOUDABI_RIGHT_SOCK_STAT_GET; break; default: *inheriting = 0; break; } } /* Converts FreeBSD's Capsicum rights to CloudABI's set of rights. */ static void convert_capabilities(const cap_rights_t *capabilities, cloudabi_filetype_t filetype, cloudabi_rights_t *base, cloudabi_rights_t *inheriting) { cloudabi_rights_t rights; /* Convert FreeBSD bits to CloudABI bits. */ rights = 0; #define MAPPING(cloudabi, ...) do { \ if (cap_rights_is_set(capabilities, ##__VA_ARGS__)) \ rights |= (cloudabi); \ } while (0); RIGHTS_MAPPINGS #undef MAPPING *base = rights; *inheriting = rights; cloudabi_remove_conflicting_rights(filetype, base, inheriting); } int cloudabi_sys_fd_stat_get(struct thread *td, struct cloudabi_sys_fd_stat_get_args *uap) { cloudabi_fdstat_t fsb = {}; struct file *fp; cap_rights_t rights; struct filecaps fcaps; int error, oflags; /* Obtain file descriptor properties. */ error = fget_cap(td, uap->fd, cap_rights_init(&rights), &fp, &fcaps); if (error != 0) return (error); oflags = OFLAGS(fp->f_flag); fsb.fs_filetype = cloudabi_convert_filetype(fp); fdrop(fp, td); /* Convert file descriptor flags. */ if (oflags & O_APPEND) fsb.fs_flags |= CLOUDABI_FDFLAG_APPEND; if (oflags & O_NONBLOCK) fsb.fs_flags |= CLOUDABI_FDFLAG_NONBLOCK; if (oflags & O_SYNC) fsb.fs_flags |= CLOUDABI_FDFLAG_SYNC; /* Convert capabilities to CloudABI rights. */ convert_capabilities(&fcaps.fc_rights, fsb.fs_filetype, &fsb.fs_rights_base, &fsb.fs_rights_inheriting); filecaps_free(&fcaps); return (copyout(&fsb, (void *)uap->buf, sizeof(fsb))); } /* Converts CloudABI rights to a set of Capsicum capabilities. */ int cloudabi_convert_rights(cloudabi_rights_t in, cap_rights_t *out) { cap_rights_init(out); #define MAPPING(cloudabi, ...) do { \ if (in & (cloudabi)) { \ cap_rights_set(out, ##__VA_ARGS__); \ in &= ~(cloudabi); \ } \ } while (0); RIGHTS_MAPPINGS #undef MAPPING if (in != 0) return (ENOTCAPABLE); return (0); } int cloudabi_sys_fd_stat_put(struct thread *td, struct cloudabi_sys_fd_stat_put_args *uap) { cloudabi_fdstat_t fsb; cap_rights_t rights; int error, oflags; error = copyin(uap->buf, &fsb, sizeof(fsb)); if (error != 0) return (error); if (uap->flags == CLOUDABI_FDSTAT_FLAGS) { /* Convert flags. */ oflags = 0; if (fsb.fs_flags & CLOUDABI_FDFLAG_APPEND) oflags |= O_APPEND; if (fsb.fs_flags & CLOUDABI_FDFLAG_NONBLOCK) oflags |= O_NONBLOCK; if (fsb.fs_flags & (CLOUDABI_FDFLAG_SYNC | CLOUDABI_FDFLAG_DSYNC | CLOUDABI_FDFLAG_RSYNC)) oflags |= O_SYNC; return (kern_fcntl(td, uap->fd, F_SETFL, oflags)); } else if (uap->flags == CLOUDABI_FDSTAT_RIGHTS) { /* Convert rights. */ error = cloudabi_convert_rights( fsb.fs_rights_base | fsb.fs_rights_inheriting, &rights); if (error != 0) return (error); return (kern_cap_rights_limit(td, uap->fd, &rights)); } return (EINVAL); } int cloudabi_sys_fd_sync(struct thread *td, struct cloudabi_sys_fd_sync_args *uap) { return (kern_fsync(td, uap->fd, true)); } Index: head/sys/compat/cloudabi64/cloudabi64_poll.c =================================================================== --- head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 312926) +++ head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 312927) @@ -1,409 +1,408 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include #include /* Converts a FreeBSD signal number to a CloudABI signal number. */ static cloudabi_signal_t convert_signal(int sig) { static const cloudabi_signal_t signals[] = { [SIGABRT] = CLOUDABI_SIGABRT, [SIGALRM] = CLOUDABI_SIGALRM, [SIGBUS] = CLOUDABI_SIGBUS, [SIGCHLD] = CLOUDABI_SIGCHLD, [SIGCONT] = CLOUDABI_SIGCONT, [SIGFPE] = CLOUDABI_SIGFPE, [SIGHUP] = CLOUDABI_SIGHUP, [SIGILL] = CLOUDABI_SIGILL, [SIGINT] = CLOUDABI_SIGINT, [SIGKILL] = CLOUDABI_SIGKILL, [SIGPIPE] = CLOUDABI_SIGPIPE, [SIGQUIT] = CLOUDABI_SIGQUIT, [SIGSEGV] = CLOUDABI_SIGSEGV, [SIGSTOP] = CLOUDABI_SIGSTOP, [SIGSYS] = CLOUDABI_SIGSYS, [SIGTERM] = CLOUDABI_SIGTERM, [SIGTRAP] = CLOUDABI_SIGTRAP, [SIGTSTP] = CLOUDABI_SIGTSTP, [SIGTTIN] = CLOUDABI_SIGTTIN, [SIGTTOU] = CLOUDABI_SIGTTOU, [SIGURG] = CLOUDABI_SIGURG, [SIGUSR1] = CLOUDABI_SIGUSR1, [SIGUSR2] = CLOUDABI_SIGUSR2, [SIGVTALRM] = CLOUDABI_SIGVTALRM, [SIGXCPU] = CLOUDABI_SIGXCPU, [SIGXFSZ] = CLOUDABI_SIGXFSZ, }; /* Convert unknown signals to SIGABRT. */ if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0) return (SIGABRT); return (signals[sig]); } struct cloudabi64_kevent_args { const cloudabi64_subscription_t *in; cloudabi64_event_t *out; bool once; }; /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ static int cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) { cloudabi64_subscription_t sub; struct cloudabi64_kevent_args *args; cloudabi_timestamp_t ts; int error; args = arg; while (count-- > 0) { /* TODO(ed): Copy in multiple entries at once. */ error = copyin(args->in++, &sub, sizeof(sub)); if (error != 0) return (error); memset(kevp, 0, sizeof(*kevp)); kevp->udata = TO_PTR(sub.userdata); switch (sub.type) { case CLOUDABI_EVENTTYPE_CLOCK: kevp->filter = EVFILT_TIMER; kevp->ident = sub.clock.identifier; kevp->fflags = NOTE_NSECONDS; if ((sub.clock.flags & CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && sub.clock.timeout > 0) { /* Convert absolute timestamp to a relative. */ error = cloudabi_clock_time_get(curthread, sub.clock.clock_id, &ts); if (error != 0) return (error); ts = ts > sub.clock.timeout ? 0 : sub.clock.timeout - ts; } else { /* Relative timestamp. */ ts = sub.clock.timeout; } kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; break; case CLOUDABI_EVENTTYPE_FD_READ: kevp->filter = EVFILT_READ; kevp->ident = sub.fd_readwrite.fd; if ((sub.fd_readwrite.flags & CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0) kevp->fflags = NOTE_FILE_POLL; break; case CLOUDABI_EVENTTYPE_FD_WRITE: kevp->filter = EVFILT_WRITE; kevp->ident = sub.fd_readwrite.fd; break; case CLOUDABI_EVENTTYPE_PROC_TERMINATE: kevp->filter = EVFILT_PROCDESC; kevp->ident = sub.proc_terminate.fd; kevp->fflags = NOTE_EXIT; break; } if (args->once) { /* Ignore flags. Simply use oneshot mode. */ kevp->flags = EV_ADD | EV_ONESHOT; } else { /* Translate flags. */ if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0) kevp->flags |= EV_ADD; if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0) kevp->flags |= EV_CLEAR; if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0) kevp->flags |= EV_DELETE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0) kevp->flags |= EV_DISABLE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0) kevp->flags |= EV_ENABLE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0) kevp->flags |= EV_ONESHOT; } ++kevp; } return (0); } /* Converts FreeBSD's struct kevent to CloudABI's event objects. */ static int cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) { cloudabi64_event_t ev; struct cloudabi64_kevent_args *args; int error; args = arg; while (count-- > 0) { /* Convert fields that should always be present. */ memset(&ev, 0, sizeof(ev)); ev.userdata = (uintptr_t)kevp->udata; switch (kevp->filter) { case EVFILT_TIMER: ev.type = CLOUDABI_EVENTTYPE_CLOCK; ev.clock.identifier = kevp->ident; break; case EVFILT_READ: ev.type = CLOUDABI_EVENTTYPE_FD_READ; ev.fd_readwrite.fd = kevp->ident; break; case EVFILT_WRITE: ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; ev.fd_readwrite.fd = kevp->ident; break; case EVFILT_PROCDESC: ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; ev.proc_terminate.fd = kevp->ident; break; } if ((kevp->flags & EV_ERROR) == 0) { /* Success. */ switch (kevp->filter) { case EVFILT_READ: case EVFILT_WRITE: ev.fd_readwrite.nbytes = kevp->data; if ((kevp->flags & EV_EOF) != 0) { ev.fd_readwrite.flags |= CLOUDABI_EVENT_FD_READWRITE_HANGUP; } break; case EVFILT_PROCDESC: if (WIFSIGNALED(kevp->data)) { /* Process got signalled. */ ev.proc_terminate.signal = convert_signal(WTERMSIG(kevp->data)); ev.proc_terminate.exitcode = 0; } else { /* Process exited. */ ev.proc_terminate.signal = 0; ev.proc_terminate.exitcode = WEXITSTATUS(kevp->data); } break; } } else { /* Error. */ ev.error = cloudabi_convert_errno(kevp->data); } ++kevp; /* TODO(ed): Copy out multiple entries at once. */ error = copyout(&ev, args->out++, sizeof(ev)); if (error != 0) return (error); } return (0); } int cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) { struct cloudabi64_kevent_args args = { .in = uap->in, .out = uap->out, .once = true, }; struct kevent_copyops copyops = { .k_copyin = cloudabi64_kevent_copyin, .k_copyout = cloudabi64_kevent_copyout, .arg = &args, }; /* * Bandaid to support CloudABI futex constructs that are not * implemented through FreeBSD's kqueue(). */ if (uap->nsubscriptions == 1) { cloudabi64_subscription_t sub; cloudabi64_event_t ev = {}; int error; error = copyin(uap->in, &sub, sizeof(sub)); if (error != 0) return (error); ev.userdata = sub.userdata; ev.type = sub.type; if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) { /* Wait on a condition variable. */ ev.condvar.condvar = sub.condvar.condvar; ev.error = cloudabi_convert_errno( cloudabi_futex_condvar_wait( td, TO_PTR(sub.condvar.condvar), sub.condvar.condvar_scope, TO_PTR(sub.condvar.lock), sub.condvar.lock_scope, CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); td->td_retval[0] = 1; return (copyout(&ev, uap->out, sizeof(ev))); } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) { /* Acquire a read lock. */ ev.lock.lock = sub.lock.lock; ev.error = cloudabi_convert_errno( cloudabi_futex_lock_rdlock( td, TO_PTR(sub.lock.lock), sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); td->td_retval[0] = 1; return (copyout(&ev, uap->out, sizeof(ev))); } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) { /* Acquire a write lock. */ ev.lock.lock = sub.lock.lock; ev.error = cloudabi_convert_errno( cloudabi_futex_lock_wrlock( td, TO_PTR(sub.lock.lock), sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); td->td_retval[0] = 1; return (copyout(&ev, uap->out, sizeof(ev))); } } else if (uap->nsubscriptions == 2) { cloudabi64_subscription_t sub[2]; cloudabi64_event_t ev[2] = {}; int error; error = copyin(uap->in, &sub, sizeof(sub)); if (error != 0) return (error); ev[0].userdata = sub[0].userdata; ev[0].type = sub[0].type; ev[1].userdata = sub[1].userdata; ev[1].type = sub[1].type; if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR && sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { /* Wait for a condition variable with timeout. */ ev[0].condvar.condvar = sub[0].condvar.condvar; ev[1].clock.identifier = sub[1].clock.identifier; error = cloudabi_futex_condvar_wait( td, TO_PTR(sub[0].condvar.condvar), sub[0].condvar.condvar_scope, TO_PTR(sub[0].condvar.lock), sub[0].condvar.lock_scope, sub[1].clock.clock_id, sub[1].clock.timeout, sub[1].clock.precision); if (error == ETIMEDOUT) { td->td_retval[0] = 1; return (copyout(&ev[1], uap->out, sizeof(ev[1]))); } ev[0].error = cloudabi_convert_errno(error); td->td_retval[0] = 1; return (copyout(&ev[0], uap->out, sizeof(ev[0]))); } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK && sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { /* Acquire a read lock with a timeout. */ ev[0].lock.lock = sub[0].lock.lock; ev[1].clock.identifier = sub[1].clock.identifier; error = cloudabi_futex_lock_rdlock( td, TO_PTR(sub[0].lock.lock), sub[0].lock.lock_scope, sub[1].clock.clock_id, sub[1].clock.timeout, sub[1].clock.precision); if (error == ETIMEDOUT) { td->td_retval[0] = 1; return (copyout(&ev[1], uap->out, sizeof(ev[1]))); } ev[0].error = cloudabi_convert_errno(error); td->td_retval[0] = 1; return (copyout(&ev[0], uap->out, sizeof(ev[0]))); } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK && sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { /* Acquire a write lock with a timeout. */ ev[0].lock.lock = sub[0].lock.lock; ev[1].clock.identifier = sub[1].clock.identifier; error = cloudabi_futex_lock_wrlock( td, TO_PTR(sub[0].lock.lock), sub[0].lock.lock_scope, sub[1].clock.clock_id, sub[1].clock.timeout, sub[1].clock.precision); if (error == ETIMEDOUT) { td->td_retval[0] = 1; return (copyout(&ev[1], uap->out, sizeof(ev[1]))); } ev[0].error = cloudabi_convert_errno(error); td->td_retval[0] = 1; return (copyout(&ev[0], uap->out, sizeof(ev[0]))); } } return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops)); } int cloudabi64_sys_poll_fd(struct thread *td, struct cloudabi64_sys_poll_fd_args *uap) { struct cloudabi64_kevent_args args = { .in = uap->in, .out = uap->out, .once = false, }; struct kevent_copyops copyops = { .k_copyin = cloudabi64_kevent_copyin, .k_copyout = cloudabi64_kevent_copyout, .arg = &args, }; cloudabi64_subscription_t subtimo; struct timespec timeout; int error; if (uap->timeout != NULL) { /* Poll with a timeout. */ error = copyin(uap->timeout, &subtimo, sizeof(subtimo)); if (error != 0) return (error); if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK || subtimo.clock.flags != 0) return (EINVAL); timeout.tv_sec = subtimo.clock.timeout / 1000000000; timeout.tv_nsec = subtimo.clock.timeout % 1000000000; return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len, ©ops, &timeout)); } else { /* Poll without a timeout. */ return (kern_kevent(td, uap->fd, uap->in_len, uap->out_len, ©ops, NULL)); } } Index: head/sys/compat/linuxkpi/common/include/linux/slab.h =================================================================== --- head/sys/compat/linuxkpi/common/include/linux/slab.h (revision 312926) +++ head/sys/compat/linuxkpi/common/include/linux/slab.h (revision 312927) @@ -1,132 +1,124 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LINUX_SLAB_H_ #define _LINUX_SLAB_H_ #include #include #include #include #include #include MALLOC_DECLARE(M_KMALLOC); #define kmalloc(size, flags) malloc((size), M_KMALLOC, (flags)) #define kvmalloc(size) kmalloc((size), 0) #define kzalloc(size, flags) kmalloc((size), M_ZERO | ((flags) ? (flags) : M_NOWAIT)) #define kzalloc_node(size, flags, node) kzalloc(size, flags) #define kfree(ptr) free(__DECONST(void *, (ptr)), M_KMALLOC) #define kfree_const(ptr) kfree(ptr) #define krealloc(ptr, size, flags) realloc((ptr), (size), M_KMALLOC, (flags)) #define kcalloc(n, size, flags) kmalloc((n) * (size), flags | M_ZERO) #define vzalloc(size) kzalloc(size, GFP_KERNEL | __GFP_NOWARN) #define vfree(arg) kfree(arg) #define kvfree(arg) kfree(arg) #define vmalloc(size) kmalloc(size, GFP_KERNEL) #define vmalloc_node(size, node) kmalloc(size, GFP_KERNEL) /* * Prefix some functions with linux_ to avoid namespace conflict * with the OpenSolaris code in the kernel. */ #define kmem_cache linux_kmem_cache #define kmem_cache_create(...) linux_kmem_cache_create(__VA_ARGS__) #define kmem_cache_alloc(...) linux_kmem_cache_alloc(__VA_ARGS__) #define kmem_cache_free(...) linux_kmem_cache_free(__VA_ARGS__) #define kmem_cache_destroy(...) linux_kmem_cache_destroy(__VA_ARGS__) struct linux_kmem_cache { uma_zone_t cache_zone; void (*cache_ctor)(void *); }; #define SLAB_HWCACHE_ALIGN 0x0001 -static inline void * -kmalloc_array(size_t n, size_t size, gfp_t flags) -{ - if (size != 0 && n > SIZE_MAX / size) - return (NULL); - return kmalloc(n * size, flags); -} -} static inline int linux_kmem_ctor(void *mem, int size, void *arg, int flags) { void (*ctor)(void *); ctor = arg; ctor(mem); return (0); } static inline struct kmem_cache * linux_kmem_cache_create(char *name, size_t size, size_t align, u_long flags, void (*ctor)(void *)) { struct kmem_cache *c; c = malloc(sizeof(*c), M_KMALLOC, M_WAITOK); if (align) align--; if (flags & SLAB_HWCACHE_ALIGN) align = UMA_ALIGN_CACHE; c->cache_zone = uma_zcreate(name, size, ctor ? linux_kmem_ctor : NULL, NULL, NULL, NULL, align, 0); c->cache_ctor = ctor; return c; } static inline void * linux_kmem_cache_alloc(struct kmem_cache *c, int flags) { return uma_zalloc_arg(c->cache_zone, c->cache_ctor, flags); } static inline void linux_kmem_cache_free(struct kmem_cache *c, void *m) { uma_zfree(c->cache_zone, m); } static inline void linux_kmem_cache_destroy(struct kmem_cache *c) { uma_zdestroy(c->cache_zone); free(c, M_KMALLOC); } #endif /* _LINUX_SLAB_H_ */ Index: head/sys/contrib/dev/acpica/components/namespace/nsxfeval.c =================================================================== --- head/sys/contrib/dev/acpica/components/namespace/nsxfeval.c (revision 312926) +++ head/sys/contrib/dev/acpica/components/namespace/nsxfeval.c (revision 312927) @@ -1,1104 +1,1078 @@ /******************************************************************************* * * Module Name: nsxfeval - Public interfaces to the ACPI subsystem * ACPI Object evaluation interfaces * ******************************************************************************/ /* * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #define EXPORT_ACPI_INTERFACES #include #include #include #include #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME ("nsxfeval") /* Local prototypes */ static void AcpiNsResolveReferences ( ACPI_EVALUATE_INFO *Info); /******************************************************************************* * * FUNCTION: AcpiEvaluateObjectTyped * * PARAMETERS: Handle - Object handle (optional) * Pathname - Object pathname (optional) * ExternalParams - List of parameters to pass to method, * terminated by NULL. May be NULL * if no parameters are being passed. * ReturnBuffer - Where to put method's return value (if * any). If NULL, no value is returned. * ReturnType - Expected type of return object * * RETURN: Status * * DESCRIPTION: Find and evaluate the given object, passing the given * parameters if necessary. One of "Handle" or "Pathname" must * be valid (non-null) * ******************************************************************************/ ACPI_STATUS AcpiEvaluateObjectTyped ( ACPI_HANDLE Handle, ACPI_STRING Pathname, ACPI_OBJECT_LIST *ExternalParams, ACPI_BUFFER *ReturnBuffer, ACPI_OBJECT_TYPE ReturnType) { ACPI_STATUS Status; BOOLEAN FreeBufferOnError = FALSE; ACPI_FUNCTION_TRACE (AcpiEvaluateObjectTyped); /* Return buffer must be valid */ if (!ReturnBuffer) { return_ACPI_STATUS (AE_BAD_PARAMETER); } if (ReturnBuffer->Length == ACPI_ALLOCATE_BUFFER) { FreeBufferOnError = TRUE; } /* Evaluate the object */ Status = AcpiEvaluateObject (Handle, Pathname, ExternalParams, ReturnBuffer); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } /* Type ANY means "don't care" */ if (ReturnType == ACPI_TYPE_ANY) { return_ACPI_STATUS (AE_OK); } if (ReturnBuffer->Length == 0) { /* Error because caller specifically asked for a return value */ ACPI_ERROR ((AE_INFO, "No return value")); return_ACPI_STATUS (AE_NULL_OBJECT); } /* Examine the object type returned from EvaluateObject */ if (((ACPI_OBJECT *) ReturnBuffer->Pointer)->Type == ReturnType) { return_ACPI_STATUS (AE_OK); } /* Return object type does not match requested type */ ACPI_ERROR ((AE_INFO, "Incorrect return type [%s] requested [%s]", AcpiUtGetTypeName (((ACPI_OBJECT *) ReturnBuffer->Pointer)->Type), AcpiUtGetTypeName (ReturnType))); if (FreeBufferOnError) { /* * Free a buffer created via ACPI_ALLOCATE_BUFFER. * Note: We use AcpiOsFree here because AcpiOsAllocate was used * to allocate the buffer. This purposefully bypasses the * (optionally enabled) allocation tracking mechanism since we * only want to track internal allocations. */ AcpiOsFree (ReturnBuffer->Pointer); ReturnBuffer->Pointer = NULL; } ReturnBuffer->Length = 0; return_ACPI_STATUS (AE_TYPE); } ACPI_EXPORT_SYMBOL (AcpiEvaluateObjectTyped) /******************************************************************************* * * FUNCTION: AcpiEvaluateObject * * PARAMETERS: Handle - Object handle (optional) * Pathname - Object pathname (optional) * ExternalParams - List of parameters to pass to method, * terminated by NULL. May be NULL * if no parameters are being passed. * ReturnBuffer - Where to put method's return value (if * any). If NULL, no value is returned. * * RETURN: Status * * DESCRIPTION: Find and evaluate the given object, passing the given * parameters if necessary. One of "Handle" or "Pathname" must * be valid (non-null) * ******************************************************************************/ ACPI_STATUS AcpiEvaluateObject ( ACPI_HANDLE Handle, ACPI_STRING Pathname, ACPI_OBJECT_LIST *ExternalParams, ACPI_BUFFER *ReturnBuffer) { ACPI_STATUS Status; ACPI_EVALUATE_INFO *Info; ACPI_SIZE BufferSpaceNeeded; UINT32 i; ACPI_FUNCTION_TRACE (AcpiEvaluateObject); /* Allocate and initialize the evaluation information block */ Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO)); if (!Info) { return_ACPI_STATUS (AE_NO_MEMORY); } /* Convert and validate the device handle */ Info->PrefixNode = AcpiNsValidateHandle (Handle); if (!Info->PrefixNode) { Status = AE_BAD_PARAMETER; goto Cleanup; } /* * Get the actual namespace node for the target object. * Handles these cases: * * 1) Null node, valid pathname from root (absolute path) * 2) Node and valid pathname (path relative to Node) * 3) Node, Null pathname */ if ((Pathname) && (ACPI_IS_ROOT_PREFIX (Pathname[0]))) { /* The path is fully qualified, just evaluate by name */ Info->PrefixNode = NULL; } else if (!Handle) { /* * A handle is optional iff a fully qualified pathname is specified. * Since we've already handled fully qualified names above, this is * an error. */ if (!Pathname) { ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Both Handle and Pathname are NULL")); } else { ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Null Handle with relative pathname [%s]", Pathname)); } Status = AE_BAD_PARAMETER; goto Cleanup; } Info->RelativePathname = Pathname; /* * Convert all external objects passed as arguments to the * internal version(s). */ if (ExternalParams && ExternalParams->Count) { Info->ParamCount = (UINT16) ExternalParams->Count; /* Warn on impossible argument count */ if (Info->ParamCount > ACPI_METHOD_NUM_ARGS) { ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS, "Excess arguments (%u) - using only %u", Info->ParamCount, ACPI_METHOD_NUM_ARGS)); Info->ParamCount = ACPI_METHOD_NUM_ARGS; } /* * Allocate a new parameter block for the internal objects * Add 1 to count to allow for null terminated internal list */ Info->Parameters = ACPI_ALLOCATE_ZEROED ( ((ACPI_SIZE) Info->ParamCount + 1) * sizeof (void *)); if (!Info->Parameters) { Status = AE_NO_MEMORY; goto Cleanup; } /* Convert each external object in the list to an internal object */ for (i = 0; i < Info->ParamCount; i++) { Status = AcpiUtCopyEobjectToIobject ( &ExternalParams->Pointer[i], &Info->Parameters[i]); if (ACPI_FAILURE (Status)) { goto Cleanup; } } Info->Parameters[Info->ParamCount] = NULL; } #ifdef _FUTURE_FEATURE /* * Begin incoming argument count analysis. Check for too few args * and too many args. */ switch (AcpiNsGetType (Info->Node)) { case ACPI_TYPE_METHOD: /* Check incoming argument count against the method definition */ if (Info->ObjDesc->Method.ParamCount > Info->ParamCount) { ACPI_ERROR ((AE_INFO, "Insufficient arguments (%u) - %u are required", Info->ParamCount, Info->ObjDesc->Method.ParamCount)); Status = AE_MISSING_ARGUMENTS; goto Cleanup; } else if (Info->ObjDesc->Method.ParamCount < Info->ParamCount) { ACPI_WARNING ((AE_INFO, "Excess arguments (%u) - only %u are required", Info->ParamCount, Info->ObjDesc->Method.ParamCount)); /* Just pass the required number of arguments */ Info->ParamCount = Info->ObjDesc->Method.ParamCount; } /* * Any incoming external objects to be passed as arguments to the * method must be converted to internal objects */ if (Info->ParamCount) { /* * Allocate a new parameter block for the internal objects * Add 1 to count to allow for null terminated internal list */ Info->Parameters = ACPI_ALLOCATE_ZEROED ( ((ACPI_SIZE) Info->ParamCount + 1) * sizeof (void *)); if (!Info->Parameters) { Status = AE_NO_MEMORY; goto Cleanup; } /* Convert each external object in the list to an internal object */ for (i = 0; i < Info->ParamCount; i++) { Status = AcpiUtCopyEobjectToIobject ( &ExternalParams->Pointer[i], &Info->Parameters[i]); if (ACPI_FAILURE (Status)) { goto Cleanup; } } Info->Parameters[Info->ParamCount] = NULL; } break; default: /* Warn if arguments passed to an object that is not a method */ if (Info->ParamCount) { ACPI_WARNING ((AE_INFO, "%u arguments were passed to a non-method ACPI object", Info->ParamCount)); } break; } #endif /* Now we can evaluate the object */ Status = AcpiNsEvaluate (Info); /* * If we are expecting a return value, and all went well above, * copy the return value to an external object. */ if (!ReturnBuffer) { goto CleanupReturnObject; } if (!Info->ReturnObject) { ReturnBuffer->Length = 0; goto Cleanup; } if (ACPI_GET_DESCRIPTOR_TYPE (Info->ReturnObject) == ACPI_DESC_TYPE_NAMED) { /* * If we received a NS Node as a return object, this means that * the object we are evaluating has nothing interesting to * return (such as a mutex, etc.) We return an error because * these types are essentially unsupported by this interface. * We don't check up front because this makes it easier to add * support for various types at a later date if necessary. */ Status = AE_TYPE; Info->ReturnObject = NULL; /* No need to delete a NS Node */ ReturnBuffer->Length = 0; } if (ACPI_FAILURE (Status)) { goto CleanupReturnObject; } /* Dereference Index and RefOf references */ AcpiNsResolveReferences (Info); /* Get the size of the returned object */ Status = AcpiUtGetObjectSize (Info->ReturnObject, &BufferSpaceNeeded); if (ACPI_SUCCESS (Status)) { /* Validate/Allocate/Clear caller buffer */ Status = AcpiUtInitializeBuffer (ReturnBuffer, BufferSpaceNeeded); if (ACPI_FAILURE (Status)) { /* * Caller's buffer is too small or a new one can't * be allocated */ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Needed buffer size %X, %s\n", (UINT32) BufferSpaceNeeded, AcpiFormatException (Status))); } else { /* We have enough space for the object, build it */ Status = AcpiUtCopyIobjectToEobject ( Info->ReturnObject, ReturnBuffer); } } CleanupReturnObject: if (Info->ReturnObject) { /* * Delete the internal return object. NOTE: Interpreter must be * locked to avoid race condition. */ AcpiExEnterInterpreter (); /* Remove one reference on the return object (should delete it) */ AcpiUtRemoveReference (Info->ReturnObject); AcpiExExitInterpreter (); } Cleanup: /* Free the input parameter list (if we created one) */ if (Info->Parameters) { /* Free the allocated parameter block */ AcpiUtDeleteInternalObjectList (Info->Parameters); } ACPI_FREE (Info); return_ACPI_STATUS (Status); } ACPI_EXPORT_SYMBOL (AcpiEvaluateObject) /******************************************************************************* * * FUNCTION: AcpiNsResolveReferences * * PARAMETERS: Info - Evaluation info block * * RETURN: Info->ReturnObject is replaced with the dereferenced object * * DESCRIPTION: Dereference certain reference objects. Called before an * internal return object is converted to an external ACPI_OBJECT. * * Performs an automatic dereference of Index and RefOf reference objects. * These reference objects are not supported by the ACPI_OBJECT, so this is a * last resort effort to return something useful. Also, provides compatibility * with other ACPI implementations. * * NOTE: does not handle references within returned package objects or nested * references, but this support could be added later if found to be necessary. * ******************************************************************************/ static void AcpiNsResolveReferences ( ACPI_EVALUATE_INFO *Info) { ACPI_OPERAND_OBJECT *ObjDesc = NULL; ACPI_NAMESPACE_NODE *Node; /* We are interested in reference objects only */ if ((Info->ReturnObject)->Common.Type != ACPI_TYPE_LOCAL_REFERENCE) { return; } /* * Two types of references are supported - those created by Index and * RefOf operators. A name reference (AML_NAMEPATH_OP) can be converted * to an ACPI_OBJECT, so it is not dereferenced here. A DdbHandle * (AML_LOAD_OP) cannot be dereferenced, nor can it be converted to * an ACPI_OBJECT. */ switch (Info->ReturnObject->Reference.Class) { case ACPI_REFCLASS_INDEX: ObjDesc = *(Info->ReturnObject->Reference.Where); break; case ACPI_REFCLASS_REFOF: Node = Info->ReturnObject->Reference.Object; if (Node) { ObjDesc = Node->Object; } break; default: return; } /* Replace the existing reference object */ if (ObjDesc) { AcpiUtAddReference (ObjDesc); AcpiUtRemoveReference (Info->ReturnObject); Info->ReturnObject = ObjDesc; } return; } /******************************************************************************* * * FUNCTION: AcpiWalkNamespace * * PARAMETERS: Type - ACPI_OBJECT_TYPE to search for * StartObject - Handle in namespace where search begins * MaxDepth - Depth to which search is to reach * DescendingCallback - Called during tree descent * when an object of "Type" is found * AscendingCallback - Called during tree ascent * when an object of "Type" is found * Context - Passed to user function(s) above * ReturnValue - Location where return value of * UserFunction is put if terminated early * * RETURNS Return value from the UserFunction if terminated early. * Otherwise, returns NULL. * * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, * starting (and ending) at the object specified by StartHandle. * The callback function is called whenever an object that matches * the type parameter is found. If the callback function returns * a non-zero value, the search is terminated immediately and this * value is returned to the caller. * * The point of this procedure is to provide a generic namespace * walk routine that can be called from multiple places to * provide multiple services; the callback function(s) can be * tailored to each task, whether it is a print function, * a compare function, etc. * ******************************************************************************/ ACPI_STATUS AcpiWalkNamespace ( ACPI_OBJECT_TYPE Type, ACPI_HANDLE StartObject, UINT32 MaxDepth, ACPI_WALK_CALLBACK DescendingCallback, ACPI_WALK_CALLBACK AscendingCallback, void *Context, void **ReturnValue) { ACPI_STATUS Status; ACPI_FUNCTION_TRACE (AcpiWalkNamespace); /* Parameter validation */ if ((Type > ACPI_TYPE_LOCAL_MAX) || (!MaxDepth) || (!DescendingCallback && !AscendingCallback)) { return_ACPI_STATUS (AE_BAD_PARAMETER); } /* * Need to acquire the namespace reader lock to prevent interference * with any concurrent table unloads (which causes the deletion of * namespace objects). We cannot allow the deletion of a namespace node * while the user function is using it. The exception to this are the * nodes created and deleted during control method execution -- these * nodes are marked as temporary nodes and are ignored by the namespace * walk. Thus, control methods can be executed while holding the * namespace deletion lock (and the user function can execute control * methods.) */ Status = AcpiUtAcquireReadLock (&AcpiGbl_NamespaceRwLock); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } /* * Lock the namespace around the walk. The namespace will be * unlocked/locked around each call to the user function - since the user * function must be allowed to make ACPICA calls itself (for example, it * will typically execute control methods during device enumeration.) */ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { goto UnlockAndExit; } /* Now we can validate the starting node */ if (!AcpiNsValidateHandle (StartObject)) { Status = AE_BAD_PARAMETER; goto UnlockAndExit2; } Status = AcpiNsWalkNamespace (Type, StartObject, MaxDepth, ACPI_NS_WALK_UNLOCK, DescendingCallback, AscendingCallback, Context, ReturnValue); UnlockAndExit2: (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); UnlockAndExit: (void) AcpiUtReleaseReadLock (&AcpiGbl_NamespaceRwLock); return_ACPI_STATUS (Status); } ACPI_EXPORT_SYMBOL (AcpiWalkNamespace) /******************************************************************************* * * FUNCTION: AcpiNsGetDeviceCallback * * PARAMETERS: Callback from AcpiGetDevice * * RETURN: Status * * DESCRIPTION: Takes callbacks from WalkNamespace and filters out all non- * present devices, or if they specified a HID, it filters based * on that. * ******************************************************************************/ static ACPI_STATUS AcpiNsGetDeviceCallback ( ACPI_HANDLE ObjHandle, UINT32 NestingLevel, void *Context, void **ReturnValue) { ACPI_GET_DEVICES_INFO *Info = Context; ACPI_STATUS Status; ACPI_NAMESPACE_NODE *Node; UINT32 Flags; ACPI_PNP_DEVICE_ID *Hid; ACPI_PNP_DEVICE_ID_LIST *Cid; UINT32 i; BOOLEAN Found; int NoMatch; Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return (Status); } Node = AcpiNsValidateHandle (ObjHandle); Status = AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return (Status); } if (!Node) { return (AE_BAD_PARAMETER); } /* * First, filter based on the device HID and CID. * * 01/2010: For this case where a specific HID is requested, we don't * want to run _STA until we have an actual HID match. Thus, we will * not unnecessarily execute _STA on devices for which the caller * doesn't care about. Previously, _STA was executed unconditionally * on all devices found here. * * A side-effect of this change is that now we will continue to search * for a matching HID even under device trees where the parent device * would have returned a _STA that indicates it is not present or * not functioning (thus aborting the search on that branch). */ if (Info->Hid != NULL) { Status = AcpiUtExecute_HID (Node, &Hid); if (Status == AE_NOT_FOUND) { return (AE_OK); } else if (ACPI_FAILURE (Status)) { return (AE_CTRL_DEPTH); } NoMatch = strcmp (Hid->String, Info->Hid); ACPI_FREE (Hid); if (NoMatch) { /* * HID does not match, attempt match within the * list of Compatible IDs (CIDs) */ Status = AcpiUtExecute_CID (Node, &Cid); if (Status == AE_NOT_FOUND) { return (AE_OK); } else if (ACPI_FAILURE (Status)) { return (AE_CTRL_DEPTH); } /* Walk the CID list */ Found = FALSE; for (i = 0; i < Cid->Count; i++) { if (strcmp (Cid->Ids[i].String, Info->Hid) == 0) { /* Found a matching CID */ Found = TRUE; break; } } ACPI_FREE (Cid); if (!Found) { return (AE_OK); } } } /* Run _STA to determine if device is present */ Status = AcpiUtExecute_STA (Node, &Flags); if (ACPI_FAILURE (Status)) { return (AE_CTRL_DEPTH); } if (!(Flags & ACPI_STA_DEVICE_PRESENT) && !(Flags & ACPI_STA_DEVICE_FUNCTIONING)) { /* * Don't examine the children of the device only when the * device is neither present nor functional. See ACPI spec, * description of _STA for more information. */ return (AE_CTRL_DEPTH); } /* We have a valid device, invoke the user function */ Status = Info->UserFunction (ObjHandle, NestingLevel, Info->Context, ReturnValue); return (Status); } /******************************************************************************* * * FUNCTION: AcpiGetDevices * * PARAMETERS: HID - HID to search for. Can be NULL. * UserFunction - Called when a matching object is found * Context - Passed to user function * ReturnValue - Location where return value of * UserFunction is put if terminated early * * RETURNS Return value from the UserFunction if terminated early. * Otherwise, returns NULL. * * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, * starting (and ending) at the object specified by StartHandle. * The UserFunction is called whenever an object of type * Device is found. If the user function returns * a non-zero value, the search is terminated immediately and this * value is returned to the caller. * * This is a wrapper for WalkNamespace, but the callback performs * additional filtering. Please see AcpiNsGetDeviceCallback. * ******************************************************************************/ ACPI_STATUS AcpiGetDevices ( char *HID, ACPI_WALK_CALLBACK UserFunction, void *Context, void **ReturnValue) { ACPI_STATUS Status; ACPI_GET_DEVICES_INFO Info; ACPI_FUNCTION_TRACE (AcpiGetDevices); /* Parameter validation */ if (!UserFunction) { return_ACPI_STATUS (AE_BAD_PARAMETER); } /* * We're going to call their callback from OUR callback, so we need * to know what it is, and their context parameter. */ Info.Hid = HID; Info.Context = Context; Info.UserFunction = UserFunction; /* * Lock the namespace around the walk. * The namespace will be unlocked/locked around each call * to the user function - since this function * must be allowed to make Acpi calls itself. */ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return_ACPI_STATUS (Status); } Status = AcpiNsWalkNamespace (ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, AcpiNsGetDeviceCallback, NULL, &Info, ReturnValue); (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); return_ACPI_STATUS (Status); } ACPI_EXPORT_SYMBOL (AcpiGetDevices) /******************************************************************************* * * FUNCTION: AcpiAttachData * * PARAMETERS: ObjHandle - Namespace node * Handler - Handler for this attachment * Data - Pointer to data to be attached * * RETURN: Status * * DESCRIPTION: Attach arbitrary data and handler to a namespace node. * ******************************************************************************/ ACPI_STATUS AcpiAttachData ( ACPI_HANDLE ObjHandle, ACPI_OBJECT_HANDLER Handler, void *Data) { ACPI_NAMESPACE_NODE *Node; ACPI_STATUS Status; /* Parameter validation */ if (!ObjHandle || !Handler || !Data) { return (AE_BAD_PARAMETER); } Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return (Status); } /* Convert and validate the handle */ Node = AcpiNsValidateHandle (ObjHandle); if (!Node) { Status = AE_BAD_PARAMETER; goto UnlockAndExit; } Status = AcpiNsAttachData (Node, Handler, Data); UnlockAndExit: (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); return (Status); } ACPI_EXPORT_SYMBOL (AcpiAttachData) /******************************************************************************* * * FUNCTION: AcpiDetachData * * PARAMETERS: ObjHandle - Namespace node handle * Handler - Handler used in call to AcpiAttachData * * RETURN: Status * * DESCRIPTION: Remove data that was previously attached to a node. * ******************************************************************************/ ACPI_STATUS AcpiDetachData ( ACPI_HANDLE ObjHandle, ACPI_OBJECT_HANDLER Handler) { ACPI_NAMESPACE_NODE *Node; ACPI_STATUS Status; /* Parameter validation */ if (!ObjHandle || !Handler) { return (AE_BAD_PARAMETER); } Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return (Status); } /* Convert and validate the handle */ Node = AcpiNsValidateHandle (ObjHandle); if (!Node) { Status = AE_BAD_PARAMETER; goto UnlockAndExit; } Status = AcpiNsDetachData (Node, Handler); UnlockAndExit: (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); return (Status); } ACPI_EXPORT_SYMBOL (AcpiDetachData) /******************************************************************************* * - * FUNCTION: AcpiGetDataFull + * FUNCTION: AcpiGetData * * PARAMETERS: ObjHandle - Namespace node - * Handle - Handler used in call to attach_data + * Handler - Handler used in call to AttachData * Data - Where the data is returned - * Callback - function to execute before returning * * RETURN: Status * - * DESCRIPTION: Retrieve data that was previously attached to a namespace node - * and execute a callback before returning. + * DESCRIPTION: Retrieve data that was previously attached to a namespace node. * ******************************************************************************/ + ACPI_STATUS -AcpiGetDataFull ( +AcpiGetData ( ACPI_HANDLE ObjHandle, ACPI_OBJECT_HANDLER Handler, - void **Data, - void (*Callback)(void *)) + void **Data) { ACPI_NAMESPACE_NODE *Node; ACPI_STATUS Status; /* Parameter validation */ if (!ObjHandle || !Handler || !Data) { return (AE_BAD_PARAMETER); } Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); if (ACPI_FAILURE (Status)) { return (Status); } /* Convert and validate the handle */ Node = AcpiNsValidateHandle (ObjHandle); if (!Node) { Status = AE_BAD_PARAMETER; goto UnlockAndExit; } Status = AcpiNsGetAttachedData (Node, Handler, Data); - if (ACPI_SUCCESS(Status) && Callback) { - Callback(*Data); - } + UnlockAndExit: (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); return (Status); } -ACPI_EXPORT_SYMBOL (AcpiGetDataFull) -/******************************************************************************* - * - * FUNCTION: AcpiGetData - * - * PARAMETERS: ObjHandle - Namespace node - * Handler - Handler used in call to AttachData - * Data - Where the data is returned - * - * RETURN: Status - * - * DESCRIPTION: Retrieve data that was previously attached to a namespace node. - * - ******************************************************************************/ -ACPI_STATUS -AcpiGetData ( - ACPI_HANDLE ObjHandle, - ACPI_OBJECT_HANDLER Handler, - void **Data) -{ - return (AcpiGetDataFull(ObjHandle, Handler, Data, NULL)); -} ACPI_EXPORT_SYMBOL (AcpiGetData) Index: head/sys/contrib/dev/acpica/include/acpixf.h =================================================================== --- head/sys/contrib/dev/acpica/include/acpixf.h (revision 312926) +++ head/sys/contrib/dev/acpica/include/acpixf.h (revision 312927) @@ -1,1294 +1,1286 @@ /****************************************************************************** * * Name: acpixf.h - External interfaces to the ACPI subsystem * *****************************************************************************/ /* * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #ifndef __ACXFACE_H__ #define __ACXFACE_H__ /* Current ACPICA subsystem version in YYYYMMDD format */ #define ACPI_CA_VERSION 0x20170119 #include #include #include #include /***************************************************************************** * * Macros used for ACPICA globals and configuration * ****************************************************************************/ /* * Ensure that global variables are defined and initialized only once. * * The use of these macros allows for a single list of globals (here) * in order to simplify maintenance of the code. */ #ifdef DEFINE_ACPI_GLOBALS #define ACPI_GLOBAL(type,name) \ extern type name; \ type name #define ACPI_INIT_GLOBAL(type,name,value) \ type name=value #else #ifndef ACPI_GLOBAL #define ACPI_GLOBAL(type,name) \ extern type name #endif #ifndef ACPI_INIT_GLOBAL #define ACPI_INIT_GLOBAL(type,name,value) \ extern type name #endif #endif /* * These macros configure the various ACPICA interfaces. They are * useful for generating stub inline functions for features that are * configured out of the current kernel or ACPICA application. */ #ifndef ACPI_EXTERNAL_RETURN_STATUS #define ACPI_EXTERNAL_RETURN_STATUS(Prototype) \ Prototype; #endif #ifndef ACPI_EXTERNAL_RETURN_OK #define ACPI_EXTERNAL_RETURN_OK(Prototype) \ Prototype; #endif #ifndef ACPI_EXTERNAL_RETURN_VOID #define ACPI_EXTERNAL_RETURN_VOID(Prototype) \ Prototype; #endif #ifndef ACPI_EXTERNAL_RETURN_UINT32 #define ACPI_EXTERNAL_RETURN_UINT32(Prototype) \ Prototype; #endif #ifndef ACPI_EXTERNAL_RETURN_PTR #define ACPI_EXTERNAL_RETURN_PTR(Prototype) \ Prototype; #endif /***************************************************************************** * * Public globals and runtime configuration options * ****************************************************************************/ /* * Enable "slack mode" of the AML interpreter? Default is FALSE, and the * interpreter strictly follows the ACPI specification. Setting to TRUE * allows the interpreter to ignore certain errors and/or bad AML constructs. * * Currently, these features are enabled by this flag: * * 1) Allow "implicit return" of last value in a control method * 2) Allow access beyond the end of an operation region * 3) Allow access to uninitialized locals/args (auto-init to integer 0) * 4) Allow ANY object type to be a source operand for the Store() operator * 5) Allow unresolved references (invalid target name) in package objects * 6) Enable warning messages for behavior that is not ACPI spec compliant */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_EnableInterpreterSlack, FALSE); /* * Automatically serialize all methods that create named objects? Default * is TRUE, meaning that all NonSerialized methods are scanned once at * table load time to determine those that create named objects. Methods * that create named objects are marked Serialized in order to prevent * possible run-time problems if they are entered by more than one thread. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_AutoSerializeMethods, TRUE); /* * Create the predefined _OSI method in the namespace? Default is TRUE * because ACPICA is fully compatible with other ACPI implementations. * Changing this will revert ACPICA (and machine ASL) to pre-OSI behavior. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_CreateOsiMethod, TRUE); /* * Optionally use default values for the ACPI register widths. Set this to * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_UseDefaultRegisterWidths, TRUE); /* * Whether or not to verify the table checksum before installation. Set * this to TRUE to verify the table checksum before install it to the table * manager. Note that enabling this option causes errors to happen in some * OSPMs during early initialization stages. Default behavior is to do such * verification. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_VerifyTableChecksum, TRUE); /* * Optionally enable output from the AML Debug Object. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_EnableAmlDebugObject, FALSE); /* * Optionally copy the entire DSDT to local memory (instead of simply * mapping it.) There are some BIOSs that corrupt or replace the original * DSDT, creating the need for this option. Default is FALSE, do not copy * the DSDT. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_CopyDsdtLocally, FALSE); /* * Optionally ignore an XSDT if present and use the RSDT instead. * Although the ACPI specification requires that an XSDT be used instead * of the RSDT, the XSDT has been found to be corrupt or ill-formed on * some machines. Default behavior is to use the XSDT if present. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DoNotUseXsdt, FALSE); /* * Optionally support group module level code. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_GroupModuleLevelCode, FALSE); /* * Optionally support module level code by parsing the entire table as * a TermList. Default is FALSE, do not execute entire table until some * lock order issues are fixed. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_ParseTableAsTermList, FALSE); /* * Optionally use 32-bit FADT addresses if and when there is a conflict * (address mismatch) between the 32-bit and 64-bit versions of the * address. Although ACPICA adheres to the ACPI specification which * requires the use of the corresponding 64-bit address if it is non-zero, * some machines have been found to have a corrupted non-zero 64-bit * address. Default is FALSE, do not favor the 32-bit addresses. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_Use32BitFadtAddresses, FALSE); /* * Optionally use 32-bit FACS table addresses. * It is reported that some platforms fail to resume from system suspending * if 64-bit FACS table address is selected: * https://bugzilla.kernel.org/show_bug.cgi?id=74021 * Default is TRUE, favor the 32-bit addresses. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_Use32BitFacsAddresses, TRUE); /* * Optionally truncate I/O addresses to 16 bits. Provides compatibility * with other ACPI implementations. NOTE: During ACPICA initialization, * this value is set to TRUE if any Windows OSI strings have been * requested by the BIOS. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_TruncateIoAddresses, FALSE); /* * Disable runtime checking and repair of values returned by control methods. * Use only if the repair is causing a problem on a particular machine. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisableAutoRepair, FALSE); /* * Optionally do not install any SSDTs from the RSDT/XSDT during initialization. * This can be useful for debugging ACPI problems on some machines. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisableSsdtTableInstall, FALSE); /* * Optionally enable runtime namespace override. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_RuntimeNamespaceOverride, TRUE); /* * We keep track of the latest version of Windows that has been requested by * the BIOS. ACPI 5.0. */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_OsiData, 0); /* * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning * that the ACPI hardware is no longer required. A flag in the FADT indicates * a reduced HW machine, and that flag is duplicated here for convenience. */ ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_ReducedHardware, FALSE); /* * Maximum number of While() loop iterations before forced method abort. * This mechanism is intended to prevent infinite loops during interpreter * execution within a host kernel. */ ACPI_INIT_GLOBAL (UINT32, AcpiGbl_MaxLoopIterations, ACPI_MAX_LOOP_COUNT); /* * This mechanism is used to trace a specified AML method. The method is * traced each time it is executed. */ ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceFlags, 0); ACPI_INIT_GLOBAL (const char *, AcpiGbl_TraceMethodName, NULL); ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceDbgLevel, ACPI_TRACE_LEVEL_DEFAULT); ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceDbgLayer, ACPI_TRACE_LAYER_DEFAULT); /* * Runtime configuration of debug output control masks. We want the debug * switches statically initialized so they are already set when the debugger * is entered. */ #ifdef ACPI_DEBUG_OUTPUT ACPI_INIT_GLOBAL (UINT32, AcpiDbgLevel, ACPI_DEBUG_DEFAULT); #else ACPI_INIT_GLOBAL (UINT32, AcpiDbgLevel, ACPI_NORMAL_DEFAULT); #endif ACPI_INIT_GLOBAL (UINT32, AcpiDbgLayer, ACPI_COMPONENT_DEFAULT); /* Optionally enable timer output with Debug Object output */ ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisplayDebugTimer, FALSE); /* * Debugger command handshake globals. Host OSes need to access these * variables to implement their own command handshake mechanism. */ #ifdef ACPI_DEBUGGER ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_MethodExecuting, FALSE); ACPI_GLOBAL (char, AcpiGbl_DbLineBuf[ACPI_DB_LINE_BUFFER_SIZE]); #endif /* * Other miscellaneous globals */ ACPI_GLOBAL (ACPI_TABLE_FADT, AcpiGbl_FADT); ACPI_GLOBAL (UINT32, AcpiCurrentGpeCount); ACPI_GLOBAL (BOOLEAN, AcpiGbl_SystemAwakeAndRunning); /***************************************************************************** * * ACPICA public interface configuration. * * Interfaces that are configured out of the ACPICA build are replaced * by inlined stubs by default. * ****************************************************************************/ /* * Hardware-reduced prototypes (default: Not hardware reduced). * * All ACPICA hardware-related interfaces that use these macros will be * configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag * is set to TRUE. * * Note: This static build option for reduced hardware is intended to * reduce ACPICA code size if desired or necessary. However, even if this * option is not specified, the runtime behavior of ACPICA is dependent * on the actual FADT reduced hardware flag (HW_REDUCED_ACPI). If set, * the flag will enable similar behavior -- ACPICA will not attempt * to access any ACPI-relate hardware (SCI, GPEs, Fixed Events, etc.) */ #if (!ACPI_REDUCED_HARDWARE) #define ACPI_HW_DEPENDENT_RETURN_STATUS(Prototype) \ ACPI_EXTERNAL_RETURN_STATUS(Prototype) #define ACPI_HW_DEPENDENT_RETURN_OK(Prototype) \ ACPI_EXTERNAL_RETURN_OK(Prototype) #define ACPI_HW_DEPENDENT_RETURN_VOID(Prototype) \ ACPI_EXTERNAL_RETURN_VOID(Prototype) #else #define ACPI_HW_DEPENDENT_RETURN_STATUS(Prototype) \ static ACPI_INLINE Prototype {return(AE_NOT_CONFIGURED);} #define ACPI_HW_DEPENDENT_RETURN_OK(Prototype) \ static ACPI_INLINE Prototype {return(AE_OK);} #define ACPI_HW_DEPENDENT_RETURN_VOID(Prototype) \ static ACPI_INLINE Prototype {return;} #endif /* !ACPI_REDUCED_HARDWARE */ /* * Error message prototypes (default: error messages enabled). * * All interfaces related to error and warning messages * will be configured out of the ACPICA build if the * ACPI_NO_ERROR_MESSAGE flag is defined. */ #ifndef ACPI_NO_ERROR_MESSAGES #define ACPI_MSG_DEPENDENT_RETURN_VOID(Prototype) \ Prototype; #else #define ACPI_MSG_DEPENDENT_RETURN_VOID(Prototype) \ static ACPI_INLINE Prototype {return;} #endif /* ACPI_NO_ERROR_MESSAGES */ /* * Debugging output prototypes (default: no debug output). * * All interfaces related to debug output messages * will be configured out of the ACPICA build unless the * ACPI_DEBUG_OUTPUT flag is defined. */ #ifdef ACPI_DEBUG_OUTPUT #define ACPI_DBG_DEPENDENT_RETURN_VOID(Prototype) \ Prototype; #else #define ACPI_DBG_DEPENDENT_RETURN_VOID(Prototype) \ static ACPI_INLINE Prototype {return;} #endif /* ACPI_DEBUG_OUTPUT */ /* * Application prototypes * * All interfaces used by application will be configured * out of the ACPICA build unless the ACPI_APPLICATION * flag is defined. */ #ifdef ACPI_APPLICATION #define ACPI_APP_DEPENDENT_RETURN_VOID(Prototype) \ Prototype; #else #define ACPI_APP_DEPENDENT_RETURN_VOID(Prototype) \ static ACPI_INLINE Prototype {return;} #endif /* ACPI_APPLICATION */ /* * Debugger prototypes * * All interfaces used by debugger will be configured * out of the ACPICA build unless the ACPI_DEBUGGER * flag is defined. */ #ifdef ACPI_DEBUGGER #define ACPI_DBR_DEPENDENT_RETURN_OK(Prototype) \ ACPI_EXTERNAL_RETURN_OK(Prototype) #define ACPI_DBR_DEPENDENT_RETURN_VOID(Prototype) \ ACPI_EXTERNAL_RETURN_VOID(Prototype) #else #define ACPI_DBR_DEPENDENT_RETURN_OK(Prototype) \ static ACPI_INLINE Prototype {return(AE_OK);} #define ACPI_DBR_DEPENDENT_RETURN_VOID(Prototype) \ static ACPI_INLINE Prototype {return;} #endif /* ACPI_DEBUGGER */ /***************************************************************************** * * ACPICA public interface prototypes * ****************************************************************************/ /* * Initialization */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiInitializeTables ( ACPI_TABLE_DESC *InitialStorage, UINT32 InitialTableCount, BOOLEAN AllowResize)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiInitializeSubsystem ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiEnableSubsystem ( UINT32 Flags)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiInitializeObjects ( UINT32 Flags)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiTerminate ( void)) /* * Miscellaneous global interfaces */ ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnable ( void)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiDisable ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiSubsystemStatus ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetSystemInfo ( ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetStatistics ( ACPI_STATISTICS *Stats)) ACPI_EXTERNAL_RETURN_PTR ( const char * AcpiFormatException ( ACPI_STATUS Exception)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiPurgeCachedObjects ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallInterface ( ACPI_STRING InterfaceName)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiRemoveInterface ( ACPI_STRING InterfaceName)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiUpdateInterfaces ( UINT8 Action)) ACPI_EXTERNAL_RETURN_UINT32 ( UINT32 AcpiCheckAddressRange ( ACPI_ADR_SPACE_TYPE SpaceId, ACPI_PHYSICAL_ADDRESS Address, ACPI_SIZE Length, BOOLEAN Warn)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiDecodePldBuffer ( UINT8 *InBuffer, ACPI_SIZE Length, ACPI_PLD_INFO **ReturnBuffer)) /* * ACPI table load/unload interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiInstallTable ( ACPI_PHYSICAL_ADDRESS Address, BOOLEAN Physical)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiLoadTable ( ACPI_TABLE_HEADER *Table)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiUnloadParentTable ( ACPI_HANDLE Object)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiLoadTables ( void)) /* * ACPI table manipulation interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiReallocateRootTable ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS ACPI_INIT_FUNCTION AcpiFindRootPointer ( ACPI_PHYSICAL_ADDRESS *RsdpAddress)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetTableHeader ( ACPI_STRING Signature, UINT32 Instance, ACPI_TABLE_HEADER *OutTableHeader)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetTable ( ACPI_STRING Signature, UINT32 Instance, ACPI_TABLE_HEADER **OutTable)) ACPI_EXTERNAL_RETURN_VOID ( void AcpiPutTable ( ACPI_TABLE_HEADER *Table)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetTableByIndex ( UINT32 TableIndex, ACPI_TABLE_HEADER **OutTable)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallTableHandler ( ACPI_TABLE_HANDLER Handler, void *Context)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiRemoveTableHandler ( ACPI_TABLE_HANDLER Handler)) /* * Namespace and name interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiWalkNamespace ( ACPI_OBJECT_TYPE Type, ACPI_HANDLE StartObject, UINT32 MaxDepth, ACPI_WALK_CALLBACK DescendingCallback, ACPI_WALK_CALLBACK AscendingCallback, void *Context, void **ReturnValue)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetDevices ( char *HID, ACPI_WALK_CALLBACK UserFunction, void *Context, void **ReturnValue)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetName ( ACPI_HANDLE Object, UINT32 NameType, ACPI_BUFFER *RetPathPtr)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetHandle ( ACPI_HANDLE Parent, ACPI_STRING Pathname, ACPI_HANDLE *RetHandle)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiAttachData ( ACPI_HANDLE Object, ACPI_OBJECT_HANDLER Handler, void *Data)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiDetachData ( ACPI_HANDLE Object, ACPI_OBJECT_HANDLER Handler)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetData ( ACPI_HANDLE Object, ACPI_OBJECT_HANDLER Handler, void **Data)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS -AcpiGetDataFull ( - ACPI_HANDLE Object, - ACPI_OBJECT_HANDLER Handler, - void **Data, - void (*Callback)(void *))) - -ACPI_EXTERNAL_RETURN_STATUS ( -ACPI_STATUS AcpiDebugTrace ( const char *Name, UINT32 DebugLevel, UINT32 DebugLayer, UINT32 Flags)) /* * Object manipulation and enumeration */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiEvaluateObject ( ACPI_HANDLE Object, ACPI_STRING Pathname, ACPI_OBJECT_LIST *ParameterObjects, ACPI_BUFFER *ReturnObjectBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiEvaluateObjectTyped ( ACPI_HANDLE Object, ACPI_STRING Pathname, ACPI_OBJECT_LIST *ExternalParams, ACPI_BUFFER *ReturnBuffer, ACPI_OBJECT_TYPE ReturnType)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetObjectInfo ( ACPI_HANDLE Object, ACPI_DEVICE_INFO **ReturnBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallMethod ( UINT8 *Buffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetNextObject ( ACPI_OBJECT_TYPE Type, ACPI_HANDLE Parent, ACPI_HANDLE Child, ACPI_HANDLE *OutHandle)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetType ( ACPI_HANDLE Object, ACPI_OBJECT_TYPE *OutType)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetParent ( ACPI_HANDLE Object, ACPI_HANDLE *OutHandle)) /* * Handler interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallInitializationHandler ( ACPI_INIT_HANDLER Handler, UINT32 Function)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallSciHandler ( ACPI_SCI_HANDLER Address, void *Context)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiRemoveSciHandler ( ACPI_SCI_HANDLER Address)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallGlobalEventHandler ( ACPI_GBL_EVENT_HANDLER Handler, void *Context)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallFixedEventHandler ( UINT32 AcpiEvent, ACPI_EVENT_HANDLER Handler, void *Context)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiRemoveFixedEventHandler ( UINT32 AcpiEvent, ACPI_EVENT_HANDLER Handler)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallGpeHandler ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, UINT32 Type, ACPI_GPE_HANDLER Address, void *Context)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallGpeRawHandler ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, UINT32 Type, ACPI_GPE_HANDLER Address, void *Context)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiRemoveGpeHandler ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, ACPI_GPE_HANDLER Address)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallNotifyHandler ( ACPI_HANDLE Device, UINT32 HandlerType, ACPI_NOTIFY_HANDLER Handler, void *Context)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiRemoveNotifyHandler ( ACPI_HANDLE Device, UINT32 HandlerType, ACPI_NOTIFY_HANDLER Handler)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallAddressSpaceHandler ( ACPI_HANDLE Device, ACPI_ADR_SPACE_TYPE SpaceId, ACPI_ADR_SPACE_HANDLER Handler, ACPI_ADR_SPACE_SETUP Setup, void *Context)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiRemoveAddressSpaceHandler ( ACPI_HANDLE Device, ACPI_ADR_SPACE_TYPE SpaceId, ACPI_ADR_SPACE_HANDLER Handler)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallExceptionHandler ( ACPI_EXCEPTION_HANDLER Handler)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiInstallInterfaceHandler ( ACPI_INTERFACE_HANDLER Handler)) /* * Global Lock interfaces */ ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiAcquireGlobalLock ( UINT16 Timeout, UINT32 *Handle)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiReleaseGlobalLock ( UINT32 Handle)) /* * Interfaces to AML mutex objects */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiAcquireMutex ( ACPI_HANDLE Handle, ACPI_STRING Pathname, UINT16 Timeout)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiReleaseMutex ( ACPI_HANDLE Handle, ACPI_STRING Pathname)) /* * Fixed Event interfaces */ ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnableEvent ( UINT32 Event, UINT32 Flags)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiDisableEvent ( UINT32 Event, UINT32 Flags)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiClearEvent ( UINT32 Event)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetEventStatus ( UINT32 Event, ACPI_EVENT_STATUS *EventStatus)) /* * General Purpose Event (GPE) Interfaces */ ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiUpdateAllGpes ( void)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnableGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiDisableGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiClearGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiSetGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, UINT8 Action)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiFinishGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiMaskGpe ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, BOOLEAN IsMasked)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiMarkGpeForWake ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiSetupGpeForWake ( ACPI_HANDLE ParentDevice, ACPI_HANDLE GpeDevice, UINT32 GpeNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiSetGpeWakeMask ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, UINT8 Action)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetGpeStatus ( ACPI_HANDLE GpeDevice, UINT32 GpeNumber, ACPI_EVENT_STATUS *EventStatus)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiDisableAllGpes ( void)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnableAllRuntimeGpes ( void)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnableAllWakeupGpes ( void)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetGpeDevice ( UINT32 GpeIndex, ACPI_HANDLE *GpeDevice)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiInstallGpeBlock ( ACPI_HANDLE GpeDevice, ACPI_GENERIC_ADDRESS *GpeBlockAddress, UINT32 RegisterCount, UINT32 InterruptNumber)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiRemoveGpeBlock ( ACPI_HANDLE GpeDevice)) /* * Resource interfaces */ typedef ACPI_STATUS (*ACPI_WALK_RESOURCE_CALLBACK) ( ACPI_RESOURCE *Resource, void *Context); ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetVendorResource ( ACPI_HANDLE Device, char *Name, ACPI_VENDOR_UUID *Uuid, ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetCurrentResources ( ACPI_HANDLE Device, ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetPossibleResources ( ACPI_HANDLE Device, ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetEventResources ( ACPI_HANDLE DeviceHandle, ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiWalkResourceBuffer ( ACPI_BUFFER *Buffer, ACPI_WALK_RESOURCE_CALLBACK UserFunction, void *Context)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiWalkResources ( ACPI_HANDLE Device, char *Name, ACPI_WALK_RESOURCE_CALLBACK UserFunction, void *Context)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiSetCurrentResources ( ACPI_HANDLE Device, ACPI_BUFFER *InBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetIrqRoutingTable ( ACPI_HANDLE Device, ACPI_BUFFER *RetBuffer)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiResourceToAddress64 ( ACPI_RESOURCE *Resource, ACPI_RESOURCE_ADDRESS64 *Out)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiBufferToResource ( UINT8 *AmlBuffer, UINT16 AmlBufferLength, ACPI_RESOURCE **ResourcePtr)) /* * Hardware (ACPI device) interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiReset ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiRead ( UINT64 *Value, ACPI_GENERIC_ADDRESS *Reg)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiWrite ( UINT64 Value, ACPI_GENERIC_ADDRESS *Reg)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiReadBitRegister ( UINT32 RegisterId, UINT32 *ReturnValue)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiWriteBitRegister ( UINT32 RegisterId, UINT32 Value)) /* * Sleep/Wake interfaces */ ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiGetSleepTypeData ( UINT8 SleepState, UINT8 *Slp_TypA, UINT8 *Slp_TypB)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiEnterSleepStatePrep ( UINT8 SleepState)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiEnterSleepState ( UINT8 SleepState)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiEnterSleepStateS4bios ( void)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiLeaveSleepStatePrep ( UINT8 SleepState)) ACPI_EXTERNAL_RETURN_STATUS ( ACPI_STATUS AcpiLeaveSleepState ( UINT8 SleepState)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiSetFirmwareWakingVector ( ACPI_PHYSICAL_ADDRESS PhysicalAddress, ACPI_PHYSICAL_ADDRESS PhysicalAddress64)) /* * ACPI Timer interfaces */ ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetTimerResolution ( UINT32 *Resolution)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetTimer ( UINT32 *Ticks)) ACPI_HW_DEPENDENT_RETURN_STATUS ( ACPI_STATUS AcpiGetTimerDuration ( UINT32 StartTicks, UINT32 EndTicks, UINT32 *TimeElapsed)) /* * Error/Warning output */ ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(3) void ACPI_INTERNAL_VAR_XFACE AcpiError ( const char *ModuleName, UINT32 LineNumber, const char *Format, ...)) ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(4) void ACPI_INTERNAL_VAR_XFACE AcpiException ( const char *ModuleName, UINT32 LineNumber, ACPI_STATUS Status, const char *Format, ...)) ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(3) void ACPI_INTERNAL_VAR_XFACE AcpiWarning ( const char *ModuleName, UINT32 LineNumber, const char *Format, ...)) ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(1) void ACPI_INTERNAL_VAR_XFACE AcpiInfo ( const char *Format, ...)) ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(3) void ACPI_INTERNAL_VAR_XFACE AcpiBiosError ( const char *ModuleName, UINT32 LineNumber, const char *Format, ...)) ACPI_MSG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(3) void ACPI_INTERNAL_VAR_XFACE AcpiBiosWarning ( const char *ModuleName, UINT32 LineNumber, const char *Format, ...)) /* * Debug output */ ACPI_DBG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(6) void ACPI_INTERNAL_VAR_XFACE AcpiDebugPrint ( UINT32 RequestedDebugLevel, UINT32 LineNumber, const char *FunctionName, const char *ModuleName, UINT32 ComponentId, const char *Format, ...)) ACPI_DBG_DEPENDENT_RETURN_VOID ( ACPI_PRINTF_LIKE(6) void ACPI_INTERNAL_VAR_XFACE AcpiDebugPrintRaw ( UINT32 RequestedDebugLevel, UINT32 LineNumber, const char *FunctionName, const char *ModuleName, UINT32 ComponentId, const char *Format, ...)) ACPI_DBG_DEPENDENT_RETURN_VOID ( void AcpiTracePoint ( ACPI_TRACE_EVENT_TYPE Type, BOOLEAN Begin, UINT8 *Aml, char *Pathname)) ACPI_STATUS AcpiInitializeDebugger ( void); void AcpiTerminateDebugger ( void); void AcpiRunDebugger ( char *BatchBuffer); void AcpiSetDebuggerThreadId ( ACPI_THREAD_ID ThreadId); #endif /* __ACXFACE_H__ */ Index: head/sys/ddb/ddb.h =================================================================== --- head/sys/ddb/ddb.h (revision 312926) +++ head/sys/ddb/ddb.h (revision 312927) @@ -1,297 +1,296 @@ /*- * Copyright (c) 1993, Garrett A. Wollman. * Copyright (c) 1993, University of Vermont and State Agricultural College. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Necessary declarations for the `ddb' kernel debugger. */ #ifndef _DDB_DDB_H_ #define _DDB_DDB_H_ #ifdef SYSCTL_DECL SYSCTL_DECL(_debug_ddb); #endif #include /* type definitions */ #include /* LIST_* */ #include /* SYSINIT */ #ifndef DB_MAXARGS #define DB_MAXARGS 10 #endif #ifndef DB_MAXLINE #define DB_MAXLINE 120 #endif #ifndef DB_MAXSCRIPTS #define DB_MAXSCRIPTS 8 #endif #ifndef DB_MAXSCRIPTNAME #define DB_MAXSCRIPTNAME 32 #endif #ifndef DB_MAXSCRIPTLEN #define DB_MAXSCRIPTLEN 128 #endif #ifndef DB_MAXSCRIPTRECURSION #define DB_MAXSCRIPTRECURSION 3 #endif #ifndef DB_CALL #define DB_CALL db_fncall_generic #else int DB_CALL(db_expr_t, db_expr_t *, int, db_expr_t[]); #endif /* * Extern variables to set the address and size of the symtab and strtab. * Most users should use db_fetch_symtab in order to set them from the * boot loader provided values. */ extern vm_offset_t ksymtab, kstrtab, ksymtab_size; /* * There are three "command tables": * - One for simple commands; a list of these is displayed * by typing 'help' at the debugger prompt. * - One for sub-commands of 'show'; to see this type 'show' * without any arguments. * - The last one for sub-commands of 'show all'; type 'show all' * without any argument to get a list. */ struct command; LIST_HEAD(command_table, command); extern struct command_table db_cmd_table; extern struct command_table db_show_table; extern struct command_table db_show_all_table; /* * Type signature for a function implementing a ddb command. */ typedef void db_cmdfcn_t(db_expr_t addr, bool have_addr, db_expr_t count, char *modif); /* * Command table entry. */ struct command { char * name; /* command name */ db_cmdfcn_t *fcn; /* function to call */ int flag; /* extra info: */ #define CS_OWN 0x1 /* non-standard syntax */ #define CS_MORE 0x2 /* standard syntax, but may have other words * at end */ #define CS_SET_DOT 0x100 /* set dot after command */ struct command_table *more; /* another level of command */ LIST_ENTRY(command) next; /* next entry in the command table */ }; /* * Arrange for the specified ddb command to be defined and * bound to the specified function. Commands can be defined * in modules in which case they will be available only when * the module is loaded. */ #define _DB_SET(_suffix, _name, _func, list, _flag, _more) \ static struct command __CONCAT(_name,_suffix) = { \ .name = __STRING(_name), \ .fcn = _func, \ .flag = _flag, \ .more = _more \ }; \ static void __CONCAT(__CONCAT(_name,_suffix),_add)(void *arg __unused) \ { db_command_register(&list, &__CONCAT(_name,_suffix)); } \ SYSINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ __CONCAT(__CONCAT(_name,_suffix),_add), NULL); \ static void __CONCAT(__CONCAT(_name,_suffix),_del)(void *arg __unused) \ { db_command_unregister(&list, &__CONCAT(_name,_suffix)); } \ SYSUNINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ __CONCAT(__CONCAT(_name,_suffix),_del), NULL); /* * Like _DB_SET but also create the function declaration which * must be followed immediately by the body; e.g. * _DB_FUNC(_cmd, panic, db_panic, db_cmd_table, 0, NULL) * { * ...panic implementation... * } * * This macro is mostly used to define commands placed in one of * the ddb command tables; see DB_COMMAND, etc. below. */ #define _DB_FUNC(_suffix, _name, _func, list, _flag, _more) \ static db_cmdfcn_t _func; \ _DB_SET(_suffix, _name, _func, list, _flag, _more); \ static void \ _func(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) /* common idom provided for backwards compatibility */ #define DB_FUNC(_name, _func, list, _flag, _more) \ _DB_FUNC(_cmd, _name, _func, list, _flag, _more) #define DB_COMMAND(cmd_name, func_name) \ _DB_FUNC(_cmd, cmd_name, func_name, db_cmd_table, 0, NULL) #define DB_ALIAS(alias_name, func_name) \ _DB_SET(_cmd, alias_name, func_name, db_cmd_table, 0, NULL) #define DB_SHOW_COMMAND(cmd_name, func_name) \ _DB_FUNC(_show, cmd_name, func_name, db_show_table, 0, NULL) #define DB_SHOW_ALIAS(alias_name, func_name) \ _DB_SET(_show, alias_name, func_name, db_show_table, 0, NULL) #define DB_SHOW_ALL_COMMAND(cmd_name, func_name) \ _DB_FUNC(_show_all, cmd_name, func_name, db_show_all_table, 0, NULL) #define DB_SHOW_ALL_ALIAS(alias_name, func_name) \ _DB_SET(_show_all, alias_name, func_name, db_show_all_table, 0, NULL) extern db_expr_t db_maxoff; extern int db_indent; extern int db_inst_count; extern int db_load_count; extern int db_store_count; extern volatile int db_pager_quit; extern db_expr_t db_radix; extern db_expr_t db_max_width; extern db_expr_t db_tab_stop_width; extern db_expr_t db_lines_per_page; struct thread; struct vm_map; void db_check_interrupt(void); void db_clear_watchpoints(void); db_addr_t db_disasm(db_addr_t loc, bool altfmt); /* instruction disassembler */ void db_error(const char *s); int db_expression(db_expr_t *valuep); int db_get_variable(db_expr_t *valuep); void db_iprintf(const char *,...) __printflike(1, 2); struct proc *db_lookup_proc(db_expr_t addr); struct thread *db_lookup_thread(db_expr_t addr, bool check_pid); struct vm_map *db_map_addr(vm_offset_t); bool db_map_current(struct vm_map *); bool db_map_equal(struct vm_map *, struct vm_map *); int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); void db_print_loc_and_inst(db_addr_t loc); void db_print_thread(void); int db_printf(const char *fmt, ...) __printflike(1, 2); int db_read_bytes(vm_offset_t addr, size_t size, char *data); /* machine-dependent */ int db_readline(char *lstart, int lsize); void db_restart_at_pc(bool watchpt); int db_set_variable(db_expr_t value); void db_set_watchpoints(void); void db_skip_to_eol(void); bool db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint); #define db_strcpy strcpy void db_trace_self(void); -void db_trace_self_depth(int); int db_trace_thread(struct thread *, int); bool db_value_of_name(const char *name, db_expr_t *valuep); bool db_value_of_name_pcpu(const char *name, db_expr_t *valuep); bool db_value_of_name_vnet(const char *name, db_expr_t *valuep); int db_write_bytes(vm_offset_t addr, size_t size, char *data); void db_command_register(struct command_table *, struct command *); void db_command_unregister(struct command_table *, struct command *); int db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end); db_cmdfcn_t db_breakpoint_cmd; db_cmdfcn_t db_capture_cmd; db_cmdfcn_t db_continue_cmd; db_cmdfcn_t db_delete_cmd; db_cmdfcn_t db_deletehwatch_cmd; db_cmdfcn_t db_deletewatch_cmd; db_cmdfcn_t db_examine_cmd; db_cmdfcn_t db_findstack_cmd; db_cmdfcn_t db_hwatchpoint_cmd; db_cmdfcn_t db_listbreak_cmd; db_cmdfcn_t db_scripts_cmd; db_cmdfcn_t db_print_cmd; db_cmdfcn_t db_ps; db_cmdfcn_t db_run_cmd; db_cmdfcn_t db_script_cmd; db_cmdfcn_t db_search_cmd; db_cmdfcn_t db_set_cmd; db_cmdfcn_t db_set_thread; db_cmdfcn_t db_show_regs; db_cmdfcn_t db_show_threads; db_cmdfcn_t db_single_step_cmd; db_cmdfcn_t db_textdump_cmd; db_cmdfcn_t db_trace_until_call_cmd; db_cmdfcn_t db_trace_until_matching_cmd; db_cmdfcn_t db_unscript_cmd; db_cmdfcn_t db_watchpoint_cmd; db_cmdfcn_t db_write_cmd; /* * Interface between DDB and the DDB output capture facility. */ struct dumperinfo; void db_capture_dump(struct dumperinfo *di); void db_capture_enterpager(void); void db_capture_exitpager(void); void db_capture_write(char *buffer, u_int buflen); void db_capture_writech(char ch); /* * Interface between DDB and the script facility. */ void db_script_kdbenter(const char *eventname); /* KDB enter event. */ /* * Interface between DDB and the textdump facility. * * Text dump blocks are of a fixed size; textdump_block_buffer is a * statically allocated buffer that code interacting with textdumps can use * to prepare and hold a pending block in when calling writenextblock(). */ #define TEXTDUMP_BLOCKSIZE 512 extern char textdump_block_buffer[TEXTDUMP_BLOCKSIZE]; void textdump_mkustar(char *block_buffer, const char *filename, u_int size); void textdump_restoreoff(off_t offset); void textdump_saveoff(off_t *offsetp); int textdump_writenextblock(struct dumperinfo *di, char *buffer); /* * Interface between the kernel and textdumps. */ extern int textdump_pending; /* Call textdump_dumpsys() instead. */ void textdump_dumpsys(struct dumperinfo *di); #endif /* !_DDB_DDB_H_ */ Index: head/sys/dev/drm2/drm_agpsupport.c =================================================================== --- head/sys/dev/drm2/drm_agpsupport.c (revision 312926) +++ head/sys/dev/drm2/drm_agpsupport.c (revision 312927) @@ -1,468 +1,466 @@ /** * \file drm_agpsupport.c * DRM support for AGP/GART backend * * \author Rickard E. (Rik) Faith * \author Gareth Hughes */ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include +#include #if __OS_HAS_AGP /** * Get AGP information. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a (output) drm_agp_info structure. * \return zero on success or a negative number on failure. * * Verifies the AGP device has been initialized and acquired and fills in the * drm_agp_info structure with the information in drm_agp_head::agp_info. */ int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info) { DRM_AGP_KERN *kern; if (!dev->agp || !dev->agp->acquired) return -EINVAL; kern = &dev->agp->agp_info; agp_get_info(dev->agp->bridge, kern); info->agp_version_major = 1; info->agp_version_minor = 0; info->mode = kern->ai_mode; info->aperture_base = kern->ai_aperture_base; info->aperture_size = kern->ai_aperture_size; info->memory_allowed = kern->ai_memory_allowed; info->memory_used = kern->ai_memory_used; info->id_vendor = kern->ai_devid & 0xffff; info->id_device = kern->ai_devid >> 16; return 0; } EXPORT_SYMBOL(drm_agp_info); int drm_agp_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_info *info = data; int err; err = drm_agp_info(dev, info); if (err) return err; return 0; } /** * Acquire the AGP device. * * \param dev DRM device that is to acquire AGP. * \return zero on success or a negative number on failure. * * Verifies the AGP device hasn't been acquired before and calls * \c agp_backend_acquire. */ int drm_agp_acquire(struct drm_device * dev) { int retcode; if (!dev->agp) return -ENODEV; if (dev->agp->acquired) return -EBUSY; retcode = agp_acquire(dev->agp->bridge); if (retcode) return -retcode; dev->agp->acquired = 1; return 0; } EXPORT_SYMBOL(drm_agp_acquire); /** * Acquire the AGP device (ioctl). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg user argument. * \return zero on success or a negative number on failure. * * Verifies the AGP device hasn't been acquired before and calls * \c agp_backend_acquire. */ int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { return drm_agp_acquire((struct drm_device *) file_priv->minor->dev); } /** * Release the AGP device. * * \param dev DRM device that is to release AGP. * \return zero on success or a negative number on failure. * * Verifies the AGP device has been acquired and calls \c agp_backend_release. */ int drm_agp_release(struct drm_device * dev) { if (!dev->agp || !dev->agp->acquired) return -EINVAL; agp_release(dev->agp->bridge); dev->agp->acquired = 0; return 0; } EXPORT_SYMBOL(drm_agp_release); int drm_agp_release_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { return drm_agp_release(dev); } /** * Enable the AGP bus. * * \param dev DRM device that has previously acquired AGP. * \param mode Requested AGP mode. * \return zero on success or a negative number on failure. * * Verifies the AGP device has been acquired but not enabled, and calls * \c agp_enable. */ int drm_agp_enable(struct drm_device * dev, struct drm_agp_mode mode) { if (!dev->agp || !dev->agp->acquired) return -EINVAL; dev->agp->mode = mode.mode; agp_enable(dev->agp->bridge, mode.mode); dev->agp->enabled = 1; return 0; } EXPORT_SYMBOL(drm_agp_enable); int drm_agp_enable_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_mode *mode = data; return drm_agp_enable(dev, *mode); } /** * Allocate AGP memory. * * \param inode device inode. * \param file_priv file private pointer. * \param cmd command. * \param arg pointer to a drm_agp_buffer structure. * \return zero on success or a negative number on failure. * * Verifies the AGP device is present and has been acquired, allocates the * memory via agp_allocate_memory() and creates a drm_agp_mem entry for it. */ int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request) { struct drm_agp_mem *entry; DRM_AGP_MEM *memory; unsigned long pages; u32 type; struct agp_memory_info info; if (!dev->agp || !dev->agp->acquired) return -EINVAL; - if (!(entry = malloc(sizeof(*entry), DRM_MEM_AGPLISTS, M_NOWAIT))) + if (!(entry = kzalloc(sizeof(*entry), GFP_KERNEL))) return -ENOMEM; - memset(entry, 0, sizeof(*entry)); - pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; type = (u32) request->type; if (!(memory = agp_alloc_memory(dev->agp->bridge, type, pages << PAGE_SHIFT))) { - free(entry, DRM_MEM_AGPLISTS); + kfree(entry); return -ENOMEM; } entry->handle = (unsigned long)memory; entry->memory = memory; entry->bound = 0; entry->pages = pages; list_add(&entry->head, &dev->agp->memory); agp_memory_info(dev->agp->bridge, entry->memory, &info); request->handle = entry->handle; request->physical = info.ami_physical; return 0; } EXPORT_SYMBOL(drm_agp_alloc); int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_buffer *request = data; return drm_agp_alloc(dev, request); } /** * Search for the AGP memory entry associated with a handle. * * \param dev DRM device structure. * \param handle AGP memory handle. * \return pointer to the drm_agp_mem structure associated with \p handle. * * Walks through drm_agp_head::memory until finding a matching handle. */ static struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device * dev, unsigned long handle) { struct drm_agp_mem *entry; list_for_each_entry(entry, &dev->agp->memory, head) { if (entry->handle == handle) return entry; } return NULL; } /** * Unbind AGP memory from the GATT (ioctl). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_agp_binding structure. * \return zero on success or a negative number on failure. * * Verifies the AGP device is present and acquired, looks-up the AGP memory * entry and passes it to the unbind_agp() function. */ int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request) { struct drm_agp_mem *entry; int ret; if (!dev->agp || !dev->agp->acquired) return -EINVAL; if (!(entry = drm_agp_lookup_entry(dev, request->handle))) return -EINVAL; if (!entry->bound) return -EINVAL; ret = drm_unbind_agp(entry->memory); if (ret == 0) entry->bound = 0; return ret; } EXPORT_SYMBOL(drm_agp_unbind); int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_binding *request = data; return drm_agp_unbind(dev, request); } /** * Bind AGP memory into the GATT (ioctl) * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_agp_binding structure. * \return zero on success or a negative number on failure. * * Verifies the AGP device is present and has been acquired and that no memory * is currently bound into the GATT. Looks-up the AGP memory entry and passes * it to bind_agp() function. */ int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request) { struct drm_agp_mem *entry; int retcode; int page; if (!dev->agp || !dev->agp->acquired) return -EINVAL; if (!(entry = drm_agp_lookup_entry(dev, request->handle))) return -EINVAL; if (entry->bound) return -EINVAL; page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE; if ((retcode = drm_bind_agp(entry->memory, page))) return retcode; entry->bound = dev->agp->base + (page << PAGE_SHIFT); DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", dev->agp->base, entry->bound); return 0; } EXPORT_SYMBOL(drm_agp_bind); int drm_agp_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_binding *request = data; return drm_agp_bind(dev, request); } /** * Free AGP memory (ioctl). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_agp_buffer structure. * \return zero on success or a negative number on failure. * * Verifies the AGP device is present and has been acquired and looks up the * AGP memory entry. If the memory it's currently bound, unbind it via * unbind_agp(). Frees it via free_agp() as well as the entry itself * and unlinks from the doubly linked list it's inserted in. */ int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request) { struct drm_agp_mem *entry; if (!dev->agp || !dev->agp->acquired) return -EINVAL; if (!(entry = drm_agp_lookup_entry(dev, request->handle))) return -EINVAL; if (entry->bound) drm_unbind_agp(entry->memory); list_del(&entry->head); drm_free_agp(entry->memory, entry->pages); - free(entry, DRM_MEM_AGPLISTS); + kfree(entry); return 0; } EXPORT_SYMBOL(drm_agp_free); int drm_agp_free_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_agp_buffer *request = data; return drm_agp_free(dev, request); } /** * Initialize the AGP resources. * * \return pointer to a drm_agp_head structure. * * Gets the drm_agp_t structure which is made available by the agpgart module * via the inter_module_* functions. Creates and initializes a drm_agp_head * structure. */ struct drm_agp_head *drm_agp_init(struct drm_device *dev) { struct drm_agp_head *head = NULL; - if (!(head = malloc(sizeof(*head), DRM_MEM_AGPLISTS, M_NOWAIT))) + if (!(head = kzalloc(sizeof(*head), GFP_KERNEL))) return NULL; - memset((void *)head, 0, sizeof(*head)); head->bridge = agp_find_device(); if (!head->bridge) { - free(head, DRM_MEM_AGPLISTS); + kfree(head); return NULL; } else { agp_get_info(head->bridge, &head->agp_info); } INIT_LIST_HEAD(&head->memory); head->cant_use_aperture = 0; head->base = head->agp_info.ai_aperture_base; return head; } #ifdef FREEBSD_NOTYET /** * Binds a collection of pages into AGP memory at the given offset, returning * the AGP memory structure containing them. * * No reference is held on the pages during this time -- it is up to the * caller to handle that. */ DRM_AGP_MEM * drm_agp_bind_pages(struct drm_device *dev, struct page **pages, unsigned long num_pages, uint32_t gtt_offset, u32 type) { DRM_AGP_MEM *mem; int ret, i; DRM_DEBUG("\n"); mem = agp_allocate_memory(dev->agp->bridge, num_pages, type); if (mem == NULL) { DRM_ERROR("Failed to allocate memory for %ld pages\n", num_pages); return NULL; } for (i = 0; i < num_pages; i++) mem->pages[i] = pages[i]; mem->page_count = num_pages; mem->is_flushed = true; ret = agp_bind_memory(mem, gtt_offset / PAGE_SIZE); if (ret != 0) { DRM_ERROR("Failed to bind AGP memory: %d\n", ret); agp_free_memory(mem); return NULL; } return mem; } EXPORT_SYMBOL(drm_agp_bind_pages); #endif /* FREEBSD_NOTYET */ #endif /* __OS_HAS_AGP */ Index: head/sys/dev/drm2/drm_bufs.c =================================================================== --- head/sys/dev/drm2/drm_bufs.c (revision 312926) +++ head/sys/dev/drm2/drm_bufs.c (revision 312927) @@ -1,1702 +1,1708 @@ /** * \file drm_bufs.c * Generic buffer template * * \author Rickard E. (Rik) Faith * \author Gareth Hughes */ /* * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include -#include -#include - #include #include /* Allocation of PCI memory resources (framebuffer, registers, etc.) for * drm_get_resource_*. Note that they are not RF_ACTIVE, so there's no virtual * address for accessing them. Cleaned up at unload. */ static int drm_alloc_resource(struct drm_device *dev, int resource) { struct resource *res; int rid; if (resource >= DRM_MAX_PCI_RESOURCE) { DRM_ERROR("Resource %d too large\n", resource); return 1; } if (dev->pcir[resource] != NULL) { return 0; } rid = PCIR_BAR(resource); res = bus_alloc_resource_any(dev->dev, SYS_RES_MEMORY, &rid, RF_SHAREABLE); if (res == NULL) { DRM_ERROR("Couldn't find resource 0x%x\n", resource); return 1; } if (dev->pcir[resource] == NULL) { dev->pcirid[resource] = rid; dev->pcir[resource] = res; } return 0; } unsigned long drm_get_resource_start(struct drm_device *dev, unsigned int resource) { unsigned long start; mtx_lock(&dev->pcir_lock); if (drm_alloc_resource(dev, resource) != 0) return 0; start = rman_get_start(dev->pcir[resource]); mtx_unlock(&dev->pcir_lock); return (start); } unsigned long drm_get_resource_len(struct drm_device *dev, unsigned int resource) { unsigned long len; mtx_lock(&dev->pcir_lock); if (drm_alloc_resource(dev, resource) != 0) return 0; len = rman_get_size(dev->pcir[resource]); mtx_unlock(&dev->pcir_lock); return (len); } static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, struct drm_local_map *map) { struct drm_map_list *entry; list_for_each_entry(entry, &dev->maplist, head) { /* * Because the kernel-userspace ABI is fixed at a 32-bit offset * while PCI resources may live above that, we only compare the * lower 32 bits of the map offset for maps of type * _DRM_FRAMEBUFFER or _DRM_REGISTERS. * It is assumed that if a driver have more than one resource * of each type, the lower 32 bits are different. */ if (!entry->map || map->type != entry->map->type || entry->master != dev->primary->master) continue; switch (map->type) { case _DRM_SHM: if (map->flags != _DRM_CONTAINS_LOCK) break; return entry; case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: if ((entry->map->offset & 0xffffffff) == (map->offset & 0xffffffff)) return entry; default: /* Make gcc happy */ ; } if (entry->map->offset == map->offset) return entry; } return NULL; } static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash, unsigned long user_token, int hashed_handle, int shm) { int use_hashed_handle, shift; unsigned long add; #if (BITS_PER_LONG == 64) use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); #elif (BITS_PER_LONG == 32) use_hashed_handle = hashed_handle; #else #error Unsupported long size. Neither 64 nor 32 bits. #endif if (!use_hashed_handle) { int ret; hash->key = user_token >> PAGE_SHIFT; ret = drm_ht_insert_item(&dev->map_hash, hash); if (ret != -EINVAL) return ret; } shift = 0; add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT; if (shm && (SHMLBA > PAGE_SIZE)) { int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1; /* For shared memory, we have to preserve the SHMLBA * bits of the eventual vma->vm_pgoff value during * mmap(). Otherwise we run into cache aliasing problems * on some platforms. On these platforms, the pgoff of * a mmap() request is used to pick a suitable virtual * address for the mmap() region such that it will not * cause cache aliasing problems. * * Therefore, make sure the SHMLBA relevant bits of the * hash value we use are equal to those in the original * kernel virtual address. */ shift = bits; add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL)); } return drm_ht_just_insert_please(&dev->map_hash, hash, user_token, 32 - PAGE_SHIFT - 3, shift, add); } /** * Core function to create a range of memory available for mapping by a * non-root process. * * Adjusts the memory offset to its absolute value according to the mapping * type. Adds the map to the map list drm_device::maplist. Adds MTRR's where * applicable and if supported by the kernel. */ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, unsigned int size, enum drm_map_type type, enum drm_map_flags flags, struct drm_map_list ** maplist) { struct drm_local_map *map; struct drm_map_list *list; drm_dma_handle_t *dmah; unsigned long user_token; int ret; int align; - map = kmalloc(sizeof(*map), GFP_KERNEL); + map = malloc(sizeof(*map), DRM_MEM_MAPS, M_NOWAIT); if (!map) return -ENOMEM; map->offset = offset; map->size = size; map->flags = flags; map->type = type; /* Only allow shared memory to be removable since we only keep enough * book keeping information about shared memory to allow for removal * when processes fork. */ if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) { - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n", (unsigned long long)map->offset, map->size, map->type); /* page-align _DRM_SHM maps. They are allocated here so there is no security * hole created by that and it works around various broken drivers that use * a non-aligned quantity to map the SAREA. --BenH */ if (map->type == _DRM_SHM) map->size = PAGE_ALIGN(map->size); /* * FreeBSD port note: FreeBSD's PAGE_MASK is the inverse of * Linux's one. That's why the test below doesn't inverse the * constant. */ if ((map->offset & ((resource_size_t)PAGE_MASK)) || (map->size & (PAGE_MASK))) { - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } map->mtrr = -1; map->handle = NULL; switch (map->type) { case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: #ifdef __linux__ #if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__) if (map->offset + (map->size-1) < map->offset || map->offset < virt_to_phys(high_memory)) { kfree(map); return -EINVAL; } #endif #endif /* Some drivers preinitialize some maps, without the X Server * needing to be aware of it. Therefore, we just return success * when the server tries to create a duplicate map. */ list = drm_find_matching_map(dev, map); if (list != NULL) { if (list->map->size != map->size) { DRM_DEBUG("Matching maps of type %d with " "mismatched sizes, (%ld vs %ld)\n", map->type, map->size, list->map->size); list->map->size = map->size; } - kfree(map); + free(map, DRM_MEM_MAPS); *maplist = list; return 0; } if (drm_core_has_MTRR(dev)) { if (map->type == _DRM_FRAME_BUFFER || (map->flags & _DRM_WRITE_COMBINING)) { if (drm_mtrr_add( map->offset, map->size, DRM_MTRR_WC) == 0) map->mtrr = 1; } } if (map->type == _DRM_REGISTERS) { drm_core_ioremap(map, dev); if (!map->handle) { - kfree(map); + free(map, DRM_MEM_MAPS); return -ENOMEM; } } break; case _DRM_SHM: list = drm_find_matching_map(dev, map); if (list != NULL) { if(list->map->size != map->size) { DRM_DEBUG("Matching maps of type %d with " "mismatched sizes, (%ld vs %ld)\n", map->type, map->size, list->map->size); list->map->size = map->size; } - kfree(map); + free(map, DRM_MEM_MAPS); *maplist = list; return 0; } - map->handle = vmalloc_user(map->size); + map->handle = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT); DRM_DEBUG("%lu %d %p\n", map->size, drm_order(map->size), map->handle); if (!map->handle) { - kfree(map); + free(map, DRM_MEM_MAPS); return -ENOMEM; } map->offset = (unsigned long)map->handle; if (map->flags & _DRM_CONTAINS_LOCK) { /* Prevent a 2nd X Server from creating a 2nd lock */ if (dev->primary->master->lock.hw_lock != NULL) { - kfree(map->handle); - kfree(map); + free(map->handle, DRM_MEM_MAPS); + free(map, DRM_MEM_MAPS); return -EBUSY; } dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ } break; case _DRM_AGP: { struct drm_agp_mem *entry; int valid = 0; if (!drm_core_has_AGP(dev)) { - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } #ifdef __linux__ #ifdef __alpha__ map->offset += dev->hose->mem_space->start; #endif #endif /* In some cases (i810 driver), user space may have already * added the AGP base itself, because dev->agp->base previously * only got set during AGP enable. So, only add the base * address if the map's offset isn't already within the * aperture. */ if (map->offset < dev->agp->base || map->offset > dev->agp->base + dev->agp->agp_info.ai_aperture_size * 1024 * 1024 - 1) { map->offset += dev->agp->base; } map->mtrr = dev->agp->agp_mtrr; /* for getmap */ /* This assumes the DRM is in total control of AGP space. * It's not always the case as AGP can be in the control * of user space (i.e. i810 driver). So this loop will get * skipped and we double check that dev->agp->memory is * actually set as well as being invalid before EPERM'ing */ list_for_each_entry(entry, &dev->agp->memory, head) { if ((map->offset >= entry->bound) && (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) { valid = 1; break; } } if (!list_empty(&dev->agp->memory) && !valid) { - kfree(map); + free(map, DRM_MEM_MAPS); return -EPERM; } DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n", (unsigned long long)map->offset, map->size); break; } case _DRM_GEM: DRM_ERROR("tried to addmap GEM object\n"); break; case _DRM_SCATTER_GATHER: if (!dev->sg) { - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } map->handle = (void *)(dev->sg->vaddr + offset); map->offset += dev->sg->vaddr; break; case _DRM_CONSISTENT: /* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G, * As we're limiting the address to 2^32-1 (or less), * casting it down to 32 bits is no problem, but we * need to point to a 64bit variable first. */ align = map->size; if ((align & (align - 1)) != 0) align = PAGE_SIZE; dmah = drm_pci_alloc(dev, map->size, align, BUS_SPACE_MAXADDR); if (!dmah) { - kfree(map); + free(map, DRM_MEM_MAPS); return -ENOMEM; } map->handle = dmah->vaddr; map->offset = dmah->busaddr; map->dmah = dmah; break; default: - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } - list = kzalloc(sizeof(*list), GFP_KERNEL); + list = malloc(sizeof(*list), DRM_MEM_MAPS, M_ZERO | M_NOWAIT); if (!list) { if (map->type == _DRM_REGISTERS) drm_core_ioremapfree(map, dev); - kfree(map); + free(map, DRM_MEM_MAPS); return -EINVAL; } list->map = map; DRM_LOCK(dev); list_add(&list->head, &dev->maplist); /* Assign a 32-bit handle */ /* We do it here so that dev->struct_mutex protects the increment */ user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : map->offset; ret = drm_map_handle(dev, &list->hash, user_token, 0, (map->type == _DRM_SHM)); if (ret) { if (map->type == _DRM_REGISTERS) drm_core_ioremapfree(map, dev); - kfree(map); - kfree(list); + free(map, DRM_MEM_MAPS); + free(list, DRM_MEM_MAPS); DRM_UNLOCK(dev); return ret; } list->user_token = list->hash.key << PAGE_SHIFT; DRM_UNLOCK(dev); if (!(map->flags & _DRM_DRIVER)) list->master = dev->primary->master; *maplist = list; return 0; } int drm_addmap(struct drm_device * dev, resource_size_t offset, unsigned int size, enum drm_map_type type, enum drm_map_flags flags, struct drm_local_map ** map_ptr) { struct drm_map_list *list; int rc; rc = drm_addmap_core(dev, offset, size, type, flags, &list); if (!rc) *map_ptr = list->map; return rc; } EXPORT_SYMBOL(drm_addmap); /** * Ioctl to specify a range of memory that is available for mapping by a * non-root process. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_map structure. * \return zero on success or a negative value on error. * */ int drm_addmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_map *map = data; struct drm_map_list *maplist; int err; if (!(DRM_SUSER(DRM_CURPROC) || map->type == _DRM_AGP || map->type == _DRM_SHM)) return -EPERM; err = drm_addmap_core(dev, map->offset, map->size, map->type, map->flags, &maplist); if (err) return err; /* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */ map->handle = (void *)(unsigned long)maplist->user_token; return 0; } /** * Remove a map private from list and deallocate resources if the mapping * isn't in use. * * Searches the map on drm_device::maplist, removes it from the list, see if * its being used, and free any associate resource (such as MTRR's) if it's not * being on use. * * \sa drm_addmap */ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) { struct drm_map_list *r_list = NULL, *list_t; int found = 0; struct drm_master *master; /* Find the list entry for the map and remove it */ list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { if (r_list->map == map) { master = r_list->master; list_del(&r_list->head); drm_ht_remove_key(&dev->map_hash, r_list->user_token >> PAGE_SHIFT); - kfree(r_list); + free(r_list, DRM_MEM_MAPS); found = 1; break; } } if (!found) return -EINVAL; switch (map->type) { case _DRM_REGISTERS: drm_core_ioremapfree(map, dev); /* FALLTHROUGH */ case _DRM_FRAME_BUFFER: if (drm_core_has_MTRR(dev) && map->mtrr >= 0) { int retcode; retcode = drm_mtrr_del(map->mtrr, map->offset, map->size, DRM_MTRR_WC); DRM_DEBUG("mtrr_del=%d\n", retcode); } break; case _DRM_SHM: - kfree(map->handle); + free(map->handle, DRM_MEM_MAPS); if (master) { if (dev->sigdata.lock == master->lock.hw_lock) dev->sigdata.lock = NULL; master->lock.hw_lock = NULL; /* SHM removed */ master->lock.file_priv = NULL; DRM_WAKEUP_INT((void *)&master->lock.lock_queue); } break; case _DRM_AGP: case _DRM_SCATTER_GATHER: break; case _DRM_CONSISTENT: drm_pci_free(dev, map->dmah); break; case _DRM_GEM: DRM_ERROR("tried to rmmap GEM object\n"); break; } - kfree(map); + free(map, DRM_MEM_MAPS); return 0; } EXPORT_SYMBOL(drm_rmmap_locked); int drm_rmmap(struct drm_device *dev, struct drm_local_map *map) { int ret; DRM_LOCK(dev); ret = drm_rmmap_locked(dev, map); DRM_UNLOCK(dev); return ret; } EXPORT_SYMBOL(drm_rmmap); /* The rmmap ioctl appears to be unnecessary. All mappings are torn down on * the last close of the device, and this is necessary for cleanup when things * exit uncleanly. Therefore, having userland manually remove mappings seems * like a pointless exercise since they're going away anyway. * * One use case might be after addmap is allowed for normal users for SHM and * gets used by drivers that the server doesn't need to care about. This seems * unlikely. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a struct drm_map structure. * \return zero on success or a negative value on error. */ int drm_rmmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_map *request = data; struct drm_local_map *map = NULL; struct drm_map_list *r_list; int ret; DRM_LOCK(dev); list_for_each_entry(r_list, &dev->maplist, head) { if (r_list->map && r_list->user_token == (unsigned long)request->handle && r_list->map->flags & _DRM_REMOVABLE) { map = r_list->map; break; } } /* List has wrapped around to the head pointer, or its empty we didn't * find anything. */ if (list_empty(&dev->maplist) || !map) { DRM_UNLOCK(dev); return -EINVAL; } /* Register and framebuffer maps are permanent */ if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { DRM_UNLOCK(dev); return 0; } ret = drm_rmmap_locked(dev, map); DRM_UNLOCK(dev); return ret; } /** * Cleanup after an error on one of the addbufs() functions. * * \param dev DRM device. * \param entry buffer entry where the error occurred. * * Frees any pages and buffers associated with the given entry. */ static void drm_cleanup_buf_error(struct drm_device * dev, struct drm_buf_entry * entry) { int i; if (entry->seg_count) { for (i = 0; i < entry->seg_count; i++) { if (entry->seglist[i]) { drm_pci_free(dev, entry->seglist[i]); } } - kfree(entry->seglist); + free(entry->seglist, DRM_MEM_SEGS); entry->seg_count = 0; } if (entry->buf_count) { for (i = 0; i < entry->buf_count; i++) { - kfree(entry->buflist[i].dev_private); + free(entry->buflist[i].dev_private, DRM_MEM_BUFS); } - kfree(entry->buflist); + free(entry->buflist, DRM_MEM_BUFS); entry->buf_count = 0; } } #if __OS_HAS_AGP /** * Add AGP buffers for DMA transfers. * * \param dev struct drm_device to which the buffers are to be added. * \param request pointer to a struct drm_buf_desc describing the request. * \return zero on success or a negative number on failure. * * After some sanity checks creates a drm_buf structure for each buffer and * reallocates the buffer list of the same size order to accommodate the new * buffers. */ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) { struct drm_device_dma *dma = dev->dma; struct drm_buf_entry *entry; struct drm_agp_mem *agp_entry; struct drm_buf *buf; unsigned long offset; unsigned long agp_offset; int count; int order; int size; int alignment; int page_order; int total; int byte_count; int i, valid; struct drm_buf **temp_buflist; if (!dma) return -EINVAL; count = request->count; order = drm_order(request->size); size = 1 << order; alignment = (request->flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) : size; page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; byte_count = 0; agp_offset = dev->agp->base + request->agp_start; DRM_DEBUG("count: %d\n", count); DRM_DEBUG("order: %d\n", order); DRM_DEBUG("size: %d\n", size); DRM_DEBUG("agp_offset: %lx\n", agp_offset); DRM_DEBUG("alignment: %d\n", alignment); DRM_DEBUG("page_order: %d\n", page_order); DRM_DEBUG("total: %d\n", total); if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; /* Make sure buffers are located in AGP memory that we own */ valid = 0; list_for_each_entry(agp_entry, &dev->agp->memory, head) { if ((agp_offset >= agp_entry->bound) && (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) { valid = 1; break; } } if (!list_empty(&dev->agp->memory) && !valid) { DRM_DEBUG("zone invalid\n"); return -EINVAL; } mtx_lock(&dev->count_lock); if (dev->buf_use) { mtx_unlock(&dev->count_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); mtx_unlock(&dev->count_lock); DRM_LOCK(dev); entry = &dma->bufs[order]; if (entry->buf_count) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; /* May only call once for each order */ } if (count < 0 || count > 4096) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -EINVAL; } - entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!entry->buflist) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } entry->buf_size = size; entry->page_order = page_order; offset = 0; while (entry->buf_count < count) { buf = &entry->buflist[entry->buf_count]; buf->idx = dma->buf_count + entry->buf_count; buf->total = alignment; buf->order = order; buf->used = 0; buf->offset = (dma->byte_count + offset); buf->bus_address = agp_offset + offset; buf->address = (void *)(agp_offset + offset); buf->next = NULL; buf->waiting = 0; buf->pending = 0; buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); offset += alignment; entry->buf_count++; byte_count += PAGE_SIZE << page_order; } DRM_DEBUG("byte_count: %d\n", byte_count); - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), GFP_KERNEL); + temp_buflist = realloc(dma->buflist, + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), + DRM_MEM_BUFS, M_NOWAIT); if (!temp_buflist) { /* Free the entry because it isn't valid */ drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } dma->buflist = temp_buflist; for (i = 0; i < entry->buf_count; i++) { dma->buflist[i + dma->buf_count] = &entry->buflist[i]; } dma->buf_count += entry->buf_count; dma->seg_count += entry->seg_count; dma->page_count += byte_count >> PAGE_SHIFT; dma->byte_count += byte_count; DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); DRM_UNLOCK(dev); request->count = entry->buf_count; request->size = size; dma->flags = _DRM_DMA_USE_AGP; atomic_dec(&dev->buf_alloc); return 0; } EXPORT_SYMBOL(drm_addbufs_agp); #endif /* __OS_HAS_AGP */ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) { struct drm_device_dma *dma = dev->dma; int count; int order; int size; int total; int page_order; struct drm_buf_entry *entry; drm_dma_handle_t *dmah; struct drm_buf *buf; int alignment; unsigned long offset; int i; int byte_count; int page_count; unsigned long *temp_pagelist; struct drm_buf **temp_buflist; if (!drm_core_check_feature(dev, DRIVER_PCI_DMA)) return -EINVAL; if (!dma) return -EINVAL; if (!DRM_SUSER(DRM_CURPROC)) return -EPERM; count = request->count; order = drm_order(request->size); size = 1 << order; DRM_DEBUG("count=%d, size=%d (%d), order=%d\n", request->count, request->size, size, order); if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; alignment = (request->flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) : size; page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; mtx_lock(&dev->count_lock); if (dev->buf_use) { mtx_unlock(&dev->count_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); mtx_unlock(&dev->count_lock); DRM_LOCK(dev); entry = &dma->bufs[order]; if (entry->buf_count) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; /* May only call once for each order */ } if (count < 0 || count > 4096) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -EINVAL; } - entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!entry->buflist) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - entry->seglist = kcalloc(count, sizeof(*entry->seglist), GFP_KERNEL); + entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS, + M_NOWAIT | M_ZERO); if (!entry->seglist) { - kfree(entry->buflist); + free(entry->buflist, DRM_MEM_BUFS); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } /* Keep the original pagelist until we know all the allocations * have succeeded */ - temp_pagelist = kmalloc_array(dma->page_count + (count << page_order), - sizeof(*dma->pagelist), - GFP_KERNEL); + temp_pagelist = malloc((dma->page_count + (count << page_order)) * + sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT); if (!temp_pagelist) { - kfree(entry->buflist); - kfree(entry->seglist); + free(entry->buflist, DRM_MEM_BUFS); + free(entry->seglist, DRM_MEM_SEGS); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } memcpy(temp_pagelist, dma->pagelist, dma->page_count * sizeof(*dma->pagelist)); DRM_DEBUG("pagelist: %d entries\n", dma->page_count + (count << page_order)); entry->buf_size = size; entry->page_order = page_order; byte_count = 0; page_count = 0; while (entry->buf_count < count) { dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000, BUS_SPACE_MAXADDR); if (!dmah) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; entry->seg_count = count; drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); + free(temp_pagelist, DRM_MEM_PAGES); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } entry->seglist[entry->seg_count++] = dmah; for (i = 0; i < (1 << page_order); i++) { DRM_DEBUG("page %d @ 0x%08lx\n", dma->page_count + page_count, (unsigned long)dmah->vaddr + PAGE_SIZE * i); temp_pagelist[dma->page_count + page_count++] = (unsigned long)dmah->vaddr + PAGE_SIZE * i; } for (offset = 0; offset + size <= total && entry->buf_count < count; offset += alignment, ++entry->buf_count) { buf = &entry->buflist[entry->buf_count]; buf->idx = dma->buf_count + entry->buf_count; buf->total = alignment; buf->order = order; buf->used = 0; buf->offset = (dma->byte_count + byte_count + offset); buf->address = (void *)((char *)dmah->vaddr + offset); buf->bus_address = dmah->busaddr + offset; buf->next = NULL; buf->waiting = 0; buf->pending = 0; buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, - GFP_KERNEL); + buf->dev_private = malloc(buf->dev_priv_size, + DRM_MEM_BUFS, M_NOWAIT | M_ZERO); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; entry->seg_count = count; drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); + free(temp_pagelist, DRM_MEM_PAGES); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); } byte_count += PAGE_SIZE << page_order; } - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); + temp_buflist = realloc(dma->buflist, + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), + DRM_MEM_BUFS, M_NOWAIT); if (!temp_buflist) { /* Free the entry because it isn't valid */ drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); + free(temp_pagelist, DRM_MEM_PAGES); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } dma->buflist = temp_buflist; for (i = 0; i < entry->buf_count; i++) { dma->buflist[i + dma->buf_count] = &entry->buflist[i]; } /* No allocations failed, so now we can replace the original pagelist * with the new one. */ if (dma->page_count) { - kfree(dma->pagelist); + free(dma->pagelist, DRM_MEM_PAGES); } dma->pagelist = temp_pagelist; dma->buf_count += entry->buf_count; dma->seg_count += entry->seg_count; dma->page_count += entry->seg_count << page_order; dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); DRM_UNLOCK(dev); request->count = entry->buf_count; request->size = size; if (request->flags & _DRM_PCI_BUFFER_RO) dma->flags = _DRM_DMA_USE_PCI_RO; atomic_dec(&dev->buf_alloc); return 0; } EXPORT_SYMBOL(drm_addbufs_pci); static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request) { struct drm_device_dma *dma = dev->dma; struct drm_buf_entry *entry; struct drm_buf *buf; unsigned long offset; unsigned long agp_offset; int count; int order; int size; int alignment; int page_order; int total; int byte_count; int i; struct drm_buf **temp_buflist; if (!drm_core_check_feature(dev, DRIVER_SG)) return -EINVAL; if (!dma) return -EINVAL; if (!DRM_SUSER(DRM_CURPROC)) return -EPERM; count = request->count; order = drm_order(request->size); size = 1 << order; alignment = (request->flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) : size; page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; byte_count = 0; agp_offset = request->agp_start; DRM_DEBUG("count: %d\n", count); DRM_DEBUG("order: %d\n", order); DRM_DEBUG("size: %d\n", size); DRM_DEBUG("agp_offset: %lu\n", agp_offset); DRM_DEBUG("alignment: %d\n", alignment); DRM_DEBUG("page_order: %d\n", page_order); DRM_DEBUG("total: %d\n", total); if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; mtx_lock(&dev->count_lock); if (dev->buf_use) { mtx_unlock(&dev->count_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); mtx_unlock(&dev->count_lock); DRM_LOCK(dev); entry = &dma->bufs[order]; if (entry->buf_count) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; /* May only call once for each order */ } if (count < 0 || count > 4096) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -EINVAL; } - entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!entry->buflist) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } entry->buf_size = size; entry->page_order = page_order; offset = 0; while (entry->buf_count < count) { buf = &entry->buflist[entry->buf_count]; buf->idx = dma->buf_count + entry->buf_count; buf->total = alignment; buf->order = order; buf->used = 0; buf->offset = (dma->byte_count + offset); buf->bus_address = agp_offset + offset; buf->address = (void *)(agp_offset + offset + (unsigned long)dev->sg->vaddr); buf->next = NULL; buf->waiting = 0; buf->pending = 0; buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); offset += alignment; entry->buf_count++; byte_count += PAGE_SIZE << page_order; } DRM_DEBUG("byte_count: %d\n", byte_count); - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); + temp_buflist = realloc(dma->buflist, + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), + DRM_MEM_BUFS, M_NOWAIT); if (!temp_buflist) { /* Free the entry because it isn't valid */ drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } dma->buflist = temp_buflist; for (i = 0; i < entry->buf_count; i++) { dma->buflist[i + dma->buf_count] = &entry->buflist[i]; } dma->buf_count += entry->buf_count; dma->seg_count += entry->seg_count; dma->page_count += byte_count >> PAGE_SHIFT; dma->byte_count += byte_count; DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); DRM_UNLOCK(dev); request->count = entry->buf_count; request->size = size; dma->flags = _DRM_DMA_USE_SG; atomic_dec(&dev->buf_alloc); return 0; } static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request) { struct drm_device_dma *dma = dev->dma; struct drm_buf_entry *entry; struct drm_buf *buf; unsigned long offset; unsigned long agp_offset; int count; int order; int size; int alignment; int page_order; int total; int byte_count; int i; struct drm_buf **temp_buflist; if (!drm_core_check_feature(dev, DRIVER_FB_DMA)) return -EINVAL; if (!dma) return -EINVAL; if (!DRM_SUSER(DRM_CURPROC)) return -EPERM; count = request->count; order = drm_order(request->size); size = 1 << order; alignment = (request->flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) : size; page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; byte_count = 0; agp_offset = request->agp_start; DRM_DEBUG("count: %d\n", count); DRM_DEBUG("order: %d\n", order); DRM_DEBUG("size: %d\n", size); DRM_DEBUG("agp_offset: %lu\n", agp_offset); DRM_DEBUG("alignment: %d\n", alignment); DRM_DEBUG("page_order: %d\n", page_order); DRM_DEBUG("total: %d\n", total); if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; mtx_lock(&dev->count_lock); if (dev->buf_use) { mtx_unlock(&dev->count_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); mtx_unlock(&dev->count_lock); DRM_LOCK(dev); entry = &dma->bufs[order]; if (entry->buf_count) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; /* May only call once for each order */ } if (count < 0 || count > 4096) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -EINVAL; } - entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!entry->buflist) { DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } entry->buf_size = size; entry->page_order = page_order; offset = 0; while (entry->buf_count < count) { buf = &entry->buflist[entry->buf_count]; buf->idx = dma->buf_count + entry->buf_count; buf->total = alignment; buf->order = order; buf->used = 0; buf->offset = (dma->byte_count + offset); buf->bus_address = agp_offset + offset; buf->address = (void *)(agp_offset + offset); buf->next = NULL; buf->waiting = 0; buf->pending = 0; buf->file_priv = NULL; buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL); + buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, + M_NOWAIT | M_ZERO); if (!buf->dev_private) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); offset += alignment; entry->buf_count++; byte_count += PAGE_SIZE << page_order; } DRM_DEBUG("byte_count: %d\n", byte_count); - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), GFP_KERNEL); + temp_buflist = realloc(dma->buflist, + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), + DRM_MEM_BUFS, M_NOWAIT); if (!temp_buflist) { /* Free the entry because it isn't valid */ drm_cleanup_buf_error(dev, entry); DRM_UNLOCK(dev); atomic_dec(&dev->buf_alloc); return -ENOMEM; } dma->buflist = temp_buflist; for (i = 0; i < entry->buf_count; i++) { dma->buflist[i + dma->buf_count] = &entry->buflist[i]; } dma->buf_count += entry->buf_count; dma->seg_count += entry->seg_count; dma->page_count += byte_count >> PAGE_SHIFT; dma->byte_count += byte_count; DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); DRM_UNLOCK(dev); request->count = entry->buf_count; request->size = size; dma->flags = _DRM_DMA_USE_FB; atomic_dec(&dev->buf_alloc); return 0; } /** * Add buffers for DMA transfers (ioctl). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a struct drm_buf_desc request. * \return zero on success or a negative number on failure. * * According with the memory type specified in drm_buf_desc::flags and the * build options, it dispatches the call either to addbufs_agp(), * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent * PCI memory respectively. */ int drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_buf_desc *request = data; int ret; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) return -EINVAL; #if __OS_HAS_AGP if (request->flags & _DRM_AGP_BUFFER) ret = drm_addbufs_agp(dev, request); else #endif if (request->flags & _DRM_SG_BUFFER) ret = drm_addbufs_sg(dev, request); else if (request->flags & _DRM_FB_BUFFER) ret = drm_addbufs_fb(dev, request); else ret = drm_addbufs_pci(dev, request); return ret; } /** * Get information about the buffer mappings. * * This was originally mean for debugging purposes, or by a sophisticated * client library to determine how best to use the available buffers (e.g., * large buffers can be used for image transfer). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_buf_info structure. * \return zero on success or a negative number on failure. * * Increments drm_device::buf_use while holding the drm_device::count_lock * lock, preventing of allocating more buffers after this call. Information * about each requested buffer is then copied into user space. */ int drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_info *request = data; int i; int count; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) return -EINVAL; if (!dma) return -EINVAL; mtx_lock(&dev->count_lock); if (atomic_read(&dev->buf_alloc)) { mtx_unlock(&dev->count_lock); return -EBUSY; } ++dev->buf_use; /* Can't allocate more after this call */ mtx_unlock(&dev->count_lock); for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { if (dma->bufs[i].buf_count) ++count; } DRM_DEBUG("count = %d\n", count); if (request->count >= count) { for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { if (dma->bufs[i].buf_count) { struct drm_buf_desc __user *to = &request->list[count]; struct drm_buf_entry *from = &dma->bufs[i]; struct drm_freelist *list = &dma->bufs[i].freelist; if (copy_to_user(&to->count, &from->buf_count, sizeof(from->buf_count)) || copy_to_user(&to->size, &from->buf_size, sizeof(from->buf_size)) || copy_to_user(&to->low_mark, &list->low_mark, sizeof(list->low_mark)) || copy_to_user(&to->high_mark, &list->high_mark, sizeof(list->high_mark))) return -EFAULT; DRM_DEBUG("%d %d %d %d %d\n", i, dma->bufs[i].buf_count, dma->bufs[i].buf_size, dma->bufs[i].freelist.low_mark, dma->bufs[i].freelist.high_mark); ++count; } } } request->count = count; return 0; } /** * Specifies a low and high water mark for buffer allocation * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg a pointer to a drm_buf_desc structure. * \return zero on success or a negative number on failure. * * Verifies that the size order is bounded between the admissible orders and * updates the respective drm_device_dma::bufs entry low and high water mark. * * \note This ioctl is deprecated and mostly never used. */ int drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_desc *request = data; int order; struct drm_buf_entry *entry; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) return -EINVAL; if (!dma) return -EINVAL; DRM_DEBUG("%d, %d, %d\n", request->size, request->low_mark, request->high_mark); order = drm_order(request->size); if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; entry = &dma->bufs[order]; if (request->low_mark < 0 || request->low_mark > entry->buf_count) return -EINVAL; if (request->high_mark < 0 || request->high_mark > entry->buf_count) return -EINVAL; entry->freelist.low_mark = request->low_mark; entry->freelist.high_mark = request->high_mark; return 0; } /** * Unreserve the buffers in list, previously reserved using drmDMA. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_buf_free structure. * \return zero on success or a negative number on failure. * * Calls free_buffer() for each used buffer. * This function is primarily used for debugging. */ int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_free *request = data; int i; int idx; struct drm_buf *buf; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) return -EINVAL; if (!dma) return -EINVAL; DRM_DEBUG("%d\n", request->count); for (i = 0; i < request->count; i++) { if (copy_from_user(&idx, &request->list[i], sizeof(idx))) return -EFAULT; if (idx < 0 || idx >= dma->buf_count) { DRM_ERROR("Index %d (of %d max)\n", idx, dma->buf_count - 1); return -EINVAL; } buf = dma->buflist[idx]; if (buf->file_priv != file_priv) { DRM_ERROR("Process %d freeing buffer not owned\n", DRM_CURRENTPID); return -EINVAL; } drm_free_buffer(dev, buf); } return 0; } /** * Maps all of the DMA buffers into client-virtual space (ioctl). * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg pointer to a drm_buf_map structure. * \return zero on success or a negative number on failure. * * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information * about each buffer into user space. For PCI buffers, it calls vm_mmap() with * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls * drm_mmap_dma(). */ int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; int retcode = 0; const int zero = 0; vm_offset_t virtual; vm_offset_t address; struct vmspace *vms; struct drm_buf_map *request = data; int i; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) return -EINVAL; if (!dma) return -EINVAL; mtx_lock(&dev->count_lock); if (atomic_read(&dev->buf_alloc)) { mtx_unlock(&dev->count_lock); return -EBUSY; } dev->buf_use++; /* Can't allocate more after this call */ mtx_unlock(&dev->count_lock); vms = DRM_CURPROC->td_proc->p_vmspace; if (request->count >= dma->buf_count) { if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) || (drm_core_check_feature(dev, DRIVER_SG) && (dma->flags & _DRM_DMA_USE_SG)) || (drm_core_check_feature(dev, DRIVER_FB_DMA) && (dma->flags & _DRM_DMA_USE_FB))) { struct drm_local_map *map = dev->agp_buffer_map; vm_ooffset_t token = dev->agp_buffer_token; if (!map) { retcode = -EINVAL; goto done; } retcode = vm_mmap(&vms->vm_map, &virtual, map->size, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, OBJT_DEVICE, file_priv->minor->device, token); } else { retcode = vm_mmap(&vms->vm_map, &virtual, dma->byte_count, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, OBJT_DEVICE, file_priv->minor->device, 0); } if (retcode) { /* Real error */ retcode = -retcode; goto done; } request->virtual = (void __user *)virtual; for (i = 0; i < dma->buf_count; i++) { if (copy_to_user(&request->list[i].idx, &dma->buflist[i]->idx, sizeof(request->list[0].idx))) { retcode = -EFAULT; goto done; } if (copy_to_user(&request->list[i].total, &dma->buflist[i]->total, sizeof(request->list[0].total))) { retcode = -EFAULT; goto done; } if (copy_to_user(&request->list[i].used, &zero, sizeof(zero))) { retcode = -EFAULT; goto done; } address = virtual + dma->buflist[i]->offset; /* *** */ if (copy_to_user(&request->list[i].address, &address, sizeof(address))) { retcode = -EFAULT; goto done; } } } done: request->count = dma->buf_count; DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode); return retcode; } /** * Compute size order. Returns the exponent of the smaller power of two which * is greater or equal to given number. * * \param size size. * \return order. * * \todo Can be made faster. */ int drm_order(unsigned long size) { int order; unsigned long tmp; for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) ; if (size & (size - 1)) ++order; return order; } EXPORT_SYMBOL(drm_order); Index: head/sys/dev/drm2/drm_dma.c =================================================================== --- head/sys/dev/drm2/drm_dma.c (revision 312926) +++ head/sys/dev/drm2/drm_dma.c (revision 312927) @@ -1,160 +1,160 @@ /** * \file drm_dma.c * DMA IOCTL and function support * * \author Rickard E. (Rik) Faith * \author Gareth Hughes */ /*- * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include -#include /** * Initialize the DMA data. * * \param dev DRM device. * \return zero on success or a negative value on failure. * * Allocate and initialize a drm_device_dma structure. */ int drm_dma_setup(struct drm_device *dev) { int i; dev->dma = malloc(sizeof(*dev->dma), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO); if (!dev->dma) return -ENOMEM; for (i = 0; i <= DRM_MAX_ORDER; i++) memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); return 0; } /** * Cleanup the DMA resources. * * \param dev DRM device. * * Free all pages associated with DMA buffers, the buffers and pages lists, and * finally the drm_device::dma structure itself. */ void drm_dma_takedown(struct drm_device *dev) { struct drm_device_dma *dma = dev->dma; int i, j; if (!dma) return; /* Clear dma buffers */ for (i = 0; i <= DRM_MAX_ORDER; i++) { if (dma->bufs[i].seg_count) { DRM_DEBUG("order %d: buf_count = %d," " seg_count = %d\n", i, dma->bufs[i].buf_count, dma->bufs[i].seg_count); for (j = 0; j < dma->bufs[i].seg_count; j++) { if (dma->bufs[i].seglist[j]) { drm_pci_free(dev, dma->bufs[i].seglist[j]); } } - kfree(dma->bufs[i].seglist); + free(dma->bufs[i].seglist, DRM_MEM_SEGS); } if (dma->bufs[i].buf_count) { for (j = 0; j < dma->bufs[i].buf_count; j++) { - kfree(dma->bufs[i].buflist[j].dev_private); + free(dma->bufs[i].buflist[j].dev_private, + DRM_MEM_BUFS); } - kfree(dma->bufs[i].buflist); + free(dma->bufs[i].buflist, DRM_MEM_BUFS); } } - kfree(dma->buflist); + free(dma->buflist, DRM_MEM_BUFS); free(dma->pagelist, DRM_MEM_PAGES); free(dev->dma, DRM_MEM_DRIVER); dev->dma = NULL; } /** * Free a buffer. * * \param dev DRM device. * \param buf buffer to free. * * Resets the fields of \p buf. */ void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf) { if (!buf) return; buf->waiting = 0; buf->pending = 0; buf->file_priv = NULL; buf->used = 0; } /** * Reclaim the buffers. * * \param file_priv DRM file private. * * Frees each buffer associated with \p file_priv not already on the hardware. */ void drm_core_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; int i; if (!dma) return; for (i = 0; i < dma->buf_count; i++) { if (dma->buflist[i]->file_priv == file_priv) { switch (dma->buflist[i]->list) { case DRM_LIST_NONE: drm_free_buffer(dev, dma->buflist[i]); break; case DRM_LIST_WAIT: dma->buflist[i]->list = DRM_LIST_RECLAIM; break; default: /* Buffer already on hardware. */ break; } } } } EXPORT_SYMBOL(drm_core_reclaim_buffers); Index: head/sys/dev/drm2/drm_drv.c =================================================================== --- head/sys/dev/drm2/drm_drv.c (revision 312926) +++ head/sys/dev/drm2/drm_drv.c (revision 312927) @@ -1,511 +1,512 @@ /** * \file drm_drv.c * Generic driver template * * \author Rickard E. (Rik) Faith * \author Gareth Hughes * * To use this template, you must at least define the following (samples * given for the MGA driver): * * \code * #define DRIVER_AUTHOR "VA Linux Systems, Inc." * * #define DRIVER_NAME "mga" * #define DRIVER_DESC "Matrox G200/G400" * #define DRIVER_DATE "20001127" * * #define drm_x mga_##x * \endcode */ /* * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include +#include #include #include #include struct sx drm_global_mutex; /** Ioctl table */ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_getctx, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_resctx, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH), /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */ DRM_IOCTL_DEF(DRM_IOCTL_DMA, NULL, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), #if __OS_HAS_AGP DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), #endif DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), #ifdef FREEBSD_NOTYET DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED), #endif /* FREEBSD_NOTYET */ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #ifdef COMPAT_FREEBSD32 extern struct drm_ioctl_desc drm_compat_ioctls[]; #endif #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) /** * Take down the DRM device. * * \param dev DRM device structure. * * Frees every resource in \p dev. * * \sa drm_device */ int drm_lastclose(struct drm_device * dev) { #ifdef __linux__ struct drm_vma_entry *vma, *vma_temp; #endif DRM_DEBUG("\n"); if (dev->driver->lastclose) dev->driver->lastclose(dev); DRM_DEBUG("driver lastclose completed\n"); if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) drm_irq_uninstall(dev); DRM_LOCK(dev); /* Clear AGP information */ if (drm_core_has_AGP(dev) && dev->agp && !drm_core_check_feature(dev, DRIVER_MODESET)) { struct drm_agp_mem *entry, *tempe; /* Remove AGP resources, but leave dev->agp intact until drv_cleanup is called. */ list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { if (entry->bound) drm_unbind_agp(entry->memory); drm_free_agp(entry->memory, entry->pages); - free(entry, DRM_MEM_AGPLISTS); + kfree(entry); } INIT_LIST_HEAD(&dev->agp->memory); if (dev->agp->acquired) drm_agp_release(dev); dev->agp->acquired = 0; dev->agp->enabled = 0; } if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && !drm_core_check_feature(dev, DRIVER_MODESET)) { drm_sg_cleanup(dev->sg); dev->sg = NULL; } #ifdef __linux__ /* Clear vma list (only built for debugging) */ list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) { list_del(&vma->head); kfree(vma); } #endif if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && !drm_core_check_feature(dev, DRIVER_MODESET)) drm_dma_takedown(dev); DRM_UNLOCK(dev); DRM_DEBUG("lastclose completed\n"); return 0; } #ifdef __linux__ /** File operations structure */ static const struct file_operations drm_stub_fops = { .owner = THIS_MODULE, .open = drm_stub_open, .llseek = noop_llseek, }; #endif static int __init drm_core_init(void) { sx_init(&drm_global_mutex, "drm_global_mutex"); drm_global_init(); #if DRM_LINUX linux_ioctl_register_handler(&drm_handler); #endif /* DRM_LINUX */ DRM_INFO("Initialized %s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); return 0; } static void __exit drm_core_exit(void) { #if DRM_LINUX linux_ioctl_unregister_handler(&drm_handler); #endif /* DRM_LINUX */ drm_global_release(); sx_destroy(&drm_global_mutex); } SYSINIT(drm_register, SI_SUB_KLD, SI_ORDER_MIDDLE, drm_core_init, NULL); SYSUNINIT(drm_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, drm_core_exit, NULL); /** * Copy and IOCTL return string to user space */ static int drm_copy_field(char *buf, size_t *buf_len, const char *value) { int len; /* don't overflow userbuf */ len = strlen(value); if (len > *buf_len) len = *buf_len; /* let userspace know exact length of driver value (which could be * larger than the userspace-supplied buffer) */ *buf_len = strlen(value); /* finally, try filling in the userbuf */ if (len && buf) if (copy_to_user(buf, value, len)) return -EFAULT; return 0; } /** * Get version information * * \param inode device inode. * \param filp file pointer. * \param cmd command. * \param arg user argument, pointing to a drm_version structure. * \return zero on success or negative number on failure. * * Fills in the version information in \p arg. */ int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_version *version = data; int err; version->version_major = dev->driver->major; version->version_minor = dev->driver->minor; version->version_patchlevel = dev->driver->patchlevel; err = drm_copy_field(version->name, &version->name_len, dev->driver->name); if (!err) err = drm_copy_field(version->date, &version->date_len, dev->driver->date); if (!err) err = drm_copy_field(version->desc, &version->desc_len, dev->driver->desc); return err; } /** * Called whenever a process performs an ioctl on /dev/drm. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg user argument. * \return zero on success or negative number on failure. * * Looks up the ioctl function in the ::ioctls table, checking for root * previleges if so required, and dispatches to the respective function. */ int drm_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int flags, DRM_STRUCTPROC *p) { struct drm_file *file_priv; struct drm_device *dev; struct drm_ioctl_desc *ioctl; drm_ioctl_t *func; unsigned int nr = DRM_IOCTL_NR(cmd); int retcode; dev = drm_get_device_from_kdev(kdev); retcode = devfs_get_cdevpriv((void **)&file_priv); if (retcode != 0) { DRM_ERROR("can't find authenticator\n"); return EINVAL; } retcode = -EINVAL; atomic_inc(&dev->ioctl_count); atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]); ++file_priv->ioctl_count; DRM_DEBUG("pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n", DRM_CURRENTPID, cmd, nr, (long)file_priv->minor->device, file_priv->authenticated); switch (cmd) { case FIONBIO: case FIOASYNC: atomic_dec(&dev->ioctl_count); return 0; case FIOSETOWN: atomic_dec(&dev->ioctl_count); return fsetown(*(int *)data, &file_priv->minor->buf_sigio); case FIOGETOWN: atomic_dec(&dev->ioctl_count); *(int *) data = fgetown(&file_priv->minor->buf_sigio); return 0; } if (IOCGROUP(cmd) != DRM_IOCTL_BASE) { atomic_dec(&dev->ioctl_count); DRM_DEBUG("Bad ioctl group 0x%x\n", (int)IOCGROUP(cmd)); return EINVAL; } if ((nr >= DRM_CORE_IOCTL_COUNT) && ((nr < DRM_COMMAND_BASE) || (nr >= DRM_COMMAND_END))) goto err_i1; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32) && (nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + *dev->driver->num_compat_ioctls) && (dev->driver->compat_ioctls[nr - DRM_COMMAND_BASE].func != NULL)) { ioctl = &dev->driver->compat_ioctls[nr - DRM_COMMAND_BASE]; } else #endif if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE]; } else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) { #ifdef COMPAT_FREEBSD32 /* * Called whenever a 32-bit process running under a 64-bit * kernel performs an ioctl on /dev/drm. */ if (SV_CURPROC_FLAG(SV_ILP32) && drm_compat_ioctls[nr].func != NULL) /* * Assume that ioctls without an explicit compat * routine will just work. This may not always be a * good assumption, but it's better than always * failing. */ ioctl = &drm_compat_ioctls[nr]; else #endif ioctl = &drm_ioctls[nr]; } else goto err_i1; /* Do not trust userspace, use our own definition */ func = ioctl->func; /* is there a local override? */ #ifdef FREEBSD_NOTYET #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32) && (nr == DRM_IOCTL_NR(DRM_IOCTL_DMA)) && dev->driver->dma_compat_ioctl) func = dev->driver->dma_compat_ioctl; else #endif #endif /* FREEBSD_NOTYET */ if ((nr == DRM_IOCTL_NR(DRM_IOCTL_DMA)) && dev->driver->dma_ioctl) func = dev->driver->dma_ioctl; if (!func) { DRM_DEBUG("no function\n"); retcode = -EINVAL; } else if (((ioctl->flags & DRM_ROOT_ONLY) && !DRM_SUSER(p)) || ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) || ((ioctl->flags & DRM_MASTER) && !file_priv->is_master) || (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL))) { retcode = -EACCES; } else { if (ioctl->flags & DRM_UNLOCKED) retcode = func(dev, data, file_priv); else { sx_xlock(&drm_global_mutex); retcode = func(dev, data, file_priv); sx_xunlock(&drm_global_mutex); } } err_i1: atomic_dec(&dev->ioctl_count); if (retcode == -ERESTARTSYS) { /* * FIXME: Find where in i915 ERESTARTSYS should be * converted to EINTR. */ DRM_DEBUG("ret = %d -> %d\n", retcode, -EINTR); retcode = -EINTR; } if (retcode) DRM_DEBUG("ret = %d\n", retcode); if (retcode != 0 && (drm_debug & DRM_DEBUGBITS_FAILED_IOCTL) != 0) { printf( "pid %d, cmd 0x%02lx, nr 0x%02x, dev 0x%lx, auth %d, res %d\n", DRM_CURRENTPID, cmd, nr, (long)file_priv->minor->device, file_priv->authenticated, -retcode); } return -retcode; } EXPORT_SYMBOL(drm_ioctl); struct drm_local_map *drm_getsarea(struct drm_device *dev) { struct drm_map_list *entry; list_for_each_entry(entry, &dev->maplist, head) { if (entry->map && entry->map->type == _DRM_SHM && (entry->map->flags & _DRM_CONTAINS_LOCK)) { return entry->map; } } return NULL; } EXPORT_SYMBOL(drm_getsarea); Index: head/sys/dev/drm2/drm_os_freebsd.c =================================================================== --- head/sys/dev/drm2/drm_os_freebsd.c (revision 312926) +++ head/sys/dev/drm2/drm_os_freebsd.c (revision 312927) @@ -1,498 +1,499 @@ #include __FBSDID("$FreeBSD$"); #include #include #include devclass_t drm_devclass; MALLOC_DEFINE(DRM_MEM_DMA, "drm_dma", "DRM DMA Data Structures"); MALLOC_DEFINE(DRM_MEM_SAREA, "drm_sarea", "DRM SAREA Data Structures"); MALLOC_DEFINE(DRM_MEM_DRIVER, "drm_driver", "DRM DRIVER Data Structures"); MALLOC_DEFINE(DRM_MEM_MAGIC, "drm_magic", "DRM MAGIC Data Structures"); MALLOC_DEFINE(DRM_MEM_MINOR, "drm_minor", "DRM MINOR Data Structures"); MALLOC_DEFINE(DRM_MEM_IOCTLS, "drm_ioctls", "DRM IOCTL Data Structures"); MALLOC_DEFINE(DRM_MEM_MAPS, "drm_maps", "DRM MAP Data Structures"); +MALLOC_DEFINE(DRM_MEM_BUFS, "drm_bufs", "DRM BUFFER Data Structures"); MALLOC_DEFINE(DRM_MEM_SEGS, "drm_segs", "DRM SEGMENTS Data Structures"); MALLOC_DEFINE(DRM_MEM_PAGES, "drm_pages", "DRM PAGES Data Structures"); MALLOC_DEFINE(DRM_MEM_FILES, "drm_files", "DRM FILE Data Structures"); MALLOC_DEFINE(DRM_MEM_QUEUES, "drm_queues", "DRM QUEUE Data Structures"); MALLOC_DEFINE(DRM_MEM_CMDS, "drm_cmds", "DRM COMMAND Data Structures"); MALLOC_DEFINE(DRM_MEM_MAPPINGS, "drm_mapping", "DRM MAPPING Data Structures"); MALLOC_DEFINE(DRM_MEM_BUFLISTS, "drm_buflists", "DRM BUFLISTS Data Structures"); -MALLOC_DEFINE(DRM_MEM_AGPLISTS, "drm_agplists", "DRM AGPLISTS Data Structures"); MALLOC_DEFINE(DRM_MEM_CTXBITMAP, "drm_ctxbitmap", "DRM CTXBITMAP Data Structures"); MALLOC_DEFINE(DRM_MEM_SGLISTS, "drm_sglists", "DRM SGLISTS Data Structures"); MALLOC_DEFINE(DRM_MEM_MM, "drm_sman", "DRM MEMORY MANAGER Data Structures"); MALLOC_DEFINE(DRM_MEM_HASHTAB, "drm_hashtab", "DRM HASHTABLE Data Structures"); MALLOC_DEFINE(DRM_MEM_KMS, "drm_kms", "DRM KMS Data Structures"); MALLOC_DEFINE(DRM_MEM_VBLANK, "drm_vblank", "DRM VBLANK Handling Data"); const char *fb_mode_option = NULL; #define NSEC_PER_USEC 1000L #define NSEC_PER_SEC 1000000000L int64_t timeval_to_ns(const struct timeval *tv) { return ((int64_t)tv->tv_sec * NSEC_PER_SEC) + tv->tv_usec * NSEC_PER_USEC; } struct timeval ns_to_timeval(const int64_t nsec) { struct timeval tv; long rem; if (nsec == 0) { tv.tv_sec = 0; tv.tv_usec = 0; return (tv); } tv.tv_sec = nsec / NSEC_PER_SEC; rem = nsec % NSEC_PER_SEC; if (rem < 0) { tv.tv_sec--; rem += NSEC_PER_SEC; } tv.tv_usec = rem / 1000; return (tv); } /* Copied from OFED. */ unsigned long drm_linux_timer_hz_mask; static void drm_linux_timer_init(void *arg) { /* * Compute an internal HZ value which can divide 2**32 to * avoid timer rounding problems when the tick value wraps * around 2**32: */ drm_linux_timer_hz_mask = 1; while (drm_linux_timer_hz_mask < (unsigned long)hz) drm_linux_timer_hz_mask *= 2; drm_linux_timer_hz_mask--; } SYSINIT(drm_linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, drm_linux_timer_init, NULL); static const drm_pci_id_list_t * drm_find_description(int vendor, int device, const drm_pci_id_list_t *idlist) { int i = 0; for (i = 0; idlist[i].vendor != 0; i++) { if ((idlist[i].vendor == vendor) && ((idlist[i].device == device) || (idlist[i].device == 0))) { return (&idlist[i]); } } return (NULL); } /* * drm_probe_helper: called by a driver at the end of its probe * method. */ int drm_probe_helper(device_t kdev, const drm_pci_id_list_t *idlist) { const drm_pci_id_list_t *id_entry; int vendor, device; vendor = pci_get_vendor(kdev); device = pci_get_device(kdev); if (pci_get_class(kdev) != PCIC_DISPLAY || (pci_get_subclass(kdev) != PCIS_DISPLAY_VGA && pci_get_subclass(kdev) != PCIS_DISPLAY_OTHER)) return (-ENXIO); id_entry = drm_find_description(vendor, device, idlist); if (id_entry != NULL) { if (device_get_desc(kdev) == NULL) { DRM_DEBUG("%s desc: %s\n", device_get_nameunit(kdev), id_entry->name); device_set_desc(kdev, id_entry->name); } return (0); } return (-ENXIO); } /* * drm_attach_helper: called by a driver at the end of its attach * method. */ int drm_attach_helper(device_t kdev, const drm_pci_id_list_t *idlist, struct drm_driver *driver) { struct drm_device *dev; int vendor, device; int ret; dev = device_get_softc(kdev); vendor = pci_get_vendor(kdev); device = pci_get_device(kdev); dev->id_entry = drm_find_description(vendor, device, idlist); ret = drm_get_pci_dev(kdev, dev, driver); return (ret); } int drm_generic_suspend(device_t kdev) { struct drm_device *dev; int error; DRM_DEBUG_KMS("Starting suspend\n"); dev = device_get_softc(kdev); if (dev->driver->suspend) { pm_message_t state; state.event = PM_EVENT_SUSPEND; error = -dev->driver->suspend(dev, state); if (error) goto out; } error = bus_generic_suspend(kdev); out: DRM_DEBUG_KMS("Finished suspend: %d\n", error); return error; } int drm_generic_resume(device_t kdev) { struct drm_device *dev; int error; DRM_DEBUG_KMS("Starting resume\n"); dev = device_get_softc(kdev); if (dev->driver->resume) { error = -dev->driver->resume(dev); if (error) goto out; } error = bus_generic_resume(kdev); out: DRM_DEBUG_KMS("Finished resume: %d\n", error); return error; } int drm_generic_detach(device_t kdev) { struct drm_device *dev; int i; dev = device_get_softc(kdev); drm_put_dev(dev); /* Clean up PCI resources allocated by drm_bufs.c. We're not really * worried about resource consumption while the DRM is inactive (between * lastclose and firstopen or unload) because these aren't actually * taking up KVA, just keeping the PCI resource allocated. */ for (i = 0; i < DRM_MAX_PCI_RESOURCE; i++) { if (dev->pcir[i] == NULL) continue; bus_release_resource(dev->dev, SYS_RES_MEMORY, dev->pcirid[i], dev->pcir[i]); dev->pcir[i] = NULL; } if (pci_disable_busmaster(dev->dev)) DRM_ERROR("Request to disable bus-master failed.\n"); return (0); } int drm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx, struct sysctl_oid *top) { struct sysctl_oid *oid; snprintf(dev->busid_str, sizeof(dev->busid_str), "pci:%04x:%02x:%02x.%d", dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func); oid = SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "busid", CTLFLAG_RD, dev->busid_str, 0, NULL); if (oid == NULL) return (-ENOMEM); dev->modesetting = (dev->driver->driver_features & DRIVER_MODESET) != 0; oid = SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "modesetting", CTLFLAG_RD, &dev->modesetting, 0, NULL); if (oid == NULL) return (-ENOMEM); return (0); } static int drm_device_find_capability(struct drm_device *dev, int cap) { return (pci_find_cap(dev->dev, cap, NULL) == 0); } int drm_pci_device_is_agp(struct drm_device *dev) { if (dev->driver->device_is_agp != NULL) { int ret; /* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely * AGP, 2 = fall back to PCI capability */ ret = (*dev->driver->device_is_agp)(dev); if (ret != DRM_MIGHT_BE_AGP) return ret; } return (drm_device_find_capability(dev, PCIY_AGP)); } int drm_pci_device_is_pcie(struct drm_device *dev) { return (drm_device_find_capability(dev, PCIY_EXPRESS)); } static bool dmi_found(const struct dmi_system_id *dsi) { char *hw_vendor, *hw_prod; int i, slot; bool res; hw_vendor = kern_getenv("smbios.planar.maker"); hw_prod = kern_getenv("smbios.planar.product"); res = true; for (i = 0; i < nitems(dsi->matches); i++) { slot = dsi->matches[i].slot; switch (slot) { case DMI_NONE: break; case DMI_SYS_VENDOR: case DMI_BOARD_VENDOR: if (hw_vendor != NULL && !strcmp(hw_vendor, dsi->matches[i].substr)) { break; } else { res = false; goto out; } case DMI_PRODUCT_NAME: case DMI_BOARD_NAME: if (hw_prod != NULL && !strcmp(hw_prod, dsi->matches[i].substr)) { break; } else { res = false; goto out; } default: res = false; goto out; } } out: freeenv(hw_vendor); freeenv(hw_prod); return (res); } bool dmi_check_system(const struct dmi_system_id *sysid) { const struct dmi_system_id *dsi; bool res; for (res = false, dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) { if (dmi_found(dsi)) { res = true; if (dsi->callback != NULL && dsi->callback(dsi)) break; } } return (res); } #if __OS_HAS_MTRR int drm_mtrr_add(unsigned long offset, unsigned long size, unsigned int flags) { int act; struct mem_range_desc mrdesc; mrdesc.mr_base = offset; mrdesc.mr_len = size; mrdesc.mr_flags = flags; act = MEMRANGE_SET_UPDATE; strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner)); return (-mem_range_attr_set(&mrdesc, &act)); } int drm_mtrr_del(int handle __unused, unsigned long offset, unsigned long size, unsigned int flags) { int act; struct mem_range_desc mrdesc; mrdesc.mr_base = offset; mrdesc.mr_len = size; mrdesc.mr_flags = flags; act = MEMRANGE_SET_REMOVE; strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner)); return (-mem_range_attr_set(&mrdesc, &act)); } #endif void drm_clflush_pages(vm_page_t *pages, unsigned long num_pages) { #if defined(__i386__) || defined(__amd64__) pmap_invalidate_cache_pages(pages, num_pages); #else DRM_ERROR("drm_clflush_pages not implemented on this architecture"); #endif } void drm_clflush_virt_range(char *addr, unsigned long length) { #if defined(__i386__) || defined(__amd64__) pmap_invalidate_cache_range((vm_offset_t)addr, (vm_offset_t)addr + length, TRUE); #else DRM_ERROR("drm_clflush_virt_range not implemented on this architecture"); #endif } void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize, char *linebuf, size_t linebuflen, bool ascii __unused) { int i, j, c; i = j = 0; while (i < len && j <= linebuflen) { c = ((const char *)buf)[i]; if (i != 0) { if (i % rowsize == 0) { /* Newline required. */ sprintf(linebuf + j, "\n"); ++j; } else if (i % groupsize == 0) { /* Space required. */ sprintf(linebuf + j, " "); ++j; } } if (j > linebuflen - 4) break; sprintf(linebuf + j, "%02X", c); j += 2; ++i; } if (j <= linebuflen) sprintf(linebuf + j, "\n"); } #if DRM_LINUX #include MODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1); #define LINUX_IOCTL_DRM_MIN 0x6400 #define LINUX_IOCTL_DRM_MAX 0x64ff static linux_ioctl_function_t drm_linux_ioctl; static struct linux_ioctl_handler drm_handler = {drm_linux_ioctl, LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX}; /* The bits for in/out are switched on Linux */ #define LINUX_IOC_IN IOC_OUT #define LINUX_IOC_OUT IOC_IN static int drm_linux_ioctl(DRM_STRUCTPROC *p, struct linux_ioctl_args* args) { int error; int cmd = args->cmd; args->cmd &= ~(LINUX_IOC_IN | LINUX_IOC_OUT); if (cmd & LINUX_IOC_IN) args->cmd |= IOC_IN; if (cmd & LINUX_IOC_OUT) args->cmd |= IOC_OUT; error = ioctl(p, (struct ioctl_args *)args); return error; } #endif /* DRM_LINUX */ static int drm_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: TUNABLE_INT_FETCH("drm.debug", &drm_debug); TUNABLE_INT_FETCH("drm.notyet", &drm_notyet); break; } return (0); } static moduledata_t drm_mod = { "drmn", drm_modevent, 0 }; DECLARE_MODULE(drmn, drm_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(drmn, 1); MODULE_DEPEND(drmn, agp, 1, 1, 1); MODULE_DEPEND(drmn, pci, 1, 1, 1); MODULE_DEPEND(drmn, mem, 1, 1, 1); +MODULE_DEPEND(drmn, linuxkpi, 1, 1, 1); MODULE_DEPEND(drmn, iicbus, 1, 1, 1); Index: head/sys/dev/drm2/drm_os_freebsd.h =================================================================== --- head/sys/dev/drm2/drm_os_freebsd.h (revision 312926) +++ head/sys/dev/drm2/drm_os_freebsd.h (revision 312927) @@ -1,703 +1,703 @@ /** * \file drm_os_freebsd.h * OS abstraction macros. */ #include __FBSDID("$FreeBSD$"); #ifndef _DRM_OS_FREEBSD_H_ #define _DRM_OS_FREEBSD_H_ #include #include #if _BYTE_ORDER == _BIG_ENDIAN #define __BIG_ENDIAN 4321 #else #define __LITTLE_ENDIAN 1234 #endif #ifdef __LP64__ #define BITS_PER_LONG 64 #else #define BITS_PER_LONG 32 #endif #ifndef __user #define __user #endif #ifndef __iomem #define __iomem #endif #ifndef __always_unused #define __always_unused #endif #ifndef __must_check #define __must_check #endif #ifndef __force #define __force #endif #ifndef uninitialized_var #define uninitialized_var(x) x #endif #define cpu_to_le16(x) htole16(x) #define le16_to_cpu(x) le16toh(x) #define cpu_to_le32(x) htole32(x) #define le32_to_cpu(x) le32toh(x) #define cpu_to_be16(x) htobe16(x) #define be16_to_cpu(x) be16toh(x) #define cpu_to_be32(x) htobe32(x) #define be32_to_cpu(x) be32toh(x) #define be32_to_cpup(x) be32toh(*x) typedef vm_paddr_t dma_addr_t; typedef vm_paddr_t resource_size_t; #define wait_queue_head_t atomic_t typedef uint64_t u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef int64_t s64; typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; typedef uint16_t __le16; typedef uint32_t __le32; typedef uint64_t __le64; typedef uint16_t __be16; typedef uint32_t __be32; typedef uint64_t __be64; #define DRM_IRQ_ARGS void *arg typedef void irqreturn_t; #define IRQ_HANDLED /* nothing */ #define IRQ_NONE /* nothing */ #define __init #define __exit #define BUILD_BUG_ON(x) CTASSERT(!(x)) #define BUILD_BUG_ON_NOT_POWER_OF_2(x) #ifndef WARN #define WARN(condition, format, ...) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ DRM_ERROR(format, ##__VA_ARGS__); \ unlikely(__ret_warn_on); \ }) #endif #define WARN_ONCE(condition, format, ...) \ WARN(condition, format, ##__VA_ARGS__) #define WARN_ON(cond) WARN(cond, "WARN ON: " #cond) #define WARN_ON_SMP(cond) WARN_ON(cond) #define BUG() panic("BUG") #define BUG_ON(cond) KASSERT(!(cond), ("BUG ON: " #cond " -> 0x%jx", (uintmax_t)(cond))) #define unlikely(x) __builtin_expect(!!(x), 0) #define likely(x) __builtin_expect(!!(x), 1) #define container_of(ptr, type, member) ({ \ __typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define KHZ2PICOS(a) (1000000000UL/(a)) #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define HZ hz #define DRM_HZ hz #define DRM_CURRENTPID curthread->td_proc->p_pid #define DRM_SUSER(p) (priv_check(p, PRIV_DRIVER) == 0) #define udelay(usecs) DELAY(usecs) #define mdelay(msecs) do { int loops = (msecs); \ while (loops--) DELAY(1000); \ } while (0) #define DRM_UDELAY(udelay) DELAY(udelay) #define drm_msleep(x, msg) pause((msg), ((int64_t)(x)) * hz / 1000) #define DRM_MSLEEP(msecs) drm_msleep((msecs), "drm_msleep") #define get_seconds() time_second #define ioread8(addr) *(volatile uint8_t *)((char *)addr) #define ioread16(addr) *(volatile uint16_t *)((char *)addr) #define ioread32(addr) *(volatile uint32_t *)((char *)addr) #define iowrite8(data, addr) *(volatile uint8_t *)((char *)addr) = data; #define iowrite16(data, addr) *(volatile uint16_t *)((char *)addr) = data; #define iowrite32(data, addr) *(volatile uint32_t *)((char *)addr) = data; #define DRM_READ8(map, offset) \ *(volatile u_int8_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset)) #define DRM_READ16(map, offset) \ le16toh(*(volatile u_int16_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset))) #define DRM_READ32(map, offset) \ le32toh(*(volatile u_int32_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset))) #define DRM_READ64(map, offset) \ le64toh(*(volatile u_int64_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset))) #define DRM_WRITE8(map, offset, val) \ *(volatile u_int8_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset)) = val #define DRM_WRITE16(map, offset, val) \ *(volatile u_int16_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset)) = htole16(val) #define DRM_WRITE32(map, offset, val) \ *(volatile u_int32_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset)) = htole32(val) #define DRM_WRITE64(map, offset, val) \ *(volatile u_int64_t *)(((vm_offset_t)(map)->handle) + \ (vm_offset_t)(offset)) = htole64(val) /* DRM_READMEMORYBARRIER() prevents reordering of reads. * DRM_WRITEMEMORYBARRIER() prevents reordering of writes. * DRM_MEMORYBARRIER() prevents reordering of reads and writes. */ #define DRM_READMEMORYBARRIER() rmb() #define DRM_WRITEMEMORYBARRIER() wmb() #define DRM_MEMORYBARRIER() mb() #define smp_rmb() rmb() #define smp_wmb() wmb() #define smp_mb__before_atomic_inc() mb() #define smp_mb__after_atomic_inc() mb() #define barrier() __compiler_membar() #define do_div(a, b) ((a) /= (b)) #define div64_u64(a, b) ((a) / (b)) #define lower_32_bits(n) ((u32)(n)) #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) #define __set_bit(n, s) set_bit((n), (s)) #define __clear_bit(n, s) clear_bit((n), (s)) #define min_t(type, x, y) ({ \ type __min1 = (x); \ type __min2 = (y); \ __min1 < __min2 ? __min1 : __min2; }) #define max_t(type, x, y) ({ \ type __max1 = (x); \ type __max2 = (y); \ __max1 > __max2 ? __max1 : __max2; }) #define memset_io(a, b, c) memset((a), (b), (c)) #define memcpy_fromio(a, b, c) memcpy((a), (b), (c)) #define memcpy_toio(a, b, c) memcpy((a), (b), (c)) #define VERIFY_READ VM_PROT_READ #define VERIFY_WRITE VM_PROT_WRITE #define access_ok(prot, p, l) useracc((p), (l), (prot)) /* XXXKIB what is the right code for the FreeBSD ? */ /* kib@ used ENXIO here -- dumbbell@ */ #define EREMOTEIO EIO #define ERESTARTSYS 512 /* Same value as Linux. */ #define KTR_DRM KTR_DEV #define KTR_DRM_REG KTR_SPARE3 #define DRM_AGP_KERN struct agp_info #define DRM_AGP_MEM void #define PCI_VENDOR_ID_APPLE 0x106b #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_VENDOR_ID_ATI 0x1002 #define PCI_VENDOR_ID_DELL 0x1028 #define PCI_VENDOR_ID_HP 0x103c #define PCI_VENDOR_ID_IBM 0x1014 #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_VENDOR_ID_SERVERWORKS 0x1166 #define PCI_VENDOR_ID_SONY 0x104d #define PCI_VENDOR_ID_VIA 0x1106 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define DIV_ROUND_CLOSEST(n,d) (((n) + (d) / 2) / (d)) #define div_u64(n, d) ((n) / (d)) #define hweight32(i) bitcount32(i) static inline unsigned long roundup_pow_of_two(unsigned long x) { return (1UL << flsl(x - 1)); } /** * ror32 - rotate a 32-bit value right * @word: value to rotate * @shift: bits to roll * * Source: include/linux/bitops.h */ static inline uint32_t ror32(uint32_t word, unsigned int shift) { return (word >> shift) | (word << (32 - shift)); } #define IS_ALIGNED(x, y) (((x) & ((y) - 1)) == 0) #define round_down(x, y) rounddown2((x), (y)) #define round_up(x, y) roundup2((x), (y)) #define get_unaligned(ptr) \ ({ __typeof__(*(ptr)) __tmp; \ memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; }) #if _BYTE_ORDER == _LITTLE_ENDIAN /* Taken from linux/include/linux/unaligned/le_struct.h. */ struct __una_u32 { u32 x; } __packed; static inline u32 __get_unaligned_cpu32(const void *p) { const struct __una_u32 *ptr = (const struct __una_u32 *)p; return (ptr->x); } static inline u32 get_unaligned_le32(const void *p) { return (__get_unaligned_cpu32((const u8 *)p)); } #else /* Taken from linux/include/linux/unaligned/le_byteshift.h. */ static inline u32 __get_unaligned_le32(const u8 *p) { return (p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24); } static inline u32 get_unaligned_le32(const void *p) { return (__get_unaligned_le32((const u8 *)p)); } #endif static inline unsigned long ilog2(unsigned long x) { return (flsl(x) - 1); } static inline int64_t abs64(int64_t x) { return (x < 0 ? -x : x); } int64_t timeval_to_ns(const struct timeval *tv); struct timeval ns_to_timeval(const int64_t nsec); #define PAGE_ALIGN(addr) round_page(addr) #define page_to_phys(x) VM_PAGE_TO_PHYS(x) #define offset_in_page(x) ((x) & PAGE_MASK) #define drm_get_device_from_kdev(_kdev) (((struct drm_minor *)(_kdev)->si_drv1)->dev) #define DRM_IOC_VOID IOC_VOID #define DRM_IOC_READ IOC_OUT #define DRM_IOC_WRITE IOC_IN #define DRM_IOC_READWRITE IOC_INOUT #define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) static inline long __copy_to_user(void __user *to, const void *from, unsigned long n) { return (copyout(from, to, n) != 0 ? n : 0); } #define copy_to_user(to, from, n) __copy_to_user((to), (from), (n)) static inline int __put_user(size_t size, void *ptr, void *x) { size = copy_to_user(ptr, x, size); return (size ? -EFAULT : size); } #define put_user(x, ptr) __put_user(sizeof(*ptr), (ptr), &(x)) static inline unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n) { return ((copyin(__DECONST(void *, from), to, n) != 0 ? n : 0)); } #define copy_from_user(to, from, n) __copy_from_user((to), (from), (n)) static inline int __get_user(size_t size, const void *ptr, void *x) { size = copy_from_user(x, ptr, size); return (size ? -EFAULT : size); } #define get_user(x, ptr) __get_user(sizeof(*ptr), (ptr), &(x)) static inline int __copy_to_user_inatomic(void __user *to, const void *from, unsigned n) { return (copyout_nofault(from, to, n) != 0 ? n : 0); } #define __copy_to_user_inatomic_nocache(to, from, n) \ __copy_to_user_inatomic((to), (from), (n)) static inline unsigned long __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) { /* * XXXKIB. Equivalent Linux function is implemented using * MOVNTI for aligned moves. For unaligned head and tail, * normal move is performed. As such, it is not incorrect, if * only somewhat slower, to use normal copyin. All uses * except shmem_pwrite_fast() have the destination mapped WC. */ return ((copyin_nofault(__DECONST(void *, from), to, n) != 0 ? n : 0)); } #define __copy_from_user_inatomic_nocache(to, from, n) \ __copy_from_user_inatomic((to), (from), (n)) static inline int fault_in_multipages_readable(const char __user *uaddr, int size) { char c; int ret = 0; const char __user *end = uaddr + size - 1; if (unlikely(size == 0)) return ret; while (uaddr <= end) { ret = -copyin(uaddr, &c, 1); if (ret != 0) return -EFAULT; uaddr += PAGE_SIZE; } /* Check whether the range spilled into the next page. */ if (((unsigned long)uaddr & ~PAGE_MASK) == ((unsigned long)end & ~PAGE_MASK)) { ret = -copyin(end, &c, 1); } return ret; } static inline int fault_in_multipages_writeable(char __user *uaddr, int size) { int ret = 0; char __user *end = uaddr + size - 1; if (unlikely(size == 0)) return ret; /* * Writing zeroes into userspace here is OK, because we know that if * the zero gets there, we'll be overwriting it. */ while (uaddr <= end) { ret = subyte(uaddr, 0); if (ret != 0) return -EFAULT; uaddr += PAGE_SIZE; } /* Check whether the range spilled into the next page. */ if (((unsigned long)uaddr & ~PAGE_MASK) == ((unsigned long)end & ~PAGE_MASK)) ret = subyte(end, 0); return ret; } enum __drm_capabilities { CAP_SYS_ADMIN }; static inline bool capable(enum __drm_capabilities cap) { switch (cap) { case CAP_SYS_ADMIN: return DRM_SUSER(curthread); default: panic("%s: unhandled capability: %0x", __func__, cap); return (false); } } #define to_user_ptr(x) ((void *)(uintptr_t)(x)) #define sigemptyset(set) SIGEMPTYSET(set) #define sigaddset(set, sig) SIGADDSET(set, sig) #define DRM_LOCK(dev) sx_xlock(&(dev)->dev_struct_lock) #define DRM_UNLOCK(dev) sx_xunlock(&(dev)->dev_struct_lock) extern unsigned long drm_linux_timer_hz_mask; #define jiffies ticks #define jiffies_to_msecs(x) (((int64_t)(x)) * 1000 / hz) #define msecs_to_jiffies(x) (((int64_t)(x)) * hz / 1000) #define timespec_to_jiffies(x) (((x)->tv_sec * 1000000 + (x)->tv_nsec) * hz / 1000000) #define time_after(a,b) ((long)(b) - (long)(a) < 0) #define time_after_eq(a,b) ((long)(b) - (long)(a) <= 0) #define round_jiffies(j) ((unsigned long)(((j) + drm_linux_timer_hz_mask) & ~drm_linux_timer_hz_mask)) #define round_jiffies_up(j) round_jiffies(j) /* TODO */ #define round_jiffies_up_relative(j) round_jiffies_up(j) /* TODO */ #define getrawmonotonic(ts) getnanouptime(ts) #define wake_up(queue) wakeup_one((void *)queue) #define wake_up_interruptible(queue) wakeup_one((void *)queue) #define wake_up_all(queue) wakeup((void *)queue) #define wake_up_interruptible_all(queue) wakeup((void *)queue) struct completion { unsigned int done; struct mtx lock; }; #define INIT_COMPLETION(c) ((c).done = 0); static inline void init_completion(struct completion *c) { mtx_init(&c->lock, "drmcompl", NULL, MTX_DEF); c->done = 0; } static inline void free_completion(struct completion *c) { mtx_destroy(&c->lock); } static inline void complete_all(struct completion *c) { mtx_lock(&c->lock); c->done++; mtx_unlock(&c->lock); wakeup(c); } static inline long wait_for_completion_interruptible_timeout(struct completion *c, unsigned long timeout) { unsigned long start_jiffies, elapsed_jiffies; bool timeout_expired = false, awakened = false; long ret = timeout; start_jiffies = ticks; mtx_lock(&c->lock); while (c->done == 0 && !timeout_expired) { ret = -msleep(c, &c->lock, PCATCH, "drmwco", timeout); switch(ret) { case -EWOULDBLOCK: timeout_expired = true; ret = 0; break; case -EINTR: case -ERESTART: ret = -ERESTARTSYS; break; case 0: awakened = true; break; } } mtx_unlock(&c->lock); if (awakened) { elapsed_jiffies = ticks - start_jiffies; ret = timeout > elapsed_jiffies ? timeout - elapsed_jiffies : 1; } return (ret); } MALLOC_DECLARE(DRM_MEM_DMA); MALLOC_DECLARE(DRM_MEM_SAREA); MALLOC_DECLARE(DRM_MEM_DRIVER); MALLOC_DECLARE(DRM_MEM_MAGIC); MALLOC_DECLARE(DRM_MEM_MINOR); MALLOC_DECLARE(DRM_MEM_IOCTLS); MALLOC_DECLARE(DRM_MEM_MAPS); +MALLOC_DECLARE(DRM_MEM_BUFS); MALLOC_DECLARE(DRM_MEM_SEGS); MALLOC_DECLARE(DRM_MEM_PAGES); MALLOC_DECLARE(DRM_MEM_FILES); MALLOC_DECLARE(DRM_MEM_QUEUES); MALLOC_DECLARE(DRM_MEM_CMDS); MALLOC_DECLARE(DRM_MEM_MAPPINGS); MALLOC_DECLARE(DRM_MEM_BUFLISTS); -MALLOC_DECLARE(DRM_MEM_AGPLISTS); MALLOC_DECLARE(DRM_MEM_CTXBITMAP); MALLOC_DECLARE(DRM_MEM_SGLISTS); MALLOC_DECLARE(DRM_MEM_MM); MALLOC_DECLARE(DRM_MEM_HASHTAB); MALLOC_DECLARE(DRM_MEM_KMS); MALLOC_DECLARE(DRM_MEM_VBLANK); #define simple_strtol(a, b, c) strtol((a), (b), (c)) typedef struct drm_pci_id_list { int vendor; int device; long driver_private; char *name; } drm_pci_id_list_t; #ifdef __i386__ #define CONFIG_X86 1 #endif #ifdef __amd64__ #define CONFIG_X86 1 #define CONFIG_X86_64 1 #endif #ifdef __ia64__ #define CONFIG_IA64 1 #endif #if defined(__i386__) || defined(__amd64__) #define CONFIG_ACPI #define CONFIG_DRM_I915_KMS #undef CONFIG_INTEL_IOMMU #endif #ifdef COMPAT_FREEBSD32 #define CONFIG_COMPAT #endif #ifndef __arm__ #define CONFIG_AGP 1 #define CONFIG_MTRR 1 #endif #define CONFIG_FB 1 extern const char *fb_mode_option; #undef CONFIG_DEBUG_FS #undef CONFIG_VGA_CONSOLE #define EXPORT_SYMBOL(x) #define EXPORT_SYMBOL_GPL(x) #define MODULE_AUTHOR(author) #define MODULE_DESCRIPTION(desc) #define MODULE_LICENSE(license) #define MODULE_PARM_DESC(name, desc) #define MODULE_DEVICE_TABLE(name, list) #define module_param_named(name, var, type, perm) #define printk printf #define pr_err DRM_ERROR #define pr_warn DRM_WARNING #define pr_warn_once DRM_WARNING #define KERN_DEBUG "" /* I2C compatibility. */ #define I2C_M_RD IIC_M_RD #define I2C_M_WR IIC_M_WR #define I2C_M_NOSTART IIC_M_NOSTART struct fb_info * framebuffer_alloc(void); void framebuffer_release(struct fb_info *info); #define console_lock() #define console_unlock() #define console_trylock() true #define PM_EVENT_SUSPEND 0x0002 #define PM_EVENT_QUIESCE 0x0008 #define PM_EVENT_PRETHAW PM_EVENT_QUIESCE typedef struct pm_message { int event; } pm_message_t; static inline int pci_read_config_byte(device_t kdev, int where, u8 *val) { *val = (u8)pci_read_config(kdev, where, 1); return (0); } static inline int pci_write_config_byte(device_t kdev, int where, u8 val) { pci_write_config(kdev, where, val, 1); return (0); } static inline int pci_read_config_word(device_t kdev, int where, uint16_t *val) { *val = (uint16_t)pci_read_config(kdev, where, 2); return (0); } static inline int pci_write_config_word(device_t kdev, int where, uint16_t val) { pci_write_config(kdev, where, val, 2); return (0); } static inline int pci_read_config_dword(device_t kdev, int where, uint32_t *val) { *val = (uint32_t)pci_read_config(kdev, where, 4); return (0); } static inline int pci_write_config_dword(device_t kdev, int where, uint32_t val) { pci_write_config(kdev, where, val, 4); return (0); } static inline void on_each_cpu(void callback(void *data), void *data, int wait) { smp_rendezvous(NULL, callback, NULL, data); } void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize, char *linebuf, size_t linebuflen, bool ascii); #define KIB_NOTYET() \ do { \ if (drm_debug && drm_notyet) \ printf("NOTYET: %s at %s:%d\n", __func__, __FILE__, __LINE__); \ } while (0) #endif /* _DRM_OS_FREEBSD_H_ */ Index: head/sys/dev/drm2/drm_stub.c =================================================================== --- head/sys/dev/drm2/drm_stub.c (revision 312926) +++ head/sys/dev/drm2/drm_stub.c (revision 312927) @@ -1,501 +1,502 @@ /** * \file drm_stub.h * Stub support * * \author Rickard E. (Rik) Faith */ /* * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org * * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include +#include #ifdef DRM_DEBUG_DEFAULT_ON unsigned int drm_debug = (DRM_DEBUGBITS_DEBUG | DRM_DEBUGBITS_KMS | DRM_DEBUGBITS_FAILED_IOCTL); #else unsigned int drm_debug = 0; /* 1 to enable debug output */ #endif EXPORT_SYMBOL(drm_debug); unsigned int drm_notyet = 0; unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ EXPORT_SYMBOL(drm_vblank_offdelay); unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ EXPORT_SYMBOL(drm_timestamp_precision); /* * Default to use monotonic timestamps for wait-for-vblank and page-flip * complete events. */ unsigned int drm_timestamp_monotonic = 1; MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); static struct cdevsw drm_cdevsw = { .d_version = D_VERSION, .d_open = drm_open, .d_read = drm_read, .d_ioctl = drm_ioctl, .d_poll = drm_poll, .d_mmap_single = drm_mmap_single, .d_name = "drm", .d_flags = D_TRACKCLOSE }; static int drm_minor_get_id(struct drm_device *dev, int type) { int new_id; new_id = device_get_unit(dev->dev); if (new_id >= 64) return -EINVAL; if (type == DRM_MINOR_CONTROL) { new_id += 64; } else if (type == DRM_MINOR_RENDER) { new_id += 128; } return new_id; } struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; master = malloc(sizeof(*master), DRM_MEM_KMS, M_NOWAIT | M_ZERO); if (!master) return NULL; refcount_init(&master->refcount, 1); mtx_init(&master->lock.spinlock, "drm_master__lock__spinlock", NULL, MTX_DEF); DRM_INIT_WAITQUEUE(&master->lock.lock_queue); drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER); INIT_LIST_HEAD(&master->magicfree); master->minor = minor; list_add_tail(&master->head, &minor->master_list); return master; } struct drm_master *drm_master_get(struct drm_master *master) { refcount_acquire(&master->refcount); return master; } EXPORT_SYMBOL(drm_master_get); static void drm_master_destroy(struct drm_master *master) { struct drm_magic_entry *pt, *next; struct drm_device *dev = master->minor->dev; struct drm_map_list *r_list, *list_temp; list_del(&master->head); if (dev->driver->master_destroy) dev->driver->master_destroy(dev, master); list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) { if (r_list->master == master) { drm_rmmap_locked(dev, r_list->map); r_list = NULL; } } if (master->unique) { free(master->unique, DRM_MEM_DRIVER); master->unique = NULL; master->unique_len = 0; } list_for_each_entry_safe(pt, next, &master->magicfree, head) { list_del(&pt->head); drm_ht_remove_item(&master->magiclist, &pt->hash_item); free(pt, DRM_MEM_MAGIC); } drm_ht_remove(&master->magiclist); free(master, DRM_MEM_KMS); } void drm_master_put(struct drm_master **master) { if (refcount_release(&(*master)->refcount)) drm_master_destroy(*master); *master = NULL; } EXPORT_SYMBOL(drm_master_put); int drm_setmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { int ret; if (file_priv->is_master) return 0; if (file_priv->minor->master && file_priv->minor->master != file_priv->master) return -EINVAL; if (!file_priv->master) return -EINVAL; if (file_priv->minor->master) return -EINVAL; DRM_LOCK(dev); file_priv->minor->master = drm_master_get(file_priv->master); file_priv->is_master = 1; if (dev->driver->master_set) { ret = dev->driver->master_set(dev, file_priv, false); if (unlikely(ret != 0)) { file_priv->is_master = 0; drm_master_put(&file_priv->minor->master); } } DRM_UNLOCK(dev); return 0; } int drm_dropmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { if (!file_priv->is_master) return -EINVAL; if (!file_priv->minor->master) return -EINVAL; DRM_LOCK(dev); if (dev->driver->master_drop) dev->driver->master_drop(dev, file_priv, false); drm_master_put(&file_priv->minor->master); file_priv->is_master = 0; DRM_UNLOCK(dev); return 0; } int drm_fill_in_dev(struct drm_device *dev, struct drm_driver *driver) { int retcode, i; INIT_LIST_HEAD(&dev->filelist); INIT_LIST_HEAD(&dev->ctxlist); INIT_LIST_HEAD(&dev->maplist); INIT_LIST_HEAD(&dev->vblank_event_list); mtx_init(&dev->irq_lock, "drmirq", NULL, MTX_DEF); mtx_init(&dev->count_lock, "drmcount", NULL, MTX_DEF); mtx_init(&dev->event_lock, "drmev", NULL, MTX_DEF); sx_init(&dev->dev_struct_lock, "drmslk"); mtx_init(&dev->ctxlist_mutex, "drmctxlist", NULL, MTX_DEF); mtx_init(&dev->pcir_lock, "drmpcir", NULL, MTX_DEF); if (drm_ht_create(&dev->map_hash, 12)) { return -ENOMEM; } /* the DRM has 6 basic counters */ dev->counters = 6; dev->types[0] = _DRM_STAT_LOCK; dev->types[1] = _DRM_STAT_OPENS; dev->types[2] = _DRM_STAT_CLOSES; dev->types[3] = _DRM_STAT_IOCTLS; dev->types[4] = _DRM_STAT_LOCKS; dev->types[5] = _DRM_STAT_UNLOCKS; /* * FIXME Linux<->FreeBSD: this is done in drm_setup() on Linux. */ for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); dev->driver = driver; retcode = drm_pci_agp_init(dev); if (retcode) goto error_out_unreg; retcode = drm_ctxbitmap_init(dev); if (retcode) { DRM_ERROR("Cannot allocate memory for context bitmap.\n"); goto error_out_unreg; } if (driver->driver_features & DRIVER_GEM) { retcode = drm_gem_init(dev); if (retcode) { DRM_ERROR("Cannot initialize graphics execution " "manager (GEM)\n"); goto error_out_unreg; } } retcode = drm_sysctl_init(dev); if (retcode != 0) { DRM_ERROR("Failed to create hw.dri sysctl entry: %d\n", retcode); } return 0; error_out_unreg: drm_cancel_fill_in_dev(dev); return retcode; } EXPORT_SYMBOL(drm_fill_in_dev); void drm_cancel_fill_in_dev(struct drm_device *dev) { struct drm_driver *driver; driver = dev->driver; drm_sysctl_cleanup(dev); if (driver->driver_features & DRIVER_GEM) drm_gem_destroy(dev); drm_ctxbitmap_cleanup(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp && dev->agp->agp_mtrr >= 0) { int retval; retval = drm_mtrr_del(dev->agp->agp_mtrr, dev->agp->agp_info.ai_aperture_base, dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC); DRM_DEBUG("mtrr_del=%d\n", retval); } - free(dev->agp, DRM_MEM_AGPLISTS); + kfree(dev->agp); dev->agp = NULL; drm_ht_remove(&dev->map_hash); mtx_destroy(&dev->irq_lock); mtx_destroy(&dev->count_lock); mtx_destroy(&dev->event_lock); sx_destroy(&dev->dev_struct_lock); mtx_destroy(&dev->ctxlist_mutex); mtx_destroy(&dev->pcir_lock); } /** * Get a secondary minor number. * * \param dev device data structure * \param sec-minor structure to hold the assigned minor * \return negative number on failure. * * Search an empty entry and initialize it to the given parameters, and * create the proc init entry via proc_init(). This routines assigns * minor numbers to secondary heads of multi-headed cards */ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) { struct drm_minor *new_minor; int ret; int minor_id; const char *minor_devname; DRM_DEBUG("\n"); minor_id = drm_minor_get_id(dev, type); if (minor_id < 0) return minor_id; new_minor = malloc(sizeof(struct drm_minor), DRM_MEM_MINOR, M_NOWAIT | M_ZERO); if (!new_minor) { ret = -ENOMEM; goto err_idr; } new_minor->type = type; new_minor->dev = dev; new_minor->index = minor_id; INIT_LIST_HEAD(&new_minor->master_list); new_minor->buf_sigio = NULL; switch (type) { case DRM_MINOR_CONTROL: minor_devname = "dri/controlD%d"; break; case DRM_MINOR_RENDER: minor_devname = "dri/renderD%d"; break; default: minor_devname = "dri/card%d"; break; } ret = make_dev_p(MAKEDEV_WAITOK | MAKEDEV_CHECKNAME, &new_minor->device, &drm_cdevsw, 0, DRM_DEV_UID, DRM_DEV_GID, DRM_DEV_MODE, minor_devname, minor_id); if (ret) { DRM_ERROR("Failed to create cdev: %d\n", ret); goto err_mem; } new_minor->device->si_drv1 = new_minor; *minor = new_minor; DRM_DEBUG("new minor assigned %d\n", minor_id); return 0; err_mem: free(new_minor, DRM_MEM_MINOR); err_idr: *minor = NULL; return ret; } EXPORT_SYMBOL(drm_get_minor); /** * Put a secondary minor number. * * \param sec_minor - structure to be released * \return always zero * * Cleans up the proc resources. Not legal for this to be the * last minor released. * */ int drm_put_minor(struct drm_minor **minor_p) { struct drm_minor *minor = *minor_p; DRM_DEBUG("release secondary minor %d\n", minor->index); funsetown(&minor->buf_sigio); destroy_dev(minor->device); free(minor, DRM_MEM_MINOR); *minor_p = NULL; return 0; } EXPORT_SYMBOL(drm_put_minor); /** * Called via drm_exit() at module unload time or when pci device is * unplugged. * * Cleans up all DRM device, calling drm_lastclose(). * */ void drm_put_dev(struct drm_device *dev) { struct drm_driver *driver; struct drm_map_list *r_list, *list_temp; DRM_DEBUG("\n"); if (!dev) { DRM_ERROR("cleanup called no dev\n"); return; } driver = dev->driver; drm_lastclose(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp && dev->agp->agp_mtrr >= 0) { int retval; retval = drm_mtrr_del(dev->agp->agp_mtrr, dev->agp->agp_info.ai_aperture_base, dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC); DRM_DEBUG("mtrr_del=%d\n", retval); } if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_mode_group_free(&dev->primary->mode_group); if (dev->driver->unload) dev->driver->unload(dev); drm_sysctl_cleanup(dev); if (drm_core_has_AGP(dev) && dev->agp) { - free(dev->agp, DRM_MEM_AGPLISTS); + kfree(dev->agp); dev->agp = NULL; } drm_vblank_cleanup(dev); list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_rmmap(dev, r_list->map); drm_ht_remove(&dev->map_hash); drm_ctxbitmap_cleanup(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); if (driver->driver_features & DRIVER_GEM) drm_gem_destroy(dev); drm_put_minor(&dev->primary); mtx_destroy(&dev->irq_lock); mtx_destroy(&dev->count_lock); mtx_destroy(&dev->event_lock); sx_destroy(&dev->dev_struct_lock); mtx_destroy(&dev->ctxlist_mutex); mtx_destroy(&dev->pcir_lock); #ifdef FREEBSD_NOTYET list_del(&dev->driver_item); #endif /* FREEBSD_NOTYET */ } EXPORT_SYMBOL(drm_put_dev); Index: head/sys/fs/nfsserver/nfs_nfsdstate.c =================================================================== --- head/sys/fs/nfsserver/nfs_nfsdstate.c (revision 312926) +++ head/sys/fs/nfsserver/nfs_nfsdstate.c (revision 312927) @@ -1,6122 +1,6122 @@ /*- * Copyright (c) 2009 Rick Macklem, University of Guelph * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #ifndef APPLEKEXT #include struct nfsrv_stablefirst nfsrv_stablefirst; int nfsrv_issuedelegs = 0; int nfsrv_dolocallocks = 0; struct nfsv4lock nfsv4rootfs_lock; extern int newnfs_numnfsd; extern struct nfsstatsv1 nfsstatsv1; extern int nfsrv_lease; extern struct timeval nfsboottime; extern u_int32_t newnfs_true, newnfs_false; NFSV4ROOTLOCKMUTEX; NFSSTATESPINLOCK; SYSCTL_DECL(_vfs_nfsd); int nfsrv_statehashsize = NFSSTATEHASHSIZE; SYSCTL_INT(_vfs_nfsd, OID_AUTO, statehashsize, CTLFLAG_RDTUN, &nfsrv_statehashsize, 0, "Size of state hash table set via loader.conf"); int nfsrv_clienthashsize = NFSCLIENTHASHSIZE; SYSCTL_INT(_vfs_nfsd, OID_AUTO, clienthashsize, CTLFLAG_RDTUN, &nfsrv_clienthashsize, 0, "Size of client hash table set via loader.conf"); int nfsrv_lockhashsize = NFSLOCKHASHSIZE; SYSCTL_INT(_vfs_nfsd, OID_AUTO, fhhashsize, CTLFLAG_RDTUN, &nfsrv_lockhashsize, 0, "Size of file handle hash table set via loader.conf"); int nfsrv_sessionhashsize = NFSSESSIONHASHSIZE; SYSCTL_INT(_vfs_nfsd, OID_AUTO, sessionhashsize, CTLFLAG_RDTUN, &nfsrv_sessionhashsize, 0, "Size of session hash table set via loader.conf"); static int nfsrv_v4statelimit = NFSRV_V4STATELIMIT; SYSCTL_INT(_vfs_nfsd, OID_AUTO, v4statelimit, CTLFLAG_RWTUN, &nfsrv_v4statelimit, 0, "High water limit for NFSv4 opens+locks+delegations"); static int nfsrv_writedelegifpos = 0; SYSCTL_INT(_vfs_nfsd, OID_AUTO, writedelegifpos, CTLFLAG_RW, &nfsrv_writedelegifpos, 0, "Issue a write delegation for read opens if possible"); /* * Hash lists for nfs V4. */ struct nfsclienthashhead *nfsclienthash; struct nfslockhashhead *nfslockhash; struct nfssessionhash *nfssessionhash; #endif /* !APPLEKEXT */ static u_int32_t nfsrv_openpluslock = 0, nfsrv_delegatecnt = 0; static time_t nfsrvboottime; static int nfsrv_returnoldstateid = 0, nfsrv_clients = 0; static int nfsrv_clienthighwater = NFSRV_CLIENTHIGHWATER; static int nfsrv_nogsscallback = 0; /* local functions */ static void nfsrv_dumpaclient(struct nfsclient *clp, struct nfsd_dumpclients *dumpp); static void nfsrv_freeopenowner(struct nfsstate *stp, int cansleep, NFSPROC_T *p); static int nfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p); static void nfsrv_freelockowner(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p); static void nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p); static void nfsrv_freenfslock(struct nfslock *lop); static void nfsrv_freenfslockfile(struct nfslockfile *lfp); static void nfsrv_freedeleg(struct nfsstate *); static int nfsrv_getstate(struct nfsclient *clp, nfsv4stateid_t *stateidp, u_int32_t flags, struct nfsstate **stpp); static void nfsrv_getowner(struct nfsstatehead *hp, struct nfsstate *new_stp, struct nfsstate **stpp); static int nfsrv_getlockfh(vnode_t vp, u_short flags, struct nfslockfile *new_lfp, fhandle_t *nfhp, NFSPROC_T *p); static int nfsrv_getlockfile(u_short flags, struct nfslockfile **new_lfpp, struct nfslockfile **lfpp, fhandle_t *nfhp, int lockit); static void nfsrv_insertlock(struct nfslock *new_lop, struct nfslock *insert_lop, struct nfsstate *stp, struct nfslockfile *lfp); static void nfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, struct nfslock **other_lopp, struct nfslockfile *lfp); static int nfsrv_getipnumber(u_char *cp); static int nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, nfsv4stateid_t *stateidp, int specialid); static int nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, u_int32_t flags); static int nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p); static int nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, uint32_t callback, int op, const char *optag, struct nfsdsession **sepp); static u_int32_t nfsrv_nextclientindex(void); static u_int32_t nfsrv_nextstateindex(struct nfsclient *clp); static void nfsrv_markstable(struct nfsclient *clp); static int nfsrv_checkstable(struct nfsclient *clp); static int nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, struct vnode *vp, NFSPROC_T *p); static int nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, vnode_t vp); static int nfsrv_cleandeleg(vnode_t vp, struct nfslockfile *lfp, struct nfsclient *clp, int *haslockp, NFSPROC_T *p); static int nfsrv_notsamecredname(struct nfsrv_descript *nd, struct nfsclient *clp); static time_t nfsrv_leaseexpiry(void); static void nfsrv_delaydelegtimeout(struct nfsstate *stp); static int nfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, struct nfsstate *stp, struct nfsrvcache *op); static int nfsrv_nootherstate(struct nfsstate *stp); static int nfsrv_locallock(vnode_t vp, struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p); static void nfsrv_localunlock(vnode_t vp, struct nfslockfile *lfp, uint64_t init_first, uint64_t init_end, NFSPROC_T *p); static int nfsrv_dolocal(vnode_t vp, struct nfslockfile *lfp, int flags, int oldflags, uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p); static void nfsrv_locallock_rollback(vnode_t vp, struct nfslockfile *lfp, NFSPROC_T *p); static void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end); static void nfsrv_locklf(struct nfslockfile *lfp); static void nfsrv_unlocklf(struct nfslockfile *lfp); static struct nfsdsession *nfsrv_findsession(uint8_t *sessionid); static int nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid); static int nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, int dont_replycache, struct nfsdsession **sepp); static int nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp); /* * Scan the client list for a match and either return the current one, * create a new entry or return an error. * If returning a non-error, the clp structure must either be linked into * the client list or free'd. */ APPLESTATIC int nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, nfsquad_t *clientidp, nfsquad_t *confirmp, NFSPROC_T *p) { struct nfsclient *clp = NULL, *new_clp = *new_clpp; int i, error = 0; struct nfsstate *stp, *tstp; struct sockaddr_in *sad, *rad; int zapit = 0, gotit, hasstate = 0, igotlock; static u_int64_t confirm_index = 0; /* * Check for state resource limit exceeded. */ if (nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } if (nfsrv_issuedelegs == 0 || ((nd->nd_flag & ND_GSS) != 0 && nfsrv_nogsscallback != 0)) /* * Don't do callbacks when delegations are disabled or * for AUTH_GSS unless enabled via nfsrv_nogsscallback. * If establishing a callback connection is attempted * when a firewall is blocking the callback path, the * server may wait too long for the connect attempt to * succeed during the Open. Some clients, such as Linux, * may timeout and give up on the Open before the server * replies. Also, since AUTH_GSS callbacks are not * yet interoperability tested, they might cause the * server to crap out, if they get past the Init call to * the client. */ new_clp->lc_program = 0; /* Lock out other nfsd threads */ NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!igotlock); NFSUNLOCKV4ROOTMUTEX(); /* * Search for a match in the client list. */ gotit = i = 0; while (i < nfsrv_clienthashsize && !gotit) { LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { if (new_clp->lc_idlen == clp->lc_idlen && !NFSBCMP(new_clp->lc_id, clp->lc_id, clp->lc_idlen)) { gotit = 1; break; } } if (gotit == 0) i++; } if (!gotit || (clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_ADMINREVOKED))) { if ((nd->nd_flag & ND_NFSV41) != 0 && confirmp->lval[1] != 0) { /* * For NFSv4.1, if confirmp->lval[1] is non-zero, the * client is trying to update a confirmed clientid. */ NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); confirmp->lval[1] = 0; error = NFSERR_NOENT; goto out; } /* * Get rid of the old one. */ if (i != nfsrv_clienthashsize) { LIST_REMOVE(clp, lc_hash); nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); zapit = 1; } /* * Add it after assigning a client id to it. */ new_clp->lc_flags |= LCL_NEEDSCONFIRM; if ((nd->nd_flag & ND_NFSV41) != 0) new_clp->lc_confirm.lval[0] = confirmp->lval[0] = ++confirm_index; else confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = (u_int32_t)nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = nfsrv_nextclientindex(); new_clp->lc_stateindex = 0; new_clp->lc_statemaxindex = 0; new_clp->lc_cbref = 0; new_clp->lc_expiry = nfsrv_leaseexpiry(); LIST_INIT(&new_clp->lc_open); LIST_INIT(&new_clp->lc_deleg); LIST_INIT(&new_clp->lc_olddeleg); LIST_INIT(&new_clp->lc_session); for (i = 0; i < nfsrv_statehashsize; i++) LIST_INIT(&new_clp->lc_stateid[i]); LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, lc_hash); nfsstatsv1.srvclients++; nfsrv_openpluslock++; nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); if (zapit) nfsrv_zapclient(clp, p); *new_clpp = NULL; goto out; } /* * Now, handle the cases where the id is already issued. */ if (nfsrv_notsamecredname(nd, clp)) { /* * Check to see if there is expired state that should go away. */ if (clp->lc_expiry < NFSD_MONOSEC && (!LIST_EMPTY(&clp->lc_open) || !LIST_EMPTY(&clp->lc_deleg))) { nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); } /* * If there is outstanding state, then reply NFSERR_CLIDINUSE per * RFC3530 Sec. 8.1.2 last para. */ if (!LIST_EMPTY(&clp->lc_deleg)) { hasstate = 1; } else if (LIST_EMPTY(&clp->lc_open)) { hasstate = 0; } else { hasstate = 0; /* Look for an Open on the OpenOwner */ LIST_FOREACH(stp, &clp->lc_open, ls_list) { if (!LIST_EMPTY(&stp->ls_open)) { hasstate = 1; break; } } } if (hasstate) { /* * If the uid doesn't match, return NFSERR_CLIDINUSE after * filling out the correct ipaddr and portnum. */ sad = NFSSOCKADDR(new_clp->lc_req.nr_nam, struct sockaddr_in *); rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *); sad->sin_addr.s_addr = rad->sin_addr.s_addr; sad->sin_port = rad->sin_port; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); error = NFSERR_CLIDINUSE; goto out; } } if (NFSBCMP(new_clp->lc_verf, clp->lc_verf, NFSX_VERF)) { /* * If the verifier has changed, the client has rebooted * and a new client id is issued. The old state info * can be thrown away once the SETCLIENTID_CONFIRM occurs. */ LIST_REMOVE(clp, lc_hash); new_clp->lc_flags |= LCL_NEEDSCONFIRM; if ((nd->nd_flag & ND_NFSV41) != 0) new_clp->lc_confirm.lval[0] = confirmp->lval[0] = ++confirm_index; else confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = nfsrv_nextclientindex(); new_clp->lc_stateindex = 0; new_clp->lc_statemaxindex = 0; new_clp->lc_cbref = 0; new_clp->lc_expiry = nfsrv_leaseexpiry(); /* * Save the state until confirmed. */ LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) tstp->ls_clp = new_clp; LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) tstp->ls_clp = new_clp; LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) tstp->ls_clp = new_clp; for (i = 0; i < nfsrv_statehashsize; i++) { LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], ls_hash); LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) tstp->ls_clp = new_clp; } LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, lc_hash); nfsstatsv1.srvclients++; nfsrv_openpluslock++; nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); /* * Must wait until any outstanding callback on the old clp * completes. */ NFSLOCKSTATE(); while (clp->lc_cbref) { clp->lc_flags |= LCL_WAKEUPWANTED; (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, "nfsd clp", 10 * hz); } NFSUNLOCKSTATE(); nfsrv_zapclient(clp, p); *new_clpp = NULL; goto out; } /* For NFSv4.1, mark that we found a confirmed clientid. */ if ((nd->nd_flag & ND_NFSV41) != 0) { clientidp->lval[0] = clp->lc_clientid.lval[0]; clientidp->lval[1] = clp->lc_clientid.lval[1]; confirmp->lval[0] = 0; /* Ignored by client */ confirmp->lval[1] = 1; } else { /* * id and verifier match, so update the net address info * and get rid of any existing callback authentication * handle, so a new one will be acquired. */ LIST_REMOVE(clp, lc_hash); new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); new_clp->lc_expiry = nfsrv_leaseexpiry(); confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = clp->lc_clientid.lval[0]; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = clp->lc_clientid.lval[1]; new_clp->lc_delegtime = clp->lc_delegtime; new_clp->lc_stateindex = clp->lc_stateindex; new_clp->lc_statemaxindex = clp->lc_statemaxindex; new_clp->lc_cbref = 0; LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) tstp->ls_clp = new_clp; LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) tstp->ls_clp = new_clp; LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) tstp->ls_clp = new_clp; for (i = 0; i < nfsrv_statehashsize; i++) { LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], ls_hash); LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) tstp->ls_clp = new_clp; } LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, lc_hash); nfsstatsv1.srvclients++; nfsrv_openpluslock++; nfsrv_clients++; } NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); if ((nd->nd_flag & ND_NFSV41) == 0) { /* * Must wait until any outstanding callback on the old clp * completes. */ NFSLOCKSTATE(); while (clp->lc_cbref) { clp->lc_flags |= LCL_WAKEUPWANTED; (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, "nfsdclp", 10 * hz); } NFSUNLOCKSTATE(); nfsrv_zapclient(clp, p); *new_clpp = NULL; } out: NFSEXITCODE2(error, nd); return (error); } /* * Check to see if the client id exists and optionally confirm it. */ APPLESTATIC int nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, struct nfsdsession *nsep, nfsquad_t confirm, uint32_t cbprogram, struct nfsrv_descript *nd, NFSPROC_T *p) { struct nfsclient *clp; struct nfsstate *stp; int i; struct nfsclienthashhead *hp; int error = 0, igotlock, doneok; struct nfssessionhash *shp; struct nfsdsession *sep; uint64_t sessid[2]; static uint64_t next_sess = 0; if (clpp) *clpp = NULL; if ((nd == NULL || (nd->nd_flag & ND_NFSV41) == 0 || opflags != CLOPS_RENEW) && nfsrvboottime != clientid.lval[0]) { error = NFSERR_STALECLIENTID; goto out; } /* * If called with opflags == CLOPS_RENEW, the State Lock is * already held. Otherwise, we need to get either that or, * for the case of Confirm, lock out the nfsd threads. */ if (opflags & CLOPS_CONFIRM) { NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!igotlock); /* * Create a new sessionid here, since we need to do it where * there is a mutex held to serialize update of next_sess. */ if ((nd->nd_flag & ND_NFSV41) != 0) { sessid[0] = ++next_sess; sessid[1] = clientid.qval; } NFSUNLOCKV4ROOTMUTEX(); } else if (opflags != CLOPS_RENEW) { NFSLOCKSTATE(); } /* For NFSv4.1, the clp is acquired from the associated session. */ if (nd != NULL && (nd->nd_flag & ND_NFSV41) != 0 && opflags == CLOPS_RENEW) { clp = NULL; if ((nd->nd_flag & ND_HASSEQUENCE) != 0) { shp = NFSSESSIONHASH(nd->nd_sessionid); NFSLOCKSESSION(shp); sep = nfsrv_findsession(nd->nd_sessionid); if (sep != NULL) clp = sep->sess_clp; NFSUNLOCKSESSION(shp); } } else { hp = NFSCLIENTHASH(clientid); LIST_FOREACH(clp, hp, lc_hash) { if (clp->lc_clientid.lval[1] == clientid.lval[1]) break; } } if (clp == NULL) { if (opflags & CLOPS_CONFIRM) error = NFSERR_STALECLIENTID; else error = NFSERR_EXPIRED; } else if (clp->lc_flags & LCL_ADMINREVOKED) { /* * If marked admin revoked, just return the error. */ error = NFSERR_ADMINREVOKED; } if (error) { if (opflags & CLOPS_CONFIRM) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } else if (opflags != CLOPS_RENEW) { NFSUNLOCKSTATE(); } goto out; } /* * Perform any operations specified by the opflags. */ if (opflags & CLOPS_CONFIRM) { if (((nd->nd_flag & ND_NFSV41) != 0 && clp->lc_confirm.lval[0] != confirm.lval[0]) || ((nd->nd_flag & ND_NFSV41) == 0 && clp->lc_confirm.qval != confirm.qval)) error = NFSERR_STALECLIENTID; else if (nfsrv_notsamecredname(nd, clp)) error = NFSERR_CLIDINUSE; if (!error) { if ((clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_DONTCLEAN)) == LCL_NEEDSCONFIRM) { /* * Hang onto the delegations (as old delegations) * for an Open with CLAIM_DELEGATE_PREV unless in * grace, but get rid of the rest of the state. */ nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_olddeleg); if (nfsrv_checkgrace(nd, clp, 0)) { /* In grace, so just delete delegations */ nfsrv_freedeleglist(&clp->lc_deleg); } else { LIST_FOREACH(stp, &clp->lc_deleg, ls_list) stp->ls_flags |= NFSLCK_OLDDELEG; clp->lc_delegtime = NFSD_MONOSEC + nfsrv_lease + NFSRV_LEASEDELTA; LIST_NEWHEAD(&clp->lc_olddeleg, &clp->lc_deleg, ls_list); } if ((nd->nd_flag & ND_NFSV41) != 0) clp->lc_program = cbprogram; } clp->lc_flags &= ~(LCL_NEEDSCONFIRM | LCL_DONTCLEAN); if (clp->lc_program) clp->lc_flags |= LCL_NEEDSCBNULL; /* For NFSv4.1, link the session onto the client. */ if (nsep != NULL) { /* Hold a reference on the xprt for a backchannel. */ if ((nsep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0 && clp->lc_req.nr_client == NULL) { clp->lc_req.nr_client = (struct __rpc_client *) clnt_bck_create(nd->nd_xprt->xp_socket, cbprogram, NFSV4_CBVERS); if (clp->lc_req.nr_client != NULL) { SVC_ACQUIRE(nd->nd_xprt); nd->nd_xprt->xp_p2 = clp->lc_req.nr_client->cl_private; /* Disable idle timeout. */ nd->nd_xprt->xp_idletimeout = 0; nsep->sess_cbsess.nfsess_xprt = nd->nd_xprt; } else nsep->sess_crflags &= ~NFSV4CRSESS_CONNBACKCHAN; } NFSBCOPY(sessid, nsep->sess_sessionid, NFSX_V4SESSIONID); NFSBCOPY(sessid, nsep->sess_cbsess.nfsess_sessionid, NFSX_V4SESSIONID); shp = NFSSESSIONHASH(nsep->sess_sessionid); NFSLOCKSTATE(); NFSLOCKSESSION(shp); LIST_INSERT_HEAD(&shp->list, nsep, sess_hash); LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list); nsep->sess_clp = clp; NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); } } } else if (clp->lc_flags & LCL_NEEDSCONFIRM) { error = NFSERR_EXPIRED; } /* * If called by the Renew Op, we must check the principal. */ if (!error && (opflags & CLOPS_RENEWOP)) { if (nfsrv_notsamecredname(nd, clp)) { doneok = 0; for (i = 0; i < nfsrv_statehashsize && doneok == 0; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if ((stp->ls_flags & NFSLCK_OPEN) && stp->ls_uid == nd->nd_cred->cr_uid) { doneok = 1; break; } } } if (!doneok) error = NFSERR_ACCES; } if (!error && (clp->lc_flags & LCL_CBDOWN)) error = NFSERR_CBPATHDOWN; } if ((!error || error == NFSERR_CBPATHDOWN) && (opflags & CLOPS_RENEW)) { clp->lc_expiry = nfsrv_leaseexpiry(); } if (opflags & CLOPS_CONFIRM) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } else if (opflags != CLOPS_RENEW) { NFSUNLOCKSTATE(); } if (clpp) *clpp = clp; out: NFSEXITCODE2(error, nd); return (error); } /* * Perform the NFSv4.1 destroy clientid. */ int nfsrv_destroyclient(nfsquad_t clientid, NFSPROC_T *p) { struct nfsclient *clp; struct nfsclienthashhead *hp; int error = 0, i, igotlock; if (nfsrvboottime != clientid.lval[0]) { error = NFSERR_STALECLIENTID; goto out; } /* Lock out other nfsd threads */ NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (igotlock == 0); NFSUNLOCKV4ROOTMUTEX(); hp = NFSCLIENTHASH(clientid); LIST_FOREACH(clp, hp, lc_hash) { if (clp->lc_clientid.lval[1] == clientid.lval[1]) break; } if (clp == NULL) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); /* Just return ok, since it is gone. */ goto out; } /* Scan for state on the clientid. */ for (i = 0; i < nfsrv_statehashsize; i++) if (!LIST_EMPTY(&clp->lc_stateid[i])) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); error = NFSERR_CLIENTIDBUSY; goto out; } if (!LIST_EMPTY(&clp->lc_session) || !LIST_EMPTY(&clp->lc_deleg)) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); error = NFSERR_CLIENTIDBUSY; goto out; } /* Destroy the clientid and return ok. */ nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); LIST_REMOVE(clp, lc_hash); NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); nfsrv_zapclient(clp, p); out: NFSEXITCODE2(error, nd); return (error); } /* * Called from the new nfssvc syscall to admin revoke a clientid. * Returns 0 for success, error otherwise. */ APPLESTATIC int nfsrv_adminrevoke(struct nfsd_clid *revokep, NFSPROC_T *p) { struct nfsclient *clp = NULL; int i, error = 0; int gotit, igotlock; /* * First, lock out the nfsd so that state won't change while the * revocation record is being written to the stable storage restart * file. */ NFSLOCKV4ROOTMUTEX(); do { igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!igotlock); NFSUNLOCKV4ROOTMUTEX(); /* * Search for a match in the client list. */ gotit = i = 0; while (i < nfsrv_clienthashsize && !gotit) { LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { if (revokep->nclid_idlen == clp->lc_idlen && !NFSBCMP(revokep->nclid_id, clp->lc_id, clp->lc_idlen)) { gotit = 1; break; } } i++; } if (!gotit) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 0); NFSUNLOCKV4ROOTMUTEX(); error = EPERM; goto out; } /* * Now, write out the revocation record */ nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); nfsrv_backupstable(); /* * and clear out the state, marking the clientid revoked. */ clp->lc_flags &= ~LCL_CALLBACKSON; clp->lc_flags |= LCL_ADMINREVOKED; nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 0); NFSUNLOCKV4ROOTMUTEX(); out: NFSEXITCODE(error); return (error); } /* * Dump out stats for all clients. Called from nfssvc(2), that is used * nfsstatsv1. */ APPLESTATIC void nfsrv_dumpclients(struct nfsd_dumpclients *dumpp, int maxcnt) { struct nfsclient *clp; int i = 0, cnt = 0; /* * First, get a reference on the nfsv4rootfs_lock so that an * exclusive lock cannot be acquired while dumping the clients. */ NFSLOCKV4ROOTMUTEX(); nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); NFSUNLOCKV4ROOTMUTEX(); NFSLOCKSTATE(); /* * Rattle through the client lists until done. */ while (i < nfsrv_clienthashsize && cnt < maxcnt) { clp = LIST_FIRST(&nfsclienthash[i]); while (clp != LIST_END(&nfsclienthash[i]) && cnt < maxcnt) { nfsrv_dumpaclient(clp, &dumpp[cnt]); cnt++; clp = LIST_NEXT(clp, lc_hash); } i++; } if (cnt < maxcnt) dumpp[cnt].ndcl_clid.nclid_idlen = 0; NFSUNLOCKSTATE(); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); } /* * Dump stats for a client. Must be called with the NFSSTATELOCK and spl'd. */ static void nfsrv_dumpaclient(struct nfsclient *clp, struct nfsd_dumpclients *dumpp) { struct nfsstate *stp, *openstp, *lckownstp; struct nfslock *lop; struct sockaddr *sad; struct sockaddr_in *rad; struct sockaddr_in6 *rad6; dumpp->ndcl_nopenowners = dumpp->ndcl_nlockowners = 0; dumpp->ndcl_nopens = dumpp->ndcl_nlocks = 0; dumpp->ndcl_ndelegs = dumpp->ndcl_nolddelegs = 0; dumpp->ndcl_flags = clp->lc_flags; dumpp->ndcl_clid.nclid_idlen = clp->lc_idlen; NFSBCOPY(clp->lc_id, dumpp->ndcl_clid.nclid_id, clp->lc_idlen); sad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr *); dumpp->ndcl_addrfam = sad->sa_family; if (sad->sa_family == AF_INET) { rad = (struct sockaddr_in *)sad; dumpp->ndcl_cbaddr.sin_addr = rad->sin_addr; } else { rad6 = (struct sockaddr_in6 *)sad; dumpp->ndcl_cbaddr.sin6_addr = rad6->sin6_addr; } /* * Now, scan the state lists and total up the opens and locks. */ LIST_FOREACH(stp, &clp->lc_open, ls_list) { dumpp->ndcl_nopenowners++; LIST_FOREACH(openstp, &stp->ls_open, ls_list) { dumpp->ndcl_nopens++; LIST_FOREACH(lckownstp, &openstp->ls_open, ls_list) { dumpp->ndcl_nlockowners++; LIST_FOREACH(lop, &lckownstp->ls_lock, lo_lckowner) { dumpp->ndcl_nlocks++; } } } } /* * and the delegation lists. */ LIST_FOREACH(stp, &clp->lc_deleg, ls_list) { dumpp->ndcl_ndelegs++; } LIST_FOREACH(stp, &clp->lc_olddeleg, ls_list) { dumpp->ndcl_nolddelegs++; } } /* * Dump out lock stats for a file. */ APPLESTATIC void nfsrv_dumplocks(vnode_t vp, struct nfsd_dumplocks *ldumpp, int maxcnt, NFSPROC_T *p) { struct nfsstate *stp; struct nfslock *lop; int cnt = 0; struct nfslockfile *lfp; struct sockaddr *sad; struct sockaddr_in *rad; struct sockaddr_in6 *rad6; int ret; fhandle_t nfh; ret = nfsrv_getlockfh(vp, 0, NULL, &nfh, p); /* * First, get a reference on the nfsv4rootfs_lock so that an * exclusive lock on it cannot be acquired while dumping the locks. */ NFSLOCKV4ROOTMUTEX(); nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); NFSUNLOCKV4ROOTMUTEX(); NFSLOCKSTATE(); if (!ret) ret = nfsrv_getlockfile(0, NULL, &lfp, &nfh, 0); if (ret) { ldumpp[0].ndlck_clid.nclid_idlen = 0; NFSUNLOCKSTATE(); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); return; } /* * For each open share on file, dump it out. */ stp = LIST_FIRST(&lfp->lf_open); while (stp != LIST_END(&lfp->lf_open) && cnt < maxcnt) { ldumpp[cnt].ndlck_flags = stp->ls_flags; ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; ldumpp[cnt].ndlck_owner.nclid_idlen = stp->ls_openowner->ls_ownerlen; NFSBCOPY(stp->ls_openowner->ls_owner, ldumpp[cnt].ndlck_owner.nclid_id, stp->ls_openowner->ls_ownerlen); ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, stp->ls_clp->lc_idlen); sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); ldumpp[cnt].ndlck_addrfam = sad->sa_family; if (sad->sa_family == AF_INET) { rad = (struct sockaddr_in *)sad; ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; } else { rad6 = (struct sockaddr_in6 *)sad; ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; } stp = LIST_NEXT(stp, ls_file); cnt++; } /* * and all locks. */ lop = LIST_FIRST(&lfp->lf_lock); while (lop != LIST_END(&lfp->lf_lock) && cnt < maxcnt) { stp = lop->lo_stp; ldumpp[cnt].ndlck_flags = lop->lo_flags; ldumpp[cnt].ndlck_first = lop->lo_first; ldumpp[cnt].ndlck_end = lop->lo_end; ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; ldumpp[cnt].ndlck_owner.nclid_idlen = stp->ls_ownerlen; NFSBCOPY(stp->ls_owner, ldumpp[cnt].ndlck_owner.nclid_id, stp->ls_ownerlen); ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, stp->ls_clp->lc_idlen); sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); ldumpp[cnt].ndlck_addrfam = sad->sa_family; if (sad->sa_family == AF_INET) { rad = (struct sockaddr_in *)sad; ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; } else { rad6 = (struct sockaddr_in6 *)sad; ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; } lop = LIST_NEXT(lop, lo_lckfile); cnt++; } /* * and the delegations. */ stp = LIST_FIRST(&lfp->lf_deleg); while (stp != LIST_END(&lfp->lf_deleg) && cnt < maxcnt) { ldumpp[cnt].ndlck_flags = stp->ls_flags; ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; ldumpp[cnt].ndlck_owner.nclid_idlen = 0; ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, stp->ls_clp->lc_idlen); sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); ldumpp[cnt].ndlck_addrfam = sad->sa_family; if (sad->sa_family == AF_INET) { rad = (struct sockaddr_in *)sad; ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; } else { rad6 = (struct sockaddr_in6 *)sad; ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; } stp = LIST_NEXT(stp, ls_file); cnt++; } /* * If list isn't full, mark end of list by setting the client name * to zero length. */ if (cnt < maxcnt) ldumpp[cnt].ndlck_clid.nclid_idlen = 0; NFSUNLOCKSTATE(); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); } /* * Server timer routine. It can scan any linked list, so long * as it holds the spin/mutex lock and there is no exclusive lock on * nfsv4rootfs_lock. * (For OpenBSD, a kthread is ok. For FreeBSD, I think it is ok * to do this from a callout, since the spin locks work. For * Darwin, I'm not sure what will work correctly yet.) * Should be called once per second. */ APPLESTATIC void nfsrv_servertimer(void) { struct nfsclient *clp, *nclp; struct nfsstate *stp, *nstp; int got_ref, i; /* * Make sure nfsboottime is set. This is used by V3 as well * as V4. Note that nfsboottime is not nfsrvboottime, which is * only used by the V4 server for leases. */ if (nfsboottime.tv_sec == 0) NFSSETBOOTTIME(nfsboottime); /* * If server hasn't started yet, just return. */ NFSLOCKSTATE(); if (nfsrv_stablefirst.nsf_eograce == 0) { NFSUNLOCKSTATE(); return; } if (!(nfsrv_stablefirst.nsf_flags & NFSNSF_UPDATEDONE)) { if (!(nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) && NFSD_MONOSEC > nfsrv_stablefirst.nsf_eograce) nfsrv_stablefirst.nsf_flags |= (NFSNSF_GRACEOVER | NFSNSF_NEEDLOCK); NFSUNLOCKSTATE(); return; } /* * Try and get a reference count on the nfsv4rootfs_lock so that * no nfsd thread can acquire an exclusive lock on it before this * call is done. If it is already exclusively locked, just return. */ NFSLOCKV4ROOTMUTEX(); got_ref = nfsv4_getref_nonblock(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); if (got_ref == 0) { NFSUNLOCKSTATE(); return; } /* * For each client... */ for (i = 0; i < nfsrv_clienthashsize; i++) { clp = LIST_FIRST(&nfsclienthash[i]); while (clp != LIST_END(&nfsclienthash[i])) { nclp = LIST_NEXT(clp, lc_hash); if (!(clp->lc_flags & LCL_EXPIREIT)) { if (((clp->lc_expiry + NFSRV_STALELEASE) < NFSD_MONOSEC && ((LIST_EMPTY(&clp->lc_deleg) && LIST_EMPTY(&clp->lc_open)) || nfsrv_clients > nfsrv_clienthighwater)) || (clp->lc_expiry + NFSRV_MOULDYLEASE) < NFSD_MONOSEC || (clp->lc_expiry < NFSD_MONOSEC && (nfsrv_openpluslock * 10 / 9) > nfsrv_v4statelimit)) { /* * Lease has expired several nfsrv_lease times ago: * PLUS * - no state is associated with it * OR * - above high water mark for number of clients * (nfsrv_clienthighwater should be large enough * that this only occurs when clients fail to * use the same nfs_client_id4.id. Maybe somewhat * higher that the maximum number of clients that * will mount this server?) * OR * Lease has expired a very long time ago * OR * Lease has expired PLUS the number of opens + locks * has exceeded 90% of capacity * * --> Mark for expiry. The actual expiry will be done * by an nfsd sometime soon. */ clp->lc_flags |= LCL_EXPIREIT; nfsrv_stablefirst.nsf_flags |= (NFSNSF_NEEDLOCK | NFSNSF_EXPIREDCLIENT); } else { /* * If there are no opens, increment no open tick cnt * If time exceeds NFSNOOPEN, mark it to be thrown away * otherwise, if there is an open, reset no open time * Hopefully, this will avoid excessive re-creation * of open owners and subsequent open confirms. */ stp = LIST_FIRST(&clp->lc_open); while (stp != LIST_END(&clp->lc_open)) { nstp = LIST_NEXT(stp, ls_list); if (LIST_EMPTY(&stp->ls_open)) { stp->ls_noopens++; if (stp->ls_noopens > NFSNOOPEN || (nfsrv_openpluslock * 2) > nfsrv_v4statelimit) nfsrv_stablefirst.nsf_flags |= NFSNSF_NOOPENS; } else { stp->ls_noopens = 0; } stp = nstp; } } } clp = nclp; } } NFSUNLOCKSTATE(); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); } /* * The following set of functions free up the various data structures. */ /* * Clear out all open/lock state related to this nfsclient. * Caller must hold an exclusive lock on nfsv4rootfs_lock, so that * there are no other active nfsd threads. */ APPLESTATIC void nfsrv_cleanclient(struct nfsclient *clp, NFSPROC_T *p) { struct nfsstate *stp, *nstp; struct nfsdsession *sep, *nsep; LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) nfsrv_freeopenowner(stp, 1, p); if ((clp->lc_flags & LCL_ADMINREVOKED) == 0) LIST_FOREACH_SAFE(sep, &clp->lc_session, sess_list, nsep) (void)nfsrv_freesession(sep, NULL); } /* * Free a client that has been cleaned. It should also already have been * removed from the lists. * (Just to be safe w.r.t. newnfs_disconnect(), call this function when * softclock interrupts are enabled.) */ APPLESTATIC void nfsrv_zapclient(struct nfsclient *clp, NFSPROC_T *p) { #ifdef notyet if ((clp->lc_flags & (LCL_GSS | LCL_CALLBACKSON)) == (LCL_GSS | LCL_CALLBACKSON) && (clp->lc_hand.nfsh_flag & NFSG_COMPLETE) && clp->lc_handlelen > 0) { clp->lc_hand.nfsh_flag &= ~NFSG_COMPLETE; clp->lc_hand.nfsh_flag |= NFSG_DESTROYED; (void) nfsrv_docallback(clp, NFSV4PROC_CBNULL, NULL, 0, NULL, NULL, NULL, p); } #endif newnfs_disconnect(&clp->lc_req); NFSSOCKADDRFREE(clp->lc_req.nr_nam); NFSFREEMUTEX(&clp->lc_req.nr_mtx); free(clp->lc_stateid, M_NFSDCLIENT); free(clp, M_NFSDCLIENT); NFSLOCKSTATE(); nfsstatsv1.srvclients--; nfsrv_openpluslock--; nfsrv_clients--; NFSUNLOCKSTATE(); } /* * Free a list of delegation state structures. * (This function will also free all nfslockfile structures that no * longer have associated state.) */ APPLESTATIC void nfsrv_freedeleglist(struct nfsstatehead *sthp) { struct nfsstate *stp, *nstp; LIST_FOREACH_SAFE(stp, sthp, ls_list, nstp) { nfsrv_freedeleg(stp); } LIST_INIT(sthp); } /* * Free up a delegation. */ static void nfsrv_freedeleg(struct nfsstate *stp) { struct nfslockfile *lfp; LIST_REMOVE(stp, ls_hash); LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_file); lfp = stp->ls_lfp; if (LIST_EMPTY(&lfp->lf_open) && LIST_EMPTY(&lfp->lf_lock) && LIST_EMPTY(&lfp->lf_deleg) && LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && lfp->lf_usecount == 0 && nfsv4_testlock(&lfp->lf_locallock_lck) == 0) nfsrv_freenfslockfile(lfp); FREE((caddr_t)stp, M_NFSDSTATE); nfsstatsv1.srvdelegates--; nfsrv_openpluslock--; nfsrv_delegatecnt--; } /* * This function frees an open owner and all associated opens. */ static void nfsrv_freeopenowner(struct nfsstate *stp, int cansleep, NFSPROC_T *p) { struct nfsstate *nstp, *tstp; LIST_REMOVE(stp, ls_list); /* * Now, free all associated opens. */ nstp = LIST_FIRST(&stp->ls_open); while (nstp != LIST_END(&stp->ls_open)) { tstp = nstp; nstp = LIST_NEXT(nstp, ls_list); (void) nfsrv_freeopen(tstp, NULL, cansleep, p); } if (stp->ls_op) nfsrvd_derefcache(stp->ls_op); FREE((caddr_t)stp, M_NFSDSTATE); nfsstatsv1.srvopenowners--; nfsrv_openpluslock--; } /* * This function frees an open (nfsstate open structure) with all associated * lock_owners and locks. It also frees the nfslockfile structure iff there * are no other opens on the file. * Returns 1 if it free'd the nfslockfile, 0 otherwise. */ static int nfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) { struct nfsstate *nstp, *tstp; struct nfslockfile *lfp; int ret; LIST_REMOVE(stp, ls_hash); LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_file); lfp = stp->ls_lfp; /* * Now, free all lockowners associated with this open. */ LIST_FOREACH_SAFE(tstp, &stp->ls_open, ls_list, nstp) nfsrv_freelockowner(tstp, vp, cansleep, p); /* * The nfslockfile is freed here if there are no locks * associated with the open. * If there are locks associated with the open, the * nfslockfile structure can be freed via nfsrv_freelockowner(). * Acquire the state mutex to avoid races with calls to * nfsrv_getlockfile(). */ if (cansleep != 0) NFSLOCKSTATE(); if (lfp != NULL && LIST_EMPTY(&lfp->lf_open) && LIST_EMPTY(&lfp->lf_deleg) && LIST_EMPTY(&lfp->lf_lock) && LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && lfp->lf_usecount == 0 && (cansleep != 0 || nfsv4_testlock(&lfp->lf_locallock_lck) == 0)) { nfsrv_freenfslockfile(lfp); ret = 1; } else ret = 0; if (cansleep != 0) NFSUNLOCKSTATE(); FREE((caddr_t)stp, M_NFSDSTATE); nfsstatsv1.srvopens--; nfsrv_openpluslock--; return (ret); } /* * Frees a lockowner and all associated locks. */ static void nfsrv_freelockowner(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) { LIST_REMOVE(stp, ls_hash); LIST_REMOVE(stp, ls_list); nfsrv_freeallnfslocks(stp, vp, cansleep, p); if (stp->ls_op) nfsrvd_derefcache(stp->ls_op); FREE((caddr_t)stp, M_NFSDSTATE); nfsstatsv1.srvlockowners--; nfsrv_openpluslock--; } /* * Free all the nfs locks on a lockowner. */ static void nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) { struct nfslock *lop, *nlop; struct nfsrollback *rlp, *nrlp; struct nfslockfile *lfp = NULL; int gottvp = 0; vnode_t tvp = NULL; uint64_t first, end; if (vp != NULL) ASSERT_VOP_UNLOCKED(vp, "nfsrv_freeallnfslocks: vnode locked"); lop = LIST_FIRST(&stp->ls_lock); while (lop != LIST_END(&stp->ls_lock)) { nlop = LIST_NEXT(lop, lo_lckowner); /* * Since all locks should be for the same file, lfp should * not change. */ if (lfp == NULL) lfp = lop->lo_lfp; else if (lfp != lop->lo_lfp) panic("allnfslocks"); /* * If vp is NULL and cansleep != 0, a vnode must be acquired * from the file handle. This only occurs when called from * nfsrv_cleanclient(). */ if (gottvp == 0) { if (nfsrv_dolocallocks == 0) tvp = NULL; else if (vp == NULL && cansleep != 0) { tvp = nfsvno_getvp(&lfp->lf_fh); NFSVOPUNLOCK(tvp, 0); } else tvp = vp; gottvp = 1; } if (tvp != NULL) { if (cansleep == 0) panic("allnfs2"); first = lop->lo_first; end = lop->lo_end; nfsrv_freenfslock(lop); nfsrv_localunlock(tvp, lfp, first, end, p); LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, nrlp) free(rlp, M_NFSDROLLBACK); LIST_INIT(&lfp->lf_rollback); } else nfsrv_freenfslock(lop); lop = nlop; } if (vp == NULL && tvp != NULL) vrele(tvp); } /* * Free an nfslock structure. */ static void nfsrv_freenfslock(struct nfslock *lop) { if (lop->lo_lckfile.le_prev != NULL) { LIST_REMOVE(lop, lo_lckfile); nfsstatsv1.srvlocks--; nfsrv_openpluslock--; } LIST_REMOVE(lop, lo_lckowner); FREE((caddr_t)lop, M_NFSDLOCK); } /* * This function frees an nfslockfile structure. */ static void nfsrv_freenfslockfile(struct nfslockfile *lfp) { LIST_REMOVE(lfp, lf_hash); FREE((caddr_t)lfp, M_NFSDLOCKFILE); } /* * This function looks up an nfsstate structure via stateid. */ static int nfsrv_getstate(struct nfsclient *clp, nfsv4stateid_t *stateidp, __unused u_int32_t flags, struct nfsstate **stpp) { struct nfsstate *stp; struct nfsstatehead *hp; int error = 0; *stpp = NULL; hp = NFSSTATEHASH(clp, *stateidp); LIST_FOREACH(stp, hp, ls_hash) { if (!NFSBCMP(stp->ls_stateid.other, stateidp->other, NFSX_STATEIDOTHER)) break; } /* * If no state id in list, return NFSERR_BADSTATEID. */ if (stp == LIST_END(hp)) { error = NFSERR_BADSTATEID; goto out; } *stpp = stp; out: NFSEXITCODE(error); return (error); } /* * This function gets an nfsstate structure via owner string. */ static void nfsrv_getowner(struct nfsstatehead *hp, struct nfsstate *new_stp, struct nfsstate **stpp) { struct nfsstate *stp; *stpp = NULL; LIST_FOREACH(stp, hp, ls_list) { if (new_stp->ls_ownerlen == stp->ls_ownerlen && !NFSBCMP(new_stp->ls_owner,stp->ls_owner,stp->ls_ownerlen)) { *stpp = stp; return; } } } /* * Lock control function called to update lock status. * Returns 0 upon success, -1 if there is no lock and the flags indicate * that one isn't to be created and an NFSERR_xxx for other errors. * The structures new_stp and new_lop are passed in as pointers that should * be set to NULL if the structure is used and shouldn't be free'd. * For the NFSLCK_TEST and NFSLCK_CHECK cases, the structures are * never used and can safely be allocated on the stack. For all other * cases, *new_stpp and *new_lopp should be malloc'd before the call, * in case they are used. */ APPLESTATIC int nfsrv_lockctrl(vnode_t vp, struct nfsstate **new_stpp, struct nfslock **new_lopp, struct nfslockconflict *cfp, nfsquad_t clientid, nfsv4stateid_t *stateidp, __unused struct nfsexstuff *exp, struct nfsrv_descript *nd, NFSPROC_T *p) { struct nfslock *lop; struct nfsstate *new_stp = *new_stpp; struct nfslock *new_lop = *new_lopp; struct nfsstate *tstp, *mystp, *nstp; int specialid = 0; struct nfslockfile *lfp; struct nfslock *other_lop = NULL; struct nfsstate *stp, *lckstp = NULL; struct nfsclient *clp = NULL; u_int32_t bits; int error = 0, haslock = 0, ret, reterr; int getlckret, delegation = 0, filestruct_locked, vnode_unlocked = 0; fhandle_t nfh; uint64_t first, end; uint32_t lock_flags; if (new_stp->ls_flags & (NFSLCK_CHECK | NFSLCK_SETATTR)) { /* * Note the special cases of "all 1s" or "all 0s" stateids and * let reads with all 1s go ahead. */ if (new_stp->ls_stateid.seqid == 0x0 && new_stp->ls_stateid.other[0] == 0x0 && new_stp->ls_stateid.other[1] == 0x0 && new_stp->ls_stateid.other[2] == 0x0) specialid = 1; else if (new_stp->ls_stateid.seqid == 0xffffffff && new_stp->ls_stateid.other[0] == 0xffffffff && new_stp->ls_stateid.other[1] == 0xffffffff && new_stp->ls_stateid.other[2] == 0xffffffff) specialid = 2; } /* * Check for restart conditions (client and server). */ error = nfsrv_checkrestart(clientid, new_stp->ls_flags, &new_stp->ls_stateid, specialid); if (error) goto out; /* * Check for state resource limit exceeded. */ if ((new_stp->ls_flags & NFSLCK_LOCK) && nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } /* * For the lock case, get another nfslock structure, * just in case we need it. * Malloc now, before we start sifting through the linked lists, * in case we have to wait for memory. */ tryagain: if (new_stp->ls_flags & NFSLCK_LOCK) MALLOC(other_lop, struct nfslock *, sizeof (struct nfslock), M_NFSDLOCK, M_WAITOK); filestruct_locked = 0; reterr = 0; lfp = NULL; /* * Get the lockfile structure for CFH now, so we can do a sanity * check against the stateid, before incrementing the seqid#, since * we want to return NFSERR_BADSTATEID on failure and the seqid# * shouldn't be incremented for this case. * If nfsrv_getlockfile() returns -1, it means "not found", which * will be handled later. * If we are doing Lock/LockU and local locking is enabled, sleep * lock the nfslockfile structure. */ getlckret = nfsrv_getlockfh(vp, new_stp->ls_flags, NULL, &nfh, p); NFSLOCKSTATE(); if (getlckret == 0) { if ((new_stp->ls_flags & (NFSLCK_LOCK | NFSLCK_UNLOCK)) != 0 && nfsrv_dolocallocks != 0 && nd->nd_repstat == 0) { getlckret = nfsrv_getlockfile(new_stp->ls_flags, NULL, &lfp, &nfh, 1); if (getlckret == 0) filestruct_locked = 1; } else getlckret = nfsrv_getlockfile(new_stp->ls_flags, NULL, &lfp, &nfh, 0); } if (getlckret != 0 && getlckret != -1) reterr = getlckret; if (filestruct_locked != 0) { LIST_INIT(&lfp->lf_rollback); if ((new_stp->ls_flags & NFSLCK_LOCK)) { /* * For local locking, do the advisory locking now, so * that any conflict can be detected. A failure later * can be rolled back locally. If an error is returned, * struct nfslockfile has been unlocked and any local * locking rolled back. */ NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl1"); vnode_unlocked = 1; NFSVOPUNLOCK(vp, 0); } reterr = nfsrv_locallock(vp, lfp, (new_lop->lo_flags & (NFSLCK_READ | NFSLCK_WRITE)), new_lop->lo_first, new_lop->lo_end, cfp, p); NFSLOCKSTATE(); } } if (specialid == 0) { if (new_stp->ls_flags & NFSLCK_TEST) { /* * RFC 3530 does not list LockT as an op that renews a * lease, but the consensus seems to be that it is ok * for a server to do so. */ error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Since NFSERR_EXPIRED, NFSERR_ADMINREVOKED are not valid * error returns for LockT, just go ahead and test for a lock, * since there are no locks for this client, but other locks * can conflict. (ie. same client will always be false) */ if (error == NFSERR_EXPIRED || error == NFSERR_ADMINREVOKED) error = 0; lckstp = new_stp; } else { error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error == 0) /* * Look up the stateid */ error = nfsrv_getstate(clp, &new_stp->ls_stateid, new_stp->ls_flags, &stp); /* * do some sanity checks for an unconfirmed open or a * stateid that refers to the wrong file, for an open stateid */ if (error == 0 && (stp->ls_flags & NFSLCK_OPEN) && ((stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM) || (getlckret == 0 && stp->ls_lfp != lfp))){ /* * NFSLCK_SETATTR should return OK rather than NFSERR_BADSTATEID * The only exception is using SETATTR with SIZE. * */ if ((new_stp->ls_flags & (NFSLCK_SETATTR | NFSLCK_CHECK)) != NFSLCK_SETATTR) error = NFSERR_BADSTATEID; } if (error == 0 && (stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) && getlckret == 0 && stp->ls_lfp != lfp) error = NFSERR_BADSTATEID; /* * If the lockowner stateid doesn't refer to the same file, * I believe that is considered ok, since some clients will * only create a single lockowner and use that for all locks * on all files. * For now, log it as a diagnostic, instead of considering it * a BadStateid. */ if (error == 0 && (stp->ls_flags & (NFSLCK_OPEN | NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) == 0 && getlckret == 0 && stp->ls_lfp != lfp) { #ifdef DIAGNOSTIC printf("Got a lock statid for different file open\n"); #endif /* error = NFSERR_BADSTATEID; */ } if (error == 0) { if (new_stp->ls_flags & NFSLCK_OPENTOLOCK) { /* * If haslock set, we've already checked the seqid. */ if (!haslock) { if (stp->ls_flags & NFSLCK_OPEN) error = nfsrv_checkseqid(nd, new_stp->ls_seq, stp->ls_openowner, new_stp->ls_op); else error = NFSERR_BADSTATEID; } if (!error) nfsrv_getowner(&stp->ls_open, new_stp, &lckstp); if (lckstp) /* * I believe this should be an error, but it * isn't obvious what NFSERR_xxx would be * appropriate, so I'll use NFSERR_INVAL for now. */ error = NFSERR_INVAL; else lckstp = new_stp; } else if (new_stp->ls_flags&(NFSLCK_LOCK|NFSLCK_UNLOCK)) { /* * If haslock set, ditto above. */ if (!haslock) { if (stp->ls_flags & NFSLCK_OPEN) error = NFSERR_BADSTATEID; else error = nfsrv_checkseqid(nd, new_stp->ls_seq, stp, new_stp->ls_op); } lckstp = stp; } else { lckstp = stp; } } /* * If the seqid part of the stateid isn't the same, return * NFSERR_OLDSTATEID for cases other than I/O Ops. * For I/O Ops, only return NFSERR_OLDSTATEID if * nfsrv_returnoldstateid is set. (The consensus on the email * list was that most clients would prefer to not receive * NFSERR_OLDSTATEID for I/O Ops, but the RFC suggests that that * is what will happen, so I use the nfsrv_returnoldstateid to * allow for either server configuration.) */ if (!error && stp->ls_stateid.seqid!=new_stp->ls_stateid.seqid && (((nd->nd_flag & ND_NFSV41) == 0 && (!(new_stp->ls_flags & NFSLCK_CHECK) || nfsrv_returnoldstateid)) || ((nd->nd_flag & ND_NFSV41) != 0 && new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; } } /* * Now we can check for grace. */ if (!error) error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; /* * If we successfully Reclaimed state, note that. */ if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error) nfsrv_markstable(clp); /* * At this point, either error == NFSERR_BADSTATEID or the * seqid# has been updated, so we can return any error. * If error == 0, there may be an error in: * nd_repstat - Set by the calling function. * reterr - Set above, if getting the nfslockfile structure * or acquiring the local lock failed. * (If both of these are set, nd_repstat should probably be * returned, since that error was detected before this * function call.) */ if (error != 0 || nd->nd_repstat != 0 || reterr != 0) { if (error == 0) { if (nd->nd_repstat != 0) error = nd->nd_repstat; else error = reterr; } if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl2"); vnode_unlocked = 1; NFSVOPUNLOCK(vp, 0); } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); } NFSUNLOCKSTATE(); goto out; } /* * Check the nfsrv_getlockfile return. * Returned -1 if no structure found. */ if (getlckret == -1) { error = NFSERR_EXPIRED; /* * Called from lockt, so no lock is OK. */ if (new_stp->ls_flags & NFSLCK_TEST) { error = 0; } else if (new_stp->ls_flags & (NFSLCK_CHECK | NFSLCK_SETATTR)) { /* * Called to check for a lock, OK if the stateid is all * 1s or all 0s, but there should be an nfsstate * otherwise. * (ie. If there is no open, I'll assume no share * deny bits.) */ if (specialid) error = 0; else error = NFSERR_BADSTATEID; } NFSUNLOCKSTATE(); goto out; } /* * For NFSLCK_CHECK and NFSLCK_LOCK, test for a share conflict. * For NFSLCK_CHECK, allow a read if write access is granted, * but check for a deny. For NFSLCK_LOCK, require correct access, * which implies a conflicting deny can't exist. */ if (new_stp->ls_flags & (NFSLCK_CHECK | NFSLCK_LOCK)) { /* * Four kinds of state id: * - specialid (all 0s or all 1s), only for NFSLCK_CHECK * - stateid for an open * - stateid for a delegation * - stateid for a lock owner */ if (!specialid) { if (stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) { delegation = 1; mystp = stp; nfsrv_delaydelegtimeout(stp); } else if (stp->ls_flags & NFSLCK_OPEN) { mystp = stp; } else { mystp = stp->ls_openstp; } /* * If locking or checking, require correct access * bit set. */ if (((new_stp->ls_flags & NFSLCK_LOCK) && !((new_lop->lo_flags >> NFSLCK_LOCKSHIFT) & mystp->ls_flags & NFSLCK_ACCESSBITS)) || ((new_stp->ls_flags & (NFSLCK_CHECK|NFSLCK_READACCESS)) == (NFSLCK_CHECK | NFSLCK_READACCESS) && !(mystp->ls_flags & NFSLCK_READACCESS)) || ((new_stp->ls_flags & (NFSLCK_CHECK|NFSLCK_WRITEACCESS)) == (NFSLCK_CHECK | NFSLCK_WRITEACCESS) && !(mystp->ls_flags & NFSLCK_WRITEACCESS))) { if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl3"); vnode_unlocked = 1; NFSVOPUNLOCK(vp, 0); } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); } NFSUNLOCKSTATE(); error = NFSERR_OPENMODE; goto out; } } else mystp = NULL; if ((new_stp->ls_flags & NFSLCK_CHECK) && !delegation) { /* * Check for a conflicting deny bit. */ LIST_FOREACH(tstp, &lfp->lf_open, ls_file) { if (tstp != mystp) { bits = tstp->ls_flags; bits >>= NFSLCK_SHIFT; if (new_stp->ls_flags & bits & NFSLCK_ACCESSBITS) { KASSERT(vnode_unlocked == 0, ("nfsrv_lockctrl: vnode unlocked1")); ret = nfsrv_clientconflict(tstp->ls_clp, &haslock, vp, p); if (ret == 1) { /* * nfsrv_clientconflict unlocks state * when it returns non-zero. */ lckstp = NULL; goto tryagain; } if (ret == 0) NFSUNLOCKSTATE(); if (ret == 2) error = NFSERR_PERM; else error = NFSERR_OPENMODE; goto out; } } } /* We're outta here */ NFSUNLOCKSTATE(); goto out; } } /* * For setattr, just get rid of all the Delegations for other clients. */ if (new_stp->ls_flags & NFSLCK_SETATTR) { KASSERT(vnode_unlocked == 0, ("nfsrv_lockctrl: vnode unlocked2")); ret = nfsrv_cleandeleg(vp, lfp, clp, &haslock, p); if (ret) { /* * nfsrv_cleandeleg() unlocks state when it * returns non-zero. */ if (ret == -1) { lckstp = NULL; goto tryagain; } error = ret; goto out; } if (!(new_stp->ls_flags & NFSLCK_CHECK) || (LIST_EMPTY(&lfp->lf_open) && LIST_EMPTY(&lfp->lf_lock) && LIST_EMPTY(&lfp->lf_deleg))) { NFSUNLOCKSTATE(); goto out; } } /* * Check for a conflicting delegation. If one is found, call * nfsrv_delegconflict() to handle it. If the v4root lock hasn't * been set yet, it will get the lock. Otherwise, it will recall * the delegation. Then, we try try again... * I currently believe the conflict algorithm to be: * For Lock Ops (Lock/LockT/LockU) * - there is a conflict iff a different client has a write delegation * For Reading (Read Op) * - there is a conflict iff a different client has a write delegation * (the specialids are always a different client) * For Writing (Write/Setattr of size) * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation * (I don't understand why this isn't allowed, but that seems to be * the current consensus?) */ tstp = LIST_FIRST(&lfp->lf_deleg); while (tstp != LIST_END(&lfp->lf_deleg)) { nstp = LIST_NEXT(tstp, ls_file); if ((((new_stp->ls_flags&(NFSLCK_LOCK|NFSLCK_UNLOCK|NFSLCK_TEST))|| ((new_stp->ls_flags & NFSLCK_CHECK) && (new_lop->lo_flags & NFSLCK_READ))) && clp != tstp->ls_clp && (tstp->ls_flags & NFSLCK_DELEGWRITE)) || ((new_stp->ls_flags & NFSLCK_CHECK) && (new_lop->lo_flags & NFSLCK_WRITE) && (clp != tstp->ls_clp || (tstp->ls_flags & NFSLCK_DELEGREAD)))) { ret = 0; if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl4"); NFSVOPUNLOCK(vp, 0); } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); NFSUNLOCKSTATE(); NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); vnode_unlocked = 0; if ((vp->v_iflag & VI_DOOMED) != 0) ret = NFSERR_SERVERFAULT; NFSLOCKSTATE(); } if (ret == 0) ret = nfsrv_delegconflict(tstp, &haslock, p, vp); if (ret) { /* * nfsrv_delegconflict unlocks state when it * returns non-zero, which it always does. */ if (other_lop) { FREE((caddr_t)other_lop, M_NFSDLOCK); other_lop = NULL; } if (ret == -1) { lckstp = NULL; goto tryagain; } error = ret; goto out; } /* Never gets here. */ } tstp = nstp; } /* * Handle the unlock case by calling nfsrv_updatelock(). * (Should I have done some access checking above for unlock? For now, * just let it happen.) */ if (new_stp->ls_flags & NFSLCK_UNLOCK) { first = new_lop->lo_first; end = new_lop->lo_end; nfsrv_updatelock(stp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(stp->ls_stateid.seqid); if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) stateidp->seqid = stp->ls_stateid.seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; if (filestruct_locked != 0) { NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl5"); vnode_unlocked = 1; NFSVOPUNLOCK(vp, 0); } /* Update the local locks. */ nfsrv_localunlock(vp, lfp, first, end, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); } NFSUNLOCKSTATE(); goto out; } /* * Search for a conflicting lock. A lock conflicts if: * - the lock range overlaps and * - at least one lock is a write lock and * - it is not owned by the same lock owner */ if (!delegation) { LIST_FOREACH(lop, &lfp->lf_lock, lo_lckfile) { if (new_lop->lo_end > lop->lo_first && new_lop->lo_first < lop->lo_end && (new_lop->lo_flags == NFSLCK_WRITE || lop->lo_flags == NFSLCK_WRITE) && lckstp != lop->lo_stp && (clp != lop->lo_stp->ls_clp || lckstp->ls_ownerlen != lop->lo_stp->ls_ownerlen || NFSBCMP(lckstp->ls_owner, lop->lo_stp->ls_owner, lckstp->ls_ownerlen))) { if (other_lop) { FREE((caddr_t)other_lop, M_NFSDLOCK); other_lop = NULL; } if (vnode_unlocked != 0) ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock, NULL, p); else ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock, vp, p); if (ret == 1) { if (filestruct_locked != 0) { if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl6"); NFSVOPUNLOCK(vp, 0); } /* Roll back local locks. */ nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); NFSUNLOCKSTATE(); NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); vnode_unlocked = 0; if ((vp->v_iflag & VI_DOOMED) != 0) { error = NFSERR_SERVERFAULT; goto out; } } /* * nfsrv_clientconflict() unlocks state when it * returns non-zero. */ lckstp = NULL; goto tryagain; } /* * Found a conflicting lock, so record the conflict and * return the error. */ if (cfp != NULL && ret == 0) { cfp->cl_clientid.lval[0]=lop->lo_stp->ls_stateid.other[0]; cfp->cl_clientid.lval[1]=lop->lo_stp->ls_stateid.other[1]; cfp->cl_first = lop->lo_first; cfp->cl_end = lop->lo_end; cfp->cl_flags = lop->lo_flags; cfp->cl_ownerlen = lop->lo_stp->ls_ownerlen; NFSBCOPY(lop->lo_stp->ls_owner, cfp->cl_owner, cfp->cl_ownerlen); } if (ret == 2) error = NFSERR_PERM; else if (new_stp->ls_flags & NFSLCK_RECLAIM) error = NFSERR_RECLAIMCONFLICT; else if (new_stp->ls_flags & NFSLCK_CHECK) error = NFSERR_LOCKED; else error = NFSERR_DENIED; if (filestruct_locked != 0 && ret == 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); if (vnode_unlocked == 0) { ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl7"); vnode_unlocked = 1; NFSVOPUNLOCK(vp, 0); } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); } if (ret == 0) NFSUNLOCKSTATE(); goto out; } } } /* * We only get here if there was no lock that conflicted. */ if (new_stp->ls_flags & (NFSLCK_TEST | NFSLCK_CHECK)) { NFSUNLOCKSTATE(); goto out; } /* * We only get here when we are creating or modifying a lock. * There are two variants: * - exist_lock_owner where lock_owner exists * - open_to_lock_owner with new lock_owner */ first = new_lop->lo_first; end = new_lop->lo_end; lock_flags = new_lop->lo_flags; if (!(new_stp->ls_flags & NFSLCK_OPENTOLOCK)) { nfsrv_updatelock(lckstp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(lckstp->ls_stateid.seqid); if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) stateidp->seqid = lckstp->ls_stateid.seqid = 1; stateidp->other[0] = lckstp->ls_stateid.other[0]; stateidp->other[1] = lckstp->ls_stateid.other[1]; stateidp->other[2] = lckstp->ls_stateid.other[2]; } else { /* * The new open_to_lock_owner case. * Link the new nfsstate into the lists. */ new_stp->ls_seq = new_stp->ls_opentolockseq; nfsrvd_refcache(new_stp->ls_op); stateidp->seqid = new_stp->ls_stateid.seqid = 1; stateidp->other[0] = new_stp->ls_stateid.other[0] = clp->lc_clientid.lval[0]; stateidp->other[1] = new_stp->ls_stateid.other[1] = clp->lc_clientid.lval[1]; stateidp->other[2] = new_stp->ls_stateid.other[2] = nfsrv_nextstateindex(clp); new_stp->ls_clp = clp; LIST_INIT(&new_stp->ls_lock); new_stp->ls_openstp = stp; new_stp->ls_lfp = lfp; nfsrv_insertlock(new_lop, (struct nfslock *)new_stp, new_stp, lfp); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_stp->ls_stateid), new_stp, ls_hash); LIST_INSERT_HEAD(&stp->ls_open, new_stp, ls_list); *new_lopp = NULL; *new_stpp = NULL; nfsstatsv1.srvlockowners++; nfsrv_openpluslock++; } if (filestruct_locked != 0) { NFSUNLOCKSTATE(); nfsrv_locallock_commit(lfp, lock_flags, first, end); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); } NFSUNLOCKSTATE(); out: if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } if (vnode_unlocked != 0) { NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0) error = NFSERR_SERVERFAULT; } if (other_lop) FREE((caddr_t)other_lop, M_NFSDLOCK); NFSEXITCODE2(error, nd); return (error); } /* * Check for state errors for Open. * repstat is passed back out as an error if more critical errors * are not detected. */ APPLESTATIC int nfsrv_opencheck(nfsquad_t clientid, nfsv4stateid_t *stateidp, struct nfsstate *new_stp, vnode_t vp, struct nfsrv_descript *nd, NFSPROC_T *p, int repstat) { struct nfsstate *stp, *nstp; struct nfsclient *clp; struct nfsstate *ownerstp; struct nfslockfile *lfp, *new_lfp; int error = 0, haslock = 0, ret, readonly = 0, getfhret = 0; if ((new_stp->ls_flags & NFSLCK_SHAREBITS) == NFSLCK_READACCESS) readonly = 1; /* * Check for restart conditions (client and server). */ error = nfsrv_checkrestart(clientid, new_stp->ls_flags, &new_stp->ls_stateid, 0); if (error) goto out; /* * Check for state resource limit exceeded. * Technically this should be SMP protected, but the worst * case error is "out by one or two" on the count when it * returns NFSERR_RESOURCE and the limit is just a rather * arbitrary high water mark, so no harm is done. */ if (nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } tryagain: MALLOC(new_lfp, struct nfslockfile *, sizeof (struct nfslockfile), M_NFSDLOCKFILE, M_WAITOK); if (vp) getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, new_lfp, NULL, p); NFSLOCKSTATE(); /* * Get the nfsclient structure. */ error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Look up the open owner. See if it needs confirmation and * check the seq#, as required. */ if (!error) nfsrv_getowner(&clp->lc_open, new_stp, &ownerstp); if (!error && ownerstp) { error = nfsrv_checkseqid(nd, new_stp->ls_seq, ownerstp, new_stp->ls_op); /* * If the OpenOwner hasn't been confirmed, assume the * old one was a replay and this one is ok. * See: RFC3530 Sec. 14.2.18. */ if (error == NFSERR_BADSEQID && (ownerstp->ls_flags & NFSLCK_NEEDSCONFIRM)) error = 0; } /* * Check for grace. */ if (!error) error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; /* * If none of the above errors occurred, let repstat be * returned. */ if (repstat && !error) error = repstat; if (error) { NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } free((caddr_t)new_lfp, M_NFSDLOCKFILE); goto out; } /* * If vp == NULL, the file doesn't exist yet, so return ok. * (This always happens on the first pass, so haslock must be 0.) */ if (vp == NULL) { NFSUNLOCKSTATE(); FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); goto out; } /* * Get the structure for the underlying file. */ if (getfhret) error = getfhret; else error = nfsrv_getlockfile(new_stp->ls_flags, &new_lfp, &lfp, NULL, 0); if (new_lfp) FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); if (error) { NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } goto out; } /* * Search for a conflicting open/share. */ if (new_stp->ls_flags & NFSLCK_DELEGCUR) { /* * For Delegate_Cur, search for the matching Delegation, * which indicates no conflict. * An old delegation should have been recovered by the * client doing a Claim_DELEGATE_Prev, so I won't let * it match and return NFSERR_EXPIRED. Should I let it * match? */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && (((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) || stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; } if (stp == LIST_END(&lfp->lf_deleg) || ((new_stp->ls_flags & NFSLCK_WRITEACCESS) && (stp->ls_flags & NFSLCK_DELEGREAD))) { NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } error = NFSERR_EXPIRED; goto out; } } /* * Check for access/deny bit conflicts. I check for the same * owner as well, in case the client didn't bother. */ LIST_FOREACH(stp, &lfp->lf_open, ls_file) { if (!(new_stp->ls_flags & NFSLCK_DELEGCUR) && (((new_stp->ls_flags & NFSLCK_ACCESSBITS) & ((stp->ls_flags>>NFSLCK_SHIFT) & NFSLCK_ACCESSBITS))|| ((stp->ls_flags & NFSLCK_ACCESSBITS) & ((new_stp->ls_flags>>NFSLCK_SHIFT)&NFSLCK_ACCESSBITS)))){ ret = nfsrv_clientconflict(stp->ls_clp,&haslock,vp,p); if (ret == 1) { /* * nfsrv_clientconflict() unlocks * state when it returns non-zero. */ goto tryagain; } if (ret == 2) error = NFSERR_PERM; else if (new_stp->ls_flags & NFSLCK_RECLAIM) error = NFSERR_RECLAIMCONFLICT; else error = NFSERR_SHAREDENIED; if (ret == 0) NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } goto out; } } /* * Check for a conflicting delegation. If one is found, call * nfsrv_delegconflict() to handle it. If the v4root lock hasn't * been set yet, it will get the lock. Otherwise, it will recall * the delegation. Then, we try try again... * (If NFSLCK_DELEGCUR is set, it has a delegation, so there * isn't a conflict.) * I currently believe the conflict algorithm to be: * For Open with Read Access and Deny None * - there is a conflict iff a different client has a write delegation * For Open with other Write Access or any Deny except None * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation * (The current consensus is that this last case should be * considered a conflict since the client with a read delegation * could have done an Open with ReadAccess and WriteDeny * locally and then not have checked for the WriteDeny.) * Don't check for a Reclaim, since that will be dealt with * by nfsrv_openctrl(). */ if (!(new_stp->ls_flags & (NFSLCK_DELEGPREV | NFSLCK_DELEGCUR | NFSLCK_RECLAIM))) { stp = LIST_FIRST(&lfp->lf_deleg); while (stp != LIST_END(&lfp->lf_deleg)) { nstp = LIST_NEXT(stp, ls_file); if ((readonly && stp->ls_clp != clp && (stp->ls_flags & NFSLCK_DELEGWRITE)) || (!readonly && (stp->ls_clp != clp || (stp->ls_flags & NFSLCK_DELEGREAD)))) { ret = nfsrv_delegconflict(stp, &haslock, p, vp); if (ret) { /* * nfsrv_delegconflict() unlocks state * when it returns non-zero. */ if (ret == -1) goto tryagain; error = ret; goto out; } } stp = nstp; } } NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } out: NFSEXITCODE2(error, nd); return (error); } /* * Open control function to create/update open state for an open. */ APPLESTATIC int nfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp, struct nfsstate **new_stpp, nfsquad_t clientid, nfsv4stateid_t *stateidp, nfsv4stateid_t *delegstateidp, u_int32_t *rflagsp, struct nfsexstuff *exp, NFSPROC_T *p, u_quad_t filerev) { struct nfsstate *new_stp = *new_stpp; struct nfsstate *stp, *nstp; struct nfsstate *openstp = NULL, *new_open, *ownerstp, *new_deleg; struct nfslockfile *lfp, *new_lfp; struct nfsclient *clp; int error = 0, haslock = 0, ret, delegate = 1, writedeleg = 1; int readonly = 0, cbret = 1, getfhret = 0; int gotstate = 0, len = 0; u_char *clidp = NULL; if ((new_stp->ls_flags & NFSLCK_SHAREBITS) == NFSLCK_READACCESS) readonly = 1; /* * Check for restart conditions (client and server). * (Paranoia, should have been detected by nfsrv_opencheck().) * If an error does show up, return NFSERR_EXPIRED, since the * the seqid# has already been incremented. */ error = nfsrv_checkrestart(clientid, new_stp->ls_flags, &new_stp->ls_stateid, 0); if (error) { printf("Nfsd: openctrl unexpected restart err=%d\n", error); error = NFSERR_EXPIRED; goto out; } clidp = malloc(NFSV4_OPAQUELIMIT, M_TEMP, M_WAITOK); tryagain: MALLOC(new_lfp, struct nfslockfile *, sizeof (struct nfslockfile), M_NFSDLOCKFILE, M_WAITOK); MALLOC(new_open, struct nfsstate *, sizeof (struct nfsstate), M_NFSDSTATE, M_WAITOK); MALLOC(new_deleg, struct nfsstate *, sizeof (struct nfsstate), M_NFSDSTATE, M_WAITOK); getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, new_lfp, NULL, p); NFSLOCKSTATE(); /* * Get the client structure. Since the linked lists could be changed * by other nfsd processes if this process does a tsleep(), one of * two things must be done. * 1 - don't tsleep() * or * 2 - get the nfsv4_lock() { indicated by haslock == 1 } * before using the lists, since this lock stops the other * nfsd. This should only be used for rare cases, since it * essentially single threads the nfsd. * At this time, it is only done for cases where the stable * storage file must be written prior to completion of state * expiration. */ error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error && (clp->lc_flags & LCL_NEEDSCBNULL) && clp->lc_program) { /* * This happens on the first open for a client * that supports callbacks. */ NFSUNLOCKSTATE(); /* * Although nfsrv_docallback() will sleep, clp won't * go away, since they are only removed when the * nfsv4_lock() has blocked the nfsd threads. The * fields in clp can change, but having multiple * threads do this Null callback RPC should be * harmless. */ cbret = nfsrv_docallback(clp, NFSV4PROC_CBNULL, NULL, 0, NULL, NULL, NULL, p); NFSLOCKSTATE(); clp->lc_flags &= ~LCL_NEEDSCBNULL; if (!cbret) clp->lc_flags |= LCL_CALLBACKSON; } /* * Look up the open owner. See if it needs confirmation and * check the seq#, as required. */ if (!error) nfsrv_getowner(&clp->lc_open, new_stp, &ownerstp); if (error) { NFSUNLOCKSTATE(); printf("Nfsd: openctrl unexpected state err=%d\n", error); free((caddr_t)new_lfp, M_NFSDLOCKFILE); free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } error = NFSERR_EXPIRED; goto out; } if (new_stp->ls_flags & NFSLCK_RECLAIM) nfsrv_markstable(clp); /* * Get the structure for the underlying file. */ if (getfhret) error = getfhret; else error = nfsrv_getlockfile(new_stp->ls_flags, &new_lfp, &lfp, NULL, 0); if (new_lfp) FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); if (error) { NFSUNLOCKSTATE(); printf("Nfsd openctrl unexpected getlockfile err=%d\n", error); free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } goto out; } /* * Search for a conflicting open/share. */ if (new_stp->ls_flags & NFSLCK_DELEGCUR) { /* * For Delegate_Cur, search for the matching Delegation, * which indicates no conflict. * An old delegation should have been recovered by the * client doing a Claim_DELEGATE_Prev, so I won't let * it match and return NFSERR_EXPIRED. Should I let it * match? */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && (((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) || stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; } if (stp == LIST_END(&lfp->lf_deleg) || ((new_stp->ls_flags & NFSLCK_WRITEACCESS) && (stp->ls_flags & NFSLCK_DELEGREAD))) { NFSUNLOCKSTATE(); printf("Nfsd openctrl unexpected expiry\n"); free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } error = NFSERR_EXPIRED; goto out; } /* * Don't issue a Delegation, since one already exists and * delay delegation timeout, as required. */ delegate = 0; nfsrv_delaydelegtimeout(stp); } /* * Check for access/deny bit conflicts. I also check for the * same owner, since the client might not have bothered to check. * Also, note an open for the same file and owner, if found, * which is all we do here for Delegate_Cur, since conflict * checking is already done. */ LIST_FOREACH(stp, &lfp->lf_open, ls_file) { if (ownerstp && stp->ls_openowner == ownerstp) openstp = stp; if (!(new_stp->ls_flags & NFSLCK_DELEGCUR)) { /* * If another client has the file open, the only * delegation that can be issued is a Read delegation * and only if it is a Read open with Deny none. */ if (clp != stp->ls_clp) { if ((stp->ls_flags & NFSLCK_SHAREBITS) == NFSLCK_READACCESS) writedeleg = 0; else delegate = 0; } if(((new_stp->ls_flags & NFSLCK_ACCESSBITS) & ((stp->ls_flags>>NFSLCK_SHIFT) & NFSLCK_ACCESSBITS))|| ((stp->ls_flags & NFSLCK_ACCESSBITS) & ((new_stp->ls_flags>>NFSLCK_SHIFT)&NFSLCK_ACCESSBITS))){ ret = nfsrv_clientconflict(stp->ls_clp,&haslock,vp,p); if (ret == 1) { /* * nfsrv_clientconflict() unlocks state * when it returns non-zero. */ free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); openstp = NULL; goto tryagain; } if (ret == 2) error = NFSERR_PERM; else if (new_stp->ls_flags & NFSLCK_RECLAIM) error = NFSERR_RECLAIMCONFLICT; else error = NFSERR_SHAREDENIED; if (ret == 0) NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); printf("nfsd openctrl unexpected client cnfl\n"); goto out; } } } /* * Check for a conflicting delegation. If one is found, call * nfsrv_delegconflict() to handle it. If the v4root lock hasn't * been set yet, it will get the lock. Otherwise, it will recall * the delegation. Then, we try try again... * (If NFSLCK_DELEGCUR is set, it has a delegation, so there * isn't a conflict.) * I currently believe the conflict algorithm to be: * For Open with Read Access and Deny None * - there is a conflict iff a different client has a write delegation * For Open with other Write Access or any Deny except None * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation * (The current consensus is that this last case should be * considered a conflict since the client with a read delegation * could have done an Open with ReadAccess and WriteDeny * locally and then not have checked for the WriteDeny.) */ if (!(new_stp->ls_flags & (NFSLCK_DELEGPREV | NFSLCK_DELEGCUR))) { stp = LIST_FIRST(&lfp->lf_deleg); while (stp != LIST_END(&lfp->lf_deleg)) { nstp = LIST_NEXT(stp, ls_file); if (stp->ls_clp != clp && (stp->ls_flags & NFSLCK_DELEGREAD)) writedeleg = 0; else delegate = 0; if ((readonly && stp->ls_clp != clp && (stp->ls_flags & NFSLCK_DELEGWRITE)) || (!readonly && (stp->ls_clp != clp || (stp->ls_flags & NFSLCK_DELEGREAD)))) { if (new_stp->ls_flags & NFSLCK_RECLAIM) { delegate = 2; } else { ret = nfsrv_delegconflict(stp, &haslock, p, vp); if (ret) { /* * nfsrv_delegconflict() unlocks state * when it returns non-zero. */ printf("Nfsd openctrl unexpected deleg cnfl\n"); free((caddr_t)new_open, M_NFSDSTATE); free((caddr_t)new_deleg, M_NFSDSTATE); if (ret == -1) { openstp = NULL; goto tryagain; } error = ret; goto out; } } } stp = nstp; } } /* * We only get here if there was no open that conflicted. * If an open for the owner exists, or in the access/deny bits. * Otherwise it is a new open. If the open_owner hasn't been * confirmed, replace the open with the new one needing confirmation, * otherwise add the open. */ if (new_stp->ls_flags & NFSLCK_DELEGPREV) { /* * Handle NFSLCK_DELEGPREV by searching the old delegations for * a match. If found, just move the old delegation to the current * delegation list and issue open. If not found, return * NFSERR_EXPIRED. */ LIST_FOREACH(stp, &clp->lc_olddeleg, ls_list) { if (stp->ls_lfp == lfp) { /* Found it */ if (stp->ls_clp != clp) panic("olddeleg clp"); LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_hash); stp->ls_flags &= ~NFSLCK_OLDDELEG; stp->ls_stateid.seqid = delegstateidp->seqid = 1; stp->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; stp->ls_stateid.other[1] = delegstateidp->other[1] = clp->lc_clientid.lval[1]; stp->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); stp->ls_compref = nd->nd_compref; LIST_INSERT_HEAD(&clp->lc_deleg, stp, ls_list); LIST_INSERT_HEAD(NFSSTATEHASH(clp, stp->ls_stateid), stp, ls_hash); if (stp->ls_flags & NFSLCK_DELEGWRITE) *rflagsp |= NFSV4OPEN_WRITEDELEGATE; else *rflagsp |= NFSV4OPEN_READDELEGATE; clp->lc_delegtime = NFSD_MONOSEC + nfsrv_lease + NFSRV_LEASEDELTA; /* * Now, do the associated open. */ new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); new_open->ls_flags = (new_stp->ls_flags&NFSLCK_DENYBITS)| NFSLCK_OPEN; if (stp->ls_flags & NFSLCK_DELEGWRITE) new_open->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS); else new_open->ls_flags |= NFSLCK_READACCESS; new_open->ls_uid = new_stp->ls_uid; new_open->ls_lfp = lfp; new_open->ls_clp = clp; LIST_INIT(&new_open->ls_open); LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), new_open, ls_hash); /* * and handle the open owner */ if (ownerstp) { new_open->ls_openowner = ownerstp; LIST_INSERT_HEAD(&ownerstp->ls_open,new_open,ls_list); } else { new_open->ls_openowner = new_stp; new_stp->ls_flags = 0; nfsrvd_refcache(new_stp->ls_op); new_stp->ls_noopens = 0; LIST_INIT(&new_stp->ls_open); LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); *new_stpp = NULL; nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } openstp = new_open; new_open = NULL; nfsstatsv1.srvopens++; nfsrv_openpluslock++; break; } } if (stp == LIST_END(&clp->lc_olddeleg)) error = NFSERR_EXPIRED; } else if (new_stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) { /* * Scan to see that no delegation for this client and file * doesn't already exist. * There also shouldn't yet be an Open for this file and * openowner. */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (stp->ls_clp == clp) break; } if (stp == LIST_END(&lfp->lf_deleg) && openstp == NULL) { /* * This is the Claim_Previous case with a delegation * type != Delegate_None. */ /* * First, add the delegation. (Although we must issue the * delegation, we can also ask for an immediate return.) */ new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = clp->lc_clientid.lval[1]; new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); if (new_stp->ls_flags & NFSLCK_DELEGWRITE) { new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; } else { new_deleg->ls_flags = (NFSLCK_DELEGREAD | NFSLCK_READACCESS); *rflagsp |= NFSV4OPEN_READDELEGATE; } new_deleg->ls_uid = new_stp->ls_uid; new_deleg->ls_lfp = lfp; new_deleg->ls_clp = clp; new_deleg->ls_filerev = filerev; new_deleg->ls_compref = nd->nd_compref; LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; if (delegate == 2 || nfsrv_issuedelegs == 0 || (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != LCL_CALLBACKSON || NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) || !NFSVNO_DELEGOK(vp)) *rflagsp |= NFSV4OPEN_RECALL; nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; /* * Now, do the associated open. */ new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); new_open->ls_flags = (new_stp->ls_flags & NFSLCK_DENYBITS) | NFSLCK_OPEN; if (new_stp->ls_flags & NFSLCK_DELEGWRITE) new_open->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS); else new_open->ls_flags |= NFSLCK_READACCESS; new_open->ls_uid = new_stp->ls_uid; new_open->ls_lfp = lfp; new_open->ls_clp = clp; LIST_INIT(&new_open->ls_open); LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), new_open, ls_hash); /* * and handle the open owner */ if (ownerstp) { new_open->ls_openowner = ownerstp; LIST_INSERT_HEAD(&ownerstp->ls_open, new_open, ls_list); } else { new_open->ls_openowner = new_stp; new_stp->ls_flags = 0; nfsrvd_refcache(new_stp->ls_op); new_stp->ls_noopens = 0; LIST_INIT(&new_stp->ls_open); LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); *new_stpp = NULL; nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } openstp = new_open; new_open = NULL; nfsstatsv1.srvopens++; nfsrv_openpluslock++; } else { error = NFSERR_RECLAIMCONFLICT; } } else if (ownerstp) { if (ownerstp->ls_flags & NFSLCK_NEEDSCONFIRM) { /* Replace the open */ if (ownerstp->ls_op) nfsrvd_derefcache(ownerstp->ls_op); ownerstp->ls_op = new_stp->ls_op; nfsrvd_refcache(ownerstp->ls_op); ownerstp->ls_seq = new_stp->ls_seq; *rflagsp |= NFSV4OPEN_RESULTCONFIRM; stp = LIST_FIRST(&ownerstp->ls_open); stp->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | NFSLCK_OPEN; stp->ls_stateid.seqid = 1; stp->ls_uid = new_stp->ls_uid; if (lfp != stp->ls_lfp) { LIST_REMOVE(stp, ls_file); LIST_INSERT_HEAD(&lfp->lf_open, stp, ls_file); stp->ls_lfp = lfp; } openstp = stp; } else if (openstp) { openstp->ls_flags |= (new_stp->ls_flags & NFSLCK_SHAREBITS); openstp->ls_stateid.seqid++; if ((nd->nd_flag & ND_NFSV41) != 0 && openstp->ls_stateid.seqid == 0) openstp->ls_stateid.seqid = 1; /* * This is where we can choose to issue a delegation. */ if (delegate == 0 || writedeleg == 0 || NFSVNO_EXRDONLY(exp) || (readonly != 0 && nfsrv_writedelegifpos == 0) || !NFSVNO_DELEGOK(vp) || (new_stp->ls_flags & NFSLCK_WANTRDELEG) != 0 || (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != LCL_CALLBACKSON) *rflagsp |= NFSV4OPEN_WDCONTENTION; else if (nfsrv_issuedelegs == 0 || NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) *rflagsp |= NFSV4OPEN_WDRESOURCE; else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) *rflagsp |= NFSV4OPEN_WDNOTWANTED; else { new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = clp->lc_clientid.lval[1]; new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; new_deleg->ls_uid = new_stp->ls_uid; new_deleg->ls_lfp = lfp; new_deleg->ls_clp = clp; new_deleg->ls_filerev = filerev; new_deleg->ls_compref = nd->nd_compref; LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; } } else { new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); new_open->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS)| NFSLCK_OPEN; new_open->ls_uid = new_stp->ls_uid; new_open->ls_openowner = ownerstp; new_open->ls_lfp = lfp; new_open->ls_clp = clp; LIST_INIT(&new_open->ls_open); LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); LIST_INSERT_HEAD(&ownerstp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), new_open, ls_hash); openstp = new_open; new_open = NULL; nfsstatsv1.srvopens++; nfsrv_openpluslock++; /* * This is where we can choose to issue a delegation. */ if (delegate == 0 || (writedeleg == 0 && readonly == 0) || !NFSVNO_DELEGOK(vp) || (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != LCL_CALLBACKSON) *rflagsp |= NFSV4OPEN_WDCONTENTION; else if (nfsrv_issuedelegs == 0 || NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) *rflagsp |= NFSV4OPEN_WDRESOURCE; else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) *rflagsp |= NFSV4OPEN_WDNOTWANTED; else { new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = clp->lc_clientid.lval[1]; new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); if (writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos && !readonly) && + (nfsrv_writedelegifpos || !readonly) && (new_stp->ls_flags & NFSLCK_WANTRDELEG) == 0) { new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; } else { new_deleg->ls_flags = (NFSLCK_DELEGREAD | NFSLCK_READACCESS); *rflagsp |= NFSV4OPEN_READDELEGATE; } new_deleg->ls_uid = new_stp->ls_uid; new_deleg->ls_lfp = lfp; new_deleg->ls_clp = clp; new_deleg->ls_filerev = filerev; new_deleg->ls_compref = nd->nd_compref; LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; } } } else { /* * New owner case. Start the open_owner sequence with a * Needs confirmation (unless a reclaim) and hang the * new open off it. */ new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); new_open->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | NFSLCK_OPEN; new_open->ls_uid = new_stp->ls_uid; LIST_INIT(&new_open->ls_open); new_open->ls_openowner = new_stp; new_open->ls_lfp = lfp; new_open->ls_clp = clp; LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); if (new_stp->ls_flags & NFSLCK_RECLAIM) { new_stp->ls_flags = 0; } else if ((nd->nd_flag & ND_NFSV41) != 0) { /* NFSv4.1 never needs confirmation. */ new_stp->ls_flags = 0; /* * This is where we can choose to issue a delegation. */ if (delegate && nfsrv_issuedelegs && (writedeleg || readonly) && (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == LCL_CALLBACKSON && !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && NFSVNO_DELEGOK(vp) && ((nd->nd_flag & ND_NFSV41) == 0 || (new_stp->ls_flags & NFSLCK_WANTNODELEG) == 0)) { new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = clp->lc_clientid.lval[1]; new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); if (writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos && !readonly) && + (nfsrv_writedelegifpos || !readonly) && ((nd->nd_flag & ND_NFSV41) == 0 || (new_stp->ls_flags & NFSLCK_WANTRDELEG) == 0)) { new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; } else { new_deleg->ls_flags = (NFSLCK_DELEGREAD | NFSLCK_READACCESS); *rflagsp |= NFSV4OPEN_READDELEGATE; } new_deleg->ls_uid = new_stp->ls_uid; new_deleg->ls_lfp = lfp; new_deleg->ls_clp = clp; new_deleg->ls_filerev = filerev; new_deleg->ls_compref = nd->nd_compref; LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; } /* * Since NFSv4.1 never does an OpenConfirm, the first * open state will be acquired here. */ if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) { clp->lc_flags |= LCL_STAMPEDSTABLE; len = clp->lc_idlen; NFSBCOPY(clp->lc_id, clidp, len); gotstate = 1; } } else { *rflagsp |= NFSV4OPEN_RESULTCONFIRM; new_stp->ls_flags = NFSLCK_NEEDSCONFIRM; } nfsrvd_refcache(new_stp->ls_op); new_stp->ls_noopens = 0; LIST_INIT(&new_stp->ls_open); LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), new_open, ls_hash); openstp = new_open; new_open = NULL; *new_stpp = NULL; nfsstatsv1.srvopens++; nfsrv_openpluslock++; nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } if (!error) { stateidp->seqid = openstp->ls_stateid.seqid; stateidp->other[0] = openstp->ls_stateid.other[0]; stateidp->other[1] = openstp->ls_stateid.other[1]; stateidp->other[2] = openstp->ls_stateid.other[2]; } NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } if (new_open) FREE((caddr_t)new_open, M_NFSDSTATE); if (new_deleg) FREE((caddr_t)new_deleg, M_NFSDSTATE); /* * If the NFSv4.1 client just acquired its first open, write a timestamp * to the stable storage file. */ if (gotstate != 0) { nfsrv_writestable(clidp, len, NFSNST_NEWSTATE, p); nfsrv_backupstable(); } out: free(clidp, M_TEMP); NFSEXITCODE2(error, nd); return (error); } /* * Open update. Does the confirm, downgrade and close. */ APPLESTATIC int nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, nfsv4stateid_t *stateidp, struct nfsrv_descript *nd, NFSPROC_T *p) { struct nfsstate *stp, *ownerstp; struct nfsclient *clp; struct nfslockfile *lfp; u_int32_t bits; int error = 0, gotstate = 0, len = 0; u_char *clidp = NULL; /* * Check for restart conditions (client and server). */ error = nfsrv_checkrestart(clientid, new_stp->ls_flags, &new_stp->ls_stateid, 0); if (error) goto out; clidp = malloc(NFSV4_OPAQUELIMIT, M_TEMP, M_WAITOK); NFSLOCKSTATE(); /* * Get the open structure via clientid and stateid. */ error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error) error = nfsrv_getstate(clp, &new_stp->ls_stateid, new_stp->ls_flags, &stp); /* * Sanity check the open. */ if (!error && (!(stp->ls_flags & NFSLCK_OPEN) || (!(new_stp->ls_flags & NFSLCK_CONFIRM) && (stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM)) || ((new_stp->ls_flags & NFSLCK_CONFIRM) && (!(stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM))))) error = NFSERR_BADSTATEID; if (!error) error = nfsrv_checkseqid(nd, new_stp->ls_seq, stp->ls_openowner, new_stp->ls_op); if (!error && stp->ls_stateid.seqid != new_stp->ls_stateid.seqid && (((nd->nd_flag & ND_NFSV41) == 0 && !(new_stp->ls_flags & NFSLCK_CONFIRM)) || ((nd->nd_flag & ND_NFSV41) != 0 && new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; if (!error && vnode_vtype(vp) != VREG) { if (vnode_vtype(vp) == VDIR) error = NFSERR_ISDIR; else error = NFSERR_INVAL; } if (error) { /* * If a client tries to confirm an Open with a bad * seqid# and there are no byte range locks or other Opens * on the openowner, just throw it away, so the next use of the * openowner will start a fresh seq#. */ if (error == NFSERR_BADSEQID && (new_stp->ls_flags & NFSLCK_CONFIRM) && nfsrv_nootherstate(stp)) nfsrv_freeopenowner(stp->ls_openowner, 0, p); NFSUNLOCKSTATE(); goto out; } /* * Set the return stateid. */ stateidp->seqid = stp->ls_stateid.seqid + 1; if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) stateidp->seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; /* * Now, handle the three cases. */ if (new_stp->ls_flags & NFSLCK_CONFIRM) { /* * If the open doesn't need confirmation, it seems to me that * there is a client error, but I'll just log it and keep going? */ if (!(stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM)) printf("Nfsv4d: stray open confirm\n"); stp->ls_openowner->ls_flags = 0; stp->ls_stateid.seqid++; if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 0) stp->ls_stateid.seqid = 1; if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) { clp->lc_flags |= LCL_STAMPEDSTABLE; len = clp->lc_idlen; NFSBCOPY(clp->lc_id, clidp, len); gotstate = 1; } NFSUNLOCKSTATE(); } else if (new_stp->ls_flags & NFSLCK_CLOSE) { ownerstp = stp->ls_openowner; lfp = stp->ls_lfp; if (nfsrv_dolocallocks != 0 && !LIST_EMPTY(&stp->ls_open)) { /* Get the lf lock */ nfsrv_locklf(lfp); NFSUNLOCKSTATE(); ASSERT_VOP_ELOCKED(vp, "nfsrv_openupdate"); NFSVOPUNLOCK(vp, 0); if (nfsrv_freeopen(stp, vp, 1, p) == 0) { NFSLOCKSTATE(); nfsrv_unlocklf(lfp); NFSUNLOCKSTATE(); } NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); } else { (void) nfsrv_freeopen(stp, NULL, 0, p); NFSUNLOCKSTATE(); } } else { /* * Update the share bits, making sure that the new set are a * subset of the old ones. */ bits = (new_stp->ls_flags & NFSLCK_SHAREBITS); if (~(stp->ls_flags) & bits) { NFSUNLOCKSTATE(); error = NFSERR_INVAL; goto out; } stp->ls_flags = (bits | NFSLCK_OPEN); stp->ls_stateid.seqid++; if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 0) stp->ls_stateid.seqid = 1; NFSUNLOCKSTATE(); } /* * If the client just confirmed its first open, write a timestamp * to the stable storage file. */ if (gotstate != 0) { nfsrv_writestable(clidp, len, NFSNST_NEWSTATE, p); nfsrv_backupstable(); } out: free(clidp, M_TEMP); NFSEXITCODE2(error, nd); return (error); } /* * Delegation update. Does the purge and return. */ APPLESTATIC int nfsrv_delegupdate(struct nfsrv_descript *nd, nfsquad_t clientid, nfsv4stateid_t *stateidp, vnode_t vp, int op, struct ucred *cred, NFSPROC_T *p) { struct nfsstate *stp; struct nfsclient *clp; int error = 0; fhandle_t fh; /* * Do a sanity check against the file handle for DelegReturn. */ if (vp) { error = nfsvno_getfh(vp, &fh, p); if (error) goto out; } /* * Check for restart conditions (client and server). */ if (op == NFSV4OP_DELEGRETURN) error = nfsrv_checkrestart(clientid, NFSLCK_DELEGRETURN, stateidp, 0); else error = nfsrv_checkrestart(clientid, NFSLCK_DELEGPURGE, stateidp, 0); NFSLOCKSTATE(); /* * Get the open structure via clientid and stateid. */ if (!error) error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error) { if (error == NFSERR_CBPATHDOWN) error = 0; if (error == NFSERR_STALECLIENTID && op == NFSV4OP_DELEGRETURN) error = NFSERR_STALESTATEID; } if (!error && op == NFSV4OP_DELEGRETURN) { error = nfsrv_getstate(clp, stateidp, NFSLCK_DELEGRETURN, &stp); if (!error && stp->ls_stateid.seqid != stateidp->seqid && ((nd->nd_flag & ND_NFSV41) == 0 || stateidp->seqid != 0)) error = NFSERR_OLDSTATEID; } /* * NFSERR_EXPIRED means that the state has gone away, * so Delegations have been purged. Just return ok. */ if (error == NFSERR_EXPIRED && op == NFSV4OP_DELEGPURGE) { NFSUNLOCKSTATE(); error = 0; goto out; } if (error) { NFSUNLOCKSTATE(); goto out; } if (op == NFSV4OP_DELEGRETURN) { if (NFSBCMP((caddr_t)&fh, (caddr_t)&stp->ls_lfp->lf_fh, sizeof (fhandle_t))) { NFSUNLOCKSTATE(); error = NFSERR_BADSTATEID; goto out; } nfsrv_freedeleg(stp); } else { nfsrv_freedeleglist(&clp->lc_olddeleg); } NFSUNLOCKSTATE(); error = 0; out: NFSEXITCODE(error); return (error); } /* * Release lock owner. */ APPLESTATIC int nfsrv_releaselckown(struct nfsstate *new_stp, nfsquad_t clientid, NFSPROC_T *p) { struct nfsstate *stp, *nstp, *openstp, *ownstp; struct nfsclient *clp; int error = 0; /* * Check for restart conditions (client and server). */ error = nfsrv_checkrestart(clientid, new_stp->ls_flags, &new_stp->ls_stateid, 0); if (error) goto out; NFSLOCKSTATE(); /* * Get the lock owner by name. */ error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, NULL, p); if (error) { NFSUNLOCKSTATE(); goto out; } LIST_FOREACH(ownstp, &clp->lc_open, ls_list) { LIST_FOREACH(openstp, &ownstp->ls_open, ls_list) { stp = LIST_FIRST(&openstp->ls_open); while (stp != LIST_END(&openstp->ls_open)) { nstp = LIST_NEXT(stp, ls_list); /* * If the owner matches, check for locks and * then free or return an error. */ if (stp->ls_ownerlen == new_stp->ls_ownerlen && !NFSBCMP(stp->ls_owner, new_stp->ls_owner, stp->ls_ownerlen)){ if (LIST_EMPTY(&stp->ls_lock)) { nfsrv_freelockowner(stp, NULL, 0, p); } else { NFSUNLOCKSTATE(); error = NFSERR_LOCKSHELD; goto out; } } stp = nstp; } } } NFSUNLOCKSTATE(); out: NFSEXITCODE(error); return (error); } /* * Get the file handle for a lock structure. */ static int nfsrv_getlockfh(vnode_t vp, u_short flags, struct nfslockfile *new_lfp, fhandle_t *nfhp, NFSPROC_T *p) { fhandle_t *fhp = NULL; int error; /* * For lock, use the new nfslock structure, otherwise just * a fhandle_t on the stack. */ if (flags & NFSLCK_OPEN) { KASSERT(new_lfp != NULL, ("nfsrv_getlockfh: new_lfp NULL")); fhp = &new_lfp->lf_fh; } else if (nfhp) { fhp = nfhp; } else { panic("nfsrv_getlockfh"); } error = nfsvno_getfh(vp, fhp, p); NFSEXITCODE(error); return (error); } /* * Get an nfs lock structure. Allocate one, as required, and return a * pointer to it. * Returns an NFSERR_xxx upon failure or -1 to indicate no current lock. */ static int nfsrv_getlockfile(u_short flags, struct nfslockfile **new_lfpp, struct nfslockfile **lfpp, fhandle_t *nfhp, int lockit) { struct nfslockfile *lfp; fhandle_t *fhp = NULL, *tfhp; struct nfslockhashhead *hp; struct nfslockfile *new_lfp = NULL; /* * For lock, use the new nfslock structure, otherwise just * a fhandle_t on the stack. */ if (flags & NFSLCK_OPEN) { new_lfp = *new_lfpp; fhp = &new_lfp->lf_fh; } else if (nfhp) { fhp = nfhp; } else { panic("nfsrv_getlockfile"); } hp = NFSLOCKHASH(fhp); LIST_FOREACH(lfp, hp, lf_hash) { tfhp = &lfp->lf_fh; if (NFSVNO_CMPFH(fhp, tfhp)) { if (lockit) nfsrv_locklf(lfp); *lfpp = lfp; return (0); } } if (!(flags & NFSLCK_OPEN)) return (-1); /* * No match, so chain the new one into the list. */ LIST_INIT(&new_lfp->lf_open); LIST_INIT(&new_lfp->lf_lock); LIST_INIT(&new_lfp->lf_deleg); LIST_INIT(&new_lfp->lf_locallock); LIST_INIT(&new_lfp->lf_rollback); new_lfp->lf_locallock_lck.nfslock_usecnt = 0; new_lfp->lf_locallock_lck.nfslock_lock = 0; new_lfp->lf_usecount = 0; LIST_INSERT_HEAD(hp, new_lfp, lf_hash); *lfpp = new_lfp; *new_lfpp = NULL; return (0); } /* * This function adds a nfslock lock structure to the list for the associated * nfsstate and nfslockfile structures. It will be inserted after the * entry pointed at by insert_lop. */ static void nfsrv_insertlock(struct nfslock *new_lop, struct nfslock *insert_lop, struct nfsstate *stp, struct nfslockfile *lfp) { struct nfslock *lop, *nlop; new_lop->lo_stp = stp; new_lop->lo_lfp = lfp; if (stp != NULL) { /* Insert in increasing lo_first order */ lop = LIST_FIRST(&lfp->lf_lock); if (lop == LIST_END(&lfp->lf_lock) || new_lop->lo_first <= lop->lo_first) { LIST_INSERT_HEAD(&lfp->lf_lock, new_lop, lo_lckfile); } else { nlop = LIST_NEXT(lop, lo_lckfile); while (nlop != LIST_END(&lfp->lf_lock) && nlop->lo_first < new_lop->lo_first) { lop = nlop; nlop = LIST_NEXT(lop, lo_lckfile); } LIST_INSERT_AFTER(lop, new_lop, lo_lckfile); } } else { new_lop->lo_lckfile.le_prev = NULL; /* list not used */ } /* * Insert after insert_lop, which is overloaded as stp or lfp for * an empty list. */ if (stp == NULL && (struct nfslockfile *)insert_lop == lfp) LIST_INSERT_HEAD(&lfp->lf_locallock, new_lop, lo_lckowner); else if ((struct nfsstate *)insert_lop == stp) LIST_INSERT_HEAD(&stp->ls_lock, new_lop, lo_lckowner); else LIST_INSERT_AFTER(insert_lop, new_lop, lo_lckowner); if (stp != NULL) { nfsstatsv1.srvlocks++; nfsrv_openpluslock++; } } /* * This function updates the locking for a lock owner and given file. It * maintains a list of lock ranges ordered on increasing file offset that * are NFSLCK_READ or NFSLCK_WRITE and non-overlapping (aka POSIX style). * It always adds new_lop to the list and sometimes uses the one pointed * at by other_lopp. */ static void nfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, struct nfslock **other_lopp, struct nfslockfile *lfp) { struct nfslock *new_lop = *new_lopp; struct nfslock *lop, *tlop, *ilop; struct nfslock *other_lop = *other_lopp; int unlock = 0, myfile = 0; u_int64_t tmp; /* * Work down the list until the lock is merged. */ if (new_lop->lo_flags & NFSLCK_UNLOCK) unlock = 1; if (stp != NULL) { ilop = (struct nfslock *)stp; lop = LIST_FIRST(&stp->ls_lock); } else { ilop = (struct nfslock *)lfp; lop = LIST_FIRST(&lfp->lf_locallock); } while (lop != NULL) { /* * Only check locks for this file that aren't before the start of * new lock's range. */ if (lop->lo_lfp == lfp) { myfile = 1; if (lop->lo_end >= new_lop->lo_first) { if (new_lop->lo_end < lop->lo_first) { /* * If the new lock ends before the start of the * current lock's range, no merge, just insert * the new lock. */ break; } if (new_lop->lo_flags == lop->lo_flags || (new_lop->lo_first <= lop->lo_first && new_lop->lo_end >= lop->lo_end)) { /* * This lock can be absorbed by the new lock/unlock. * This happens when it covers the entire range * of the old lock or is contiguous * with the old lock and is of the same type or an * unlock. */ if (lop->lo_first < new_lop->lo_first) new_lop->lo_first = lop->lo_first; if (lop->lo_end > new_lop->lo_end) new_lop->lo_end = lop->lo_end; tlop = lop; lop = LIST_NEXT(lop, lo_lckowner); nfsrv_freenfslock(tlop); continue; } /* * All these cases are for contiguous locks that are not the * same type, so they can't be merged. */ if (new_lop->lo_first <= lop->lo_first) { /* * This case is where the new lock overlaps with the * first part of the old lock. Move the start of the * old lock to just past the end of the new lock. The * new lock will be inserted in front of the old, since * ilop hasn't been updated. (We are done now.) */ lop->lo_first = new_lop->lo_end; break; } if (new_lop->lo_end >= lop->lo_end) { /* * This case is where the new lock overlaps with the * end of the old lock's range. Move the old lock's * end to just before the new lock's first and insert * the new lock after the old lock. * Might not be done yet, since the new lock could * overlap further locks with higher ranges. */ lop->lo_end = new_lop->lo_first; ilop = lop; lop = LIST_NEXT(lop, lo_lckowner); continue; } /* * The final case is where the new lock's range is in the * middle of the current lock's and splits the current lock * up. Use *other_lopp to handle the second part of the * split old lock range. (We are done now.) * For unlock, we use new_lop as other_lop and tmp, since * other_lop and new_lop are the same for this case. * We noted the unlock case above, so we don't need * new_lop->lo_flags any longer. */ tmp = new_lop->lo_first; if (other_lop == NULL) { if (!unlock) panic("nfsd srv update unlock"); other_lop = new_lop; *new_lopp = NULL; } other_lop->lo_first = new_lop->lo_end; other_lop->lo_end = lop->lo_end; other_lop->lo_flags = lop->lo_flags; other_lop->lo_stp = stp; other_lop->lo_lfp = lfp; lop->lo_end = tmp; nfsrv_insertlock(other_lop, lop, stp, lfp); *other_lopp = NULL; ilop = lop; break; } } ilop = lop; lop = LIST_NEXT(lop, lo_lckowner); if (myfile && (lop == NULL || lop->lo_lfp != lfp)) break; } /* * Insert the new lock in the list at the appropriate place. */ if (!unlock) { nfsrv_insertlock(new_lop, ilop, stp, lfp); *new_lopp = NULL; } } /* * This function handles sequencing of locks, etc. * It returns an error that indicates what the caller should do. */ static int nfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, struct nfsstate *stp, struct nfsrvcache *op) { int error = 0; if ((nd->nd_flag & ND_NFSV41) != 0) /* NFSv4.1 ignores the open_seqid and lock_seqid. */ goto out; if (op != nd->nd_rp) panic("nfsrvstate checkseqid"); if (!(op->rc_flag & RC_INPROG)) panic("nfsrvstate not inprog"); if (stp->ls_op && stp->ls_op->rc_refcnt <= 0) { printf("refcnt=%d\n", stp->ls_op->rc_refcnt); panic("nfsrvstate op refcnt"); } if ((stp->ls_seq + 1) == seqid) { if (stp->ls_op) nfsrvd_derefcache(stp->ls_op); stp->ls_op = op; nfsrvd_refcache(op); stp->ls_seq = seqid; goto out; } else if (stp->ls_seq == seqid && stp->ls_op && op->rc_xid == stp->ls_op->rc_xid && op->rc_refcnt == 0 && op->rc_reqlen == stp->ls_op->rc_reqlen && op->rc_cksum == stp->ls_op->rc_cksum) { if (stp->ls_op->rc_flag & RC_INPROG) { error = NFSERR_DONTREPLY; goto out; } nd->nd_rp = stp->ls_op; nd->nd_rp->rc_flag |= RC_INPROG; nfsrvd_delcache(op); error = NFSERR_REPLYFROMCACHE; goto out; } error = NFSERR_BADSEQID; out: NFSEXITCODE2(error, nd); return (error); } /* * Get the client ip address for callbacks. If the strings can't be parsed, * just set lc_program to 0 to indicate no callbacks are possible. * (For cases where the address can't be parsed or is 0.0.0.0.0.0, set * the address to the client's transport address. This won't be used * for callbacks, but can be printed out by nfsstats for info.) * Return error if the xdr can't be parsed, 0 otherwise. */ APPLESTATIC int nfsrv_getclientipaddr(struct nfsrv_descript *nd, struct nfsclient *clp) { u_int32_t *tl; u_char *cp, *cp2; int i, j; struct sockaddr_in *rad, *sad; u_char protocol[5], addr[24]; int error = 0, cantparse = 0; union { u_long ival; u_char cval[4]; } ip; union { u_short sval; u_char cval[2]; } port; rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *); rad->sin_family = AF_INET; rad->sin_len = sizeof (struct sockaddr_in); rad->sin_addr.s_addr = 0; rad->sin_port = 0; clp->lc_req.nr_client = NULL; clp->lc_req.nr_lock = 0; NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); i = fxdr_unsigned(int, *tl); if (i >= 3 && i <= 4) { error = nfsrv_mtostr(nd, protocol, i); if (error) goto nfsmout; if (!strcmp(protocol, "tcp")) { clp->lc_flags |= LCL_TCPCALLBACK; clp->lc_req.nr_sotype = SOCK_STREAM; clp->lc_req.nr_soproto = IPPROTO_TCP; } else if (!strcmp(protocol, "udp")) { clp->lc_req.nr_sotype = SOCK_DGRAM; clp->lc_req.nr_soproto = IPPROTO_UDP; } else { cantparse = 1; } } else { cantparse = 1; if (i > 0) { error = nfsm_advance(nd, NFSM_RNDUP(i), -1); if (error) goto nfsmout; } } NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); i = fxdr_unsigned(int, *tl); if (i < 0) { error = NFSERR_BADXDR; goto nfsmout; } else if (i == 0) { cantparse = 1; } else if (!cantparse && i <= 23 && i >= 11) { error = nfsrv_mtostr(nd, addr, i); if (error) goto nfsmout; /* * Parse out the address fields. We expect 6 decimal numbers * separated by '.'s. */ cp = addr; i = 0; while (*cp && i < 6) { cp2 = cp; while (*cp2 && *cp2 != '.') cp2++; if (*cp2) *cp2++ = '\0'; else if (i != 5) { cantparse = 1; break; } j = nfsrv_getipnumber(cp); if (j >= 0) { if (i < 4) ip.cval[3 - i] = j; else port.cval[5 - i] = j; } else { cantparse = 1; break; } cp = cp2; i++; } if (!cantparse) { if (ip.ival != 0x0) { rad->sin_addr.s_addr = htonl(ip.ival); rad->sin_port = htons(port.sval); } else { cantparse = 1; } } } else { cantparse = 1; if (i > 0) { error = nfsm_advance(nd, NFSM_RNDUP(i), -1); if (error) goto nfsmout; } } if (cantparse) { sad = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *); rad->sin_addr.s_addr = sad->sin_addr.s_addr; rad->sin_port = 0x0; clp->lc_program = 0; } nfsmout: NFSEXITCODE2(error, nd); return (error); } /* * Turn a string of up to three decimal digits into a number. Return -1 upon * error. */ static int nfsrv_getipnumber(u_char *cp) { int i = 0, j = 0; while (*cp) { if (j > 2 || *cp < '0' || *cp > '9') return (-1); i *= 10; i += (*cp - '0'); cp++; j++; } if (i < 256) return (i); return (-1); } /* * This function checks for restart conditions. */ static int nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, nfsv4stateid_t *stateidp, int specialid) { int ret = 0; /* * First check for a server restart. Open, LockT, ReleaseLockOwner * and DelegPurge have a clientid, the rest a stateid. */ if (flags & (NFSLCK_OPEN | NFSLCK_TEST | NFSLCK_RELEASE | NFSLCK_DELEGPURGE)) { if (clientid.lval[0] != nfsrvboottime) { ret = NFSERR_STALECLIENTID; goto out; } } else if (stateidp->other[0] != nfsrvboottime && specialid == 0) { ret = NFSERR_STALESTATEID; goto out; } /* * Read, Write, Setattr and LockT can return NFSERR_GRACE and do * not use a lock/open owner seqid#, so the check can be done now. * (The others will be checked, as required, later.) */ if (!(flags & (NFSLCK_CHECK | NFSLCK_TEST))) goto out; NFSLOCKSTATE(); ret = nfsrv_checkgrace(NULL, NULL, flags); NFSUNLOCKSTATE(); out: NFSEXITCODE(ret); return (ret); } /* * Check for grace. */ static int nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, u_int32_t flags) { int error = 0; if ((nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) != 0) { if (flags & NFSLCK_RECLAIM) { error = NFSERR_NOGRACE; goto out; } } else { if (!(flags & NFSLCK_RECLAIM)) { error = NFSERR_GRACE; goto out; } if (nd != NULL && clp != NULL && (nd->nd_flag & ND_NFSV41) != 0 && (clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) { error = NFSERR_NOGRACE; goto out; } /* * If grace is almost over and we are still getting Reclaims, * extend grace a bit. */ if ((NFSD_MONOSEC + NFSRV_LEASEDELTA) > nfsrv_stablefirst.nsf_eograce) nfsrv_stablefirst.nsf_eograce = NFSD_MONOSEC + NFSRV_LEASEDELTA; } out: NFSEXITCODE(error); return (error); } /* * Do a server callback. */ static int nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p) { mbuf_t m; u_int32_t *tl; struct nfsrv_descript nfsd, *nd = &nfsd; struct ucred *cred; int error = 0; u_int32_t callback; struct nfsdsession *sep = NULL; cred = newnfs_getcred(); NFSLOCKSTATE(); /* mostly for lc_cbref++ */ if (clp->lc_flags & LCL_NEEDSCONFIRM) { NFSUNLOCKSTATE(); panic("docallb"); } clp->lc_cbref++; /* * Fill the callback program# and version into the request * structure for newnfs_connect() to use. */ clp->lc_req.nr_prog = clp->lc_program; #ifdef notnow if ((clp->lc_flags & LCL_NFSV41) != 0) clp->lc_req.nr_vers = NFSV41_CBVERS; else #endif clp->lc_req.nr_vers = NFSV4_CBVERS; /* * First, fill in some of the fields of nd and cr. */ nd->nd_flag = ND_NFSV4; if (clp->lc_flags & LCL_GSS) nd->nd_flag |= ND_KERBV; if ((clp->lc_flags & LCL_NFSV41) != 0) nd->nd_flag |= ND_NFSV41; nd->nd_repstat = 0; cred->cr_uid = clp->lc_uid; cred->cr_gid = clp->lc_gid; callback = clp->lc_callback; NFSUNLOCKSTATE(); cred->cr_ngroups = 1; /* * Get the first mbuf for the request. */ MGET(m, M_WAITOK, MT_DATA); mbuf_setlen(m, 0); nd->nd_mreq = nd->nd_mb = m; nd->nd_bpos = NFSMTOD(m, caddr_t); /* * and build the callback request. */ if (procnum == NFSV4OP_CBGETATTR) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBGETATTR, "CB Getattr", &sep); if (error != 0) { mbuf_freem(nd->nd_mreq); goto errout; } (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); (void)nfsrv_putattrbit(nd, attrbitp); } else if (procnum == NFSV4OP_CBRECALL) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBRECALL, "CB Recall", &sep); if (error != 0) { mbuf_freem(nd->nd_mreq); goto errout; } NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID); *tl++ = txdr_unsigned(stateidp->seqid); NFSBCOPY((caddr_t)stateidp->other, (caddr_t)tl, NFSX_STATEIDOTHER); tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); if (trunc) *tl = newnfs_true; else *tl = newnfs_false; (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); } else if (procnum == NFSV4PROC_CBNULL) { nd->nd_procnum = NFSV4PROC_CBNULL; if ((clp->lc_flags & LCL_NFSV41) != 0) { error = nfsv4_getcbsession(clp, &sep); if (error != 0) { mbuf_freem(nd->nd_mreq); goto errout; } } } else { error = NFSERR_SERVERFAULT; mbuf_freem(nd->nd_mreq); goto errout; } /* * Call newnfs_connect(), as required, and then newnfs_request(). */ (void) newnfs_sndlock(&clp->lc_req.nr_lock); if (clp->lc_req.nr_client == NULL) { if ((clp->lc_flags & LCL_NFSV41) != 0) error = ECONNREFUSED; else if (nd->nd_procnum == NFSV4PROC_CBNULL) error = newnfs_connect(NULL, &clp->lc_req, cred, NULL, 1); else error = newnfs_connect(NULL, &clp->lc_req, cred, NULL, 3); } newnfs_sndunlock(&clp->lc_req.nr_lock); if (!error) { if ((nd->nd_flag & ND_NFSV41) != 0) { KASSERT(sep != NULL, ("sep NULL")); if (sep->sess_cbsess.nfsess_xprt != NULL) error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, NULL, cred, clp->lc_program, clp->lc_req.nr_vers, NULL, 1, NULL, &sep->sess_cbsess); else { /* * This should probably never occur, but if a * client somehow does an RPC without a * SequenceID Op that causes a callback just * after the nfsd threads have been terminated * and restared we could conceivably get here * without a backchannel xprt. */ printf("nfsrv_docallback: no xprt\n"); error = ECONNREFUSED; } nfsrv_freesession(sep, NULL); } else error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, NULL, cred, clp->lc_program, clp->lc_req.nr_vers, NULL, 1, NULL, NULL); } errout: NFSFREECRED(cred); /* * If error is set here, the Callback path isn't working * properly, so twiddle the appropriate LCL_ flags. * (nd_repstat != 0 indicates the Callback path is working, * but the callback failed on the client.) */ if (error) { /* * Mark the callback pathway down, which disabled issuing * of delegations and gets Renew to return NFSERR_CBPATHDOWN. */ NFSLOCKSTATE(); clp->lc_flags |= LCL_CBDOWN; NFSUNLOCKSTATE(); } else { /* * Callback worked. If the callback path was down, disable * callbacks, so no more delegations will be issued. (This * is done on the assumption that the callback pathway is * flakey.) */ NFSLOCKSTATE(); if (clp->lc_flags & LCL_CBDOWN) clp->lc_flags &= ~(LCL_CBDOWN | LCL_CALLBACKSON); NFSUNLOCKSTATE(); if (nd->nd_repstat) error = nd->nd_repstat; else if (error == 0 && procnum == NFSV4OP_CBGETATTR) error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p, NULL); mbuf_freem(nd->nd_mrep); } NFSLOCKSTATE(); clp->lc_cbref--; if ((clp->lc_flags & LCL_WAKEUPWANTED) && clp->lc_cbref == 0) { clp->lc_flags &= ~LCL_WAKEUPWANTED; wakeup(clp); } NFSUNLOCKSTATE(); NFSEXITCODE(error); return (error); } /* * Set up the compound RPC for the callback. */ static int nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, uint32_t callback, int op, const char *optag, struct nfsdsession **sepp) { uint32_t *tl; int error, len; len = strlen(optag); (void)nfsm_strtom(nd, optag, len); NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED); if ((nd->nd_flag & ND_NFSV41) != 0) { *tl++ = txdr_unsigned(NFSV41_MINORVERSION); *tl++ = txdr_unsigned(callback); *tl++ = txdr_unsigned(2); *tl = txdr_unsigned(NFSV4OP_CBSEQUENCE); error = nfsv4_setcbsequence(nd, clp, 1, sepp); if (error != 0) return (error); NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(op); } else { *tl++ = txdr_unsigned(NFSV4_MINORVERSION); *tl++ = txdr_unsigned(callback); *tl++ = txdr_unsigned(1); *tl = txdr_unsigned(op); } return (0); } /* * Return the next index# for a clientid. Mostly just increment and return * the next one, but... if the 32bit unsigned does actually wrap around, * it should be rebooted. * At an average rate of one new client per second, it will wrap around in * approximately 136 years. (I think the server will have been shut * down or rebooted before then.) */ static u_int32_t nfsrv_nextclientindex(void) { static u_int32_t client_index = 0; client_index++; if (client_index != 0) return (client_index); printf("%s: out of clientids\n", __func__); return (client_index); } /* * Return the next index# for a stateid. Mostly just increment and return * the next one, but... if the 32bit unsigned does actually wrap around * (will a BSD server stay up that long?), find * new start and end values. */ static u_int32_t nfsrv_nextstateindex(struct nfsclient *clp) { struct nfsstate *stp; int i; u_int32_t canuse, min_index, max_index; if (!(clp->lc_flags & LCL_INDEXNOTOK)) { clp->lc_stateindex++; if (clp->lc_stateindex != clp->lc_statemaxindex) return (clp->lc_stateindex); } /* * Yuck, we've hit the end. * Look for a new min and max. */ min_index = 0; max_index = 0xffffffff; for (i = 0; i < nfsrv_statehashsize; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if (stp->ls_stateid.other[2] > 0x80000000) { if (stp->ls_stateid.other[2] < max_index) max_index = stp->ls_stateid.other[2]; } else { if (stp->ls_stateid.other[2] > min_index) min_index = stp->ls_stateid.other[2]; } } } /* * Yikes, highly unlikely, but I'll handle it anyhow. */ if (min_index == 0x80000000 && max_index == 0x80000001) { canuse = 0; /* * Loop around until we find an unused entry. Return that * and set LCL_INDEXNOTOK, so the search will continue next time. * (This is one of those rare cases where a goto is the * cleanest way to code the loop.) */ tryagain: for (i = 0; i < nfsrv_statehashsize; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if (stp->ls_stateid.other[2] == canuse) { canuse++; goto tryagain; } } } clp->lc_flags |= LCL_INDEXNOTOK; return (canuse); } /* * Ok to start again from min + 1. */ clp->lc_stateindex = min_index + 1; clp->lc_statemaxindex = max_index; clp->lc_flags &= ~LCL_INDEXNOTOK; return (clp->lc_stateindex); } /* * The following functions handle the stable storage file that deals with * the edge conditions described in RFC3530 Sec. 8.6.3. * The file is as follows: * - a single record at the beginning that has the lease time of the * previous server instance (before the last reboot) and the nfsrvboottime * values for the previous server boots. * These previous boot times are used to ensure that the current * nfsrvboottime does not, somehow, get set to a previous one. * (This is important so that Stale ClientIDs and StateIDs can * be recognized.) * The number of previous nfsvrboottime values precedes the list. * - followed by some number of appended records with: * - client id string * - flag that indicates it is a record revoking state via lease * expiration or similar * OR has successfully acquired state. * These structures vary in length, with the client string at the end, up * to NFSV4_OPAQUELIMIT in size. * * At the end of the grace period, the file is truncated, the first * record is rewritten with updated information and any acquired state * records for successful reclaims of state are written. * * Subsequent records are appended when the first state is issued to * a client and when state is revoked for a client. * * When reading the file in, state issued records that come later in * the file override older ones, since the append log is in cronological order. * If, for some reason, the file can't be read, the grace period is * immediately terminated and all reclaims get NFSERR_NOGRACE. */ /* * Read in the stable storage file. Called by nfssvc() before the nfsd * processes start servicing requests. */ APPLESTATIC void nfsrv_setupstable(NFSPROC_T *p) { struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; struct nfsrv_stable *sp, *nsp; struct nfst_rec *tsp; int error, i, tryagain; off_t off = 0; ssize_t aresid, len; /* * If NFSNSF_UPDATEDONE is set, this is a restart of the nfsds without * a reboot, so state has not been lost. */ if (sf->nsf_flags & NFSNSF_UPDATEDONE) return; /* * Set Grace over just until the file reads successfully. */ nfsrvboottime = time_second; LIST_INIT(&sf->nsf_head); sf->nsf_flags = (NFSNSF_GRACEOVER | NFSNSF_NEEDLOCK); sf->nsf_eograce = NFSD_MONOSEC + NFSRV_LEASEDELTA; if (sf->nsf_fp == NULL) return; error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), (caddr_t)&sf->nsf_rec, sizeof (struct nfsf_rec), off, UIO_SYSSPACE, 0, NFSFPCRED(sf->nsf_fp), &aresid, p); if (error || aresid || sf->nsf_numboots == 0 || sf->nsf_numboots > NFSNSF_MAXNUMBOOTS) return; /* * Now, read in the boottimes. */ sf->nsf_bootvals = (time_t *)malloc((sf->nsf_numboots + 1) * sizeof (time_t), M_TEMP, M_WAITOK); off = sizeof (struct nfsf_rec); error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), (caddr_t)sf->nsf_bootvals, sf->nsf_numboots * sizeof (time_t), off, UIO_SYSSPACE, 0, NFSFPCRED(sf->nsf_fp), &aresid, p); if (error || aresid) { free((caddr_t)sf->nsf_bootvals, M_TEMP); sf->nsf_bootvals = NULL; return; } /* * Make sure this nfsrvboottime is different from all recorded * previous ones. */ do { tryagain = 0; for (i = 0; i < sf->nsf_numboots; i++) { if (nfsrvboottime == sf->nsf_bootvals[i]) { nfsrvboottime++; tryagain = 1; break; } } } while (tryagain); sf->nsf_flags |= NFSNSF_OK; off += (sf->nsf_numboots * sizeof (time_t)); /* * Read through the file, building a list of records for grace * checking. * Each record is between sizeof (struct nfst_rec) and * sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1 * and is actually sizeof (struct nfst_rec) + nst_len - 1. */ tsp = (struct nfst_rec *)malloc(sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1, M_TEMP, M_WAITOK); do { error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), (caddr_t)tsp, sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1, off, UIO_SYSSPACE, 0, NFSFPCRED(sf->nsf_fp), &aresid, p); len = (sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1) - aresid; if (error || (len > 0 && (len < sizeof (struct nfst_rec) || len < (sizeof (struct nfst_rec) + tsp->len - 1)))) { /* * Yuck, the file has been corrupted, so just return * after clearing out any restart state, so the grace period * is over. */ LIST_FOREACH_SAFE(sp, &sf->nsf_head, nst_list, nsp) { LIST_REMOVE(sp, nst_list); free((caddr_t)sp, M_TEMP); } free((caddr_t)tsp, M_TEMP); sf->nsf_flags &= ~NFSNSF_OK; free((caddr_t)sf->nsf_bootvals, M_TEMP); sf->nsf_bootvals = NULL; return; } if (len > 0) { off += sizeof (struct nfst_rec) + tsp->len - 1; /* * Search the list for a matching client. */ LIST_FOREACH(sp, &sf->nsf_head, nst_list) { if (tsp->len == sp->nst_len && !NFSBCMP(tsp->client, sp->nst_client, tsp->len)) break; } if (sp == LIST_END(&sf->nsf_head)) { sp = (struct nfsrv_stable *)malloc(tsp->len + sizeof (struct nfsrv_stable) - 1, M_TEMP, M_WAITOK); NFSBCOPY((caddr_t)tsp, (caddr_t)&sp->nst_rec, sizeof (struct nfst_rec) + tsp->len - 1); LIST_INSERT_HEAD(&sf->nsf_head, sp, nst_list); } else { if (tsp->flag == NFSNST_REVOKE) sp->nst_flag |= NFSNST_REVOKE; else /* * A subsequent timestamp indicates the client * did a setclientid/confirm and any previous * revoke is no longer relevant. */ sp->nst_flag &= ~NFSNST_REVOKE; } } } while (len > 0); free((caddr_t)tsp, M_TEMP); sf->nsf_flags = NFSNSF_OK; sf->nsf_eograce = NFSD_MONOSEC + sf->nsf_lease + NFSRV_LEASEDELTA; } /* * Update the stable storage file, now that the grace period is over. */ APPLESTATIC void nfsrv_updatestable(NFSPROC_T *p) { struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; struct nfsrv_stable *sp, *nsp; int i; struct nfsvattr nva; vnode_t vp; #if defined(__FreeBSD_version) && (__FreeBSD_version >= 500000) mount_t mp = NULL; #endif int error; if (sf->nsf_fp == NULL || (sf->nsf_flags & NFSNSF_UPDATEDONE)) return; sf->nsf_flags |= NFSNSF_UPDATEDONE; /* * Ok, we need to rewrite the stable storage file. * - truncate to 0 length * - write the new first structure * - loop through the data structures, writing out any that * have timestamps older than the old boot */ if (sf->nsf_bootvals) { sf->nsf_numboots++; for (i = sf->nsf_numboots - 2; i >= 0; i--) sf->nsf_bootvals[i + 1] = sf->nsf_bootvals[i]; } else { sf->nsf_numboots = 1; sf->nsf_bootvals = (time_t *)malloc(sizeof (time_t), M_TEMP, M_WAITOK); } sf->nsf_bootvals[0] = nfsrvboottime; sf->nsf_lease = nfsrv_lease; NFSVNO_ATTRINIT(&nva); NFSVNO_SETATTRVAL(&nva, size, 0); vp = NFSFPVNODE(sf->nsf_fp); vn_start_write(vp, &mp, V_WAIT); if (NFSVOPLOCK(vp, LK_EXCLUSIVE) == 0) { error = nfsvno_setattr(vp, &nva, NFSFPCRED(sf->nsf_fp), p, NULL); NFSVOPUNLOCK(vp, 0); } else error = EPERM; vn_finished_write(mp); if (!error) error = NFSD_RDWR(UIO_WRITE, vp, (caddr_t)&sf->nsf_rec, sizeof (struct nfsf_rec), (off_t)0, UIO_SYSSPACE, IO_SYNC, NFSFPCRED(sf->nsf_fp), NULL, p); if (!error) error = NFSD_RDWR(UIO_WRITE, vp, (caddr_t)sf->nsf_bootvals, sf->nsf_numboots * sizeof (time_t), (off_t)(sizeof (struct nfsf_rec)), UIO_SYSSPACE, IO_SYNC, NFSFPCRED(sf->nsf_fp), NULL, p); free((caddr_t)sf->nsf_bootvals, M_TEMP); sf->nsf_bootvals = NULL; if (error) { sf->nsf_flags &= ~NFSNSF_OK; printf("EEK! Can't write NfsV4 stable storage file\n"); return; } sf->nsf_flags |= NFSNSF_OK; /* * Loop through the list and write out timestamp records for * any clients that successfully reclaimed state. */ LIST_FOREACH_SAFE(sp, &sf->nsf_head, nst_list, nsp) { if (sp->nst_flag & NFSNST_GOTSTATE) { nfsrv_writestable(sp->nst_client, sp->nst_len, NFSNST_NEWSTATE, p); sp->nst_clp->lc_flags |= LCL_STAMPEDSTABLE; } LIST_REMOVE(sp, nst_list); free((caddr_t)sp, M_TEMP); } nfsrv_backupstable(); } /* * Append a record to the stable storage file. */ APPLESTATIC void nfsrv_writestable(u_char *client, int len, int flag, NFSPROC_T *p) { struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; struct nfst_rec *sp; int error; if (!(sf->nsf_flags & NFSNSF_OK) || sf->nsf_fp == NULL) return; sp = (struct nfst_rec *)malloc(sizeof (struct nfst_rec) + len - 1, M_TEMP, M_WAITOK); sp->len = len; NFSBCOPY(client, sp->client, len); sp->flag = flag; error = NFSD_RDWR(UIO_WRITE, NFSFPVNODE(sf->nsf_fp), (caddr_t)sp, sizeof (struct nfst_rec) + len - 1, (off_t)0, UIO_SYSSPACE, (IO_SYNC | IO_APPEND), NFSFPCRED(sf->nsf_fp), NULL, p); free((caddr_t)sp, M_TEMP); if (error) { sf->nsf_flags &= ~NFSNSF_OK; printf("EEK! Can't write NfsV4 stable storage file\n"); } } /* * This function is called during the grace period to mark a client * that successfully reclaimed state. */ static void nfsrv_markstable(struct nfsclient *clp) { struct nfsrv_stable *sp; /* * First find the client structure. */ LIST_FOREACH(sp, &nfsrv_stablefirst.nsf_head, nst_list) { if (sp->nst_len == clp->lc_idlen && !NFSBCMP(sp->nst_client, clp->lc_id, sp->nst_len)) break; } if (sp == LIST_END(&nfsrv_stablefirst.nsf_head)) return; /* * Now, just mark it and set the nfsclient back pointer. */ sp->nst_flag |= NFSNST_GOTSTATE; sp->nst_clp = clp; } /* * This function is called for a reclaim, to see if it gets grace. * It returns 0 if a reclaim is allowed, 1 otherwise. */ static int nfsrv_checkstable(struct nfsclient *clp) { struct nfsrv_stable *sp; /* * First, find the entry for the client. */ LIST_FOREACH(sp, &nfsrv_stablefirst.nsf_head, nst_list) { if (sp->nst_len == clp->lc_idlen && !NFSBCMP(sp->nst_client, clp->lc_id, sp->nst_len)) break; } /* * If not in the list, state was revoked or no state was issued * since the previous reboot, a reclaim is denied. */ if (sp == LIST_END(&nfsrv_stablefirst.nsf_head) || (sp->nst_flag & NFSNST_REVOKE) || !(nfsrv_stablefirst.nsf_flags & NFSNSF_OK)) return (1); return (0); } /* * Test for and try to clear out a conflicting client. This is called by * nfsrv_lockctrl() and nfsrv_openctrl() when conflicts with other clients * a found. * The trick here is that it can't revoke a conflicting client with an * expired lease unless it holds the v4root lock, so... * If no v4root lock, get the lock and return 1 to indicate "try again". * Return 0 to indicate the conflict can't be revoked and 1 to indicate * the revocation worked and the conflicting client is "bye, bye", so it * can be tried again. * Return 2 to indicate that the vnode is VI_DOOMED after NFSVOPLOCK(). * Unlocks State before a non-zero value is returned. */ static int nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp, NFSPROC_T *p) { int gotlock, lktype = 0; /* * If lease hasn't expired, we can't fix it. */ if (clp->lc_expiry >= NFSD_MONOSEC || !(nfsrv_stablefirst.nsf_flags & NFSNSF_UPDATEDONE)) return (0); if (*haslockp == 0) { NFSUNLOCKSTATE(); if (vp != NULL) { lktype = NFSVOPISLOCKED(vp); NFSVOPUNLOCK(vp, 0); } NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!gotlock); NFSUNLOCKV4ROOTMUTEX(); *haslockp = 1; if (vp != NULL) { NFSVOPLOCK(vp, lktype | LK_RETRY); if ((vp->v_iflag & VI_DOOMED) != 0) return (2); } return (1); } NFSUNLOCKSTATE(); /* * Ok, we can expire the conflicting client. */ nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); nfsrv_backupstable(); nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); LIST_REMOVE(clp, lc_hash); nfsrv_zapclient(clp, p); return (1); } /* * Resolve a delegation conflict. * Returns 0 to indicate the conflict was resolved without sleeping. * Return -1 to indicate that the caller should check for conflicts again. * Return > 0 for an error that should be returned, normally NFSERR_DELAY. * * Also, manipulate the nfsv4root_lock, as required. It isn't changed * for a return of 0, since there was no sleep and it could be required * later. It is released for a return of NFSERR_DELAY, since the caller * will return that error. It is released when a sleep was done waiting * for the delegation to be returned or expire (so that other nfsds can * handle ops). Then, it must be acquired for the write to stable storage. * (This function is somewhat similar to nfsrv_clientconflict(), but * the semantics differ in a couple of subtle ways. The return of 0 * indicates the conflict was resolved without sleeping here, not * that the conflict can't be resolved and the handling of nfsv4root_lock * differs, as noted above.) * Unlocks State before returning a non-zero value. */ static int nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, vnode_t vp) { struct nfsclient *clp = stp->ls_clp; int gotlock, error, lktype = 0, retrycnt, zapped_clp; nfsv4stateid_t tstateid; fhandle_t tfh; /* * If the conflict is with an old delegation... */ if (stp->ls_flags & NFSLCK_OLDDELEG) { /* * You can delete it, if it has expired. */ if (clp->lc_delegtime < NFSD_MONOSEC) { nfsrv_freedeleg(stp); NFSUNLOCKSTATE(); error = -1; goto out; } NFSUNLOCKSTATE(); /* * During this delay, the old delegation could expire or it * could be recovered by the client via an Open with * CLAIM_DELEGATE_PREV. * Release the nfsv4root_lock, if held. */ if (*haslockp) { *haslockp = 0; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } error = NFSERR_DELAY; goto out; } /* * It's a current delegation, so: * - check to see if the delegation has expired * - if so, get the v4root lock and then expire it */ if (!(stp->ls_flags & NFSLCK_DELEGRECALL)) { /* * - do a recall callback, since not yet done * For now, never allow truncate to be set. To use * truncate safely, it must be guaranteed that the * Remove, Rename or Setattr with size of 0 will * succeed and that would require major changes to * the VFS/Vnode OPs. * Set the expiry time large enough so that it won't expire * until after the callback, then set it correctly, once * the callback is done. (The delegation will now time * out whether or not the Recall worked ok. The timeout * will be extended when ops are done on the delegation * stateid, up to the timelimit.) */ stp->ls_delegtime = NFSD_MONOSEC + (2 * nfsrv_lease) + NFSRV_LEASEDELTA; stp->ls_delegtimelimit = NFSD_MONOSEC + (6 * nfsrv_lease) + NFSRV_LEASEDELTA; stp->ls_flags |= NFSLCK_DELEGRECALL; /* * Loop NFSRV_CBRETRYCNT times while the CBRecall replies * NFSERR_BADSTATEID or NFSERR_BADHANDLE. This is done * in order to try and avoid a race that could happen * when a CBRecall request passed the Open reply with * the delegation in it when transitting the network. * Since nfsrv_docallback will sleep, don't use stp after * the call. */ NFSBCOPY((caddr_t)&stp->ls_stateid, (caddr_t)&tstateid, sizeof (tstateid)); NFSBCOPY((caddr_t)&stp->ls_lfp->lf_fh, (caddr_t)&tfh, sizeof (tfh)); NFSUNLOCKSTATE(); if (*haslockp) { *haslockp = 0; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } retrycnt = 0; do { error = nfsrv_docallback(clp, NFSV4OP_CBRECALL, &tstateid, 0, &tfh, NULL, NULL, p); retrycnt++; } while ((error == NFSERR_BADSTATEID || error == NFSERR_BADHANDLE) && retrycnt < NFSV4_CBRETRYCNT); error = NFSERR_DELAY; goto out; } if (clp->lc_expiry >= NFSD_MONOSEC && stp->ls_delegtime >= NFSD_MONOSEC) { NFSUNLOCKSTATE(); /* * A recall has been done, but it has not yet expired. * So, RETURN_DELAY. */ if (*haslockp) { *haslockp = 0; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } error = NFSERR_DELAY; goto out; } /* * If we don't yet have the lock, just get it and then return, * since we need that before deleting expired state, such as * this delegation. * When getting the lock, unlock the vnode, so other nfsds that * are in progress, won't get stuck waiting for the vnode lock. */ if (*haslockp == 0) { NFSUNLOCKSTATE(); if (vp != NULL) { lktype = NFSVOPISLOCKED(vp); NFSVOPUNLOCK(vp, 0); } NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!gotlock); NFSUNLOCKV4ROOTMUTEX(); *haslockp = 1; if (vp != NULL) { NFSVOPLOCK(vp, lktype | LK_RETRY); if ((vp->v_iflag & VI_DOOMED) != 0) { *haslockp = 0; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); error = NFSERR_PERM; goto out; } } error = -1; goto out; } NFSUNLOCKSTATE(); /* * Ok, we can delete the expired delegation. * First, write the Revoke record to stable storage and then * clear out the conflict. * Since all other nfsd threads are now blocked, we can safely * sleep without the state changing. */ nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); nfsrv_backupstable(); if (clp->lc_expiry < NFSD_MONOSEC) { nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); LIST_REMOVE(clp, lc_hash); zapped_clp = 1; } else { nfsrv_freedeleg(stp); zapped_clp = 0; } if (zapped_clp) nfsrv_zapclient(clp, p); error = -1; out: NFSEXITCODE(error); return (error); } /* * Check for a remove allowed, if remove is set to 1 and get rid of * delegations. */ APPLESTATIC int nfsrv_checkremove(vnode_t vp, int remove, NFSPROC_T *p) { struct nfsstate *stp; struct nfslockfile *lfp; int error, haslock = 0; fhandle_t nfh; /* * First, get the lock file structure. * (A return of -1 means no associated state, so remove ok.) */ error = nfsrv_getlockfh(vp, NFSLCK_CHECK, NULL, &nfh, p); tryagain: NFSLOCKSTATE(); if (!error) error = nfsrv_getlockfile(NFSLCK_CHECK, NULL, &lfp, &nfh, 0); if (error) { NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } if (error == -1) error = 0; goto out; } /* * Now, we must Recall any delegations. */ error = nfsrv_cleandeleg(vp, lfp, NULL, &haslock, p); if (error) { /* * nfsrv_cleandeleg() unlocks state for non-zero * return. */ if (error == -1) goto tryagain; if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } goto out; } /* * Now, look for a conflicting open share. */ if (remove) { /* * If the entry in the directory was the last reference to the * corresponding filesystem object, the object can be destroyed * */ if(lfp->lf_usecount>1) LIST_FOREACH(stp, &lfp->lf_open, ls_file) { if (stp->ls_flags & NFSLCK_WRITEDENY) { error = NFSERR_FILEOPEN; break; } } } NFSUNLOCKSTATE(); if (haslock) { NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } out: NFSEXITCODE(error); return (error); } /* * Clear out all delegations for the file referred to by lfp. * May return NFSERR_DELAY, if there will be a delay waiting for * delegations to expire. * Returns -1 to indicate it slept while recalling a delegation. * This function has the side effect of deleting the nfslockfile structure, * if it no longer has associated state and didn't have to sleep. * Unlocks State before a non-zero value is returned. */ static int nfsrv_cleandeleg(vnode_t vp, struct nfslockfile *lfp, struct nfsclient *clp, int *haslockp, NFSPROC_T *p) { struct nfsstate *stp, *nstp; int ret = 0; stp = LIST_FIRST(&lfp->lf_deleg); while (stp != LIST_END(&lfp->lf_deleg)) { nstp = LIST_NEXT(stp, ls_file); if (stp->ls_clp != clp) { ret = nfsrv_delegconflict(stp, haslockp, p, vp); if (ret) { /* * nfsrv_delegconflict() unlocks state * when it returns non-zero. */ goto out; } } stp = nstp; } out: NFSEXITCODE(ret); return (ret); } /* * There are certain operations that, when being done outside of NFSv4, * require that any NFSv4 delegation for the file be recalled. * This function is to be called for those cases: * VOP_RENAME() - When a delegation is being recalled for any reason, * the client may have to do Opens against the server, using the file's * final component name. If the file has been renamed on the server, * that component name will be incorrect and the Open will fail. * VOP_REMOVE() - Theoretically, a client could Open a file after it has * been removed on the server, if there is a delegation issued to * that client for the file. I say "theoretically" since clients * normally do an Access Op before the Open and that Access Op will * fail with ESTALE. Note that NFSv2 and 3 don't even do Opens, so * they will detect the file's removal in the same manner. (There is * one case where RFC3530 allows a client to do an Open without first * doing an Access Op, which is passage of a check against the ACE * returned with a Write delegation, but current practice is to ignore * the ACE and always do an Access Op.) * Since the functions can only be called with an unlocked vnode, this * can't be done at this time. * VOP_ADVLOCK() - When a client holds a delegation, it can issue byte range * locks locally in the client, which are not visible to the server. To * deal with this, issuing of delegations for a vnode must be disabled * and all delegations for the vnode recalled. This is done via the * second function, using the VV_DISABLEDELEG vflag on the vnode. */ APPLESTATIC void nfsd_recalldelegation(vnode_t vp, NFSPROC_T *p) { time_t starttime; int error; /* * First, check to see if the server is currently running and it has * been called for a regular file when issuing delegations. */ if (newnfs_numnfsd == 0 || vp->v_type != VREG || nfsrv_issuedelegs == 0) return; KASSERT((NFSVOPISLOCKED(vp) != LK_EXCLUSIVE), ("vp %p is locked", vp)); /* * First, get a reference on the nfsv4rootfs_lock so that an * exclusive lock cannot be acquired by another thread. */ NFSLOCKV4ROOTMUTEX(); nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); NFSUNLOCKV4ROOTMUTEX(); /* * Now, call nfsrv_checkremove() in a loop while it returns * NFSERR_DELAY. Return upon any other error or when timed out. */ starttime = NFSD_MONOSEC; do { if (NFSVOPLOCK(vp, LK_EXCLUSIVE) == 0) { error = nfsrv_checkremove(vp, 0, p); NFSVOPUNLOCK(vp, 0); } else error = EPERM; if (error == NFSERR_DELAY) { if (NFSD_MONOSEC - starttime > NFS_REMOVETIMEO) break; /* Sleep for a short period of time */ (void) nfs_catnap(PZERO, 0, "nfsremove"); } } while (error == NFSERR_DELAY); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); } APPLESTATIC void nfsd_disabledelegation(vnode_t vp, NFSPROC_T *p) { #ifdef VV_DISABLEDELEG /* * First, flag issuance of delegations disabled. */ atomic_set_long(&vp->v_vflag, VV_DISABLEDELEG); #endif /* * Then call nfsd_recalldelegation() to get rid of all extant * delegations. */ nfsd_recalldelegation(vp, p); } /* * Check for conflicting locks, etc. and then get rid of delegations. * (At one point I thought that I should get rid of delegations for any * Setattr, since it could potentially disallow the I/O op (read or write) * allowed by the delegation. However, Setattr Ops that aren't changing * the size get a stateid of all 0s, so you can't tell if it is a delegation * for the same client or a different one, so I decided to only get rid * of delegations for other clients when the size is being changed.) * In general, a Setattr can disable NFS I/O Ops that are outstanding, such * as Write backs, even if there is no delegation, so it really isn't any * different?) */ APPLESTATIC int nfsrv_checksetattr(vnode_t vp, struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, struct nfsvattr *nvap, nfsattrbit_t *attrbitp, struct nfsexstuff *exp, NFSPROC_T *p) { struct nfsstate st, *stp = &st; struct nfslock lo, *lop = &lo; int error = 0; nfsquad_t clientid; if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SIZE)) { stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS); lop->lo_first = nvap->na_size; } else { stp->ls_flags = 0; lop->lo_first = 0; } if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_OWNER) || NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_OWNERGROUP) || NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_MODE) || NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_ACL)) stp->ls_flags |= NFSLCK_SETATTR; if (stp->ls_flags == 0) goto out; lop->lo_end = NFS64BITSSET; lop->lo_flags = NFSLCK_WRITE; stp->ls_ownerlen = 0; stp->ls_op = NULL; stp->ls_uid = nd->nd_cred->cr_uid; stp->ls_stateid.seqid = stateidp->seqid; clientid.lval[0] = stp->ls_stateid.other[0] = stateidp->other[0]; clientid.lval[1] = stp->ls_stateid.other[1] = stateidp->other[1]; stp->ls_stateid.other[2] = stateidp->other[2]; error = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid, stateidp, exp, nd, p); out: NFSEXITCODE2(error, nd); return (error); } /* * Check for a write delegation and do a CBGETATTR if there is one, updating * the attributes, as required. * Should I return an error if I can't get the attributes? (For now, I'll * just return ok. */ APPLESTATIC int nfsrv_checkgetattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap, nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p) { struct nfsstate *stp; struct nfslockfile *lfp; struct nfsclient *clp; struct nfsvattr nva; fhandle_t nfh; int error = 0; nfsattrbit_t cbbits; u_quad_t delegfilerev; NFSCBGETATTR_ATTRBIT(attrbitp, &cbbits); if (!NFSNONZERO_ATTRBIT(&cbbits)) goto out; /* * Get the lock file structure. * (A return of -1 means no associated state, so return ok.) */ error = nfsrv_getlockfh(vp, NFSLCK_CHECK, NULL, &nfh, p); NFSLOCKSTATE(); if (!error) error = nfsrv_getlockfile(NFSLCK_CHECK, NULL, &lfp, &nfh, 0); if (error) { NFSUNLOCKSTATE(); if (error == -1) error = 0; goto out; } /* * Now, look for a write delegation. */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (stp->ls_flags & NFSLCK_DELEGWRITE) break; } if (stp == LIST_END(&lfp->lf_deleg)) { NFSUNLOCKSTATE(); goto out; } clp = stp->ls_clp; delegfilerev = stp->ls_filerev; /* * If the Write delegation was issued as a part of this Compound RPC * or if we have an Implied Clientid (used in a previous Op in this * compound) and it is the client the delegation was issued to, * just return ok. * I also assume that it is from the same client iff the network * host IP address is the same as the callback address. (Not * exactly correct by the RFC, but avoids a lot of Getattr * callbacks.) */ if (nd->nd_compref == stp->ls_compref || ((nd->nd_flag & ND_IMPLIEDCLID) && clp->lc_clientid.qval == nd->nd_clientid.qval) || nfsaddr2_match(clp->lc_req.nr_nam, nd->nd_nam)) { NFSUNLOCKSTATE(); goto out; } /* * We are now done with the delegation state structure, * so the statelock can be released and we can now tsleep(). */ /* * Now, we must do the CB Getattr callback, to see if Change or Size * has changed. */ if (clp->lc_expiry >= NFSD_MONOSEC) { NFSUNLOCKSTATE(); NFSVNO_ATTRINIT(&nva); nva.na_filerev = NFS64BITSSET; error = nfsrv_docallback(clp, NFSV4OP_CBGETATTR, NULL, 0, &nfh, &nva, &cbbits, p); if (!error) { if ((nva.na_filerev != NFS64BITSSET && nva.na_filerev > delegfilerev) || (NFSVNO_ISSETSIZE(&nva) && nva.na_size != nvap->na_size)) { error = nfsvno_updfilerev(vp, nvap, cred, p); if (NFSVNO_ISSETSIZE(&nva)) nvap->na_size = nva.na_size; } } else error = 0; /* Ignore callback errors for now. */ } else { NFSUNLOCKSTATE(); } out: NFSEXITCODE2(error, nd); return (error); } /* * This function looks for openowners that haven't had any opens for * a while and throws them away. Called by an nfsd when NFSNSF_NOOPENS * is set. */ APPLESTATIC void nfsrv_throwawayopens(NFSPROC_T *p) { struct nfsclient *clp, *nclp; struct nfsstate *stp, *nstp; int i; NFSLOCKSTATE(); nfsrv_stablefirst.nsf_flags &= ~NFSNSF_NOOPENS; /* * For each client... */ for (i = 0; i < nfsrv_clienthashsize; i++) { LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) { if (LIST_EMPTY(&stp->ls_open) && (stp->ls_noopens > NFSNOOPEN || (nfsrv_openpluslock * 2) > nfsrv_v4statelimit)) nfsrv_freeopenowner(stp, 0, p); } } } NFSUNLOCKSTATE(); } /* * This function checks to see if the credentials are the same. * Returns 1 for not same, 0 otherwise. */ static int nfsrv_notsamecredname(struct nfsrv_descript *nd, struct nfsclient *clp) { if (nd->nd_flag & ND_GSS) { if (!(clp->lc_flags & LCL_GSS)) return (1); if (clp->lc_flags & LCL_NAME) { if (nd->nd_princlen != clp->lc_namelen || NFSBCMP(nd->nd_principal, clp->lc_name, clp->lc_namelen)) return (1); else return (0); } if (nd->nd_cred->cr_uid == clp->lc_uid) return (0); else return (1); } else if (clp->lc_flags & LCL_GSS) return (1); /* * For AUTH_SYS, allow the same uid or root. (This is underspecified * in RFC3530, which talks about principals, but doesn't say anything * about uids for AUTH_SYS.) */ if (nd->nd_cred->cr_uid == clp->lc_uid || nd->nd_cred->cr_uid == 0) return (0); else return (1); } /* * Calculate the lease expiry time. */ static time_t nfsrv_leaseexpiry(void) { if (nfsrv_stablefirst.nsf_eograce > NFSD_MONOSEC) return (NFSD_MONOSEC + 2 * (nfsrv_lease + NFSRV_LEASEDELTA)); return (NFSD_MONOSEC + nfsrv_lease + NFSRV_LEASEDELTA); } /* * Delay the delegation timeout as far as ls_delegtimelimit, as required. */ static void nfsrv_delaydelegtimeout(struct nfsstate *stp) { if ((stp->ls_flags & NFSLCK_DELEGRECALL) == 0) return; if ((stp->ls_delegtime + 15) > NFSD_MONOSEC && stp->ls_delegtime < stp->ls_delegtimelimit) { stp->ls_delegtime += nfsrv_lease; if (stp->ls_delegtime > stp->ls_delegtimelimit) stp->ls_delegtime = stp->ls_delegtimelimit; } } /* * This function checks to see if there is any other state associated * with the openowner for this Open. * It returns 1 if there is no other state, 0 otherwise. */ static int nfsrv_nootherstate(struct nfsstate *stp) { struct nfsstate *tstp; LIST_FOREACH(tstp, &stp->ls_openowner->ls_open, ls_list) { if (tstp != stp || !LIST_EMPTY(&tstp->ls_lock)) return (0); } return (1); } /* * Create a list of lock deltas (changes to local byte range locking * that can be rolled back using the list) and apply the changes via * nfsvno_advlock(). Optionally, lock the list. It is expected that either * the rollback or update function will be called after this. * It returns an error (and rolls back, as required), if any nfsvno_advlock() * call fails. If it returns an error, it will unlock the list. */ static int nfsrv_locallock(vnode_t vp, struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p) { struct nfslock *lop, *nlop; int error = 0; /* Loop through the list of locks. */ lop = LIST_FIRST(&lfp->lf_locallock); while (first < end && lop != NULL) { nlop = LIST_NEXT(lop, lo_lckowner); if (first >= lop->lo_end) { /* not there yet */ lop = nlop; } else if (first < lop->lo_first) { /* new one starts before entry in list */ if (end <= lop->lo_first) { /* no overlap between old and new */ error = nfsrv_dolocal(vp, lfp, flags, NFSLCK_UNLOCK, first, end, cfp, p); if (error != 0) break; first = end; } else { /* handle fragment overlapped with new one */ error = nfsrv_dolocal(vp, lfp, flags, NFSLCK_UNLOCK, first, lop->lo_first, cfp, p); if (error != 0) break; first = lop->lo_first; } } else { /* new one overlaps this entry in list */ if (end <= lop->lo_end) { /* overlaps all of new one */ error = nfsrv_dolocal(vp, lfp, flags, lop->lo_flags, first, end, cfp, p); if (error != 0) break; first = end; } else { /* handle fragment overlapped with new one */ error = nfsrv_dolocal(vp, lfp, flags, lop->lo_flags, first, lop->lo_end, cfp, p); if (error != 0) break; first = lop->lo_end; lop = nlop; } } } if (first < end && error == 0) /* handle fragment past end of list */ error = nfsrv_dolocal(vp, lfp, flags, NFSLCK_UNLOCK, first, end, cfp, p); NFSEXITCODE(error); return (error); } /* * Local lock unlock. Unlock all byte ranges that are no longer locked * by NFSv4. To do this, unlock any subranges of first-->end that * do not overlap with the byte ranges of any lock in the lfp->lf_lock * list. This list has all locks for the file held by other * tuples. The list is ordered by increasing * lo_first value, but may have entries that overlap each other, for * the case of read locks. */ static void nfsrv_localunlock(vnode_t vp, struct nfslockfile *lfp, uint64_t init_first, uint64_t init_end, NFSPROC_T *p) { struct nfslock *lop; uint64_t first, end, prevfirst; first = init_first; end = init_end; while (first < init_end) { /* Loop through all nfs locks, adjusting first and end */ prevfirst = 0; LIST_FOREACH(lop, &lfp->lf_lock, lo_lckfile) { KASSERT(prevfirst <= lop->lo_first, ("nfsv4 locks out of order")); KASSERT(lop->lo_first < lop->lo_end, ("nfsv4 bogus lock")); prevfirst = lop->lo_first; if (first >= lop->lo_first && first < lop->lo_end) /* * Overlaps with initial part, so trim * off that initial part by moving first past * it. */ first = lop->lo_end; else if (end > lop->lo_first && lop->lo_first > first) { /* * This lock defines the end of the * segment to unlock, so set end to the * start of it and break out of the loop. */ end = lop->lo_first; break; } if (first >= end) /* * There is no segment left to do, so * break out of this loop and then exit * the outer while() since first will be set * to end, which must equal init_end here. */ break; } if (first < end) { /* Unlock this segment */ (void) nfsrv_dolocal(vp, lfp, NFSLCK_UNLOCK, NFSLCK_READ, first, end, NULL, p); nfsrv_locallock_commit(lfp, NFSLCK_UNLOCK, first, end); } /* * Now move past this segment and look for any further * segment in the range, if there is one. */ first = end; end = init_end; } } /* * Do the local lock operation and update the rollback list, as required. * Perform the rollback and return the error if nfsvno_advlock() fails. */ static int nfsrv_dolocal(vnode_t vp, struct nfslockfile *lfp, int flags, int oldflags, uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p) { struct nfsrollback *rlp; int error = 0, ltype, oldltype; if (flags & NFSLCK_WRITE) ltype = F_WRLCK; else if (flags & NFSLCK_READ) ltype = F_RDLCK; else ltype = F_UNLCK; if (oldflags & NFSLCK_WRITE) oldltype = F_WRLCK; else if (oldflags & NFSLCK_READ) oldltype = F_RDLCK; else oldltype = F_UNLCK; if (ltype == oldltype || (oldltype == F_WRLCK && ltype == F_RDLCK)) /* nothing to do */ goto out; error = nfsvno_advlock(vp, ltype, first, end, p); if (error != 0) { if (cfp != NULL) { cfp->cl_clientid.lval[0] = 0; cfp->cl_clientid.lval[1] = 0; cfp->cl_first = 0; cfp->cl_end = NFS64BITSSET; cfp->cl_flags = NFSLCK_WRITE; cfp->cl_ownerlen = 5; NFSBCOPY("LOCAL", cfp->cl_owner, 5); } nfsrv_locallock_rollback(vp, lfp, p); } else if (ltype != F_UNLCK) { rlp = malloc(sizeof (struct nfsrollback), M_NFSDROLLBACK, M_WAITOK); rlp->rlck_first = first; rlp->rlck_end = end; rlp->rlck_type = oldltype; LIST_INSERT_HEAD(&lfp->lf_rollback, rlp, rlck_list); } out: NFSEXITCODE(error); return (error); } /* * Roll back local lock changes and free up the rollback list. */ static void nfsrv_locallock_rollback(vnode_t vp, struct nfslockfile *lfp, NFSPROC_T *p) { struct nfsrollback *rlp, *nrlp; LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, nrlp) { (void) nfsvno_advlock(vp, rlp->rlck_type, rlp->rlck_first, rlp->rlck_end, p); free(rlp, M_NFSDROLLBACK); } LIST_INIT(&lfp->lf_rollback); } /* * Update local lock list and delete rollback list (ie now committed to the * local locks). Most of the work is done by the internal function. */ static void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end) { struct nfsrollback *rlp, *nrlp; struct nfslock *new_lop, *other_lop; new_lop = malloc(sizeof (struct nfslock), M_NFSDLOCK, M_WAITOK); if (flags & (NFSLCK_READ | NFSLCK_WRITE)) other_lop = malloc(sizeof (struct nfslock), M_NFSDLOCK, M_WAITOK); else other_lop = NULL; new_lop->lo_flags = flags; new_lop->lo_first = first; new_lop->lo_end = end; nfsrv_updatelock(NULL, &new_lop, &other_lop, lfp); if (new_lop != NULL) free(new_lop, M_NFSDLOCK); if (other_lop != NULL) free(other_lop, M_NFSDLOCK); /* and get rid of the rollback list */ LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, nrlp) free(rlp, M_NFSDROLLBACK); LIST_INIT(&lfp->lf_rollback); } /* * Lock the struct nfslockfile for local lock updating. */ static void nfsrv_locklf(struct nfslockfile *lfp) { int gotlock; /* lf_usecount ensures *lfp won't be free'd */ lfp->lf_usecount++; do { gotlock = nfsv4_lock(&lfp->lf_locallock_lck, 1, NULL, NFSSTATEMUTEXPTR, NULL); } while (gotlock == 0); lfp->lf_usecount--; } /* * Unlock the struct nfslockfile after local lock updating. */ static void nfsrv_unlocklf(struct nfslockfile *lfp) { nfsv4_unlock(&lfp->lf_locallock_lck, 0); } /* * Clear out all state for the NFSv4 server. * Must be called by a thread that can sleep when no nfsds are running. */ void nfsrv_throwawayallstate(NFSPROC_T *p) { struct nfsclient *clp, *nclp; struct nfslockfile *lfp, *nlfp; int i; /* * For each client, clean out the state and then free the structure. */ for (i = 0; i < nfsrv_clienthashsize; i++) { LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); free(clp->lc_stateid, M_NFSDCLIENT); free(clp, M_NFSDCLIENT); } } /* * Also, free up any remaining lock file structures. */ for (i = 0; i < nfsrv_lockhashsize; i++) { LIST_FOREACH_SAFE(lfp, &nfslockhash[i], lf_hash, nlfp) { printf("nfsd unload: fnd a lock file struct\n"); nfsrv_freenfslockfile(lfp); } } } /* * Check the sequence# for the session and slot provided as an argument. * Also, renew the lease if the session will return NFS_OK. */ int nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid, uint32_t *highest_slotidp, uint32_t *target_highest_slotidp, int cache_this, uint32_t *sflagsp, NFSPROC_T *p) { struct nfsdsession *sep; struct nfssessionhash *shp; int error; SVCXPRT *savxprt; shp = NFSSESSIONHASH(nd->nd_sessionid); NFSLOCKSESSION(shp); sep = nfsrv_findsession(nd->nd_sessionid); if (sep == NULL) { NFSUNLOCKSESSION(shp); return (NFSERR_BADSESSION); } error = nfsv4_seqsession(sequenceid, nd->nd_slotid, *highest_slotidp, sep->sess_slots, NULL, NFSV4_SLOTS - 1); if (error != 0) { NFSUNLOCKSESSION(shp); return (error); } if (cache_this != 0) nd->nd_flag |= ND_SAVEREPLY; /* Renew the lease. */ sep->sess_clp->lc_expiry = nfsrv_leaseexpiry(); nd->nd_clientid.qval = sep->sess_clp->lc_clientid.qval; nd->nd_flag |= ND_IMPLIEDCLID; /* * If this session handles the backchannel, save the nd_xprt for this * RPC, since this is the one being used. */ if (sep->sess_clp->lc_req.nr_client != NULL && (sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) { savxprt = sep->sess_cbsess.nfsess_xprt; SVC_ACQUIRE(nd->nd_xprt); nd->nd_xprt->xp_p2 = sep->sess_clp->lc_req.nr_client->cl_private; nd->nd_xprt->xp_idletimeout = 0; /* Disable timeout. */ sep->sess_cbsess.nfsess_xprt = nd->nd_xprt; if (savxprt != NULL) SVC_RELEASE(savxprt); } *sflagsp = 0; if (sep->sess_clp->lc_req.nr_client == NULL) *sflagsp |= NFSV4SEQ_CBPATHDOWN; NFSUNLOCKSESSION(shp); if (error == NFSERR_EXPIRED) { *sflagsp |= NFSV4SEQ_EXPIREDALLSTATEREVOKED; error = 0; } else if (error == NFSERR_ADMINREVOKED) { *sflagsp |= NFSV4SEQ_ADMINSTATEREVOKED; error = 0; } *highest_slotidp = *target_highest_slotidp = NFSV4_SLOTS - 1; return (0); } /* * Check/set reclaim complete for this session/clientid. */ int nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd) { struct nfsdsession *sep; struct nfssessionhash *shp; int error = 0; shp = NFSSESSIONHASH(nd->nd_sessionid); NFSLOCKSTATE(); NFSLOCKSESSION(shp); sep = nfsrv_findsession(nd->nd_sessionid); if (sep == NULL) { NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); return (NFSERR_BADSESSION); } /* Check to see if reclaim complete has already happened. */ if ((sep->sess_clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) error = NFSERR_COMPLETEALREADY; else sep->sess_clp->lc_flags |= LCL_RECLAIMCOMPLETE; NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); return (error); } /* * Cache the reply in a session slot. */ void nfsrv_cache_session(uint8_t *sessionid, uint32_t slotid, int repstat, struct mbuf **m) { struct nfsdsession *sep; struct nfssessionhash *shp; shp = NFSSESSIONHASH(sessionid); NFSLOCKSESSION(shp); sep = nfsrv_findsession(sessionid); if (sep == NULL) { NFSUNLOCKSESSION(shp); printf("nfsrv_cache_session: no session\n"); m_freem(*m); return; } nfsv4_seqsess_cacherep(slotid, sep->sess_slots, repstat, m); NFSUNLOCKSESSION(shp); } /* * Search for a session that matches the sessionid. */ static struct nfsdsession * nfsrv_findsession(uint8_t *sessionid) { struct nfsdsession *sep; struct nfssessionhash *shp; shp = NFSSESSIONHASH(sessionid); LIST_FOREACH(sep, &shp->list, sess_hash) { if (!NFSBCMP(sessionid, sep->sess_sessionid, NFSX_V4SESSIONID)) break; } return (sep); } /* * Destroy a session. */ int nfsrv_destroysession(struct nfsrv_descript *nd, uint8_t *sessionid) { int error, samesess; samesess = 0; if (!NFSBCMP(sessionid, nd->nd_sessionid, NFSX_V4SESSIONID)) { samesess = 1; if ((nd->nd_flag & ND_LASTOP) == 0) return (NFSERR_BADSESSION); } error = nfsrv_freesession(NULL, sessionid); if (error == 0 && samesess != 0) nd->nd_flag &= ~ND_HASSEQUENCE; return (error); } /* * Free up a session structure. */ static int nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid) { struct nfssessionhash *shp; int i; NFSLOCKSTATE(); if (sep == NULL) { shp = NFSSESSIONHASH(sessionid); NFSLOCKSESSION(shp); sep = nfsrv_findsession(sessionid); } else { shp = NFSSESSIONHASH(sep->sess_sessionid); NFSLOCKSESSION(shp); } if (sep != NULL) { sep->sess_refcnt--; if (sep->sess_refcnt > 0) { NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); return (0); } LIST_REMOVE(sep, sess_hash); LIST_REMOVE(sep, sess_list); } NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); if (sep == NULL) return (NFSERR_BADSESSION); for (i = 0; i < NFSV4_SLOTS; i++) if (sep->sess_slots[i].nfssl_reply != NULL) m_freem(sep->sess_slots[i].nfssl_reply); if (sep->sess_cbsess.nfsess_xprt != NULL) SVC_RELEASE(sep->sess_cbsess.nfsess_xprt); free(sep, M_NFSDSESSION); return (0); } /* * Free a stateid. * RFC5661 says that it should fail when there are associated opens, locks * or delegations. Since stateids represent opens, I don't see how you can * free an open stateid (it will be free'd when closed), so this function * only works for lock stateids (freeing the lock_owner) or delegations. */ int nfsrv_freestateid(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, NFSPROC_T *p) { struct nfsclient *clp; struct nfsstate *stp; int error; NFSLOCKSTATE(); /* * Look up the stateid */ error = nfsrv_getclient((nfsquad_t)((u_quad_t)0), CLOPS_RENEW, &clp, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error == 0) { /* First, check for a delegation. */ LIST_FOREACH(stp, &clp->lc_deleg, ls_list) { if (!NFSBCMP(stp->ls_stateid.other, stateidp->other, NFSX_STATEIDOTHER)) break; } if (stp != NULL) { nfsrv_freedeleg(stp); NFSUNLOCKSTATE(); return (error); } } /* Not a delegation, try for a lock_owner. */ if (error == 0) error = nfsrv_getstate(clp, stateidp, 0, &stp); if (error == 0 && ((stp->ls_flags & (NFSLCK_OPEN | NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) != 0 || (stp->ls_flags & NFSLCK_LOCK) == 0)) /* Not a lock_owner stateid. */ error = NFSERR_LOCKSHELD; if (error == 0 && !LIST_EMPTY(&stp->ls_lock)) error = NFSERR_LOCKSHELD; if (error == 0) nfsrv_freelockowner(stp, NULL, 0, p); NFSUNLOCKSTATE(); return (error); } /* * Generate the xdr for an NFSv4.1 CBSequence Operation. */ static int nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, int dont_replycache, struct nfsdsession **sepp) { struct nfsdsession *sep; uint32_t *tl, slotseq = 0; int maxslot, slotpos; uint8_t sessionid[NFSX_V4SESSIONID]; int error; error = nfsv4_getcbsession(clp, sepp); if (error != 0) return (error); sep = *sepp; (void)nfsv4_sequencelookup(NULL, &sep->sess_cbsess, &slotpos, &maxslot, &slotseq, sessionid); KASSERT(maxslot >= 0, ("nfsv4_setcbsequence neg maxslot")); /* Build the Sequence arguments. */ NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 5 * NFSX_UNSIGNED); bcopy(sessionid, tl, NFSX_V4SESSIONID); tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; nd->nd_slotseq = tl; *tl++ = txdr_unsigned(slotseq); *tl++ = txdr_unsigned(slotpos); *tl++ = txdr_unsigned(maxslot); if (dont_replycache == 0) *tl++ = newnfs_true; else *tl++ = newnfs_false; *tl = 0; /* No referring call list, for now. */ nd->nd_flag |= ND_HASSEQUENCE; return (0); } /* * Get a session for the callback. */ static int nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp) { struct nfsdsession *sep; NFSLOCKSTATE(); LIST_FOREACH(sep, &clp->lc_session, sess_list) { if ((sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) break; } if (sep == NULL) { NFSUNLOCKSTATE(); return (NFSERR_BADSESSION); } sep->sess_refcnt++; *sepp = sep; NFSUNLOCKSTATE(); return (0); } /* * Free up all backchannel xprts. This needs to be done when the nfsd threads * exit, since those transports will all be going away. * This is only called after all the nfsd threads are done performing RPCs, * so locking shouldn't be an issue. */ APPLESTATIC void nfsrv_freeallbackchannel_xprts(void) { struct nfsdsession *sep; struct nfsclient *clp; SVCXPRT *xprt; int i; for (i = 0; i < nfsrv_clienthashsize; i++) { LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { LIST_FOREACH(sep, &clp->lc_session, sess_list) { xprt = sep->sess_cbsess.nfsess_xprt; sep->sess_cbsess.nfsess_xprt = NULL; if (xprt != NULL) SVC_RELEASE(xprt); } } } } Index: head/sys/kern/kern_descrip.c =================================================================== --- head/sys/kern/kern_descrip.c (revision 312926) +++ head/sys/kern/kern_descrip.c (revision 312927) @@ -1,4137 +1,4129 @@ /*- * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_descrip.c 8.6 (Berkeley) 4/19/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #include #include #include #include #include static MALLOC_DEFINE(M_FILEDESC, "filedesc", "Open file descriptor table"); static MALLOC_DEFINE(M_FILEDESC_TO_LEADER, "filedesc_to_leader", "file desc to leader structures"); static MALLOC_DEFINE(M_SIGIO, "sigio", "sigio structures"); MALLOC_DEFINE(M_FILECAPS, "filecaps", "descriptor capabilities"); MALLOC_DECLARE(M_FADVISE); static uma_zone_t file_zone; static uma_zone_t filedesc0_zone; static int closefp(struct filedesc *fdp, int fd, struct file *fp, struct thread *td, int holdleaders); static int fd_first_free(struct filedesc *fdp, int low, int size); static int fd_last_used(struct filedesc *fdp, int size); static void fdgrowtable(struct filedesc *fdp, int nfd); static void fdgrowtable_exp(struct filedesc *fdp, int nfd); static void fdunused(struct filedesc *fdp, int fd); static void fdused(struct filedesc *fdp, int fd); static int getmaxfd(struct thread *td); /* * Each process has: * * - An array of open file descriptors (fd_ofiles) * - An array of file flags (fd_ofileflags) * - A bitmap recording which descriptors are in use (fd_map) * * A process starts out with NDFILE descriptors. The value of NDFILE has * been selected based the historical limit of 20 open files, and an * assumption that the majority of processes, especially short-lived * processes like shells, will never need more. * * If this initial allocation is exhausted, a larger descriptor table and * map are allocated dynamically, and the pointers in the process's struct * filedesc are updated to point to those. This is repeated every time * the process runs out of file descriptors (provided it hasn't hit its * resource limit). * * Since threads may hold references to individual descriptor table * entries, the tables are never freed. Instead, they are placed on a * linked list and freed only when the struct filedesc is released. */ #define NDFILE 20 #define NDSLOTSIZE sizeof(NDSLOTTYPE) #define NDENTRIES (NDSLOTSIZE * __CHAR_BIT) #define NDSLOT(x) ((x) / NDENTRIES) #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES)) #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES) /* * SLIST entry used to keep track of ofiles which must be reclaimed when * the process exits. */ struct freetable { struct fdescenttbl *ft_table; SLIST_ENTRY(freetable) ft_next; }; /* * Initial allocation: a filedesc structure + the head of SLIST used to * keep track of old ofiles + enough space for NDFILE descriptors. */ struct fdescenttbl0 { int fdt_nfiles; struct filedescent fdt_ofiles[NDFILE]; }; struct filedesc0 { struct filedesc fd_fd; SLIST_HEAD(, freetable) fd_free; struct fdescenttbl0 fd_dfiles; NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)]; }; /* * Descriptor management. */ volatile int openfiles; /* actual number of open files */ struct mtx sigio_lock; /* mtx to protect pointers to sigio */ void (*mq_fdclose)(struct thread *td, int fd, struct file *fp); /* * If low >= size, just return low. Otherwise find the first zero bit in the * given bitmap, starting at low and not exceeding size - 1. Return size if * not found. */ static int fd_first_free(struct filedesc *fdp, int low, int size) { NDSLOTTYPE *map = fdp->fd_map; NDSLOTTYPE mask; int off, maxoff; if (low >= size) return (low); off = NDSLOT(low); if (low % NDENTRIES) { mask = ~(~(NDSLOTTYPE)0 >> (NDENTRIES - (low % NDENTRIES))); if ((mask &= ~map[off]) != 0UL) return (off * NDENTRIES + ffsl(mask) - 1); ++off; } for (maxoff = NDSLOTS(size); off < maxoff; ++off) if (map[off] != ~0UL) return (off * NDENTRIES + ffsl(~map[off]) - 1); return (size); } /* * Find the highest non-zero bit in the given bitmap, starting at 0 and * not exceeding size - 1. Return -1 if not found. */ static int fd_last_used(struct filedesc *fdp, int size) { NDSLOTTYPE *map = fdp->fd_map; NDSLOTTYPE mask; int off, minoff; off = NDSLOT(size); if (size % NDENTRIES) { mask = ~(~(NDSLOTTYPE)0 << (size % NDENTRIES)); if ((mask &= map[off]) != 0) return (off * NDENTRIES + flsl(mask) - 1); --off; } for (minoff = NDSLOT(0); off >= minoff; --off) if (map[off] != 0) return (off * NDENTRIES + flsl(map[off]) - 1); return (-1); } static int fdisused(struct filedesc *fdp, int fd) { KASSERT(fd >= 0 && fd < fdp->fd_nfiles, ("file descriptor %d out of range (0, %d)", fd, fdp->fd_nfiles)); return ((fdp->fd_map[NDSLOT(fd)] & NDBIT(fd)) != 0); } /* * Mark a file descriptor as used. */ static void fdused_init(struct filedesc *fdp, int fd) { KASSERT(!fdisused(fdp, fd), ("fd=%d is already used", fd)); fdp->fd_map[NDSLOT(fd)] |= NDBIT(fd); } static void fdused(struct filedesc *fdp, int fd) { FILEDESC_XLOCK_ASSERT(fdp); fdused_init(fdp, fd); if (fd > fdp->fd_lastfile) fdp->fd_lastfile = fd; if (fd == fdp->fd_freefile) fdp->fd_freefile = fd_first_free(fdp, fd, fdp->fd_nfiles); } /* * Mark a file descriptor as unused. */ static void fdunused(struct filedesc *fdp, int fd) { FILEDESC_XLOCK_ASSERT(fdp); KASSERT(fdisused(fdp, fd), ("fd=%d is already unused", fd)); KASSERT(fdp->fd_ofiles[fd].fde_file == NULL, ("fd=%d is still in use", fd)); fdp->fd_map[NDSLOT(fd)] &= ~NDBIT(fd); if (fd < fdp->fd_freefile) fdp->fd_freefile = fd; if (fd == fdp->fd_lastfile) fdp->fd_lastfile = fd_last_used(fdp, fd); } /* * Free a file descriptor. * * Avoid some work if fdp is about to be destroyed. */ static inline void fdefree_last(struct filedescent *fde) { filecaps_free(&fde->fde_caps); } static inline void fdfree(struct filedesc *fdp, int fd) { struct filedescent *fde; fde = &fdp->fd_ofiles[fd]; #ifdef CAPABILITIES seq_write_begin(&fde->fde_seq); #endif fdefree_last(fde); fde->fde_file = NULL; fdunused(fdp, fd); #ifdef CAPABILITIES seq_write_end(&fde->fde_seq); #endif } void pwd_ensure_dirs(void) { struct filedesc *fdp; fdp = curproc->p_fd; FILEDESC_XLOCK(fdp); if (fdp->fd_cdir == NULL) { fdp->fd_cdir = rootvnode; vrefact(rootvnode); } if (fdp->fd_rdir == NULL) { fdp->fd_rdir = rootvnode; vrefact(rootvnode); } FILEDESC_XUNLOCK(fdp); } /* * System calls on descriptors. */ #ifndef _SYS_SYSPROTO_H_ struct getdtablesize_args { int dummy; }; #endif /* ARGSUSED */ int sys_getdtablesize(struct thread *td, struct getdtablesize_args *uap) { #ifdef RACCT uint64_t lim; #endif td->td_retval[0] = min((int)lim_cur(td, RLIMIT_NOFILE), maxfilesperproc); #ifdef RACCT PROC_LOCK(td->td_proc); lim = racct_get_limit(td->td_proc, RACCT_NOFILE); PROC_UNLOCK(td->td_proc); if (lim < td->td_retval[0]) td->td_retval[0] = lim; #endif return (0); } /* * Duplicate a file descriptor to a particular value. * * Note: keep in mind that a potential race condition exists when closing * descriptors from a shared descriptor table (via rfork). */ #ifndef _SYS_SYSPROTO_H_ struct dup2_args { u_int from; u_int to; }; #endif /* ARGSUSED */ int sys_dup2(struct thread *td, struct dup2_args *uap) { return (kern_dup(td, FDDUP_FIXED, 0, (int)uap->from, (int)uap->to)); } /* * Duplicate a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct dup_args { u_int fd; }; #endif /* ARGSUSED */ int sys_dup(struct thread *td, struct dup_args *uap) { return (kern_dup(td, FDDUP_NORMAL, 0, (int)uap->fd, 0)); } /* * The file control system call. */ #ifndef _SYS_SYSPROTO_H_ struct fcntl_args { int fd; int cmd; long arg; }; #endif /* ARGSUSED */ int sys_fcntl(struct thread *td, struct fcntl_args *uap) { return (kern_fcntl_freebsd(td, uap->fd, uap->cmd, uap->arg)); } int kern_fcntl_freebsd(struct thread *td, int fd, int cmd, long arg) { struct flock fl; struct __oflock ofl; intptr_t arg1; int error, newcmd; error = 0; newcmd = cmd; switch (cmd) { case F_OGETLK: case F_OSETLK: case F_OSETLKW: /* * Convert old flock structure to new. */ error = copyin((void *)(intptr_t)arg, &ofl, sizeof(ofl)); fl.l_start = ofl.l_start; fl.l_len = ofl.l_len; fl.l_pid = ofl.l_pid; fl.l_type = ofl.l_type; fl.l_whence = ofl.l_whence; fl.l_sysid = 0; switch (cmd) { case F_OGETLK: newcmd = F_GETLK; break; case F_OSETLK: newcmd = F_SETLK; break; case F_OSETLKW: newcmd = F_SETLKW; break; } arg1 = (intptr_t)&fl; break; case F_GETLK: case F_SETLK: case F_SETLKW: case F_SETLK_REMOTE: error = copyin((void *)(intptr_t)arg, &fl, sizeof(fl)); arg1 = (intptr_t)&fl; break; default: arg1 = arg; break; } if (error) return (error); error = kern_fcntl(td, fd, newcmd, arg1); if (error) return (error); if (cmd == F_OGETLK) { ofl.l_start = fl.l_start; ofl.l_len = fl.l_len; ofl.l_pid = fl.l_pid; ofl.l_type = fl.l_type; ofl.l_whence = fl.l_whence; error = copyout(&ofl, (void *)(intptr_t)arg, sizeof(ofl)); } else if (cmd == F_GETLK) { error = copyout(&fl, (void *)(intptr_t)arg, sizeof(fl)); } return (error); } int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) { struct filedesc *fdp; struct flock *flp; struct file *fp, *fp2; struct filedescent *fde; struct proc *p; struct vnode *vp; cap_rights_t rights; int error, flg, tmp; uint64_t bsize; off_t foffset; error = 0; flg = F_POSIX; p = td->td_proc; fdp = p->p_fd; AUDIT_ARG_FD(cmd); AUDIT_ARG_CMD(cmd); switch (cmd) { case F_DUPFD: tmp = arg; error = kern_dup(td, FDDUP_FCNTL, 0, fd, tmp); break; case F_DUPFD_CLOEXEC: tmp = arg; error = kern_dup(td, FDDUP_FCNTL, FDDUP_FLAG_CLOEXEC, fd, tmp); break; case F_DUP2FD: tmp = arg; error = kern_dup(td, FDDUP_FIXED, 0, fd, tmp); break; case F_DUP2FD_CLOEXEC: tmp = arg; error = kern_dup(td, FDDUP_FIXED, FDDUP_FLAG_CLOEXEC, fd, tmp); break; case F_GETFD: error = EBADF; FILEDESC_SLOCK(fdp); fde = fdeget_locked(fdp, fd); if (fde != NULL) { td->td_retval[0] = (fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0; error = 0; } FILEDESC_SUNLOCK(fdp); break; case F_SETFD: error = EBADF; FILEDESC_XLOCK(fdp); fde = fdeget_locked(fdp, fd); if (fde != NULL) { fde->fde_flags = (fde->fde_flags & ~UF_EXCLOSE) | (arg & FD_CLOEXEC ? UF_EXCLOSE : 0); error = 0; } FILEDESC_XUNLOCK(fdp); break; case F_GETFL: error = fget_fcntl(td, fd, cap_rights_init(&rights, CAP_FCNTL), F_GETFL, &fp); if (error != 0) break; td->td_retval[0] = OFLAGS(fp->f_flag); fdrop(fp, td); break; case F_SETFL: error = fget_fcntl(td, fd, cap_rights_init(&rights, CAP_FCNTL), F_SETFL, &fp); if (error != 0) break; do { tmp = flg = fp->f_flag; tmp &= ~FCNTLFLAGS; tmp |= FFLAGS(arg & ~O_ACCMODE) & FCNTLFLAGS; } while(atomic_cmpset_int(&fp->f_flag, flg, tmp) == 0); tmp = fp->f_flag & FNONBLOCK; error = fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td); if (error != 0) { fdrop(fp, td); break; } tmp = fp->f_flag & FASYNC; error = fo_ioctl(fp, FIOASYNC, &tmp, td->td_ucred, td); if (error == 0) { fdrop(fp, td); break; } atomic_clear_int(&fp->f_flag, FNONBLOCK); tmp = 0; (void)fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td); fdrop(fp, td); break; case F_GETOWN: error = fget_fcntl(td, fd, cap_rights_init(&rights, CAP_FCNTL), F_GETOWN, &fp); if (error != 0) break; error = fo_ioctl(fp, FIOGETOWN, &tmp, td->td_ucred, td); if (error == 0) td->td_retval[0] = tmp; fdrop(fp, td); break; case F_SETOWN: error = fget_fcntl(td, fd, cap_rights_init(&rights, CAP_FCNTL), F_SETOWN, &fp); if (error != 0) break; tmp = arg; error = fo_ioctl(fp, FIOSETOWN, &tmp, td->td_ucred, td); fdrop(fp, td); break; case F_SETLK_REMOTE: error = priv_check(td, PRIV_NFS_LOCKD); if (error) return (error); flg = F_REMOTE; goto do_setlk; case F_SETLKW: flg |= F_WAIT; /* FALLTHROUGH F_SETLK */ case F_SETLK: do_setlk: cap_rights_init(&rights, CAP_FLOCK); error = fget_unlocked(fdp, fd, &rights, &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { error = EBADF; fdrop(fp, td); break; } flp = (struct flock *)arg; if (flp->l_whence == SEEK_CUR) { foffset = foffset_get(fp); if (foffset < 0 || (flp->l_start > 0 && foffset > OFF_MAX - flp->l_start)) { error = EOVERFLOW; fdrop(fp, td); break; } flp->l_start += foffset; } vp = fp->f_vnode; switch (flp->l_type) { case F_RDLCK: if ((fp->f_flag & FREAD) == 0) { error = EBADF; break; } PROC_LOCK(p->p_leader); p->p_leader->p_flag |= P_ADVLOCK; PROC_UNLOCK(p->p_leader); error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK, flp, flg); break; case F_WRLCK: if ((fp->f_flag & FWRITE) == 0) { error = EBADF; break; } PROC_LOCK(p->p_leader); p->p_leader->p_flag |= P_ADVLOCK; PROC_UNLOCK(p->p_leader); error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK, flp, flg); break; case F_UNLCK: error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, flp, flg); break; case F_UNLCKSYS: /* * Temporary api for testing remote lock * infrastructure. */ if (flg != F_REMOTE) { error = EINVAL; break; } error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCKSYS, flp, flg); break; default: error = EINVAL; break; } if (error != 0 || flp->l_type == F_UNLCK || flp->l_type == F_UNLCKSYS) { fdrop(fp, td); break; } /* * Check for a race with close. * * The vnode is now advisory locked (or unlocked, but this case * is not really important) as the caller requested. * We had to drop the filedesc lock, so we need to recheck if * the descriptor is still valid, because if it was closed * in the meantime we need to remove advisory lock from the * vnode - close on any descriptor leading to an advisory * locked vnode, removes that lock. * We will return 0 on purpose in that case, as the result of * successful advisory lock might have been externally visible * already. This is fine - effectively we pretend to the caller * that the closing thread was a bit slower and that the * advisory lock succeeded before the close. */ error = fget_unlocked(fdp, fd, &rights, &fp2, NULL); if (error != 0) { fdrop(fp, td); break; } if (fp != fp2) { flp->l_whence = SEEK_SET; flp->l_start = 0; flp->l_len = 0; flp->l_type = F_UNLCK; (void) VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, flp, F_POSIX); } fdrop(fp, td); fdrop(fp2, td); break; case F_GETLK: error = fget_unlocked(fdp, fd, cap_rights_init(&rights, CAP_FLOCK), &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { error = EBADF; fdrop(fp, td); break; } flp = (struct flock *)arg; if (flp->l_type != F_RDLCK && flp->l_type != F_WRLCK && flp->l_type != F_UNLCK) { error = EINVAL; fdrop(fp, td); break; } if (flp->l_whence == SEEK_CUR) { foffset = foffset_get(fp); if ((flp->l_start > 0 && foffset > OFF_MAX - flp->l_start) || (flp->l_start < 0 && foffset < OFF_MIN - flp->l_start)) { error = EOVERFLOW; fdrop(fp, td); break; } flp->l_start += foffset; } vp = fp->f_vnode; error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_GETLK, flp, F_POSIX); fdrop(fp, td); break; case F_RDAHEAD: arg = arg ? 128 * 1024: 0; /* FALLTHROUGH */ case F_READAHEAD: error = fget_unlocked(fdp, fd, cap_rights_init(&rights), &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); error = EBADF; break; } vp = fp->f_vnode; /* * Exclusive lock synchronizes against f_seqcount reads and * writes in sequential_heuristic(). */ error = vn_lock(vp, LK_EXCLUSIVE); if (error != 0) { fdrop(fp, td); break; } if (arg >= 0) { bsize = fp->f_vnode->v_mount->mnt_stat.f_iosize; fp->f_seqcount = (arg + bsize - 1) / bsize; atomic_set_int(&fp->f_flag, FRDAHEAD); } else { atomic_clear_int(&fp->f_flag, FRDAHEAD); } VOP_UNLOCK(vp, 0); fdrop(fp, td); break; default: error = EINVAL; break; } return (error); } static int getmaxfd(struct thread *td) { return (min((int)lim_cur(td, RLIMIT_NOFILE), maxfilesperproc)); } /* * Common code for dup, dup2, fcntl(F_DUPFD) and fcntl(F_DUP2FD). */ int kern_dup(struct thread *td, u_int mode, int flags, int old, int new) { struct filedesc *fdp; struct filedescent *oldfde, *newfde; struct proc *p; struct file *delfp; int error, maxfd; p = td->td_proc; fdp = p->p_fd; MPASS((flags & ~(FDDUP_FLAG_CLOEXEC)) == 0); MPASS(mode < FDDUP_LASTMODE); AUDIT_ARG_FD(old); /* XXXRW: if (flags & FDDUP_FIXED) AUDIT_ARG_FD2(new); */ /* * Verify we have a valid descriptor to dup from and possibly to * dup to. Unlike dup() and dup2(), fcntl()'s F_DUPFD should * return EINVAL when the new descriptor is out of bounds. */ if (old < 0) return (EBADF); if (new < 0) return (mode == FDDUP_FCNTL ? EINVAL : EBADF); maxfd = getmaxfd(td); if (new >= maxfd) return (mode == FDDUP_FCNTL ? EINVAL : EBADF); error = EBADF; FILEDESC_XLOCK(fdp); if (fget_locked(fdp, old) == NULL) goto unlock; if ((mode == FDDUP_FIXED || mode == FDDUP_MUSTREPLACE) && old == new) { td->td_retval[0] = new; if (flags & FDDUP_FLAG_CLOEXEC) fdp->fd_ofiles[new].fde_flags |= UF_EXCLOSE; error = 0; goto unlock; } /* * If the caller specified a file descriptor, make sure the file * table is large enough to hold it, and grab it. Otherwise, just * allocate a new descriptor the usual way. */ switch (mode) { case FDDUP_NORMAL: case FDDUP_FCNTL: if ((error = fdalloc(td, new, &new)) != 0) goto unlock; break; case FDDUP_MUSTREPLACE: /* Target file descriptor must exist. */ if (fget_locked(fdp, new) == NULL) goto unlock; break; case FDDUP_FIXED: if (new >= fdp->fd_nfiles) { /* * The resource limits are here instead of e.g. * fdalloc(), because the file descriptor table may be * shared between processes, so we can't really use * racct_add()/racct_sub(). Instead of counting the * number of actually allocated descriptors, just put * the limit on the size of the file descriptor table. */ #ifdef RACCT if (racct_enable) { PROC_LOCK(p); error = racct_set(p, RACCT_NOFILE, new + 1); PROC_UNLOCK(p); if (error != 0) { error = EMFILE; goto unlock; } } #endif fdgrowtable_exp(fdp, new + 1); } if (!fdisused(fdp, new)) fdused(fdp, new); break; default: KASSERT(0, ("%s unsupported mode %d", __func__, mode)); } KASSERT(old != new, ("new fd is same as old")); oldfde = &fdp->fd_ofiles[old]; fhold(oldfde->fde_file); newfde = &fdp->fd_ofiles[new]; delfp = newfde->fde_file; /* * Duplicate the source descriptor. */ #ifdef CAPABILITIES seq_write_begin(&newfde->fde_seq); #endif filecaps_free(&newfde->fde_caps); memcpy(newfde, oldfde, fde_change_size); filecaps_copy(&oldfde->fde_caps, &newfde->fde_caps, true); if ((flags & FDDUP_FLAG_CLOEXEC) != 0) newfde->fde_flags = oldfde->fde_flags | UF_EXCLOSE; else newfde->fde_flags = oldfde->fde_flags & ~UF_EXCLOSE; #ifdef CAPABILITIES seq_write_end(&newfde->fde_seq); #endif td->td_retval[0] = new; error = 0; if (delfp != NULL) { (void) closefp(fdp, new, delfp, td, 1); FILEDESC_UNLOCK_ASSERT(fdp); } else { unlock: FILEDESC_XUNLOCK(fdp); } return (error); } /* * If sigio is on the list associated with a process or process group, * disable signalling from the device, remove sigio from the list and * free sigio. */ void funsetown(struct sigio **sigiop) { struct sigio *sigio; if (*sigiop == NULL) return; SIGIO_LOCK(); sigio = *sigiop; if (sigio == NULL) { SIGIO_UNLOCK(); return; } *(sigio->sio_myref) = NULL; if ((sigio)->sio_pgid < 0) { struct pgrp *pg = (sigio)->sio_pgrp; PGRP_LOCK(pg); SLIST_REMOVE(&sigio->sio_pgrp->pg_sigiolst, sigio, sigio, sio_pgsigio); PGRP_UNLOCK(pg); } else { struct proc *p = (sigio)->sio_proc; PROC_LOCK(p); SLIST_REMOVE(&sigio->sio_proc->p_sigiolst, sigio, sigio, sio_pgsigio); PROC_UNLOCK(p); } SIGIO_UNLOCK(); crfree(sigio->sio_ucred); free(sigio, M_SIGIO); } /* * Free a list of sigio structures. * We only need to lock the SIGIO_LOCK because we have made ourselves * inaccessible to callers of fsetown and therefore do not need to lock * the proc or pgrp struct for the list manipulation. */ void funsetownlst(struct sigiolst *sigiolst) { struct proc *p; struct pgrp *pg; struct sigio *sigio; sigio = SLIST_FIRST(sigiolst); if (sigio == NULL) return; p = NULL; pg = NULL; /* * Every entry of the list should belong * to a single proc or pgrp. */ if (sigio->sio_pgid < 0) { pg = sigio->sio_pgrp; PGRP_LOCK_ASSERT(pg, MA_NOTOWNED); } else /* if (sigio->sio_pgid > 0) */ { p = sigio->sio_proc; PROC_LOCK_ASSERT(p, MA_NOTOWNED); } SIGIO_LOCK(); while ((sigio = SLIST_FIRST(sigiolst)) != NULL) { *(sigio->sio_myref) = NULL; if (pg != NULL) { KASSERT(sigio->sio_pgid < 0, ("Proc sigio in pgrp sigio list")); KASSERT(sigio->sio_pgrp == pg, ("Bogus pgrp in sigio list")); PGRP_LOCK(pg); SLIST_REMOVE(&pg->pg_sigiolst, sigio, sigio, sio_pgsigio); PGRP_UNLOCK(pg); } else /* if (p != NULL) */ { KASSERT(sigio->sio_pgid > 0, ("Pgrp sigio in proc sigio list")); KASSERT(sigio->sio_proc == p, ("Bogus proc in sigio list")); PROC_LOCK(p); SLIST_REMOVE(&p->p_sigiolst, sigio, sigio, sio_pgsigio); PROC_UNLOCK(p); } SIGIO_UNLOCK(); crfree(sigio->sio_ucred); free(sigio, M_SIGIO); SIGIO_LOCK(); } SIGIO_UNLOCK(); } /* * This is common code for FIOSETOWN ioctl called by fcntl(fd, F_SETOWN, arg). * * After permission checking, add a sigio structure to the sigio list for * the process or process group. */ int fsetown(pid_t pgid, struct sigio **sigiop) { struct proc *proc; struct pgrp *pgrp; struct sigio *sigio; int ret; if (pgid == 0) { funsetown(sigiop); return (0); } ret = 0; /* Allocate and fill in the new sigio out of locks. */ sigio = malloc(sizeof(struct sigio), M_SIGIO, M_WAITOK); sigio->sio_pgid = pgid; sigio->sio_ucred = crhold(curthread->td_ucred); sigio->sio_myref = sigiop; sx_slock(&proctree_lock); if (pgid > 0) { proc = pfind(pgid); if (proc == NULL) { ret = ESRCH; goto fail; } /* * Policy - Don't allow a process to FSETOWN a process * in another session. * * Remove this test to allow maximum flexibility or * restrict FSETOWN to the current process or process * group for maximum safety. */ PROC_UNLOCK(proc); if (proc->p_session != curthread->td_proc->p_session) { ret = EPERM; goto fail; } pgrp = NULL; } else /* if (pgid < 0) */ { pgrp = pgfind(-pgid); if (pgrp == NULL) { ret = ESRCH; goto fail; } PGRP_UNLOCK(pgrp); /* * Policy - Don't allow a process to FSETOWN a process * in another session. * * Remove this test to allow maximum flexibility or * restrict FSETOWN to the current process or process * group for maximum safety. */ if (pgrp->pg_session != curthread->td_proc->p_session) { ret = EPERM; goto fail; } proc = NULL; } funsetown(sigiop); if (pgid > 0) { PROC_LOCK(proc); /* * Since funsetownlst() is called without the proctree * locked, we need to check for P_WEXIT. * XXX: is ESRCH correct? */ if ((proc->p_flag & P_WEXIT) != 0) { PROC_UNLOCK(proc); ret = ESRCH; goto fail; } SLIST_INSERT_HEAD(&proc->p_sigiolst, sigio, sio_pgsigio); sigio->sio_proc = proc; PROC_UNLOCK(proc); } else { PGRP_LOCK(pgrp); SLIST_INSERT_HEAD(&pgrp->pg_sigiolst, sigio, sio_pgsigio); sigio->sio_pgrp = pgrp; PGRP_UNLOCK(pgrp); } sx_sunlock(&proctree_lock); SIGIO_LOCK(); *sigiop = sigio; SIGIO_UNLOCK(); return (0); fail: sx_sunlock(&proctree_lock); crfree(sigio->sio_ucred); free(sigio, M_SIGIO); return (ret); } /* * This is common code for FIOGETOWN ioctl called by fcntl(fd, F_GETOWN, arg). */ pid_t fgetown(sigiop) struct sigio **sigiop; { pid_t pgid; SIGIO_LOCK(); pgid = (*sigiop != NULL) ? (*sigiop)->sio_pgid : 0; SIGIO_UNLOCK(); return (pgid); } /* * Function drops the filedesc lock on return. */ static int closefp(struct filedesc *fdp, int fd, struct file *fp, struct thread *td, int holdleaders) { int error; FILEDESC_XLOCK_ASSERT(fdp); if (holdleaders) { if (td->td_proc->p_fdtol != NULL) { /* * Ask fdfree() to sleep to ensure that all relevant * process leaders can be traversed in closef(). */ fdp->fd_holdleaderscount++; } else { holdleaders = 0; } } /* * We now hold the fp reference that used to be owned by the * descriptor array. We have to unlock the FILEDESC *AFTER* * knote_fdclose to prevent a race of the fd getting opened, a knote * added, and deleteing a knote for the new fd. */ knote_fdclose(td, fd); /* * We need to notify mqueue if the object is of type mqueue. */ if (fp->f_type == DTYPE_MQUEUE) mq_fdclose(td, fd, fp); FILEDESC_XUNLOCK(fdp); error = closef(fp, td); if (holdleaders) { FILEDESC_XLOCK(fdp); fdp->fd_holdleaderscount--; if (fdp->fd_holdleaderscount == 0 && fdp->fd_holdleaderswakeup != 0) { fdp->fd_holdleaderswakeup = 0; wakeup(&fdp->fd_holdleaderscount); } FILEDESC_XUNLOCK(fdp); } return (error); } /* * Close a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct close_args { int fd; }; #endif /* ARGSUSED */ int sys_close(struct thread *td, struct close_args *uap) { return (kern_close(td, uap->fd)); } int kern_close(struct thread *td, int fd) { struct filedesc *fdp; struct file *fp; fdp = td->td_proc->p_fd; AUDIT_SYSCLOSE(td, fd); FILEDESC_XLOCK(fdp); if ((fp = fget_locked(fdp, fd)) == NULL) { FILEDESC_XUNLOCK(fdp); return (EBADF); } fdfree(fdp, fd); /* closefp() drops the FILEDESC lock for us. */ return (closefp(fdp, fd, fp, td, 1)); } /* * Close open file descriptors. */ #ifndef _SYS_SYSPROTO_H_ struct closefrom_args { int lowfd; }; #endif /* ARGSUSED */ int sys_closefrom(struct thread *td, struct closefrom_args *uap) { struct filedesc *fdp; int fd; fdp = td->td_proc->p_fd; AUDIT_ARG_FD(uap->lowfd); /* * Treat negative starting file descriptor values identical to * closefrom(0) which closes all files. */ if (uap->lowfd < 0) uap->lowfd = 0; FILEDESC_SLOCK(fdp); for (fd = uap->lowfd; fd <= fdp->fd_lastfile; fd++) { if (fdp->fd_ofiles[fd].fde_file != NULL) { FILEDESC_SUNLOCK(fdp); (void)kern_close(td, fd); FILEDESC_SLOCK(fdp); } } FILEDESC_SUNLOCK(fdp); return (0); } #if defined(COMPAT_43) /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ofstat_args { int fd; struct ostat *sb; }; #endif /* ARGSUSED */ int ofstat(struct thread *td, struct ofstat_args *uap) { struct ostat oub; struct stat ub; int error; error = kern_fstat(td, uap->fd, &ub); if (error == 0) { cvtstat(&ub, &oub); error = copyout(&oub, uap->sb, sizeof(oub)); } return (error); } #endif /* COMPAT_43 */ /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fstat_args { int fd; struct stat *sb; }; #endif /* ARGSUSED */ int sys_fstat(struct thread *td, struct fstat_args *uap) { struct stat ub; int error; error = kern_fstat(td, uap->fd, &ub); if (error == 0) error = copyout(&ub, uap->sb, sizeof(ub)); return (error); } int kern_fstat(struct thread *td, int fd, struct stat *sbp) { struct file *fp; cap_rights_t rights; int error; AUDIT_ARG_FD(fd); error = fget(td, fd, cap_rights_init(&rights, CAP_FSTAT), &fp); if (error != 0) return (error); AUDIT_ARG_FILE(td->td_proc, fp); error = fo_stat(fp, sbp, td->td_ucred, td); fdrop(fp, td); #ifdef KTRACE if (error == 0 && KTRPOINT(td, KTR_STRUCT)) ktrstat(sbp); #endif return (error); } /* * Return status information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct nfstat_args { int fd; struct nstat *sb; }; #endif /* ARGSUSED */ int sys_nfstat(struct thread *td, struct nfstat_args *uap) { struct nstat nub; struct stat ub; int error; error = kern_fstat(td, uap->fd, &ub); if (error == 0) { cvtnstat(&ub, &nub); error = copyout(&nub, uap->sb, sizeof(nub)); } return (error); } /* * Return pathconf information about a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fpathconf_args { int fd; int name; }; #endif /* ARGSUSED */ int sys_fpathconf(struct thread *td, struct fpathconf_args *uap) { struct file *fp; struct vnode *vp; cap_rights_t rights; int error; error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FPATHCONF), &fp); if (error != 0) return (error); if (uap->name == _PC_ASYNC_IO) { td->td_retval[0] = _POSIX_ASYNCHRONOUS_IO; goto out; } vp = fp->f_vnode; if (vp != NULL) { vn_lock(vp, LK_SHARED | LK_RETRY); error = VOP_PATHCONF(vp, uap->name, td->td_retval); VOP_UNLOCK(vp, 0); } else if (fp->f_type == DTYPE_PIPE || fp->f_type == DTYPE_SOCKET) { if (uap->name != _PC_PIPE_BUF) { error = EINVAL; } else { td->td_retval[0] = PIPE_BUF; error = 0; } } else { error = EOPNOTSUPP; } out: fdrop(fp, td); return (error); } /* * Initialize filecaps structure. */ void filecaps_init(struct filecaps *fcaps) { bzero(fcaps, sizeof(*fcaps)); fcaps->fc_nioctls = -1; } /* * Copy filecaps structure allocating memory for ioctls array if needed. * * The last parameter indicates whether the fdtable is locked. If it is not and * ioctls are encountered, copying fails and the caller must lock the table. * * Note that if the table was not locked, the caller has to check the relevant * sequence counter to determine whether the operation was successful. */ int filecaps_copy(const struct filecaps *src, struct filecaps *dst, bool locked) { size_t size; *dst = *src; if (src->fc_ioctls == NULL) return (0); if (!locked) return (1); KASSERT(src->fc_nioctls > 0, ("fc_ioctls != NULL, but fc_nioctls=%hd", src->fc_nioctls)); size = sizeof(src->fc_ioctls[0]) * src->fc_nioctls; dst->fc_ioctls = malloc(size, M_FILECAPS, M_WAITOK); bcopy(src->fc_ioctls, dst->fc_ioctls, size); return (0); } /* * Move filecaps structure to the new place and clear the old place. */ void filecaps_move(struct filecaps *src, struct filecaps *dst) { *dst = *src; bzero(src, sizeof(*src)); } /* * Fill the given filecaps structure with full rights. */ static void filecaps_fill(struct filecaps *fcaps) { CAP_ALL(&fcaps->fc_rights); fcaps->fc_ioctls = NULL; fcaps->fc_nioctls = -1; fcaps->fc_fcntls = CAP_FCNTL_ALL; } /* * Free memory allocated within filecaps structure. */ void filecaps_free(struct filecaps *fcaps) { free(fcaps->fc_ioctls, M_FILECAPS); bzero(fcaps, sizeof(*fcaps)); } /* * Validate the given filecaps structure. */ static void filecaps_validate(const struct filecaps *fcaps, const char *func) { KASSERT(cap_rights_is_valid(&fcaps->fc_rights), ("%s: invalid rights", func)); KASSERT((fcaps->fc_fcntls & ~CAP_FCNTL_ALL) == 0, ("%s: invalid fcntls", func)); KASSERT(fcaps->fc_fcntls == 0 || cap_rights_is_set(&fcaps->fc_rights, CAP_FCNTL), ("%s: fcntls without CAP_FCNTL", func)); KASSERT(fcaps->fc_ioctls != NULL ? fcaps->fc_nioctls > 0 : (fcaps->fc_nioctls == -1 || fcaps->fc_nioctls == 0), ("%s: invalid ioctls", func)); KASSERT(fcaps->fc_nioctls == 0 || cap_rights_is_set(&fcaps->fc_rights, CAP_IOCTL), ("%s: ioctls without CAP_IOCTL", func)); } static void fdgrowtable_exp(struct filedesc *fdp, int nfd) { int nfd1; FILEDESC_XLOCK_ASSERT(fdp); nfd1 = fdp->fd_nfiles * 2; if (nfd1 < nfd) nfd1 = nfd; fdgrowtable(fdp, nfd1); } /* * Grow the file table to accommodate (at least) nfd descriptors. */ static void fdgrowtable(struct filedesc *fdp, int nfd) { struct filedesc0 *fdp0; struct freetable *ft; struct fdescenttbl *ntable; struct fdescenttbl *otable; int nnfiles, onfiles; NDSLOTTYPE *nmap, *omap; /* * If lastfile is -1 this struct filedesc was just allocated and we are * growing it to accommodate for the one we are going to copy from. There * is no need to have a lock on this one as it's not visible to anyone. */ if (fdp->fd_lastfile != -1) FILEDESC_XLOCK_ASSERT(fdp); KASSERT(fdp->fd_nfiles > 0, ("zero-length file table")); /* save old values */ onfiles = fdp->fd_nfiles; otable = fdp->fd_files; omap = fdp->fd_map; /* compute the size of the new table */ nnfiles = NDSLOTS(nfd) * NDENTRIES; /* round up */ if (nnfiles <= onfiles) /* the table is already large enough */ return; /* * Allocate a new table. We need enough space for the number of * entries, file entries themselves and the struct freetable we will use * when we decommission the table and place it on the freelist. * We place the struct freetable in the middle so we don't have * to worry about padding. */ ntable = malloc(offsetof(struct fdescenttbl, fdt_ofiles) + nnfiles * sizeof(ntable->fdt_ofiles[0]) + sizeof(struct freetable), M_FILEDESC, M_ZERO | M_WAITOK); /* copy the old data */ ntable->fdt_nfiles = nnfiles; memcpy(ntable->fdt_ofiles, otable->fdt_ofiles, onfiles * sizeof(ntable->fdt_ofiles[0])); /* * Allocate a new map only if the old is not large enough. It will * grow at a slower rate than the table as it can map more * entries than the table can hold. */ if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) { nmap = malloc(NDSLOTS(nnfiles) * NDSLOTSIZE, M_FILEDESC, M_ZERO | M_WAITOK); /* copy over the old data and update the pointer */ memcpy(nmap, omap, NDSLOTS(onfiles) * sizeof(*omap)); fdp->fd_map = nmap; } /* * Make sure that ntable is correctly initialized before we replace * fd_files poiner. Otherwise fget_unlocked() may see inconsistent * data. */ atomic_store_rel_ptr((volatile void *)&fdp->fd_files, (uintptr_t)ntable); /* * Do not free the old file table, as some threads may still * reference entries within it. Instead, place it on a freelist * which will be processed when the struct filedesc is released. * * Note that if onfiles == NDFILE, we're dealing with the original * static allocation contained within (struct filedesc0 *)fdp, * which must not be freed. */ if (onfiles > NDFILE) { ft = (struct freetable *)&otable->fdt_ofiles[onfiles]; fdp0 = (struct filedesc0 *)fdp; ft->ft_table = otable; SLIST_INSERT_HEAD(&fdp0->fd_free, ft, ft_next); } /* * The map does not have the same possibility of threads still * holding references to it. So always free it as long as it * does not reference the original static allocation. */ if (NDSLOTS(onfiles) > NDSLOTS(NDFILE)) free(omap, M_FILEDESC); } /* * Allocate a file descriptor for the process. */ int fdalloc(struct thread *td, int minfd, int *result) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; int fd, maxfd, allocfd; #ifdef RACCT int error; #endif FILEDESC_XLOCK_ASSERT(fdp); if (fdp->fd_freefile > minfd) minfd = fdp->fd_freefile; maxfd = getmaxfd(td); /* * Search the bitmap for a free descriptor starting at minfd. * If none is found, grow the file table. */ fd = fd_first_free(fdp, minfd, fdp->fd_nfiles); if (fd >= maxfd) return (EMFILE); if (fd >= fdp->fd_nfiles) { allocfd = min(fd * 2, maxfd); #ifdef RACCT if (racct_enable) { PROC_LOCK(p); error = racct_set(p, RACCT_NOFILE, allocfd); PROC_UNLOCK(p); if (error != 0) return (EMFILE); } #endif /* * fd is already equal to first free descriptor >= minfd, so * we only need to grow the table and we are done. */ fdgrowtable_exp(fdp, allocfd); } /* * Perform some sanity checks, then mark the file descriptor as * used and return it to the caller. */ KASSERT(fd >= 0 && fd < min(maxfd, fdp->fd_nfiles), ("invalid descriptor %d", fd)); KASSERT(!fdisused(fdp, fd), ("fd_first_free() returned non-free descriptor")); KASSERT(fdp->fd_ofiles[fd].fde_file == NULL, ("file descriptor isn't free")); fdused(fdp, fd); *result = fd; return (0); } /* * Allocate n file descriptors for the process. */ int fdallocn(struct thread *td, int minfd, int *fds, int n) { struct proc *p = td->td_proc; struct filedesc *fdp = p->p_fd; int i; FILEDESC_XLOCK_ASSERT(fdp); for (i = 0; i < n; i++) if (fdalloc(td, 0, &fds[i]) != 0) break; if (i < n) { for (i--; i >= 0; i--) fdunused(fdp, fds[i]); return (EMFILE); } return (0); } /* * Create a new open file structure and allocate a file descriptor for the * process that refers to it. We add one reference to the file for the * descriptor table and one reference for resultfp. This is to prevent us * being preempted and the entry in the descriptor table closed after we * release the FILEDESC lock. */ int falloc_caps(struct thread *td, struct file **resultfp, int *resultfd, int flags, struct filecaps *fcaps) { struct file *fp; int error, fd; error = falloc_noinstall(td, &fp); if (error) return (error); /* no reference held on error */ error = finstall(td, fp, &fd, flags, fcaps); if (error) { fdrop(fp, td); /* one reference (fp only) */ return (error); } if (resultfp != NULL) *resultfp = fp; /* copy out result */ else fdrop(fp, td); /* release local reference */ if (resultfd != NULL) *resultfd = fd; return (0); } /* * Create a new open file structure without allocating a file descriptor. */ int falloc_noinstall(struct thread *td, struct file **resultfp) { struct file *fp; int maxuserfiles = maxfiles - (maxfiles / 20); int openfiles_new; static struct timeval lastfail; static int curfail; KASSERT(resultfp != NULL, ("%s: resultfp == NULL", __func__)); openfiles_new = atomic_fetchadd_int(&openfiles, 1) + 1; if ((openfiles_new >= maxuserfiles && priv_check(td, PRIV_MAXFILES) != 0) || openfiles_new >= maxfiles) { atomic_subtract_int(&openfiles, 1); if (ppsratecheck(&lastfail, &curfail, 1)) { printf("kern.maxfiles limit exceeded by uid %i, (%s) " "please see tuning(7).\n", td->td_ucred->cr_ruid, td->td_proc->p_comm); } return (ENFILE); } fp = uma_zalloc(file_zone, M_WAITOK | M_ZERO); refcount_init(&fp->f_count, 1); fp->f_cred = crhold(td->td_ucred); fp->f_ops = &badfileops; *resultfp = fp; return (0); } /* * Install a file in a file descriptor table. */ void _finstall(struct filedesc *fdp, struct file *fp, int fd, int flags, struct filecaps *fcaps) { struct filedescent *fde; MPASS(fp != NULL); if (fcaps != NULL) filecaps_validate(fcaps, __func__); FILEDESC_XLOCK_ASSERT(fdp); fde = &fdp->fd_ofiles[fd]; #ifdef CAPABILITIES seq_write_begin(&fde->fde_seq); #endif fde->fde_file = fp; fde->fde_flags = (flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0; if (fcaps != NULL) filecaps_move(fcaps, &fde->fde_caps); else filecaps_fill(&fde->fde_caps); #ifdef CAPABILITIES seq_write_end(&fde->fde_seq); #endif } int finstall(struct thread *td, struct file *fp, int *fd, int flags, struct filecaps *fcaps) { struct filedesc *fdp = td->td_proc->p_fd; int error; MPASS(fd != NULL); FILEDESC_XLOCK(fdp); if ((error = fdalloc(td, 0, fd))) { FILEDESC_XUNLOCK(fdp); return (error); } fhold(fp); _finstall(fdp, fp, *fd, flags, fcaps); FILEDESC_XUNLOCK(fdp); return (0); } /* * Build a new filedesc structure from another. * Copy the current, root, and jail root vnode references. * * If fdp is not NULL, return with it shared locked. */ struct filedesc * fdinit(struct filedesc *fdp, bool prepfiles) { struct filedesc0 *newfdp0; struct filedesc *newfdp; newfdp0 = uma_zalloc(filedesc0_zone, M_WAITOK | M_ZERO); newfdp = &newfdp0->fd_fd; /* Create the file descriptor table. */ FILEDESC_LOCK_INIT(newfdp); refcount_init(&newfdp->fd_refcnt, 1); refcount_init(&newfdp->fd_holdcnt, 1); newfdp->fd_cmask = CMASK; newfdp->fd_map = newfdp0->fd_dmap; newfdp->fd_lastfile = -1; newfdp->fd_files = (struct fdescenttbl *)&newfdp0->fd_dfiles; newfdp->fd_files->fdt_nfiles = NDFILE; if (fdp == NULL) return (newfdp); if (prepfiles && fdp->fd_lastfile >= newfdp->fd_nfiles) fdgrowtable(newfdp, fdp->fd_lastfile + 1); FILEDESC_SLOCK(fdp); newfdp->fd_cdir = fdp->fd_cdir; if (newfdp->fd_cdir) vrefact(newfdp->fd_cdir); newfdp->fd_rdir = fdp->fd_rdir; if (newfdp->fd_rdir) vrefact(newfdp->fd_rdir); newfdp->fd_jdir = fdp->fd_jdir; if (newfdp->fd_jdir) vrefact(newfdp->fd_jdir); if (!prepfiles) { FILEDESC_SUNLOCK(fdp); } else { while (fdp->fd_lastfile >= newfdp->fd_nfiles) { FILEDESC_SUNLOCK(fdp); fdgrowtable(newfdp, fdp->fd_lastfile + 1); FILEDESC_SLOCK(fdp); } } return (newfdp); } static struct filedesc * fdhold(struct proc *p) { struct filedesc *fdp; PROC_LOCK_ASSERT(p, MA_OWNED); fdp = p->p_fd; if (fdp != NULL) refcount_acquire(&fdp->fd_holdcnt); return (fdp); } static void fddrop(struct filedesc *fdp) { if (fdp->fd_holdcnt > 1) { if (refcount_release(&fdp->fd_holdcnt) == 0) return; } FILEDESC_LOCK_DESTROY(fdp); uma_zfree(filedesc0_zone, fdp); } /* * Share a filedesc structure. */ struct filedesc * fdshare(struct filedesc *fdp) { refcount_acquire(&fdp->fd_refcnt); return (fdp); } /* * Unshare a filedesc structure, if necessary by making a copy */ void fdunshare(struct thread *td) { struct filedesc *tmp; struct proc *p = td->td_proc; if (p->p_fd->fd_refcnt == 1) return; tmp = fdcopy(p->p_fd); fdescfree(td); p->p_fd = tmp; } void fdinstall_remapped(struct thread *td, struct filedesc *fdp) { fdescfree(td); td->td_proc->p_fd = fdp; } /* * Copy a filedesc structure. A NULL pointer in returns a NULL reference, * this is to ease callers, not catch errors. */ struct filedesc * fdcopy(struct filedesc *fdp) { struct filedesc *newfdp; struct filedescent *nfde, *ofde; int i; MPASS(fdp != NULL); newfdp = fdinit(fdp, true); /* copy all passable descriptors (i.e. not kqueue) */ newfdp->fd_freefile = -1; for (i = 0; i <= fdp->fd_lastfile; ++i) { ofde = &fdp->fd_ofiles[i]; if (ofde->fde_file == NULL || (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; continue; } nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; newfdp->fd_cmask = fdp->fd_cmask; FILEDESC_SUNLOCK(fdp); return (newfdp); } /* * Copies a filedesc structure, while remapping all file descriptors * stored inside using a translation table. * * File descriptors are copied over to the new file descriptor table, * regardless of whether the close-on-exec flag is set. */ int fdcopy_remapped(struct filedesc *fdp, const int *fds, size_t nfds, struct filedesc **ret) { struct filedesc *newfdp; struct filedescent *nfde, *ofde; int error, i; MPASS(fdp != NULL); newfdp = fdinit(fdp, true); if (nfds > fdp->fd_lastfile + 1) { /* New table cannot be larger than the old one. */ error = E2BIG; goto bad; } /* Copy all passable descriptors (i.e. not kqueue). */ newfdp->fd_freefile = nfds; for (i = 0; i < nfds; ++i) { if (fds[i] < 0 || fds[i] > fdp->fd_lastfile) { /* File descriptor out of bounds. */ error = EBADF; goto bad; } ofde = &fdp->fd_ofiles[fds[i]]; if (ofde->fde_file == NULL) { /* Unused file descriptor. */ error = EBADF; goto bad; } if ((ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { /* File descriptor cannot be passed. */ error = EINVAL; goto bad; } nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } newfdp->fd_cmask = fdp->fd_cmask; FILEDESC_SUNLOCK(fdp); *ret = newfdp; return (0); bad: FILEDESC_SUNLOCK(fdp); fdescfree_remapped(newfdp); return (error); } /* * Clear POSIX style locks. This is only used when fdp looses a reference (i.e. * one of processes using it exits) and the table used to be shared. */ static void fdclearlocks(struct thread *td) { struct filedesc *fdp; struct filedesc_to_leader *fdtol; struct flock lf; struct file *fp; struct proc *p; struct vnode *vp; int i; p = td->td_proc; fdp = p->p_fd; fdtol = p->p_fdtol; MPASS(fdtol != NULL); FILEDESC_XLOCK(fdp); KASSERT(fdtol->fdl_refcount > 0, ("filedesc_to_refcount botch: fdl_refcount=%d", fdtol->fdl_refcount)); if (fdtol->fdl_refcount == 1 && (p->p_leader->p_flag & P_ADVLOCK) != 0) { for (i = 0; i <= fdp->fd_lastfile; i++) { fp = fdp->fd_ofiles[i].fde_file; if (fp == NULL || fp->f_type != DTYPE_VNODE) continue; fhold(fp); FILEDESC_XUNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; vp = fp->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, &lf, F_POSIX); FILEDESC_XLOCK(fdp); fdrop(fp, td); } } retry: if (fdtol->fdl_refcount == 1) { if (fdp->fd_holdleaderscount > 0 && (p->p_leader->p_flag & P_ADVLOCK) != 0) { /* * close() or kern_dup() has cleared a reference * in a shared file descriptor table. */ fdp->fd_holdleaderswakeup = 1; sx_sleep(&fdp->fd_holdleaderscount, FILEDESC_LOCK(fdp), PLOCK, "fdlhold", 0); goto retry; } if (fdtol->fdl_holdcount > 0) { /* * Ensure that fdtol->fdl_leader remains * valid in closef(). */ fdtol->fdl_wakeup = 1; sx_sleep(fdtol, FILEDESC_LOCK(fdp), PLOCK, "fdlhold", 0); goto retry; } } fdtol->fdl_refcount--; if (fdtol->fdl_refcount == 0 && fdtol->fdl_holdcount == 0) { fdtol->fdl_next->fdl_prev = fdtol->fdl_prev; fdtol->fdl_prev->fdl_next = fdtol->fdl_next; } else fdtol = NULL; p->p_fdtol = NULL; FILEDESC_XUNLOCK(fdp); if (fdtol != NULL) free(fdtol, M_FILEDESC_TO_LEADER); } /* * Release a filedesc structure. */ static void fdescfree_fds(struct thread *td, struct filedesc *fdp, bool needclose) { struct filedesc0 *fdp0; struct freetable *ft, *tft; struct filedescent *fde; struct file *fp; int i; for (i = 0; i <= fdp->fd_lastfile; i++) { fde = &fdp->fd_ofiles[i]; fp = fde->fde_file; if (fp != NULL) { fdefree_last(fde); if (needclose) (void) closef(fp, td); else fdrop(fp, td); } } if (NDSLOTS(fdp->fd_nfiles) > NDSLOTS(NDFILE)) free(fdp->fd_map, M_FILEDESC); if (fdp->fd_nfiles > NDFILE) free(fdp->fd_files, M_FILEDESC); fdp0 = (struct filedesc0 *)fdp; SLIST_FOREACH_SAFE(ft, &fdp0->fd_free, ft_next, tft) free(ft->ft_table, M_FILEDESC); fddrop(fdp); } void fdescfree(struct thread *td) { struct proc *p; struct filedesc *fdp; struct vnode *cdir, *jdir, *rdir; p = td->td_proc; fdp = p->p_fd; MPASS(fdp != NULL); #ifdef RACCT if (racct_enable) { PROC_LOCK(p); racct_set(p, RACCT_NOFILE, 0); PROC_UNLOCK(p); } #endif if (p->p_fdtol != NULL) fdclearlocks(td); PROC_LOCK(p); p->p_fd = NULL; PROC_UNLOCK(p); if (refcount_release(&fdp->fd_refcnt) == 0) return; FILEDESC_XLOCK(fdp); cdir = fdp->fd_cdir; fdp->fd_cdir = NULL; rdir = fdp->fd_rdir; fdp->fd_rdir = NULL; jdir = fdp->fd_jdir; fdp->fd_jdir = NULL; FILEDESC_XUNLOCK(fdp); if (cdir != NULL) vrele(cdir); if (rdir != NULL) vrele(rdir); if (jdir != NULL) vrele(jdir); fdescfree_fds(td, fdp, 1); } void fdescfree_remapped(struct filedesc *fdp) { if (fdp->fd_cdir != NULL) vrele(fdp->fd_cdir); if (fdp->fd_rdir != NULL) vrele(fdp->fd_rdir); if (fdp->fd_jdir != NULL) vrele(fdp->fd_jdir); fdescfree_fds(curthread, fdp, 0); } /* * For setugid programs, we don't want to people to use that setugidness * to generate error messages which write to a file which otherwise would * otherwise be off-limits to the process. We check for filesystems where * the vnode can change out from under us after execve (like [lin]procfs). * * Since fdsetugidsafety calls this only for fd 0, 1 and 2, this check is * sufficient. We also don't check for setugidness since we know we are. */ static bool is_unsafe(struct file *fp) { struct vnode *vp; if (fp->f_type != DTYPE_VNODE) return (false); vp = fp->f_vnode; return ((vp->v_vflag & VV_PROCDEP) != 0); } /* * Make this setguid thing safe, if at all possible. */ void fdsetugidsafety(struct thread *td) { struct filedesc *fdp; struct file *fp; int i; fdp = td->td_proc->p_fd; KASSERT(fdp->fd_refcnt == 1, ("the fdtable should not be shared")); MPASS(fdp->fd_nfiles >= 3); for (i = 0; i <= 2; i++) { fp = fdp->fd_ofiles[i].fde_file; if (fp != NULL && is_unsafe(fp)) { FILEDESC_XLOCK(fdp); knote_fdclose(td, i); /* * NULL-out descriptor prior to close to avoid * a race while close blocks. */ fdfree(fdp, i); FILEDESC_XUNLOCK(fdp); (void) closef(fp, td); } } } /* * If a specific file object occupies a specific file descriptor, close the * file descriptor entry and drop a reference on the file object. This is a * convenience function to handle a subsequent error in a function that calls * falloc() that handles the race that another thread might have closed the * file descriptor out from under the thread creating the file object. */ void fdclose(struct thread *td, struct file *fp, int idx) { struct filedesc *fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); if (fdp->fd_ofiles[idx].fde_file == fp) { fdfree(fdp, idx); FILEDESC_XUNLOCK(fdp); fdrop(fp, td); } else FILEDESC_XUNLOCK(fdp); } /* * Close any files on exec? */ void fdcloseexec(struct thread *td) { struct filedesc *fdp; struct filedescent *fde; struct file *fp; int i; fdp = td->td_proc->p_fd; KASSERT(fdp->fd_refcnt == 1, ("the fdtable should not be shared")); for (i = 0; i <= fdp->fd_lastfile; i++) { fde = &fdp->fd_ofiles[i]; fp = fde->fde_file; if (fp != NULL && (fp->f_type == DTYPE_MQUEUE || (fde->fde_flags & UF_EXCLOSE))) { FILEDESC_XLOCK(fdp); fdfree(fdp, i); (void) closefp(fdp, i, fp, td, 0); FILEDESC_UNLOCK_ASSERT(fdp); } } } /* * It is unsafe for set[ug]id processes to be started with file * descriptors 0..2 closed, as these descriptors are given implicit * significance in the Standard C library. fdcheckstd() will create a * descriptor referencing /dev/null for each of stdin, stdout, and * stderr that is not already open. */ int fdcheckstd(struct thread *td) { struct filedesc *fdp; register_t save; int i, error, devnull; fdp = td->td_proc->p_fd; KASSERT(fdp->fd_refcnt == 1, ("the fdtable should not be shared")); MPASS(fdp->fd_nfiles >= 3); devnull = -1; for (i = 0; i <= 2; i++) { if (fdp->fd_ofiles[i].fde_file != NULL) continue; save = td->td_retval[0]; if (devnull != -1) { error = kern_dup(td, FDDUP_FIXED, 0, devnull, i); } else { error = kern_openat(td, AT_FDCWD, "/dev/null", UIO_SYSSPACE, O_RDWR, 0); if (error == 0) { devnull = td->td_retval[0]; KASSERT(devnull == i, ("we didn't get our fd")); } } td->td_retval[0] = save; if (error != 0) return (error); } return (0); } /* * Internal form of close. Decrement reference count on file structure. * Note: td may be NULL when closing a file that was being passed in a * message. * * XXXRW: Giant is not required for the caller, but often will be held; this * makes it moderately likely the Giant will be recursed in the VFS case. */ int closef(struct file *fp, struct thread *td) { struct vnode *vp; struct flock lf; struct filedesc_to_leader *fdtol; struct filedesc *fdp; /* * POSIX record locking dictates that any close releases ALL * locks owned by this process. This is handled by setting * a flag in the unlock to free ONLY locks obeying POSIX * semantics, and not to free BSD-style file locks. * If the descriptor was in a message, POSIX-style locks * aren't passed with the descriptor, and the thread pointer * will be NULL. Callers should be careful only to pass a * NULL thread pointer when there really is no owning * context that might have locks, or the locks will be * leaked. */ if (fp->f_type == DTYPE_VNODE && td != NULL) { vp = fp->f_vnode; if ((td->td_proc->p_leader->p_flag & P_ADVLOCK) != 0) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; (void) VOP_ADVLOCK(vp, (caddr_t)td->td_proc->p_leader, F_UNLCK, &lf, F_POSIX); } fdtol = td->td_proc->p_fdtol; if (fdtol != NULL) { /* * Handle special case where file descriptor table is * shared between multiple process leaders. */ fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); for (fdtol = fdtol->fdl_next; fdtol != td->td_proc->p_fdtol; fdtol = fdtol->fdl_next) { if ((fdtol->fdl_leader->p_flag & P_ADVLOCK) == 0) continue; fdtol->fdl_holdcount++; FILEDESC_XUNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; vp = fp->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)fdtol->fdl_leader, F_UNLCK, &lf, F_POSIX); FILEDESC_XLOCK(fdp); fdtol->fdl_holdcount--; if (fdtol->fdl_holdcount == 0 && fdtol->fdl_wakeup != 0) { fdtol->fdl_wakeup = 0; wakeup(fdtol); } } FILEDESC_XUNLOCK(fdp); } } return (fdrop(fp, td)); } /* * Initialize the file pointer with the specified properties. * * The ops are set with release semantics to be certain that the flags, type, * and data are visible when ops is. This is to prevent ops methods from being * called with bad data. */ void finit(struct file *fp, u_int flag, short type, void *data, struct fileops *ops) { fp->f_data = data; fp->f_flag = flag; fp->f_type = type; atomic_store_rel_ptr((volatile uintptr_t *)&fp->f_ops, (uintptr_t)ops); } int fget_cap_locked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, struct file **fpp, struct filecaps *havecapsp) { struct filedescent *fde; int error; FILEDESC_LOCK_ASSERT(fdp); fde = fdeget_locked(fdp, fd); if (fde == NULL) { error = EBADF; goto out; } #ifdef CAPABILITIES error = cap_check(cap_rights_fde(fde), needrightsp); if (error != 0) goto out; #endif if (havecapsp != NULL) filecaps_copy(&fde->fde_caps, havecapsp, true); *fpp = fde->fde_file; error = 0; out: return (error); } int fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, struct file **fpp, struct filecaps *havecapsp) { struct filedesc *fdp = td->td_proc->p_fd; int error; #ifndef CAPABILITIES error = fget_unlocked(fdp, fd, needrightsp, fpp, NULL); if (error == 0 && havecapsp != NULL) filecaps_fill(havecapsp); #else struct file *fp; seq_t seq; for (;;) { error = fget_unlocked(fdp, fd, needrightsp, &fp, &seq); if (error != 0) return (error); if (havecapsp != NULL) { if (!filecaps_copy(&fdp->fd_ofiles[fd].fde_caps, havecapsp, false)) { fdrop(fp, td); goto get_locked; } } if (!fd_modified(fdp, fd, seq)) break; fdrop(fp, td); } *fpp = fp; return (0); get_locked: FILEDESC_SLOCK(fdp); error = fget_cap_locked(fdp, fd, needrightsp, fpp, havecapsp); if (error == 0) fhold(*fpp); FILEDESC_SUNLOCK(fdp); #endif return (error); } int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, struct file **fpp, seq_t *seqp) { #ifdef CAPABILITIES struct filedescent *fde; #endif struct fdescenttbl *fdt; struct file *fp; u_int count; #ifdef CAPABILITIES seq_t seq; cap_rights_t haverights; int error; #endif fdt = fdp->fd_files; if ((u_int)fd >= fdt->fdt_nfiles) return (EBADF); /* * Fetch the descriptor locklessly. We avoid fdrop() races by * never raising a refcount above 0. To accomplish this we have * to use a cmpset loop rather than an atomic_add. The descriptor * must be re-verified once we acquire a reference to be certain * that the identity is still correct and we did not lose a race * due to preemption. */ for (;;) { #ifdef CAPABILITIES seq = seq_read(fd_seq(fdt, fd)); fde = &fdt->fdt_ofiles[fd]; haverights = *cap_rights_fde(fde); fp = fde->fde_file; if (!seq_consistent(fd_seq(fdt, fd), seq)) continue; #else fp = fdt->fdt_ofiles[fd].fde_file; #endif if (fp == NULL) return (EBADF); #ifdef CAPABILITIES error = cap_check(&haverights, needrightsp); if (error != 0) return (error); #endif retry: count = fp->f_count; if (count == 0) { /* * Force a reload. Other thread could reallocate the * table before this fd was closed, so it possible that * there is a stale fp pointer in cached version. */ fdt = *(struct fdescenttbl * volatile *)&(fdp->fd_files); continue; } /* * Use an acquire barrier to force re-reading of fdt so it is * refreshed for verification. */ if (atomic_cmpset_acq_int(&fp->f_count, count, count + 1) == 0) goto retry; fdt = fdp->fd_files; #ifdef CAPABILITIES if (seq_consistent_nomb(fd_seq(fdt, fd), seq)) #else if (fp == fdt->fdt_ofiles[fd].fde_file) #endif break; fdrop(fp, curthread); } *fpp = fp; if (seqp != NULL) { #ifdef CAPABILITIES *seqp = seq; #endif } return (0); } /* * Extract the file pointer associated with the specified descriptor for the * current user process. * * If the descriptor doesn't exist or doesn't match 'flags', EBADF is * returned. * * File's rights will be checked against the capability rights mask. * * If an error occurred the non-zero error is returned and *fpp is set to * NULL. Otherwise *fpp is held and set and zero is returned. Caller is * responsible for fdrop(). */ static __inline int _fget(struct thread *td, int fd, struct file **fpp, int flags, cap_rights_t *needrightsp, seq_t *seqp) { struct filedesc *fdp; struct file *fp; int error; *fpp = NULL; fdp = td->td_proc->p_fd; error = fget_unlocked(fdp, fd, needrightsp, &fp, seqp); if (error != 0) return (error); if (fp->f_ops == &badfileops) { fdrop(fp, td); return (EBADF); } /* * FREAD and FWRITE failure return EBADF as per POSIX. */ error = 0; switch (flags) { case FREAD: case FWRITE: if ((fp->f_flag & flags) == 0) error = EBADF; break; case FEXEC: if ((fp->f_flag & (FREAD | FEXEC)) == 0 || ((fp->f_flag & FWRITE) != 0)) error = EBADF; break; case 0: break; default: KASSERT(0, ("wrong flags")); } if (error != 0) { fdrop(fp, td); return (error); } *fpp = fp; return (0); } int fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { return (_fget(td, fd, fpp, 0, rightsp, NULL)); } int fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, u_char *maxprotp, struct file **fpp) { int error; #ifndef CAPABILITIES error = _fget(td, fd, fpp, 0, rightsp, NULL); if (maxprotp != NULL) *maxprotp = VM_PROT_ALL; #else struct filedesc *fdp = td->td_proc->p_fd; seq_t seq; MPASS(cap_rights_is_set(rightsp, CAP_MMAP)); for (;;) { error = _fget(td, fd, fpp, 0, rightsp, &seq); if (error != 0) return (error); /* * If requested, convert capability rights to access flags. */ if (maxprotp != NULL) *maxprotp = cap_rights_to_vmprot(cap_rights(fdp, fd)); if (!fd_modified(fdp, fd, seq)) break; fdrop(*fpp, td); } #endif return (error); } int fget_read(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { return (_fget(td, fd, fpp, FREAD, rightsp, NULL)); } int fget_write(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { return (_fget(td, fd, fpp, FWRITE, rightsp, NULL)); } int fget_fcntl(struct thread *td, int fd, cap_rights_t *rightsp, int needfcntl, struct file **fpp) { struct filedesc *fdp = td->td_proc->p_fd; #ifndef CAPABILITIES return (fget_unlocked(fdp, fd, rightsp, fpp, NULL)); #else int error; seq_t seq; MPASS(cap_rights_is_set(rightsp, CAP_FCNTL)); for (;;) { error = fget_unlocked(fdp, fd, rightsp, fpp, &seq); if (error != 0) return (error); error = cap_fcntl_check(fdp, fd, needfcntl); if (!fd_modified(fdp, fd, seq)) break; fdrop(*fpp, td); } if (error != 0) { fdrop(*fpp, td); *fpp = NULL; } return (error); #endif } /* * Like fget() but loads the underlying vnode, or returns an error if the * descriptor does not represent a vnode. Note that pipes use vnodes but * never have VM objects. The returned vnode will be vref()'d. * * XXX: what about the unused flags ? */ static __inline int _fgetvp(struct thread *td, int fd, int flags, cap_rights_t *needrightsp, struct vnode **vpp) { struct file *fp; int error; *vpp = NULL; error = _fget(td, fd, &fp, flags, needrightsp, NULL); if (error != 0) return (error); if (fp->f_vnode == NULL) { error = EINVAL; } else { *vpp = fp->f_vnode; vrefact(*vpp); } fdrop(fp, td); return (error); } int fgetvp(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { return (_fgetvp(td, fd, 0, rightsp, vpp)); } int fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp, struct filecaps *havecaps, struct vnode **vpp) { struct filedesc *fdp; struct filecaps caps; struct file *fp; int error; fdp = td->td_proc->p_fd; error = fget_cap_locked(fdp, fd, needrightsp, &fp, &caps); if (error != 0) return (error); if (fp->f_ops == &badfileops) { error = EBADF; goto out; } if (fp->f_vnode == NULL) { error = EINVAL; goto out; } *havecaps = caps; *vpp = fp->f_vnode; vrefact(*vpp); return (0); out: filecaps_free(&caps); return (error); } int fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { return (_fgetvp(td, fd, FREAD, rightsp, vpp)); } int fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { return (_fgetvp(td, fd, FEXEC, rightsp, vpp)); } #ifdef notyet int fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { return (_fgetvp(td, fd, FWRITE, rightsp, vpp)); } #endif /* * Handle the last reference to a file being closed. */ int _fdrop(struct file *fp, struct thread *td) { int error; if (fp->f_count != 0) panic("fdrop: count %d", fp->f_count); error = fo_close(fp, td); atomic_subtract_int(&openfiles, 1); crfree(fp->f_cred); free(fp->f_advice, M_FADVISE); uma_zfree(file_zone, fp); return (error); } /* * Apply an advisory lock on a file descriptor. * * Just attempt to get a record lock of the requested type on the entire file * (l_whence = SEEK_SET, l_start = 0, l_len = 0). */ #ifndef _SYS_SYSPROTO_H_ struct flock_args { int fd; int how; }; #endif /* ARGSUSED */ int sys_flock(struct thread *td, struct flock_args *uap) { struct file *fp; struct vnode *vp; struct flock lf; cap_rights_t rights; int error; error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FLOCK), &fp); if (error != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); return (EOPNOTSUPP); } vp = fp->f_vnode; lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (uap->how & LOCK_UN) { lf.l_type = F_UNLCK; atomic_clear_int(&fp->f_flag, FHASLOCK); error = VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK); goto done2; } if (uap->how & LOCK_EX) lf.l_type = F_WRLCK; else if (uap->how & LOCK_SH) lf.l_type = F_RDLCK; else { error = EBADF; goto done2; } atomic_set_int(&fp->f_flag, FHASLOCK); error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, (uap->how & LOCK_NB) ? F_FLOCK : F_FLOCK | F_WAIT); done2: fdrop(fp, td); return (error); } /* * Duplicate the specified descriptor to a free descriptor. */ int dupfdopen(struct thread *td, struct filedesc *fdp, int dfd, int mode, int openerror, int *indxp) { struct filedescent *newfde, *oldfde; struct file *fp; int error, indx; KASSERT(openerror == ENODEV || openerror == ENXIO, ("unexpected error %d in %s", openerror, __func__)); /* * If the to-be-dup'd fd number is greater than the allowed number * of file descriptors, or the fd to be dup'd has already been * closed, then reject. */ FILEDESC_XLOCK(fdp); if ((fp = fget_locked(fdp, dfd)) == NULL) { FILEDESC_XUNLOCK(fdp); return (EBADF); } error = fdalloc(td, 0, &indx); if (error != 0) { FILEDESC_XUNLOCK(fdp); return (error); } /* * There are two cases of interest here. * * For ENODEV simply dup (dfd) to file descriptor (indx) and return. * * For ENXIO steal away the file structure from (dfd) and store it in * (indx). (dfd) is effectively closed by this operation. */ switch (openerror) { case ENODEV: /* * Check that the mode the file is being opened for is a * subset of the mode of the existing descriptor. */ if (((mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { fdunused(fdp, indx); FILEDESC_XUNLOCK(fdp); return (EACCES); } fhold(fp); newfde = &fdp->fd_ofiles[indx]; oldfde = &fdp->fd_ofiles[dfd]; #ifdef CAPABILITIES seq_write_begin(&newfde->fde_seq); #endif memcpy(newfde, oldfde, fde_change_size); filecaps_copy(&oldfde->fde_caps, &newfde->fde_caps, true); #ifdef CAPABILITIES seq_write_end(&newfde->fde_seq); #endif break; case ENXIO: /* * Steal away the file pointer from dfd and stuff it into indx. */ newfde = &fdp->fd_ofiles[indx]; oldfde = &fdp->fd_ofiles[dfd]; #ifdef CAPABILITIES seq_write_begin(&newfde->fde_seq); #endif memcpy(newfde, oldfde, fde_change_size); oldfde->fde_file = NULL; fdunused(fdp, dfd); #ifdef CAPABILITIES seq_write_end(&newfde->fde_seq); #endif break; } FILEDESC_XUNLOCK(fdp); *indxp = indx; return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, "Allow a process to chroot(2) if it has a directory open"); /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(struct filedesc *fdp) { struct vnode *vp; struct file *fp; int fd; FILEDESC_LOCK_ASSERT(fdp); for (fd = 0; fd <= fdp->fd_lastfile; fd++) { fp = fget_locked(fdp, fd); if (fp == NULL) continue; if (fp->f_type == DTYPE_VNODE) { vp = fp->f_vnode; if (vp->v_type == VDIR) return (EPERM); } } return (0); } /* * Common routine for kern_chroot() and jail_attach(). The caller is * responsible for invoking priv_check() and mac_vnode_check_chroot() to * authorize this operation. */ int pwd_chroot(struct thread *td, struct vnode *vp) { struct filedesc *fdp; struct vnode *oldvp; int error; fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { error = chroot_refuse_vdir_fds(fdp); if (error != 0) { FILEDESC_XUNLOCK(fdp); return (error); } } oldvp = fdp->fd_rdir; vrefact(vp); fdp->fd_rdir = vp; if (fdp->fd_jdir == NULL) { vrefact(vp); fdp->fd_jdir = vp; } FILEDESC_XUNLOCK(fdp); vrele(oldvp); return (0); } void pwd_chdir(struct thread *td, struct vnode *vp) { struct filedesc *fdp; struct vnode *oldvp; fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); VNASSERT(vp->v_usecount > 0, vp, ("chdir to a vnode with zero usecount")); oldvp = fdp->fd_cdir; fdp->fd_cdir = vp; FILEDESC_XUNLOCK(fdp); vrele(oldvp); } /* * Scan all active processes and prisons to see if any of them have a current * or root directory of `olddp'. If so, replace them with the new mount point. */ void mountcheckdirs(struct vnode *olddp, struct vnode *newdp) { struct filedesc *fdp; struct prison *pr; struct proc *p; int nrele; if (vrefcnt(olddp) == 1) return; nrele = 0; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); fdp = fdhold(p); PROC_UNLOCK(p); if (fdp == NULL) continue; FILEDESC_XLOCK(fdp); if (fdp->fd_cdir == olddp) { vrefact(newdp); fdp->fd_cdir = newdp; nrele++; } if (fdp->fd_rdir == olddp) { vrefact(newdp); fdp->fd_rdir = newdp; nrele++; } if (fdp->fd_jdir == olddp) { vrefact(newdp); fdp->fd_jdir = newdp; nrele++; } FILEDESC_XUNLOCK(fdp); fddrop(fdp); } sx_sunlock(&allproc_lock); if (rootvnode == olddp) { vrefact(newdp); rootvnode = newdp; nrele++; } mtx_lock(&prison0.pr_mtx); if (prison0.pr_root == olddp) { vrefact(newdp); prison0.pr_root = newdp; nrele++; } mtx_unlock(&prison0.pr_mtx); sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { mtx_lock(&pr->pr_mtx); if (pr->pr_root == olddp) { vrefact(newdp); pr->pr_root = newdp; nrele++; } mtx_unlock(&pr->pr_mtx); } sx_sunlock(&allprison_lock); while (nrele--) vrele(olddp); } struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader) { struct filedesc_to_leader *fdtol; fdtol = malloc(sizeof(struct filedesc_to_leader), M_FILEDESC_TO_LEADER, M_WAITOK); fdtol->fdl_refcount = 1; fdtol->fdl_holdcount = 0; fdtol->fdl_wakeup = 0; fdtol->fdl_leader = leader; if (old != NULL) { FILEDESC_XLOCK(fdp); fdtol->fdl_next = old->fdl_next; fdtol->fdl_prev = old; old->fdl_next = fdtol; fdtol->fdl_next->fdl_prev = fdtol; FILEDESC_XUNLOCK(fdp); } else { fdtol->fdl_next = fdtol; fdtol->fdl_prev = fdtol; } return (fdtol); } static int sysctl_kern_proc_nfds(SYSCTL_HANDLER_ARGS) { struct filedesc *fdp; int i, count, slots; if (*(int *)arg1 != 0) return (EINVAL); fdp = curproc->p_fd; count = 0; FILEDESC_SLOCK(fdp); slots = NDSLOTS(fdp->fd_lastfile + 1); for (i = 0; i < slots; i++) count += bitcountl(fdp->fd_map[i]); FILEDESC_SUNLOCK(fdp); return (SYSCTL_OUT(req, &count, sizeof(count))); } static SYSCTL_NODE(_kern_proc, KERN_PROC_NFDS, nfds, CTLFLAG_RD|CTLFLAG_CAPRD|CTLFLAG_MPSAFE, sysctl_kern_proc_nfds, "Number of open file descriptors"); /* * Get file structures globally. */ static int sysctl_kern_file(SYSCTL_HANDLER_ARGS) { struct xfile xf; struct filedesc *fdp; struct file *fp; struct proc *p; int error, n; error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); if (req->oldptr == NULL) { n = 0; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } fdp = fdhold(p); PROC_UNLOCK(p); if (fdp == NULL) continue; /* overestimates sparse tables. */ if (fdp->fd_lastfile > 0) n += fdp->fd_lastfile; fddrop(fdp); } sx_sunlock(&allproc_lock); return (SYSCTL_OUT(req, 0, n * sizeof(xf))); } error = 0; bzero(&xf, sizeof(xf)); xf.xf_size = sizeof(xf); sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } if (p_cansee(req->td, p) != 0) { PROC_UNLOCK(p); continue; } xf.xf_pid = p->p_pid; xf.xf_uid = p->p_ucred->cr_uid; fdp = fdhold(p); PROC_UNLOCK(p); if (fdp == NULL) continue; FILEDESC_SLOCK(fdp); for (n = 0; fdp->fd_refcnt > 0 && n <= fdp->fd_lastfile; ++n) { if ((fp = fdp->fd_ofiles[n].fde_file) == NULL) continue; xf.xf_fd = n; xf.xf_file = fp; xf.xf_data = fp->f_data; xf.xf_vnode = fp->f_vnode; xf.xf_type = fp->f_type; xf.xf_count = fp->f_count; xf.xf_msgcount = 0; xf.xf_offset = foffset_get(fp); xf.xf_flag = fp->f_flag; error = SYSCTL_OUT(req, &xf, sizeof(xf)); if (error) break; } FILEDESC_SUNLOCK(fdp); fddrop(fdp); if (error) break; } sx_sunlock(&allproc_lock); return (error); } SYSCTL_PROC(_kern, KERN_FILE, file, CTLTYPE_OPAQUE|CTLFLAG_RD|CTLFLAG_MPSAFE, 0, 0, sysctl_kern_file, "S,xfile", "Entire file table"); #ifdef KINFO_FILE_SIZE CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static int xlate_fflags(int fflags) { static const struct { int fflag; int kf_fflag; } fflags_table[] = { { FAPPEND, KF_FLAG_APPEND }, { FASYNC, KF_FLAG_ASYNC }, { FFSYNC, KF_FLAG_FSYNC }, { FHASLOCK, KF_FLAG_HASLOCK }, { FNONBLOCK, KF_FLAG_NONBLOCK }, { FREAD, KF_FLAG_READ }, { FWRITE, KF_FLAG_WRITE }, { O_CREAT, KF_FLAG_CREAT }, { O_DIRECT, KF_FLAG_DIRECT }, { O_EXCL, KF_FLAG_EXCL }, { O_EXEC, KF_FLAG_EXEC }, { O_EXLOCK, KF_FLAG_EXLOCK }, { O_NOFOLLOW, KF_FLAG_NOFOLLOW }, { O_SHLOCK, KF_FLAG_SHLOCK }, { O_TRUNC, KF_FLAG_TRUNC } }; unsigned int i; int kflags; kflags = 0; for (i = 0; i < nitems(fflags_table); i++) if (fflags & fflags_table[i].fflag) kflags |= fflags_table[i].kf_fflag; return (kflags); } /* Trim unused data from kf_path by truncating the structure size. */ static void pack_kinfo(struct kinfo_file *kif) { kif->kf_structsize = offsetof(struct kinfo_file, kf_path) + strlen(kif->kf_path) + 1; kif->kf_structsize = roundup(kif->kf_structsize, sizeof(uint64_t)); } static void export_file_to_kinfo(struct file *fp, int fd, cap_rights_t *rightsp, struct kinfo_file *kif, struct filedesc *fdp, int flags) { int error; bzero(kif, sizeof(*kif)); /* Set a default type to allow for empty fill_kinfo() methods. */ kif->kf_type = KF_TYPE_UNKNOWN; kif->kf_flags = xlate_fflags(fp->f_flag); if (rightsp != NULL) kif->kf_cap_rights = *rightsp; else cap_rights_init(&kif->kf_cap_rights); kif->kf_fd = fd; kif->kf_ref_count = fp->f_count; kif->kf_offset = foffset_get(fp); /* * This may drop the filedesc lock, so the 'fp' cannot be * accessed after this call. */ error = fo_fill_kinfo(fp, kif, fdp); if (error == 0) kif->kf_status |= KF_ATTR_VALID; if ((flags & KERN_FILEDESC_PACK_KINFO) != 0) pack_kinfo(kif); else kif->kf_structsize = roundup2(sizeof(*kif), sizeof(uint64_t)); } static void export_vnode_to_kinfo(struct vnode *vp, int fd, int fflags, struct kinfo_file *kif, int flags) { int error; bzero(kif, sizeof(*kif)); kif->kf_type = KF_TYPE_VNODE; error = vn_fill_kinfo_vnode(vp, kif); if (error == 0) kif->kf_status |= KF_ATTR_VALID; kif->kf_flags = xlate_fflags(fflags); cap_rights_init(&kif->kf_cap_rights); kif->kf_fd = fd; kif->kf_ref_count = -1; kif->kf_offset = -1; if ((flags & KERN_FILEDESC_PACK_KINFO) != 0) pack_kinfo(kif); else kif->kf_structsize = roundup2(sizeof(*kif), sizeof(uint64_t)); vrele(vp); } struct export_fd_buf { struct filedesc *fdp; struct sbuf *sb; ssize_t remainder; struct kinfo_file kif; int flags; }; static int export_kinfo_to_sb(struct export_fd_buf *efbuf) { struct kinfo_file *kif; kif = &efbuf->kif; if (efbuf->remainder != -1) { if (efbuf->remainder < kif->kf_structsize) { /* Terminate export. */ efbuf->remainder = 0; return (0); } efbuf->remainder -= kif->kf_structsize; } return (sbuf_bcat(efbuf->sb, kif, kif->kf_structsize) == 0 ? 0 : ENOMEM); } static int export_file_to_sb(struct file *fp, int fd, cap_rights_t *rightsp, struct export_fd_buf *efbuf) { int error; if (efbuf->remainder == 0) return (0); export_file_to_kinfo(fp, fd, rightsp, &efbuf->kif, efbuf->fdp, efbuf->flags); FILEDESC_SUNLOCK(efbuf->fdp); error = export_kinfo_to_sb(efbuf); FILEDESC_SLOCK(efbuf->fdp); return (error); } static int export_vnode_to_sb(struct vnode *vp, int fd, int fflags, struct export_fd_buf *efbuf) { int error; if (efbuf->remainder == 0) return (0); if (efbuf->fdp != NULL) FILEDESC_SUNLOCK(efbuf->fdp); export_vnode_to_kinfo(vp, fd, fflags, &efbuf->kif, efbuf->flags); error = export_kinfo_to_sb(efbuf); if (efbuf->fdp != NULL) FILEDESC_SLOCK(efbuf->fdp); return (error); } /* * Store a process file descriptor information to sbuf. * * Takes a locked proc as argument, and returns with the proc unlocked. */ int kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags) { struct file *fp; struct filedesc *fdp; struct export_fd_buf *efbuf; struct vnode *cttyvp, *textvp, *tracevp; int error, i; cap_rights_t rights; PROC_LOCK_ASSERT(p, MA_OWNED); /* ktrace vnode */ tracevp = p->p_tracevp; if (tracevp != NULL) vrefact(tracevp); /* text vnode */ textvp = p->p_textvp; if (textvp != NULL) vrefact(textvp); /* Controlling tty. */ cttyvp = NULL; if (p->p_pgrp != NULL && p->p_pgrp->pg_session != NULL) { cttyvp = p->p_pgrp->pg_session->s_ttyvp; if (cttyvp != NULL) vrefact(cttyvp); } fdp = fdhold(p); PROC_UNLOCK(p); efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); efbuf->fdp = NULL; efbuf->sb = sb; efbuf->remainder = maxlen; efbuf->flags = flags; if (tracevp != NULL) export_vnode_to_sb(tracevp, KF_FD_TYPE_TRACE, FREAD | FWRITE, efbuf); if (textvp != NULL) export_vnode_to_sb(textvp, KF_FD_TYPE_TEXT, FREAD, efbuf); if (cttyvp != NULL) export_vnode_to_sb(cttyvp, KF_FD_TYPE_CTTY, FREAD | FWRITE, efbuf); error = 0; if (fdp == NULL) goto fail; efbuf->fdp = fdp; FILEDESC_SLOCK(fdp); /* working directory */ if (fdp->fd_cdir != NULL) { vrefact(fdp->fd_cdir); export_vnode_to_sb(fdp->fd_cdir, KF_FD_TYPE_CWD, FREAD, efbuf); } /* root directory */ if (fdp->fd_rdir != NULL) { vrefact(fdp->fd_rdir); export_vnode_to_sb(fdp->fd_rdir, KF_FD_TYPE_ROOT, FREAD, efbuf); } /* jail directory */ if (fdp->fd_jdir != NULL) { vrefact(fdp->fd_jdir); export_vnode_to_sb(fdp->fd_jdir, KF_FD_TYPE_JAIL, FREAD, efbuf); } for (i = 0; fdp->fd_refcnt > 0 && i <= fdp->fd_lastfile; i++) { if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) continue; #ifdef CAPABILITIES rights = *cap_rights(fdp, i); #else /* !CAPABILITIES */ cap_rights_init(&rights); #endif /* * Create sysctl entry. It is OK to drop the filedesc * lock inside of export_file_to_sb() as we will * re-validate and re-evaluate its properties when the * loop continues. */ error = export_file_to_sb(fp, i, &rights, efbuf); if (error != 0 || efbuf->remainder == 0) break; } FILEDESC_SUNLOCK(fdp); fddrop(fdp); fail: free(efbuf, M_TEMP); return (error); } #define FILEDESC_SBUF_SIZE (sizeof(struct kinfo_file) * 5) /* * Get per-process file descriptors for use by procstat(1), et al. */ static int sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) { struct sbuf sb; struct proc *p; ssize_t maxlen; int error, error2, *name; name = (int *)arg1; sbuf_new_for_sysctl(&sb, NULL, FILEDESC_SBUF_SIZE, req); sbuf_clear_flags(&sb, SBUF_INCLUDENUL); error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error != 0) { sbuf_delete(&sb); return (error); } maxlen = req->oldptr != NULL ? req->oldlen : -1; error = kern_proc_filedesc_out(p, &sb, maxlen, KERN_FILEDESC_PACK_KINFO); error2 = sbuf_finish(&sb); sbuf_delete(&sb); return (error != 0 ? error : error2); } #ifdef KINFO_OFILE_SIZE CTASSERT(sizeof(struct kinfo_ofile) == KINFO_OFILE_SIZE); #endif #ifdef COMPAT_FREEBSD7 static void kinfo_to_okinfo(struct kinfo_file *kif, struct kinfo_ofile *okif) { okif->kf_structsize = sizeof(*okif); okif->kf_type = kif->kf_type; okif->kf_fd = kif->kf_fd; okif->kf_ref_count = kif->kf_ref_count; okif->kf_flags = kif->kf_flags & (KF_FLAG_READ | KF_FLAG_WRITE | KF_FLAG_APPEND | KF_FLAG_ASYNC | KF_FLAG_FSYNC | KF_FLAG_NONBLOCK | KF_FLAG_DIRECT | KF_FLAG_HASLOCK); okif->kf_offset = kif->kf_offset; okif->kf_vnode_type = kif->kf_vnode_type; okif->kf_sock_domain = kif->kf_sock_domain; okif->kf_sock_type = kif->kf_sock_type; okif->kf_sock_protocol = kif->kf_sock_protocol; strlcpy(okif->kf_path, kif->kf_path, sizeof(okif->kf_path)); okif->kf_sa_local = kif->kf_sa_local; okif->kf_sa_peer = kif->kf_sa_peer; } static int export_vnode_for_osysctl(struct vnode *vp, int type, struct kinfo_file *kif, struct kinfo_ofile *okif, struct filedesc *fdp, struct sysctl_req *req) { int error; vrefact(vp); FILEDESC_SUNLOCK(fdp); export_vnode_to_kinfo(vp, type, 0, kif, KERN_FILEDESC_PACK_KINFO); kinfo_to_okinfo(kif, okif); error = SYSCTL_OUT(req, okif, sizeof(*okif)); FILEDESC_SLOCK(fdp); return (error); } /* * Get per-process file descriptors for use by procstat(1), et al. */ static int sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) { struct kinfo_ofile *okif; struct kinfo_file *kif; struct filedesc *fdp; int error, i, *name; struct file *fp; struct proc *p; name = (int *)arg1; error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error != 0) return (error); fdp = fdhold(p); PROC_UNLOCK(p); if (fdp == NULL) return (ENOENT); kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); okif = malloc(sizeof(*okif), M_TEMP, M_WAITOK); FILEDESC_SLOCK(fdp); if (fdp->fd_cdir != NULL) export_vnode_for_osysctl(fdp->fd_cdir, KF_FD_TYPE_CWD, kif, okif, fdp, req); if (fdp->fd_rdir != NULL) export_vnode_for_osysctl(fdp->fd_rdir, KF_FD_TYPE_ROOT, kif, okif, fdp, req); if (fdp->fd_jdir != NULL) export_vnode_for_osysctl(fdp->fd_jdir, KF_FD_TYPE_JAIL, kif, okif, fdp, req); for (i = 0; fdp->fd_refcnt > 0 && i <= fdp->fd_lastfile; i++) { if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) continue; export_file_to_kinfo(fp, i, NULL, kif, fdp, KERN_FILEDESC_PACK_KINFO); FILEDESC_SUNLOCK(fdp); kinfo_to_okinfo(kif, okif); error = SYSCTL_OUT(req, okif, sizeof(*okif)); FILEDESC_SLOCK(fdp); if (error) break; } FILEDESC_SUNLOCK(fdp); fddrop(fdp); free(kif, M_TEMP); free(okif, M_TEMP); return (0); } static SYSCTL_NODE(_kern_proc, KERN_PROC_OFILEDESC, ofiledesc, CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_ofiledesc, "Process ofiledesc entries"); #endif /* COMPAT_FREEBSD7 */ int vntype_to_kinfo(int vtype) { struct { int vtype; int kf_vtype; } vtypes_table[] = { { VBAD, KF_VTYPE_VBAD }, { VBLK, KF_VTYPE_VBLK }, { VCHR, KF_VTYPE_VCHR }, { VDIR, KF_VTYPE_VDIR }, { VFIFO, KF_VTYPE_VFIFO }, { VLNK, KF_VTYPE_VLNK }, { VNON, KF_VTYPE_VNON }, { VREG, KF_VTYPE_VREG }, { VSOCK, KF_VTYPE_VSOCK } }; unsigned int i; /* * Perform vtype translation. */ for (i = 0; i < nitems(vtypes_table); i++) if (vtypes_table[i].vtype == vtype) return (vtypes_table[i].kf_vtype); return (KF_VTYPE_UNKNOWN); } static SYSCTL_NODE(_kern_proc, KERN_PROC_FILEDESC, filedesc, CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_filedesc, "Process filedesc entries"); /* * Store a process current working directory information to sbuf. * * Takes a locked proc as argument, and returns with the proc unlocked. */ int kern_proc_cwd_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) { struct filedesc *fdp; struct export_fd_buf *efbuf; int error; PROC_LOCK_ASSERT(p, MA_OWNED); fdp = fdhold(p); PROC_UNLOCK(p); if (fdp == NULL) return (EINVAL); efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK); efbuf->fdp = fdp; efbuf->sb = sb; efbuf->remainder = maxlen; FILEDESC_SLOCK(fdp); if (fdp->fd_cdir == NULL) error = EINVAL; else { vrefact(fdp->fd_cdir); error = export_vnode_to_sb(fdp->fd_cdir, KF_FD_TYPE_CWD, FREAD, efbuf); } FILEDESC_SUNLOCK(fdp); fddrop(fdp); free(efbuf, M_TEMP); return (error); } /* * Get per-process current working directory. */ static int sysctl_kern_proc_cwd(SYSCTL_HANDLER_ARGS) { struct sbuf sb; struct proc *p; ssize_t maxlen; int error, error2, *name; name = (int *)arg1; sbuf_new_for_sysctl(&sb, NULL, sizeof(struct kinfo_file), req); sbuf_clear_flags(&sb, SBUF_INCLUDENUL); error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error != 0) { sbuf_delete(&sb); return (error); } maxlen = req->oldptr != NULL ? req->oldlen : -1; error = kern_proc_cwd_out(p, &sb, maxlen); error2 = sbuf_finish(&sb); sbuf_delete(&sb); return (error != 0 ? error : error2); } static SYSCTL_NODE(_kern_proc, KERN_PROC_CWD, cwd, CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_cwd, "Process current working directory"); #ifdef DDB /* * For the purposes of debugging, generate a human-readable string for the * file type. */ static const char * file_type_to_name(short type) { switch (type) { case 0: return ("zero"); case DTYPE_VNODE: return ("vnod"); case DTYPE_SOCKET: return ("sock"); case DTYPE_PIPE: return ("pipe"); case DTYPE_FIFO: return ("fifo"); case DTYPE_KQUEUE: return ("kque"); case DTYPE_CRYPTO: return ("crpt"); case DTYPE_MQUEUE: return ("mque"); case DTYPE_SHM: return ("shm"); case DTYPE_SEM: return ("ksem"); default: return ("unkn"); } } /* * For the purposes of debugging, identify a process (if any, perhaps one of * many) that references the passed file in its file descriptor array. Return * NULL if none. */ static struct proc * file_to_first_proc(struct file *fp) { struct filedesc *fdp; struct proc *p; int n; FOREACH_PROC_IN_SYSTEM(p) { if (p->p_state == PRS_NEW) continue; fdp = p->p_fd; if (fdp == NULL) continue; for (n = 0; n <= fdp->fd_lastfile; n++) { if (fp == fdp->fd_ofiles[n].fde_file) return (p); } } return (NULL); } static void db_print_file(struct file *fp, int header) { struct proc *p; if (header) db_printf("%8s %4s %8s %8s %4s %5s %6s %8s %5s %12s\n", "File", "Type", "Data", "Flag", "GCFl", "Count", "MCount", "Vnode", "FPID", "FCmd"); p = file_to_first_proc(fp); db_printf("%8p %4s %8p %08x %04x %5d %6d %8p %5d %12s\n", fp, file_type_to_name(fp->f_type), fp->f_data, fp->f_flag, 0, fp->f_count, 0, fp->f_vnode, p != NULL ? p->p_pid : -1, p != NULL ? p->p_comm : "-"); } DB_SHOW_COMMAND(file, db_show_file) { struct file *fp; if (!have_addr) { db_printf("usage: show file \n"); return; } fp = (struct file *)addr; db_print_file(fp, 1); } DB_SHOW_COMMAND(files, db_show_files) { struct filedesc *fdp; struct file *fp; struct proc *p; int header; int n; header = 1; FOREACH_PROC_IN_SYSTEM(p) { if (p->p_state == PRS_NEW) continue; if ((fdp = p->p_fd) == NULL) continue; for (n = 0; n <= fdp->fd_lastfile; ++n) { if ((fp = fdp->fd_ofiles[n].fde_file) == NULL) continue; db_print_file(fp, header); header = 0; } } } #endif SYSCTL_INT(_kern, KERN_MAXFILESPERPROC, maxfilesperproc, CTLFLAG_RW, &maxfilesperproc, 0, "Maximum files allowed open per process"); SYSCTL_INT(_kern, KERN_MAXFILES, maxfiles, CTLFLAG_RW, &maxfiles, 0, "Maximum number of files"); SYSCTL_INT(_kern, OID_AUTO, openfiles, CTLFLAG_RD, __DEVOLATILE(int *, &openfiles), 0, "System-wide number of open files"); /* ARGSUSED*/ static void filelistinit(void *dummy) { file_zone = uma_zcreate("Files", sizeof(struct file), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); filedesc0_zone = uma_zcreate("filedesc0", sizeof(struct filedesc0), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); mtx_init(&sigio_lock, "sigio lock", NULL, MTX_DEF); } SYSINIT(select, SI_SUB_LOCK, SI_ORDER_FIRST, filelistinit, NULL); /*-------------------------------------------------------------------*/ static int badfo_readwrite(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { return (EBADF); } static int badfo_truncate(struct file *fp, off_t length, struct ucred *active_cred, struct thread *td) { return (EINVAL); } static int badfo_ioctl(struct file *fp, u_long com, void *data, struct ucred *active_cred, struct thread *td) { return (EBADF); } static int badfo_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td) { return (0); } static int badfo_kqfilter(struct file *fp, struct knote *kn) { return (EBADF); } static int badfo_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td) { return (EBADF); } static int badfo_close(struct file *fp, struct thread *td) { return (0); } static int badfo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, struct thread *td) { return (EBADF); } static int badfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td) { return (EBADF); } static int badfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, struct thread *td) { return (EBADF); } static int badfo_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { return (0); } struct fileops badfileops = { .fo_read = badfo_readwrite, .fo_write = badfo_readwrite, .fo_truncate = badfo_truncate, .fo_ioctl = badfo_ioctl, .fo_poll = badfo_poll, .fo_kqfilter = badfo_kqfilter, .fo_stat = badfo_stat, .fo_close = badfo_close, .fo_chmod = badfo_chmod, .fo_chown = badfo_chown, .fo_sendfile = badfo_sendfile, .fo_fill_kinfo = badfo_fill_kinfo, }; int invfo_rdwr(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { return (EOPNOTSUPP); } int invfo_truncate(struct file *fp, off_t length, struct ucred *active_cred, struct thread *td) { return (EINVAL); } int invfo_ioctl(struct file *fp, u_long com, void *data, struct ucred *active_cred, struct thread *td) { return (ENOTTY); } int invfo_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td) { return (poll_no_poll(events)); } int invfo_kqfilter(struct file *fp, struct knote *kn) { return (EINVAL); } int invfo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, struct thread *td) { return (EINVAL); } int invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td) { return (EINVAL); } int invfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, struct thread *td) { return (EINVAL); -} - -bool -fd_modified(struct filedesc *fdp, int fd, uint32_t seq) -{ - - return (!seq_consistent(fd_seq(fdp->fd_files, fd), seq)); } /*-------------------------------------------------------------------*/ /* * File Descriptor pseudo-device driver (/dev/fd/). * * Opening minor device N dup()s the file (if any) connected to file * descriptor N belonging to the calling process. Note that this driver * consists of only the ``open()'' routine, because all subsequent * references to this file will be direct to the other driver. * * XXX: we could give this one a cloning event handler if necessary. */ /* ARGSUSED */ static int fdopen(struct cdev *dev, int mode, int type, struct thread *td) { /* * XXX Kludge: set curthread->td_dupfd to contain the value of the * the file descriptor being sought for duplication. The error * return ensures that the vnode for this device will be released * by vn_open. Open will detect this special error and take the * actions in dupfdopen below. Other callers of vn_open or VOP_OPEN * will simply report the error. */ td->td_dupfd = dev2unit(dev); return (ENODEV); } static struct cdevsw fildesc_cdevsw = { .d_version = D_VERSION, .d_open = fdopen, .d_name = "FD", }; static void fildesc_drvinit(void *unused) { struct cdev *dev; dev = make_dev_credf(MAKEDEV_ETERNAL, &fildesc_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0666, "fd/0"); make_dev_alias(dev, "stdin"); dev = make_dev_credf(MAKEDEV_ETERNAL, &fildesc_cdevsw, 1, NULL, UID_ROOT, GID_WHEEL, 0666, "fd/1"); make_dev_alias(dev, "stdout"); dev = make_dev_credf(MAKEDEV_ETERNAL, &fildesc_cdevsw, 2, NULL, UID_ROOT, GID_WHEEL, 0666, "fd/2"); make_dev_alias(dev, "stderr"); } SYSINIT(fildescdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, fildesc_drvinit, NULL); Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c (revision 312926) +++ head/sys/kern/kern_shutdown.c (revision 312927) @@ -1,1266 +1,1258 @@ /*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_shutdown.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_ekcd.h" #include "opt_kdb.h" #include "opt_panic.h" #include "opt_sched.h" #include "opt_watchdog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_DUMPER, "dumper", "dumper block buffer"); #ifndef PANIC_REBOOT_WAIT_TIME #define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ #endif static int panic_reboot_wait_time = PANIC_REBOOT_WAIT_TIME; SYSCTL_INT(_kern, OID_AUTO, panic_reboot_wait_time, CTLFLAG_RWTUN, &panic_reboot_wait_time, 0, "Seconds to wait before rebooting after a panic"); /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #include #ifdef KDB #ifdef KDB_UNATTENDED int debugger_on_panic = 0; #else int debugger_on_panic = 1; #endif SYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, CTLFLAG_RWTUN | CTLFLAG_SECURE, &debugger_on_panic, 0, "Run debugger on kernel panic"); #ifdef KDB_TRACE static int trace_on_panic = 1; #else static int trace_on_panic = 0; #endif SYSCTL_INT(_debug, OID_AUTO, trace_on_panic, CTLFLAG_RWTUN | CTLFLAG_SECURE, &trace_on_panic, 0, "Print stack trace on kernel panic"); #endif /* KDB */ static int sync_on_panic = 0; SYSCTL_INT(_kern, OID_AUTO, sync_on_panic, CTLFLAG_RWTUN, &sync_on_panic, 0, "Do a sync before rebooting from a panic"); static SYSCTL_NODE(_kern, OID_AUTO, shutdown, CTLFLAG_RW, 0, "Shutdown environment"); #ifndef DIAGNOSTIC static int show_busybufs; #else static int show_busybufs = 1; #endif SYSCTL_INT(_kern_shutdown, OID_AUTO, show_busybufs, CTLFLAG_RW, &show_busybufs, 0, ""); int suspend_blocked = 0; SYSCTL_INT(_kern, OID_AUTO, suspend_blocked, CTLFLAG_RW, &suspend_blocked, 0, "Block suspend due to a pending shutdown"); #ifdef EKCD FEATURE(ekcd, "Encrypted kernel crash dumps support"); MALLOC_DEFINE(M_EKCD, "ekcd", "Encrypted kernel crash dumps data"); struct kerneldumpcrypto { uint8_t kdc_encryption; uint8_t kdc_iv[KERNELDUMP_IV_MAX_SIZE]; keyInstance kdc_ki; cipherInstance kdc_ci; off_t kdc_nextoffset; uint32_t kdc_dumpkeysize; struct kerneldumpkey kdc_dumpkey[]; }; #endif /* * Variable panicstr contains argument to first call to panic; used as flag * to indicate that the kernel has already called panic. */ const char *panicstr; int dumping; /* system is dumping */ int rebooting; /* system is rebooting */ static struct dumperinfo dumper; /* our selected dumper */ /* Context information for dump-debuggers. */ static struct pcb dumppcb; /* Registers. */ lwpid_t dumptid; /* Thread ID. */ static struct cdevsw reroot_cdevsw = { .d_version = D_VERSION, .d_name = "reroot", }; static void poweroff_wait(void *, int); static void shutdown_halt(void *junk, int howto); static void shutdown_panic(void *junk, int howto); static void shutdown_reset(void *junk, int howto); static int kern_reroot(void); /* register various local shutdown events */ static void shutdown_conf(void *unused) { EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL, SHUTDOWN_PRI_FIRST); EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_reset, NULL, SHUTDOWN_PRI_LAST + 200); } SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL); /* * The only reason this exists is to create the /dev/reroot/ directory, * used by reroot code in init(8) as a mountpoint for tmpfs. */ static void reroot_conf(void *unused) { int error; struct cdev *cdev; error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &cdev, &reroot_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "reroot/reroot"); if (error != 0) { printf("%s: failed to create device node, error %d", __func__, error); } } SYSINIT(reroot_conf, SI_SUB_DEVFS, SI_ORDER_ANY, reroot_conf, NULL); /* * The system call that results in a reboot. */ /* ARGSUSED */ int sys_reboot(struct thread *td, struct reboot_args *uap) { int error; error = 0; #ifdef MAC error = mac_system_check_reboot(td->td_ucred, uap->opt); #endif if (error == 0) error = priv_check(td, PRIV_REBOOT); if (error == 0) { if (uap->opt & RB_REROOT) { error = kern_reroot(); } else { mtx_lock(&Giant); kern_reboot(uap->opt); mtx_unlock(&Giant); } } return (error); } /* * Called by events that want to shut down.. e.g on a PC */ void shutdown_nice(int howto) { if (initproc != NULL) { /* Send a signal to init(8) and have it shutdown the world. */ PROC_LOCK(initproc); if (howto & RB_POWEROFF) kern_psignal(initproc, SIGUSR2); else if (howto & RB_HALT) kern_psignal(initproc, SIGUSR1); else kern_psignal(initproc, SIGINT); PROC_UNLOCK(initproc); } else { /* No init(8) running, so simply reboot. */ kern_reboot(howto | RB_NOSYNC); } } static void print_uptime(void) { int f; struct timespec ts; getnanouptime(&ts); printf("Uptime: "); f = 0; if (ts.tv_sec >= 86400) { printf("%ldd", (long)ts.tv_sec / 86400); ts.tv_sec %= 86400; f = 1; } if (f || ts.tv_sec >= 3600) { printf("%ldh", (long)ts.tv_sec / 3600); ts.tv_sec %= 3600; f = 1; } if (f || ts.tv_sec >= 60) { printf("%ldm", (long)ts.tv_sec / 60); ts.tv_sec %= 60; f = 1; } printf("%lds\n", (long)ts.tv_sec); } int doadump(boolean_t textdump) { boolean_t coredump; int error; error = 0; if (dumping) return (EBUSY); if (dumper.dumper == NULL) return (ENXIO); savectx(&dumppcb); dumptid = curthread->td_tid; dumping++; coredump = TRUE; #ifdef DDB if (textdump && textdump_pending) { coredump = FALSE; textdump_dumpsys(&dumper); } #endif if (coredump) error = dumpsys(&dumper); dumping--; return (error); } /* * Shutdown the system cleanly to prepare for reboot, halt, or power off. */ void kern_reboot(int howto) { static int once = 0; #if defined(SMP) /* * Bind us to CPU 0 so that all shutdown code runs there. Some * systems don't shutdown properly (i.e., ACPI power off) if we * run on another processor. */ if (!SCHEDULER_STOPPED()) { thread_lock(curthread); sched_bind(curthread, 0); thread_unlock(curthread); KASSERT(PCPU_GET(cpuid) == 0, ("boot: not running on cpu 0")); } #endif /* We're in the process of rebooting. */ rebooting = 1; /* We are out of the debugger now. */ kdb_active = 0; /* * Do any callouts that should be done BEFORE syncing the filesystems. */ EVENTHANDLER_INVOKE(shutdown_pre_sync, howto); /* * Now sync filesystems */ if (!cold && (howto & RB_NOSYNC) == 0 && once == 0) { once = 1; bufshutdown(show_busybufs); } print_uptime(); cngrab(); /* * Ok, now do things that assume all filesystem activity has * been completed. */ EVENTHANDLER_INVOKE(shutdown_post_sync, howto); if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold && !dumping) doadump(TRUE); /* Now that we're going to really halt the system... */ EVENTHANDLER_INVOKE(shutdown_final, howto); for(;;) ; /* safety against shutdown_reset not working */ /* NOTREACHED */ } /* * The system call that results in changing the rootfs. */ static int kern_reroot(void) { struct vnode *oldrootvnode, *vp; struct mount *mp, *devmp; int error; if (curproc != initproc) return (EPERM); /* * Mark the filesystem containing currently-running executable * (the temporary copy of init(8)) busy. */ vp = curproc->p_textvp; error = vn_lock(vp, LK_SHARED); if (error != 0) return (error); mp = vp->v_mount; error = vfs_busy(mp, MBF_NOWAIT); if (error != 0) { vfs_ref(mp); VOP_UNLOCK(vp, 0); error = vfs_busy(mp, 0); vn_lock(vp, LK_SHARED | LK_RETRY); vfs_rel(mp); if (error != 0) { VOP_UNLOCK(vp, 0); return (ENOENT); } if (vp->v_iflag & VI_DOOMED) { VOP_UNLOCK(vp, 0); vfs_unbusy(mp); return (ENOENT); } } VOP_UNLOCK(vp, 0); /* * Remove the filesystem containing currently-running executable * from the mount list, to prevent it from being unmounted * by vfs_unmountall(), and to avoid confusing vfs_mountroot(). * * Also preserve /dev - forcibly unmounting it could cause driver * reinitialization. */ vfs_ref(rootdevmp); devmp = rootdevmp; rootdevmp = NULL; mtx_lock(&mountlist_mtx); TAILQ_REMOVE(&mountlist, mp, mnt_list); TAILQ_REMOVE(&mountlist, devmp, mnt_list); mtx_unlock(&mountlist_mtx); oldrootvnode = rootvnode; /* * Unmount everything except for the two filesystems preserved above. */ vfs_unmountall(); /* * Add /dev back; vfs_mountroot() will move it into its new place. */ mtx_lock(&mountlist_mtx); TAILQ_INSERT_HEAD(&mountlist, devmp, mnt_list); mtx_unlock(&mountlist_mtx); rootdevmp = devmp; vfs_rel(rootdevmp); /* * Mount the new rootfs. */ vfs_mountroot(); /* * Update all references to the old rootvnode. */ mountcheckdirs(oldrootvnode, rootvnode); /* * Add the temporary filesystem back and unbusy it. */ mtx_lock(&mountlist_mtx); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); vfs_unbusy(mp); return (0); } /* * If the shutdown was a clean halt, behave accordingly. */ static void shutdown_halt(void *junk, int howto) { if (howto & RB_HALT) { printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); switch (cngetc()) { case -1: /* No console, just die */ cpu_halt(); /* NOTREACHED */ default: howto &= ~RB_HALT; break; } } } /* * Check to see if the system paniced, pause and then reboot * according to the specified delay. */ static void shutdown_panic(void *junk, int howto) { int loop; if (howto & RB_DUMP) { if (panic_reboot_wait_time != 0) { if (panic_reboot_wait_time != -1) { printf("Automatic reboot in %d seconds - " "press a key on the console to abort\n", panic_reboot_wait_time); for (loop = panic_reboot_wait_time * 10; loop > 0; --loop) { DELAY(1000 * 100); /* 1/10th second */ /* Did user type a key? */ if (cncheckc() != -1) break; } if (!loop) return; } } else { /* zero time specified - reboot NOW */ return; } printf("--> Press a key on the console to reboot,\n"); printf("--> or switch off the system now.\n"); cngetc(); } } /* * Everything done, now reset */ static void shutdown_reset(void *junk, int howto) { printf("Rebooting...\n"); DELAY(1000000); /* wait 1 sec for printf's to complete and be read */ /* * Acquiring smp_ipi_mtx here has a double effect: * - it disables interrupts avoiding CPU0 preemption * by fast handlers (thus deadlocking against other CPUs) * - it avoids deadlocks against smp_rendezvous() or, more * generally, threads busy-waiting, with this spinlock held, * and waiting for responses by threads on other CPUs * (ie. smp_tlb_shootdown()). * * For the !SMP case it just needs to handle the former problem. */ #ifdef SMP mtx_lock_spin(&smp_ipi_mtx); #else spinlock_enter(); #endif /* cpu_boot(howto); */ /* doesn't do anything at the moment */ cpu_reset(); /* NOTREACHED */ /* assuming reset worked */ } #if defined(WITNESS) || defined(INVARIANT_SUPPORT) static int kassert_warn_only = 0; #ifdef KDB static int kassert_do_kdb = 0; #endif #ifdef KTR static int kassert_do_ktr = 0; #endif static int kassert_do_log = 1; static int kassert_log_pps_limit = 4; static int kassert_log_mute_at = 0; static int kassert_log_panic_at = 0; static int kassert_warnings = 0; SYSCTL_NODE(_debug, OID_AUTO, kassert, CTLFLAG_RW, NULL, "kassert options"); SYSCTL_INT(_debug_kassert, OID_AUTO, warn_only, CTLFLAG_RWTUN, &kassert_warn_only, 0, "KASSERT triggers a panic (1) or just a warning (0)"); #ifdef KDB SYSCTL_INT(_debug_kassert, OID_AUTO, do_kdb, CTLFLAG_RWTUN, &kassert_do_kdb, 0, "KASSERT will enter the debugger"); #endif #ifdef KTR SYSCTL_UINT(_debug_kassert, OID_AUTO, do_ktr, CTLFLAG_RWTUN, &kassert_do_ktr, 0, "KASSERT does a KTR, set this to the KTRMASK you want"); #endif SYSCTL_INT(_debug_kassert, OID_AUTO, do_log, CTLFLAG_RWTUN, &kassert_do_log, 0, "KASSERT triggers a panic (1) or just a warning (0)"); SYSCTL_INT(_debug_kassert, OID_AUTO, warnings, CTLFLAG_RWTUN, &kassert_warnings, 0, "number of KASSERTs that have been triggered"); SYSCTL_INT(_debug_kassert, OID_AUTO, log_panic_at, CTLFLAG_RWTUN, &kassert_log_panic_at, 0, "max number of KASSERTS before we will panic"); SYSCTL_INT(_debug_kassert, OID_AUTO, log_pps_limit, CTLFLAG_RWTUN, &kassert_log_pps_limit, 0, "limit number of log messages per second"); SYSCTL_INT(_debug_kassert, OID_AUTO, log_mute_at, CTLFLAG_RWTUN, &kassert_log_mute_at, 0, "max number of KASSERTS to log"); static int kassert_sysctl_kassert(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_debug_kassert, OID_AUTO, kassert, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, NULL, 0, kassert_sysctl_kassert, "I", "set to trigger a test kassert"); static int kassert_sysctl_kassert(SYSCTL_HANDLER_ARGS) { int error, i; error = sysctl_wire_old_buffer(req, sizeof(int)); if (error == 0) { i = 0; error = sysctl_handle_int(oidp, &i, 0, req); } if (error != 0 || req->newptr == NULL) return (error); KASSERT(0, ("kassert_sysctl_kassert triggered kassert %d", i)); return (0); } /* * Called by KASSERT, this decides if we will panic * or if we will log via printf and/or ktr. */ void kassert_panic(const char *fmt, ...) { static char buf[256]; va_list ap; va_start(ap, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); /* * panic if we're not just warning, or if we've exceeded * kassert_log_panic_at warnings. */ if (!kassert_warn_only || (kassert_log_panic_at > 0 && kassert_warnings >= kassert_log_panic_at)) { va_start(ap, fmt); vpanic(fmt, ap); /* NORETURN */ } #ifdef KTR if (kassert_do_ktr) CTR0(ktr_mask, buf); #endif /* KTR */ /* * log if we've not yet met the mute limit. */ if (kassert_do_log && (kassert_log_mute_at == 0 || kassert_warnings < kassert_log_mute_at)) { static struct timeval lasterr; static int curerr; if (ppsratecheck(&lasterr, &curerr, kassert_log_pps_limit)) { printf("KASSERT failed: %s\n", buf); kdb_backtrace(); } } #ifdef KDB if (kassert_do_kdb) { kdb_enter(KDB_WHY_KASSERT, buf); } #endif atomic_add_int(&kassert_warnings, 1); } #endif /* * Panic is called on unresolvable fatal errors. It prints "panic: mesg", * and then reboots. If we are called twice, then we avoid trying to sync * the disks as this often leads to recursive panics. */ void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vpanic(fmt, ap); } void vpanic(const char *fmt, va_list ap) { #ifdef SMP cpuset_t other_cpus; #endif struct thread *td = curthread; int bootopt, newpanic; static char buf[256]; spinlock_enter(); -#if 0 -/***** DEBUGGING DRM *****/ - - doadump(0); - EVENTHANDLER_INVOKE(shutdown_final, RB_NOSYNC); - -/************************/ -#endif #ifdef SMP /* * stop_cpus_hard(other_cpus) should prevent multiple CPUs from * concurrently entering panic. Only the winner will proceed * further. */ if (panicstr == NULL && !kdb_active) { other_cpus = all_cpus; CPU_CLR(PCPU_GET(cpuid), &other_cpus); stop_cpus_hard(other_cpus); } #endif /* * Ensure that the scheduler is stopped while panicking, even if panic * has been entered from kdb. */ td->td_stopsched = 1; bootopt = RB_AUTOBOOT; newpanic = 0; if (panicstr) bootopt |= RB_NOSYNC; else { bootopt |= RB_DUMP; panicstr = fmt; newpanic = 1; } if (newpanic) { (void)vsnprintf(buf, sizeof(buf), fmt, ap); panicstr = buf; cngrab(); printf("panic: %s\n", buf); } else { printf("panic: "); vprintf(fmt, ap); printf("\n"); } #ifdef SMP printf("cpuid = %d\n", PCPU_GET(cpuid)); #endif #ifdef KDB if (newpanic && trace_on_panic) kdb_backtrace(); if (debugger_on_panic) kdb_enter(KDB_WHY_PANIC, "panic"); #endif /*thread_lock(td); */ td->td_flags |= TDF_INPANIC; /* thread_unlock(td); */ if (!sync_on_panic) bootopt |= RB_NOSYNC; kern_reboot(bootopt); } /* * Support for poweroff delay. * * Please note that setting this delay too short might power off your machine * before the write cache on your hard disk has been flushed, leading to * soft-updates inconsistencies. */ #ifndef POWEROFF_DELAY # define POWEROFF_DELAY 5000 #endif static int poweroff_delay = POWEROFF_DELAY; SYSCTL_INT(_kern_shutdown, OID_AUTO, poweroff_delay, CTLFLAG_RW, &poweroff_delay, 0, "Delay before poweroff to write disk caches (msec)"); static void poweroff_wait(void *junk, int howto) { if (!(howto & RB_POWEROFF) || poweroff_delay <= 0) return; DELAY(poweroff_delay * 1000); } /* * Some system processes (e.g. syncer) need to be stopped at appropriate * points in their main loops prior to a system shutdown, so that they * won't interfere with the shutdown process (e.g. by holding a disk buf * to cause sync to fail). For each of these system processes, register * shutdown_kproc() as a handler for one of shutdown events. */ static int kproc_shutdown_wait = 60; SYSCTL_INT(_kern_shutdown, OID_AUTO, kproc_shutdown_wait, CTLFLAG_RW, &kproc_shutdown_wait, 0, "Max wait time (sec) to stop for each process"); void kproc_shutdown(void *arg, int howto) { struct proc *p; int error; if (panicstr) return; p = (struct proc *)arg; printf("Waiting (max %d seconds) for system process `%s' to stop... ", kproc_shutdown_wait, p->p_comm); error = kproc_suspend(p, kproc_shutdown_wait * hz); if (error == EWOULDBLOCK) printf("timed out\n"); else printf("done\n"); } void kthread_shutdown(void *arg, int howto) { struct thread *td; int error; if (panicstr) return; td = (struct thread *)arg; printf("Waiting (max %d seconds) for system thread `%s' to stop... ", kproc_shutdown_wait, td->td_name); error = kthread_suspend(td, kproc_shutdown_wait * hz); if (error == EWOULDBLOCK) printf("timed out\n"); else printf("done\n"); } static char dumpdevname[sizeof(((struct cdev*)NULL)->si_name)]; SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD, dumpdevname, 0, "Device for kernel dumps"); #ifdef EKCD static struct kerneldumpcrypto * kerneldumpcrypto_create(size_t blocksize, uint8_t encryption, const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey) { struct kerneldumpcrypto *kdc; struct kerneldumpkey *kdk; uint32_t dumpkeysize; dumpkeysize = roundup2(sizeof(*kdk) + encryptedkeysize, blocksize); kdc = malloc(sizeof(*kdc) + dumpkeysize, M_EKCD, M_WAITOK | M_ZERO); arc4rand(kdc->kdc_iv, sizeof(kdc->kdc_iv), 0); kdc->kdc_encryption = encryption; switch (kdc->kdc_encryption) { case KERNELDUMP_ENC_AES_256_CBC: if (rijndael_makeKey(&kdc->kdc_ki, DIR_ENCRYPT, 256, key) <= 0) goto failed; break; default: goto failed; } kdc->kdc_dumpkeysize = dumpkeysize; kdk = kdc->kdc_dumpkey; kdk->kdk_encryption = kdc->kdc_encryption; memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv)); kdk->kdk_encryptedkeysize = htod32(encryptedkeysize); memcpy(kdk->kdk_encryptedkey, encryptedkey, encryptedkeysize); return (kdc); failed: explicit_bzero(kdc, sizeof(*kdc) + dumpkeysize); free(kdc, M_EKCD); return (NULL); } #endif /* EKCD */ int kerneldumpcrypto_init(struct kerneldumpcrypto *kdc) { #ifndef EKCD return (0); #else uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_CTX ctx; struct kerneldumpkey *kdk; int error; error = 0; if (kdc == NULL) return (0); /* * When a user enters ddb it can write a crash dump multiple times. * Each time it should be encrypted using a different IV. */ SHA256_Init(&ctx); SHA256_Update(&ctx, kdc->kdc_iv, sizeof(kdc->kdc_iv)); SHA256_Final(hash, &ctx); bcopy(hash, kdc->kdc_iv, sizeof(kdc->kdc_iv)); switch (kdc->kdc_encryption) { case KERNELDUMP_ENC_AES_256_CBC: if (rijndael_cipherInit(&kdc->kdc_ci, MODE_CBC, kdc->kdc_iv) <= 0) { error = EINVAL; goto out; } break; default: error = EINVAL; goto out; } kdc->kdc_nextoffset = 0; kdk = kdc->kdc_dumpkey; memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv)); out: explicit_bzero(hash, sizeof(hash)); return (error); #endif } uint32_t kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc) { #ifdef EKCD if (kdc == NULL) return (0); return (kdc->kdc_dumpkeysize); #else return (0); #endif } /* Registration of dumpers */ int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td, uint8_t encryption, const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey) { size_t wantcopy; int error; error = priv_check(td, PRIV_SETDUMPER); if (error != 0) return (error); if (di == NULL) { error = 0; goto cleanup; } if (dumper.dumper != NULL) return (EBUSY); dumper = *di; dumper.blockbuf = NULL; dumper.kdc = NULL; if (encryption != KERNELDUMP_ENC_NONE) { #ifdef EKCD dumper.kdc = kerneldumpcrypto_create(di->blocksize, encryption, key, encryptedkeysize, encryptedkey); if (dumper.kdc == NULL) { error = EINVAL; goto cleanup; } #else error = EOPNOTSUPP; goto cleanup; #endif } wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname)); if (wantcopy >= sizeof(dumpdevname)) { printf("set_dumper: device name truncated from '%s' -> '%s'\n", devname, dumpdevname); } dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO); return (0); cleanup: #ifdef EKCD if (dumper.kdc != NULL) { explicit_bzero(dumper.kdc, sizeof(*dumper.kdc) + dumper.kdc->kdc_dumpkeysize); free(dumper.kdc, M_EKCD); } #endif if (dumper.blockbuf != NULL) { explicit_bzero(dumper.blockbuf, dumper.blocksize); free(dumper.blockbuf, M_DUMPER); } explicit_bzero(&dumper, sizeof(dumper)); dumpdevname[0] = '\0'; return (error); } static int dump_check_bounds(struct dumperinfo *di, off_t offset, size_t length) { if (length != 0 && (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { printf("Attempt to write outside dump device boundaries.\n" "offset(%jd), mediaoffset(%jd), length(%ju), mediasize(%jd).\n", (intmax_t)offset, (intmax_t)di->mediaoffset, (uintmax_t)length, (intmax_t)di->mediasize); return (ENOSPC); } return (0); } #ifdef EKCD static int dump_encrypt(struct kerneldumpcrypto *kdc, uint8_t *buf, size_t size) { switch (kdc->kdc_encryption) { case KERNELDUMP_ENC_AES_256_CBC: if (rijndael_blockEncrypt(&kdc->kdc_ci, &kdc->kdc_ki, buf, 8 * size, buf) <= 0) { return (EIO); } if (rijndael_cipherInit(&kdc->kdc_ci, MODE_CBC, buf + size - 16 /* IV size for AES-256-CBC */) <= 0) { return (EIO); } break; default: return (EINVAL); } return (0); } /* Encrypt data and call dumper. */ static int dump_encrypted_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length) { static uint8_t buf[KERNELDUMP_BUFFER_SIZE]; struct kerneldumpcrypto *kdc; int error; size_t nbytes; off_t nextoffset; kdc = di->kdc; error = dump_check_bounds(di, offset, length); if (error != 0) return (error); /* Signal completion. */ if (virtual == NULL && physical == 0 && offset == 0 && length == 0) { return (di->dumper(di->priv, virtual, physical, offset, length)); } /* Data have to be aligned to block size. */ if ((length % di->blocksize) != 0) return (EINVAL); /* * Data have to be written continuously becase we're encrypting using * CBC mode which has this assumption. */ if (kdc->kdc_nextoffset != 0 && kdc->kdc_nextoffset != offset) return (EINVAL); nextoffset = offset + (off_t)length; while (length > 0) { nbytes = MIN(length, sizeof(buf)); bcopy(virtual, buf, nbytes); if (dump_encrypt(kdc, buf, nbytes) != 0) return (EIO); error = di->dumper(di->priv, buf, physical, offset, nbytes); if (error != 0) return (error); offset += nbytes; virtual = (void *)((uint8_t *)virtual + nbytes); length -= nbytes; } kdc->kdc_nextoffset = nextoffset; return (0); } #endif /* EKCD */ /* Call dumper with bounds checking. */ static int dump_raw_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length) { int error; error = dump_check_bounds(di, offset, length); if (error != 0) return (error); return (di->dumper(di->priv, virtual, physical, offset, length)); } int dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length) { #ifdef EKCD if (di->kdc != NULL) { return (dump_encrypted_write(di, virtual, physical, offset, length)); } #endif return (dump_raw_write(di, virtual, physical, offset, length)); } static int dump_pad(struct dumperinfo *di, void *virtual, size_t length, void **buf, size_t *size) { if (length > di->blocksize) return (ENOMEM); *size = di->blocksize; if (length == di->blocksize) { *buf = virtual; } else { *buf = di->blockbuf; memcpy(*buf, virtual, length); memset((uint8_t *)*buf + length, 0, di->blocksize - length); } return (0); } static int dump_raw_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length, size_t *size) { void *buf; int error; error = dump_pad(di, virtual, length, &buf, size); if (error != 0) return (error); return (dump_raw_write(di, buf, physical, offset, *size)); } int dump_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length, size_t *size) { void *buf; int error; error = dump_pad(di, virtual, length, &buf, size); if (error != 0) return (error); return (dump_write(di, buf, physical, offset, *size)); } int dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh, vm_offset_t physical, off_t offset) { size_t size; int ret; ret = dump_raw_write_pad(di, kdh, physical, offset, sizeof(*kdh), &size); if (ret == 0 && size != di->blocksize) ret = EINVAL; return (ret); } int dump_write_key(struct dumperinfo *di, vm_offset_t physical, off_t offset) { #ifndef EKCD return (0); #else /* EKCD */ struct kerneldumpcrypto *kdc; kdc = di->kdc; if (kdc == NULL) return (0); return (dump_raw_write(di, kdc->kdc_dumpkey, physical, offset, kdc->kdc_dumpkeysize)); #endif /* !EKCD */ } void mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, uint64_t dumplen, uint32_t dumpkeysize, uint32_t blksz) { bzero(kdh, sizeof(*kdh)); strlcpy(kdh->magic, magic, sizeof(kdh->magic)); strlcpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); kdh->version = htod32(KERNELDUMPVERSION); kdh->architectureversion = htod32(archver); kdh->dumplength = htod64(dumplen); kdh->dumptime = htod64(time_second); kdh->dumpkeysize = htod32(dumpkeysize); kdh->blocksize = htod32(blksz); strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname)); strlcpy(kdh->versionstring, version, sizeof(kdh->versionstring)); if (panicstr != NULL) strlcpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); kdh->parity = kerneldump_parity(kdh); } #ifdef DDB DB_SHOW_COMMAND(panic, db_show_panic) { if (panicstr == NULL) db_printf("panicstr not set\n"); else db_printf("panic: %s\n", panicstr); } #endif Index: head/sys/modules/drm2/drm2/Makefile =================================================================== --- head/sys/modules/drm2/drm2/Makefile (revision 312926) +++ head/sys/modules/drm2/drm2/Makefile (revision 312927) @@ -1,63 +1,65 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../../dev/drm2 ${.CURDIR}/../../../dev/drm2/ttm KMOD = drm2 SRCS = \ drm_agpsupport.c \ drm_auth.c \ drm_bufs.c \ drm_buffer.c \ drm_context.c \ drm_crtc.c \ drm_crtc_helper.c \ drm_dma.c \ drm_dp_helper.c \ drm_dp_iic_helper.c \ drm_drv.c \ drm_edid.c \ drm_fb_helper.c \ drm_fops.c \ drm_gem.c \ drm_gem_names.c \ drm_global.c \ drm_hashtab.c \ drm_ioctl.c \ drm_irq.c \ drm_linux_list_sort.c \ drm_lock.c \ drm_memory.c \ drm_mm.c \ drm_modes.c \ drm_pci.c \ drm_scatter.c \ drm_stub.c \ drm_sysctl.c \ drm_vm.c \ drm_os_freebsd.c \ ttm_agp_backend.c \ ttm_lock.c \ ttm_object.c \ ttm_tt.c \ ttm_bo_util.c \ ttm_bo.c \ ttm_bo_manager.c \ ttm_execbuf_util.c \ ttm_memory.c \ ttm_page_alloc.c \ ttm_bo_vm.c \ ati_pcigart.c #ttm_page_alloc_dma.c +CFLAGS+= -I${.CURDIR}/../../../compat/linuxkpi/common/include + .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64" SRCS += drm_ioc32.c .endif SRCS +=device_if.h bus_if.h pci_if.h device_if.h iicbus_if.h opt_drm.h \ opt_vm.h opt_compat.h opt_syscons.h .if ${MACHINE_CPUARCH} == "powerpc" CWARNFLAGS+=-Wno-cast-qual .endif .include Index: head/tools/build/Makefile =================================================================== --- head/tools/build/Makefile (revision 312926) +++ head/tools/build/Makefile (revision 312927) @@ -1,57 +1,52 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../include LIB= egacy SRC= INCSGROUPS= INCS SYSINCS INCS= SYSINCSDIR= ${INCLUDEDIR}/sys BOOTSTRAPPING?= 0 _WITH_PWCACHEDB!= grep -c pwcache_groupdb /usr/include/grp.h || true .if ${_WITH_PWCACHEDB} == 0 .PATH: ${.CURDIR}/../../contrib/libc-pwcache CFLAGS+= -I${.CURDIR}/../../contrib/libc-pwcache \ -I${.CURDIR}/../../lib/libc/include SRCS+= pwcache.c .endif _WITH_STRSVIS!= grep -c strsvis /usr/include/vis.h || true .if ${_WITH_STRSVIS} == 0 .PATH: ${.CURDIR}/../../contrib/libc-vis SRCS+= vis.c CFLAGS+= -I${.CURDIR}/../../contrib/libc-vis \ -I${.CURDIR}/../../lib/libc/include .endif _WITH_REALLOCARRAY!= grep -c reallocarray /usr/include/stdlib.h || true .if ${_WITH_REALLOCARRAY} == 0 .PATH: ${.CURDIR}/../../lib/libc/stdlib INCS+= stdlib.h SRCS+= reallocarray.c CFLAGS+= -I${.CURDIR}/../../lib/libc/include .endif _WITH_UTIMENS!= grep -c utimensat /usr/include/sys/stat.h || true .if ${_WITH_UTIMENS} == 0 SYSINCS+= stat.h SRCS+= futimens.c utimensat.c .endif -.if !exists(/usr/include/capsicum_helpers.h) -.PATH: ${.CURDIR}/../../lib/libcapsicum/ -INCS+= capsicum_helpers.h -.endif - .if empty(SRCS) SRCS= dummy.c .endif .if defined(CROSS_BUILD_TESTING) SUBDIR= cross-build .endif .include Index: head/tools/tools/locale/etc/unicode.conf =================================================================== --- head/tools/tools/locale/etc/unicode.conf (revision 312926) +++ head/tools/tools/locale/etc/unicode.conf (revision 312927) @@ -1,4 +1,4 @@ # $FreeBSD$ -cldr /home/bapt/unicode/cldr/30.0.3 -unidata /home/bapt/unicode/UNIDATA/9.0.0 +cldr ~/unicode/cldr/30.0.3 +unidata ~/unicode/UNIDATA/9.0.0 Index: head/usr.bin/getconf/confstr.gperf =================================================================== --- head/usr.bin/getconf/confstr.gperf (revision 312926) +++ head/usr.bin/getconf/confstr.gperf (revision 312927) @@ -1,73 +1,70 @@ %{ /* * Copyright is disclaimed as to the contents of this file. * * $FreeBSD$ */ #include #include #include #include "getconf.h" /* * Override gperf's built-in external scope. */ static const struct map *in_word_set(const char *str); /* * The Standard seems a bit ambiguous over whether the POSIX_V6_* * are specified with or without a leading underscore, so we just * use both. */ %} struct map { const char *name; int key; int valid; }; %% PATH, _CS_PATH POSIX_V6_ILP32_OFF32_CFLAGS, _CS_POSIX_V6_ILP32_OFF32_CFLAGS POSIX_V6_ILP32_OFF32_LDFLAGS, _CS_POSIX_V6_ILP32_OFF32_LDFLAGS POSIX_V6_ILP32_OFF32_LIBS, _CS_POSIX_V6_ILP32_OFF32_LIBS POSIX_V6_ILP32_OFFBIG_CFLAGS, _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS POSIX_V6_ILP32_OFFBIG_LDFLAGS, _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS POSIX_V6_ILP32_OFFBIG_LIBS, _CS_POSIX_V6_ILP32_OFFBIG_LIBS POSIX_V6_LP64_OFF64_CFLAGS, _CS_POSIX_V6_LP64_OFF64_CFLAGS POSIX_V6_LP64_OFF64_LDFLAGS, _CS_POSIX_V6_LP64_OFF64_LDFLAGS POSIX_V6_LP64_OFF64_LIBS, _CS_POSIX_V6_LP64_OFF64_LIBS POSIX_V6_LPBIG_OFFBIG_CFLAGS, _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS POSIX_V6_LPBIG_OFFBIG_LDFLAGS, _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS POSIX_V6_LPBIG_OFFBIG_LIBS, _CS_POSIX_V6_LPBIG_OFFBIG_LIBS POSIX_V6_WIDTH_RESTRICTED_ENVS, _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS _POSIX_V6_ILP32_OFF32_CFLAGS, _CS_POSIX_V6_ILP32_OFF32_CFLAGS _POSIX_V6_ILP32_OFF32_LDFLAGS, _CS_POSIX_V6_ILP32_OFF32_LDFLAGS _POSIX_V6_ILP32_OFF32_LIBS, _CS_POSIX_V6_ILP32_OFF32_LIBS _POSIX_V6_ILP32_OFFBIG_CFLAGS, _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS _POSIX_V6_ILP32_OFFBIG_LDFLAGS, _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS _POSIX_V6_ILP32_OFFBIG_LIBS, _CS_POSIX_V6_ILP32_OFFBIG_LIBS _POSIX_V6_LP64_OFF64_CFLAGS, _CS_POSIX_V6_LP64_OFF64_CFLAGS _POSIX_V6_LP64_OFF64_LDFLAGS, _CS_POSIX_V6_LP64_OFF64_LDFLAGS _POSIX_V6_LP64_OFF64_LIBS, _CS_POSIX_V6_LP64_OFF64_LIBS _POSIX_V6_LPBIG_OFFBIG_CFLAGS, _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS _POSIX_V6_LPBIG_OFFBIG_LDFLAGS, _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS _POSIX_V6_LPBIG_OFFBIG_LIBS, _CS_POSIX_V6_LPBIG_OFFBIG_LIBS _POSIX_V6_WIDTH_RESTRICTED_ENVS, _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS -DARWIN_USER_DIR, _CS_DARWIN_USER_DIR -DARWIN_USER_TEMP_DIR, _CS_DARWIN_USER_TEMP_DIR -DARWIN_USER_CACHE_DIR, _CS_DARWIN_USER_CACHE_DIR %% int find_confstr(const char *name, int *key) { const struct map *rv; rv = in_word_set(name); if (rv != NULL) { if (rv->valid) { *key = rv->key; return 1; } return -1; } return 0; } Index: head/usr.bin/getconf/getconf.c =================================================================== --- head/usr.bin/getconf/getconf.c (revision 312926) +++ head/usr.bin/getconf/getconf.c (revision 312927) @@ -1,189 +1,190 @@ /* * Copyright 2000 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "getconf.h" static void do_confstr(const char *name, int key); static void do_sysconf(const char *name, int key); static void do_pathconf(const char *name, int key, const char *path); static void usage(void) { fprintf(stderr, "usage: getconf [-v prog_env] system_var\n" " getconf [-v prog_env] path_var pathname\n"); exit(EX_USAGE); } int main(int argc, char **argv) { int c, key, valid; const char *name, *vflag, *alt_path; intmax_t limitval; vflag = NULL; while ((c = getopt(argc, argv, "v:")) != -1) { switch (c) { case 'v': vflag = optarg; break; default: usage(); } } if ((name = argv[optind]) == NULL) usage(); if (vflag != NULL) { if ((valid = find_progenv(vflag, &alt_path)) == 0) errx(EX_USAGE, "invalid programming environment %s", vflag); if (valid > 0 && alt_path != NULL) { if (argv[optind + 1] == NULL) execl(alt_path, "getconf", argv[optind], (char *)NULL); else execl(alt_path, "getconf", argv[optind], argv[optind + 1], (char *)NULL); err(EX_OSERR, "execl: %s", alt_path); } if (valid < 0) errx(EX_UNAVAILABLE, "environment %s is not available", vflag); } if (argv[optind + 1] == NULL) { /* confstr or sysconf */ if ((valid = find_limit(name, &limitval)) != 0) { if (valid > 0) printf("%" PRIdMAX "\n", limitval); else printf("undefined\n"); return 0; } if ((valid = find_confstr(name, &key)) != 0) { if (valid > 0) do_confstr(name, key); else printf("undefined\n"); } else { valid = find_sysconf(name, &key); if (valid > 0) { do_sysconf(name, key); } else if (valid < 0) { printf("undefined\n"); } else errx(EX_USAGE, "no such configuration parameter `%s'", name); } } else { valid = find_pathconf(name, &key); if (valid != 0) { if (valid > 0) do_pathconf(name, key, argv[optind + 1]); else printf("undefined\n"); } else errx(EX_USAGE, "no such path configuration parameter `%s'", name); } return 0; } static void do_confstr(const char *name, int key) { size_t len; int savederr; savederr = errno; errno = 0; len = confstr(key, 0, 0); if (len == 0) { if (errno) err(EX_OSERR, "confstr: %s", name); else printf("undefined\n"); } else { char buf[len + 1]; confstr(key, buf, len); printf("%s\n", buf); } errno = savederr; } static void do_sysconf(const char *name, int key) { long value; errno = 0; value = sysconf(key); if (value == -1 && errno != 0) err(EX_OSERR, "sysconf: %s", name); else if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } static void do_pathconf(const char *name, int key, const char *path) { long value; errno = 0; value = pathconf(path, key); if (value == -1 && errno != 0) err(EX_OSERR, "pathconf: %s", name); else if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } + Index: head/usr.bin/getconf/limits.gperf =================================================================== --- head/usr.bin/getconf/limits.gperf (revision 312926) +++ head/usr.bin/getconf/limits.gperf (revision 312927) @@ -1,142 +1,118 @@ %{ /* * Copyright is disclaimed as to the contents of this file. * * $FreeBSD$ */ #include #include #include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ #include "getconf.h" /* * Override gperf's built-in external scope. */ static const struct map *in_word_set(const char *str); %} struct map { const char *name; intmax_t value; int valid; }; %% +_POSIX_CLOCKRES_MIN, _POSIX_CLOCKRES_MIN _POSIX_AIO_LISTIO_MAX, _POSIX_AIO_LISTIO_MAX _POSIX_AIO_MAX, _POSIX_AIO_MAX _POSIX_ARG_MAX, _POSIX_ARG_MAX _POSIX_CHILD_MAX, _POSIX_CHILD_MAX -_POSIX_CLOCKRES_MIN, _POSIX_CLOCKRES_MIN _POSIX_DELAYTIMER_MAX, _POSIX_DELAYTIMER_MAX _POSIX_HOST_NAME_MAX, _POSIX_HOST_NAME_MAX _POSIX_LINK_MAX, _POSIX_LINK_MAX _POSIX_LOGIN_NAME_MAX, _POSIX_LOGIN_NAME_MAX _POSIX_MAX_CANON, _POSIX_MAX_CANON _POSIX_MAX_INPUT, _POSIX_MAX_INPUT _POSIX_MQ_OPEN_MAX, _POSIX_MQ_OPEN_MAX _POSIX_MQ_PRIO_MAX, _POSIX_MQ_PRIO_MAX _POSIX_NAME_MAX, _POSIX_NAME_MAX _POSIX_NGROUPS_MAX, _POSIX_NGROUPS_MAX _POSIX_OPEN_MAX, _POSIX_OPEN_MAX _POSIX_PATH_MAX, _POSIX_PATH_MAX _POSIX_PIPE_BUF, __POSIX_PIPE_BUF _POSIX_RE_DUP_MAX, _POSIX_RE_DUP_MAX _POSIX_RTSIG_MAX, _POSIX_RTSIG_MAX _POSIX_SEM_NSEMS_MAX, _POSIX_SEM_NSEMS_MAX _POSIX_SEM_VALUE_MAX, _POSIX_SEM_VALUE_MAX _POSIX_SIGQUEUE_MAX, _POSIX_SIGQUEUE_MAX _POSIX_SSIZE_MAX, _POSIX_SSIZE_MAX _POSIX_STREAM_MAX, _POSIX_STREAM_MAX _POSIX_SS_REPL_MAX, _POSIX_SS_REPL_MAX _POSIX_SYMLINK_MAX, _POSIX_SYMLINK_MAX _POSIX_SYMLOOP_MAX, _POSIX_SYMLOOP_MAX _POSIX_THREAD_DESTRUCTOR_ITERATIONS, _POSIX_THREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_KEYS_MAX, _POSIX_THREAD_KEYS_MAX _POSIX_THREAD_THREADS_MAX, _POSIX_THREAD_THREADS_MAX _POSIX_TIMER_MAX, _POSIX_TIMER_MAX _POSIX_TRACE_EVENT_NAME_MAX, _POSIX_TRACE_EVENT_NAME_MAX _POSIX_TRACE_NAME_MAX, _POSIX_TRACE_NAME_MAX _POSIX_TRACE_SYS_MAX, _POSIX_TRACE_SYS_MAX _POSIX_TRACE_USER_EVENT_MAX, _POSIX_TRACE_USER_EVENT_MAX _POSIX_TTY_NAME_MAX, _POSIX_TTY_NAME_MAX _POSIX_TZNAME_MAX, _POSIX_TZNAME_MAX _POSIX2_BC_BASE_MAX, _POSIX2_BC_BASE_MAX _POSIX2_BC_DIM_MAX, _POSIX2_BC_DIM_MAX _POSIX2_BC_SCALE_MAX, _POSIX2_BC_SCALE_MAX _POSIX2_BC_STRING_MAX, _POSIX2_BC_STRING_MAX _POSIX2_CHARCLASS_NAME_MAX, _POSIX2_CHARCLASS_NAME_MAX _POSIX2_COLL_WEIGHTS_MAX, _POSIX2_COLL_WEIGHTS_MAX -_POSIX2_EXPR_NEST_MAX, _POSIX2_EXPR_NEST_MAX +_POSIX2_EXPR_NEXT_MAX, _POSIX2_EXPR_NEST_MAX _POSIX2_LINE_MAX, _POSIX2_LINE_MAX _POSIX2_RE_DUP_MAX, _POSIX2_RE_DUP_MAX _XOPEN_IOV_MAX, _XOPEN_IOV_MAX _XOPEN_NAME_MAX, _XOPEN_NAME_MAX _XOPEN_PATH_MAX, _XOPEN_PATH_MAX CHAR_BIT, CHAR_BIT CHAR_MAX, CHAR_MAX CHAR_MIN, CHAR_MIN INT_MAX, INT_MAX INT_MIN, INT_MIN LLONG_MIN, LLONG_MIN LLONG_MAX, LLONG_MAX LONG_BIT, LONG_BIT LONG_MAX, LONG_MAX LONG_MIN, LONG_MIN MB_LEN_MAX, MB_LEN_MAX SCHAR_MAX, SCHAR_MAX SCHAR_MIN, SCHAR_MIN SHRT_MAX, SHRT_MAX SHRT_MIN, SHRT_MIN SSIZE_MAX, SSIZE_MAX UCHAR_MAX, UCHAR_MAX UINT_MAX, UINT_MAX ULLONG_MAX, ULLONG_MAX ULONG_MAX, ULONG_MAX USHRT_MAX, USHRT_MAX WORD_BIT, WORD_BIT CHARCLASS_NAME_MAX, CHARCLASS_NAME_MAX NL_ARGMAX, NL_ARGMAX ML_LANGMAX, NL_LANGMAX NL_MSGMAX, NL_MSGMAX NL_NMAX, NL_NMAX NL_SETMAX, NL_SETMAX NL_TEXTMAX, NL_TEXTMAX NZERO, NZERO %% int find_limit(const char *name, intmax_t *value) { const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ rv = in_word_set(name); if (rv != NULL) { if (rv->valid) { *value = rv->value; return 1; } return -1; } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *value = rv->value; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ return 0; } Index: head/usr.bin/getconf/pathconf.gperf =================================================================== --- head/usr.bin/getconf/pathconf.gperf (revision 312926) +++ head/usr.bin/getconf/pathconf.gperf (revision 312927) @@ -1,88 +1,70 @@ %{ /* * Copyright is disclaimed as to the contents of this file. * * $FreeBSD$ */ #include #include #include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ #include "getconf.h" /* * Override gperf's built-in external scope. */ static const struct map *in_word_set(const char *str); %} struct map { const char *name; int key; int valid; }; %% +ACL_EXTENDED, _PC_ACL_EXTENDED +ACL_NFS4, _PC_ACL_NFS4 +ACL_PATH_MAX, _PC_ACL_PATH_MAX +CAP_PRESENT, _PC_CAP_PRESENT FILESIZEBITS, _PC_FILESIZEBITS +INF_PRESENT, _PC_INF_PRESENT LINK_MAX, _PC_LINK_MAX +MAC_PRESENT, _PC_MAC_PRESENT MAX_CANON, _PC_MAX_CANON MAX_INPUT, _PC_MAX_INPUT +MIN_HOLE_SIZE, _PC_MIN_HOLE_SIZE NAME_MAX, _PC_NAME_MAX PATH_MAX, _PC_PATH_MAX PIPE_BUF, _PC_PIPE_BUF POSIX_ALLOC_SIZE_MIN, _PC_ALLOC_SIZE_MIN POSIX_REC_INCR_XFER_SIZE, _PC_REC_INCR_XFER_SIZE POSIX_REC_MAX_XFER_SIZE, _PC_REC_MAX_XFER_SIZE POSIX_REC_MIN_XFER_SIZE, _PC_REC_MIN_XFER_SIZE POSIX_REC_XFER_ALIGN, _PC_REC_XFER_ALIGN -POSIX2_SYMLINKS, _PC_2_SYMLINKS SYMLINK_MAX, _PC_SYMLINK_MAX TRUSTEDBSD_ACL_EXTENDED, _PC_ACL_EXTENDED +TRUSTEDBSD_ACL_NFS4, _PC_ACL_NFS4 TRUSTEDBSD_ACL_PATH_MAX, _PC_ACL_PATH_MAX TRUSTEDBSD_CAP_PRESENT, _PC_CAP_PRESENT TRUSTEDBSD_INF_PRESENT, _PC_INF_PRESENT TRUSTEDBSD_MAC_PRESENT, _PC_MAC_PRESENT -_POSIX_ASYNC_IO, _PC_ASYNC_IO _POSIX_CHOWN_RESTRICTED, _PC_CHOWN_RESTRICTED _POSIX_NO_TRUNC, _PC_NO_TRUNC -_POSIX_PATH_MAX, _PC_PATH_MAX +_POSIX_VDISABLE, _PC_VDISABLE +_POSIX_ASYNC_IO, _PC_ASYNC_IO _POSIX_PRIO_IO, _PC_PRIO_IO _POSIX_SYNC_IO, _PC_SYNC_IO -_POSIX_VDISABLE, _PC_VDISABLE %% int find_pathconf(const char *name, int *key) { const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ rv = in_word_set(name); if (rv != NULL) { if (rv->valid) { *key = rv->key; return 1; } return -1; } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ return 0; } Index: head/usr.bin/getconf/progenv.gperf =================================================================== --- head/usr.bin/getconf/progenv.gperf (revision 312926) +++ head/usr.bin/getconf/progenv.gperf (revision 312927) @@ -1,70 +1,67 @@ %{ /* * Copyright is disclaimed as to the contents of this file. * * $FreeBSD$ */ #include #include #include #include "getconf.h" /* * Override gperf's built-in external scope. */ static const struct map *in_word_set(const char *str); /* * The Standard seems a bit ambiguous over whether the POSIX_V6_* * are specified with or without a leading underscore, so we just * use both. */ /* * The alt_path member gives the path containing another `getconf' * executable which was compiled using the specified programming * environment. If it is NULL, the current executable is good enough. * If we ever support multiple environments, this table will need to * be updated. (We cheat here and define the supported environments * statically.) */ -#if defined(__alpha__) || defined(__sparc64__) +#if defined(__sparc64__) || defined(__amd64__) #define have_LP64_OFF64 NULL -#elif defined(__APPLE__) -#define have_LP64_OFF64 NULL -#define have_LPBIG_OFFBIG NULL #endif -#if defined(__i386__) || defined(__powerpc__) || defined(__x86_64__) +#if defined(__i386__) || defined(__powerpc__) #define have_ILP32_OFFBIG NULL #endif %} struct map { const char *name; const char *alt_path; int valid; }; %% POSIX_V6_ILP32_OFF32, notdef POSIX_V6_ILP32_OFFBIG, have_ILP32_OFFBIG POSIX_V6_LP64_OFF64, have_LP64_OFF64 -POSIX_V6_LPBIG_OFFBIG, have_LPBIG_OFFBIG +POSIX_V6_LPBIG_OFFBIG, notdef _POSIX_V6_ILP32_OFF32, notdef _POSIX_V6_ILP32_OFFBIG, have_ILP32_OFFBIG _POSIX_V6_LP64_OFF64, have_LP64_OFF64 -_POSIX_V6_LPBIG_OFFBIG, have_LPBIG_OFFBIG +_POSIX_V6_LPBIG_OFFBIG, notdef %% int find_progenv(const char *name, const char **alt_path) { const struct map *rv; rv = in_word_set(name); if (rv != NULL) { if (rv->valid) { *alt_path = rv->alt_path; return 1; } return -1; } return 0; } Index: head/usr.bin/getconf/sysconf.gperf =================================================================== --- head/usr.bin/getconf/sysconf.gperf (revision 312926) +++ head/usr.bin/getconf/sysconf.gperf (revision 312927) @@ -1,187 +1,149 @@ %{ /* * Copyright is disclaimed as to the contents of this file. * * $FreeBSD$ */ #include #include #include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ #include "getconf.h" /* * Override gperf's built-in external scope. */ static const struct map *in_word_set(const char *str); %} struct map { const char *name; int key; int valid; }; %% AIO_LISTIO_MAX, _SC_AIO_LISTIO_MAX AIO_MAX, _SC_AIO_MAX AIO_PRIO_DELTA_MAX, _SC_AIO_PRIO_DELTA_MAX ARG_MAX, _SC_ARG_MAX ATEXIT_MAX, _SC_ATEXIT_MAX BC_BASE_MAX, _SC_BC_BASE_MAX BC_DIM_MAX, _SC_BC_DIM_MAX BC_SCALE_MAX, _SC_BC_SCALE_MAX BC_STRING_MAX, _SC_BC_STRING_MAX CHILD_MAX, _SC_CHILD_MAX CLK_TCK, _SC_CLK_TCK COLL_WEIGHTS_MAX, _SC_COLL_WEIGHTS_MAX DELAYTIMER_MAX, _SC_DELAYTIMER_MAX EXPR_NEST_MAX, _SC_EXPR_NEST_MAX GETGR_R_SIZE_MAX, _SC_GETGR_R_SIZE_MAX GETPW_R_SIZE_MAX, _SC_GETPW_R_SIZE_MAX HOST_NAME_MAX, _SC_HOST_NAME_MAX IOV_MAX, _SC_IOV_MAX LINE_MAX, _SC_LINE_MAX LOGIN_NAME_MAX, _SC_LOGIN_NAME_MAX MQ_OPEN_MAX, _SC_MQ_OPEN_MAX MQ_PRIO_MAX, _SC_MQ_PRIO_MAX NGROUPS_MAX, _SC_NGROUPS_MAX NPROCESSORS_CONF, _SC_NPROCESSORS_CONF NPROCESSORS_ONLN, _SC_NPROCESSORS_ONLN OPEN_MAX, _SC_OPEN_MAX PAGESIZE, _SC_PAGESIZE PAGE_SIZE, _SC_PAGESIZE PASS_MAX, _SC_PASS_MAX PTHREAD_DESTRUCTOR_ITERATIONS, _SC_THREAD_DESTRUCTOR_ITERATIONS PTHREAD_KEYS_MAX, _SC_THREAD_KEYS_MAX PTHREAD_STACK_MIN, _SC_THREAD_STACK_MIN PTHREAD_THREADS_MAX, _SC_THREAD_THREADS_MAX RE_DUP_MAX, _SC_RE_DUP_MAX RTSIG_MAX, _SC_RTSIG_MAX SEM_NSEMS_MAX, _SC_SEM_NSEMS_MAX SEM_VALUE_MAX, _SC_SEM_VALUE_MAX SIGQUEUE_MAX, _SC_SIGQUEUE_MAX STREAM_MAX, _SC_STREAM_MAX SYMLOOP_MAX, _SC_SYMLOOP_MAX TIMER_MAX, _SC_TIMER_MAX TTY_NAME_MAX, _SC_TTY_NAME_MAX TZNAME_MAX, _SC_TZNAME_MAX _POSIX2_CHAR_TERM, _SC_2_CHAR_TERM _POSIX2_C_BIND, _SC_2_C_BIND _POSIX2_C_DEV, _SC_2_C_DEV _POSIX2_C_VERSION, _SC_2_C_VERSION _POSIX2_FORT_DEV, _SC_2_FORT_DEV _POSIX2_FORT_RUN, _SC_2_FORT_RUN _POSIX2_LOCALEDEF, _SC_2_LOCALEDEF -_POSIX2_PBS, _SC_PBS -_POSIX2_PBS_ACCOUNTING, _SC_PBS_ACCOUNTING -_POSIX2_PBS_CHECKPOINT, _SC_PBS_CHECKPOINT -_POSIX2_PBS_LOCATE, _SC_PBS_LOCATE -_POSIX2_PBS_MESSAGE, _SC_PBS_MESSAGE -_POSIX2_PBS_TRACK, _SC_PBS_TRACK _POSIX2_SW_DEV, _SC_2_SW_DEV _POSIX2_UPE, _SC_2_UPE _POSIX2_VERSION, _SC_2_VERSION -_POSIX_ADVISORY_INFO, _SC_ADVISORY_INFO _POSIX_ASYNCHRONOUS_IO, _SC_ASYNCHRONOUS_IO _POSIX_BARRIERS, _SC_BARRIERS _POSIX_CLOCK_SELECTION, _SC_CLOCK_SELECTION _POSIX_CPUTIME, _SC_CPUTIME _POSIX_FILE_LOCKING, _SC_FILE_LOCKING _POSIX_FSYNC, _SC_FSYNC _POSIX_IPV6, _SC_IPV6 _POSIX_JOB_CONTROL, _SC_JOB_CONTROL _POSIX_MAPPED_FILES, _SC_MAPPED_FILES _POSIX_MEMLOCK, _SC_MEMLOCK _POSIX_MEMLOCK_RANGE, _SC_MEMLOCK_RANGE _POSIX_MEMORY_PROTECTION, _SC_MEMORY_PROTECTION _POSIX_MESSAGE_PASSING, _SC_MESSAGE_PASSING _POSIX_MONOTONIC_CLOCK, _SC_MONOTONIC_CLOCK _POSIX_PRIORITIZED_IO, _SC_PRIORITIZED_IO _POSIX_PRIORITY_SCHEDULING, _SC_PRIORITY_SCHEDULING -_POSIX_RAW_SOCKETS, _SC_RAW_SOCKETS _POSIX_READER_WRITER_LOCKS, _SC_READER_WRITER_LOCKS _POSIX_REALTIME_SIGNALS, _SC_REALTIME_SIGNALS _POSIX_REGEXP, _SC_REGEXP _POSIX_SAVED_IDS, _SC_SAVED_IDS _POSIX_SEMAPHORES, _SC_SEMAPHORES _POSIX_SHARED_MEMORY_OBJECTS, _SC_SHARED_MEMORY_OBJECTS _POSIX_SHELL, _SC_SHELL _POSIX_SPAWN, _SC_SPAWN _POSIX_SPIN_LOCKS, _SC_SPIN_LOCKS _POSIX_SPORADIC_SERVER, _SC_SPORADIC_SERVER -_POSIX_SS_REPL_MAX, _SC_SS_REPL_MAX _POSIX_SYNCHRONIZED_IO, _SC_SYNCHRONIZED_IO _POSIX_THREADS, _SC_THREADS _POSIX_THREAD_ATTR_STACKADDR, _SC_THREAD_ATTR_STACKADDR _POSIX_THREAD_ATTR_STACKSIZE, _SC_THREAD_ATTR_STACKSIZE _POSIX_THREAD_CPUTIME, _SC_THREAD_CPUTIME _POSIX_THREAD_PRIORITY_SCHEDULING, _SC_THREAD_PRIORITY_SCHEDULING _POSIX_THREAD_PRIO_INHERIT, _SC_THREAD_PRIO_INHERIT _POSIX_THREAD_PRIO_PROTECT, _SC_THREAD_PRIO_PROTECT _POSIX_THREAD_PROCESS_SHARED, _SC_THREAD_PROCESS_SHARED _POSIX_THREAD_SAFE_FUNCTIONS, _SC_THREAD_SAFE_FUNCTIONS _POSIX_THREAD_SPORADIC_SERVER, _SC_THREAD_SPORADIC_SERVER _POSIX_TIMEOUTS, _SC_TIMEOUTS -_POSIX_TIMERS, _SC_TIMERS _POSIX_TRACE, _SC_TRACE _POSIX_TRACE_EVENT_FILTER, _SC_TRACE_EVENT_FILTER -_POSIX_TRACE_EVENT_NAME_MAX, _SC_TRACE_EVENT_NAME_MAX _POSIX_TRACE_INHERIT, _SC_TRACE_INHERIT _POSIX_TRACE_LOG, _SC_TRACE_LOG -_POSIX_TRACE_NAME_MAX, _SC_TRACE_NAME_MAX -_POSIX_TRACE_SYS_MAX, _SC_TRACE_SYS_MAX -_POSIX_TRACE_USER_EVENT_MAX, _SC_TRACE_USER_EVENT_MAX +_POSIX_TIMERS, _SC_TIMERS _POSIX_TYPED_MEMORY_OBJECTS, _SC_TYPED_MEMORY_OBJECTS +_POSIX_VERSION, _SC_VERSION _POSIX_V6_ILP32_OFF32, _SC_V6_ILP32_OFF32 _POSIX_V6_ILP32_OFFBIG, _SC_V6_ILP32_OFFBIG _POSIX_V6_LP64_OFF64, _SC_V6_LP64_OFF64 _POSIX_V6_LPBIG_OFFBIG, _SC_V6_LPBIG_OFFBIG -_POSIX_VERSION, _SC_VERSION _XOPEN_CRYPT, _SC_XOPEN_CRYPT _XOPEN_ENH_I18N, _SC_XOPEN_ENH_I18N _XOPEN_LEGACY, _SC_XOPEN_LEGACY _XOPEN_REALTIME, _SC_XOPEN_REALTIME _XOPEN_REALTIME_THREADS, _SC_XOPEN_REALTIME_THREADS _XOPEN_SHM, _SC_XOPEN_SHM -_XOPEN_STREAMS, _SC_XOPEN_STREAMS _XOPEN_UNIX, _SC_XOPEN_UNIX _XOPEN_VERSION, _SC_XOPEN_VERSION _XOPEN_XCU_VERSION, _SC_XCU_VERSION %% int find_sysconf(const char *name, int *key) { const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ rv = in_word_set(name); if (rv != NULL) { if (rv->valid) { *key = rv->key; return 1; } return -1; } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ return 0; } Index: head/usr.sbin/nscd/nscd.c =================================================================== --- head/usr.sbin/nscd/nscd.c (revision 312926) +++ head/usr.sbin/nscd/nscd.c (revision 312927) @@ -1,869 +1,870 @@ /*- * Copyright (c) 2005 Michael Bushkov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in thereg * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "agents/passwd.h" #include "agents/group.h" #include "agents/services.h" #include "cachelib.h" #include "config.h" #include "debug.h" #include "log.h" #include "nscdcli.h" #include "parser.h" #include "query.h" #include "singletons.h" #ifndef CONFIG_PATH #define CONFIG_PATH "/etc/nscd.conf" #endif #define DEFAULT_CONFIG_PATH "nscd.conf" #define MAX_SOCKET_IO_SIZE 4096 struct processing_thread_args { cache the_cache; struct configuration *the_configuration; struct runtime_env *the_runtime_env; }; static void accept_connection(struct kevent *, struct runtime_env *, struct configuration *); static void destroy_cache_(cache); static void destroy_runtime_env(struct runtime_env *); static cache init_cache_(struct configuration *); static struct runtime_env *init_runtime_env(struct configuration *); static void processing_loop(cache, struct runtime_env *, struct configuration *); static void process_socket_event(struct kevent *, struct runtime_env *, struct configuration *); static void process_timer_event(struct kevent *, struct runtime_env *, struct configuration *); static void *processing_thread(void *); static void usage(void); void get_time_func(struct timeval *); static void usage(void) { fprintf(stderr, "usage: nscd [-dnst] [-i cachename] [-I cachename]\n"); exit(1); } static cache init_cache_(struct configuration *config) { struct cache_params params; cache retval; struct configuration_entry *config_entry; size_t size, i; int res; TRACE_IN(init_cache_); memset(¶ms, 0, sizeof(struct cache_params)); params.get_time_func = get_time_func; retval = init_cache(¶ms); size = configuration_get_entries_size(config); for (i = 0; i < size; ++i) { config_entry = configuration_get_entry(config, i); /* * We should register common entries now - multipart entries * would be registered automatically during the queries. */ res = register_cache_entry(retval, (struct cache_entry_params *) &config_entry->positive_cache_params); config_entry->positive_cache_entry = find_cache_entry(retval, config_entry->positive_cache_params.cep.entry_name); assert(config_entry->positive_cache_entry != INVALID_CACHE_ENTRY); res = register_cache_entry(retval, (struct cache_entry_params *) &config_entry->negative_cache_params); config_entry->negative_cache_entry = find_cache_entry(retval, config_entry->negative_cache_params.cep.entry_name); assert(config_entry->negative_cache_entry != INVALID_CACHE_ENTRY); } LOG_MSG_2("cache", "cache was successfully initialized"); TRACE_OUT(init_cache_); return (retval); } static void destroy_cache_(cache the_cache) { TRACE_IN(destroy_cache_); destroy_cache(the_cache); TRACE_OUT(destroy_cache_); } /* * Socket and kqueues are prepared here. We have one global queue for both * socket and timers events. */ static struct runtime_env * init_runtime_env(struct configuration *config) { int serv_addr_len; struct sockaddr_un serv_addr; struct kevent eventlist; struct timespec timeout; struct runtime_env *retval; TRACE_IN(init_runtime_env); retval = calloc(1, sizeof(*retval)); assert(retval != NULL); - retval->sockfd = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0); + retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); if (config->force_unlink == 1) unlink(config->socket_path); memset(&serv_addr, 0, sizeof(struct sockaddr_un)); serv_addr.sun_family = PF_LOCAL; strlcpy(serv_addr.sun_path, config->socket_path, sizeof(serv_addr.sun_path)); serv_addr_len = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path) + 1; if (bind(retval->sockfd, (struct sockaddr *)&serv_addr, serv_addr_len) == -1) { close(retval->sockfd); free(retval); LOG_ERR_2("runtime environment", "can't bind socket to path: " "%s", config->socket_path); TRACE_OUT(init_runtime_env); return (NULL); } LOG_MSG_2("runtime environment", "using socket %s", config->socket_path); /* * Here we're marking socket as non-blocking and setting its backlog * to the maximum value */ chmod(config->socket_path, config->socket_mode); listen(retval->sockfd, -1); + fcntl(retval->sockfd, F_SETFL, O_NONBLOCK); retval->queue = kqueue(); assert(retval->queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0); memset(&timeout, 0, sizeof(struct timespec)); kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); LOG_MSG_2("runtime environment", "successfully initialized"); TRACE_OUT(init_runtime_env); return (retval); } static void destroy_runtime_env(struct runtime_env *env) { TRACE_IN(destroy_runtime_env); close(env->queue); close(env->sockfd); free(env); TRACE_OUT(destroy_runtime_env); } static void accept_connection(struct kevent *event_data, struct runtime_env *env, struct configuration *config) { struct kevent eventlist[2]; struct timespec timeout; struct query_state *qstate; int fd; int res; uid_t euid; gid_t egid; TRACE_IN(accept_connection); fd = accept(event_data->ident, NULL, NULL); if (fd == -1) { LOG_ERR_2("accept_connection", "error %d during accept()", errno); TRACE_OUT(accept_connection); return; } if (getpeereid(fd, &euid, &egid) != 0) { LOG_ERR_2("accept_connection", "error %d during getpeereid()", errno); TRACE_OUT(accept_connection); return; } qstate = init_query_state(fd, sizeof(int), euid, egid); if (qstate == NULL) { LOG_ERR_2("accept_connection", "can't init query_state"); TRACE_OUT(accept_connection); return; } memset(&timeout, 0, sizeof(struct timespec)); EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, qstate->timeout.tv_sec * 1000, qstate); EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, qstate->kevent_watermark, qstate); res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout); if (res < 0) LOG_ERR_2("accept_connection", "kevent error"); TRACE_OUT(accept_connection); } static void process_socket_event(struct kevent *event_data, struct runtime_env *env, struct configuration *config) { struct kevent eventlist[2]; struct timeval query_timeout; struct timespec kevent_timeout; int nevents; int eof_res, res; ssize_t io_res; struct query_state *qstate; TRACE_IN(process_socket_event); eof_res = event_data->flags & EV_EOF ? 1 : 0; res = 0; memset(&kevent_timeout, 0, sizeof(struct timespec)); EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout); if (nevents == -1) { if (errno == ENOENT) { /* the timer is already handling this event */ TRACE_OUT(process_socket_event); return; } else { /* some other error happened */ LOG_ERR_2("process_socket_event", "kevent error, errno" " is %d", errno); TRACE_OUT(process_socket_event); return; } } qstate = (struct query_state *)event_data->udata; /* * If the buffer that is to be send/received is too large, * we send it implicitly, by using query_io_buffer_read and * query_io_buffer_write functions in the query_state. These functions * use the temporary buffer, which is later send/received in parts. * The code below implements buffer splitting/mergind for send/receive * operations. It also does the actual socket IO operations. */ if (((qstate->use_alternate_io == 0) && (qstate->kevent_watermark <= (size_t)event_data->data)) || ((qstate->use_alternate_io != 0) && (qstate->io_buffer_watermark <= (size_t)event_data->data))) { if (qstate->use_alternate_io != 0) { switch (qstate->io_buffer_filter) { case EVFILT_READ: io_res = query_socket_read(qstate, qstate->io_buffer_p, qstate->io_buffer_watermark); if (io_res < 0) { qstate->use_alternate_io = 0; qstate->process_func = NULL; } else { qstate->io_buffer_p += io_res; if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) { qstate->io_buffer_p = qstate->io_buffer; qstate->use_alternate_io = 0; } } break; default: break; } } if (qstate->use_alternate_io == 0) { do { res = qstate->process_func(qstate); } while ((qstate->kevent_watermark == 0) && (qstate->process_func != NULL) && (res == 0)); if (res != 0) qstate->process_func = NULL; } if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_filter == EVFILT_WRITE)) { io_res = query_socket_write(qstate, qstate->io_buffer_p, qstate->io_buffer_watermark); if (io_res < 0) { qstate->use_alternate_io = 0; qstate->process_func = NULL; } else qstate->io_buffer_p += io_res; } } else { /* assuming that socket was closed */ qstate->process_func = NULL; qstate->use_alternate_io = 0; } if (((qstate->process_func == NULL) && (qstate->use_alternate_io == 0)) || (eof_res != 0) || (res != 0)) { destroy_query_state(qstate); close(event_data->ident); TRACE_OUT(process_socket_event); return; } /* updating the query_state lifetime variable */ get_time_func(&query_timeout); query_timeout.tv_usec = 0; query_timeout.tv_sec -= qstate->creation_time.tv_sec; if (query_timeout.tv_sec > qstate->timeout.tv_sec) query_timeout.tv_sec = 0; else query_timeout.tv_sec = qstate->timeout.tv_sec - query_timeout.tv_sec; if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size)) qstate->use_alternate_io = 0; if (qstate->use_alternate_io == 0) { /* * If we must send/receive the large block of data, * we should prepare the query_state's io_XXX fields. * We should also substitute its write_func and read_func * with the query_io_buffer_write and query_io_buffer_read, * which will allow us to implicitly send/receive this large * buffer later (in the subsequent calls to the * process_socket_event). */ if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) { if (qstate->io_buffer != NULL) free(qstate->io_buffer); qstate->io_buffer = calloc(1, qstate->kevent_watermark); assert(qstate->io_buffer != NULL); qstate->io_buffer_p = qstate->io_buffer; qstate->io_buffer_size = qstate->kevent_watermark; qstate->io_buffer_filter = qstate->kevent_filter; qstate->write_func = query_io_buffer_write; qstate->read_func = query_io_buffer_read; if (qstate->kevent_filter == EVFILT_READ) qstate->use_alternate_io = 1; qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; EV_SET(&eventlist[1], event_data->ident, qstate->kevent_filter, EV_ADD | EV_ONESHOT, NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); } else { EV_SET(&eventlist[1], event_data->ident, qstate->kevent_filter, EV_ADD | EV_ONESHOT, NOTE_LOWAT, qstate->kevent_watermark, qstate); } } else { if (qstate->io_buffer + qstate->io_buffer_size - qstate->io_buffer_p < MAX_SOCKET_IO_SIZE) { qstate->io_buffer_watermark = qstate->io_buffer + qstate->io_buffer_size - qstate->io_buffer_p; EV_SET(&eventlist[1], event_data->ident, qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, NOTE_LOWAT, qstate->io_buffer_watermark, qstate); } else { qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; EV_SET(&eventlist[1], event_data->ident, qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); } } EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate); kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout); TRACE_OUT(process_socket_event); } /* * This routine is called if timer event has been signaled in the kqueue. It * just closes the socket and destroys the query_state. */ static void process_timer_event(struct kevent *event_data, struct runtime_env *env, struct configuration *config) { struct query_state *qstate; TRACE_IN(process_timer_event); qstate = (struct query_state *)event_data->udata; destroy_query_state(qstate); close(event_data->ident); TRACE_OUT(process_timer_event); } /* * Processing loop is the basic processing routine, that forms a body of each * procssing thread */ static void processing_loop(cache the_cache, struct runtime_env *env, struct configuration *config) { struct timespec timeout; const int eventlist_size = 1; struct kevent eventlist[eventlist_size]; int nevents, i; TRACE_MSG("=> processing_loop"); memset(&timeout, 0, sizeof(struct timespec)); memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size); for (;;) { nevents = kevent(env->queue, NULL, 0, eventlist, eventlist_size, NULL); /* * we can only receive 1 event on success */ if (nevents == 1) { struct kevent *event_data; event_data = &eventlist[0]; if ((int)event_data->ident == env->sockfd) { for (i = 0; i < event_data->data; ++i) accept_connection(event_data, env, config); EV_SET(eventlist, s_runtime_env->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0); memset(&timeout, 0, sizeof(struct timespec)); kevent(s_runtime_env->queue, eventlist, 1, NULL, 0, &timeout); } else { switch (event_data->filter) { case EVFILT_READ: case EVFILT_WRITE: process_socket_event(event_data, env, config); break; case EVFILT_TIMER: process_timer_event(event_data, env, config); break; default: break; } } } else { /* this branch shouldn't be currently executed */ } } TRACE_MSG("<= processing_loop"); } /* * Wrapper above the processing loop function. It sets the thread signal mask * to avoid SIGPIPE signals (which can happen if the client works incorrectly). */ static void * processing_thread(void *data) { struct processing_thread_args *args; sigset_t new; TRACE_MSG("=> processing_thread"); args = (struct processing_thread_args *)data; sigemptyset(&new); sigaddset(&new, SIGPIPE); if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) LOG_ERR_1("processing thread", "thread can't block the SIGPIPE signal"); processing_loop(args->the_cache, args->the_runtime_env, args->the_configuration); free(args); TRACE_MSG("<= processing_thread"); return (NULL); } void get_time_func(struct timeval *time) { struct timespec res; memset(&res, 0, sizeof(struct timespec)); clock_gettime(CLOCK_MONOTONIC, &res); time->tv_sec = res.tv_sec; time->tv_usec = 0; } /* * The idea of _nss_cache_cycle_prevention_function is that nsdispatch * will search for this symbol in the executable. This symbol is the * attribute of the caching daemon. So, if it exists, nsdispatch won't try * to connect to the caching daemon and will just ignore the 'cache' * source in the nsswitch.conf. This method helps to avoid cycles and * organize self-performing requests. * * (not actually a function; it used to be, but it doesn't make any * difference, as long as it has external linkage) */ void *_nss_cache_cycle_prevention_function; int main(int argc, char *argv[]) { struct processing_thread_args *thread_args; pthread_t *threads; struct pidfh *pidfile; pid_t pid; char const *config_file; char const *error_str; int error_line; int i, res; int trace_mode_enabled; int force_single_threaded; int do_not_daemonize; int clear_user_cache_entries, clear_all_cache_entries; char *user_config_entry_name, *global_config_entry_name; int show_statistics; int daemon_mode, interactive_mode; /* by default all debug messages are omitted */ TRACE_OFF(); /* parsing command line arguments */ trace_mode_enabled = 0; force_single_threaded = 0; do_not_daemonize = 0; clear_user_cache_entries = 0; clear_all_cache_entries = 0; show_statistics = 0; user_config_entry_name = NULL; global_config_entry_name = NULL; while ((res = getopt(argc, argv, "nstdi:I:")) != -1) { switch (res) { case 'n': do_not_daemonize = 1; break; case 's': force_single_threaded = 1; break; case 't': trace_mode_enabled = 1; break; case 'i': clear_user_cache_entries = 1; if (optarg != NULL) if (strcmp(optarg, "all") != 0) user_config_entry_name = strdup(optarg); break; case 'I': clear_all_cache_entries = 1; if (optarg != NULL) if (strcmp(optarg, "all") != 0) global_config_entry_name = strdup(optarg); break; case 'd': show_statistics = 1; break; case '?': default: usage(); /* NOT REACHED */ } } daemon_mode = do_not_daemonize | force_single_threaded | trace_mode_enabled; interactive_mode = clear_user_cache_entries | clear_all_cache_entries | show_statistics; if ((daemon_mode != 0) && (interactive_mode != 0)) { LOG_ERR_1("main", "daemon mode and interactive_mode arguments " "can't be used together"); usage(); } if (interactive_mode != 0) { FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r"); char pidbuf[256]; struct nscd_connection_params connection_params; nscd_connection connection; int result; if (pidfin == NULL) errx(EXIT_FAILURE, "There is no daemon running."); memset(pidbuf, 0, sizeof(pidbuf)); fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin); fclose(pidfin); if (ferror(pidfin) != 0) errx(EXIT_FAILURE, "Can't read from pidfile."); if (sscanf(pidbuf, "%d", &pid) != 1) errx(EXIT_FAILURE, "Invalid pidfile."); LOG_MSG_1("main", "daemon PID is %d", pid); memset(&connection_params, 0, sizeof(struct nscd_connection_params)); connection_params.socket_path = DEFAULT_SOCKET_PATH; connection = open_nscd_connection__(&connection_params); if (connection == INVALID_NSCD_CONNECTION) errx(EXIT_FAILURE, "Can't connect to the daemon."); if (clear_user_cache_entries != 0) { result = nscd_transform__(connection, user_config_entry_name, TT_USER); if (result != 0) LOG_MSG_1("main", "user cache transformation failed"); else LOG_MSG_1("main", "user cache_transformation " "succeeded"); } if (clear_all_cache_entries != 0) { if (geteuid() != 0) errx(EXIT_FAILURE, "Only root can initiate " "global cache transformation."); result = nscd_transform__(connection, global_config_entry_name, TT_ALL); if (result != 0) LOG_MSG_1("main", "global cache transformation " "failed"); else LOG_MSG_1("main", "global cache transformation " "succeeded"); } close_nscd_connection__(connection); free(user_config_entry_name); free(global_config_entry_name); return (EXIT_SUCCESS); } pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid); if (pidfile == NULL) { if (errno == EEXIST) errx(EXIT_FAILURE, "Daemon already running, pid: %d.", pid); warn("Cannot open or create pidfile"); } if (trace_mode_enabled == 1) TRACE_ON(); /* blocking the main thread from receiving SIGPIPE signal */ sigblock(sigmask(SIGPIPE)); /* daemonization */ if (do_not_daemonize == 0) { res = daemon(0, trace_mode_enabled == 0 ? 0 : 1); if (res != 0) { LOG_ERR_1("main", "can't daemonize myself: %s", strerror(errno)); pidfile_remove(pidfile); goto fin; } else LOG_MSG_1("main", "successfully daemonized"); } pidfile_write(pidfile); s_agent_table = init_agent_table(); register_agent(s_agent_table, init_passwd_agent()); register_agent(s_agent_table, init_passwd_mp_agent()); register_agent(s_agent_table, init_group_agent()); register_agent(s_agent_table, init_group_mp_agent()); register_agent(s_agent_table, init_services_agent()); register_agent(s_agent_table, init_services_mp_agent()); LOG_MSG_1("main", "request agents registered successfully"); /* * Hosts agent can't work properly until we have access to the * appropriate dtab structures, which are used in nsdispatch * calls * register_agent(s_agent_table, init_hosts_agent()); */ /* configuration initialization */ s_configuration = init_configuration(); fill_configuration_defaults(s_configuration); error_str = NULL; error_line = 0; config_file = CONFIG_PATH; res = parse_config_file(s_configuration, config_file, &error_str, &error_line); if ((res != 0) && (error_str == NULL)) { config_file = DEFAULT_CONFIG_PATH; res = parse_config_file(s_configuration, config_file, &error_str, &error_line); } if (res != 0) { if (error_str != NULL) { LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n", config_file, error_line, error_str); } else { LOG_ERR_1("main", "no configuration file found " "- was looking for %s and %s", CONFIG_PATH, DEFAULT_CONFIG_PATH); } destroy_configuration(s_configuration); return (-1); } if (force_single_threaded == 1) s_configuration->threads_num = 1; /* cache initialization */ s_cache = init_cache_(s_configuration); if (s_cache == NULL) { LOG_ERR_1("main", "can't initialize the cache"); destroy_configuration(s_configuration); return (-1); } /* runtime environment initialization */ s_runtime_env = init_runtime_env(s_configuration); if (s_runtime_env == NULL) { LOG_ERR_1("main", "can't initialize the runtime environment"); destroy_configuration(s_configuration); destroy_cache_(s_cache); return (-1); } if (s_configuration->threads_num > 1) { threads = calloc(1, sizeof(*threads) * s_configuration->threads_num); for (i = 0; i < s_configuration->threads_num; ++i) { thread_args = malloc( sizeof(*thread_args)); thread_args->the_cache = s_cache; thread_args->the_runtime_env = s_runtime_env; thread_args->the_configuration = s_configuration; LOG_MSG_1("main", "thread #%d was successfully created", i); pthread_create(&threads[i], NULL, processing_thread, thread_args); thread_args = NULL; } for (i = 0; i < s_configuration->threads_num; ++i) pthread_join(threads[i], NULL); } else { LOG_MSG_1("main", "working in single-threaded mode"); processing_loop(s_cache, s_runtime_env, s_configuration); } fin: /* runtime environment destruction */ destroy_runtime_env(s_runtime_env); /* cache destruction */ destroy_cache_(s_cache); /* configuration destruction */ destroy_configuration(s_configuration); /* agents table destruction */ destroy_agent_table(s_agent_table); pidfile_remove(pidfile); return (EXIT_SUCCESS); } Index: head/usr.sbin/nscd/nscdcli.c =================================================================== --- head/usr.sbin/nscd/nscdcli.c (revision 312926) +++ head/usr.sbin/nscd/nscdcli.c (revision 312927) @@ -1,286 +1,287 @@ /*- * Copyright (c) 2005 Michael Bushkov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "nscdcli.h" #include "protocol.h" #define DEFAULT_NSCD_IO_TIMEOUT 4 static int safe_write(struct nscd_connection_ *, const void *, size_t); static int safe_read(struct nscd_connection_ *, void *, size_t); static int send_credentials(struct nscd_connection_ *, int); static int safe_write(struct nscd_connection_ *connection, const void *data, size_t data_size) { struct kevent eventlist; int nevents; size_t result; ssize_t s_result; struct timespec timeout; if (data_size == 0) return (0); timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, &timeout); if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { s_result = write(connection->sockfd, (char *)data + result, (size_t)eventlist.data < data_size - result ? (size_t)eventlist.data : data_size - result); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } static int safe_read(struct nscd_connection_ *connection, void *data, size_t data_size) { struct kevent eventlist; size_t result; ssize_t s_result; struct timespec timeout; int nevents; if (data_size == 0) return (0); timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1, &timeout); if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) { s_result = read(connection->sockfd, (char *)data + result, (size_t)eventlist.data <= data_size - result ? (size_t)eventlist.data : data_size - result); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } static int send_credentials(struct nscd_connection_ *connection, int type) { struct kevent eventlist; int nevents; ssize_t result; int res; struct msghdr cred_hdr; struct iovec iov; struct { struct cmsghdr hdr; struct cmsgcred creds; } cmsg; TRACE_IN(send_credentials); memset(&cmsg, 0, sizeof(cmsg)); cmsg.hdr.cmsg_len = sizeof(cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; memset(&cred_hdr, 0, sizeof(struct msghdr)); cred_hdr.msg_iov = &iov; cred_hdr.msg_iovlen = 1; cred_hdr.msg_control = &cmsg; cred_hdr.msg_controllen = sizeof(cmsg); iov.iov_base = &type; iov.iov_len = sizeof(int); EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, NOTE_LOWAT, sizeof(int), NULL); res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL); if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1 : 0; EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); TRACE_OUT(send_credentials); return (result); } else { TRACE_OUT(send_credentials); return (-1); } } struct nscd_connection_ * open_nscd_connection__(struct nscd_connection_params const *params) { struct nscd_connection_ *retval; struct kevent eventlist; struct sockaddr_un client_address; int client_address_len, client_socket; int res; TRACE_IN(open_nscd_connection); assert(params != NULL); - client_socket = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0); + client_socket = socket(PF_LOCAL, SOCK_STREAM, 0); client_address.sun_family = PF_LOCAL; strlcpy(client_address.sun_path, params->socket_path, sizeof(client_address.sun_path)); client_address_len = sizeof(client_address.sun_family) + strlen(client_address.sun_path) + 1; res = connect(client_socket, (struct sockaddr *)&client_address, client_address_len); if (res == -1) { close(client_socket); TRACE_OUT(open_nscd_connection); return (NULL); } + fcntl(client_socket, F_SETFL, O_NONBLOCK); retval = calloc(1, sizeof(*retval)); assert(retval != NULL); retval->sockfd = client_socket; retval->write_queue = kqueue(); assert(retval->write_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); retval->read_queue = kqueue(); assert(retval->read_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); TRACE_OUT(open_nscd_connection); return (retval); } void close_nscd_connection__(struct nscd_connection_ *connection) { TRACE_IN(close_nscd_connection); assert(connection != NULL); close(connection->sockfd); close(connection->read_queue); close(connection->write_queue); free(connection); TRACE_OUT(close_nscd_connection); } int nscd_transform__(struct nscd_connection_ *connection, const char *entry_name, int transformation_type) { size_t name_size; int error_code; int result; TRACE_IN(nscd_transform); error_code = -1; result = 0; result = send_credentials(connection, CET_TRANSFORM_REQUEST); if (result != 0) goto fin; if (entry_name != NULL) name_size = strlen(entry_name); else name_size = 0; result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &transformation_type, sizeof(int)); if (result != 0) goto fin; if (entry_name != NULL) { result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; } result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) error_code = -1; fin: TRACE_OUT(nscd_transform); return (error_code); }