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 /*
27 * Support routines for managing state related to memory modules.
28 */
29
30 #include <cmd_mem.h>
31 #include <cmd_dimm.h>
32 #include <cmd_bank.h>
33 #include <cmd.h>
34 #ifdef sun4u
35 #include <cmd_dp.h>
36 #endif
37 #ifdef sun4v
38 #include <cmd_branch.h>
39 #endif
40
41 #include <errno.h>
42 #include <strings.h>
43 #include <ctype.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <fm/fmd_api.h>
47 #include <sys/fm/protocol.h>
48 #include <sys/mem.h>
49 #include <sys/nvpair.h>
50
51 const char *
cmd_fmri_get_unum(nvlist_t * fmri)52 cmd_fmri_get_unum(nvlist_t *fmri)
53 {
54 const char *scheme, *unum;
55 uint8_t vers;
56
57 if (nvlist_lookup_pairs(fmri, 0,
58 FM_VERSION, DATA_TYPE_UINT8, &vers,
59 FM_FMRI_SCHEME, DATA_TYPE_STRING, &scheme,
60 FM_FMRI_MEM_UNUM, DATA_TYPE_STRING, &unum,
61 NULL) != 0 || vers > FM_MEM_SCHEME_VERSION ||
62 strcmp(scheme, FM_FMRI_SCHEME_MEM) != 0)
63 return (NULL);
64
65 return (unum);
66 }
67
68 char *
cmd_mem_serdnm_create(fmd_hdl_t * hdl,const char * serdbase,const char * unum)69 cmd_mem_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, const char *unum)
70 {
71 const char *fmt = "%s_%s_serd";
72 size_t sz = snprintf(NULL, 0, fmt, serdbase, unum) + 1;
73 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
74 (void) snprintf(nm, sz, fmt, serdbase, unum);
75
76 return (nm);
77 }
78
79 char *
cmd_page_serdnm_create(fmd_hdl_t * hdl,const char * serdbase,uint64_t phys_addr)80 cmd_page_serdnm_create(fmd_hdl_t *hdl, const char *serdbase,
81 uint64_t phys_addr)
82 {
83 const char *fmt = "%s_%llXserd";
84 size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr) + 1;
85 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
86 (void) snprintf(nm, sz, fmt, serdbase, phys_addr);
87
88 return (nm);
89 }
90
91 char *
cmd_mq_serdnm_create(fmd_hdl_t * hdl,const char * serdbase,uint64_t phys_addr,uint16_t cw,uint16_t pos)92 cmd_mq_serdnm_create(fmd_hdl_t *hdl, const char *serdbase,
93 uint64_t phys_addr, uint16_t cw, uint16_t pos)
94 {
95 const char *fmt = "%s_%llX_%x_%x_serd";
96 size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr, cw, pos) + 1;
97 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
98 (void) snprintf(nm, sz, fmt, serdbase, phys_addr, cw, pos);
99
100 return (nm);
101 }
102
103 void
cmd_mem_case_restore(fmd_hdl_t * hdl,cmd_case_t * cc,fmd_case_t * cp,const char * serdbase,const char * unum)104 cmd_mem_case_restore(fmd_hdl_t *hdl, cmd_case_t *cc, fmd_case_t *cp,
105 const char *serdbase, const char *unum)
106 {
107 cmd_case_restore(hdl, cc, cp, cmd_mem_serdnm_create(hdl, serdbase,
108 unum));
109 }
110
111 void
cmd_mem_retirestat_create(fmd_hdl_t * hdl,fmd_stat_t * st,const char * unum,uint64_t value,const char * prefix)112 cmd_mem_retirestat_create(fmd_hdl_t *hdl, fmd_stat_t *st, const char *unum,
113 uint64_t value, const char *prefix)
114 {
115 char *c;
116
117 /*
118 * Prior to Niagara-2, every bank had to have at least two dimms; it
119 * was therefore impossible for the retirestat of a bank to ever have
120 * the same name (strcmp() == 0) as that of a dimm.
121 *
122 * Niagara-2 and VF, in "single channel mode" , retrieve an entire
123 * cache line from a single dimm. We therefore use a different
124 * prefix to name the bank retirestat vs. the dimm retirestat,
125 * or else the DE will abort trying to register a duplicate stat name
126 * with fmd.
127 */
128 (void) snprintf(st->fmds_name, sizeof (st->fmds_name), "%s%s",
129 prefix, unum);
130 (void) snprintf(st->fmds_desc, sizeof (st->fmds_desc),
131 "retirements for %s", unum);
132 st->fmds_type = FMD_TYPE_UINT64;
133 st->fmds_value.ui64 = value;
134
135 /*
136 * Sanitize the name of the statistic -- standard unums won't get
137 * by fmd's validity checker.
138 */
139 for (c = st->fmds_name; *c != '\0'; c++) {
140 if (!isupper(*c) && !islower(*c) &&
141 !isdigit(*c) && *c != '-' && *c != '_' && *c != '.')
142 *c = '_';
143 }
144
145 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 1, st);
146 }
147
148 int
cmd_mem_thresh_check(fmd_hdl_t * hdl,uint_t nret)149 cmd_mem_thresh_check(fmd_hdl_t *hdl, uint_t nret)
150 {
151 ulong_t npages = cmd_mem_get_phys_pages(hdl);
152 ulong_t wrnpgs;
153
154 fmd_hdl_debug(hdl, "thresh_check: npages is %lu\n", npages);
155 if (npages == 0) {
156 return (0);
157 }
158
159 if (cmd.cmd_thresh_abs_sysmem != 0) {
160 wrnpgs = cmd.cmd_thresh_abs_sysmem;
161 } else {
162 /* threshold is in thousandths of a percent */
163 wrnpgs = npages * cmd.cmd_thresh_tpct_sysmem / 100000;
164 }
165
166 fmd_hdl_debug(hdl, "thresh_check: nret %u, wrn %lu\n", nret, wrnpgs);
167
168 return (nret > wrnpgs);
169 }
170
171 nvlist_t *
cmd_mem_fmri_create(const char * unum,char ** serids,size_t nserids)172 cmd_mem_fmri_create(const char *unum, char **serids, size_t nserids)
173 {
174 nvlist_t *fmri;
175
176 if ((errno = nvlist_alloc(&fmri, NV_UNIQUE_NAME, 0)) != 0)
177 return (NULL);
178
179 if ((errno = nvlist_add_uint8(fmri, FM_VERSION,
180 FM_MEM_SCHEME_VERSION)) != 0 || (errno = nvlist_add_string(fmri,
181 FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM)) != 0 || (errno =
182 nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum)) != 0) {
183 nvlist_free(fmri);
184 return (NULL);
185 }
186
187 if ((nserids > 0) && (serids != NULL)) {
188 (void) nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
189 serids, nserids);
190 }
191 return (fmri);
192 }
193
194 nvlist_t *
cmd_mem_fmri_derive(fmd_hdl_t * hdl,uint64_t afar,uint64_t afsr,uint16_t synd)195 cmd_mem_fmri_derive(fmd_hdl_t *hdl, uint64_t afar, uint64_t afsr, uint16_t synd)
196 {
197 mem_name_t mn;
198 nvlist_t *fmri;
199 int fd;
200
201 if ((fd = open("/dev/mem", O_RDONLY)) < 0)
202 return (NULL);
203
204 mn.m_addr = afar;
205 mn.m_synd = synd;
206 mn.m_type[0] = afsr;
207 mn.m_type[1] = 0;
208 mn.m_namelen = 100;
209
210 for (;;) {
211 mn.m_name = fmd_hdl_alloc(hdl, mn.m_namelen, FMD_SLEEP);
212
213 if (ioctl(fd, MEM_NAME, &mn) == 0)
214 break;
215
216 fmd_hdl_free(hdl, mn.m_name, mn.m_namelen);
217
218 if (errno != ENOSPC) {
219 (void) close(fd);
220 return (NULL);
221 }
222
223 mn.m_namelen *= 2;
224 }
225
226 (void) close(fd);
227
228 fmri = cmd_mem_fmri_create(mn.m_name, NULL, 0);
229 fmd_hdl_free(hdl, mn.m_name, mn.m_namelen);
230
231 return (fmri);
232 }
233
234 void
cmd_iorxefrx_queue(fmd_hdl_t * hdl,cmd_iorxefrx_t * rf)235 cmd_iorxefrx_queue(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf)
236 {
237
238 fmd_hdl_debug(hdl, "queueing IOxE/RxE/FRx for matching\n");
239
240 rf->rf_expid = fmd_timer_install(hdl, (void *)CMD_TIMERTYPE_MEM, NULL,
241 cmd.cmd_iorxefrx_window);
242 cmd_list_append(&cmd.cmd_iorxefrx, rf);
243 }
244
245 void
cmd_iorxefrx_free(fmd_hdl_t * hdl,cmd_iorxefrx_t * rf)246 cmd_iorxefrx_free(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf)
247 {
248 /* It's not persisted, so just remove it */
249 cmd_list_delete(&cmd.cmd_iorxefrx, rf);
250 fmd_hdl_free(hdl, rf, sizeof (cmd_iorxefrx_t));
251 }
252
253 void
cmd_mem_timeout(fmd_hdl_t * hdl,id_t id)254 cmd_mem_timeout(fmd_hdl_t *hdl, id_t id)
255 {
256 cmd_iorxefrx_t *rf;
257
258 for (rf = cmd_list_next(&cmd.cmd_iorxefrx); rf != NULL;
259 rf = cmd_list_next(rf)) {
260 if (rf->rf_expid == id) {
261 fmd_hdl_debug(hdl, "reclaiming iorxefrx tid %d\n", id);
262 cmd_iorxefrx_free(hdl, rf);
263 return;
264 }
265 }
266 }
267
268 void
cmd_mem_gc(fmd_hdl_t * hdl)269 cmd_mem_gc(fmd_hdl_t *hdl)
270 {
271 cmd_dimm_gc(hdl);
272 cmd_bank_gc(hdl);
273 #ifdef sun4v
274 cmd_branch_gc(hdl);
275 #endif
276 }
277
278 void
cmd_mem_fini(fmd_hdl_t * hdl)279 cmd_mem_fini(fmd_hdl_t *hdl)
280 {
281 cmd_iorxefrx_t *rf;
282
283 cmd_dimm_fini(hdl);
284 cmd_bank_fini(hdl);
285 #ifdef sun4v
286 cmd_branch_fini(hdl);
287 #endif
288
289 while ((rf = cmd_list_next(&cmd.cmd_iorxefrx)) != NULL)
290 cmd_iorxefrx_free(hdl, rf);
291 }
292