1f2dbfd32SRobert Mustacchi /* 2f2dbfd32SRobert Mustacchi * This file and its contents are supplied under the terms of the 3f2dbfd32SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0. 4f2dbfd32SRobert Mustacchi * You may only use this file in accordance with the terms of version 5f2dbfd32SRobert Mustacchi * 1.0 of the CDDL. 6f2dbfd32SRobert Mustacchi * 7f2dbfd32SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this 8f2dbfd32SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at 9f2dbfd32SRobert Mustacchi * http://www.illumos.org/license/CDDL. 10f2dbfd32SRobert Mustacchi */ 11f2dbfd32SRobert Mustacchi 12f2dbfd32SRobert Mustacchi /* 13f2dbfd32SRobert Mustacchi * Copyright 2019, Joyent, Inc. 14*1045e13aSRobert Mustacchi * Copyright 2020 Oxide Computer Company 15f2dbfd32SRobert Mustacchi */ 16f2dbfd32SRobert Mustacchi 17f2dbfd32SRobert Mustacchi /* 18f2dbfd32SRobert Mustacchi * This file provides routines to interact with the kernel sensor framework. 19f2dbfd32SRobert Mustacchi * Currently, modules that require interacting with a kernel sensor need to 20f2dbfd32SRobert Mustacchi * build this file as part of the module. This takes care of all the work of 21*1045e13aSRobert Mustacchi * setting up and creating the sensor, given a path to that sensor. 22f2dbfd32SRobert Mustacchi */ 23f2dbfd32SRobert Mustacchi 24f2dbfd32SRobert Mustacchi #include <sys/types.h> 25f2dbfd32SRobert Mustacchi #include <sys/stat.h> 26f2dbfd32SRobert Mustacchi #include <fcntl.h> 27f2dbfd32SRobert Mustacchi #include <stdio.h> 28f2dbfd32SRobert Mustacchi #include <string.h> 29f2dbfd32SRobert Mustacchi #include <unistd.h> 30f2dbfd32SRobert Mustacchi #include <libnvpair.h> 31f2dbfd32SRobert Mustacchi #include <sys/sensors.h> 32f2dbfd32SRobert Mustacchi #include <sys/fm/protocol.h> 33f2dbfd32SRobert Mustacchi #include <fm/topo_mod.h> 34f2dbfd32SRobert Mustacchi 35*1045e13aSRobert Mustacchi #define TOPO_METH_TOPO_SENSOR_SCALAR "topo_sensor_scalar_reading" 36*1045e13aSRobert Mustacchi #define TOPO_METH_TOPO_SENSOR_SCALAR_DESC "Kernel Sensor Scalar Reading" 37*1045e13aSRobert Mustacchi #define TOPO_METH_TOPO_SENSOR_SCALAR_VERSION 0 38f2dbfd32SRobert Mustacchi 39f2dbfd32SRobert Mustacchi static int 40*1045e13aSRobert Mustacchi topo_sensor_scalar_read(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 41f2dbfd32SRobert Mustacchi nvlist_t *in, nvlist_t **out) 42f2dbfd32SRobert Mustacchi { 43f2dbfd32SRobert Mustacchi int fd = -1, ret; 44f2dbfd32SRobert Mustacchi nvlist_t *args, *nvl; 45f2dbfd32SRobert Mustacchi char *path; 46*1045e13aSRobert Mustacchi sensor_ioctl_scalar_t scalar; 47*1045e13aSRobert Mustacchi double value; 48f2dbfd32SRobert Mustacchi 49*1045e13aSRobert Mustacchi if (vers != TOPO_METH_TOPO_SENSOR_SCALAR_VERSION) { 50f2dbfd32SRobert Mustacchi return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 51f2dbfd32SRobert Mustacchi } 52f2dbfd32SRobert Mustacchi 53f2dbfd32SRobert Mustacchi if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 54f2dbfd32SRobert Mustacchi nvlist_lookup_string(args, TOPO_IO_DEV_PATH, &path) != 0) { 55f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to lookup sensor path from " 56f2dbfd32SRobert Mustacchi "property %s", TOPO_IO_DEV_PATH); 57f2dbfd32SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 58f2dbfd32SRobert Mustacchi } 59f2dbfd32SRobert Mustacchi 60f2dbfd32SRobert Mustacchi if ((fd = open(path, O_RDONLY)) < 0) { 61f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to open sensor path %s: %s", 62f2dbfd32SRobert Mustacchi path, strerror(errno)); 63f2dbfd32SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 64f2dbfd32SRobert Mustacchi } 65f2dbfd32SRobert Mustacchi 66*1045e13aSRobert Mustacchi (void) memset(&scalar, '\0', sizeof (scalar)); 67*1045e13aSRobert Mustacchi if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) { 68*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "failed to read sensor %s: %s", path, 69*1045e13aSRobert Mustacchi strerror(errno)); 70f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 71f2dbfd32SRobert Mustacchi goto out; 72f2dbfd32SRobert Mustacchi } 73f2dbfd32SRobert Mustacchi 74f2dbfd32SRobert Mustacchi /* 75f2dbfd32SRobert Mustacchi * Check to see if we need to change the value to get it into an 76*1045e13aSRobert Mustacchi * accurate reading. Positive granularities indicate that the sensor 77*1045e13aSRobert Mustacchi * reading is in a fractional number of units and that each unit 78*1045e13aSRobert Mustacchi * contains scalar.sis_gran steps. A negative number means that the 79*1045e13aSRobert Mustacchi * sensor reading represents scalar.sis_gran units. 80f2dbfd32SRobert Mustacchi */ 81*1045e13aSRobert Mustacchi value = (double)scalar.sis_value; 82*1045e13aSRobert Mustacchi if (scalar.sis_gran > 1) { 83*1045e13aSRobert Mustacchi value /= (double)scalar.sis_gran; 84*1045e13aSRobert Mustacchi } else if (scalar.sis_gran < -1) { 85*1045e13aSRobert Mustacchi value *= (double)labs(scalar.sis_gran); 86f2dbfd32SRobert Mustacchi } 87f2dbfd32SRobert Mustacchi 88f2dbfd32SRobert Mustacchi if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 89*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "failed to allocate output nvl"); 90f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_NOMEM); 91f2dbfd32SRobert Mustacchi goto out; 92f2dbfd32SRobert Mustacchi } 93f2dbfd32SRobert Mustacchi 94f2dbfd32SRobert Mustacchi if (nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_SENSOR_READING) != 95f2dbfd32SRobert Mustacchi 0 || 96f2dbfd32SRobert Mustacchi nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 || 97*1045e13aSRobert Mustacchi nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, value) != 0) { 98f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to add members to output " 99*1045e13aSRobert Mustacchi "sensor nvlist"); 100f2dbfd32SRobert Mustacchi nvlist_free(nvl); 101f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_NOMEM); 102f2dbfd32SRobert Mustacchi goto out; 103f2dbfd32SRobert Mustacchi } 104f2dbfd32SRobert Mustacchi 105f2dbfd32SRobert Mustacchi *out = nvl; 106f2dbfd32SRobert Mustacchi ret = 0; 107f2dbfd32SRobert Mustacchi out: 108f2dbfd32SRobert Mustacchi if (fd >= 0) { 109f2dbfd32SRobert Mustacchi (void) close(fd); 110f2dbfd32SRobert Mustacchi } 111f2dbfd32SRobert Mustacchi return (ret); 112f2dbfd32SRobert Mustacchi } 113f2dbfd32SRobert Mustacchi 114*1045e13aSRobert Mustacchi static const topo_method_t topo_sensor_scalar_fac_methods[] = { 115*1045e13aSRobert Mustacchi { TOPO_METH_TOPO_SENSOR_SCALAR, TOPO_METH_TOPO_SENSOR_SCALAR_DESC, 116*1045e13aSRobert Mustacchi TOPO_METH_TOPO_SENSOR_SCALAR_VERSION, TOPO_STABILITY_INTERNAL, 117*1045e13aSRobert Mustacchi topo_sensor_scalar_read }, 118f2dbfd32SRobert Mustacchi { NULL } 119f2dbfd32SRobert Mustacchi }; 120f2dbfd32SRobert Mustacchi 121f2dbfd32SRobert Mustacchi static topo_sensor_unit_t 122*1045e13aSRobert Mustacchi topo_sensor_units(const sensor_ioctl_scalar_t *scalar) 123f2dbfd32SRobert Mustacchi { 124*1045e13aSRobert Mustacchi switch (scalar->sis_unit) { 125f2dbfd32SRobert Mustacchi case SENSOR_UNIT_CELSIUS: 126f2dbfd32SRobert Mustacchi return (TOPO_SENSOR_UNITS_DEGREES_C); 127f2dbfd32SRobert Mustacchi case SENSOR_UNIT_FAHRENHEIT: 128f2dbfd32SRobert Mustacchi return (TOPO_SENSOR_UNITS_DEGREES_F); 129f2dbfd32SRobert Mustacchi case SENSOR_UNIT_KELVIN: 130f2dbfd32SRobert Mustacchi return (TOPO_SENSOR_UNITS_DEGREES_K); 131*1045e13aSRobert Mustacchi case SENSOR_UNIT_VOLTS: 132*1045e13aSRobert Mustacchi return (TOPO_SENSOR_UNITS_VOLTS); 133*1045e13aSRobert Mustacchi case SENSOR_UNIT_AMPS: 134*1045e13aSRobert Mustacchi return (TOPO_SENSOR_UNITS_AMPS); 135f2dbfd32SRobert Mustacchi default: 136f2dbfd32SRobert Mustacchi return (TOPO_SENSOR_UNITS_UNSPECIFIED); 137f2dbfd32SRobert Mustacchi } 138f2dbfd32SRobert Mustacchi } 139f2dbfd32SRobert Mustacchi 140f2dbfd32SRobert Mustacchi int 141*1045e13aSRobert Mustacchi topo_sensor_create_scalar_sensor(topo_mod_t *mod, tnode_t *pnode, 142f2dbfd32SRobert Mustacchi const char *path, const char *fname) 143f2dbfd32SRobert Mustacchi { 144f2dbfd32SRobert Mustacchi int fd, ret, err; 145f2dbfd32SRobert Mustacchi sensor_ioctl_kind_t sik; 146*1045e13aSRobert Mustacchi sensor_ioctl_scalar_t scalar; 147*1045e13aSRobert Mustacchi uint32_t topo_type; 148f2dbfd32SRobert Mustacchi tnode_t *fnode = NULL; 149f2dbfd32SRobert Mustacchi topo_pgroup_info_t pgi; 150f2dbfd32SRobert Mustacchi nvlist_t *reader_arg = NULL; 151f2dbfd32SRobert Mustacchi 152f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "attempting to create sensor for %s at %s", 153f2dbfd32SRobert Mustacchi topo_node_name(pnode), path); 154f2dbfd32SRobert Mustacchi 155f2dbfd32SRobert Mustacchi (void) memset(&sik, '\0', sizeof (sik)); 156*1045e13aSRobert Mustacchi (void) memset(&scalar, '\0', sizeof (scalar)); 157f2dbfd32SRobert Mustacchi 158f2dbfd32SRobert Mustacchi if ((fd = open(path, O_RDONLY)) < 0) { 159f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to open sensor path %s: %s", 160f2dbfd32SRobert Mustacchi path, strerror(errno)); 161f2dbfd32SRobert Mustacchi 162f2dbfd32SRobert Mustacchi /* 163*1045e13aSRobert Mustacchi * We always try to create sensors; however, they may not exist 164*1045e13aSRobert Mustacchi * or be supported on the system in question. Therefore ENOENT 165*1045e13aSRobert Mustacchi * is totally acceptable. 166f2dbfd32SRobert Mustacchi */ 167f2dbfd32SRobert Mustacchi if (errno == ENOENT) { 168f2dbfd32SRobert Mustacchi return (0); 169f2dbfd32SRobert Mustacchi } 170f2dbfd32SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 171f2dbfd32SRobert Mustacchi } 172f2dbfd32SRobert Mustacchi 173*1045e13aSRobert Mustacchi if (ioctl(fd, SENSOR_IOCTL_KIND, &sik) != 0) { 174f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to verify sensor kind for sensor " 175f2dbfd32SRobert Mustacchi "%s: %s", path, strerror(errno)); 176f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 177f2dbfd32SRobert Mustacchi goto out; 178f2dbfd32SRobert Mustacchi } 179f2dbfd32SRobert Mustacchi 180*1045e13aSRobert Mustacchi switch (sik.sik_kind) { 181*1045e13aSRobert Mustacchi case SENSOR_KIND_TEMPERATURE: 182*1045e13aSRobert Mustacchi topo_type = TOPO_SENSOR_TYPE_TEMP; 183*1045e13aSRobert Mustacchi break; 184*1045e13aSRobert Mustacchi case SENSOR_KIND_VOLTAGE: 185*1045e13aSRobert Mustacchi topo_type = TOPO_SENSOR_TYPE_VOLTAGE; 186*1045e13aSRobert Mustacchi break; 187*1045e13aSRobert Mustacchi case SENSOR_KIND_CURRENT: 188*1045e13aSRobert Mustacchi topo_type = TOPO_SENSOR_TYPE_CURRENT; 189*1045e13aSRobert Mustacchi break; 190*1045e13aSRobert Mustacchi default: 191*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "unknown sensor kind for %s, found 0x%x", 192*1045e13aSRobert Mustacchi path, sik.sik_kind); 193f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 194f2dbfd32SRobert Mustacchi goto out; 195*1045e13aSRobert Mustacchi 196f2dbfd32SRobert Mustacchi } 197f2dbfd32SRobert Mustacchi 198*1045e13aSRobert Mustacchi if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) { 199*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "failed to read scalar sensor %s: %s", 200*1045e13aSRobert Mustacchi path, strerror(errno)); 201f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 202f2dbfd32SRobert Mustacchi goto out; 203f2dbfd32SRobert Mustacchi } 204f2dbfd32SRobert Mustacchi 205f2dbfd32SRobert Mustacchi (void) close(fd); 206f2dbfd32SRobert Mustacchi fd = -1; 207f2dbfd32SRobert Mustacchi 208f2dbfd32SRobert Mustacchi if ((fnode = topo_node_facbind(mod, pnode, fname, 209f2dbfd32SRobert Mustacchi TOPO_FAC_TYPE_SENSOR)) == NULL) { 210*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "failed to bind sensor facility " 211f2dbfd32SRobert Mustacchi "node to %s: %d", path, topo_mod_errno(mod)); 212f2dbfd32SRobert Mustacchi ret = -1; 213f2dbfd32SRobert Mustacchi goto out; 214f2dbfd32SRobert Mustacchi } 215f2dbfd32SRobert Mustacchi 216f2dbfd32SRobert Mustacchi pgi.tpi_name = TOPO_PGROUP_FACILITY; 217f2dbfd32SRobert Mustacchi pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 218f2dbfd32SRobert Mustacchi pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 219f2dbfd32SRobert Mustacchi pgi.tpi_version = 1; 220f2dbfd32SRobert Mustacchi 221f2dbfd32SRobert Mustacchi if (topo_pgroup_create(fnode, &pgi, &err) != 0) { 222f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to create facility pgroup: %s", 223f2dbfd32SRobert Mustacchi topo_strerror(err)); 224f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, err); 225f2dbfd32SRobert Mustacchi goto out; 226f2dbfd32SRobert Mustacchi } 227f2dbfd32SRobert Mustacchi 228f2dbfd32SRobert Mustacchi if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, 229f2dbfd32SRobert Mustacchi TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE, 230f2dbfd32SRobert Mustacchi TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 || 231f2dbfd32SRobert Mustacchi topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY, 232*1045e13aSRobert Mustacchi TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, topo_type, &err) != 0 || 233f2dbfd32SRobert Mustacchi topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY, 234*1045e13aSRobert Mustacchi TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, topo_sensor_units(&scalar), 235f2dbfd32SRobert Mustacchi &err) != 0) { 236f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to set properties for sensor " 237f2dbfd32SRobert Mustacchi "%s: %s", path, topo_strerror(err)); 238f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, err); 239f2dbfd32SRobert Mustacchi goto out; 240f2dbfd32SRobert Mustacchi 241f2dbfd32SRobert Mustacchi } 242f2dbfd32SRobert Mustacchi 243*1045e13aSRobert Mustacchi if (topo_method_register(mod, fnode, topo_sensor_scalar_fac_methods) < 244f2dbfd32SRobert Mustacchi 0) { 245f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to register reading methods on " 246f2dbfd32SRobert Mustacchi "%s", path); 247f2dbfd32SRobert Mustacchi ret = -1; 248f2dbfd32SRobert Mustacchi goto out; 249f2dbfd32SRobert Mustacchi } 250f2dbfd32SRobert Mustacchi 251f2dbfd32SRobert Mustacchi if (topo_mod_nvalloc(mod, &reader_arg, NV_UNIQUE_NAME) != 0 || 252f2dbfd32SRobert Mustacchi nvlist_add_string(reader_arg, TOPO_IO_DEV_PATH, path) != 0) { 253f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "Failed to set up reader argument nvl"); 254f2dbfd32SRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_NOMEM); 255f2dbfd32SRobert Mustacchi goto out; 256f2dbfd32SRobert Mustacchi } 257f2dbfd32SRobert Mustacchi 258f2dbfd32SRobert Mustacchi if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY, 259*1045e13aSRobert Mustacchi TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, TOPO_METH_TOPO_SENSOR_SCALAR, 260f2dbfd32SRobert Mustacchi reader_arg, &err) != 0) { 261f2dbfd32SRobert Mustacchi topo_mod_dprintf(mod, "failed to set argument for sensor %s: " 262f2dbfd32SRobert Mustacchi "%s", path, topo_strerror(err)); 263f2dbfd32SRobert Mustacchi err = topo_mod_seterrno(mod, err); 264f2dbfd32SRobert Mustacchi goto out; 265f2dbfd32SRobert Mustacchi } 266f2dbfd32SRobert Mustacchi 267*1045e13aSRobert Mustacchi topo_mod_dprintf(mod, "created sensor at %s", path); 268*1045e13aSRobert Mustacchi 269f2dbfd32SRobert Mustacchi nvlist_free(reader_arg); 270f2dbfd32SRobert Mustacchi return (0); 271f2dbfd32SRobert Mustacchi out: 272f2dbfd32SRobert Mustacchi if (fd >= 0) { 273f2dbfd32SRobert Mustacchi (void) close(fd); 274f2dbfd32SRobert Mustacchi } 275f2dbfd32SRobert Mustacchi 276f2dbfd32SRobert Mustacchi topo_node_unbind(fnode); 277f2dbfd32SRobert Mustacchi nvlist_free(reader_arg); 278f2dbfd32SRobert Mustacchi return (ret); 279f2dbfd32SRobert Mustacchi } 280