Index: user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_lockf.c =================================================================== --- user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_lockf.c (revision 290915) +++ user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_lockf.c (revision 290916) @@ -1,262 +1,265 @@ /* $NetBSD: t_lockf.c,v 1.9 2013/10/19 17:45:00 christos Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 #include #include #include #include #include /* * lockf1 regression test: * * Tests: * Fork N child processes, each of which gets M random byte range locks * on a common file. We ignore all lock errors (practically speaking, * this means EDEADLK or ENOLOCK), but we make numerous passes over all * the children to make sure that they are still awake. (We do this by * verifying that we can ptrace(ATTACH/DETACH) to the children and get * their status via waitpid().) * When finished, reap all the children. */ #define nlocks 500 /* number of locks per thread */ #define nprocs 10 /* number of processes to spawn */ #define npasses 50 /* number of passes to make over the children */ #define sleeptime 150000 /* sleep time between locks, usec */ #define filesize 8192 /* size of file to lock */ const char *lockfile = "lockf_test"; static u_int32_t random_uint32(void) { return lrand48(); } static void trylocks(int id) { int i, fd; srand48(getpid()); fd = open (lockfile, O_RDWR, 0); if (fd < 0) err(1, "%s", lockfile); printf("%d: start\n", id); for (i = 0; i < nlocks; i++) { struct flock fl; fl.l_start = random_uint32() % filesize; fl.l_len = random_uint32() % filesize; switch (random_uint32() % 3) { case 0: fl.l_type = F_RDLCK; break; case 1: fl.l_type = F_WRLCK; break; case 2: fl.l_type = F_UNLCK; break; } fl.l_whence = SEEK_SET; (void)fcntl(fd, F_SETLKW, &fl); if (usleep(sleeptime) < 0) +#if defined(__FreeBSD__) + if (errno != EINTR) +#endif err(1, "usleep"); } printf("%d: done\n", id); close (fd); } ATF_TC(randlock); ATF_TC_HEAD(randlock, tc) { atf_tc_set_md_var(tc, "timeout", "300"); atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) locking"); } ATF_TC_BODY(randlock, tc) { int i, j, fd; int pipe_fd[2]; pid_t *pid; int status; char pipe_in, pipe_out; const char pipe_errmsg[] = "child: pipe write failed\n"; (void)unlink(lockfile); fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666); ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno)); ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0, "ftruncate(%s): %s", lockfile, strerror(errno)); ATF_REQUIRE_MSG(pipe(pipe_fd) == 0, "pipe: %s", strerror(errno)); fsync(fd); close(fd); pid = malloc(nprocs * sizeof(pid_t)); for (i = 0; i < nprocs; i++) { pipe_out = (char)('A' + i); pid[i] = fork(); switch (pid[i]) { case 0: if (write(pipe_fd[1], &pipe_out, 1) != 1) write(STDERR_FILENO, pipe_errmsg, __arraycount(pipe_errmsg) - 1); else trylocks(i); _exit(0); break; case -1: atf_tc_fail("fork %d failed", i); break; default: ATF_REQUIRE_MSG(read(pipe_fd[0], &pipe_in, 1) == 1, "parent: read_pipe(%i): %s", i, strerror(errno)); ATF_REQUIRE_MSG(pipe_in == pipe_out, "parent: pipe does not match"); break; } } for (j = 0; j < npasses; j++) { printf("parent: run %i\n", j+1); for (i = 0; i < nprocs; i++) { ATF_REQUIRE_MSG(ptrace(PT_ATTACH, pid[i], 0, 0) >= 0, "ptrace attach %d", pid[i]); ATF_REQUIRE_MSG(waitpid(pid[i], &status, WUNTRACED) >= 0, "waitpid(ptrace)"); usleep(sleeptime / 3); ATF_REQUIRE_MSG(ptrace(PT_DETACH, pid[i], (caddr_t)1, 0) >= 0, "ptrace detach %d", pid[i]); usleep(sleeptime / 3); } } for (i = 0; i < nprocs; i++) { printf("reap %d: ", i); fflush(stdout); kill(pid[i], SIGINT); waitpid(pid[i], &status, 0); printf(" status %d\n", status); } atf_tc_pass(); } static int dolock(int fd, int op, off_t lk_off, off_t lk_size) { off_t result; int ret; result = lseek(fd, lk_off, SEEK_SET); if (result == -1) { return errno; } ATF_REQUIRE_MSG(result == lk_off, "lseek to wrong offset"); ret = lockf(fd, op, lk_size); if (ret == -1) { return errno; } return 0; } ATF_TC(deadlock); ATF_TC_HEAD(deadlock, tc) { atf_tc_set_md_var(tc, "timeout", "30"); atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) deadlock detection"); } ATF_TC_BODY(deadlock, tc) { int fd; int error; int ret; pid_t pid; (void)unlink(lockfile); fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666); ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno)); ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0, "ftruncate(%s): %s", lockfile, strerror(errno)); fsync(fd); error = dolock(fd, F_LOCK, 0, 1); ATF_REQUIRE_MSG(error == 0, "initial dolock: %s", strerror(errno)); pid = fork(); ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno)); if (pid == 0) { error = dolock(fd, F_LOCK, 1, 1); ATF_REQUIRE_MSG(error == 0, "child dolock: %s", strerror(errno)); dolock(fd, F_LOCK, 0, 1); /* will block */ atf_tc_fail("child did not block"); } sleep(1); /* give child time to grab its lock then block */ error = dolock(fd, F_LOCK, 1, 1); ATF_REQUIRE_MSG(error == EDEADLK, "parent did not detect deadlock: %s", strerror(errno)); ret = kill(pid, SIGKILL); ATF_REQUIRE_MSG(ret != -1, "failed to kill child: %s", strerror(errno)); atf_tc_pass(); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, randlock); ATF_TP_ADD_TC(tp, deadlock); return atf_no_error(); } Index: user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_mqueue.c =================================================================== --- user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_mqueue.c (revision 290915) +++ user/ngie/more-tests2/contrib/netbsd-tests/kernel/t_mqueue.c (revision 290916) @@ -1,137 +1,156 @@ /* $NetBSD: t_mqueue.c,v 1.4 2014/03/02 19:56:48 jmmv Exp $ */ /* * Test for POSIX message queue priority handling. * * This file is in the Public Domain. */ +#ifdef __FreeBSD__ +#include +#include + +#include "freebsd_test_suite/macros.h" +#endif + #include #include #include #include #include #include #include #define MQ_PRIO_BASE 24 static void send_msgs(mqd_t mqfd) { char msg[2]; msg[1] = '\0'; msg[0] = 'a'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), MQ_PRIO_BASE) != -1, "mq_send 1 failed: %d", errno); msg[0] = 'b'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), MQ_PRIO_BASE + 1) != -1, "mq_send 2 failed: %d", errno); msg[0] = 'c'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), MQ_PRIO_BASE) != -1, "mq_send 3 failed: %d", errno); msg[0] = 'd'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), MQ_PRIO_BASE - 1) != -1, "mq_send 4 failed: %d", errno); msg[0] = 'e'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), 0) != -1, "mq_send 5 failed: %d", errno); msg[0] = 'f'; ATF_REQUIRE_MSG(mq_send(mqfd, msg, sizeof(msg), MQ_PRIO_BASE + 1) != -1, "mq_send 6 failed: %d", errno); } static void receive_msgs(mqd_t mqfd) { struct mq_attr mqa; char *m; unsigned p; int len; ATF_REQUIRE_MSG(mq_getattr(mqfd, &mqa) != -1, "mq_getattr failed %d", errno); len = mqa.mq_msgsize; m = calloc(1, len); ATF_REQUIRE_MSG(m != NULL, "calloc failed"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 1 failed: %d", errno); ATF_REQUIRE_MSG(p == (MQ_PRIO_BASE + 1) && m[0] == 'b', "mq_receive 1 prio/data mismatch"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 2 failed: %d", errno); ATF_REQUIRE_MSG(p == (MQ_PRIO_BASE + 1) && m[0] == 'f', "mq_receive 2 prio/data mismatch"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 3 failed: %d", errno); ATF_REQUIRE_MSG(p == MQ_PRIO_BASE && m[0] == 'a', "mq_receive 3 prio/data mismatch"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 4 failed: %d", errno); ATF_REQUIRE_MSG(p == MQ_PRIO_BASE && m[0] == 'c', "mq_receive 4 prio/data mismatch"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 5 failed: %d", errno); ATF_REQUIRE_MSG(p == (MQ_PRIO_BASE - 1) && m[0] == 'd', "mq_receive 5 prio/data mismatch"); ATF_REQUIRE_MSG(mq_receive(mqfd, m, len, &p) != -1, "mq_receive 6 failed: %d", errno); ATF_REQUIRE_MSG(p == 0 && m[0] == 'e', "mq_receive 6 prio/data mismatch"); } ATF_TC(mqueue); ATF_TC_HEAD(mqueue, tc) { atf_tc_set_md_var(tc, "timeout", "3"); atf_tc_set_md_var(tc, "descr", "Checks mqueue send/receive"); } ATF_TC_BODY(mqueue, tc) { int status; char *tmpdir; char template[32]; char mq_name[64]; +#ifdef __FreeBSD__ + ATF_REQUIRE_KERNEL_MODULE("mqueuefs"); +#endif + strlcpy(template, "./t_mqueue.XXXXXX", sizeof(template)); tmpdir = mkdtemp(template); ATF_REQUIRE_MSG(tmpdir != NULL, "mkdtemp failed: %d", errno); +#ifdef __FreeBSD__ + snprintf(mq_name, sizeof(mq_name), "/t_mqueue"); +#else snprintf(mq_name, sizeof(mq_name), "%s/mq", tmpdir); +#endif mqd_t mqfd; mqfd = mq_open(mq_name, O_RDWR | O_CREAT, S_IRUSR | S_IRWXG | S_IROTH, NULL); +#ifdef __FreeBSD__ + ATF_REQUIRE_MSG(mqfd != (mqd_t)-1, "mq_open failed: %d", errno); +#else ATF_REQUIRE_MSG(mqfd != -1, "mq_open failed: %d", errno); +#endif send_msgs(mqfd); receive_msgs(mqfd); status = mq_close(mqfd); ATF_REQUIRE_MSG(status == 0, "mq_close failed: %d", errno); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, mqueue); return atf_no_error(); } Index: user/ngie/more-tests2/contrib/netbsd-tests/lib/libcrypt/t_crypt.c =================================================================== --- user/ngie/more-tests2/contrib/netbsd-tests/lib/libcrypt/t_crypt.c (revision 290915) +++ user/ngie/more-tests2/contrib/netbsd-tests/lib/libcrypt/t_crypt.c (revision 290916) @@ -1,145 +1,149 @@ /* $NetBSD: t_crypt.c,v 1.3 2011/12/28 22:07:40 christos Exp $ */ /* * This version is derived from the original implementation of FreeSec * (release 1.1) by David Burren. I've reviewed the changes made in * OpenBSD (as of 2.7) and modified the original code in a similar way * where applicable. I've also made it reentrant and made a number of * other changes. * - Solar Designer */ /* * FreeSec: libcrypt for NetBSD * * Copyright (c) 1994 David Burren * 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 author nor the names of other 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. * * $Owl: Owl/packages/glibc/crypt_freesec.c,v 1.6 2010/02/20 14:45:06 solar Exp $ * Id: crypt.c,v 1.15 1994/09/13 04:58:49 davidb Exp * * This is an original implementation of the DES and the crypt(3) interfaces * by David Burren . * * An excellent reference on the underlying algorithm (and related * algorithms) is: * * B. Schneier, Applied Cryptography: protocols, algorithms, * and source code in C, John Wiley & Sons, 1994. * * Note that in that book's description of DES the lookups for the initial, * pbox, and final permutations are inverted (this has been brought to the * attention of the author). A list of errata for this book has been * posted to the sci.crypt newsgroup by the author and is available for FTP. * * ARCHITECTURE ASSUMPTIONS: * This code used to have some nasty ones, but these have been removed * by now. The code requires a 32-bit integer type, though. */ #include __RCSID("$NetBSD: t_crypt.c,v 1.3 2011/12/28 22:07:40 christos Exp $"); #include #include #include #include #include static const struct { const char *hash; const char *pw; } tests[] = { /* "new"-style */ /* 0 */ { "_J9..CCCCXBrJUJV154M", "U*U*U*U*" }, /* 1 */ { "_J9..CCCCXUhOBTXzaiE", "U*U***U" }, /* 2 */ { "_J9..CCCC4gQ.mB/PffM", "U*U***U*" }, /* 3 */ { "_J9..XXXXvlzQGqpPPdk", "*U*U*U*U" }, /* 4 */ { "_J9..XXXXsqM/YSSP..Y", "*U*U*U*U*" }, /* 5 */ { "_J9..XXXXVL7qJCnku0I", "*U*U*U*U*U*U*U*U" }, /* 6 */ { "_J9..XXXXAj8cFbP5scI", "*U*U*U*U*U*U*U*U*" }, /* 7 */ { "_J9..SDizh.vll5VED9g", "ab1234567" }, /* 8 */ { "_J9..SDizRjWQ/zePPHc", "cr1234567" }, /* 9 */ { "_J9..SDizxmRI1GjnQuE", "zxyDPWgydbQjgq" }, /* 10 */ { "_K9..SaltNrQgIYUAeoY", "726 even" }, /* 11 */ { "_J9..SDSD5YGyRCr4W4c", "" }, /* "old"-style, valid salts */ /* 12 */ { "CCNf8Sbh3HDfQ", "U*U*U*U*" }, /* 13 */ { "CCX.K.MFy4Ois", "U*U***U" }, /* 14 */ { "CC4rMpbg9AMZ.", "U*U***U*" }, /* 15 */ { "XXxzOu6maQKqQ", "*U*U*U*U" }, /* 16 */ { "SDbsugeBiC58A", "" }, /* 17 */ { "./xZjzHv5vzVE", "password" }, /* 18 */ { "0A2hXM1rXbYgo", "password" }, /* 19 */ { "A9RXdR23Y.cY6", "password" }, /* 20 */ { "ZziFATVXHo2.6", "password" }, /* 21 */ { "zZDDIZ0NOlPzw", "password" }, /* "old"-style, "reasonable" invalid salts, UFC-crypt behavior expected */ /* 22 */ { "\001\002wyd0KZo65Jo", "password" }, /* 23 */ { "a_C10Dk/ExaG.", "password" }, /* 24 */ { "~\377.5OTsRVjwLo", "password" }, /* The below are erroneous inputs, so NULL return is expected/required */ /* 25 */ { "", "" }, /* no salt */ /* 26 */ { " ", "" }, /* setting string is too short */ /* 27 */ { "a:", "" }, /* unsafe character */ /* 28 */ { "\na", "" }, /* unsafe character */ /* 29 */ { "_/......", "" }, /* setting string is too short for its type */ /* 30 */ { "_........", "" }, /* zero iteration count */ /* 31 */ { "_/!......", "" }, /* invalid character in count */ /* 32 */ { "_/......!", "" }, /* invalid character in salt */ /* 33 */ { NULL, NULL } }; ATF_TC(crypt_salts); ATF_TC_HEAD(crypt_salts, tc) { atf_tc_set_md_var(tc, "descr", "crypt(3) salt consistency checks"); } ATF_TC_BODY(crypt_salts, tc) { for (size_t i = 0; tests[i].hash; i++) { +#if defined(__FreeBSD__) + if (22 <= i) + atf_tc_expect_fail("Old-style/bad inputs fail on FreeBSD"); +#endif char *hash = crypt(tests[i].pw, tests[i].hash); if (!hash) { ATF_CHECK_MSG(0, "Test %zu NULL\n", i); continue; } if (strcmp(hash, "*0") == 0 && strlen(tests[i].hash) < 13) continue; /* expected failure */ if (strcmp(hash, tests[i].hash)) ATF_CHECK_MSG(0, "Test %zu %s != %s\n", i, hash, tests[i].hash); } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, crypt_salts); return atf_no_error(); } Index: user/ngie/more-tests2/etc/mtree/BSD.tests.dist =================================================================== --- user/ngie/more-tests2/etc/mtree/BSD.tests.dist (revision 290915) +++ user/ngie/more-tests2/etc/mtree/BSD.tests.dist (revision 290916) @@ -1,618 +1,620 @@ # $FreeBSD$ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin cat .. chown .. date .. dd .. expr .. ls .. mv .. pax .. pkill .. sh builtins .. errors .. execution .. expansion .. parameters .. parser .. set-e .. .. sleep .. test .. .. cddl lib .. sbin .. usr.bin .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. .. .. .. etc rc.d .. .. games .. gnu lib .. usr.bin diff .. .. .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. libarchive .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. inet .. locale .. net getaddrinfo data .. .. .. regex data .. .. rpc .. ssp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcrypt .. libmp .. libnv .. libpam .. libproc .. librt .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-sh .. .. rtld-elf .. .. sbin dhclient .. devd .. growfs .. ifconfig .. mdconfig .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. plain .. .. .. .. sys acl .. aio .. fifo .. file .. kern acct .. execve .. + pipe + .. .. kqueue .. mqueue .. netinet .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. .. posixshm .. vfs .. vm .. .. usr.bin apply .. basename .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. calendar .. cmp .. cpio .. col .. comm .. cut .. dirname .. file2c .. grep .. gzip .. ident .. join .. jot .. lastcomm .. limits .. m4 .. mkimg .. ncal .. opensm .. printf .. sed regress.multitest.out .. .. soelim .. tar .. timeout .. tr .. truncate .. units .. uudecode .. uuencode .. xargs .. xo .. yacc yacc .. .. .. usr.sbin etcupdate .. fstyp .. makefs .. newsyslog .. nmtree .. pw .. sa .. .. .. # vim: set expandtab ts=4 sw=4: Index: user/ngie/more-tests2/lib/libc/tests/sys/Makefile =================================================================== --- user/ngie/more-tests2/lib/libc/tests/sys/Makefile (revision 290915) +++ user/ngie/more-tests2/lib/libc/tests/sys/Makefile (revision 290916) @@ -1,83 +1,85 @@ # $FreeBSD$ .include +ATF_TESTS_C+= queue_test + # TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg, # swapcontext NETBSD_ATF_TESTS_C+= access_test NETBSD_ATF_TESTS_C+= chroot_test NETBSD_ATF_TESTS_C+= clock_gettime_test NETBSD_ATF_TESTS_C+= connect_test NETBSD_ATF_TESTS_C+= dup_test NETBSD_ATF_TESTS_C+= fsync_test NETBSD_ATF_TESTS_C+= getcontext_test NETBSD_ATF_TESTS_C+= getgroups_test NETBSD_ATF_TESTS_C+= getitimer_test NETBSD_ATF_TESTS_C+= getlogin_test NETBSD_ATF_TESTS_C+= getpid_test NETBSD_ATF_TESTS_C+= getrusage_test NETBSD_ATF_TESTS_C+= getsid_test NETBSD_ATF_TESTS_C+= gettimeofday_test NETBSD_ATF_TESTS_C+= issetugid_test NETBSD_ATF_TESTS_C+= kevent_test NETBSD_ATF_TESTS_C+= kill_test NETBSD_ATF_TESTS_C+= link_test NETBSD_ATF_TESTS_C+= listen_test NETBSD_ATF_TESTS_C+= mincore_test NETBSD_ATF_TESTS_C+= mkdir_test NETBSD_ATF_TESTS_C+= mkfifo_test NETBSD_ATF_TESTS_C+= mknod_test NETBSD_ATF_TESTS_C+= mlock_test NETBSD_ATF_TESTS_C+= mmap_test NETBSD_ATF_TESTS_C+= mprotect_test NETBSD_ATF_TESTS_C+= msgctl_test NETBSD_ATF_TESTS_C+= msgget_test NETBSD_ATF_TESTS_C+= msgrcv_test NETBSD_ATF_TESTS_C+= msgsnd_test NETBSD_ATF_TESTS_C+= msync_test NETBSD_ATF_TESTS_C+= nanosleep_test NETBSD_ATF_TESTS_C+= pipe_test NETBSD_ATF_TESTS_C+= pipe2_test NETBSD_ATF_TESTS_C+= poll_test NETBSD_ATF_TESTS_C+= revoke_test NETBSD_ATF_TESTS_C+= select_test NETBSD_ATF_TESTS_C+= setrlimit_test NETBSD_ATF_TESTS_C+= setuid_test NETBSD_ATF_TESTS_C+= sigaction_test NETBSD_ATF_TESTS_C+= sigqueue_test NETBSD_ATF_TESTS_C+= sigtimedwait_test NETBSD_ATF_TESTS_C+= socketpair_test NETBSD_ATF_TESTS_C+= stat_test NETBSD_ATF_TESTS_C+= timer_create_test NETBSD_ATF_TESTS_C+= truncate_test NETBSD_ATF_TESTS_C+= ucontext_test NETBSD_ATF_TESTS_C+= umask_test NETBSD_ATF_TESTS_C+= unlink_test NETBSD_ATF_TESTS_C+= write_test DPADD.getpid_test+= ${LIBPTHREAD} LDADD.getpid_test+= -lpthread DPADD.timer_create_test+= ${LIBRT} LDADD.timer_create_test+= -lrt .include "../Makefile.netbsd-tests" .if ${COMPILER_TYPE} == "gcc" WARNS?= 3 .else WARNS?= 4 .endif FILESGROUPS= FILES truncate_test_FILES truncate_test_FILES= truncate_test.root_owned truncate_test_FILESDIR= ${TESTSDIR} truncate_test_FILESMODE= 0600 truncate_test_FILESOWNER= root truncate_test_FILESGRP= wheel CLEANFILES= truncate_test.root_owned truncate_test.root_owned: dd if=/dev/null bs=1 count=1 of=${.TARGET} .include Index: user/ngie/more-tests2/lib/libc/tests/sys/queue_test.c =================================================================== --- user/ngie/more-tests2/lib/libc/tests/sys/queue_test.c (nonexistent) +++ user/ngie/more-tests2/lib/libc/tests/sys/queue_test.c (revision 290916) @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 2015 EMC 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. + * 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 + +ATF_TC(slist_test); +ATF_TC_HEAD(slist_test, tc) +{ + + atf_tc_set_md_var(tc, "descr", "SLIST macro feature tests"); +} + +ATF_TC_BODY(slist_test, tc) +{ + SLIST_HEAD(stailhead, entry) head = SLIST_HEAD_INITIALIZER(head); + struct entry { + SLIST_ENTRY(entry) entries; + int i; + } *n1, *n2, *n3, *np; + int i, j, length; + + SLIST_INIT(&head); + + printf("Ensuring SLIST_EMPTY works\n"); + + ATF_REQUIRE(SLIST_EMPTY(&head)); + + i = length = 0; + + SLIST_FOREACH(np, &head, entries) { + length++; + } + ATF_REQUIRE_EQ(length, 0); + + printf("Ensuring SLIST_INSERT_HEAD works\n"); + + n1 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n1 != NULL); + n1->i = i++; + + SLIST_INSERT_HEAD(&head, n1, entries); + + printf("Ensuring SLIST_FIRST returns element 1\n"); + ATF_REQUIRE_EQ(SLIST_FIRST(&head), n1); + + j = length = 0; + SLIST_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 1); + + printf("Ensuring SLIST_INSERT_AFTER works\n"); + + n2 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n2 != NULL); + n2->i = i++; + + SLIST_INSERT_AFTER(n1, n2, entries); + + n3 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n3 != NULL); + n3->i = i++; + + SLIST_INSERT_AFTER(n2, n3, entries); + + j = length = 0; + SLIST_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 3); + + printf("Ensuring SLIST_REMOVE_HEAD works\n"); + + printf("Ensuring SLIST_FIRST returns element 1\n"); + ATF_REQUIRE_EQ(SLIST_FIRST(&head), n1); + + SLIST_REMOVE_HEAD(&head, entries); + + printf("Ensuring SLIST_FIRST now returns element 2\n"); + ATF_REQUIRE_EQ(SLIST_FIRST(&head), n2); + + j = 1; /* Starting point's 1 this time */ + length = 0; + SLIST_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 2); + + printf("Ensuring SLIST_REMOVE_AFTER works by removing the tail\n"); + + SLIST_REMOVE_AFTER(n2, entries); + + j = 1; /* Starting point's 1 this time */ + length = 0; + SLIST_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 1); + + printf("Ensuring SLIST_FIRST returns element 2\n"); + ATF_REQUIRE_EQ(SLIST_FIRST(&head), n2); + +} + +ATF_TC(stailq_test); +ATF_TC_HEAD(stailq_test, tc) +{ + + atf_tc_set_md_var(tc, "descr", "STAILQ macro feature tests"); +} + +ATF_TC_BODY(stailq_test, tc) +{ + STAILQ_HEAD(stailhead, entry) head = STAILQ_HEAD_INITIALIZER(head); + struct entry { + STAILQ_ENTRY(entry) entries; + int i; + } *n1, *n2, *n3, *np; + int i, j, length; + + printf("Ensuring empty STAILQs are treated properly\n"); + STAILQ_INIT(&head); + ATF_REQUIRE(STAILQ_EMPTY(&head)); + + i = length = 0; + + STAILQ_FOREACH(np, &head, entries) { + length++; + } + ATF_REQUIRE_EQ(length, 0); + + printf("Ensuring STAILQ_INSERT_HEAD works\n"); + + n1 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n1 != NULL); + n1->i = i++; + + STAILQ_INSERT_HEAD(&head, n1, entries); + + j = length = 0; + STAILQ_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 1); + + printf("Ensuring STAILQ_INSERT_TAIL works\n"); + + n2 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n2 != NULL); + n2->i = i++; + + STAILQ_INSERT_TAIL(&head, n2, entries); + + n3 = malloc(sizeof(struct entry)); + ATF_REQUIRE(n3 != NULL); + n3->i = i++; + + STAILQ_INSERT_TAIL(&head, n3, entries); + + j = length = 0; + STAILQ_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 3); + + printf("Ensuring STAILQ_REMOVE_HEAD works\n"); + + STAILQ_REMOVE_HEAD(&head, entries); + + j = 1; /* Starting point's 1 this time */ + length = 0; + STAILQ_FOREACH(np, &head, entries) { + ATF_REQUIRE_EQ_MSG(np->i, j, + "%d (entry counter) != %d (counter)", np->i, j); + j++; + length++; + } + ATF_REQUIRE_EQ(length, 2); + +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, slist_test); + ATF_TP_ADD_TC(tp, stailq_test); + + return (atf_no_error()); +} Property changes on: user/ngie/more-tests2/lib/libc/tests/sys/queue_test.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/ngie/more-tests2/lib/libc =================================================================== --- user/ngie/more-tests2/lib/libc (revision 290915) +++ user/ngie/more-tests2/lib/libc (revision 290916) Property changes on: user/ngie/more-tests2/lib/libc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libc:r290900-290915 Index: user/ngie/more-tests2/lib/libcrypt/tests/Makefile =================================================================== --- user/ngie/more-tests2/lib/libcrypt/tests/Makefile (revision 290915) +++ user/ngie/more-tests2/lib/libcrypt/tests/Makefile (revision 290916) @@ -1,10 +1,12 @@ # $FreeBSD$ -# exercise libcrypt +ATF_TESTS_C+= crypt_tests -ATF_TESTS_C= crypt_tests +NETBSD_ATF_TESTS_C+= crypt_test CFLAGS+= -I${.CURDIR:H} LIBADD= crypt + +.include .include Index: user/ngie/more-tests2/sbin/restore/tape.c =================================================================== --- user/ngie/more-tests2/sbin/restore/tape.c (revision 290915) +++ user/ngie/more-tests2/sbin/restore/tape.c (revision 290916) @@ -1,1756 +1,1780 @@ /* * Copyright (c) 1983, 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. * 4. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" static long fssize = MAXBSIZE; static int mt = -1; static int pipein = 0; static int pipecmdin = 0; static FILE *popenfp = NULL; static char *magtape; static int blkcnt; static int numtrec; static char *tapebuf; static union u_spcl endoftapemark; static long byteslide = 0; static long blksread; /* blocks read since last header */ static int64_t tapeaddr = 0; /* current TP_BSIZE tape record */ static long tapesread; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static char *host = NULL; static int readmapflag; static int ofile; static char *map; static char lnkbuf[MAXPATHLEN + 1]; static int pathlen; int Bcvt; /* Swap Bytes */ int oldinofmt; /* FreeBSD 1 inode format needs cvt */ #define FLUSHTAPEBUF() blkcnt = ntrec + 1 char *namespace_names[] = EXTATTR_NAMESPACE_NAMES; static void accthdr(struct s_spcl *); static int checksum(int *); static void findinode(struct s_spcl *); static void findtapeblksize(void); static char *setupextattr(int); static void xtrattr(char *, long); static void set_extattr_link(char *, void *, int); static void set_extattr_fd(int, char *, void *, int); +static void skiphole(void (*)(char *, long), long *); static int gethead(struct s_spcl *); static void readtape(char *); static void setdumpnum(void); static u_long swabl(u_long); static u_char *swablong(u_char *, int); static u_char *swabshort(u_char *, int); static void terminateinput(void); static void xtrfile(char *, long); static void xtrlnkfile(char *, long); static void xtrlnkskip(char *, long); static void xtrmap(char *, long); static void xtrmapskip(char *, long); static void xtrskip(char *, long); /* * Set up an input source */ void setinput(char *source, int ispipecommand) { FLUSHTAPEBUF(); if (bflag) newtapebuf(ntrec); else newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); terminal = stdin; if (ispipecommand) pipecmdin++; else #ifdef RRESTORE if (strchr(source, ':')) { host = source; source = strchr(host, ':'); *source++ = '\0'; if (rmthost(host) == 0) done(1); } else #endif if (strcmp(source, "-") == 0) { /* * Since input is coming from a pipe we must establish * our own connection to the terminal. */ terminal = fopen(_PATH_TTY, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_TTY, strerror(errno)); terminal = fopen(_PATH_DEVNULL, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_DEVNULL, strerror(errno)); done(1); } } pipein++; } /* no longer need or want root privileges */ if (setuid(getuid()) != 0) { fprintf(stderr, "setuid failed\n"); done(1); } magtape = strdup(source); if (magtape == NULL) { fprintf(stderr, "Cannot allocate space for magtape buffer\n"); done(1); } } void newtapebuf(long size) { static int tapebufsize = -1; ntrec = size; if (size <= tapebufsize) return; if (tapebuf != NULL) free(tapebuf - TP_BSIZE); tapebuf = malloc((size+1) * TP_BSIZE); if (tapebuf == NULL) { fprintf(stderr, "Cannot allocate space for tape buffer\n"); done(1); } tapebuf += TP_BSIZE; tapebufsize = size; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup(void) { int i, j, *ip; struct stat stbuf; vprintf(stdout, "Verify tape and initialize maps\n"); if (pipecmdin) { if (setenv("RESTORE_VOLUME", "1", 1) == -1) { fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n", strerror(errno)); done(1); } popenfp = popen(magtape, "r"); mt = popenfp ? fileno(popenfp) : -1; } else #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif if (pipein) mt = 0; else mt = open(magtape, O_RDONLY, 0); if (mt < 0) { fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); done(1); } volno = 1; setdumpnum(); FLUSHTAPEBUF(); if (!pipein && !pipecmdin && !bflag) findtapeblksize(); if (gethead(&spcl) == FAIL) { fprintf(stderr, "Tape is not a dump tape\n"); done(1); } if (pipein) { endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC; endoftapemark.s_spcl.c_type = TS_END; ip = (int *)&endoftapemark; j = sizeof(union u_spcl) / sizeof(int); i = 0; do i += *ip++; while (--j); endoftapemark.s_spcl.c_checksum = CHECKSUM - i; } if (vflag || command == 't') printdumpinfo(); dumptime = _time64_to_time(spcl.c_ddate); dumpdate = _time64_to_time(spcl.c_date); if (stat(".", &stbuf) < 0) { fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); done(1); } if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE ) fssize = TP_BSIZE; if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) fssize = stbuf.st_blksize; if (((TP_BSIZE - 1) & stbuf.st_blksize) != 0) { fprintf(stderr, "Warning: filesystem with non-multiple-of-%d " "blocksize (%d);\n", TP_BSIZE, stbuf.st_blksize); fssize = roundup(fssize, TP_BSIZE); fprintf(stderr, "\twriting using blocksize %ld\n", fssize); } if (spcl.c_volume != 1) { fprintf(stderr, "Tape is not volume 1 of the dump\n"); done(1); } if (gethead(&spcl) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); panic("no header after volume mark!\n"); } findinode(&spcl); if (spcl.c_type != TS_CLRI) { fprintf(stderr, "Cannot find file removal list\n"); done(1); } maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; dprintf(stdout, "maxino = %ju\n", (uintmax_t)maxino); map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == NULL) panic("no memory for active inode map\n"); usedinomap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip, xtrmapskip); if (spcl.c_type != TS_BITS) { fprintf(stderr, "Cannot find file dump list\n"); done(1); } map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == (char *)NULL) panic("no memory for file dump list\n"); dumpmap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip, xtrmapskip); /* * If there may be whiteout entries on the tape, pretend that the * whiteout inode exists, so that the whiteout entries can be * extracted. */ SETINO(WINO, dumpmap); /* 'r' restores don't call getvol() for tape 1, so mark it as read. */ if (command == 'r') tapesread = 1; } /* * Prompt user to load a new dump volume. * "Nextvol" is the next suggested volume to use. * This suggested volume is enforced when doing full * or incremental restores, but can be overridden by * the user when only extracting a subset of the files. */ void getvol(long nextvol) { int64_t prevtapea; long i, newvol, savecnt; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE]; if (nextvol == 1) { tapesread = 0; gettingfile = 0; } prevtapea = tapeaddr; savecnt = blksread; if (pipein) { if (nextvol != 1) { panic("Changing volumes on pipe input?\n"); /* Avoid looping if we couldn't ask the user. */ if (yflag || ferror(terminal) || feof(terminal)) done(1); } if (volno == 1) return; goto gethdr; } again: if (pipein) done(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) newvol = nextvol; else newvol = 0; while (newvol <= 0) { if (tapesread == 0) { fprintf(stderr, "%s%s%s%s%s%s%s", "You have not read any tapes yet.\n", "If you are extracting just a few files,", " start with the last volume\n", "and work towards the first; restore", " can quickly skip tapes that\n", "have no further files to extract.", " Otherwise, begin with volume 1.\n"); } else { fprintf(stderr, "You have read volumes"); strcpy(buf, ": "); for (i = 0; i < 32; i++) if (tapesread & (1 << i)) { fprintf(stderr, "%s%ld", buf, i + 1); strcpy(buf, ", "); } fprintf(stderr, "\n"); } do { fprintf(stderr, "Specify next volume #: "); (void) fflush(stderr); if (fgets(buf, BUFSIZ, terminal) == NULL) done(1); } while (buf[0] == '\n'); newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, "Volume numbers are positive numerics\n"); } } if (newvol == volno) { tapesread |= 1 << (volno - 1); return; } closemt(); fprintf(stderr, "Mount tape volume %ld\n", newvol); fprintf(stderr, "Enter ``none'' if there are no more tapes\n"); fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape); (void) fflush(stderr); if (fgets(buf, BUFSIZ, terminal) == NULL) done(1); if (!strcmp(buf, "none\n")) { terminateinput(); return; } if (buf[0] != '\n') { (void) strcpy(magtape, buf); magtape[strlen(magtape) - 1] = '\0'; } if (pipecmdin) { char volno[sizeof("2147483647")]; (void)sprintf(volno, "%ld", newvol); if (setenv("RESTORE_VOLUME", volno, 1) == -1) { fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n", strerror(errno)); done(1); } popenfp = popen(magtape, "r"); mt = popenfp ? fileno(popenfp) : -1; } else #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif mt = open(magtape, O_RDONLY, 0); if (mt == -1) { fprintf(stderr, "Cannot open %s\n", magtape); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); FLUSHTAPEBUF(); if (gethead(&tmpbuf) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; goto again; } if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%jd)\n", (intmax_t)tmpbuf.c_volume); volno = 0; goto again; } if (_time64_to_time(tmpbuf.c_date) != dumpdate || _time64_to_time(tmpbuf.c_ddate) != dumptime) { time_t t = _time64_to_time(tmpbuf.c_date); fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t)); fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); volno = 0; goto again; } tapesread |= 1 << (volno - 1); blksread = savecnt; /* * If continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. * * If coming to this volume at random, skip to the beginning * of the next record. */ dprintf(stdout, "last rec %jd, tape starts with %jd\n", (intmax_t)prevtapea, (intmax_t)tmpbuf.c_tapea); if (tmpbuf.c_type == TS_TAPE) { if (curfile.action != USING) { /* * XXX Dump incorrectly sets c_count to 1 in the * volume header of the first tape, so ignore * c_count when volno == 1. */ if (volno != 1) for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_tapea <= prevtapea) { /* * Normally the value of c_tapea in the volume * header is the record number of the header itself. * However in the volume header following an EOT- * terminated tape, it is the record number of the * first continuation data block (dump bug?). * * The next record we want is `prevtapea + 1'. */ i = prevtapea + 1 - tmpbuf.c_tapea; dprintf(stderr, "Skipping %ld duplicate record%s.\n", i, i > 1 ? "s" : ""); while (--i >= 0) readtape(buf); } } if (curfile.action == USING) { if (volno == 1) panic("active file into volume 1\n"); return; } (void) gethead(&spcl); findinode(&spcl); if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * Handle unexpected EOF. */ static void terminateinput(void) { if (gettingfile && curfile.action == USING) { printf("Warning: %s %s\n", "End-of-input encountered while extracting", curfile.name); } curfile.name = ""; curfile.action = UNKNOWN; curfile.mode = 0; curfile.ino = maxino; if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. */ static void setdumpnum(void) { struct mtop tcom; if (dumpnum == 1 || volno != 1) return; if (pipein) { fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); done(1); } tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; #ifdef RRESTORE if (host) rmtioctl(MTFSF, dumpnum - 1); else #endif if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0) fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); } void printdumpinfo(void) { time_t t; t = _time64_to_time(spcl.c_date); fprintf(stdout, "Dump date: %s", ctime(&t)); t = _time64_to_time(spcl.c_ddate); fprintf(stdout, "Dumped from: %s", (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t)); if (spcl.c_host[0] == '\0') return; fprintf(stderr, "Level %jd dump of %s on %s:%s\n", (intmax_t)spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev); fprintf(stderr, "Label: %s\n", spcl.c_label); } int extractfile(char *name) { int flags; uid_t uid; gid_t gid; mode_t mode; int extsize; struct timespec mtimep[2], ctimep[2]; struct entry *ep; char *buf; curfile.name = name; curfile.action = USING; mtimep[0].tv_sec = curfile.atime_sec; mtimep[0].tv_nsec = curfile.atime_nsec; mtimep[1].tv_sec = curfile.mtime_sec; mtimep[1].tv_nsec = curfile.mtime_nsec; ctimep[0].tv_sec = curfile.atime_sec; ctimep[0].tv_nsec = curfile.atime_nsec; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_nsec = curfile.birthtime_nsec; extsize = curfile.extsize; uid = getuid(); if (uid == 0) uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; flags = curfile.file_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; buf = setupextattr(extsize); getfile(xtrlnkfile, xtrattr, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == GOOD) { if (extsize > 0) set_extattr_link(name, buf, extsize); (void) lchown(name, uid, gid); (void) lchmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, AT_SYMLINK_NOFOLLOW); (void) utimensat(AT_FDCWD, name, mtimep, AT_SYMLINK_NOFOLLOW); (void) lchflags(name, flags); return (GOOD); } return (FAIL); case IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mkfifo(name, 0600) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, 0); (void) utimensat(AT_FDCWD, name, mtimep, 0); (void) chflags(name, flags); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600, (int)curfile.rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, 0); (void) utimensat(AT_FDCWD, name, mtimep, 0); (void) chflags(name, flags); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } buf = setupextattr(extsize); getfile(xtrfile, xtrattr, xtrskip); if (extsize > 0) set_extattr_fd(ofile, name, buf, extsize); (void) fchown(ofile, uid, gid); (void) fchmod(ofile, mode); (void) futimens(ofile, ctimep); (void) futimens(ofile, mtimep); (void) fchflags(ofile, flags); (void) close(ofile); return (GOOD); } /* NOTREACHED */ } /* * Set attributes for a file. */ void set_extattr_file(char *name, void *buf, int size) { struct extattr *eap, *eaend; vprintf(stdout, "Set attributes for %s:", name); eaend = buf + size; for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { /* * Make sure this entry is complete. */ if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { dprintf(stdout, "\n\t%scorrupted", eap == buf ? "" : "remainder "); break; } if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) continue; vprintf(stdout, "\n\t%s, (%d bytes), %*s", namespace_names[eap->ea_namespace], eap->ea_length, eap->ea_namelength, eap->ea_name); /* * First we try the general attribute setting interface. * However, some attributes can only be set by root or * by using special interfaces (for example, ACLs). */ if (extattr_set_file(name, eap->ea_namespace, eap->ea_name, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { dprintf(stdout, " (set using extattr_set_file)"); continue; } /* * If the general interface refuses to set the attribute, * then we try all the specialized interfaces that we * know about. */ if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { if (acl_set_file(name, ACL_TYPE_ACCESS, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_file)"); continue; } } if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { if (acl_set_file(name, ACL_TYPE_DEFAULT, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_file)"); continue; } } vprintf(stdout, " (unable to set)"); } vprintf(stdout, "\n"); } /* * Set attributes for a symbolic link. */ static void set_extattr_link(char *name, void *buf, int size) { struct extattr *eap, *eaend; vprintf(stdout, "Set attributes for %s:", name); eaend = buf + size; for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { /* * Make sure this entry is complete. */ if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { dprintf(stdout, "\n\t%scorrupted", eap == buf ? "" : "remainder "); break; } if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) continue; vprintf(stdout, "\n\t%s, (%d bytes), %*s", namespace_names[eap->ea_namespace], eap->ea_length, eap->ea_namelength, eap->ea_name); /* * First we try the general attribute setting interface. * However, some attributes can only be set by root or * by using special interfaces (for example, ACLs). */ if (extattr_set_link(name, eap->ea_namespace, eap->ea_name, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { dprintf(stdout, " (set using extattr_set_link)"); continue; } /* * If the general interface refuses to set the attribute, * then we try all the specialized interfaces that we * know about. */ if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { if (acl_set_link_np(name, ACL_TYPE_ACCESS, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_link_np)"); continue; } } if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { if (acl_set_link_np(name, ACL_TYPE_DEFAULT, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_link_np)"); continue; } } vprintf(stdout, " (unable to set)"); } vprintf(stdout, "\n"); } /* * Set attributes on a file descriptor. */ static void set_extattr_fd(int fd, char *name, void *buf, int size) { struct extattr *eap, *eaend; vprintf(stdout, "Set attributes for %s:", name); eaend = buf + size; for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { /* * Make sure this entry is complete. */ if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { dprintf(stdout, "\n\t%scorrupted", eap == buf ? "" : "remainder "); break; } if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) continue; vprintf(stdout, "\n\t%s, (%d bytes), %*s", namespace_names[eap->ea_namespace], eap->ea_length, eap->ea_namelength, eap->ea_name); /* * First we try the general attribute setting interface. * However, some attributes can only be set by root or * by using special interfaces (for example, ACLs). */ if (extattr_set_fd(fd, eap->ea_namespace, eap->ea_name, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { dprintf(stdout, " (set using extattr_set_fd)"); continue; } /* * If the general interface refuses to set the attribute, * then we try all the specialized interfaces that we * know about. */ if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { if (acl_set_fd(fd, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_fd)"); continue; } } if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { if (acl_set_file(name, ACL_TYPE_DEFAULT, EXTATTR_CONTENT(eap)) != -1) { dprintf(stdout, " (set using acl_set_file)"); continue; } } vprintf(stdout, " (unable to set)"); } vprintf(stdout, "\n"); } /* * skip over bit maps on the tape */ void skipmaps(void) { while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) skipfile(); } /* * skip over a file on the tape */ void skipfile(void) { curfile.action = SKIP; getfile(xtrnull, xtrnull, xtrnull); } /* + * Skip a hole in an output file + */ +static void +skiphole(void (*skip)(char *, long), long *seekpos) +{ + char buf[MAXBSIZE]; + + if (*seekpos > 0) { + (*skip)(buf, *seekpos); + *seekpos = 0; + } +} + +/* * Extract a file from the tape. * When an allocated block is found it is passed to the fill function; * when an unallocated block (hole) is found, a zeroed buffer is passed * to the skip function. */ void getfile(void (*datafill)(char *, long), void (*attrfill)(char *, long), void (*skip)(char *, long)) { int i; off_t size; + long seekpos; int curblk, attrsize; void (*fillit)(char *, long); - static char clearedbuf[MAXBSIZE]; char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; curblk = 0; size = spcl.c_size; + seekpos = 0; attrsize = spcl.c_extsize; if (spcl.c_type == TS_END) panic("ran off end of tape\n"); if (spcl.c_magic != FS_UFS2_MAGIC) panic("not at beginning of a file\n"); if (!gettingfile && setjmp(restart) != 0) return; gettingfile++; fillit = datafill; if (size == 0 && attrsize > 0) { fillit = attrfill; size = attrsize; attrsize = 0; } loop: for (i = 0; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { if (Dflag) { fprintf(stderr, "spcl.c_count = %jd\n", (intmax_t)spcl.c_count); break; } else panic("spcl.c_count = %jd\n", (intmax_t)spcl.c_count); } if (readmapflag || spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { + skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } } else { if (curblk > 0) { + skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } - (*skip)(clearedbuf, (long)(size > TP_BSIZE ? - TP_BSIZE : size)); + /* + * We have a block of a hole. Don't skip it + * now, because there may be next adjacent + * block of the hole in the file. Postpone the + * seek until next file write. + */ + seekpos += (long)(size > TP_BSIZE ? TP_BSIZE : size); } if ((size -= TP_BSIZE) <= 0) { if (size > -TP_BSIZE && curblk > 0) { + skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)((curblk * TP_BSIZE) + size)); curblk = 0; } if (attrsize > 0) { fillit = attrfill; size = attrsize; attrsize = 0; continue; } if (spcl.c_count - i > 1) dprintf(stdout, "skipping %d junk block(s)\n", spcl.c_count - i - 1); for (i++; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { if (Dflag) { fprintf(stderr, "spcl.c_count = %jd\n", (intmax_t)spcl.c_count); break; } else panic("spcl.c_count = %jd\n", (intmax_t)spcl.c_count); } if (readmapflag || spcl.c_addr[i]) readtape(junk); } break; } } if (gethead(&spcl) == GOOD && size > 0) { if (spcl.c_type == TS_ADDR) goto loop; dprintf(stdout, "Missing address (header) block for %s at %ld blocks\n", curfile.name, blksread); } if (curblk > 0) panic("getfile: lost data\n"); findinode(&spcl); gettingfile = 0; } /* * These variables are shared between the next two functions. */ static int extbufsize = 0; static char *extbuf; static int extloc; /* * Allocate a buffer into which to extract extended attributes. */ static char * setupextattr(int extsize) { extloc = 0; if (extsize <= extbufsize) return (extbuf); if (extbufsize > 0) free(extbuf); if ((extbuf = malloc(extsize)) != NULL) { extbufsize = extsize; return (extbuf); } extbufsize = 0; extbuf = NULL; fprintf(stderr, "Cannot extract %d bytes %s for inode %ju, name %s\n", extsize, "of extended attributes", (uintmax_t)curfile.ino, curfile.name); return (NULL); } /* * Extract the next block of extended attributes. */ static void xtrattr(char *buf, long size) { if (extloc + size > extbufsize) panic("overrun attribute buffer\n"); memmove(&extbuf[extloc], buf, size); extloc += size; } /* * Write out the next block of a file. */ static void xtrfile(char *buf, long size) { if (Nflag) return; if (write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\nwrite: %s\n", (uintmax_t)curfile.ino, curfile.name, strerror(errno)); } } /* * Skip over a hole in a file. */ /* ARGSUSED */ static void xtrskip(char *buf, long size) { if (lseek(ofile, size, SEEK_CUR) == -1) { fprintf(stderr, "seek error extracting inode %ju, name %s\nlseek: %s\n", (uintmax_t)curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Collect the next block of a symbolic link. */ static void xtrlnkfile(char *buf, long size) { pathlen += size; if (pathlen > MAXPATHLEN) { fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", curfile.name, lnkbuf, buf, pathlen); done(1); } (void) strcat(lnkbuf, buf); } /* * Skip over a hole in a symbolic link (should never happen). */ /* ARGSUSED */ static void xtrlnkskip(char *buf, long size) { fprintf(stderr, "unallocated block in symbolic link %s\n", curfile.name); done(1); } /* * Collect the next block of a bit map. */ static void xtrmap(char *buf, long size) { memmove(map, buf, size); map += size; } /* * Skip over a hole in a bit map (should never happen). */ /* ARGSUSED */ static void xtrmapskip(char *buf, long size) { panic("hole in map\n"); map += size; } /* * Noop, when an extraction function is not needed. */ /* ARGSUSED */ void xtrnull(char *buf, long size) { return; } /* * Read TP_BSIZE blocks from the input. * Handle read errors, and end of media. */ static void readtape(char *buf) { long rd, newvol, i, oldnumtrec; int cnt, seek_failed; if (blkcnt + (byteslide > 0) < numtrec) { memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE); blksread++; tapeaddr++; return; } if (numtrec > 0) memmove(&tapebuf[-TP_BSIZE], &tapebuf[(numtrec-1) * TP_BSIZE], (long)TP_BSIZE); oldnumtrec = numtrec; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; if (numtrec == 0) numtrec = ntrec; cnt = ntrec * TP_BSIZE; rd = 0; getmore: #ifdef RRESTORE if (host) i = rmtread(&tapebuf[rd], cnt); else #endif i = read(mt, &tapebuf[rd], cnt); /* * Check for mid-tape short read error. * If found, skip rest of buffer and start with the next. */ if (!pipein && !pipecmdin && numtrec < ntrec && i > 0) { dprintf(stdout, "mid-media short read error.\n"); numtrec = ntrec; } /* * Handle partial block read. */ if ((pipein || pipecmdin) && i == 0 && rd > 0) i = rd; else if (i > 0 && i != ntrec * TP_BSIZE) { if (pipein || pipecmdin) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { /* * Short read. Process the blocks read. */ if (i % TP_BSIZE != 0) vprintf(stdout, "partial block read: %ld should be %ld\n", i, ntrec * TP_BSIZE); numtrec = i / TP_BSIZE; } } /* * Handle read error. */ if (i < 0) { fprintf(stderr, "Tape read error while "); switch (curfile.action) { default: fprintf(stderr, "trying to set up tape\n"); break; case UNKNOWN: fprintf(stderr, "trying to resynchronize\n"); break; case USING: fprintf(stderr, "restoring %s\n", curfile.name); break; case SKIP: fprintf(stderr, "skipping over inode %ju\n", (uintmax_t)curfile.ino); break; } if (!yflag && !reply("continue")) done(1); i = ntrec * TP_BSIZE; memset(tapebuf, 0, i); #ifdef RRESTORE if (host) seek_failed = (rmtseek(i, 1) < 0); else #endif seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); if (seek_failed) { fprintf(stderr, "continuation failed: %s\n", strerror(errno)); done(1); } } /* * Handle end of tape. */ if (i == 0) { vprintf(stdout, "End-of-tape encountered\n"); if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(buf); return; } if (rd % TP_BSIZE != 0) panic("partial block read: %ld should be %ld\n", rd, ntrec * TP_BSIZE); terminateinput(); memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); } if (oldnumtrec == 0) blkcnt = 0; else blkcnt -= oldnumtrec; memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE); blksread++; tapeaddr++; } static void findtapeblksize(void) { long i; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; blkcnt = 0; #ifdef RRESTORE if (host) i = rmtread(tapebuf, ntrec * TP_BSIZE); else #endif i = read(mt, tapebuf, ntrec * TP_BSIZE); if (i <= 0) { fprintf(stderr, "tape read error: %s\n", strerror(errno)); done(1); } if (i % TP_BSIZE != 0) { fprintf(stderr, "Tape block size (%ld) %s (%d)\n", i, "is not a multiple of dump block size", TP_BSIZE); done(1); } ntrec = i / TP_BSIZE; numtrec = ntrec; vprintf(stdout, "Tape block size is %ld\n", ntrec); } void closemt(void) { if (mt < 0) return; if (pipecmdin) { pclose(popenfp); popenfp = NULL; } else #ifdef RRESTORE if (host) rmtclose(); else #endif (void) close(mt); } /* * Read the next block from the tape. * If it is not any valid header, return an error. */ static int gethead(struct s_spcl *buf) { long i; readtape((char *)buf); if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) { if (buf->c_magic == OFS_MAGIC) { fprintf(stderr, "Format of dump tape is too old. Must use\n"); fprintf(stderr, "a version of restore from before 2002.\n"); return (FAIL); } if (swabl(buf->c_magic) != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) { if (buf->c_magic == OFS_MAGIC) { fprintf(stderr, "Format of dump tape is too old. Must use\n"); fprintf(stderr, "a version of restore from before 2002.\n"); } return (FAIL); } if (!Bcvt) { vprintf(stdout, "Note: Doing Byte swapping\n"); Bcvt = 1; } } if (checksum((int *)buf) == FAIL) return (FAIL); if (Bcvt) { swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf); swabst((u_char *)"l",(u_char *) &buf->c_level); swabst((u_char *)"2l4q",(u_char *) &buf->c_flags); } readmapflag = 0; switch (buf->c_type) { case TS_CLRI: case TS_BITS: /* * Have to patch up missing information in bit map headers */ buf->c_size = buf->c_count * TP_BSIZE; if (buf->c_count > TP_NINDIR) readmapflag = 1; else for (i = 0; i < buf->c_count; i++) buf->c_addr[i]++; /* FALL THROUGH */ case TS_TAPE: if (buf->c_magic == NFS_MAGIC && (buf->c_flags & NFS_DR_NEWINODEFMT) == 0) oldinofmt = 1; /* FALL THROUGH */ case TS_END: buf->c_inumber = 0; /* FALL THROUGH */ case TS_ADDR: case TS_INODE: /* * For old dump tapes, have to copy up old fields to * new locations. */ if (buf->c_magic == NFS_MAGIC) { buf->c_tapea = buf->c_old_tapea; buf->c_firstrec = buf->c_old_firstrec; buf->c_date = _time32_to_time(buf->c_old_date); buf->c_ddate = _time32_to_time(buf->c_old_ddate); buf->c_atime = _time32_to_time(buf->c_old_atime); buf->c_mtime = _time32_to_time(buf->c_old_mtime); buf->c_birthtime = 0; buf->c_birthtimensec = 0; buf->c_extsize = 0; } break; default: panic("gethead: unknown inode type %d\n", buf->c_type); break; } if (dumpdate != 0 && _time64_to_time(buf->c_date) != dumpdate) fprintf(stderr, "Header with wrong dumpdate.\n"); /* * If we're restoring a filesystem with the old (FreeBSD 1) * format inodes, copy the uid/gid to the new location */ if (oldinofmt) { buf->c_uid = buf->c_spare1[1]; buf->c_gid = buf->c_spare1[2]; } buf->c_magic = FS_UFS2_MAGIC; tapeaddr = buf->c_tapea; if (dflag) accthdr(buf); return(GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void accthdr(struct s_spcl *header) { static ino_t previno = 0x7fffffff; static int prevtype; static long predict; long blks, i; if (header->c_type == TS_TAPE) { fprintf(stderr, "Volume header "); if (header->c_firstrec) fprintf(stderr, "begins with record %jd", (intmax_t)header->c_firstrec); fprintf(stderr, "\n"); previno = 0x7fffffff; return; } if (previno == 0x7fffffff) goto newcalc; switch (prevtype) { case TS_BITS: fprintf(stderr, "Dumped inodes map header"); break; case TS_CLRI: fprintf(stderr, "Used inodes map header"); break; case TS_INODE: fprintf(stderr, "File header, ino %ju", (uintmax_t)previno); break; case TS_ADDR: fprintf(stderr, "File continuation header, ino %ju", (uintmax_t)previno); break; case TS_END: fprintf(stderr, "End of tape header"); break; } if (predict != blksread - 1) fprintf(stderr, "; predicted %ld blocks, got %ld blocks", predict, blksread - 1); fprintf(stderr, "\n"); newcalc: blks = 0; if (header->c_type != TS_END) for (i = 0; i < header->c_count; i++) if (readmapflag || header->c_addr[i] != 0) blks++; predict = blks; blksread = 0; prevtype = header->c_type; previno = header->c_inumber; } /* * Find an inode header. * Complain if had to skip. */ static void findinode(struct s_spcl *header) { static long skipcnt = 0; long i; char buf[TP_BSIZE]; int htype; curfile.name = ""; curfile.action = UNKNOWN; curfile.mode = 0; curfile.ino = 0; do { htype = header->c_type; switch (htype) { case TS_ADDR: /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if (header->c_addr[i]) readtape(buf); while (gethead(header) == FAIL || _time64_to_time(header->c_date) != dumpdate) { skipcnt++; if (Dflag) { byteslide++; if (byteslide < TP_BSIZE) { blkcnt--; blksread--; } else byteslide = 0; } } break; case TS_INODE: curfile.mode = header->c_mode; curfile.uid = header->c_uid; curfile.gid = header->c_gid; curfile.file_flags = header->c_file_flags; curfile.rdev = header->c_rdev; curfile.atime_sec = header->c_atime; curfile.atime_nsec = header->c_atimensec; curfile.mtime_sec = header->c_mtime; curfile.mtime_nsec = header->c_mtimensec; curfile.birthtime_sec = header->c_birthtime; curfile.birthtime_nsec = header->c_birthtimensec; curfile.extsize = header->c_extsize; curfile.size = header->c_size; curfile.ino = header->c_inumber; break; case TS_END: /* If we missed some tapes, get another volume. */ if (tapesread & (tapesread + 1)) { getvol(0); continue; } curfile.ino = maxino; break; case TS_CLRI: curfile.name = ""; break; case TS_BITS: curfile.name = ""; break; case TS_TAPE: if (Dflag) fprintf(stderr, "unexpected tape header\n"); else panic("unexpected tape header\n"); default: if (Dflag) fprintf(stderr, "unknown tape header type %d\n", spcl.c_type); else panic("unknown tape header type %d\n", spcl.c_type); while (gethead(header) == FAIL || _time64_to_time(header->c_date) != dumpdate) { skipcnt++; if (Dflag) { byteslide++; if (byteslide < TP_BSIZE) { blkcnt--; blksread--; } else byteslide = 0; } } } } while (htype == TS_ADDR); if (skipcnt > 0) fprintf(stderr, "resync restore, skipped %ld %s\n", skipcnt, Dflag ? "bytes" : "blocks"); skipcnt = 0; } static int checksum(int *buf) { int i, j; j = sizeof(union u_spcl) / sizeof(int); i = 0; if (!Bcvt) { do i += *buf++; while (--j); } else { /* What happens if we want to read restore tapes for a 16bit int machine??? */ do i += swabl(*buf++); while (--j); } if (i != CHECKSUM) { fprintf(stderr, "Checksum error %o, inode %ju file %s\n", i, (uintmax_t)curfile.ino, curfile.name); return(FAIL); } return(GOOD); } #ifdef RRESTORE #include void msg(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); } #endif /* RRESTORE */ static u_char * swabshort(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[1]; sp[1] = c; sp += 2; } return (sp); } static u_char * swablong(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[3]; sp[3] = c; c = sp[2]; sp[2] = sp[1]; sp[1] = c; sp += 4; } return (sp); } static u_char * swabquad(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[7]; sp[7] = c; c = sp[1]; sp[1] = sp[6]; sp[6] = c; c = sp[2]; sp[2] = sp[5]; sp[5] = c; c = sp[3]; sp[3] = sp[4]; sp[4] = c; sp += 8; } return (sp); } void swabst(u_char *cp, u_char *sp) { int n = 0; while (*cp) { switch (*cp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = (n * 10) + (*cp++ - '0'); continue; case 's': case 'w': case 'h': if (n == 0) n = 1; sp = swabshort(sp, n); break; case 'l': if (n == 0) n = 1; sp = swablong(sp, n); break; case 'q': if (n == 0) n = 1; sp = swabquad(sp, n); break; case 'b': if (n == 0) n = 1; sp += n; break; default: fprintf(stderr, "Unknown conversion character: %c\n", *cp); done(0); break; } cp++; n = 0; } } static u_long swabl(u_long x) { swabst((u_char *)"l", (u_char *)&x); return (x); } Index: user/ngie/more-tests2/sbin =================================================================== --- user/ngie/more-tests2/sbin (revision 290915) +++ user/ngie/more-tests2/sbin (revision 290916) Property changes on: user/ngie/more-tests2/sbin ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sbin:r290900-290915 Index: user/ngie/more-tests2/share/locale-links/Makefile =================================================================== --- user/ngie/more-tests2/share/locale-links/Makefile (revision 290915) +++ user/ngie/more-tests2/share/locale-links/Makefile (revision 290916) @@ -1,22 +1,22 @@ # $FreeBSD$ LOCALEDIR= ${SHAREDIR}/locale LC_FILES= LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC \ LC_TIME ALIASES= zh_Hans_CN.GB18030 zh_CN.GB18030 \ zh_Hans_CN.GB2312 zh_CN.GB2312 \ zh_Hans_CN.GBK zh_CN.GBK \ zh_Hans_CN.eucCN zh_CN.eucCN \ zh_Hant_HK.Big5HKSCS zh_HK.Big5HKSCS \ zh_Hant_HK.UTF-8 zh_HK.UTF-8 \ zh_Hant_TW.Big5 zh_TW.Big5 \ zh_Hant_TW.UTF-8 zh_TW.UTF-8 .for from to in ${ALIASES} .for f in ${LC_FILES} -SYMLINKS+= ${from}/${f} ${LOCALEDIR}/${to}/${f} +SYMLINKS+= ../${from}/${f} ${LOCALEDIR}/${to}/${f} .endfor .endfor .include Index: user/ngie/more-tests2/share =================================================================== --- user/ngie/more-tests2/share (revision 290915) +++ user/ngie/more-tests2/share (revision 290916) Property changes on: user/ngie/more-tests2/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r290900-290915 Index: user/ngie/more-tests2/sys/mips/atheros/ar71xx_ehci.c =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/ar71xx_ehci.c (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/ar71xx_ehci.c (revision 290916) @@ -1,276 +1,278 @@ /*- * Copyright (c) 2008 Sam Leffler. 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 ``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. */ /* * AR71XX attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for stuff in ar71xx_cpudef.h */ #include #include #define EHCI_HC_DEVSTR "AR71XX Integrated USB 2.0 controller" struct ar71xx_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ }; static device_attach_t ar71xx_ehci_attach; static device_detach_t ar71xx_ehci_detach; bs_r_1_proto(reversed); bs_w_1_proto(reversed); static int ar71xx_ehci_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_NOWILDCARD); } static void ar71xx_ehci_intr(void *arg) { /* XXX TODO: should really see if this was our interrupt.. */ ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_USB); ehci_interrupt(arg); } static int ar71xx_ehci_attach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; /* NB: hints fix the memory location and irq */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } /* * Craft special resource for bus space ops that handle * byte-alignment of non-word addresses. */ sc->sc_io_tag = ar71xx_bus_space_reversed; sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Atheros"); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, ar71xx_ehci_intr, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Arrange to force Host mode, select big-endian byte alignment, * and arrange to not terminate reset operations (the adapter * will ignore it if we do but might as well save a reg write). * Also, the controller has an embedded Transaction Translator * which means port speed must be read from the Port Status * register following a port enable. */ sc->sc_flags = EHCI_SCFLG_SETMODE; switch (ar71xx_soc) { case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; break; default: /* fallthrough */ break; } /* * ehci_reset() needs the correct offset to access the host controller * registers. The AR724x/AR913x offsets aren't 0. */ sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); (void) ehci_reset(sc); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ar71xx_ehci_detach(self); return (ENXIO); } static int ar71xx_ehci_detach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_ehci_probe), DEVMETHOD(device_attach, ar71xx_ehci_attach), DEVMETHOD(device_detach, ar71xx_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(struct ar71xx_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0); DRIVER_MODULE(ehci, apb, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: user/ngie/more-tests2/sys/mips/atheros/ar71xx_gpio.c =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/ar71xx_gpio.c (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/ar71xx_gpio.c (revision 290916) @@ -1,572 +1,589 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2009, Luiz Otavio O Souza. * 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 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. */ /* * GPIO driver for AR71xx */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include "gpio_if.h" #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * Helpers */ static void ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask); static void ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, uint32_t mask); static void ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, struct gpio_pin *pin, uint32_t flags); /* * Driver stuff */ static int ar71xx_gpio_probe(device_t dev); static int ar71xx_gpio_attach(device_t dev); static int ar71xx_gpio_detach(device_t dev); static int ar71xx_gpio_filter(void *arg); static void ar71xx_gpio_intr(void *arg); /* * GPIO interface */ static device_t ar71xx_gpio_get_bus(device_t); static int ar71xx_gpio_pin_max(device_t dev, int *maxpin); static int ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); static int ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name); static int ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); static int ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin); /* * Enable/disable the GPIO function control space. * * This is primarily for the AR71xx, which has SPI CS1/CS2, UART, SLIC, I2S * as GPIO pin options. */ static void ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask) { + + /* + * XXX TODO: refactor this out into a per-chipset method. + */ if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344 || + ar71xx_soc == AR71XX_SOC_QCA9533 || + ar71xx_soc == AR71XX_SOC_QCA9533_V2 || ar71xx_soc == AR71XX_SOC_QCA9556 || ar71xx_soc == AR71XX_SOC_QCA9558) GPIO_SET_BITS(sc, AR934X_GPIO_REG_FUNC, mask); else GPIO_SET_BITS(sc, AR71XX_GPIO_FUNCTION, mask); } static void ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, uint32_t mask) { + + /* + * XXX TODO: refactor this out into a per-chipset method. + */ if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344 || + ar71xx_soc == AR71XX_SOC_QCA9533 || + ar71xx_soc == AR71XX_SOC_QCA9533_V2 || ar71xx_soc == AR71XX_SOC_QCA9556 || ar71xx_soc == AR71XX_SOC_QCA9558) GPIO_CLEAR_BITS(sc, AR934X_GPIO_REG_FUNC, mask); else GPIO_CLEAR_BITS(sc, AR71XX_GPIO_FUNCTION, mask); } static void ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { uint32_t mask; mask = 1 << pin->gp_pin; /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; GPIO_SET_BITS(sc, AR71XX_GPIO_OE, mask); } else { pin->gp_flags |= GPIO_PIN_INPUT; GPIO_CLEAR_BITS(sc, AR71XX_GPIO_OE, mask); } } } static device_t ar71xx_gpio_get_bus(device_t dev) { struct ar71xx_gpio_softc *sc; sc = device_get_softc(dev); return (sc->busdev); } static int ar71xx_gpio_pin_max(device_t dev, int *maxpin) { switch (ar71xx_soc) { case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: *maxpin = AR91XX_GPIO_PINS - 1; break; case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: *maxpin = AR724X_GPIO_PINS - 1; break; case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: *maxpin = AR933X_GPIO_COUNT - 1; break; case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: *maxpin = AR934X_GPIO_COUNT - 1; + break; + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: + *maxpin = QCA953X_GPIO_COUNT - 1; break; case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: *maxpin = QCA955X_GPIO_COUNT - 1; break; default: *maxpin = AR71XX_GPIO_PINS - 1; } return (0); } static int ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { int i; struct ar71xx_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); if (value) GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); else GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); return (0); } static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); *val = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; return (0); } static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin) { int res, i; struct ar71xx_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); res = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; if (res) GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); else GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); return (0); } static int ar71xx_gpio_filter(void *arg) { /* TODO: something useful */ return (FILTER_STRAY); } static void ar71xx_gpio_intr(void *arg) { struct ar71xx_gpio_softc *sc = arg; GPIO_LOCK(sc); /* TODO: something useful */ GPIO_UNLOCK(sc); } static int ar71xx_gpio_probe(device_t dev) { device_set_desc(dev, "Atheros AR71XX GPIO driver"); return (0); } static int ar71xx_gpio_attach(device_t dev) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i, j, maxpin; int mask, pinon; uint32_t oe; KASSERT((device_get_unit(dev) == 0), ("ar71xx_gpio: Only one gpio module supported")); mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Map control/status registers. */ sc->gpio_mem_rid = 0; sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->gpio_mem_rid, RF_ACTIVE); if (sc->gpio_mem_res == NULL) { device_printf(dev, "couldn't map memory\n"); ar71xx_gpio_detach(dev); return (ENXIO); } if ((sc->gpio_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->gpio_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); ar71xx_gpio_detach(dev); return (ENXIO); } if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC, ar71xx_gpio_filter, ar71xx_gpio_intr, sc, &sc->gpio_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); ar71xx_gpio_detach(dev); return (ENXIO); } sc->dev = dev; /* Enable function bits that are required */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "function_set", &mask) == 0) { device_printf(dev, "function_set: 0x%x\n", mask); ar71xx_gpio_function_enable(sc, mask); } /* Disable function bits that are required */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "function_clear", &mask) == 0) { device_printf(dev, "function_clear: 0x%x\n", mask); ar71xx_gpio_function_disable(sc, mask); } /* Disable interrupts for all pins. */ GPIO_WRITE(sc, AR71XX_GPIO_INT_MASK, 0); /* Initialise all pins specified in the mask, up to the pin count */ (void) ar71xx_gpio_pin_max(dev, &maxpin); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "pinmask", &mask) != 0) mask = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "pinon", &pinon) != 0) pinon = 0; device_printf(dev, "gpio pinmask=0x%x\n", mask); for (j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; sc->gpio_npins++; } /* Iniatilize the GPIO pins, keep the loader settings. */ oe = GPIO_READ(sc, AR71XX_GPIO_OE); sc->gpio_pins = malloc(sizeof(*sc->gpio_pins) * sc->gpio_npins, M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0, j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", j); sc->gpio_pins[i].gp_pin = j; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; if (oe & (1 << j)) sc->gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT; else sc->gpio_pins[i].gp_flags = GPIO_PIN_INPUT; i++; } /* Turn on the hinted pins. */ for (i = 0; i < sc->gpio_npins; i++) { j = sc->gpio_pins[i].gp_pin; if ((pinon & (1 << j)) != 0) { ar71xx_gpio_pin_setflags(dev, j, GPIO_PIN_OUTPUT); ar71xx_gpio_pin_set(dev, j, 1); } } /* * Search through the function hints, in case there's some * overrides such as LNA control. * * hint.gpio.X.func..gpiofunc= * hint.gpio.X.func..gpiomode=1 (for output, default low) */ for (i = 0; i <= maxpin; i++) { char buf[32]; int gpiofunc, gpiomode; snprintf(buf, 32, "func.%d.gpiofunc", i); if (resource_int_value(device_get_name(dev), device_get_unit(dev), buf, &gpiofunc) != 0) continue; /* Get the mode too */ snprintf(buf, 32, "func.%d.gpiomode", i); if (resource_int_value(device_get_name(dev), device_get_unit(dev), buf, &gpiomode) != 0) continue; /* We only handle mode=1 for now */ if (gpiomode != 1) continue; device_printf(dev, "%s: GPIO %d: func=%d, mode=%d\n", __func__, i, gpiofunc, gpiomode); /* Set output (bit == 0) */ oe = GPIO_READ(sc, AR71XX_GPIO_OE); oe &= ~ (1 << i); GPIO_WRITE(sc, AR71XX_GPIO_OE, oe); /* Set pin value = 0, so it stays low by default */ oe = GPIO_READ(sc, AR71XX_GPIO_OUT); oe &= ~ (1 << i); GPIO_WRITE(sc, AR71XX_GPIO_OUT, oe); /* Finally: Set the output config */ ar71xx_gpio_ouput_configure(i, gpiofunc); } sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) { ar71xx_gpio_detach(dev); return (ENXIO); } return (0); } static int ar71xx_gpio_detach(device_t dev) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); gpiobus_detach_bus(dev); if (sc->gpio_ih) bus_teardown_intr(dev, sc->gpio_irq_res, sc->gpio_ih); if (sc->gpio_irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->gpio_irq_rid, sc->gpio_irq_res); if (sc->gpio_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, sc->gpio_mem_res); if (sc->gpio_pins) free(sc->gpio_pins, M_DEVBUF); mtx_destroy(&sc->gpio_mtx); return(0); } static device_method_t ar71xx_gpio_methods[] = { DEVMETHOD(device_probe, ar71xx_gpio_probe), DEVMETHOD(device_attach, ar71xx_gpio_attach), DEVMETHOD(device_detach, ar71xx_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, ar71xx_gpio_get_bus), DEVMETHOD(gpio_pin_max, ar71xx_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ar71xx_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ar71xx_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ar71xx_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ar71xx_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ar71xx_gpio_pin_get), DEVMETHOD(gpio_pin_set, ar71xx_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ar71xx_gpio_pin_toggle), {0, 0}, }; static driver_t ar71xx_gpio_driver = { "gpio", ar71xx_gpio_methods, sizeof(struct ar71xx_gpio_softc), }; static devclass_t ar71xx_gpio_devclass; DRIVER_MODULE(ar71xx_gpio, apb, ar71xx_gpio_driver, ar71xx_gpio_devclass, 0, 0); Index: user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.c =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.c (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.c (revision 290916) @@ -1,217 +1,235 @@ /*- * Copyright (c) 2010 Adrian Chadd * 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 "opt_ddb.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 #define AR71XX_SYS_TYPE_LEN 128 static char ar71xx_sys_type[AR71XX_SYS_TYPE_LEN]; enum ar71xx_soc_type ar71xx_soc; struct ar71xx_cpu_def * ar71xx_cpu_ops = NULL; void ar71xx_detect_sys_type(void) { char *chip = "????"; uint32_t id; uint32_t major; uint32_t minor; uint32_t rev = 0; id = ATH_READ_REG(AR71XX_RST_RESET_REG_REV_ID); major = id & REV_ID_MAJOR_MASK; switch (major) { case REV_ID_MAJOR_AR71XX: minor = id & AR71XX_REV_ID_MINOR_MASK; rev = id >> AR71XX_REV_ID_REVISION_SHIFT; rev &= AR71XX_REV_ID_REVISION_MASK; ar71xx_cpu_ops = &ar71xx_chip_def; switch (minor) { case AR71XX_REV_ID_MINOR_AR7130: ar71xx_soc = AR71XX_SOC_AR7130; chip = "7130"; break; case AR71XX_REV_ID_MINOR_AR7141: ar71xx_soc = AR71XX_SOC_AR7141; chip = "7141"; break; case AR71XX_REV_ID_MINOR_AR7161: ar71xx_soc = AR71XX_SOC_AR7161; chip = "7161"; break; } break; case REV_ID_MAJOR_AR7240: ar71xx_soc = AR71XX_SOC_AR7240; chip = "7240"; ar71xx_cpu_ops = &ar724x_chip_def; rev = (id & AR724X_REV_ID_REVISION_MASK); break; case REV_ID_MAJOR_AR7241: ar71xx_soc = AR71XX_SOC_AR7241; chip = "7241"; ar71xx_cpu_ops = &ar724x_chip_def; rev = (id & AR724X_REV_ID_REVISION_MASK); break; case REV_ID_MAJOR_AR7242: ar71xx_soc = AR71XX_SOC_AR7242; chip = "7242"; ar71xx_cpu_ops = &ar724x_chip_def; rev = (id & AR724X_REV_ID_REVISION_MASK); break; case REV_ID_MAJOR_AR913X: minor = id & AR91XX_REV_ID_MINOR_MASK; rev = id >> AR91XX_REV_ID_REVISION_SHIFT; rev &= AR91XX_REV_ID_REVISION_MASK; ar71xx_cpu_ops = &ar91xx_chip_def; switch (minor) { case AR91XX_REV_ID_MINOR_AR9130: ar71xx_soc = AR71XX_SOC_AR9130; chip = "9130"; break; case AR91XX_REV_ID_MINOR_AR9132: ar71xx_soc = AR71XX_SOC_AR9132; chip = "9132"; break; } break; case REV_ID_MAJOR_AR9330: minor = 0; rev = (id & AR933X_REV_ID_REVISION_MASK); chip = "9330"; ar71xx_cpu_ops = &ar933x_chip_def; ar71xx_soc = AR71XX_SOC_AR9330; break; case REV_ID_MAJOR_AR9331: minor = 1; rev = (id & AR933X_REV_ID_REVISION_MASK); chip = "9331"; ar71xx_soc = AR71XX_SOC_AR9331; ar71xx_cpu_ops = &ar933x_chip_def; break; case REV_ID_MAJOR_AR9341: minor = 0; rev = (id & AR934X_REV_ID_REVISION_MASK); chip = "9341"; ar71xx_soc = AR71XX_SOC_AR9341; ar71xx_cpu_ops = &ar934x_chip_def; break; case REV_ID_MAJOR_AR9342: minor = 0; rev = (id & AR934X_REV_ID_REVISION_MASK); chip = "9342"; ar71xx_soc = AR71XX_SOC_AR9342; ar71xx_cpu_ops = &ar934x_chip_def; break; case REV_ID_MAJOR_AR9344: minor = 0; rev = (id & AR934X_REV_ID_REVISION_MASK); chip = "9344"; ar71xx_soc = AR71XX_SOC_AR9344; ar71xx_cpu_ops = &ar934x_chip_def; + break; + + case REV_ID_MAJOR_QCA9533: + minor = 0; + rev = (id & QCA953X_REV_ID_REVISION_MASK); + chip = "9533"; + ar71xx_soc = AR71XX_SOC_QCA9533; + ar71xx_cpu_ops = &qca953x_chip_def; + break; + + case REV_ID_MAJOR_QCA9533_V2: + minor = 0; + rev = (id & QCA953X_REV_ID_REVISION_MASK); + chip = "9533v2"; + ar71xx_soc = AR71XX_SOC_QCA9533_V2; + ar71xx_cpu_ops = &qca953x_chip_def; break; case REV_ID_MAJOR_QCA9556: minor = 0; rev = (id & QCA955X_REV_ID_REVISION_MASK); chip = "9556"; ar71xx_soc = AR71XX_SOC_QCA9556; ar71xx_cpu_ops = &qca955x_chip_def; break; case REV_ID_MAJOR_QCA9558: minor = 0; rev = (id & QCA955X_REV_ID_REVISION_MASK); chip = "9558"; ar71xx_soc = AR71XX_SOC_QCA9558; ar71xx_cpu_ops = &qca955x_chip_def; break; default: panic("ar71xx: unknown chip id:0x%08x\n", id); } sprintf(ar71xx_sys_type, "Atheros AR%s rev %u", chip, rev); } const char * ar71xx_get_system_type(void) { return ar71xx_sys_type; } Index: user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.h =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.h (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/ar71xx_setup.h (revision 290916) @@ -1,55 +1,57 @@ /*- * Copyright (c) 2010 Adrian Chadd * 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. */ /* $FreeBSD$ */ #ifndef __AR71XX_SETUP_H__ #define __AR71XX_SETUP_H__ enum ar71xx_soc_type { AR71XX_SOC_UNKNOWN, AR71XX_SOC_AR7130, AR71XX_SOC_AR7141, AR71XX_SOC_AR7161, AR71XX_SOC_AR7240, AR71XX_SOC_AR7241, AR71XX_SOC_AR7242, AR71XX_SOC_AR9130, AR71XX_SOC_AR9132, AR71XX_SOC_AR9330, AR71XX_SOC_AR9331, AR71XX_SOC_AR9341, AR71XX_SOC_AR9342, AR71XX_SOC_AR9344, AR71XX_SOC_QCA9556, AR71XX_SOC_QCA9558, + AR71XX_SOC_QCA9533, + AR71XX_SOC_QCA9533_V2, }; extern enum ar71xx_soc_type ar71xx_soc; extern void ar71xx_detect_sys_type(void); extern const char *ar71xx_get_system_type(void); #endif Index: user/ngie/more-tests2/sys/mips/atheros/files.ar71xx =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/files.ar71xx (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/files.ar71xx (revision 290916) @@ -1,33 +1,34 @@ # $FreeBSD$ mips/atheros/apb.c optional ar71xx_apb mips/atheros/ar71xx_gpio.c optional gpio mips/atheros/ar71xx_machdep.c standard mips/atheros/ar71xx_ehci.c optional ehci mips/atheros/ar71xx_ohci.c optional ohci mips/atheros/ar71xx_pci.c optional ar71xx_pci pci mips/atheros/ar724x_pci.c optional ar724x_pci pci mips/atheros/ar71xx_pci_bus_space.c optional pci mips/atheros/ar71xx_spi.c optional ar71xx_spi mips/atheros/ar71xx_macaddr.c standard mips/atheros/pcf2123_rtc.c optional pcf2123_rtc ar71xx_spi mips/atheros/ar71xx_wdog.c optional ar71xx_wdog mips/atheros/if_arge.c optional arge mips/atheros/uart_bus_ar71xx.c optional uart_ar71xx mips/atheros/uart_cpu_ar71xx.c optional uart_ar71xx mips/atheros/uart_bus_ar933x.c optional uart_ar933x mips/atheros/uart_cpu_ar933x.c optional uart_ar933x mips/atheros/uart_dev_ar933x.c optional uart_ar933x mips/atheros/ar71xx_bus_space_reversed.c standard mips/mips/intr_machdep.c standard mips/mips/tick.c standard mips/atheros/ar71xx_setup.c standard mips/atheros/ar71xx_chip.c standard mips/atheros/ar724x_chip.c standard mips/atheros/ar91xx_chip.c standard mips/atheros/ar933x_chip.c standard mips/atheros/ar934x_chip.c standard +mips/atheros/qca953x_chip.c standard mips/atheros/qca955x_chip.c standard mips/atheros/ar71xx_fixup.c optional ar71xx_ath_eeprom mips/atheros/qca955x_apb.c optional qca955x_apb mips/atheros/qca955x_pci.c optional qca955x_pci pci Index: user/ngie/more-tests2/sys/mips/atheros/if_arge.c =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/if_arge.c (revision 290915) +++ user/ngie/more-tests2/sys/mips/atheros/if_arge.c (revision 290916) @@ -1,2699 +1,2720 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * 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 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$"); /* * AR71XX gigabit ethernet driver */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include "opt_arge.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 "opt_arge.h" #if defined(ARGE_MDIO) #include #include #include "mdio_if.h" #endif MODULE_DEPEND(arge, ether, 1, 1, 1); MODULE_DEPEND(arge, miibus, 1, 1, 1); MODULE_VERSION(arge, 1); #include "miibus_if.h" #include #include #include /* XXX tsk! */ +#include /* XXX tsk! */ #include /* XXX tsk! */ #include #include #include #include typedef enum { ARGE_DBG_MII = 0x00000001, ARGE_DBG_INTR = 0x00000002, ARGE_DBG_TX = 0x00000004, ARGE_DBG_RX = 0x00000008, ARGE_DBG_ERR = 0x00000010, ARGE_DBG_RESET = 0x00000020, ARGE_DBG_PLL = 0x00000040, } arge_debug_flags; static const char * arge_miicfg_str[] = { "NONE", "GMII", "MII", "RGMII", "RMII", "SGMII" }; #ifdef ARGE_DEBUG #define ARGEDEBUG(_sc, _m, ...) \ do { \ if ((_m) & (_sc)->arge_debug) \ device_printf((_sc)->arge_dev, __VA_ARGS__); \ } while (0) #else #define ARGEDEBUG(_sc, _m, ...) #endif static int arge_attach(device_t); static int arge_detach(device_t); static void arge_flush_ddr(struct arge_softc *); static int arge_ifmedia_upd(struct ifnet *); static void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int arge_ioctl(struct ifnet *, u_long, caddr_t); static void arge_init(void *); static void arge_init_locked(struct arge_softc *); static void arge_link_task(void *, int); static void arge_update_link_locked(struct arge_softc *sc); static void arge_set_pll(struct arge_softc *, int, int); static int arge_miibus_readreg(device_t, int, int); static void arge_miibus_statchg(device_t); static int arge_miibus_writereg(device_t, int, int, int); static int arge_probe(device_t); static void arge_reset_dma(struct arge_softc *); static int arge_resume(device_t); static int arge_rx_ring_init(struct arge_softc *); static void arge_rx_ring_free(struct arge_softc *sc); static int arge_tx_ring_init(struct arge_softc *); static void arge_tx_ring_free(struct arge_softc *); #ifdef DEVICE_POLLING static int arge_poll(struct ifnet *, enum poll_cmd, int); #endif static int arge_shutdown(device_t); static void arge_start(struct ifnet *); static void arge_start_locked(struct ifnet *); static void arge_stop(struct arge_softc *); static int arge_suspend(device_t); static int arge_rx_locked(struct arge_softc *); static void arge_tx_locked(struct arge_softc *); static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); static void arge_hinted_child(device_t bus, const char *dname, int dunit); /* * ifmedia callbacks for multiPHY MAC */ void arge_multiphy_mediastatus(struct ifnet *, struct ifmediareq *); int arge_multiphy_mediachange(struct ifnet *); static void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int arge_dma_alloc(struct arge_softc *); static void arge_dma_free(struct arge_softc *); static int arge_newbuf(struct arge_softc *, int); static __inline void arge_fixup_rx(struct mbuf *); static device_method_t arge_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arge_probe), DEVMETHOD(device_attach, arge_attach), DEVMETHOD(device_detach, arge_detach), DEVMETHOD(device_suspend, arge_suspend), DEVMETHOD(device_resume, arge_resume), DEVMETHOD(device_shutdown, arge_shutdown), /* MII interface */ DEVMETHOD(miibus_readreg, arge_miibus_readreg), DEVMETHOD(miibus_writereg, arge_miibus_writereg), DEVMETHOD(miibus_statchg, arge_miibus_statchg), /* bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), DEVMETHOD(bus_hinted_child, arge_hinted_child), DEVMETHOD_END }; static driver_t arge_driver = { "arge", arge_methods, sizeof(struct arge_softc) }; static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); #if defined(ARGE_MDIO) static int argemdio_probe(device_t); static int argemdio_attach(device_t); static int argemdio_detach(device_t); /* * Declare an additional, separate driver for accessing the MDIO bus. */ static device_method_t argemdio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, argemdio_probe), DEVMETHOD(device_attach, argemdio_attach), DEVMETHOD(device_detach, argemdio_detach), /* bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), /* MDIO access */ DEVMETHOD(mdio_readreg, arge_miibus_readreg), DEVMETHOD(mdio_writereg, arge_miibus_writereg), }; DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods, sizeof(struct arge_softc)); static devclass_t argemdio_devclass; DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0); DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0); DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0); #endif static struct mtx miibus_mtx; MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF); /* * Flushes all * * XXX this needs to be done at interrupt time! Grr! */ static void arge_flush_ddr(struct arge_softc *sc) { switch (sc->arge_mac_unit) { case 0: ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_GE0); break; case 1: ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_GE1); break; default: device_printf(sc->arge_dev, "%s: unknown unit (%d)\n", __func__, sc->arge_mac_unit); break; } } static int arge_probe(device_t dev) { device_set_desc(dev, "Atheros AR71xx built-in ethernet interface"); return (BUS_PROBE_NOWILDCARD); } #ifdef ARGE_DEBUG static void arge_attach_intr_sysctl(device_t dev, struct sysctl_oid_list *parent) { struct arge_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); struct sysctl_oid *tree = device_get_sysctl_tree(dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); char sn[8]; int i; tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "intr", CTLFLAG_RD, NULL, "Interrupt statistics"); child = SYSCTL_CHILDREN(tree); for (i = 0; i < 32; i++) { snprintf(sn, sizeof(sn), "%d", i); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD, &sc->intr_stats.count[i], 0, ""); } } #endif static void arge_attach_sysctl(device_t dev) { struct arge_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); struct sysctl_oid *tree = device_get_sysctl_tree(dev); #ifdef ARGE_DEBUG SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->arge_debug, 0, "arge interface debugging flags"); arge_attach_intr_sysctl(dev, SYSCTL_CHILDREN(tree)); #endif SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_aligned", CTLFLAG_RW, &sc->stats.tx_pkts_aligned, 0, "number of TX aligned packets"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_unaligned", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned, 0, "number of TX unaligned packets"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_unaligned_start", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned_start, 0, "number of TX unaligned packets (start)"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_unaligned_len", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned_len, 0, "number of TX unaligned packets (len)"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_nosegs", CTLFLAG_RW, &sc->stats.tx_pkts_nosegs, 0, "number of TX packets fail with no ring slots avail"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "intr_stray_filter", CTLFLAG_RW, &sc->stats.intr_stray, 0, "number of stray interrupts (filter)"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "intr_stray_intr", CTLFLAG_RW, &sc->stats.intr_stray2, 0, "number of stray interrupts (intr)"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "intr_ok", CTLFLAG_RW, &sc->stats.intr_ok, 0, "number of OK interrupts"); #ifdef ARGE_DEBUG SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod", CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cons", CTLFLAG_RW, &sc->arge_cdata.arge_tx_cons, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cnt", CTLFLAG_RW, &sc->arge_cdata.arge_tx_cnt, 0, ""); #endif } static void arge_reset_mac(struct arge_softc *sc) { uint32_t reg; uint32_t reset_reg; ARGEDEBUG(sc, ARGE_DBG_RESET, "%s called\n", __func__); /* Step 1. Soft-reset MAC */ ARGE_SET_BITS(sc, AR71XX_MAC_CFG1, MAC_CFG1_SOFT_RESET); DELAY(20); /* Step 2. Punt the MAC core from the central reset register */ /* * XXX TODO: migrate this (and other) chip specific stuff into * a chipdef method. */ if (sc->arge_mac_unit == 0) { reset_reg = RST_RESET_GE0_MAC; } else { reset_reg = RST_RESET_GE1_MAC; } /* * AR934x (and later) also needs the MDIO block reset. * XXX should methodize this! */ if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344) { if (sc->arge_mac_unit == 0) { reset_reg |= AR934X_RESET_GE0_MDIO; } else { reset_reg |= AR934X_RESET_GE1_MDIO; } } if (ar71xx_soc == AR71XX_SOC_QCA9556 || ar71xx_soc == AR71XX_SOC_QCA9558) { if (sc->arge_mac_unit == 0) { reset_reg |= QCA955X_RESET_GE0_MDIO; } else { reset_reg |= QCA955X_RESET_GE1_MDIO; } } + + if (ar71xx_soc == AR71XX_SOC_QCA9533 || + ar71xx_soc == AR71XX_SOC_QCA9533_V2) { + if (sc->arge_mac_unit == 0) { + reset_reg |= QCA953X_RESET_GE0_MDIO; + } else { + reset_reg |= QCA953X_RESET_GE1_MDIO; + } + } + ar71xx_device_stop(reset_reg); DELAY(100); ar71xx_device_start(reset_reg); /* Step 3. Reconfigure MAC block */ ARGE_WRITE(sc, AR71XX_MAC_CFG1, MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE | MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE); reg = ARGE_READ(sc, AR71XX_MAC_CFG2); reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ; ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg); ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); } /* * These values map to the divisor values programmed into * AR71XX_MAC_MII_CFG. * * The index of each value corresponds to the divisor section * value in AR71XX_MAC_MII_CFG (ie, table[0] means '0' in * AR71XX_MAC_MII_CFG, table[1] means '1', etc.) */ static const uint32_t ar71xx_mdio_div_table[] = { 4, 4, 6, 8, 10, 14, 20, 28, }; static const uint32_t ar7240_mdio_div_table[] = { 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, }; static const uint32_t ar933x_mdio_div_table[] = { 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, }; /* * Lookup the divisor to use based on the given frequency. * * Returns the divisor to use, or -ve on error. */ static int arge_mdio_get_divider(struct arge_softc *sc, unsigned long mdio_clock) { unsigned long ref_clock, t; const uint32_t *table; int ndivs; int i; /* * This is the base MDIO frequency on the SoC. * The dividers .. well, divide. Duh. */ ref_clock = ar71xx_mdio_freq(); /* * If either clock is undefined, just tell the * caller to fall through to the defaults. */ if (ref_clock == 0 || mdio_clock == 0) return (-EINVAL); /* * Pick the correct table! */ switch (ar71xx_soc) { case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: table = ar933x_mdio_div_table; ndivs = nitems(ar933x_mdio_div_table); break; case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: table = ar7240_mdio_div_table; ndivs = nitems(ar7240_mdio_div_table); break; default: table = ar71xx_mdio_div_table; ndivs = nitems(ar71xx_mdio_div_table); } /* * Now, walk through the list and find the first divisor * that falls under the target MDIO frequency. * * The divisors go up, but the corresponding frequencies * are actually decreasing. */ for (i = 0; i < ndivs; i++) { t = ref_clock / table[i]; if (t <= mdio_clock) { return (i); } } ARGEDEBUG(sc, ARGE_DBG_RESET, "No divider found; MDIO=%lu Hz; target=%lu Hz\n", ref_clock, mdio_clock); return (-ENOENT); } /* * Fetch the MDIO bus clock rate. * * For now, the default is DIV_28 for everything * bar AR934x, which will be DIV_58. * * It will definitely need updating to take into account * the MDIO bus core clock rate and the target clock * rate for the chip. */ static uint32_t arge_fetch_mdiobus_clock_rate(struct arge_softc *sc) { int mdio_freq, div; /* * Is the MDIO frequency defined? If so, find a divisor that * makes reasonable sense. Don't overshoot the frequency. */ if (resource_int_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "mdio_freq", &mdio_freq) == 0) { sc->arge_mdiofreq = mdio_freq; div = arge_mdio_get_divider(sc, sc->arge_mdiofreq); if (bootverbose) device_printf(sc->arge_dev, "%s: mdio ref freq=%llu Hz, target freq=%llu Hz," " divisor index=%d\n", __func__, (unsigned long long) ar71xx_mdio_freq(), (unsigned long long) mdio_freq, div); if (div >= 0) return (div); } /* * Default value(s). * * XXX obviously these need .. fixing. * * From Linux/OpenWRT: * * + 7240? DIV_6 * + Builtin-switch port and not 934x? DIV_10 * + Not built-in switch port and 934x? DIV_58 * + .. else DIV_28. */ switch (ar71xx_soc) { case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: return (MAC_MII_CFG_CLOCK_DIV_58); break; default: return (MAC_MII_CFG_CLOCK_DIV_28); } } static void arge_reset_miibus(struct arge_softc *sc) { uint32_t mdio_div; mdio_div = arge_fetch_mdiobus_clock_rate(sc); /* * XXX AR934x and later; should we be also resetting the * MDIO block(s) using the reset register block? */ /* Reset MII bus; program in the default divisor */ ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET | mdio_div); DELAY(100); ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, mdio_div); DELAY(100); } static void arge_fetch_pll_config(struct arge_softc *sc) { long int val; if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_10", &val) == 0) { sc->arge_pllcfg.pll_10 = val; device_printf(sc->arge_dev, "%s: pll_10 = 0x%x\n", __func__, (int) val); } if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_100", &val) == 0) { sc->arge_pllcfg.pll_100 = val; device_printf(sc->arge_dev, "%s: pll_100 = 0x%x\n", __func__, (int) val); } if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_1000", &val) == 0) { sc->arge_pllcfg.pll_1000 = val; device_printf(sc->arge_dev, "%s: pll_1000 = 0x%x\n", __func__, (int) val); } } static int arge_attach(device_t dev) { struct ifnet *ifp; struct arge_softc *sc; int error = 0, rid, i; uint32_t hint; long eeprom_mac_addr = 0; int miicfg = 0; int readascii = 0; int local_mac = 0; uint8_t local_macaddr[ETHER_ADDR_LEN]; char * local_macstr; char devid_str[32]; int count; sc = device_get_softc(dev); sc->arge_dev = dev; sc->arge_mac_unit = device_get_unit(dev); /* * See if there's a "board" MAC address hint available for * this particular device. * * This is in the environment - it'd be nice to use the resource_*() * routines, but at the moment the system is booting, the resource hints * are set to the 'static' map so they're not pulling from kenv. */ snprintf(devid_str, 32, "hint.%s.%d.macaddr", device_get_name(dev), device_get_unit(dev)); if ((local_macstr = kern_getenv(devid_str)) != NULL) { uint32_t tmpmac[ETHER_ADDR_LEN]; /* Have a MAC address; should use it */ device_printf(dev, "Overriding MAC address from environment: '%s'\n", local_macstr); /* Extract out the MAC address */ /* XXX this should all be a generic method */ count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", &tmpmac[0], &tmpmac[1], &tmpmac[2], &tmpmac[3], &tmpmac[4], &tmpmac[5]); if (count == 6) { /* Valid! */ local_mac = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) local_macaddr[i] = tmpmac[i]; } /* Done! */ freeenv(local_macstr); local_macstr = NULL; } /* * Hardware workarounds. */ switch (ar71xx_soc) { case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: /* Arbitrary alignment */ sc->arge_hw_flags |= ARGE_HW_FLG_TX_DESC_ALIGN_1BYTE; sc->arge_hw_flags |= ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE; break; default: sc->arge_hw_flags |= ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE; sc->arge_hw_flags |= ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE; break; } /* * Some units (eg the TP-Link WR-1043ND) do not have a convenient * EEPROM location to read the ethernet MAC address from. * OpenWRT simply snaffles it from a fixed location. * * Since multiple units seem to use this feature, include * a method of setting the MAC address based on an flash location * in CPU address space. * * Some vendors have decided to store the mac address as a literal * string of 18 characters in xx:xx:xx:xx:xx:xx format instead of * an array of numbers. Expose a hint to turn on this conversion * feature via strtol() */ if (local_mac == 0 && resource_long_value(device_get_name(dev), device_get_unit(dev), "eeprommac", &eeprom_mac_addr) == 0) { local_mac = 1; int i; const char *mac = (const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr); device_printf(dev, "Overriding MAC from EEPROM\n"); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "readascii", &readascii) == 0) { device_printf(dev, "Vendor stores MAC in ASCII format\n"); for (i = 0; i < 6; i++) { local_macaddr[i] = strtol(&(mac[i*3]), NULL, 16); } } else { for (i = 0; i < 6; i++) { local_macaddr[i] = mac[i]; } } } KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)), ("if_arge: Only MAC0 and MAC1 supported")); /* * Fetch the PLL configuration. */ arge_fetch_pll_config(sc); /* * Get the MII configuration, if applicable. */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "miimode", &miicfg) == 0) { /* XXX bounds check? */ device_printf(dev, "%s: overriding MII mode to '%s'\n", __func__, arge_miicfg_str[miicfg]); sc->arge_miicfg = miicfg; } /* * Get which PHY of 5 available we should use for this unit */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "phymask", &sc->arge_phymask) != 0) { /* * Use port 4 (WAN) for GE0. For any other port use * its PHY the same as its unit number */ if (sc->arge_mac_unit == 0) sc->arge_phymask = (1 << 4); else /* Use all phys up to 4 */ sc->arge_phymask = (1 << 4) - 1; device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask); } /* * Get default/hard-coded media & duplex mode. */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "media", &hint) != 0) hint = 0; if (hint == 1000) sc->arge_media_type = IFM_1000_T; else if (hint == 100) sc->arge_media_type = IFM_100_TX; else if (hint == 10) sc->arge_media_type = IFM_10_T; else sc->arge_media_type = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "fduplex", &hint) != 0) hint = 1; if (hint) sc->arge_duplex_mode = IFM_FDX; else sc->arge_duplex_mode = 0; mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc); /* Map control/status registers. */ sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } /* Allocate interrupts */ rid = 0; sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->arge_irq == NULL) { device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } /* Allocate ifnet structure. */ ifp = sc->arge_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "couldn't allocate ifnet structure\n"); error = ENOSPC; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = arge_ioctl; ifp->if_start = arge_start; ifp->if_init = arge_init; sc->arge_if_flags = ifp->if_flags; /* XXX: add real size */ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); /* Tell the upper layer(s) we support long frames. */ ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif /* If there's a local mac defined, copy that in */ if (local_mac == 1) { (void) ar71xx_mac_addr_init(sc->arge_eaddr, local_macaddr, 0, 0); } else { /* * No MAC address configured. Generate the random one. */ if (bootverbose) device_printf(dev, "Generating random ethernet address.\n"); (void) ar71xx_mac_addr_random_init(sc->arge_eaddr); } if (arge_dma_alloc(sc) != 0) { error = ENXIO; goto fail; } /* * Don't do this for the MDIO bus case - it's already done * as part of the MDIO bus attachment. */ #if !defined(ARGE_MDIO) /* Initialize the MAC block */ arge_reset_mac(sc); arge_reset_miibus(sc); #endif /* Configure MII mode, just for convienence */ if (sc->arge_miicfg != 0) ar71xx_device_set_mii_if(sc->arge_mac_unit, sc->arge_miicfg); /* * Set all Ethernet address registers to the same initial values * set all four addresses to 66-88-aa-cc-dd-ee */ ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24) | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8) | sc->arge_eaddr[5]); ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8) | sc->arge_eaddr[1]); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); /* * SoC specific bits. */ switch (ar71xx_soc) { case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0010ffff); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x015500aa); break; /* AR71xx, AR913x */ default: ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff); } ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, FIFO_RX_FILTMATCH_DEFAULT); ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, FIFO_RX_FILTMASK_DEFAULT); #if defined(ARGE_MDIO) sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev); #endif device_printf(sc->arge_dev, "finishing attachment, phymask %04x" ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ? "null" : "set"); for (i = 0; i < ARGE_NPHY; i++) { if (((1 << i) & sc->arge_phymask) != 0) { error = mii_attach(sc->arge_miiproxy != NULL ? sc->arge_miiproxy : sc->arge_dev, &sc->arge_miibus, sc->arge_ifp, arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, i, MII_OFFSET_ANY, 0); if (error != 0) { device_printf(sc->arge_dev, "unable to attach" " PHY %d: %d\n", i, error); goto fail; } } } if (sc->arge_miibus == NULL) { /* no PHY, so use hard-coded values */ ifmedia_init(&sc->arge_ifmedia, 0, arge_multiphy_mediachange, arge_multiphy_mediastatus); ifmedia_add(&sc->arge_ifmedia, IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode, 0, NULL); ifmedia_set(&sc->arge_ifmedia, IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode); arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode); } /* Call MI attach routine. */ ether_ifattach(sc->arge_ifp, sc->arge_eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); if (error) { device_printf(sc->arge_dev, "couldn't set up irq\n"); ether_ifdetach(sc->arge_ifp); goto fail; } /* setup sysctl variables */ arge_attach_sysctl(sc->arge_dev); fail: if (error) arge_detach(dev); return (error); } static int arge_detach(device_t dev) { struct arge_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->arge_ifp; KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized")); /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { ARGE_LOCK(sc); sc->arge_detach = 1; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif arge_stop(sc); ARGE_UNLOCK(sc); taskqueue_drain(taskqueue_swi, &sc->arge_link_task); ether_ifdetach(ifp); } if (sc->arge_miibus) device_delete_child(dev, sc->arge_miibus); if (sc->arge_miiproxy) device_delete_child(dev, sc->arge_miiproxy); bus_generic_detach(dev); if (sc->arge_intrhand) bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand); if (sc->arge_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid, sc->arge_res); if (ifp) if_free(ifp); arge_dma_free(sc); mtx_destroy(&sc->arge_mtx); return (0); } static int arge_suspend(device_t dev) { panic("%s", __func__); return 0; } static int arge_resume(device_t dev) { panic("%s", __func__); return 0; } static int arge_shutdown(device_t dev) { struct arge_softc *sc; sc = device_get_softc(dev); ARGE_LOCK(sc); arge_stop(sc); ARGE_UNLOCK(sc); return (0); } static void arge_hinted_child(device_t bus, const char *dname, int dunit) { BUS_ADD_CHILD(bus, 0, dname, dunit); device_printf(bus, "hinted child %s%d\n", dname, dunit); } static int arge_mdio_busy(struct arge_softc *sc) { int i,result; for (i = 0; i < ARGE_MII_TIMEOUT; i++) { DELAY(5); ARGE_MDIO_BARRIER_READ(sc); result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR); if (! result) return (0); DELAY(5); } return (-1); } static int arge_miibus_readreg(device_t dev, int phy, int reg) { struct arge_softc * sc = device_get_softc(dev); int result; uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); mtx_lock(&miibus_mtx); ARGE_MDIO_BARRIER_RW(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); ARGE_MDIO_BARRIER_WRITE(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); ARGE_MDIO_BARRIER_WRITE(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); if (arge_mdio_busy(sc) != 0) { mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__); /* XXX: return ERRNO istead? */ return (-1); } ARGE_MDIO_BARRIER_READ(sc); result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; ARGE_MDIO_BARRIER_RW(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, phy, reg, addr, result); return (result); } static int arge_miibus_writereg(device_t dev, int phy, int reg, int data) { struct arge_softc * sc = device_get_softc(dev); uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); mtx_lock(&miibus_mtx); ARGE_MDIO_BARRIER_RW(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); ARGE_MDIO_BARRIER_WRITE(sc); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); ARGE_MDIO_BARRIER_WRITE(sc); if (arge_mdio_busy(sc) != 0) { mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__); /* XXX: return ERRNO istead? */ return (-1); } mtx_unlock(&miibus_mtx); return (0); } static void arge_miibus_statchg(device_t dev) { struct arge_softc *sc; sc = device_get_softc(dev); taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task); } static void arge_link_task(void *arg, int pending) { struct arge_softc *sc; sc = (struct arge_softc *)arg; ARGE_LOCK(sc); arge_update_link_locked(sc); ARGE_UNLOCK(sc); } static void arge_update_link_locked(struct arge_softc *sc) { struct mii_data *mii; struct ifnet *ifp; uint32_t media, duplex; mii = device_get_softc(sc->arge_miibus); ifp = sc->arge_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { return; } /* * If we have a static media type configured, then * use that. Some PHY configurations (eg QCA955x -> AR8327) * use a static speed/duplex between the SoC and switch, * even though the front-facing PHY speed changes. */ if (sc->arge_media_type != 0) { ARGEDEBUG(sc, ARGE_DBG_MII, "%s: fixed; media=%d, duplex=%d\n", __func__, sc->arge_media_type, sc->arge_duplex_mode); if (mii->mii_media_status & IFM_ACTIVE) { sc->arge_link_status = 1; } else { sc->arge_link_status = 0; } arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode); } if (mii->mii_media_status & IFM_ACTIVE) { media = IFM_SUBTYPE(mii->mii_media_active); if (media != IFM_NONE) { sc->arge_link_status = 1; duplex = mii->mii_media_active & IFM_GMASK; ARGEDEBUG(sc, ARGE_DBG_MII, "%s: media=%d, duplex=%d\n", __func__, media, duplex); arge_set_pll(sc, media, duplex); } } else { sc->arge_link_status = 0; } } static void arge_set_pll(struct arge_softc *sc, int media, int duplex) { uint32_t cfg, ifcontrol, rx_filtmask; uint32_t fifo_tx, pll; int if_speed; /* * XXX Verify - is this valid for all chips? * QCA955x (and likely some of the earlier chips!) define * this as nibble mode and byte mode, and those have to do * with the interface type (MII/SMII versus GMII/RGMII.) */ ARGEDEBUG(sc, ARGE_DBG_PLL, "set_pll(%04x, %s)\n", media, duplex == IFM_FDX ? "full" : "half"); cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); cfg &= ~(MAC_CFG2_IFACE_MODE_1000 | MAC_CFG2_IFACE_MODE_10_100 | MAC_CFG2_FULL_DUPLEX); if (duplex == IFM_FDX) cfg |= MAC_CFG2_FULL_DUPLEX; ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL); ifcontrol &= ~MAC_IFCONTROL_SPEED; rx_filtmask = ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK); rx_filtmask &= ~FIFO_RX_MASK_BYTE_MODE; switch(media) { case IFM_10_T: cfg |= MAC_CFG2_IFACE_MODE_10_100; if_speed = 10; break; case IFM_100_TX: cfg |= MAC_CFG2_IFACE_MODE_10_100; ifcontrol |= MAC_IFCONTROL_SPEED; if_speed = 100; break; case IFM_1000_T: case IFM_1000_SX: cfg |= MAC_CFG2_IFACE_MODE_1000; rx_filtmask |= FIFO_RX_MASK_BYTE_MODE; if_speed = 1000; break; default: if_speed = 100; device_printf(sc->arge_dev, "Unknown media %d\n", media); } ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: if_speed=%d\n", __func__, if_speed); switch (ar71xx_soc) { case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: + case AR71XX_SOC_QCA9533: + case AR71XX_SOC_QCA9533_V2: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: fifo_tx = 0x01f00140; break; case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: fifo_tx = 0x00780fff; break; /* AR71xx */ default: fifo_tx = 0x008001ff; } ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg); ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol); ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, rx_filtmask); ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, fifo_tx); /* fetch PLL registers */ pll = ar71xx_device_get_eth_pll(sc->arge_mac_unit, if_speed); ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: pll=0x%x\n", __func__, pll); /* Override if required by platform data */ if (if_speed == 10 && sc->arge_pllcfg.pll_10 != 0) pll = sc->arge_pllcfg.pll_10; else if (if_speed == 100 && sc->arge_pllcfg.pll_100 != 0) pll = sc->arge_pllcfg.pll_100; else if (if_speed == 1000 && sc->arge_pllcfg.pll_1000 != 0) pll = sc->arge_pllcfg.pll_1000; ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: final pll=0x%x\n", __func__, pll); /* XXX ensure pll != 0 */ ar71xx_device_set_pll_ge(sc->arge_mac_unit, if_speed, pll); /* set MII registers */ /* * This was introduced to match what the Linux ag71xx ethernet * driver does. For the AR71xx case, it does set the port * MII speed. However, if this is done, non-gigabit speeds * are not at all reliable when speaking via RGMII through * 'bridge' PHY port that's pretending to be a local PHY. * * Until that gets root caused, and until an AR71xx + normal * PHY board is tested, leave this disabled. */ #if 0 ar71xx_device_set_mii_speed(sc->arge_mac_unit, if_speed); #endif } static void arge_reset_dma(struct arge_softc *sc) { ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: called\n", __func__); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0); /* Clear all possible RX interrupts */ while(ARGE_READ(sc, AR71XX_DMA_RX_STATUS) & DMA_RX_STATUS_PKT_RECVD) ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); /* * Clear all possible TX interrupts */ while(ARGE_READ(sc, AR71XX_DMA_TX_STATUS) & DMA_TX_STATUS_PKT_SENT) ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); /* * Now Rx/Tx errors */ ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW); ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN); /* * Force a DDR flush so any pending data is properly * flushed to RAM before underlying buffers are freed. */ arge_flush_ddr(sc); } static void arge_init(void *xsc) { struct arge_softc *sc = xsc; ARGE_LOCK(sc); arge_init_locked(sc); ARGE_UNLOCK(sc); } static void arge_init_locked(struct arge_softc *sc) { struct ifnet *ifp = sc->arge_ifp; struct mii_data *mii; ARGE_LOCK_ASSERT(sc); if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) return; /* Init circular RX list. */ if (arge_rx_ring_init(sc) != 0) { device_printf(sc->arge_dev, "initialization failed: no memory for rx buffers\n"); arge_stop(sc); return; } /* Init tx descriptors. */ arge_tx_ring_init(sc); arge_reset_dma(sc); if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); mii_mediachg(mii); } else { /* * Sun always shines over multiPHY interface */ sc->arge_link_status = 1; } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (sc->arge_miibus) { callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); arge_update_link_locked(sc); } ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); /* Start listening */ ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); /* Enable interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); } /* * Return whether the mbuf chain is correctly aligned * for the arge TX engine. * * All the MACs have a length requirement: any non-final * fragment (ie, descriptor with MORE bit set) needs to have * a length divisible by 4. * * The AR71xx, AR913x require the start address also be * DWORD aligned. The later MACs don't. */ static int arge_mbuf_chain_is_tx_aligned(struct arge_softc *sc, struct mbuf *m0) { struct mbuf *m; for (m = m0; m != NULL; m = m->m_next) { /* * Only do this for chips that require it. */ if ((sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE) && (mtod(m, intptr_t) & 3) != 0) { sc->stats.tx_pkts_unaligned_start++; return 0; } /* * All chips have this requirement for length. */ if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0)) { sc->stats.tx_pkts_unaligned_len++; return 0; } } return 1; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int arge_encap(struct arge_softc *sc, struct mbuf **m_head) { struct arge_txdesc *txd; struct arge_desc *desc, *prev_desc; bus_dma_segment_t txsegs[ARGE_MAXFRAGS]; int error, i, nsegs, prod, prev_prod; struct mbuf *m; ARGE_LOCK_ASSERT(sc); /* * Fix mbuf chain based on hardware alignment constraints. */ m = *m_head; if (! arge_mbuf_chain_is_tx_aligned(sc, m)) { sc->stats.tx_pkts_unaligned++; m = m_defrag(*m_head, M_NOWAIT); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m; } else sc->stats.tx_pkts_aligned++; prod = sc->arge_cdata.arge_tx_prod; txd = &sc->arge_cdata.arge_txdesc[prod]; error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { panic("EFBIG"); } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* Check number of available descriptors. */ if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 2)) { bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); sc->stats.tx_pkts_nosegs++; return (ENOBUFS); } txd->tx_m = *m_head; bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); /* * Make a list of descriptors for this packet. DMA controller will * walk through it while arge_link is not zero. * * Since we're in a endless circular buffer, ensure that * the first descriptor in a multi-descriptor ring is always * set to EMPTY, then un-do it when we're done populating. */ prev_prod = prod; desc = prev_desc = NULL; for (i = 0; i < nsegs; i++) { uint32_t tmp; desc = &sc->arge_rdata.arge_tx_ring[prod]; /* * Set DESC_EMPTY so the hardware (hopefully) stops at this * point. We don't want it to start transmitting descriptors * before we've finished fleshing this out. */ tmp = ARGE_DMASIZE(txsegs[i].ds_len); if (i == 0) tmp |= ARGE_DESC_EMPTY; desc->packet_ctrl = tmp; /* XXX Note: only relevant for older MACs; but check length! */ if ((sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE) && (txsegs[i].ds_addr & 3)) panic("TX packet address unaligned\n"); desc->packet_addr = txsegs[i].ds_addr; /* link with previous descriptor */ if (prev_desc) prev_desc->packet_ctrl |= ARGE_DESC_MORE; sc->arge_cdata.arge_tx_cnt++; prev_desc = desc; ARGE_INC(prod, ARGE_TX_RING_COUNT); } /* Update producer index. */ sc->arge_cdata.arge_tx_prod = prod; /* * The descriptors are updated, so enable the first one. */ desc = &sc->arge_rdata.arge_tx_ring[prev_prod]; desc->packet_ctrl &= ~ ARGE_DESC_EMPTY; /* Sync descriptors. */ bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Flush writes */ ARGE_BARRIER_WRITE(sc); /* Start transmitting */ ARGEDEBUG(sc, ARGE_DBG_TX, "%s: setting DMA_TX_CONTROL_EN\n", __func__); ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); return (0); } static void arge_start(struct ifnet *ifp) { struct arge_softc *sc; sc = ifp->if_softc; ARGE_LOCK(sc); arge_start_locked(ifp); ARGE_UNLOCK(sc); } static void arge_start_locked(struct ifnet *ifp) { struct arge_softc *sc; struct mbuf *m_head; int enq = 0; sc = ifp->if_softc; ARGE_LOCK_ASSERT(sc); ARGEDEBUG(sc, ARGE_DBG_TX, "%s: beginning\n", __func__); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || sc->arge_link_status == 0 ) return; /* * Before we go any further, check whether we're already full. * The below check errors out immediately if the ring is full * and never gets a chance to set this flag. Although it's * likely never needed, this at least avoids an unexpected * situation. */ if (sc->arge_cdata.arge_tx_cnt >= ARGE_TX_RING_COUNT - 2) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; ARGEDEBUG(sc, ARGE_DBG_ERR, "%s: tx_cnt %d >= max %d; setting IFF_DRV_OACTIVE\n", __func__, sc->arge_cdata.arge_tx_cnt, ARGE_TX_RING_COUNT - 2); return; } arge_flush_ddr(sc); for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. */ if (arge_encap(sc, &m_head)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); } ARGEDEBUG(sc, ARGE_DBG_TX, "%s: finished; queued %d packets\n", __func__, enq); } static void arge_stop(struct arge_softc *sc) { struct ifnet *ifp; ARGE_LOCK_ASSERT(sc); ifp = sc->arge_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); if (sc->arge_miibus) callout_stop(&sc->arge_stat_callout); /* mask out interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); arge_reset_dma(sc); /* Flush FIFO and free any existing mbufs */ arge_flush_ddr(sc); arge_rx_ring_free(sc); arge_tx_ring_free(sc); } static int arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct arge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error; #ifdef DEVICE_POLLING int mask; #endif switch (command) { case SIOCSIFFLAGS: ARGE_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->arge_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { /* XXX: handle promisc & multi flags */ } } else { if (!sc->arge_detach) arge_init_locked(sc); } } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; arge_stop(sc); } sc->arge_if_flags = ifp->if_flags; ARGE_UNLOCK(sc); error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: /* XXX: implement SIOCDELMULTI */ error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); } else error = ifmedia_ioctl(ifp, ifr, &sc->arge_ifmedia, command); break; case SIOCSIFCAP: /* XXX: Check other capabilities */ #ifdef DEVICE_POLLING mask = ifp->if_capenable ^ ifr->ifr_reqcap; if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); error = ether_poll_register(arge_poll, ifp); if (error) return error; ARGE_LOCK(sc); ifp->if_capenable |= IFCAP_POLLING; ARGE_UNLOCK(sc); } else { ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); error = ether_poll_deregister(ifp); ARGE_LOCK(sc); ifp->if_capenable &= ~IFCAP_POLLING; ARGE_UNLOCK(sc); } } error = 0; break; #endif default: error = ether_ioctl(ifp, command, data); break; } return (error); } /* * Set media options. */ static int arge_ifmedia_upd(struct ifnet *ifp) { struct arge_softc *sc; struct mii_data *mii; struct mii_softc *miisc; int error; sc = ifp->if_softc; ARGE_LOCK(sc); mii = device_get_softc(sc->arge_miibus); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); ARGE_UNLOCK(sc); return (error); } /* * Report current media status. */ static void arge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct arge_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->arge_miibus); ARGE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; ARGE_UNLOCK(sc); } struct arge_dmamap_arg { bus_addr_t arge_busaddr; }; static void arge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct arge_dmamap_arg *ctx; if (error != 0) return; ctx = arg; ctx->arge_busaddr = segs[0].ds_addr; } static int arge_dma_alloc(struct arge_softc *sc) { struct arge_dmamap_arg ctx; struct arge_txdesc *txd; struct arge_rxdesc *rxd; int error, i; int arge_tx_align, arge_rx_align; /* Assume 4 byte alignment by default */ arge_tx_align = 4; arge_rx_align = 4; if (sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_1BYTE) arge_tx_align = 1; if (sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE) arge_rx_align = 1; /* Create parent DMA tag. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->arge_dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_parent_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create parent DMA tag\n"); goto fail; } /* Create tag for Tx ring. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ ARGE_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ARGE_TX_DMA_SIZE, /* maxsize */ 1, /* nsegments */ ARGE_TX_DMA_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_tx_ring_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n"); goto fail; } /* Create tag for Rx ring. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ ARGE_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ARGE_RX_DMA_SIZE, /* maxsize */ 1, /* nsegments */ ARGE_RX_DMA_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_rx_ring_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n"); goto fail; } /* Create tag for Tx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ arge_tx_align, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES * ARGE_MAXFRAGS, /* maxsize */ ARGE_MAXFRAGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_tx_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx DMA tag\n"); goto fail; } /* Create tag for Rx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ arge_rx_align, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, /* maxsize */ ARGE_MAXFRAGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_rx_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx DMA tag\n"); goto fail; } /* Allocate DMA'able memory and load the DMA map for Tx ring. */ error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag, (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map); if (error != 0) { device_printf(sc->arge_dev, "failed to allocate DMA'able memory for Tx ring\n"); goto fail; } ctx.arge_busaddr = 0; error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring, ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); if (error != 0 || ctx.arge_busaddr == 0) { device_printf(sc->arge_dev, "failed to load DMA'able memory for Tx ring\n"); goto fail; } sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr; /* Allocate DMA'able memory and load the DMA map for Rx ring. */ error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag, (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map); if (error != 0) { device_printf(sc->arge_dev, "failed to allocate DMA'able memory for Rx ring\n"); goto fail; } ctx.arge_busaddr = 0; error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring, ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); if (error != 0 || ctx.arge_busaddr == 0) { device_printf(sc->arge_dev, "failed to load DMA'able memory for Rx ring\n"); goto fail; } sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr; /* Create DMA maps for Tx buffers. */ for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; txd->tx_m = NULL; txd->tx_dmamap = NULL; error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0, &txd->tx_dmamap); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx dmamap\n"); goto fail; } } /* Create DMA maps for Rx buffers. */ if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, &sc->arge_cdata.arge_rx_sparemap)) != 0) { device_printf(sc->arge_dev, "failed to create spare Rx dmamap\n"); goto fail; } for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_dmamap = NULL; error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, &rxd->rx_dmamap); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx dmamap\n"); goto fail; } } fail: return (error); } static void arge_dma_free(struct arge_softc *sc) { struct arge_txdesc *txd; struct arge_rxdesc *rxd; int i; /* Tx ring. */ if (sc->arge_cdata.arge_tx_ring_tag) { if (sc->arge_rdata.arge_tx_ring_paddr) bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map); if (sc->arge_rdata.arge_tx_ring) bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag, sc->arge_rdata.arge_tx_ring, sc->arge_cdata.arge_tx_ring_map); sc->arge_rdata.arge_tx_ring = NULL; sc->arge_rdata.arge_tx_ring_paddr = 0; bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag); sc->arge_cdata.arge_tx_ring_tag = NULL; } /* Rx ring. */ if (sc->arge_cdata.arge_rx_ring_tag) { if (sc->arge_rdata.arge_rx_ring_paddr) bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map); if (sc->arge_rdata.arge_rx_ring) bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag, sc->arge_rdata.arge_rx_ring, sc->arge_cdata.arge_rx_ring_map); sc->arge_rdata.arge_rx_ring = NULL; sc->arge_rdata.arge_rx_ring_paddr = 0; bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag); sc->arge_cdata.arge_rx_ring_tag = NULL; } /* Tx buffers. */ if (sc->arge_cdata.arge_tx_tag) { for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; if (txd->tx_dmamap) { bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); txd->tx_dmamap = NULL; } } bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag); sc->arge_cdata.arge_tx_tag = NULL; } /* Rx buffers. */ if (sc->arge_cdata.arge_rx_tag) { for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; if (rxd->rx_dmamap) { bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); rxd->rx_dmamap = NULL; } } if (sc->arge_cdata.arge_rx_sparemap) { bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, sc->arge_cdata.arge_rx_sparemap); sc->arge_cdata.arge_rx_sparemap = 0; } bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag); sc->arge_cdata.arge_rx_tag = NULL; } if (sc->arge_cdata.arge_parent_tag) { bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag); sc->arge_cdata.arge_parent_tag = NULL; } } /* * Initialize the transmit descriptors. */ static int arge_tx_ring_init(struct arge_softc *sc) { struct arge_ring_data *rd; struct arge_txdesc *txd; bus_addr_t addr; int i; sc->arge_cdata.arge_tx_prod = 0; sc->arge_cdata.arge_tx_cons = 0; sc->arge_cdata.arge_tx_cnt = 0; rd = &sc->arge_rdata; bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring)); for (i = 0; i < ARGE_TX_RING_COUNT; i++) { if (i == ARGE_TX_RING_COUNT - 1) addr = ARGE_TX_RING_ADDR(sc, 0); else addr = ARGE_TX_RING_ADDR(sc, i + 1); rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; rd->arge_tx_ring[i].next_desc = addr; txd = &sc->arge_cdata.arge_txdesc[i]; txd->tx_m = NULL; } bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } /* * Free the Tx ring, unload any pending dma transaction and free the mbuf. */ static void arge_tx_ring_free(struct arge_softc *sc) { struct arge_txdesc *txd; int i; /* Free the Tx buffers. */ for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; if (txd->tx_dmamap) { bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); } if (txd->tx_m) m_freem(txd->tx_m); txd->tx_m = NULL; } } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int arge_rx_ring_init(struct arge_softc *sc) { struct arge_ring_data *rd; struct arge_rxdesc *rxd; bus_addr_t addr; int i; sc->arge_cdata.arge_rx_cons = 0; rd = &sc->arge_rdata; bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring)); for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; if (rxd->rx_m != NULL) { device_printf(sc->arge_dev, "%s: ring[%d] rx_m wasn't free?\n", __func__, i); } rxd->rx_m = NULL; rxd->desc = &rd->arge_rx_ring[i]; if (i == ARGE_RX_RING_COUNT - 1) addr = ARGE_RX_RING_ADDR(sc, 0); else addr = ARGE_RX_RING_ADDR(sc, i + 1); rd->arge_rx_ring[i].next_desc = addr; if (arge_newbuf(sc, i) != 0) { return (ENOBUFS); } } bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); return (0); } /* * Free all the buffers in the RX ring. * * TODO: ensure that DMA is disabled and no pending DMA * is lurking in the FIFO. */ static void arge_rx_ring_free(struct arge_softc *sc) { int i; struct arge_rxdesc *rxd; ARGE_LOCK_ASSERT(sc); for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; /* Unmap the mbuf */ if (rxd->rx_m != NULL) { bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); m_free(rxd->rx_m); rxd->rx_m = NULL; } } } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int arge_newbuf(struct arge_softc *sc, int idx) { struct arge_desc *desc; struct arge_rxdesc *rxd; struct mbuf *m; bus_dma_segment_t segs[1]; bus_dmamap_t map; int nsegs; /* XXX TODO: should just allocate an explicit 2KiB buffer */ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; /* * Add extra space to "adjust" (copy) the packet back to be aligned * for purposes of IPv4/IPv6 header contents. */ if (sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE) m_adj(m, sizeof(uint64_t)); /* * If it's a 1-byte aligned buffer, then just offset it two bytes * and that will give us a hopefully correctly DWORD aligned * L3 payload - and we won't have to undo it afterwards. */ else if (sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE) m_adj(m, sizeof(uint16_t)); if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag, sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); rxd = &sc->arge_cdata.arge_rxdesc[idx]; if (rxd->rx_m != NULL) { bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); } map = rxd->rx_dmamap; rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap; sc->arge_cdata.arge_rx_sparemap = map; rxd->rx_m = m; desc = rxd->desc; if ((sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE) && segs[0].ds_addr & 3) panic("RX packet address unaligned"); desc->packet_addr = segs[0].ds_addr; desc->packet_ctrl = ARGE_DESC_EMPTY | ARGE_DMASIZE(segs[0].ds_len); bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); return (0); } /* * Move the data backwards 16 bits to (hopefully!) ensure the * IPv4/IPv6 payload is aligned. * * This is required for earlier hardware where the RX path * requires DWORD aligned buffers. */ static __inline void arge_fixup_rx(struct mbuf *m) { int i; uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - 1; for (i = 0; i < m->m_len / sizeof(uint16_t); i++) { *dst++ = *src++; } if (m->m_len % sizeof(uint16_t)) *(uint8_t *)dst = *(uint8_t *)src; m->m_data -= ETHER_ALIGN; } #ifdef DEVICE_POLLING static int arge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct arge_softc *sc = ifp->if_softc; int rx_npkts = 0; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ARGE_LOCK(sc); arge_tx_locked(sc); rx_npkts = arge_rx_locked(sc); ARGE_UNLOCK(sc); } return (rx_npkts); } #endif /* DEVICE_POLLING */ static void arge_tx_locked(struct arge_softc *sc) { struct arge_txdesc *txd; struct arge_desc *cur_tx; struct ifnet *ifp; uint32_t ctrl; int cons, prod; ARGE_LOCK_ASSERT(sc); cons = sc->arge_cdata.arge_tx_cons; prod = sc->arge_cdata.arge_tx_prod; ARGEDEBUG(sc, ARGE_DBG_TX, "%s: cons=%d, prod=%d\n", __func__, cons, prod); if (cons == prod) return; bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); ifp = sc->arge_ifp; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) { cur_tx = &sc->arge_rdata.arge_tx_ring[cons]; ctrl = cur_tx->packet_ctrl; /* Check if descriptor has "finished" flag */ if ((ctrl & ARGE_DESC_EMPTY) == 0) break; ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); sc->arge_cdata.arge_tx_cnt--; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; txd = &sc->arge_cdata.arge_txdesc[cons]; if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); /* Free only if it's first descriptor in list */ if (txd->tx_m) m_freem(txd->tx_m); txd->tx_m = NULL; /* reset descriptor */ cur_tx->packet_addr = 0; } sc->arge_cdata.arge_tx_cons = cons; bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE); } static int arge_rx_locked(struct arge_softc *sc) { struct arge_rxdesc *rxd; struct ifnet *ifp = sc->arge_ifp; int cons, prog, packet_len, i; struct arge_desc *cur_rx; struct mbuf *m; int rx_npkts = 0; ARGE_LOCK_ASSERT(sc); cons = sc->arge_cdata.arge_rx_cons; bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (prog = 0; prog < ARGE_RX_RING_COUNT; ARGE_INC(cons, ARGE_RX_RING_COUNT)) { cur_rx = &sc->arge_rdata.arge_rx_ring[cons]; rxd = &sc->arge_cdata.arge_rxdesc[cons]; m = rxd->rx_m; if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0) break; ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); prog++; packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl); bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); m = rxd->rx_m; /* * If the MAC requires 4 byte alignment then the RX setup * routine will have pre-offset things; so un-offset it here. */ if (sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE) arge_fixup_rx(m); m->m_pkthdr.rcvif = ifp; /* Skip 4 bytes of CRC */ m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); rx_npkts++; ARGE_UNLOCK(sc); (*ifp->if_input)(ifp, m); ARGE_LOCK(sc); cur_rx->packet_addr = 0; } if (prog > 0) { i = sc->arge_cdata.arge_rx_cons; for (; prog > 0 ; prog--) { if (arge_newbuf(sc, i) != 0) { device_printf(sc->arge_dev, "Failed to allocate buffer\n"); break; } ARGE_INC(i, ARGE_RX_RING_COUNT); } bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); sc->arge_cdata.arge_rx_cons = cons; } return (rx_npkts); } static int arge_intr_filter(void *arg) { struct arge_softc *sc = arg; uint32_t status, ints; status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); ints = ARGE_READ(sc, AR71XX_DMA_INTR); ARGEDEBUG(sc, ARGE_DBG_INTR, "int mask(filter) = %b\n", ints, "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); ARGEDEBUG(sc, ARGE_DBG_INTR, "status(filter) = %b\n", status, "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); if (status & DMA_INTR_ALL) { sc->arge_intr_status |= status; ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); sc->stats.intr_ok++; return (FILTER_SCHEDULE_THREAD); } sc->arge_intr_status = 0; sc->stats.intr_stray++; return (FILTER_STRAY); } static void arge_intr(void *arg) { struct arge_softc *sc = arg; uint32_t status; struct ifnet *ifp = sc->arge_ifp; #ifdef ARGE_DEBUG int i; #endif status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); status |= sc->arge_intr_status; ARGEDEBUG(sc, ARGE_DBG_INTR, "int status(intr) = %b\n", status, "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); /* * Is it our interrupt at all? */ if (status == 0) { sc->stats.intr_stray2++; return; } #ifdef ARGE_DEBUG for (i = 0; i < 32; i++) { if (status & (1U << i)) { sc->intr_stats.count[i]++; } } #endif if (status & DMA_INTR_RX_BUS_ERROR) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR); device_printf(sc->arge_dev, "RX bus error"); return; } if (status & DMA_INTR_TX_BUS_ERROR) { ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR); device_printf(sc->arge_dev, "TX bus error"); return; } ARGE_LOCK(sc); arge_flush_ddr(sc); if (status & DMA_INTR_RX_PKT_RCVD) arge_rx_locked(sc); /* * RX overrun disables the receiver. * Clear indication and re-enable rx. */ if ( status & DMA_INTR_RX_OVERFLOW) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_OVERFLOW); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); sc->stats.rx_overflow++; } if (status & DMA_INTR_TX_PKT_SENT) arge_tx_locked(sc); /* * Underrun turns off TX. Clear underrun indication. * If there's anything left in the ring, reactivate the tx. */ if (status & DMA_INTR_TX_UNDERRUN) { ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_UNDERRUN); sc->stats.tx_underflow++; ARGEDEBUG(sc, ARGE_DBG_TX, "%s: TX underrun; tx_cnt=%d\n", __func__, sc->arge_cdata.arge_tx_cnt); if (sc->arge_cdata.arge_tx_cnt > 0 ) { ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); } } /* * If we've finished TXing and there's space for more packets * to be queued for TX, do so. Otherwise we may end up in a * situation where the interface send queue was filled * whilst the hardware queue was full, then the hardware * queue was drained by the interface send queue wasn't, * and thus if_start() is never called to kick-start * the send process (and all subsequent packets are simply * discarded. * * XXX TODO: make sure that the hardware deals nicely * with the possibility of the queue being enabled above * after a TX underrun, then having the hardware queue added * to below. */ if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN) && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { if (!IFQ_IS_EMPTY(&ifp->if_snd)) arge_start_locked(ifp); } /* * We handled all bits, clear status */ sc->arge_intr_status = 0; ARGE_UNLOCK(sc); /* * re-enable all interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); } static void arge_tick(void *xsc) { struct arge_softc *sc = xsc; struct mii_data *mii; ARGE_LOCK_ASSERT(sc); if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); mii_tick(mii); callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); } } int arge_multiphy_mediachange(struct ifnet *ifp) { struct arge_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->arge_ifmedia; struct ifmedia_entry *ife = ifm->ifm_cur; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { device_printf(sc->arge_dev, "AUTO is not supported for multiphy MAC"); return (EINVAL); } /* * Ignore everything */ return (0); } void arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct arge_softc *sc = ifp->if_softc; ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; ifmr->ifm_active = IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode; } #if defined(ARGE_MDIO) static int argemdio_probe(device_t dev) { device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller"); return (0); } static int argemdio_attach(device_t dev) { struct arge_softc *sc; int error = 0; sc = device_get_softc(dev); sc->arge_dev = dev; sc->arge_mac_unit = device_get_unit(dev); sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } /* Reset MAC - required for AR71xx MDIO to successfully occur */ arge_reset_mac(sc); /* Reset MII bus */ arge_reset_miibus(sc); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); error = bus_generic_attach(dev); fail: return (error); } static int argemdio_detach(device_t dev) { return (0); } #endif Index: user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.c =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.c (nonexistent) +++ user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.c (revision 290916) @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 2015 Adrian Chadd + * 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 "opt_ddb.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 + +static void +qca953x_chip_detect_mem_size(void) +{ +} + +static void +qca953x_chip_detect_sys_frequency(void) +{ + unsigned long ref_rate; + unsigned long cpu_rate; + unsigned long ddr_rate; + unsigned long ahb_rate; + uint32_t pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv; + uint32_t cpu_pll, ddr_pll; + uint32_t bootstrap; + + bootstrap = ATH_READ_REG(QCA953X_RESET_REG_BOOTSTRAP); + if (bootstrap & QCA953X_BOOTSTRAP_REF_CLK_40) + ref_rate = 40 * 1000 * 1000; + else + ref_rate = 25 * 1000 * 1000; + + pll = ATH_READ_REG(QCA953X_PLL_CPU_CONFIG_REG); + out_div = (pll >> QCA953X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + QCA953X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA953X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + QCA953X_PLL_CPU_CONFIG_REFDIV_MASK; + nint = (pll >> QCA953X_PLL_CPU_CONFIG_NINT_SHIFT) & + QCA953X_PLL_CPU_CONFIG_NINT_MASK; + frac = (pll >> QCA953X_PLL_CPU_CONFIG_NFRAC_SHIFT) & + QCA953X_PLL_CPU_CONFIG_NFRAC_MASK; + + cpu_pll = nint * ref_rate / ref_div; + cpu_pll += frac * (ref_rate >> 6) / ref_div; + cpu_pll /= (1 << out_div); + + pll = ATH_READ_REG(QCA953X_PLL_DDR_CONFIG_REG); + out_div = (pll >> QCA953X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + QCA953X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA953X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + QCA953X_PLL_DDR_CONFIG_REFDIV_MASK; + nint = (pll >> QCA953X_PLL_DDR_CONFIG_NINT_SHIFT) & + QCA953X_PLL_DDR_CONFIG_NINT_MASK; + frac = (pll >> QCA953X_PLL_DDR_CONFIG_NFRAC_SHIFT) & + QCA953X_PLL_DDR_CONFIG_NFRAC_MASK; + + ddr_pll = nint * ref_rate / ref_div; + ddr_pll += frac * (ref_rate >> 6) / (ref_div << 4); + ddr_pll /= (1 << out_div); + + clk_ctrl = ATH_READ_REG(QCA953X_PLL_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_CPU_PLL_BYPASS) + cpu_rate = ref_rate; + else if (clk_ctrl & QCA953X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL) + cpu_rate = cpu_pll / (postdiv + 1); + else + cpu_rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_DDR_PLL_BYPASS) + ddr_rate = ref_rate; + else if (clk_ctrl & QCA953X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL) + ddr_rate = ddr_pll / (postdiv + 1); + else + ddr_rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & + QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & QCA953X_PLL_CLK_CTRL_AHB_PLL_BYPASS) + ahb_rate = ref_rate; + else if (clk_ctrl & QCA953X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL) + ahb_rate = ddr_pll / (postdiv + 1); + else + ahb_rate = cpu_pll / (postdiv + 1); + + u_ar71xx_ddr_freq = ddr_rate; + u_ar71xx_cpu_freq = cpu_rate; + u_ar71xx_ahb_freq = ahb_rate; + + u_ar71xx_wdt_freq = ref_rate; + u_ar71xx_uart_freq = ref_rate; + u_ar71xx_mdio_freq = ref_rate; + u_ar71xx_refclk = ref_rate; +} + +static void +qca953x_chip_device_stop(uint32_t mask) +{ + uint32_t reg; + + reg = ATH_READ_REG(QCA953X_RESET_REG_RESET_MODULE); + ATH_WRITE_REG(QCA953X_RESET_REG_RESET_MODULE, reg | mask); +} + +static void +qca953x_chip_device_start(uint32_t mask) +{ + uint32_t reg; + + reg = ATH_READ_REG(QCA953X_RESET_REG_RESET_MODULE); + ATH_WRITE_REG(QCA953X_RESET_REG_RESET_MODULE, reg & ~mask); +} + +static int +qca953x_chip_device_stopped(uint32_t mask) +{ + uint32_t reg; + + reg = ATH_READ_REG(QCA953X_RESET_REG_RESET_MODULE); + return ((reg & mask) == mask); +} + +static void +qca953x_chip_set_mii_speed(uint32_t unit, uint32_t speed) +{ + + /* XXX TODO */ + return; +} + +static void +qca953x_chip_set_pll_ge(int unit, int speed, uint32_t pll) +{ + switch (unit) { + case 0: + ATH_WRITE_REG(QCA953X_PLL_ETH_XMII_CONTROL_REG, pll); + break; + case 1: + ATH_WRITE_REG(QCA953X_PLL_ETH_SGMII_CONTROL_REG, pll); + break; + default: + printf("%s: invalid PLL set for arge unit: %d\n", + __func__, unit); + return; + } +} + +static void +qca953x_chip_ddr_flush(ar71xx_flush_ddr_id_t id) +{ + + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: + ar71xx_ddr_flush(QCA953X_DDR_REG_FLUSH_GE0); + break; + case AR71XX_CPU_DDR_FLUSH_GE1: + ar71xx_ddr_flush(QCA953X_DDR_REG_FLUSH_GE1); + break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(QCA953X_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE: + ar71xx_ddr_flush(QCA953X_DDR_REG_FLUSH_PCIE); + break; + case AR71XX_CPU_DDR_FLUSH_WMAC: + ar71xx_ddr_flush(QCA953X_DDR_REG_FLUSH_WMAC); + break; + default: + printf("%s: invalid flush (%d)\n", __func__, id); + } +} + +static uint32_t +qca953x_chip_get_eth_pll(unsigned int mac, int speed) +{ + uint32_t pll; + + switch (speed) { + case 10: + pll = QCA953X_PLL_VAL_10; + break; + case 100: + pll = QCA953X_PLL_VAL_100; + break; + case 1000: + pll = QCA953X_PLL_VAL_1000; + break; + default: + printf("%s%d: invalid speed %d\n", __func__, mac, speed); + pll = 0; + } + return (pll); +} + +static void +qca953x_chip_reset_ethernet_switch(void) +{ +} + +static void +qca953x_configure_gmac(uint32_t gmac_cfg) +{ + uint32_t reg; + + reg = ATH_READ_REG(QCA953X_GMAC_REG_ETH_CFG); + printf("%s: ETH_CFG=0x%08x\n", __func__, reg); + reg &= ~(QCA953X_ETH_CFG_SW_ONLY_MODE | + QCA953X_ETH_CFG_SW_PHY_SWAP | + QCA953X_ETH_CFG_SW_APB_ACCESS | + QCA953X_ETH_CFG_SW_ACC_MSB_FIRST); + + reg |= gmac_cfg; + ATH_WRITE_REG(QCA953X_GMAC_REG_ETH_CFG, reg); +} + +static void +qca953x_chip_init_usb_peripheral(void) +{ + uint32_t bootstrap; + + bootstrap = ATH_READ_REG(QCA953X_RESET_REG_BOOTSTRAP); + + ar71xx_device_stop(QCA953X_RESET_USBSUS_OVERRIDE); + DELAY(1000); + + ar71xx_device_start(QCA953X_RESET_USB_PHY); + DELAY(1000); + + ar71xx_device_start(QCA953X_RESET_USB_PHY_ANALOG); + DELAY(1000); + + ar71xx_device_start(QCA953X_RESET_USB_HOST); + DELAY(1000); +} + +static void +qca953x_chip_set_mii_if(uint32_t unit, uint32_t mii_mode) +{ + + /* + * XXX ! + * + * Nothing to see here; although gmac0 can have its + * MII configuration changed, the register values + * are slightly different. + */ +} + +/* + * XXX TODO: fetch default MII divider configuration + */ + +static void +qca953x_chip_reset_wmac(void) +{ + + /* XXX TODO */ +} + +static void +qca953x_chip_init_gmac(void) +{ + long gmac_cfg; + + if (resource_long_value("qca953x_gmac", 0, "gmac_cfg", + &gmac_cfg) == 0) { + printf("%s: gmac_cfg=0x%08lx\n", + __func__, + (long) gmac_cfg); + qca953x_configure_gmac((uint32_t) gmac_cfg); + } +} + +/* + * Reset the NAND Flash Controller. + * + * + active=1 means "make it active". + * + active=0 means "make it inactive". + */ +static void +qca953x_chip_reset_nfc(int active) +{ +} + +/* + * Configure the GPIO output mux setup. + * + * The QCA953x has an output mux which allowed + * certain functions to be configured on any pin. + * Specifically, the switch PHY link LEDs and + * WMAC external RX LNA switches are not limited to + * a specific GPIO pin. + */ +static void +qca953x_chip_gpio_output_configure(int gpio, uint8_t func) +{ + uint32_t reg, s; + uint32_t t; + + if (gpio > QCA953X_GPIO_COUNT) + return; + + reg = QCA953X_GPIO_REG_OUT_FUNC0 + 4 * (gpio / 4); + s = 8 * (gpio % 4); + + /* read-modify-write */ + t = ATH_READ_REG(AR71XX_GPIO_BASE + reg); + t &= ~(0xff << s); + t |= func << s; + ATH_WRITE_REG(AR71XX_GPIO_BASE + reg, t); + + /* flush write */ + ATH_READ_REG(AR71XX_GPIO_BASE + reg); +} + +struct ar71xx_cpu_def qca953x_chip_def = { + &qca953x_chip_detect_mem_size, + &qca953x_chip_detect_sys_frequency, + &qca953x_chip_device_stop, + &qca953x_chip_device_start, + &qca953x_chip_device_stopped, + &qca953x_chip_set_pll_ge, + &qca953x_chip_set_mii_speed, + &qca953x_chip_set_mii_if, + &qca953x_chip_get_eth_pll, + &qca953x_chip_ddr_flush, + &qca953x_chip_init_usb_peripheral, + &qca953x_chip_reset_ethernet_switch, + &qca953x_chip_reset_wmac, + &qca953x_chip_init_gmac, + &qca953x_chip_reset_nfc, + &qca953x_chip_gpio_output_configure, +}; Property changes on: user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.h =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.h (nonexistent) +++ user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.h (revision 290916) @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2015 Adrian Chadd + * 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. + */ + +/* $FreeBSD$ */ + +#ifndef __QCA953X_CHIP_H__ +#define __QCA953X_CHIP_H__ + +extern struct ar71xx_cpu_def qca953x_chip_def; + +#endif Property changes on: user/ngie/more-tests2/sys/mips/atheros/qca953x_chip.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/ngie/more-tests2/sys/mips/atheros/qca953xreg.h =================================================================== --- user/ngie/more-tests2/sys/mips/atheros/qca953xreg.h (nonexistent) +++ user/ngie/more-tests2/sys/mips/atheros/qca953xreg.h (revision 290916) @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2015 Adrian Chadd + * 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. + * + * $FreeBSD$ + */ +#ifndef __QCA953XREG_H__ +#define __QCA953XREG_H__ + +#define BIT(x) (1 << (x)) + +/* Revision ID information */ +#define REV_ID_MAJOR_QCA9533 0x0140 +#define REV_ID_MAJOR_QCA9533_V2 0x0160 +#define QCA953X_REV_ID_REVISION_MASK 0xf + +/* Big enough to cover APB and SPI, and most peripherals */ +/* + * it needs to cover SPI because right now the if_ath_ahb + * code uses rman to map in the SPI address into memory + * to read data instead of us squirreling it away at early + * boot-time and using the firmware interface. + * + * if_ath_ahb.c should use the same firmware interface + * that if_ath_pci.c uses. + */ +#define QCA953X_GMAC_BASE (AR71XX_APB_BASE + 0x00070000) +#define QCA953X_GMAC_SIZE 0x14 +#define QCA953X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) +#define QCA953X_WMAC_SIZE 0x20000 +#define QCA953X_EHCI_BASE 0x1b000000 +#define QCA953X_EHCI_SIZE 0x200 +#define QCA953X_SRIF_BASE (AR71XX_APB_BASE + 0x00116000) +#define QCA953X_SRIF_SIZE 0x1000 + +#define QCA953X_PCI_CFG_BASE0 0x14000000 +#define QCA953X_PCI_CTRL_BASE0 (AR71XX_APB_BASE + 0x000f0000) +#define QCA953X_PCI_CRP_BASE0 (AR71XX_APB_BASE + 0x000c0000) +#define QCA953X_PCI_MEM_BASE0 0x10000000 +#define QCA953X_PCI_MEM_SIZE 0x02000000 + +/* PLL Block */ +#define QCA953X_PLL_CPU_CONFIG_REG (AR71XX_PLL_CPU_BASE + 0x00) +#define QCA953X_PLL_DDR_CONFIG_REG (AR71XX_PLL_CPU_BASE + 0x04) +#define QCA953X_PLL_CLK_CTRL_REG (AR71XX_PLL_CPU_BASE + 0x08) + +#define QCA953X_PLL_ETH_XMII_CONTROL_REG (AR71XX_PLL_CPU_BASE + 0x2c) +#define QCA953X_PLL_ETH_SGMII_CONTROL_REG (AR71XX_PLL_CPU_BASE + 0x48) + +#define QCA953X_PLL_CPU_CONFIG_NFRAC_SHIFT 0 +#define QCA953X_PLL_CPU_CONFIG_NFRAC_MASK 0x3f +#define QCA953X_PLL_CPU_CONFIG_NINT_SHIFT 6 +#define QCA953X_PLL_CPU_CONFIG_NINT_MASK 0x3f +#define QCA953X_PLL_CPU_CONFIG_REFDIV_SHIFT 12 +#define QCA953X_PLL_CPU_CONFIG_REFDIV_MASK 0x1f +#define QCA953X_PLL_CPU_CONFIG_OUTDIV_SHIFT 19 +#define QCA953X_PLL_CPU_CONFIG_OUTDIV_MASK 0x3 + +#define QCA953X_PLL_DDR_CONFIG_NFRAC_SHIFT 0 +#define QCA953X_PLL_DDR_CONFIG_NFRAC_MASK 0x3ff +#define QCA953X_PLL_DDR_CONFIG_NINT_SHIFT 10 +#define QCA953X_PLL_DDR_CONFIG_NINT_MASK 0x3f +#define QCA953X_PLL_DDR_CONFIG_REFDIV_SHIFT 16 +#define QCA953X_PLL_DDR_CONFIG_REFDIV_MASK 0x1f +#define QCA953X_PLL_DDR_CONFIG_OUTDIV_SHIFT 23 +#define QCA953X_PLL_DDR_CONFIG_OUTDIV_MASK 0x7 + +#define QCA953X_PLL_CLK_CTRL_CPU_PLL_BYPASS BIT(2) +#define QCA953X_PLL_CLK_CTRL_DDR_PLL_BYPASS BIT(3) +#define QCA953X_PLL_CLK_CTRL_AHB_PLL_BYPASS BIT(4) +#define QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT 5 +#define QCA953X_PLL_CLK_CTRL_CPU_POST_DIV_MASK 0x1f +#define QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT 10 +#define QCA953X_PLL_CLK_CTRL_DDR_POST_DIV_MASK 0x1f +#define QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT 15 +#define QCA953X_PLL_CLK_CTRL_AHB_POST_DIV_MASK 0x1f +#define QCA953X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL BIT(20) +#define QCA953X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL BIT(21) +#define QCA953X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24) + +#define QCA953X_PLL_VAL_1000 0x16000000 +#define QCA953X_PLL_VAL_100 0x00000101 +#define QCA953X_PLL_VAL_10 0x00001616 + +/* Reset block */ + +#define QCA953X_RESET_REG_RESET_MODULE (AR71XX_RST_BLOCK_BASE + 0x1c) +#define QCA953X_RESET_USB_EXT_PWR BIT(29) +#define QCA953X_RESET_EXTERNAL BIT(28) +#define QCA953X_RESET_RTC BIT(27) +#define QCA953X_RESET_FULL_CHIP BIT(24) +#define QCA953X_RESET_GE1_MDIO BIT(23) +#define QCA953X_RESET_GE0_MDIO BIT(22) +#define QCA953X_RESET_CPU_NMI BIT(21) +#define QCA953X_RESET_CPU_COLD BIT(20) +#define QCA953X_RESET_DDR BIT(16) +#define QCA953X_RESET_USB_PHY_PLL_PWD_EXT BIT(15) +#define QCA953X_RESET_GE1_MAC BIT(13) +#define QCA953X_RESET_ETH_SWITCH_ANALOG BIT(12) +#define QCA953X_RESET_USB_PHY_ANALOG BIT(11) +#define QCA953X_RESET_GE0_MAC BIT(9) +#define QCA953X_RESET_ETH_SWITCH BIT(8) +#define QCA953X_RESET_PCIE_PHY BIT(7) +#define QCA953X_RESET_PCIE BIT(6) +#define QCA953X_RESET_USB_HOST BIT(5) +#define QCA953X_RESET_USB_PHY BIT(4) +#define QCA953X_RESET_USBSUS_OVERRIDE BIT(3) + +#define QCA953X_RESET_REG_BOOTSTRAP (AR71XX_RST_BLOCK_BASE + 0xb0) +#define QCA953X_BOOTSTRAP_SW_OPTION2 BIT(12) +#define QCA953X_BOOTSTRAP_SW_OPTION1 BIT(11) +#define QCA953X_BOOTSTRAP_EJTAG_MODE BIT(5) +#define QCA953X_BOOTSTRAP_REF_CLK_40 BIT(4) +#define QCA953X_BOOTSTRAP_SDRAM_DISABLED BIT(1) +#define QCA953X_BOOTSTRAP_DDR1 BIT(0) + +#define QCA953X_RESET_REG_EXT_INT_STATUS (AR71XX_RST_BLOCK_BASE + 0xac) + +#define QCA953X_DDR_REG_FLUSH_GE0 (AR71XX_APB_BASE + 0x9c) +#define QCA953X_DDR_REG_FLUSH_GE1 (AR71XX_APB_BASE + 0xa0) +#define QCA953X_DDR_REG_FLUSH_USB (AR71XX_APB_BASE + 0xa4) +#define QCA953X_DDR_REG_FLUSH_PCIE (AR71XX_APB_BASE + 0xa8) +#define QCA953X_DDR_REG_FLUSH_WMAC (AR71XX_APB_BASE + 0xac) + +/* GPIO block */ +#define QCA953X_GPIO_REG_OUT_FUNC0 0x2c +#define QCA953X_GPIO_REG_OUT_FUNC1 0x30 +#define QCA953X_GPIO_REG_OUT_FUNC2 0x34 +#define QCA953X_GPIO_REG_OUT_FUNC3 0x38 +#define QCA953X_GPIO_REG_OUT_FUNC4 0x3c +#define QCA953X_GPIO_REG_IN_ENABLE0 0x44 +#define QCA953X_GPIO_REG_FUNC 0x6c + +#define QCA953X_GPIO_OUT_MUX_SPI_CS1 10 +#define QCA953X_GPIO_OUT_MUX_SPI_CS2 11 +#define QCA953X_GPIO_OUT_MUX_SPI_CS0 9 +#define QCA953X_GPIO_OUT_MUX_SPI_CLK 8 +#define QCA953X_GPIO_OUT_MUX_SPI_MOSI 12 +#define QCA953X_GPIO_OUT_MUX_LED_LINK1 41 +#define QCA953X_GPIO_OUT_MUX_LED_LINK2 42 +#define QCA953X_GPIO_OUT_MUX_LED_LINK3 43 +#define QCA953X_GPIO_OUT_MUX_LED_LINK4 44 +#define QCA953X_GPIO_OUT_MUX_LED_LINK5 45 + +#define QCA953X_GPIO_COUNT 18 + +/* GMAC block */ +#define QCA953X_GMAC_REG_ETH_CFG (QCA953X_GMAC_BASE + 0x00) + +#define QCA953X_ETH_CFG_SW_ONLY_MODE BIT(6) +#define QCA953X_ETH_CFG_SW_PHY_SWAP BIT(7) +#define QCA953X_ETH_CFG_SW_APB_ACCESS BIT(9) +#define QCA953X_ETH_CFG_SW_ACC_MSB_FIRST BIT(13) + +/* SRIF block */ +#define QCA953X_SRIF_CPU_DPLL1_REG 0x1c0 +#define QCA953X_SRIF_CPU_DPLL2_REG 0x1c4 +#define QCA953X_SRIF_CPU_DPLL3_REG 0x1c8 + +#define QCA953X_SRIF_DDR_DPLL1_REG 0x240 +#define QCA953X_SRIF_DDR_DPLL2_REG 0x244 +#define QCA953X_SRIF_DDR_DPLL3_REG 0x248 + +#define QCA953X_SRIF_DPLL1_REFDIV_SHIFT 27 +#define QCA953X_SRIF_DPLL1_REFDIV_MASK 0x1f +#define QCA953X_SRIF_DPLL1_NINT_SHIFT 18 +#define QCA953X_SRIF_DPLL1_NINT_MASK 0x1ff +#define QCA953X_SRIF_DPLL1_NFRAC_MASK 0x0003ffff + +#define QCA953X_SRIF_DPLL2_LOCAL_PLL BIT(30) +#define QCA953X_SRIF_DPLL2_OUTDIV_SHIFT 13 +#define QCA953X_SRIF_DPLL2_OUTDIV_MASK 0x7 + +#endif /* __QCA953XREG_H__ */ Property changes on: user/ngie/more-tests2/sys/mips/atheros/qca953xreg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/ngie/more-tests2/sys/vm/vm_pageout.c =================================================================== --- user/ngie/more-tests2/sys/vm/vm_pageout.c (revision 290915) +++ user/ngie/more-tests2/sys/vm/vm_pageout.c (revision 290916) @@ -1,1938 +1,1939 @@ /*- * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 John S. Dyson * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * Copyright (c) 2005 Yahoo! Technologies Norway AS * All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * * from: @(#)vm_pageout.c 7.4 (Berkeley) 5/7/91 * * * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * * Authors: Avadis Tevanian, Jr., Michael Wayne Young * * 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 "AS IS" * 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. */ /* * The proverbial page-out daemon. */ #include __FBSDID("$FreeBSD$"); #include "opt_vm.h" #include "opt_kdtrace.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 /* * System initialization */ /* the kernel process "vm_pageout"*/ static void vm_pageout(void); static void vm_pageout_init(void); static int vm_pageout_clean(vm_page_t m); static int vm_pageout_cluster(vm_page_t m); static void vm_pageout_scan(struct vm_domain *vmd, int pass); static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass); SYSINIT(pagedaemon_init, SI_SUB_KTHREAD_PAGE, SI_ORDER_FIRST, vm_pageout_init, NULL); struct proc *pageproc; static struct kproc_desc page_kp = { "pagedaemon", vm_pageout, &pageproc }; SYSINIT(pagedaemon, SI_SUB_KTHREAD_PAGE, SI_ORDER_SECOND, kproc_start, &page_kp); SDT_PROVIDER_DEFINE(vm); SDT_PROBE_DEFINE(vm, , , vm__lowmem_cache); SDT_PROBE_DEFINE(vm, , , vm__lowmem_scan); #if !defined(NO_SWAPPING) /* the kernel process "vm_daemon"*/ static void vm_daemon(void); static struct proc *vmproc; static struct kproc_desc vm_kp = { "vmdaemon", vm_daemon, &vmproc }; SYSINIT(vmdaemon, SI_SUB_KTHREAD_VM, SI_ORDER_FIRST, kproc_start, &vm_kp); #endif int vm_pages_needed; /* Event on which pageout daemon sleeps */ int vm_pageout_deficit; /* Estimated number of pages deficit */ int vm_pageout_wakeup_thresh; #if !defined(NO_SWAPPING) static int vm_pageout_req_swapout; /* XXX */ static int vm_daemon_needed; static struct mtx vm_daemon_mtx; /* Allow for use by vm_pageout before vm_daemon is initialized. */ MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "vm daemon", MTX_DEF); #endif static int vm_max_launder = 32; static int vm_pageout_update_period; static int defer_swap_pageouts; static int disable_swap_pageouts; static int lowmem_period = 10; static time_t lowmem_uptime; #if defined(NO_SWAPPING) static int vm_swap_enabled = 0; static int vm_swap_idle_enabled = 0; #else static int vm_swap_enabled = 1; static int vm_swap_idle_enabled = 0; #endif static int vm_panic_on_oom = 0; SYSCTL_INT(_vm, OID_AUTO, panic_on_oom, CTLFLAG_RWTUN, &vm_panic_on_oom, 0, "panic on out of memory instead of killing the largest process"); SYSCTL_INT(_vm, OID_AUTO, pageout_wakeup_thresh, CTLFLAG_RW, &vm_pageout_wakeup_thresh, 0, "free page threshold for waking up the pageout daemon"); SYSCTL_INT(_vm, OID_AUTO, max_launder, CTLFLAG_RW, &vm_max_launder, 0, "Limit dirty flushes in pageout"); SYSCTL_INT(_vm, OID_AUTO, pageout_update_period, CTLFLAG_RW, &vm_pageout_update_period, 0, "Maximum active LRU update period"); SYSCTL_INT(_vm, OID_AUTO, lowmem_period, CTLFLAG_RW, &lowmem_period, 0, "Low memory callback period"); #if defined(NO_SWAPPING) SYSCTL_INT(_vm, VM_SWAPPING_ENABLED, swap_enabled, CTLFLAG_RD, &vm_swap_enabled, 0, "Enable entire process swapout"); SYSCTL_INT(_vm, OID_AUTO, swap_idle_enabled, CTLFLAG_RD, &vm_swap_idle_enabled, 0, "Allow swapout on idle criteria"); #else SYSCTL_INT(_vm, VM_SWAPPING_ENABLED, swap_enabled, CTLFLAG_RW, &vm_swap_enabled, 0, "Enable entire process swapout"); SYSCTL_INT(_vm, OID_AUTO, swap_idle_enabled, CTLFLAG_RW, &vm_swap_idle_enabled, 0, "Allow swapout on idle criteria"); #endif SYSCTL_INT(_vm, OID_AUTO, defer_swapspace_pageouts, CTLFLAG_RW, &defer_swap_pageouts, 0, "Give preference to dirty pages in mem"); SYSCTL_INT(_vm, OID_AUTO, disable_swapspace_pageouts, CTLFLAG_RW, &disable_swap_pageouts, 0, "Disallow swapout of dirty pages"); static int pageout_lock_miss; SYSCTL_INT(_vm, OID_AUTO, pageout_lock_miss, CTLFLAG_RD, &pageout_lock_miss, 0, "vget() lock misses during pageout"); #define VM_PAGEOUT_PAGE_COUNT 16 int vm_pageout_page_count = VM_PAGEOUT_PAGE_COUNT; int vm_page_max_wired; /* XXX max # of wired pages system-wide */ SYSCTL_INT(_vm, OID_AUTO, max_wired, CTLFLAG_RW, &vm_page_max_wired, 0, "System-wide limit to wired page count"); static boolean_t vm_pageout_fallback_object_lock(vm_page_t, vm_page_t *); static boolean_t vm_pageout_launder(struct vm_pagequeue *pq, int, vm_paddr_t, vm_paddr_t); #if !defined(NO_SWAPPING) static void vm_pageout_map_deactivate_pages(vm_map_t, long); static void vm_pageout_object_deactivate_pages(pmap_t, vm_object_t, long); static void vm_req_vmdaemon(int req); #endif static boolean_t vm_pageout_page_lock(vm_page_t, vm_page_t *); /* * Initialize a dummy page for marking the caller's place in the specified * paging queue. In principle, this function only needs to set the flag * PG_MARKER. Nonetheless, it wirte busies and initializes the hold count * to one as safety precautions. */ static void vm_pageout_init_marker(vm_page_t marker, u_short queue) { bzero(marker, sizeof(*marker)); marker->flags = PG_MARKER; marker->busy_lock = VPB_SINGLE_EXCLUSIVER; marker->queue = queue; marker->hold_count = 1; } /* * vm_pageout_fallback_object_lock: * * Lock vm object currently associated with `m'. VM_OBJECT_TRYWLOCK is * known to have failed and page queue must be either PQ_ACTIVE or * PQ_INACTIVE. To avoid lock order violation, unlock the page queues * while locking the vm object. Use marker page to detect page queue * changes and maintain notion of next page on page queue. Return * TRUE if no changes were detected, FALSE otherwise. vm object is * locked on return. * * This function depends on both the lock portion of struct vm_object * and normal struct vm_page being type stable. */ static boolean_t vm_pageout_fallback_object_lock(vm_page_t m, vm_page_t *next) { struct vm_page marker; struct vm_pagequeue *pq; boolean_t unchanged; u_short queue; vm_object_t object; queue = m->queue; vm_pageout_init_marker(&marker, queue); pq = vm_page_pagequeue(m); object = m->object; TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, plinks.q); vm_pagequeue_unlock(pq); vm_page_unlock(m); VM_OBJECT_WLOCK(object); vm_page_lock(m); vm_pagequeue_lock(pq); /* * The page's object might have changed, and/or the page might * have moved from its original position in the queue. If the * page's object has changed, then the caller should abandon * processing the page because the wrong object lock was * acquired. Use the marker's plinks.q, not the page's, to * determine if the page has been moved. The state of the * page's plinks.q can be indeterminate; whereas, the marker's * plinks.q must be valid. */ *next = TAILQ_NEXT(&marker, plinks.q); unchanged = m->object == object && m == TAILQ_PREV(&marker, pglist, plinks.q); KASSERT(!unchanged || m->queue == queue, ("page %p queue %d %d", m, queue, m->queue)); TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } /* * Lock the page while holding the page queue lock. Use marker page * to detect page queue changes and maintain notion of next page on * page queue. Return TRUE if no changes were detected, FALSE * otherwise. The page is locked on return. The page queue lock might * be dropped and reacquired. * * This function depends on normal struct vm_page being type stable. */ static boolean_t vm_pageout_page_lock(vm_page_t m, vm_page_t *next) { struct vm_page marker; struct vm_pagequeue *pq; boolean_t unchanged; u_short queue; vm_page_lock_assert(m, MA_NOTOWNED); if (vm_page_trylock(m)) return (TRUE); queue = m->queue; vm_pageout_init_marker(&marker, queue); pq = vm_page_pagequeue(m); TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, plinks.q); vm_pagequeue_unlock(pq); vm_page_lock(m); vm_pagequeue_lock(pq); /* Page queue might have changed. */ *next = TAILQ_NEXT(&marker, plinks.q); unchanged = m == TAILQ_PREV(&marker, pglist, plinks.q); KASSERT(!unchanged || m->queue == queue, ("page %p queue %d %d", m, queue, m->queue)); TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } /* * vm_pageout_clean: * * Clean the page and remove it from the laundry. * * We set the busy bit to cause potential page faults on this page to * block. Note the careful timing, however, the busy bit isn't set till * late and we cannot do anything that will mess with the page. */ static int vm_pageout_cluster(vm_page_t m) { vm_object_t object; vm_page_t mc[2*vm_pageout_page_count], pb, ps; int pageout_count; int ib, is, page_base; vm_pindex_t pindex = m->pindex; vm_page_lock_assert(m, MA_OWNED); object = m->object; VM_OBJECT_ASSERT_WLOCKED(object); /* * It doesn't cost us anything to pageout OBJT_DEFAULT or OBJT_SWAP * with the new swapper, but we could have serious problems paging * out other object types if there is insufficient memory. * * Unfortunately, checking free memory here is far too late, so the * check has been moved up a procedural level. */ /* * Can't clean the page if it's busy or held. */ vm_page_assert_unbusied(m); KASSERT(m->hold_count == 0, ("vm_pageout_clean: page %p is held", m)); vm_page_unlock(m); mc[vm_pageout_page_count] = pb = ps = m; pageout_count = 1; page_base = vm_pageout_page_count; ib = 1; is = 1; /* * Scan object for clusterable pages. * * We can cluster ONLY if: ->> the page is NOT * clean, wired, busy, held, or mapped into a * buffer, and one of the following: * 1) The page is inactive, or a seldom used * active page. * -or- * 2) we force the issue. * * During heavy mmap/modification loads the pageout * daemon can really fragment the underlying file * due to flushing pages out of order and not trying * align the clusters (which leave sporatic out-of-order * holes). To solve this problem we do the reverse scan * first and attempt to align our cluster, then do a * forward scan if room remains. */ more: while (ib && pageout_count < vm_pageout_page_count) { vm_page_t p; if (ib > pindex) { ib = 0; break; } if ((p = vm_page_prev(pb)) == NULL || vm_page_busied(p)) { ib = 0; break; } vm_page_test_dirty(p); if (p->dirty == 0) { ib = 0; break; } vm_page_lock(p); if (p->queue != PQ_INACTIVE || p->hold_count != 0) { /* may be undergoing I/O */ vm_page_unlock(p); ib = 0; break; } vm_page_unlock(p); mc[--page_base] = pb = p; ++pageout_count; ++ib; /* * alignment boundry, stop here and switch directions. Do * not clear ib. */ if ((pindex - (ib - 1)) % vm_pageout_page_count == 0) break; } while (pageout_count < vm_pageout_page_count && pindex + is < object->size) { vm_page_t p; if ((p = vm_page_next(ps)) == NULL || vm_page_busied(p)) break; vm_page_test_dirty(p); if (p->dirty == 0) break; vm_page_lock(p); if (p->queue != PQ_INACTIVE || p->hold_count != 0) { /* may be undergoing I/O */ vm_page_unlock(p); break; } vm_page_unlock(p); mc[page_base + pageout_count] = ps = p; ++pageout_count; ++is; } /* * If we exhausted our forward scan, continue with the reverse scan * when possible, even past a page boundry. This catches boundry * conditions. */ if (ib && pageout_count < vm_pageout_page_count) goto more; /* * we allow reads during pageouts... */ return (vm_pageout_flush(&mc[page_base], pageout_count, 0, 0, NULL, NULL)); } /* * vm_pageout_flush() - launder the given pages * * The given pages are laundered. Note that we setup for the start of * I/O ( i.e. busy the page ), mark it read-only, and bump the object * reference count all in here rather then in the parent. If we want * the parent to do more sophisticated things we may have to change * the ordering. * * Returned runlen is the count of pages between mreq and first * page after mreq with status VM_PAGER_AGAIN. * *eio is set to TRUE if pager returned VM_PAGER_ERROR or VM_PAGER_FAIL * for any page in runlen set. */ int vm_pageout_flush(vm_page_t *mc, int count, int flags, int mreq, int *prunlen, boolean_t *eio) { vm_object_t object = mc[0]->object; int pageout_status[count]; int numpagedout = 0; int i, runlen; VM_OBJECT_ASSERT_WLOCKED(object); /* * Initiate I/O. Bump the vm_page_t->busy counter and * mark the pages read-only. * * We do not have to fixup the clean/dirty bits here... we can * allow the pager to do it after the I/O completes. * * NOTE! mc[i]->dirty may be partial or fragmented due to an * edge case with file fragments. */ for (i = 0; i < count; i++) { KASSERT(mc[i]->valid == VM_PAGE_BITS_ALL, ("vm_pageout_flush: partially invalid page %p index %d/%d", mc[i], i, count)); vm_page_sbusy(mc[i]); pmap_remove_write(mc[i]); } vm_object_pip_add(object, count); vm_pager_put_pages(object, mc, count, flags, pageout_status); runlen = count - mreq; if (eio != NULL) *eio = FALSE; for (i = 0; i < count; i++) { vm_page_t mt = mc[i]; KASSERT(pageout_status[i] == VM_PAGER_PEND || !pmap_page_is_write_mapped(mt), ("vm_pageout_flush: page %p is not write protected", mt)); switch (pageout_status[i]) { case VM_PAGER_OK: case VM_PAGER_PEND: numpagedout++; break; case VM_PAGER_BAD: /* * Page outside of range of object. Right now we * essentially lose the changes by pretending it * worked. */ vm_page_undirty(mt); break; case VM_PAGER_ERROR: case VM_PAGER_FAIL: /* * If page couldn't be paged out, then reactivate the * page so it doesn't clog the inactive list. (We * will try paging out it again later). */ vm_page_lock(mt); vm_page_activate(mt); vm_page_unlock(mt); if (eio != NULL && i >= mreq && i - mreq < runlen) *eio = TRUE; break; case VM_PAGER_AGAIN: if (i >= mreq && i - mreq < runlen) runlen = i - mreq; break; } /* * If the operation is still going, leave the page busy to * block all other accesses. Also, leave the paging in * progress indicator set so that we don't attempt an object * collapse. */ if (pageout_status[i] != VM_PAGER_PEND) { vm_object_pip_wakeup(object); vm_page_sunbusy(mt); } } if (prunlen != NULL) *prunlen = runlen; return (numpagedout); } static boolean_t vm_pageout_launder(struct vm_pagequeue *pq, int tries, vm_paddr_t low, vm_paddr_t high) { struct mount *mp; struct vnode *vp; vm_object_t object; vm_paddr_t pa; vm_page_t m, m_tmp, next; int lockmode; vm_pagequeue_lock(pq); TAILQ_FOREACH_SAFE(m, &pq->pq_pl, plinks.q, next) { if ((m->flags & PG_MARKER) != 0) continue; pa = VM_PAGE_TO_PHYS(m); if (pa < low || pa + PAGE_SIZE > high) continue; if (!vm_pageout_page_lock(m, &next) || m->hold_count != 0) { vm_page_unlock(m); continue; } object = m->object; if ((!VM_OBJECT_TRYWLOCK(object) && (!vm_pageout_fallback_object_lock(m, &next) || m->hold_count != 0)) || vm_page_busied(m)) { vm_page_unlock(m); VM_OBJECT_WUNLOCK(object); continue; } vm_page_test_dirty(m); if (m->dirty == 0 && object->ref_count != 0) pmap_remove_all(m); if (m->dirty != 0) { vm_page_unlock(m); if (tries == 0 || (object->flags & OBJ_DEAD) != 0) { VM_OBJECT_WUNLOCK(object); continue; } if (object->type == OBJT_VNODE) { vm_pagequeue_unlock(pq); vp = object->handle; vm_object_reference_locked(object); VM_OBJECT_WUNLOCK(object); (void)vn_start_write(vp, &mp, V_WAIT); lockmode = MNT_SHARED_WRITES(vp->v_mount) ? LK_SHARED : LK_EXCLUSIVE; vn_lock(vp, lockmode | LK_RETRY); VM_OBJECT_WLOCK(object); vm_object_page_clean(object, 0, 0, OBJPC_SYNC); VM_OBJECT_WUNLOCK(object); VOP_UNLOCK(vp, 0); vm_object_deallocate(object); vn_finished_write(mp); return (TRUE); } else if (object->type == OBJT_SWAP || object->type == OBJT_DEFAULT) { vm_pagequeue_unlock(pq); m_tmp = m; vm_pageout_flush(&m_tmp, 1, VM_PAGER_PUT_SYNC, 0, NULL, NULL); VM_OBJECT_WUNLOCK(object); return (TRUE); } } else { /* * Dequeue here to prevent lock recursion in * vm_page_cache(). */ vm_page_dequeue_locked(m); vm_page_cache(m); vm_page_unlock(m); } VM_OBJECT_WUNLOCK(object); } vm_pagequeue_unlock(pq); return (FALSE); } /* * Increase the number of cached pages. The specified value, "tries", * determines which categories of pages are cached: * * 0: All clean, inactive pages within the specified physical address range * are cached. Will not sleep. * 1: The vm_lowmem handlers are called. All inactive pages within * the specified physical address range are cached. May sleep. * 2: The vm_lowmem handlers are called. All inactive and active pages * within the specified physical address range are cached. May sleep. */ void vm_pageout_grow_cache(int tries, vm_paddr_t low, vm_paddr_t high) { int actl, actmax, inactl, inactmax, dom, initial_dom; static int start_dom = 0; if (tries > 0) { /* * Decrease registered cache sizes. The vm_lowmem handlers * may acquire locks and/or sleep, so they can only be invoked * when "tries" is greater than zero. */ SDT_PROBE0(vm, , , vm__lowmem_cache); EVENTHANDLER_INVOKE(vm_lowmem, 0); /* * We do this explicitly after the caches have been drained * above. */ uma_reclaim(); } /* * Make the next scan start on the next domain. */ initial_dom = atomic_fetchadd_int(&start_dom, 1) % vm_ndomains; inactl = 0; inactmax = vm_cnt.v_inactive_count; actl = 0; actmax = tries < 2 ? 0 : vm_cnt.v_active_count; dom = initial_dom; /* * Scan domains in round-robin order, first inactive queues, * then active. Since domain usually owns large physically * contiguous chunk of memory, it makes sense to completely * exhaust one domain before switching to next, while growing * the pool of contiguous physical pages. * * Do not even start launder a domain which cannot contain * the specified address range, as indicated by segments * constituting the domain. */ again: if (inactl < inactmax) { if (vm_phys_domain_intersects(vm_dom[dom].vmd_segs, low, high) && vm_pageout_launder(&vm_dom[dom].vmd_pagequeues[PQ_INACTIVE], tries, low, high)) { inactl++; goto again; } if (++dom == vm_ndomains) dom = 0; if (dom != initial_dom) goto again; } if (actl < actmax) { if (vm_phys_domain_intersects(vm_dom[dom].vmd_segs, low, high) && vm_pageout_launder(&vm_dom[dom].vmd_pagequeues[PQ_ACTIVE], tries, low, high)) { actl++; goto again; } if (++dom == vm_ndomains) dom = 0; if (dom != initial_dom) goto again; } } #if !defined(NO_SWAPPING) /* * vm_pageout_object_deactivate_pages * * Deactivate enough pages to satisfy the inactive target * requirements. * * The object and map must be locked. */ static void vm_pageout_object_deactivate_pages(pmap_t pmap, vm_object_t first_object, long desired) { vm_object_t backing_object, object; vm_page_t p; int act_delta, remove_mode; VM_OBJECT_ASSERT_LOCKED(first_object); if ((first_object->flags & OBJ_FICTITIOUS) != 0) return; for (object = first_object;; object = backing_object) { if (pmap_resident_count(pmap) <= desired) goto unlock_return; VM_OBJECT_ASSERT_LOCKED(object); if ((object->flags & OBJ_UNMANAGED) != 0 || object->paging_in_progress != 0) goto unlock_return; remove_mode = 0; if (object->shadow_count > 1) remove_mode = 1; /* * Scan the object's entire memory queue. */ TAILQ_FOREACH(p, &object->memq, listq) { if (pmap_resident_count(pmap) <= desired) goto unlock_return; if (vm_page_busied(p)) continue; PCPU_INC(cnt.v_pdpages); vm_page_lock(p); if (p->wire_count != 0 || p->hold_count != 0 || !pmap_page_exists_quick(pmap, p)) { vm_page_unlock(p); continue; } act_delta = pmap_ts_referenced(p); if ((p->aflags & PGA_REFERENCED) != 0) { if (act_delta == 0) act_delta = 1; vm_page_aflag_clear(p, PGA_REFERENCED); } if (p->queue != PQ_ACTIVE && act_delta != 0) { vm_page_activate(p); p->act_count += act_delta; } else if (p->queue == PQ_ACTIVE) { if (act_delta == 0) { p->act_count -= min(p->act_count, ACT_DECLINE); if (!remove_mode && p->act_count == 0) { pmap_remove_all(p); vm_page_deactivate(p); } else vm_page_requeue(p); } else { vm_page_activate(p); if (p->act_count < ACT_MAX - ACT_ADVANCE) p->act_count += ACT_ADVANCE; vm_page_requeue(p); } } else if (p->queue == PQ_INACTIVE) pmap_remove_all(p); vm_page_unlock(p); } if ((backing_object = object->backing_object) == NULL) goto unlock_return; VM_OBJECT_RLOCK(backing_object); if (object != first_object) VM_OBJECT_RUNLOCK(object); } unlock_return: if (object != first_object) VM_OBJECT_RUNLOCK(object); } /* * deactivate some number of pages in a map, try to do it fairly, but * that is really hard to do. */ static void vm_pageout_map_deactivate_pages(map, desired) vm_map_t map; long desired; { vm_map_entry_t tmpe; vm_object_t obj, bigobj; int nothingwired; if (!vm_map_trylock(map)) return; bigobj = NULL; nothingwired = TRUE; /* * first, search out the biggest object, and try to free pages from * that. */ tmpe = map->header.next; while (tmpe != &map->header) { if ((tmpe->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) { obj = tmpe->object.vm_object; if (obj != NULL && VM_OBJECT_TRYRLOCK(obj)) { if (obj->shadow_count <= 1 && (bigobj == NULL || bigobj->resident_page_count < obj->resident_page_count)) { if (bigobj != NULL) VM_OBJECT_RUNLOCK(bigobj); bigobj = obj; } else VM_OBJECT_RUNLOCK(obj); } } if (tmpe->wired_count > 0) nothingwired = FALSE; tmpe = tmpe->next; } if (bigobj != NULL) { vm_pageout_object_deactivate_pages(map->pmap, bigobj, desired); VM_OBJECT_RUNLOCK(bigobj); } /* * Next, hunt around for other pages to deactivate. We actually * do this search sort of wrong -- .text first is not the best idea. */ tmpe = map->header.next; while (tmpe != &map->header) { if (pmap_resident_count(vm_map_pmap(map)) <= desired) break; if ((tmpe->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) { obj = tmpe->object.vm_object; if (obj != NULL) { VM_OBJECT_RLOCK(obj); vm_pageout_object_deactivate_pages(map->pmap, obj, desired); VM_OBJECT_RUNLOCK(obj); } } tmpe = tmpe->next; } /* * Remove all mappings if a process is swapped out, this will free page * table pages. */ if (desired == 0 && nothingwired) { pmap_remove(vm_map_pmap(map), vm_map_min(map), vm_map_max(map)); } vm_map_unlock(map); } #endif /* !defined(NO_SWAPPING) */ /* * Attempt to acquire all of the necessary locks to launder a page and * then call through the clustering layer to PUTPAGES. Wait a short * time for a vnode lock. * * Requires the page and object lock on entry, releases both before return. * Returns 0 on success and an errno otherwise. */ static int vm_pageout_clean(vm_page_t m) { struct vnode *vp; struct mount *mp; vm_object_t object; vm_pindex_t pindex; int error, lockmode; vm_page_assert_locked(m); object = m->object; VM_OBJECT_ASSERT_WLOCKED(object); error = 0; vp = NULL; mp = NULL; /* * The object is already known NOT to be dead. It * is possible for the vget() to block the whole * pageout daemon, but the new low-memory handling * code should prevent it. * * We can't wait forever for the vnode lock, we might * deadlock due to a vn_read() getting stuck in * vm_wait while holding this vnode. We skip the * vnode if we can't get it in a reasonable amount * of time. */ if (object->type == OBJT_VNODE) { vm_page_unlock(m); vp = object->handle; if (vp->v_type == VREG && vn_start_write(vp, &mp, V_NOWAIT) != 0) { mp = NULL; error = EDEADLK; goto unlock_all; } KASSERT(mp != NULL, ("vp %p with NULL v_mount", vp)); vm_object_reference_locked(object); pindex = m->pindex; VM_OBJECT_WUNLOCK(object); lockmode = MNT_SHARED_WRITES(vp->v_mount) ? LK_SHARED : LK_EXCLUSIVE; if (vget(vp, lockmode | LK_TIMELOCK, curthread)) { vp = NULL; error = EDEADLK; goto unlock_mp; } VM_OBJECT_WLOCK(object); vm_page_lock(m); /* * While the object and page were unlocked, the page * may have been: * (1) moved to a different queue, * (2) reallocated to a different object, * (3) reallocated to a different offset, or * (4) cleaned. */ if (m->queue != PQ_INACTIVE || m->object != object || m->pindex != pindex || m->dirty == 0) { vm_page_unlock(m); error = ENXIO; goto unlock_all; } /* * The page may have been busied or held while the object * and page locks were released. */ if (vm_page_busied(m) || m->hold_count != 0) { vm_page_unlock(m); error = EBUSY; goto unlock_all; } } /* * If a page is dirty, then it is either being washed * (but not yet cleaned) or it is still in the * laundry. If it is still in the laundry, then we * start the cleaning operation. */ if (vm_pageout_cluster(m) == 0) error = EIO; unlock_all: VM_OBJECT_WUNLOCK(object); unlock_mp: vm_page_lock_assert(m, MA_NOTOWNED); if (mp != NULL) { if (vp != NULL) vput(vp); vm_object_deallocate(object); vn_finished_write(mp); } return (error); } /* * vm_pageout_scan does the dirty work for the pageout daemon. * * pass 0 - Update active LRU/deactivate pages * pass 1 - Move inactive to cache or free * pass 2 - Launder dirty pages */ static void vm_pageout_scan(struct vm_domain *vmd, int pass) { vm_page_t m, next; struct vm_pagequeue *pq; vm_object_t object; long min_scan; int act_delta, addl_page_shortage, deficit, error, maxlaunder, maxscan; int page_shortage, scan_tick, scanned, vnodes_skipped; boolean_t pageout_ok, queues_locked; /* * If we need to reclaim memory ask kernel caches to return * some. We rate limit to avoid thrashing. */ if (vmd == &vm_dom[0] && pass > 0 && (time_uptime - lowmem_uptime) >= lowmem_period) { /* * Decrease registered cache sizes. */ SDT_PROBE0(vm, , , vm__lowmem_scan); EVENTHANDLER_INVOKE(vm_lowmem, 0); /* * We do this explicitly after the caches have been * drained above. */ uma_reclaim(); lowmem_uptime = time_uptime; } /* * The addl_page_shortage is the number of temporarily * stuck pages in the inactive queue. In other words, the * number of pages from the inactive count that should be * discounted in setting the target for the active queue scan. */ addl_page_shortage = 0; /* * Calculate the number of pages we want to either free or move * to the cache. */ if (pass > 0) { deficit = atomic_readandclear_int(&vm_pageout_deficit); page_shortage = vm_paging_target() + deficit; } else page_shortage = deficit = 0; /* * maxlaunder limits the number of dirty pages we flush per scan. * For most systems a smaller value (16 or 32) is more robust under * extreme memory and disk pressure because any unnecessary writes * to disk can result in extreme performance degredation. However, * systems with excessive dirty pages (especially when MAP_NOSYNC is * used) will die horribly with limited laundering. If the pageout * daemon cannot clean enough pages in the first pass, we let it go * all out in succeeding passes. */ if ((maxlaunder = vm_max_launder) <= 1) maxlaunder = 1; if (pass > 1) maxlaunder = 10000; vnodes_skipped = 0; /* * Start scanning the inactive queue for pages we can move to the * cache or free. The scan will stop when the target is reached or * we have scanned the entire inactive queue. Note that m->act_count * is not used to form decisions for the inactive queue, only for the * active queue. */ pq = &vmd->vmd_pagequeues[PQ_INACTIVE]; maxscan = pq->pq_cnt; vm_pagequeue_lock(pq); queues_locked = TRUE; for (m = TAILQ_FIRST(&pq->pq_pl); m != NULL && maxscan-- > 0 && page_shortage > 0; m = next) { vm_pagequeue_assert_locked(pq); KASSERT(queues_locked, ("unlocked queues")); KASSERT(m->queue == PQ_INACTIVE, ("Inactive queue %p", m)); PCPU_INC(cnt.v_pdpages); next = TAILQ_NEXT(m, plinks.q); /* * skip marker pages */ if (m->flags & PG_MARKER) continue; KASSERT((m->flags & PG_FICTITIOUS) == 0, ("Fictitious page %p cannot be in inactive queue", m)); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("Unmanaged page %p cannot be in inactive queue", m)); /* * The page or object lock acquisitions fail if the * page was removed from the queue or moved to a * different position within the queue. In either * case, addl_page_shortage should not be incremented. */ if (!vm_pageout_page_lock(m, &next)) goto unlock_page; else if (m->hold_count != 0) { /* * Held pages are essentially stuck in the * queue. So, they ought to be discounted * from the inactive count. See the * calculation of the page_shortage for the * loop over the active queue below. */ addl_page_shortage++; goto unlock_page; } object = m->object; if (!VM_OBJECT_TRYWLOCK(object)) { if (!vm_pageout_fallback_object_lock(m, &next)) goto unlock_object; else if (m->hold_count != 0) { addl_page_shortage++; goto unlock_object; } } if (vm_page_busied(m)) { /* * Don't mess with busy pages. Leave them at * the front of the queue. Most likely, they * are being paged out and will leave the * queue shortly after the scan finishes. So, * they ought to be discounted from the * inactive count. */ addl_page_shortage++; unlock_object: VM_OBJECT_WUNLOCK(object); unlock_page: vm_page_unlock(m); continue; } KASSERT(m->hold_count == 0, ("Held page %p", m)); /* * We unlock the inactive page queue, invalidating the * 'next' pointer. Use our marker to remember our * place. */ TAILQ_INSERT_AFTER(&pq->pq_pl, m, &vmd->vmd_marker, plinks.q); vm_pagequeue_unlock(pq); queues_locked = FALSE; /* * Invalid pages can be easily freed. They cannot be * mapped, vm_page_free() asserts this. */ if (m->valid == 0) goto free_page; /* * If the page has been referenced and the object is not dead, * reactivate or requeue the page depending on whether the * object is mapped. */ if ((m->aflags & PGA_REFERENCED) != 0) { vm_page_aflag_clear(m, PGA_REFERENCED); act_delta = 1; } else act_delta = 0; if (object->ref_count != 0) { act_delta += pmap_ts_referenced(m); } else { KASSERT(!pmap_page_is_mapped(m), ("vm_pageout_scan: page %p is mapped", m)); } if (act_delta != 0) { if (object->ref_count != 0) { vm_page_activate(m); /* * Increase the activation count if the page * was referenced while in the inactive queue. * This makes it less likely that the page will * be returned prematurely to the inactive * queue. */ m->act_count += act_delta + ACT_ADVANCE; goto drop_page; } else if ((object->flags & OBJ_DEAD) == 0) goto requeue_page; } /* * If the page appears to be clean at the machine-independent * layer, then remove all of its mappings from the pmap in * anticipation of placing it onto the cache queue. If, * however, any of the page's mappings allow write access, * then the page may still be modified until the last of those * mappings are removed. */ if (object->ref_count != 0) { vm_page_test_dirty(m); if (m->dirty == 0) pmap_remove_all(m); } if (m->dirty == 0) { /* * Clean pages can be freed. */ free_page: vm_page_free(m); PCPU_INC(cnt.v_dfree); --page_shortage; } else if ((object->flags & OBJ_DEAD) != 0) { /* * Leave dirty pages from dead objects at the front of * the queue. They are being paged out and freed by * the thread that destroyed the object. They will * leave the queue shortly after the scan finishes, so * they should be discounted from the inactive count. */ addl_page_shortage++; } else if ((m->flags & PG_WINATCFLS) == 0 && pass < 2) { /* * Dirty pages need to be paged out, but flushing * a page is extremely expensive versus freeing * a clean page. Rather then artificially limiting * the number of pages we can flush, we instead give * dirty pages extra priority on the inactive queue * by forcing them to be cycled through the queue * twice before being flushed, after which the * (now clean) page will cycle through once more * before being freed. This significantly extends * the thrash point for a heavily loaded machine. */ m->flags |= PG_WINATCFLS; requeue_page: vm_pagequeue_lock(pq); queues_locked = TRUE; vm_page_requeue_locked(m); } else if (maxlaunder > 0) { /* * We always want to try to flush some dirty pages if * we encounter them, to keep the system stable. * Normally this number is small, but under extreme * pressure where there are insufficient clean pages * on the inactive queue, we may have to go all out. */ if (object->type != OBJT_SWAP && object->type != OBJT_DEFAULT) pageout_ok = TRUE; else if (disable_swap_pageouts) pageout_ok = FALSE; else if (defer_swap_pageouts) pageout_ok = vm_page_count_min(); else pageout_ok = TRUE; if (!pageout_ok) goto requeue_page; error = vm_pageout_clean(m); /* * Decrement page_shortage on success to account for * the (future) cleaned page. Otherwise we could wind * up laundering or cleaning too many pages. */ if (error == 0) { page_shortage--; maxlaunder--; } else if (error == EDEADLK) { pageout_lock_miss++; vnodes_skipped++; } else if (error == EBUSY) { addl_page_shortage++; } vm_page_lock_assert(m, MA_NOTOWNED); goto relock_queues; } drop_page: vm_page_unlock(m); VM_OBJECT_WUNLOCK(object); relock_queues: if (!queues_locked) { vm_pagequeue_lock(pq); queues_locked = TRUE; } next = TAILQ_NEXT(&vmd->vmd_marker, plinks.q); TAILQ_REMOVE(&pq->pq_pl, &vmd->vmd_marker, plinks.q); } vm_pagequeue_unlock(pq); #if !defined(NO_SWAPPING) /* * Wakeup the swapout daemon if we didn't cache or free the targeted * number of pages. */ if (vm_swap_enabled && page_shortage > 0) vm_req_vmdaemon(VM_SWAP_NORMAL); #endif /* * Wakeup the sync daemon if we skipped a vnode in a writeable object * and we didn't cache or free enough pages. */ if (vnodes_skipped > 0 && page_shortage > vm_cnt.v_free_target - vm_cnt.v_free_min) (void)speedup_syncer(); /* * Compute the number of pages we want to try to move from the * active queue to the inactive queue. */ page_shortage = vm_cnt.v_inactive_target - vm_cnt.v_inactive_count + vm_paging_target() + deficit + addl_page_shortage; pq = &vmd->vmd_pagequeues[PQ_ACTIVE]; vm_pagequeue_lock(pq); maxscan = pq->pq_cnt; /* * If we're just idle polling attempt to visit every * active page within 'update_period' seconds. */ scan_tick = ticks; if (vm_pageout_update_period != 0) { min_scan = pq->pq_cnt; min_scan *= scan_tick - vmd->vmd_last_active_scan; min_scan /= hz * vm_pageout_update_period; } else min_scan = 0; if (min_scan > 0 || (page_shortage > 0 && maxscan > 0)) vmd->vmd_last_active_scan = scan_tick; /* * Scan the active queue for pages that can be deactivated. Update * the per-page activity counter and use it to identify deactivation * candidates. */ for (m = TAILQ_FIRST(&pq->pq_pl), scanned = 0; m != NULL && (scanned < min_scan || (page_shortage > 0 && scanned < maxscan)); m = next, scanned++) { KASSERT(m->queue == PQ_ACTIVE, ("vm_pageout_scan: page %p isn't active", m)); next = TAILQ_NEXT(m, plinks.q); if ((m->flags & PG_MARKER) != 0) continue; KASSERT((m->flags & PG_FICTITIOUS) == 0, ("Fictitious page %p cannot be in active queue", m)); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("Unmanaged page %p cannot be in active queue", m)); if (!vm_pageout_page_lock(m, &next)) { vm_page_unlock(m); continue; } /* * The count for pagedaemon pages is done after checking the * page for eligibility... */ PCPU_INC(cnt.v_pdpages); /* * Check to see "how much" the page has been used. */ if ((m->aflags & PGA_REFERENCED) != 0) { vm_page_aflag_clear(m, PGA_REFERENCED); act_delta = 1; } else act_delta = 0; /* * Unlocked object ref count check. Two races are possible. * 1) The ref was transitioning to zero and we saw non-zero, * the pmap bits will be checked unnecessarily. * 2) The ref was transitioning to one and we saw zero. * The page lock prevents a new reference to this page so * we need not check the reference bits. */ if (m->object->ref_count != 0) act_delta += pmap_ts_referenced(m); /* * Advance or decay the act_count based on recent usage. */ if (act_delta != 0) { m->act_count += ACT_ADVANCE + act_delta; if (m->act_count > ACT_MAX) m->act_count = ACT_MAX; } else m->act_count -= min(m->act_count, ACT_DECLINE); /* * Move this page to the tail of the active or inactive * queue depending on usage. */ if (m->act_count == 0) { /* Dequeue to avoid later lock recursion. */ vm_page_dequeue_locked(m); vm_page_deactivate(m); page_shortage--; } else vm_page_requeue_locked(m); vm_page_unlock(m); } vm_pagequeue_unlock(pq); #if !defined(NO_SWAPPING) /* * Idle process swapout -- run once per second. */ if (vm_swap_idle_enabled) { static long lsec; if (time_second != lsec) { vm_req_vmdaemon(VM_SWAP_IDLE); lsec = time_second; } } #endif /* * If we are critically low on one of RAM or swap and low on * the other, kill the largest process. However, we avoid * doing this on the first pass in order to give ourselves a * chance to flush out dirty vnode-backed pages and to allow * active pages to be moved to the inactive queue and reclaimed. */ vm_pageout_mightbe_oom(vmd, pass); } static int vm_pageout_oom_vote; /* * The pagedaemon threads randlomly select one to perform the * OOM. Trying to kill processes before all pagedaemons * failed to reach free target is premature. */ static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass) { int old_vote; if (pass <= 1 || !((swap_pager_avail < 64 && vm_page_count_min()) || (swap_pager_full && vm_paging_target() > 0))) { if (vmd->vmd_oom) { vmd->vmd_oom = FALSE; atomic_subtract_int(&vm_pageout_oom_vote, 1); } return; } if (vmd->vmd_oom) return; vmd->vmd_oom = TRUE; old_vote = atomic_fetchadd_int(&vm_pageout_oom_vote, 1); if (old_vote != vm_ndomains - 1) return; /* * The current pagedaemon thread is the last in the quorum to * start OOM. Initiate the selection and signaling of the * victim. */ vm_pageout_oom(VM_OOM_MEM); /* * After one round of OOM terror, recall our vote. On the * next pass, current pagedaemon would vote again if the low * memory condition is still there, due to vmd_oom being * false. */ vmd->vmd_oom = FALSE; atomic_subtract_int(&vm_pageout_oom_vote, 1); } void vm_pageout_oom(int shortage) { struct proc *p, *bigproc; vm_offset_t size, bigsize; struct thread *td; struct vmspace *vm; /* * We keep the process bigproc locked once we find it to keep anyone * from messing with it; however, there is a possibility of * deadlock if process B is bigproc and one of it's child processes * attempts to propagate a signal to B while we are waiting for A's * lock while walking this list. To avoid this, we don't block on * the process lock but just skip a process if it is already locked. */ bigproc = NULL; bigsize = 0; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { int breakout; PROC_LOCK(p); /* * If this is a system, protected or killed process, skip it. */ if (p->p_state != PRS_NORMAL || (p->p_flag & (P_INEXEC | P_PROTECTED | P_SYSTEM | P_WEXIT)) != 0 || p->p_pid == 1 || P_KILLED(p) || (p->p_pid < 48 && swap_pager_avail != 0)) { PROC_UNLOCK(p); continue; } /* * If the process is in a non-running type state, * don't touch it. Check all the threads individually. */ breakout = 0; FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (!TD_ON_RUNQ(td) && !TD_IS_RUNNING(td) && !TD_IS_SLEEPING(td) && - !TD_IS_SUSPENDED(td)) { + !TD_IS_SUSPENDED(td) && + !TD_IS_SWAPPED(td)) { thread_unlock(td); breakout = 1; break; } thread_unlock(td); } if (breakout) { PROC_UNLOCK(p); continue; } /* * get the process size */ vm = vmspace_acquire_ref(p); if (vm == NULL) { PROC_UNLOCK(p); continue; } _PHOLD(p); if (!vm_map_trylock_read(&vm->vm_map)) { _PRELE(p); PROC_UNLOCK(p); vmspace_free(vm); continue; } PROC_UNLOCK(p); size = vmspace_swap_count(vm); vm_map_unlock_read(&vm->vm_map); if (shortage == VM_OOM_MEM) size += vmspace_resident_count(vm); vmspace_free(vm); /* * if the this process is bigger than the biggest one * remember it. */ if (size > bigsize) { if (bigproc != NULL) PRELE(bigproc); bigproc = p; bigsize = size; } else { PRELE(p); } } sx_sunlock(&allproc_lock); if (bigproc != NULL) { if (vm_panic_on_oom != 0) panic("out of swap space"); PROC_LOCK(bigproc); killproc(bigproc, "out of swap space"); sched_nice(bigproc, PRIO_MIN); _PRELE(bigproc); PROC_UNLOCK(bigproc); wakeup(&vm_cnt.v_free_count); } } static void vm_pageout_worker(void *arg) { struct vm_domain *domain; int domidx; domidx = (uintptr_t)arg; domain = &vm_dom[domidx]; /* * XXXKIB It could be useful to bind pageout daemon threads to * the cores belonging to the domain, from which vm_page_array * is allocated. */ KASSERT(domain->vmd_segs != 0, ("domain without segments")); domain->vmd_last_active_scan = ticks; vm_pageout_init_marker(&domain->vmd_marker, PQ_INACTIVE); vm_pageout_init_marker(&domain->vmd_inacthead, PQ_INACTIVE); TAILQ_INSERT_HEAD(&domain->vmd_pagequeues[PQ_INACTIVE].pq_pl, &domain->vmd_inacthead, plinks.q); /* * The pageout daemon worker is never done, so loop forever. */ while (TRUE) { /* * If we have enough free memory, wakeup waiters. Do * not clear vm_pages_needed until we reach our target, * otherwise we may be woken up over and over again and * waste a lot of cpu. */ mtx_lock(&vm_page_queue_free_mtx); if (vm_pages_needed && !vm_page_count_min()) { if (!vm_paging_needed()) vm_pages_needed = 0; wakeup(&vm_cnt.v_free_count); } if (vm_pages_needed) { /* * We're still not done. Either vm_pages_needed was * set by another thread during the previous scan * (typically, this happens during a level 0 scan) or * vm_pages_needed was already set and the scan failed * to free enough pages. If we haven't yet performed * a level >= 2 scan (unlimited dirty cleaning), then * upgrade the level and scan again now. Otherwise, * sleep a bit and try again later. While sleeping, * vm_pages_needed can be cleared. */ if (domain->vmd_pass > 1) msleep(&vm_pages_needed, &vm_page_queue_free_mtx, PVM, "psleep", hz / 2); } else { /* * Good enough, sleep until required to refresh * stats. */ msleep(&vm_pages_needed, &vm_page_queue_free_mtx, PVM, "psleep", hz); } if (vm_pages_needed) { vm_cnt.v_pdwakeups++; domain->vmd_pass++; } else domain->vmd_pass = 0; mtx_unlock(&vm_page_queue_free_mtx); vm_pageout_scan(domain, domain->vmd_pass); } } /* * vm_pageout_init initialises basic pageout daemon settings. */ static void vm_pageout_init(void) { /* * Initialize some paging parameters. */ vm_cnt.v_interrupt_free_min = 2; if (vm_cnt.v_page_count < 2000) vm_pageout_page_count = 8; /* * v_free_reserved needs to include enough for the largest * swap pager structures plus enough for any pv_entry structs * when paging. */ if (vm_cnt.v_page_count > 1024) vm_cnt.v_free_min = 4 + (vm_cnt.v_page_count - 1024) / 200; else vm_cnt.v_free_min = 4; vm_cnt.v_pageout_free_min = (2*MAXBSIZE)/PAGE_SIZE + vm_cnt.v_interrupt_free_min; vm_cnt.v_free_reserved = vm_pageout_page_count + vm_cnt.v_pageout_free_min + (vm_cnt.v_page_count / 768); vm_cnt.v_free_severe = vm_cnt.v_free_min / 2; vm_cnt.v_free_target = 4 * vm_cnt.v_free_min + vm_cnt.v_free_reserved; vm_cnt.v_free_min += vm_cnt.v_free_reserved; vm_cnt.v_free_severe += vm_cnt.v_free_reserved; vm_cnt.v_inactive_target = (3 * vm_cnt.v_free_target) / 2; if (vm_cnt.v_inactive_target > vm_cnt.v_free_count / 3) vm_cnt.v_inactive_target = vm_cnt.v_free_count / 3; /* * Set the default wakeup threshold to be 10% above the minimum * page limit. This keeps the steady state out of shortfall. */ vm_pageout_wakeup_thresh = (vm_cnt.v_free_min / 10) * 11; /* * Set interval in seconds for active scan. We want to visit each * page at least once every ten minutes. This is to prevent worst * case paging behaviors with stale active LRU. */ if (vm_pageout_update_period == 0) vm_pageout_update_period = 600; /* XXX does not really belong here */ if (vm_page_max_wired == 0) vm_page_max_wired = vm_cnt.v_free_count / 3; } /* * vm_pageout is the high level pageout daemon. */ static void vm_pageout(void) { int error; #if MAXMEMDOM > 1 int i; #endif swap_pager_swap_init(); #if MAXMEMDOM > 1 for (i = 1; i < vm_ndomains; i++) { error = kthread_add(vm_pageout_worker, (void *)(uintptr_t)i, curproc, NULL, 0, 0, "dom%d", i); if (error != 0) { panic("starting pageout for domain %d, error %d\n", i, error); } } #endif error = kthread_add(uma_reclaim_worker, NULL, curproc, NULL, 0, 0, "uma"); if (error != 0) panic("starting uma_reclaim helper, error %d\n", error); vm_pageout_worker((void *)(uintptr_t)0); } /* * Unless the free page queue lock is held by the caller, this function * should be regarded as advisory. Specifically, the caller should * not msleep() on &vm_cnt.v_free_count following this function unless * the free page queue lock is held until the msleep() is performed. */ void pagedaemon_wakeup(void) { if (!vm_pages_needed && curthread->td_proc != pageproc) { vm_pages_needed = 1; wakeup(&vm_pages_needed); } } #if !defined(NO_SWAPPING) static void vm_req_vmdaemon(int req) { static int lastrun = 0; mtx_lock(&vm_daemon_mtx); vm_pageout_req_swapout |= req; if ((ticks > (lastrun + hz)) || (ticks < lastrun)) { wakeup(&vm_daemon_needed); lastrun = ticks; } mtx_unlock(&vm_daemon_mtx); } static void vm_daemon(void) { struct rlimit rsslim; struct proc *p; struct thread *td; struct vmspace *vm; int breakout, swapout_flags, tryagain, attempts; #ifdef RACCT uint64_t rsize, ravailable; #endif while (TRUE) { mtx_lock(&vm_daemon_mtx); msleep(&vm_daemon_needed, &vm_daemon_mtx, PPAUSE, "psleep", #ifdef RACCT racct_enable ? hz : 0 #else 0 #endif ); swapout_flags = vm_pageout_req_swapout; vm_pageout_req_swapout = 0; mtx_unlock(&vm_daemon_mtx); if (swapout_flags) swapout_procs(swapout_flags); /* * scan the processes for exceeding their rlimits or if * process is swapped out -- deactivate pages */ tryagain = 0; attempts = 0; again: attempts++; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { vm_pindex_t limit, size; /* * if this is a system process or if we have already * looked at this process, skip it. */ PROC_LOCK(p); if (p->p_state != PRS_NORMAL || p->p_flag & (P_INEXEC | P_SYSTEM | P_WEXIT)) { PROC_UNLOCK(p); continue; } /* * if the process is in a non-running type state, * don't touch it. */ breakout = 0; FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (!TD_ON_RUNQ(td) && !TD_IS_RUNNING(td) && !TD_IS_SLEEPING(td) && !TD_IS_SUSPENDED(td)) { thread_unlock(td); breakout = 1; break; } thread_unlock(td); } if (breakout) { PROC_UNLOCK(p); continue; } /* * get a limit */ lim_rlimit_proc(p, RLIMIT_RSS, &rsslim); limit = OFF_TO_IDX( qmin(rsslim.rlim_cur, rsslim.rlim_max)); /* * let processes that are swapped out really be * swapped out set the limit to nothing (will force a * swap-out.) */ if ((p->p_flag & P_INMEM) == 0) limit = 0; /* XXX */ vm = vmspace_acquire_ref(p); PROC_UNLOCK(p); if (vm == NULL) continue; size = vmspace_resident_count(vm); if (size >= limit) { vm_pageout_map_deactivate_pages( &vm->vm_map, limit); } #ifdef RACCT if (racct_enable) { rsize = IDX_TO_OFF(size); PROC_LOCK(p); racct_set(p, RACCT_RSS, rsize); ravailable = racct_get_available(p, RACCT_RSS); PROC_UNLOCK(p); if (rsize > ravailable) { /* * Don't be overly aggressive; this * might be an innocent process, * and the limit could've been exceeded * by some memory hog. Don't try * to deactivate more than 1/4th * of process' resident set size. */ if (attempts <= 8) { if (ravailable < rsize - (rsize / 4)) { ravailable = rsize - (rsize / 4); } } vm_pageout_map_deactivate_pages( &vm->vm_map, OFF_TO_IDX(ravailable)); /* Update RSS usage after paging out. */ size = vmspace_resident_count(vm); rsize = IDX_TO_OFF(size); PROC_LOCK(p); racct_set(p, RACCT_RSS, rsize); PROC_UNLOCK(p); if (rsize > ravailable) tryagain = 1; } } #endif vmspace_free(vm); } sx_sunlock(&allproc_lock); if (tryagain != 0 && attempts <= 10) goto again; } } #endif /* !defined(NO_SWAPPING) */ Index: user/ngie/more-tests2/sys =================================================================== --- user/ngie/more-tests2/sys (revision 290915) +++ user/ngie/more-tests2/sys (revision 290916) Property changes on: user/ngie/more-tests2/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r290900-290915 Index: user/ngie/more-tests2/tests/sys/kern/Makefile =================================================================== --- user/ngie/more-tests2/tests/sys/kern/Makefile (revision 290915) +++ user/ngie/more-tests2/tests/sys/kern/Makefile (revision 290916) @@ -1,19 +1,26 @@ # $FreeBSD$ +TESTSRC= ${SRCTOP}/contrib/netbsd-tests/kernel + TESTSDIR= ${TESTSBASE}/sys/kern ATF_TESTS_C+= kern_copyin ATF_TESTS_C+= kern_descrip_test ATF_TESTS_C+= ptrace_test ATF_TESTS_C+= unix_seqpacket_test TEST_METADATA.unix_seqpacket_test+= timeout="15" LDADD.ptrace_test+= -lpthread LDADD.unix_seqpacket_test+= -lpthread +NETBSD_ATF_TESTS_C+= lockf_test + WARNS?= 5 TESTS_SUBDIRS+= acct TESTS_SUBDIRS+= execve +TESTS_SUBDIRS+= pipe + +.include .include Index: user/ngie/more-tests2/tests/sys/kern/pipe/Makefile =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/Makefile (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/Makefile (revision 290916) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/kern/pipe + +PLAIN_TESTS_C+= big_pipe_test +PLAIN_TESTS_C+= pipe_fstat_bug_test +PLAIN_TESTS_C+= pipe_ino_test +PLAIN_TESTS_C+= pipe_overcommit1_test +PLAIN_TESTS_C+= pipe_overcommit2_test +PLAIN_TESTS_C+= pipe_reverse2_test +PLAIN_TESTS_C+= pipe_reverse_test +PLAIN_TESTS_C+= pipe_wraparound_test + +WARNS?= 6 + +.include Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/big_pipe_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/big_pipe_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/big_pipe_test.c (revision 290916) @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BIG_PIPE_SIZE 64*1024 /* From sys/pipe.h */ + +/* + * Test for the non-blocking big pipe bug (write(2) returning + * EAGAIN while select(2) returns the descriptor as ready for write). + * + * $FreeBSD$ + */ + +static void +write_frame(int fd, char *buf, unsigned long buflen) +{ + fd_set wfd; + int i; + + while (buflen) { + FD_ZERO(&wfd); + FD_SET(fd, &wfd); + i = select(fd+1, NULL, &wfd, NULL, NULL); + if (i < 0) + err(1, "select failed"); + if (i != 1) { + errx(1, "select returned unexpected value %d\n", i); + exit(1); + } + i = write(fd, buf, buflen); + if (i < 0) { + if (errno != EAGAIN) + warn("write failed"); + exit(1); + } + buf += i; + buflen -= i; + } +} + +int +main(void) +{ + /* any value over PIPE_SIZE should do */ + char buf[BIG_PIPE_SIZE]; + int i, flags, fd[2]; + + if (pipe(fd) < 0) + errx(1, "pipe failed"); + + flags = fcntl(fd[1], F_GETFL); + if (flags == -1 || fcntl(fd[1], F_SETFL, flags|O_NONBLOCK) == -1) { + printf("fcntl failed: %s\n", strerror(errno)); + exit(1); + } + + switch (fork()) { + case -1: + err(1, "fork failed: %s\n", strerror(errno)); + break; + case 0: + close(fd[1]); + for (;;) { + /* Any small size should do */ + i = read(fd[0], buf, 256); + if (i == 0) + break; + if (i < 0) + err(1, "read"); + } + exit(0); + default: + break; + } + + close(fd[0]); + memset(buf, 0, sizeof buf); + for (i = 0; i < 1000; i++) + write_frame(fd[1], buf, sizeof buf); + + printf("ok\n"); + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/big_pipe_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_fstat_bug_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_fstat_bug_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_fstat_bug_test.c (revision 290916) @@ -0,0 +1,138 @@ +/* +Copyright (C) 2004 Michael J. Silbersack. 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * $FreeBSD$ + * The goal of this program is to see if fstat reports the correct + * data count for a pipe. Prior to revision 1.172 of sys_pipe.c, + * 0 would be returned once the pipe entered direct write mode. + * + * Linux (2.6) always returns zero, so it's not a valuable platform + * for comparison. + */ + +int +main(void) +{ + char buffer[32768], buffer2[32768], go[] = "go", go2[] = "go2"; + int desc[2], ipc_coord[2]; + ssize_t error; + int successes = 0; + struct stat status; + pid_t new_pid; + + error = pipe(desc); + if (error == -1) + err(1, "Couldn't allocate data pipe"); + + error = pipe(ipc_coord); + if (error == -1) + err(1, "Couldn't allocate IPC coordination pipe"); + + new_pid = fork(); + assert(new_pid != -1); + + close(new_pid == 0 ? desc[0] : desc[1]); + +#define SYNC_R(i, _buf) do { \ + int _error = errno; \ + warnx("%d: waiting for synchronization", __LINE__); \ + if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \ + errno = _error; \ + } while(0) + +#define SYNC_W(i, _buf) do { \ + int _error = errno; \ + warnx("%d: sending synchronization", __LINE__); \ + if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \ + errno = _error; \ + } while(0) + +#define WRITE(s) do { \ + ssize_t _size; \ + if ((_size = write(desc[1], &buffer, s)) != s) \ + warn("short write; wrote %zd, expected %d", _size, s); \ + } while(0) + + if (new_pid == 0) { + + SYNC_R(0, go); + WRITE(145); + SYNC_W(0, go2); + + SYNC_R(0, go); + WRITE(2048); + SYNC_W(0, go2); + + SYNC_R(0, go); + WRITE(4096); + SYNC_W(0, go2); + + SYNC_R(0, go); + WRITE(8191); + SYNC_W(0, go2); + + SYNC_R(0, go); + SYNC_W(0, go2); /* XXX: why is this required? */ + WRITE(8192); + SYNC_W(0, go2); + + close(ipc_coord[0]); + close(ipc_coord[1]); + + _exit(0); + } + + while (successes < 5) { + SYNC_W(1, go); + SYNC_R(1, go2); + fstat(desc[0], &status); + error = read(desc[0], &buffer2, sizeof(buffer2)); + + if (status.st_size != error) + err(1, "FAILURE: stat size %jd read size %zd", + (intmax_t)status.st_size, error); + if (error > 0) { + printf("SUCCESS at stat size %jd read size %zd\n", + (intmax_t)status.st_size, error); + successes++; + } + } + + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_fstat_bug_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_ino_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_ino_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_ino_test.c (revision 290916) @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2011 Giovanni Trematerra + * 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. + */ + +/* + * $FreeBSD$ + * Test conformance to stat(2) SUSv4 description: + * "For all other file types defined in this volume of POSIX.1-2008, the + * structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atim, + * st_ctim, and st_mtim shall have meaningful values ...". + * Check that st_dev and st_ino are meaningful. + */ + +#include +#include +#include +#include +#include + +int +main(void) +{ + int pipefd[2]; + struct stat st1, st2; + + if (pipe(pipefd) == -1) + err(1, "FAIL: pipe"); + + if (fstat(pipefd[0], &st1) == -1) + err(1, "FAIL: fstat st1"); + if (fstat(pipefd[1], &st2) == -1) + err(1, "FAIL: fstat st2"); + if (st1.st_dev != st2.st_dev || st1.st_dev == 0 || st2.st_dev == 0) + errx(1, "FAIL: wrong dev number %d %d", st1.st_dev, st2.st_dev); + if (st1.st_ino == st2.st_ino) + errx(1, "FAIL: inode numbers are equal: %d", st1.st_ino); + + close(pipefd[0]); + close(pipefd[1]); + printf("PASS\n"); + + return (0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_ino_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit1_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit1_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit1_test.c (revision 290916) @@ -0,0 +1,52 @@ +/*- + * Copyright (C) 2005 Michael J. Silbersack + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), 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 COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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 + +/* + * $FreeBSD$ + * This program just allocates as many pipes as it can to ensure + * that using up all pipe memory doesn't cause a panic. + */ + +int +main(void) +{ + int pipes[10000], returnval; + unsigned int i; + + for (i = 0; i < nitems(pipes); i++) { + returnval = pipe(&pipes[i]); + } + printf("PASS\n"); + + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit1_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit2_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit2_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit2_test.c (revision 290916) @@ -0,0 +1,83 @@ +/*- + * Copyright (C) 2005 Michael J. Silbersack + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), 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 COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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 + +/* + * $FreeBSD$ + * This program tests how sys_pipe.c handles the case where there + * is ample memory to allocate a pipe, but the file descriptor + * limit for that user has been exceeded. + */ + +int +main(void) +{ + char template[] = "pipe.XXXXXXXXXX"; + int lastfd, pipes[10000], returnval; + unsigned int i; + + lastfd = -1; + + if (mkstemp(template) == -1) + err(1, "mkstemp failed"); + + for (i = 0; i < nitems(pipes); i++) { + returnval = open(template, O_RDONLY); + if (returnval == -1 && (errno == ENFILE || errno == EMFILE)) + break; /* All descriptors exhausted. */ + else + lastfd = returnval; + } + + /* First falloc failure case in sys_pipe.c:pipe() */ + for (i = 0; i < 1000; i++) { + returnval = pipe(&pipes[i]); + } + + /* + * Free just one FD so that the second falloc failure + * case will occur. + */ + close(lastfd); + + for (i = 0; i < 1000; i++) { + returnval = pipe(&pipes[i]); + } + printf("PASS\n"); + + unlink(template); + + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_overcommit2_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse2_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse2_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse2_test.c (revision 290916) @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2010 Jilles Tjoelker + * 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. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include + +/* + * Check that pipes can be selected for writing in the reverse direction. + */ +int +main(void) +{ + int pip[2]; + fd_set set; + int n; + + if (pipe(pip) == -1) + err(1, "FAIL: pipe"); + + FD_ZERO(&set); + FD_SET(pip[0], &set); + n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 }); + if (n != 1) + errx(1, "FAIL: select initial reverse direction"); + + n = write(pip[0], "x", 1); + if (n != 1) + err(1, "FAIL: write reverse direction"); + + FD_ZERO(&set); + FD_SET(pip[0], &set); + n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 }); + if (n != 1) + errx(1, "FAIL: select reverse direction after write"); + + printf("PASS\n"); + + return (0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse2_test.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse_test.c (revision 290916) @@ -0,0 +1,149 @@ +/* +Copyright (C) 2004 Michael J. Silbersack. 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * $FreeBSD$ + * This program simply tests writing through the reverse direction of + * a pipe. Nothing too fancy, it's only needed because most pipe-using + * programs never touch the reverse direction (it doesn't exist on + * Linux.) + */ + +int +main(void) +{ + char buffer[65535], buffer2[65535], go[] = "go", go2[] = "go2"; + int desc[2], ipc_coord[2]; + size_t i; + ssize_t total; + int buggy, error; + pid_t new_pid; + + buggy = 0; + total = 0; + + error = pipe(desc); + if (error == -1) + err(1, "Couldn't allocate data pipe"); + + error = pipe(ipc_coord); + if (error == -1) + err(1, "Couldn't allocate IPC coordination pipe"); + + buffer[0] = 'A'; + + for (i = 1; i < (int)sizeof(buffer); i++) { + buffer[i] = buffer[i - 1] + 1; + if (buffer[i] > 'Z') + buffer[i] = 'A'; + } + + new_pid = fork(); + assert(new_pid != -1); + +#define SYNC_R(i, _buf) do { \ + int _error = errno; \ + warnx("%d: waiting for synchronization", __LINE__); \ + if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \ + errno = _error; \ + } while(0) + +#define SYNC_W(i, _buf) do { \ + int _error = errno; \ + warnx("%d: sending synchronization", __LINE__); \ + if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \ + errno = _error; \ + } while(0) + +#define WRITE(s) do { \ + ssize_t _size; \ + if ((_size = write(desc[1], &buffer[total], s)) != s) \ + warn("short write; wrote %zd, expected %d", _size, s); \ + total += _size; \ + } while(0) + + if (new_pid == 0) { + SYNC_R(0, go); + for (i = 0; i < 8; i++) + WRITE(4096); + + SYNC_W(0, go2); + SYNC_R(0, go); + + for (i = 0; i < 2; i++) + WRITE(4096); + + SYNC_W(0, go2); + + _exit(0); + } + + SYNC_W(1, go); + SYNC_R(1, go2); + + error = read(desc[0], &buffer2, 8 * 4096); + total += error; + printf("Read %d bytes\n", error); + + SYNC_W(1, go); + SYNC_R(1, go2); + + error = read(desc[0], &buffer2[total], 2 * 4096); + total += error; + printf("Read %d bytes, done\n", error); + + if (memcmp(buffer, buffer2, total) != 0) { + for (i = 0; i < (size_t)total; i++) { + if (buffer[i] != buffer2[i]) { + buggy = 1; + printf("Location %zu input: %hhx " + "output: %hhx\n", + i, buffer[i], buffer2[i]); + } + } + } + + waitpid(new_pid, NULL, 0); + + if ((buggy == 1) || (total != 10 * 4096)) + errx(1, "FAILED"); + else + printf("SUCCESS\n"); + + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_reverse_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_wraparound_test.c =================================================================== --- user/ngie/more-tests2/tests/sys/kern/pipe/pipe_wraparound_test.c (nonexistent) +++ user/ngie/more-tests2/tests/sys/kern/pipe/pipe_wraparound_test.c (revision 290916) @@ -0,0 +1,140 @@ +/* +Copyright (C) 2004 Michael J. Silbersack. 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * $FreeBSD$ + * This program tests to make sure that wraparound writes and reads + * are working, assuming that 16K socket buffers are used. In order + * to really stress the pipe code with this test, kernel modifications + * nay be necessary. + */ + +int main (void) +{ + char buffer[32768], buffer2[32768], go[] = "go", go2[] = "go2"; + int desc[2], ipc_coord[2]; + ssize_t error, total; + int buggy, i; + pid_t new_pid; + + buggy = 0; + total = 0; + + error = pipe(desc); + if (error == -1) + err(1, "Couldn't allocate data pipe"); + + error = pipe(ipc_coord); + if (error == -1) + err(1, "Couldn't allocate IPC coordination pipe"); + + buffer[0] = 'A'; + + for (i = 1; i < (int)sizeof(buffer); i++) { + buffer[i] = buffer[i - 1] + 1; + if (buffer[i] > 'Z') + buffer[i] = 'A'; + } + + new_pid = fork(); + assert(new_pid != -1); + +#define SYNC_R(i, _buf) do { \ + int _error = errno; \ + warnx("%d: waiting for synchronization", __LINE__); \ + if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \ + errno = _error; \ + } while(0) + +#define SYNC_W(i, _buf) do { \ + int _error = errno; \ + warnx("%d: sending synchronization", __LINE__); \ + if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \ + err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \ + errno = _error; \ + } while(0) + +#define WRITE(s) do { \ + ssize_t _size; \ + if ((_size = write(desc[1], &buffer[total], s)) != s) \ + warn("short write; wrote %zd, expected %d", _size, s); \ + total += _size; \ + } while(0) + + if (new_pid == 0) { + WRITE(4096); + WRITE(4096); + WRITE(4000); + SYNC_W(0, go2); + + SYNC_R(0, go); + WRITE(3000); + WRITE(3000); + SYNC_W(0, go2); + + _exit(0); + } + + SYNC_R(1, go2); + error = read(desc[0], &buffer2, 8192); + total += error; + printf("Read %zd bytes\n", error); + SYNC_W(1, go); + SYNC_R(1, go2); + error = read(desc[0], &buffer2[total], 16384); + total += error; + printf("Read %zd bytes, done\n", error); + + if (memcmp(buffer, buffer2, total) != 0) { + for (i = 0; i < total; i++) { + if (buffer[i] != buffer2[i]) { + buggy = 1; + printf("Location %d input: %hhx output: %hhx\n", + i, buffer[i], buffer2[i]); + } + } + } + + waitpid(new_pid, NULL, 0); + + if (buggy) + errx(1, "FAILURE"); + + printf("SUCCESS\n"); + + exit(0); +} Property changes on: user/ngie/more-tests2/tests/sys/kern/pipe/pipe_wraparound_test.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit1.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit1.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit1.c (nonexistent) @@ -1,47 +0,0 @@ -/*- - * Copyright (C) 2005 Michael J. Silbersack - * 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(s), this list of conditions and the following disclaimer as - * the first lines of this file unmodified other than the possible - * addition of one or more copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), 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 COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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 - -/* - * $FreeBSD$ - * This program just allocates as many pipes as it can to ensure - * that using up all pipe memory doesn't cause a panic. - */ - -int main (void) - -{ - int i, returnval; - int pipes[10000]; - for (i = 0; i < 10000; i++) { - returnval = pipe(&pipes[i]); - } - printf("PASS\n"); -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit1.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit2.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit2.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit2.c (nonexistent) @@ -1,69 +0,0 @@ -/*- - * Copyright (C) 2005 Michael J. Silbersack - * 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(s), this list of conditions and the following disclaimer as - * the first lines of this file unmodified other than the possible - * addition of one or more copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), 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 COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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 - -/* - * $FreeBSD$ - * This program tests how sys_pipe.c handles the case where there - * is ample memory to allocate a pipe, but the file descriptor - * limit for that user has been exceeded. - */ - -int main (int argc, void *argv[]) - -{ - int i, returnval, lastfd; - int pipes[10000]; - - for (i = 0; i < 100000; i++) { - returnval = open(argv[0], O_RDONLY); - if (returnval < 1) - break; /* All descriptors exhausted. */ - else - lastfd = returnval; - } - - /* First falloc failure case in sys_pipe.c:pipe() */ - for (i = 0; i < 1000; i++) { - returnval = pipe(&pipes[i]); - } - - /* - * Free just one FD so that the second falloc failure - * case will occur. - */ - close(lastfd); - - for (i = 0; i < 1000; i++) { - returnval = pipe(&pipes[i]); - } - printf("PASS\n"); -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-overcommit2.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-wraparound.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-wraparound.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-wraparound.c (nonexistent) @@ -1,103 +0,0 @@ -/* -Copyright (C) 2004 Michael J. Silbersack. 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 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 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 -#include -#include - -/* - * $FreeBSD$ - * This program tests to make sure that wraparound writes and reads - * are working, assuming that 16K socket buffers are used. In order - * to really stress the pipe code with this test, kernel modifications - * nay be necessary. - */ - -int main (void) -{ -char buffer[32768], buffer2[32768]; -int desc[2]; -int buggy, error, i, successes, total; -struct stat status; -pid_t new_pid; - -buggy = 0; -total = 0; - -error = pipe(desc); - -if (error) - err(0, "Couldn't allocate fds\n"); - -buffer[0] = 'A'; - -for (i = 1; i < 32768; i++) { - buffer[i] = buffer[i - 1] + 1; - if (buffer[i] > 'Z') - buffer[i] = 'A'; - } - -new_pid = fork(); - -if (new_pid == 0) { - error = write(desc[1], &buffer, 4096); - total += error; - error = write(desc[1], &buffer[total], 4096); - total += error; - error = write(desc[1], &buffer[total], 4000); - total += error; - printf("Wrote %d bytes, sleeping\n", total); - usleep(1000000); - error = write(desc[1], &buffer[total], 3000); - total += error; - error = write(desc[1], &buffer[total], 3000); - total += error; - printf("Wrote another 6000 bytes, %d total, done\n", total); -} else { - usleep(500000); - error = read(desc[0], &buffer2, 8192); - total += error; - printf("Read %d bytes, going back to sleep\n", error); - usleep(1000000); - error = read(desc[0], &buffer2[total], 16384); - total += error; - printf("Read %d bytes, done\n", error); - - for (i = 0; i < total; i++) { - if (buffer[i] != buffer2[i]) { - buggy = 1; - printf("Location %d input: %hhx output: %hhx\n", - i, buffer[i], buffer2[i]); - } - } - -if (buggy) - printf("FAILURE\n"); -else - printf("SUCCESS\n"); -} - - -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-wraparound.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/bigpipetest.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/bigpipetest.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/bigpipetest.c (nonexistent) @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define BIG_PIPE_SIZE 64*1024 /* From sys/pipe.h */ - -/* - * Test for the non-blocking big pipe bug (write(2) returning - * EAGAIN while select(2) returns the descriptor as ready for write). - * - * $FreeBSD$ - */ - -void write_frame(int fd, char *buf, unsigned long buflen) -{ - fd_set wfd; - int i; - - while (buflen) { - FD_ZERO(&wfd); - FD_SET(fd, &wfd); - i = select(fd+1, NULL, &wfd, NULL, NULL); - if (i < 0) { - perror("select"); - exit(1); - } - if (i != 1) { - fprintf(stderr, "select returned unexpected value %d\n", i); - exit(1); - } - i = write(fd, buf, buflen); - if (i < 0) { - if (errno != EAGAIN) - perror("write"); - exit(1); - } - buf += i; - buflen -= i; - } -} - -int main() -{ - char buf[BIG_PIPE_SIZE]; /* any value over PIPE_SIZE should do */ - int i, flags, fd[2]; - - printf("1..1\n"); - - if (pipe(fd) < 0) { perror("pipe"); exit(1); } - - flags = fcntl(fd[1], F_GETFL); - if (flags == -1 || fcntl(fd[1], F_SETFL, flags|O_NONBLOCK) == -1) { - perror("fcntl"); - exit(1); - } - - switch (fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - close(fd[1]); - for (;;) { - i = read(fd[0], buf, 256); /* any small size should do */ - if (i == 0) break; - if (i < 0) { perror("read"); exit(1); } - } - exit(0); - default: - break; - } - - close(fd[0]); - memset(buf, 0, sizeof buf); - for (i = 0; i < 1000; i++) write_frame(fd[1], buf, sizeof buf); - printf("ok 1\n"); - exit(0); -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/bigpipetest.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/bigpipetest.t =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/bigpipetest.t (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/bigpipetest.t (nonexistent) @@ -1,10 +0,0 @@ -#!/bin/sh -# $FreeBSD$ - -cd `dirname $0` - -executable=`basename $0 .t` - -make $executable 2>&1 > /dev/null - -exec ./$executable Property changes on: user/ngie/more-tests2/tools/regression/pipe/bigpipetest.t ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-reverse.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-reverse.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-reverse.c (nonexistent) @@ -1,113 +0,0 @@ -/* -Copyright (C) 2004 Michael J. Silbersack. 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 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 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 -#include -#include - -/* - * $FreeBSD$ - * This program simply tests writing through the reverse direction of - * a pipe. Nothing too fancy, it's only needed because most pipe-using - * programs never touch the reverse direction (it doesn't exist on - * Linux.) - */ - -int main (void) -{ -char buffer[65535], buffer2[65535]; -int desc[2]; -int buggy, error, i, successes, total; -struct stat status; -pid_t new_pid; - -buggy = 0; -total = 0; - -error = pipe(desc); - -if (error) - err(0, "Couldn't allocate fds\n"); - -buffer[0] = 'A'; - -for (i = 1; i < 65535; i++) { - buffer[i] = buffer[i - 1] + 1; - if (buffer[i] > 'Z') - buffer[i] = 'A'; - } - -new_pid = fork(); - -if (new_pid == 0) { - error = write(desc[0], &buffer, 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - printf("Wrote %d bytes, sleeping\n", total); - usleep(1000000); - error = write(desc[0], &buffer[total], 4096); - total += error; - error = write(desc[0], &buffer[total], 4096); - total += error; - printf("Wrote another 8192 bytes, %d total, done\n", total); -} else { - usleep(500000); - error = read(desc[1], &buffer2, 32768); - total += error; - printf("Read %d bytes, going back to sleep\n", error); - usleep(1000000); - error = read(desc[1], &buffer2[total], 8192); - total += error; - printf("Read %d bytes, done\n", error); - - for (i = 0; i < total; i++) { - if (buffer[i] != buffer2[i]) { - buggy = 1; - printf("Location %d input: %hhx output: %hhx\n", - i, buffer[i], buffer2[i]); - } - } - -if ((buggy == 1) || (total != 40960)) - printf("FAILURE\n"); -else - printf("SUCCESS\n"); - -} - -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-reverse.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-fstatbug.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-fstatbug.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-fstatbug.c (nonexistent) @@ -1,82 +0,0 @@ -/* -Copyright (C) 2004 Michael J. Silbersack. 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 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 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 -#include -#include - -/* - * $FreeBSD$ - * The goal of this program is to see if fstat reports the correct - * data count for a pipe. Prior to revision 1.172 of sys_pipe.c, - * 0 would be returned once the pipe entered direct write mode. - * - * Linux (2.6) always returns zero, so it's not a valuable platform - * for comparison. - */ - -int main (void) -{ -char buffer[32768], buffer2[32768]; -int desc[2]; -int error, successes = 0; -struct stat status; -pid_t new_pid; - -error = pipe(desc); - -if (error) - err(0, "Couldn't allocate fds\n"); - -new_pid = fork(); - -if (new_pid == 0) { - write(desc[1], &buffer, 145); - usleep(1000000); - write(desc[1], &buffer, 2048); - usleep(1000000); - write(desc[1], &buffer, 4096); - usleep(1000000); - write(desc[1], &buffer, 8191); - usleep(1000000); - write(desc[1], &buffer, 8192); - usleep(1000000); -} else { - while (successes < 5) { - usleep(3000); - fstat(desc[0], &status); - error = read(desc[0], &buffer2, 32768); - if (status.st_size != error) - err(0, "FAILURE: stat size %d read size %d\n", (int)status.st_size, error); - if (error > 0) { - printf("SUCCESS at stat size %d read size %d\n", (int)status.st_size, error); - successes++; - /* Sleep to avoid the natural race in reading st_size. */ - usleep(1000000); - } - } -} - -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-fstatbug.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-reverse2.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-reverse2.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-reverse2.c (nonexistent) @@ -1,67 +0,0 @@ -/*- - * Copyright (c) 2010 Jilles Tjoelker - * 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. - * - * $FreeBSD$ - */ - -#include - -#include -#include -#include - -/* - * Check that pipes can be selected for writing in the reverse direction. - */ -int -main(int argc, char *argv[]) -{ - int pip[2]; - fd_set set; - int n; - - if (pipe(pip) == -1) - err(1, "FAIL: pipe"); - - FD_ZERO(&set); - FD_SET(pip[0], &set); - n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 }); - if (n != 1) - errx(1, "FAIL: select initial reverse direction"); - - n = write(pip[0], "x", 1); - if (n != 1) - err(1, "FAIL: write reverse direction"); - - FD_ZERO(&set); - FD_SET(pip[0], &set); - n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 }); - if (n != 1) - errx(1, "FAIL: select reverse direction after write"); - - printf("PASS\n"); - - return (0); -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-reverse2.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/pipe-ino.c =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/pipe-ino.c (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/pipe-ino.c (nonexistent) @@ -1,66 +0,0 @@ -/*- - * Copyright (c) 2011 Giovanni Trematerra - * 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. - */ - -/* - * $FreeBSD$ - * Test conformance to stat(2) SUSv4 description: - * "For all other file types defined in this volume of POSIX.1-2008, the - * structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atim, - * st_ctim, and st_mtim shall have meaningful values ...". - * Check that st_dev and st_ino are meaningful. - */ - -#include -#include -#include -#include -#include - -int -main(int argc, char **argv) -{ - int pipefd[2]; - struct stat st1, st2; - - if (pipe(pipefd) == -1) - err(1, "FAIL: pipe"); - - if (fstat(pipefd[0], &st1) == -1) - err(1, "FAIL: fstat st1"); - if (fstat(pipefd[1], &st2) == -1) - err(1, "FAIL: fstat st2"); - if (st1.st_dev != st2.st_dev || st1.st_dev == 0 || st2.st_dev == 0) { - errx(1, "FAIL: wrong dev number %d %d", - st1.st_dev, st2.st_dev); - } - if (st1.st_ino == st2.st_ino) - errx(1, "FAIL: inode numbers are equal: %d", st1.st_ino); - close(pipefd[0]); - close(pipefd[1]); - printf("PASS\n"); - - return (0); -} Property changes on: user/ngie/more-tests2/tools/regression/pipe/pipe-ino.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/tools/regression/pipe/Makefile =================================================================== --- user/ngie/more-tests2/tools/regression/pipe/Makefile (revision 290915) +++ user/ngie/more-tests2/tools/regression/pipe/Makefile (nonexistent) @@ -1,16 +0,0 @@ -# -# $FreeBSD$ -# -# "make" then "make regress". -# -PROG= bigpipetest -MAN= - -regress: - @if ./bigpipetest; then \ - echo "PASS"; \ - else \ - echo "FAIL"; \ - fi - -.include Property changes on: user/ngie/more-tests2/tools/regression/pipe/Makefile ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: user/ngie/more-tests2/usr.sbin/ypbind/ypbind.c =================================================================== --- user/ngie/more-tests2/usr.sbin/ypbind/ypbind.c (revision 290915) +++ user/ngie/more-tests2/usr.sbin/ypbind/ypbind.c (revision 290916) @@ -1,1010 +1,1010 @@ /* * Copyright (c) 1992/3 Theo de Raadt * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. */ #include __FBSDID("$FreeBSD$"); #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 "yp_ping.h" #ifndef BINDINGDIR #define BINDINGDIR "/var/yp/binding" #endif #ifndef YPBINDLOCK #define YPBINDLOCK "/var/run/ypbind.lock" #endif struct _dom_binding { struct _dom_binding *dom_pnext; char dom_domain[YPMAXDOMAIN + 1]; struct sockaddr_in dom_server_addr; long int dom_vers; int dom_lockfd; int dom_alive; int dom_broadcast_pid; int dom_pipe_fds[2]; int dom_default; }; #define READFD ypdb->dom_pipe_fds[0] #define WRITEFD ypdb->dom_pipe_fds[1] #define BROADFD broad_domain->dom_pipe_fds[1] extern bool_t xdr_domainname(), xdr_ypbind_resp(); extern bool_t xdr_ypreq_key(), xdr_ypresp_val(); extern bool_t xdr_ypbind_setdom(); void checkwork(void); void *ypbindproc_null_2_yp(SVCXPRT *, void *, CLIENT *); void *ypbindproc_setdom_2_yp(SVCXPRT *, struct ypbind_setdom *, CLIENT *); void rpc_received(char *, struct sockaddr_in *, int); void broadcast(struct _dom_binding *); int ping(struct _dom_binding *); int tell_parent(char *, struct sockaddr_in *); void handle_children(struct _dom_binding *); void reaper(int); void terminate(int); void yp_restricted_mode(char *); int verify(struct in_addr); static char *domain_name; static struct _dom_binding *ypbindlist; static struct _dom_binding *broad_domain; #define YPSET_NO 0 #define YPSET_LOCAL 1 #define YPSET_ALL 2 static int ypsetmode = YPSET_NO; static int ypsecuremode = 0; static int ppid; #define NOT_RESPONDING_HYSTERESIS 10 static int not_responding_count = 0; /* * Special restricted mode variables: when in restricted mode, only the * specified restricted_domain will be bound, and only the servers listed * in restricted_addrs will be used for binding. */ #define RESTRICTED_SERVERS 10 static int yp_restricted = 0; static int yp_manycast = 0; static struct in_addr restricted_addrs[RESTRICTED_SERVERS]; /* No more than MAX_CHILDREN child broadcasters at a time. */ #ifndef MAX_CHILDREN #define MAX_CHILDREN 5 #endif /* No more than MAX_DOMAINS simultaneous domains */ #ifndef MAX_DOMAINS #define MAX_DOMAINS 200 #endif /* RPC timeout value */ #ifndef FAIL_THRESHOLD #define FAIL_THRESHOLD 20 #endif /* Number of times to fish for a response froma particular set of hosts */ #ifndef MAX_RETRIES #define MAX_RETRIES 30 #endif static int retries = 0; static int children = 0; static int domains = 0; static int yplockfd; static fd_set fdsr; static SVCXPRT *udptransp, *tcptransp; void * ypbindproc_null_2_yp(SVCXPRT *transp, void *argp, CLIENT *clnt) { static char res; bzero(&res, sizeof(res)); return &res; } static struct ypbind_resp * ypbindproc_domain_2_yp(SVCXPRT *transp, domainname *argp, CLIENT *clnt) { static struct ypbind_resp res; struct _dom_binding *ypdb; char path[MAXPATHLEN]; bzero(&res, sizeof res); res.ypbind_status = YPBIND_FAIL_VAL; res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV; if (strchr(*argp, '/')) { syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \ rejecting.", *argp); return(&res); } for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) { if (strcmp(ypdb->dom_domain, *argp) == 0) break; } if (ypdb == NULL) { if (yp_restricted) { syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp); return (&res); } if (domains >= MAX_DOMAINS) { syslog(LOG_WARNING, "domain limit (%d) exceeded", MAX_DOMAINS); res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC; return (&res); } - ypdb = (struct _dom_binding *)malloc(sizeof *ypdb); + ypdb = malloc(sizeof *ypdb); if (ypdb == NULL) { syslog(LOG_WARNING, "malloc: %m"); res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC; return (&res); } bzero(ypdb, sizeof *ypdb); strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain); ypdb->dom_vers = YPVERS; ypdb->dom_alive = 0; ypdb->dom_default = 0; ypdb->dom_lockfd = -1; sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers); unlink(path); ypdb->dom_pnext = ypbindlist; ypbindlist = ypdb; domains++; } if (ping(ypdb)) { return (&res); } res.ypbind_status = YPBIND_SUCC_VAL; res.ypbind_resp_u.ypbind_error = 0; /* Success */ memcpy(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr, &ypdb->dom_server_addr.sin_addr.s_addr, sizeof(u_int32_t)); memcpy(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port, &ypdb->dom_server_addr.sin_port, sizeof(u_short)); /*printf("domain %s at %s/%d\n", ypdb->dom_domain, inet_ntoa(ypdb->dom_server_addr.sin_addr), ntohs(ypdb->dom_server_addr.sin_port));*/ return (&res); } void * ypbindproc_setdom_2_yp(SVCXPRT *transp, ypbind_setdom *argp, CLIENT *clnt) { struct sockaddr_in *fromsin, bindsin; static char *result = NULL; if (strchr(argp->ypsetdom_domain, '/')) { syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \ rejecting.", argp->ypsetdom_domain); return(NULL); } fromsin = svc_getcaller(transp); switch (ypsetmode) { case YPSET_LOCAL: if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { svcerr_noprog(transp); return(NULL); } break; case YPSET_ALL: break; case YPSET_NO: default: svcerr_noprog(transp); return(NULL); } if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) { svcerr_noprog(transp); return(NULL); } if (argp->ypsetdom_vers != YPVERS) { svcerr_noprog(transp); return(NULL); } bzero(&bindsin, sizeof bindsin); bindsin.sin_family = AF_INET; memcpy(&bindsin.sin_addr.s_addr, &argp->ypsetdom_binding.ypbind_binding_addr, sizeof(u_int32_t)); memcpy(&bindsin.sin_port, &argp->ypsetdom_binding.ypbind_binding_port, sizeof(u_short)); rpc_received(argp->ypsetdom_domain, &bindsin, 1); return((void *) &result); } void ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp) { union { domainname ypbindproc_domain_2_arg; struct ypbind_setdom ypbindproc_setdom_2_arg; } argument; struct authunix_parms *creds; char *result; bool_t (*xdr_argument)(), (*xdr_result)(); char *(*local)(); switch (rqstp->rq_proc) { case YPBINDPROC_NULL: xdr_argument = xdr_void; xdr_result = xdr_void; local = (char *(*)()) ypbindproc_null_2_yp; break; case YPBINDPROC_DOMAIN: xdr_argument = xdr_domainname; xdr_result = xdr_ypbind_resp; local = (char *(*)()) ypbindproc_domain_2_yp; break; case YPBINDPROC_SETDOM: switch (rqstp->rq_cred.oa_flavor) { case AUTH_UNIX: creds = (struct authunix_parms *)rqstp->rq_clntcred; if (creds->aup_uid != 0) { svcerr_auth(transp, AUTH_BADCRED); return; } break; default: svcerr_auth(transp, AUTH_TOOWEAK); return; } xdr_argument = xdr_ypbind_setdom; xdr_result = xdr_void; local = (char *(*)()) ypbindproc_setdom_2_yp; break; default: svcerr_noproc(transp); return; } bzero(&argument, sizeof(argument)); if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) { svcerr_decode(transp); return; } result = (*local)(transp, &argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) { svcerr_systemerr(transp); } return; } /* Jack the reaper */ void reaper(int sig) { int st; while (wait3(&st, WNOHANG, NULL) > 0) children--; } void terminate(int sig) { struct _dom_binding *ypdb; char path[MAXPATHLEN]; if (ppid != getpid()) exit(0); for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) { close(ypdb->dom_lockfd); if (ypdb->dom_broadcast_pid) kill(ypdb->dom_broadcast_pid, SIGINT); sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers); unlink(path); } close(yplockfd); unlink(YPBINDLOCK); pmap_unset(YPBINDPROG, YPBINDVERS); exit(0); } int main(int argc, char *argv[]) { struct timeval tv; int i; DIR *dird; struct dirent *dirp; struct _dom_binding *ypdb, *next; /* Check that another ypbind isn't already running. */ if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) err(1, "%s", YPBINDLOCK); if (flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) errx(1, "another ypbind is already running. Aborting"); /* XXX domainname will be overridden if we use restricted mode */ yp_get_default_domain(&domain_name); if (domain_name[0] == '\0') errx(1, "domainname not set. Aborting"); for (i = 1; i i) yp_restricted_mode(argv[++i]); else if (strcmp("-m", argv[i]) == 0) yp_manycast++; else errx(1, "unknown option: %s", argv[i]); } /* blow away everything in BINDINGDIR (if it exists) */ if ((dird = opendir(BINDINGDIR)) != NULL) { char path[MAXPATHLEN]; while ((dirp = readdir(dird)) != NULL) if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name); unlink(path); } closedir(dird); } #ifdef DAEMON if (daemon(0,0)) err(1, "fork"); #endif pmap_unset(YPBINDPROG, YPBINDVERS); udptransp = svcudp_create(RPC_ANYSOCK); if (udptransp == NULL) errx(1, "cannot create udp service"); if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, IPPROTO_UDP)) errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)"); tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0); if (tcptransp == NULL) errx(1, "cannot create tcp service"); if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, IPPROTO_TCP)) errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)"); /* build initial domain binding, make it "unsuccessful" */ - ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist); + ypbindlist = malloc(sizeof *ypbindlist); if (ypbindlist == NULL) errx(1, "malloc"); bzero(ypbindlist, sizeof *ypbindlist); strncpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain); ypbindlist->dom_vers = YPVERS; ypbindlist->dom_alive = 0; ypbindlist->dom_lockfd = -1; ypbindlist->dom_default = 1; domains++; signal(SIGCHLD, reaper); signal(SIGTERM, terminate); ppid = getpid(); /* Remember who we are. */ openlog(argv[0], LOG_PID, LOG_DAEMON); if (madvise(NULL, 0, MADV_PROTECT) != 0) syslog(LOG_WARNING, "madvise(): %m"); /* Kick off the default domain */ broadcast(ypbindlist); while (1) { fdsr = svc_fdset; tv.tv_sec = 60; tv.tv_usec = 0; switch (select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) { case 0: checkwork(); break; case -1: if (errno != EINTR) syslog(LOG_WARNING, "select: %m"); break; default: for (ypdb = ypbindlist; ypdb; ypdb = next) { next = ypdb->dom_pnext; if (READFD > 0 && FD_ISSET(READFD, &fdsr)) { handle_children(ypdb); if (children == (MAX_CHILDREN - 1)) checkwork(); } } svc_getreqset(&fdsr); break; } } /* NOTREACHED */ exit(1); } void checkwork(void) { struct _dom_binding *ypdb; for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) ping(ypdb); } /* The clnt_broadcast() callback mechanism sucks. */ /* * Receive results from broadcaster. Don't worry about passing * bogus info to rpc_received() -- it can handle it. Note that we * must be sure to invalidate the dom_pipe_fds descriptors here: * since descriptors can be re-used, we have to make sure we * don't mistake one of the RPC descriptors for one of the pipes. * What's weird is that forgetting to invalidate the pipe descriptors * doesn't always result in an error (otherwise I would have caught * the mistake much sooner), even though logically it should. */ void handle_children(struct _dom_binding *ypdb) { char buf[YPMAXDOMAIN + 1]; struct sockaddr_in addr; int d = 0, a = 0; struct _dom_binding *y, *prev = NULL; char path[MAXPATHLEN]; if ((d = read(READFD, &buf, sizeof(buf))) <= 0) syslog(LOG_WARNING, "could not read from child: %m"); if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0) syslog(LOG_WARNING, "could not read from child: %m"); close(READFD); FD_CLR(READFD, &fdsr); FD_CLR(READFD, &svc_fdset); READFD = WRITEFD = -1; if (d > 0 && a > 0) rpc_received(buf, &addr, 0); else { for (y = ypbindlist; y; y = y->dom_pnext) { if (y == ypdb) break; prev = y; } switch (ypdb->dom_default) { case 0: if (prev == NULL) ypbindlist = y->dom_pnext; else prev->dom_pnext = y->dom_pnext; sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, YPVERS); close(ypdb->dom_lockfd); unlink(path); free(ypdb); domains--; return; case 1: ypdb->dom_broadcast_pid = 0; ypdb->dom_alive = 0; broadcast(ypdb); return; default: break; } } return; } /* * Send our dying words back to our parent before we perish. */ int tell_parent(char *dom, struct sockaddr_in *addr) { char buf[YPMAXDOMAIN + 1]; struct timeval timeout; fd_set fds; timeout.tv_sec = 5; timeout.tv_usec = 0; sprintf(buf, "%s", broad_domain->dom_domain); if (write(BROADFD, &buf, sizeof(buf)) < 0) return(1); /* * Stay in sync with parent: wait for it to read our first * message before sending the second. */ FD_ZERO(&fds); FD_SET(BROADFD, &fds); if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1) return(1); if (FD_ISSET(BROADFD, &fds)) { if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0) return(1); } else { return(1); } close(BROADFD); return (0); } static bool_t broadcast_result(bool_t *out, struct sockaddr_in *addr) { if (retries >= MAX_RETRIES) { bzero(addr, sizeof(struct sockaddr_in)); if (tell_parent(broad_domain->dom_domain, addr)) syslog(LOG_WARNING, "lost connection to parent"); return (TRUE); } if (yp_restricted && verify(addr->sin_addr)) { retries++; syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr)); return (FALSE); } else { if (tell_parent(broad_domain->dom_domain, addr)) syslog(LOG_WARNING, "lost connection to parent"); return (TRUE); } } /* * The right way to send RPC broadcasts. * Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast() * blocks while waiting for replies, so we have to fork off separate * broadcaster processes that do the waiting and then transmit their * results back to the parent for processing. We also have to remember * to save the name of the domain we're trying to bind in a global * variable since clnt_broadcast() provides no way to pass things to * the 'eachresult' callback function. */ void broadcast(struct _dom_binding *ypdb) { bool_t out = FALSE; enum clnt_stat stat; if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid) return; if (pipe(ypdb->dom_pipe_fds) < 0) { syslog(LOG_WARNING, "pipe: %m"); return; } if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr) { if (not_responding_count++ >= NOT_RESPONDING_HYSTERESIS) { not_responding_count = NOT_RESPONDING_HYSTERESIS; syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding", inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain); } } broad_domain = ypdb; flock(ypdb->dom_lockfd, LOCK_UN); switch ((ypdb->dom_broadcast_pid = fork())) { case 0: close(READFD); signal(SIGCHLD, SIG_DFL); signal(SIGTERM, SIG_DFL); break; case -1: syslog(LOG_WARNING, "fork: %m"); close(READFD); close(WRITEFD); return; default: close(WRITEFD); FD_SET(READFD, &svc_fdset); children++; return; } /* Release all locks before doing anything else. */ while (ypbindlist) { close(ypbindlist->dom_lockfd); ypbindlist = ypbindlist->dom_pnext; } close(yplockfd); /* * Special 'many-cast' behavior. If we're in restricted mode, * we have a list of possible server addresses to try. What * we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK * procedure and time the replies. Whoever replies fastest * gets to be our server. Note that this is not a broadcast * operation: we transmit uni-cast datagrams only. */ if (yp_restricted && yp_manycast) { short port; int i; struct sockaddr_in sin; i = __yp_ping(restricted_addrs, yp_restricted, ypdb->dom_domain, &port); if (i == -1) { bzero(&ypdb->dom_server_addr, sizeof(struct sockaddr_in)); if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr)) syslog(LOG_WARNING, "lost connection to parent"); } else { bzero(&sin, sizeof(struct sockaddr_in)); bcopy(&restricted_addrs[i], &sin.sin_addr, sizeof(struct in_addr)); sin.sin_family = AF_INET; sin.sin_port = port; if (tell_parent(broad_domain->dom_domain, &sin)) syslog(LOG_WARNING, "lost connection to parent"); } _exit(0); } retries = 0; { char *ptr; ptr = ypdb->dom_domain; stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK, (xdrproc_t)xdr_domainname, &ptr, (xdrproc_t)xdr_bool, &out, (resultproc_t)broadcast_result); } if (stat != RPC_SUCCESS) { bzero(&ypdb->dom_server_addr, sizeof(struct sockaddr_in)); if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr)) syslog(LOG_WARNING, "lost connection to parent"); } _exit(0); } /* * The right way to check if a server is alive. * Attempt to get a client handle pointing to the server and send a * YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE, * we invalidate this binding entry and send out a broadcast to try to * establish a new binding. Note that we treat non-default domains * specially: once bound, we keep tabs on our server, but if it * goes away and fails to respond after one round of broadcasting, we * abandon it until a client specifically references it again. We make * every effort to keep our default domain bound, however, since we * need it to keep the system on its feet. */ int ping(struct _dom_binding *ypdb) { bool_t out; struct timeval interval, timeout; enum clnt_stat stat; int rpcsock = RPC_ANYSOCK; CLIENT *client_handle; interval.tv_sec = FAIL_THRESHOLD; interval.tv_usec = 0; timeout.tv_sec = FAIL_THRESHOLD; timeout.tv_usec = 0; if (ypdb->dom_broadcast_pid) return(1); if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr, YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (CLIENT *)NULL) { /* Can't get a handle: we're dead. */ ypdb->dom_alive = 0; ypdb->dom_vers = -1; broadcast(ypdb); return(1); } { char *ptr; ptr = ypdb->dom_domain; stat = clnt_call(client_handle, YPPROC_DOMAIN, (xdrproc_t)xdr_domainname, &ptr, (xdrproc_t)xdr_bool, &out, timeout); if (stat != RPC_SUCCESS || out == FALSE) { ypdb->dom_alive = 0; ypdb->dom_vers = -1; clnt_destroy(client_handle); broadcast(ypdb); return(1); } } clnt_destroy(client_handle); return(0); } void rpc_received(char *dom, struct sockaddr_in *raddrp, int force) { struct _dom_binding *ypdb, *prev = NULL; struct iovec iov[2]; struct ypbind_resp ybr; char path[MAXPATHLEN]; int fd; /*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port), dom);*/ if (dom == NULL) return; for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) { if (strcmp(ypdb->dom_domain, dom) == 0) break; prev = ypdb; } if (ypdb && force) { if (ypdb->dom_broadcast_pid) { kill(ypdb->dom_broadcast_pid, SIGINT); close(READFD); FD_CLR(READFD, &fdsr); FD_CLR(READFD, &svc_fdset); READFD = WRITEFD = -1; } } /* if in secure mode, check originating port number */ if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) { syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.", inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port), dom); if (ypdb != NULL) { ypdb->dom_broadcast_pid = 0; ypdb->dom_alive = 0; } return; } if (raddrp->sin_addr.s_addr == (long)0) { switch (ypdb->dom_default) { case 0: if (prev == NULL) ypbindlist = ypdb->dom_pnext; else prev->dom_pnext = ypdb->dom_pnext; sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, YPVERS); close(ypdb->dom_lockfd); unlink(path); free(ypdb); domains--; return; case 1: ypdb->dom_broadcast_pid = 0; ypdb->dom_alive = 0; broadcast(ypdb); return; default: break; } } if (ypdb == NULL) { if (force == 0) return; - ypdb = (struct _dom_binding *)malloc(sizeof *ypdb); + ypdb = malloc(sizeof *ypdb); if (ypdb == NULL) { syslog(LOG_WARNING, "malloc: %m"); return; } bzero(ypdb, sizeof *ypdb); strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain); ypdb->dom_lockfd = -1; ypdb->dom_default = 0; ypdb->dom_pnext = ypbindlist; ypbindlist = ypdb; } /* We've recovered from a crash: inform the world. */ if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr) { if (not_responding_count >= NOT_RESPONDING_HYSTERESIS) { not_responding_count = 0; syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK", inet_ntoa(raddrp->sin_addr), ypdb->dom_domain); } } bcopy(raddrp, &ypdb->dom_server_addr, sizeof ypdb->dom_server_addr); ypdb->dom_vers = YPVERS; ypdb->dom_alive = 1; ypdb->dom_broadcast_pid = 0; if (ypdb->dom_lockfd != -1) close(ypdb->dom_lockfd); sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers); #ifdef O_SHLOCK if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) { (void)mkdir(BINDINGDIR, 0755); if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) return; } #else if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) { (void)mkdir(BINDINGDIR, 0755); if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) return; } flock(fd, LOCK_SH); #endif /* * ok, if BINDINGDIR exists, and we can create the binding file, * then write to it.. */ ypdb->dom_lockfd = fd; iov[0].iov_base = (char *)&(udptransp->xp_port); iov[0].iov_len = sizeof udptransp->xp_port; iov[1].iov_base = (char *)&ybr; iov[1].iov_len = sizeof ybr; bzero(&ybr, sizeof ybr); ybr.ypbind_status = YPBIND_SUCC_VAL; memcpy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr, &raddrp->sin_addr.s_addr, sizeof(u_int32_t)); memcpy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port, &raddrp->sin_port, sizeof(u_short)); if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) { syslog(LOG_WARNING, "write: %m"); close(ypdb->dom_lockfd); ypdb->dom_lockfd = -1; return; } } /* * Check address against list of allowed servers. Return 0 if okay, * 1 if not matched. */ int verify(struct in_addr addr) { int i; for (i = 0; i < RESTRICTED_SERVERS; i++) if (!bcmp(&addr, &restricted_addrs[i], sizeof(struct in_addr))) return(0); return(1); } /* * Try to set restricted mode. We default to normal mode if we can't * resolve the specified hostnames. */ void yp_restricted_mode(char *args) { struct hostent *h; int i = 0; char *s; /* Find the restricted domain. */ if ((s = strsep(&args, ",")) == NULL) return; domain_name = s; /* Get the addresses of the servers. */ while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) { if ((h = gethostbyname(s)) == NULL) return; bcopy (h->h_addr_list[0], &restricted_addrs[i], sizeof(struct in_addr)); i++; } /* ypset and ypsetme not allowed with restricted mode */ ypsetmode = YPSET_NO; yp_restricted = i; return; } Index: user/ngie/more-tests2 =================================================================== --- user/ngie/more-tests2 (revision 290915) +++ user/ngie/more-tests2 (revision 290916) Property changes on: user/ngie/more-tests2 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r290900-290915