1 /* 2 * Copyright (c) 2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/kernel.h> 38 #include <sys/lock.h> 39 #include <sys/malloc.h> 40 #include <sys/module.h> 41 #include <sys/sensors.h> 42 #include <sys/sysctl.h> 43 #include <sys/systm.h> 44 45 #include <dev/misc/dimm/dimm.h> 46 47 #define DIMM_TEMP_HIWAT_DEFAULT 85 48 #define DIMM_TEMP_LOWAT_DEFAULT 75 49 50 struct dimm_softc { 51 TAILQ_ENTRY(dimm_softc) dimm_link; 52 int dimm_node; 53 int dimm_chan; 54 int dimm_slot; 55 int dimm_temp_hiwat; 56 int dimm_temp_lowat; 57 int dimm_id; 58 int dimm_ref; 59 60 struct ksensordev dimm_sensdev; 61 uint32_t dimm_sens_taskflags; /* DIMM_SENS_TF_ */ 62 63 struct sysctl_ctx_list dimm_sysctl_ctx; 64 struct sysctl_oid *dimm_sysctl_tree; 65 }; 66 TAILQ_HEAD(dimm_softc_list, dimm_softc); 67 68 #define DIMM_SENS_TF_TEMP_CRIT 0x1 69 70 static void dimm_mod_unload(void); 71 72 /* In the ascending order of dimm_softc.dimm_id */ 73 static struct dimm_softc_list dimm_softc_list; 74 75 static SYSCTL_NODE(_hw, OID_AUTO, dimminfo, CTLFLAG_RD, NULL, 76 "DIMM information"); 77 78 struct dimm_softc * 79 dimm_create(int node, int chan, int slot) 80 { 81 struct dimm_softc *sc, *after = NULL; 82 int dimm_id = 0; 83 84 SYSCTL_XLOCK(); 85 86 TAILQ_FOREACH(sc, &dimm_softc_list, dimm_link) { 87 /* 88 * Already exists; done. 89 */ 90 if (sc->dimm_node == node && sc->dimm_chan == chan && 91 sc->dimm_slot == slot) { 92 KASSERT(sc->dimm_ref > 0, ("invalid dimm reference %d", 93 sc->dimm_ref)); 94 sc->dimm_ref++; 95 SYSCTL_XUNLOCK(); 96 return sc; 97 } 98 99 /* 100 * Find the lowest usable id. 101 */ 102 if (sc->dimm_id == dimm_id) { 103 ++dimm_id; 104 after = sc; 105 } 106 } 107 108 sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 109 sc->dimm_node = node; 110 sc->dimm_chan = chan; 111 sc->dimm_slot = slot; 112 sc->dimm_id = dimm_id; 113 sc->dimm_ref = 1; 114 sc->dimm_temp_hiwat = DIMM_TEMP_HIWAT_DEFAULT; 115 sc->dimm_temp_lowat = DIMM_TEMP_LOWAT_DEFAULT; 116 117 ksnprintf(sc->dimm_sensdev.xname, sizeof(sc->dimm_sensdev.xname), 118 "dimm%d", sc->dimm_id); 119 120 /* 121 * Create sysctl tree for the location information. Use 122 * same name as the sensor device. 123 */ 124 sysctl_ctx_init(&sc->dimm_sysctl_ctx); 125 sc->dimm_sysctl_tree = SYSCTL_ADD_NODE(&sc->dimm_sysctl_ctx, 126 SYSCTL_STATIC_CHILDREN(_hw_dimminfo), OID_AUTO, 127 sc->dimm_sensdev.xname, CTLFLAG_RD, 0, ""); 128 if (sc->dimm_sysctl_tree != NULL) { 129 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx, 130 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO, 131 "node", CTLFLAG_RD, &sc->dimm_node, 0, 132 "CPU node of this DIMM"); 133 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx, 134 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO, 135 "chan", CTLFLAG_RD, &sc->dimm_chan, 0, 136 "channel of this DIMM"); 137 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx, 138 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO, 139 "slot", CTLFLAG_RD, &sc->dimm_slot, 0, 140 "slot of this DIMM"); 141 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx, 142 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO, 143 "temp_hiwat", CTLFLAG_RW, &sc->dimm_temp_hiwat, 0, 144 "Raise alarm once DIMM temperature is above this value " 145 "(unit: C)"); 146 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx, 147 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO, 148 "temp_lowat", CTLFLAG_RW, &sc->dimm_temp_lowat, 0, 149 "Cancel alarm once DIMM temperature is below this value " 150 "(unit: C)"); 151 } 152 153 if (after == NULL) { 154 KKASSERT(sc->dimm_id == 0); 155 TAILQ_INSERT_HEAD(&dimm_softc_list, sc, dimm_link); 156 } else { 157 TAILQ_INSERT_AFTER(&dimm_softc_list, after, sc, dimm_link); 158 } 159 160 sensordev_install(&sc->dimm_sensdev); 161 162 SYSCTL_XUNLOCK(); 163 return sc; 164 } 165 166 int 167 dimm_destroy(struct dimm_softc *sc) 168 { 169 SYSCTL_XLOCK(); 170 171 KASSERT(sc->dimm_ref > 0, ("invalid dimm reference %d", sc->dimm_ref)); 172 sc->dimm_ref--; 173 if (sc->dimm_ref > 0) { 174 SYSCTL_XUNLOCK(); 175 return EAGAIN; 176 } 177 178 sensordev_deinstall(&sc->dimm_sensdev); 179 180 TAILQ_REMOVE(&dimm_softc_list, sc, dimm_link); 181 if (sc->dimm_sysctl_tree != NULL) 182 sysctl_ctx_free(&sc->dimm_sysctl_ctx); 183 kfree(sc, M_DEVBUF); 184 185 SYSCTL_XUNLOCK(); 186 return 0; 187 } 188 189 void 190 dimm_sensor_attach(struct dimm_softc *sc, struct ksensor *sens) 191 { 192 sensor_attach(&sc->dimm_sensdev, sens); 193 } 194 195 void 196 dimm_sensor_detach(struct dimm_softc *sc, struct ksensor *sens) 197 { 198 sensor_detach(&sc->dimm_sensdev, sens); 199 } 200 201 void 202 dimm_set_temp_thresh(struct dimm_softc *sc, int hiwat, int lowat) 203 { 204 sc->dimm_temp_hiwat = hiwat; 205 sc->dimm_temp_lowat = lowat; 206 } 207 208 void 209 dimm_sensor_temp(struct dimm_softc *sc, struct ksensor *sens, int temp) 210 { 211 if (temp >= sc->dimm_temp_hiwat && 212 (sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT) == 0) { 213 char temp_str[16], data[64]; 214 215 ksnprintf(temp_str, sizeof(temp_str), "%d", temp); 216 ksnprintf(data, sizeof(data), "node=%d channel=%d dimm=%d", 217 sc->dimm_node, sc->dimm_chan, sc->dimm_slot); 218 devctl_notify("memtemp", "Thermal", temp_str, data); 219 220 kprintf("dimm%d: node%d channel%d DIMM%d " 221 "temperature (%dC) is too high (>= %dC)\n", 222 sc->dimm_id, sc->dimm_node, sc->dimm_chan, sc->dimm_slot, 223 temp, sc->dimm_temp_hiwat); 224 225 sc->dimm_sens_taskflags |= DIMM_SENS_TF_TEMP_CRIT; 226 } else if ((sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT) && 227 temp < sc->dimm_temp_lowat) { 228 sc->dimm_sens_taskflags &= ~DIMM_SENS_TF_TEMP_CRIT; 229 } 230 231 if (sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT) 232 sens->status = SENSOR_S_CRIT; 233 else 234 sens->status = SENSOR_S_OK; 235 sens->flags &= ~SENSOR_FINVALID; 236 sens->value = (temp * 1000000) + 273150000; 237 } 238 239 static void 240 dimm_mod_unload(void) 241 { 242 struct dimm_softc *sc; 243 244 SYSCTL_XLOCK(); 245 246 while ((sc = TAILQ_FIRST(&dimm_softc_list)) != NULL) { 247 int error; 248 249 error = dimm_destroy(sc); 250 KASSERT(!error, ("dimm%d is still referenced, ref %d", 251 sc->dimm_id, sc->dimm_ref)); 252 } 253 254 SYSCTL_XUNLOCK(); 255 } 256 257 static int 258 dimm_mod_event(module_t mod, int type, void *unused) 259 { 260 switch (type) { 261 case MOD_LOAD: 262 TAILQ_INIT(&dimm_softc_list); 263 return 0; 264 265 case MOD_UNLOAD: 266 dimm_mod_unload(); 267 return 0; 268 269 default: 270 return 0; 271 } 272 } 273 274 static moduledata_t dimm_mod = { 275 "dimm", 276 dimm_mod_event, 277 0 278 }; 279 DECLARE_MODULE(dimm, dimm_mod, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY); 280 MODULE_VERSION(dimm, 1); 281