1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <thread.h> 27 #include <stdlib.h> 28 #include <errno.h> 29 #include <strings.h> 30 #include <tiuser.h> 31 #include <syslog.h> 32 #include <zone.h> 33 #include <sys/priocntl.h> 34 #include <sys/fxpriocntl.h> 35 #include <nfs/nfs.h> 36 #include <nfs/nfssys.h> 37 #include "thrpool.h" 38 39 extern int _nfssys(int, void *); 40 41 /* 42 * Thread to call into the kernel and do work on behalf of NFS. 43 */ 44 static void * 45 svcstart(void *arg) 46 { 47 int id = (int)arg; 48 49 /* 50 * Create a kernel worker thread to service 51 * new incoming requests on a pool. 52 */ 53 _nfssys(SVCPOOL_RUN, &id); 54 55 /* 56 * Returned from the kernel, this thread's work is done, 57 * and it should exit. For new incoming requests, 58 * svcblock() will spawn another worker thread by 59 * calling svcstart() again. 60 */ 61 thr_exit(NULL); 62 return (NULL); 63 } 64 65 static void * 66 svc_rdma_creator(void *arg) 67 { 68 struct rdma_svc_args *rsap = (struct rdma_svc_args *)arg; 69 70 if (_nfssys(RDMA_SVC_INIT, rsap) < 0) { 71 if (errno != ENODEV) { 72 (void) syslog(LOG_INFO, "RDMA transport startup " 73 "failed with %m"); 74 } 75 } 76 free(rsap); 77 thr_exit(NULL); 78 return (NULL); 79 } 80 81 /* 82 * User-space "creator" thread. This thread blocks in the kernel 83 * until new worker threads need to be created for the service 84 * pool. On return to userspace, if there is no error, create a 85 * new thread for the service pool. 86 */ 87 static void * 88 svcblock(void *arg) 89 { 90 int id = (int)arg; 91 92 /* CONSTCOND */ 93 while (1) { 94 thread_t tid; 95 96 /* 97 * Call into the kernel, and hang out there 98 * until a thread needs to be created. 99 */ 100 if (_nfssys(SVCPOOL_WAIT, &id) < 0) { 101 if (errno == ECANCELED || errno == EINTR || 102 errno == EBUSY) 103 /* 104 * If we get back ECANCELED or EINTR, 105 * the service pool is exiting, and we 106 * may as well clean up this thread. If 107 * EBUSY is returned, there's already a 108 * thread looping on this pool, so we 109 * should give up. 110 */ 111 break; 112 else 113 continue; 114 } 115 116 /* 117 * User portion of the thread does no real work since 118 * the svcpool threads actually spend their entire 119 * lives in the kernel. So, user portion of the thread 120 * should have the smallest stack possible. 121 */ 122 (void) thr_create(NULL, THR_MIN_STACK, svcstart, (void *)id, 123 THR_BOUND | THR_DETACHED, &tid); 124 } 125 126 thr_exit(NULL); 127 return (NULL); 128 } 129 130 void 131 svcsetprio(void) 132 { 133 pcinfo_t pcinfo; 134 pri_t maxupri; 135 136 /* 137 * By default, all threads should be part of the FX scheduler 138 * class. As nfsd/lockd server threads used to be part of the 139 * kernel, they're used to being scheduled in the SYS class. 140 * Userland threads shouldn't be in SYS, but they can be given a 141 * higher priority by default. This change still renders nfsd/lockd 142 * managable by an admin by utilizing commands to change scheduling 143 * manually, or by using resource management tools such as pools 144 * to associate them with a different scheduling class and segregate 145 * the workload. 146 * 147 * We set the threads' priority to the upper bound for priorities 148 * in FX. This should be 60, but since the desired action is to 149 * make nfsd/lockd more important than TS threads, we bow to the 150 * system's knowledge rather than setting it manually. Furthermore, 151 * since the SYS class doesn't timeslice, use an "infinite" quantum. 152 * If anything fails, just log the failure and let the daemon 153 * default to TS. 154 * 155 * The change of scheduling class is expected to fail in a non-global 156 * zone, so we avoid worrying the zone administrator unnecessarily. 157 */ 158 (void) strcpy(pcinfo.pc_clname, "FX"); 159 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) != -1) { 160 maxupri = ((fxinfo_t *)pcinfo.pc_clinfo)->fx_maxupri; 161 if (priocntl(P_LWPID, P_MYID, PC_SETXPARMS, "FX", 162 FX_KY_UPRILIM, maxupri, FX_KY_UPRI, maxupri, 163 FX_KY_TQNSECS, FX_TQINF, NULL) != 0 && 164 getzoneid() == GLOBAL_ZONEID) 165 (void) syslog(LOG_ERR, "Unable to use FX scheduler: " 166 "%m. Using system default scheduler."); 167 } else 168 (void) syslog(LOG_ERR, "Unable to determine parameters " 169 "for FX scheduler. Using system default scheduler."); 170 } 171 172 int 173 svcrdma(int id, int versmin, int versmax, int delegation) 174 { 175 thread_t tid; 176 struct rdma_svc_args *rsa; 177 178 rsa = (struct rdma_svc_args *)malloc(sizeof (struct rdma_svc_args)); 179 rsa->poolid = (uint32_t)id; 180 rsa->netid = NULL; 181 rsa->nfs_versmin = versmin; 182 rsa->nfs_versmax = versmax; 183 rsa->delegation = delegation; 184 185 /* 186 * Create a thread to handle RDMA start and stop. 187 */ 188 if (thr_create(NULL, THR_MIN_STACK * 2, svc_rdma_creator, (void *)rsa, 189 THR_BOUND | THR_DETACHED, &tid)) 190 return (1); 191 192 return (0); 193 } 194 195 int 196 svcwait(int id) 197 { 198 thread_t tid; 199 200 /* 201 * Create a bound thread to wait for kernel LWPs that 202 * need to be created. This thread also has little need 203 * of stackspace, so should be created with that in mind. 204 */ 205 if (thr_create(NULL, THR_MIN_STACK * 2, svcblock, (void *)id, 206 THR_BOUND | THR_DETACHED, &tid)) 207 return (1); 208 209 return (0); 210 } 211