1 /*
   2  * Copyright (c) 2007-2008
   3  *      Swinburne University of Technology, Melbourne, Australia.
   4  * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org>
   5  * Copyright (c) 2010 The FreeBSD Foundation
   6  * All rights reserved.
   7  * Copyright (c) 2017 by Delphix. All rights reserved.
   8  *
   9  * This software was developed at the Centre for Advanced Internet
  10  * Architectures, Swinburne University of Technology, by Lawrence Stewart and
  11  * James Healy, made possible in part by a grant from the Cisco University
  12  * Research Program Fund at Community Foundation Silicon Valley.
  13  *
  14  * Portions of this software were developed at the Centre for Advanced
  15  * Internet Architectures, Swinburne University of Technology, Melbourne,
  16  * Australia by David Hayes under sponsorship from the FreeBSD Foundation.
  17  *
  18  * Redistribution and use in source and binary forms, with or without
  19  * modification, are permitted provided that the following conditions
  20  * are met:
  21  * 1. Redistributions of source code must retain the above copyright
  22  *    notice, this list of conditions and the following disclaimer.
  23  * 2. Redistributions in binary form must reproduce the above copyright
  24  *    notice, this list of conditions and the following disclaimer in the
  25  *    documentation and/or other materials provided with the distribution.
  26  *
  27  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  30  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  37  * SUCH DAMAGE.
  38  */
  39 
  40 /*
  41  * This software was first released in 2007 by James Healy and Lawrence Stewart
  42  * whilst working on the NewTCP research project at Swinburne University of
  43  * Technology's Centre for Advanced Internet Architectures, Melbourne,
  44  * Australia, which was made possible in part by a grant from the Cisco
  45  * University Research Program Fund at Community Foundation Silicon Valley.
  46  * More details are available at:
  47  *   http://caia.swin.edu.au/urp/newtcp/
  48  */
  49 
  50 #include <sys/param.h>
  51 #include <sys/errno.h>
  52 #include <sys/systm.h>
  53 #include <sys/queue.h>
  54 #include <inet/cc.h>
  55 #include <inet/tcp.h>
  56 #include <sys/sdt.h>
  57 
  58 #define CC_KMODDIR      "cc"
  59 
  60 /*
  61  * List of available cc algorithms on the current system. Access is
  62  * synchronized using cc_list_lock.
  63  */
  64 static STAILQ_HEAD(cc_head, cc_algo) cc_list = STAILQ_HEAD_INITIALIZER(cc_list);
  65 static kmutex_t cc_list_lock;
  66 
  67 /*
  68  * Initialise CC subsystem on system boot.
  69  */
  70 void
  71 cc_init(void)
  72 {
  73         STAILQ_INIT(&cc_list);
  74 }
  75 
  76 struct cc_algo *
  77 cc_load_algo(const char *name)
  78 {
  79         struct cc_algo *algo;
  80         boolean_t tried_modload = B_FALSE, found = B_FALSE;
  81         char modname[CC_ALGO_NAME_MAX + 3] = "cc_"; /* cc_<name> */
  82 
  83 find_registered_algo:
  84         mutex_enter(&cc_list_lock);
  85         STAILQ_FOREACH(algo, &cc_list, entries) {
  86                 if (strncmp(algo->name, name, CC_ALGO_NAME_MAX) == 0) {
  87                         found = B_TRUE;
  88                         break;
  89                 }
  90         }
  91         mutex_exit(&cc_list_lock);
  92 
  93         if (!found) {
  94                 algo = NULL;
  95                 if (!tried_modload) {
  96                         /*
  97                          * If the module has not yet been loaded, then attempt
  98                          * to load it now.  If modload() succeeds, the
  99                          * algorithm should have registered using
 100                          * cc_register_algo(), in which case we can go back and
 101                          * attempt to find it again.
 102                          */
 103                         (void) strlcat(modname, name, sizeof (modname));
 104                         if (modload(CC_KMODDIR, modname) == -1) {
 105                                 cmn_err(CE_WARN, "failed to load TCP congestion"
 106                                     "control algorithm: %s", modname);
 107                         } else {
 108                                 tried_modload = B_TRUE;
 109                                 goto find_registered_algo;
 110                         }
 111                 }
 112         }
 113 
 114         return (algo);
 115 }
 116 
 117 /*
 118  * Returns non-zero on success, 0 on failure.
 119  */
 120 int
 121 cc_deregister_algo(struct cc_algo *remove_cc)
 122 {
 123         struct cc_algo *funcs, *tmpfuncs;
 124         int err = ENOENT;
 125 
 126         mutex_enter(&cc_list_lock);
 127         STAILQ_FOREACH_SAFE(funcs, &cc_list, entries, tmpfuncs) {
 128                 if (funcs == remove_cc) {
 129                         STAILQ_REMOVE(&cc_list, funcs, cc_algo, entries);
 130                         err = 0;
 131                         break;
 132                 }
 133         }
 134         mutex_exit(&cc_list_lock);
 135         return (err);
 136 }
 137 
 138 /*
 139  * Returns 0 on success, non-zero on failure.
 140  */
 141 int
 142 cc_register_algo(struct cc_algo *add_cc)
 143 {
 144         struct cc_algo *funcs;
 145         int err = 0;
 146 
 147         /*
 148          * Iterate over list of registered CC algorithms and make sure
 149          * we're not trying to add a duplicate.
 150          */
 151         mutex_enter(&cc_list_lock);
 152         STAILQ_FOREACH(funcs, &cc_list, entries) {
 153                 if (strncmp(funcs->name, add_cc->name, CC_ALGO_NAME_MAX) == 0)
 154                         err = EEXIST;
 155         }
 156 
 157         if (err == 0)
 158                 STAILQ_INSERT_TAIL(&cc_list, add_cc, entries);
 159 
 160         mutex_exit(&cc_list_lock);
 161 
 162         return (err);
 163 }