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.
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.
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]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2016 Nexenta Systems, Inc.
29 #include <sys/types.h>
32 * Dependent on types.h, but not including it...
35 #include <sys/types.h>
37 #include <sys/dktp/fdisk.h>
38 #include <sys/mnttab.h>
39 #include <sys/mntent.h>
40 #include <sys/sysmacros.h>
41 #include <sys/mkdev.h>
44 #include <nfs/nfs_clnt.h>
48 #include <libdevinfo.h>
56 #include <sys/scsi/adapters/scsi_vhci.h>
59 #include "statcommon.h"
61 /* where we get kstat name translation information from */
62 static di_node_t di_root
; /* from di_init: for devid */
63 static di_dim_t di_dim
; /* from di_dim_init: for /dev names */
64 static int scsi_vhci_fd
= -1; /* from scsi_vhci: for mpxio path */
66 /* disk/tape/misc info */
71 static minor_match_t mm_disk
= {"a", 1};
72 static minor_match_t mm_tape
= {"", 0};
73 static minor_match_t mm_misc
= {"0", 0};
74 static minor_match_t
*mma_disk_tape_misc
[] =
75 {&mm_disk
, &mm_tape
, &mm_misc
, NULL
};
76 #define DISKLIST_MOD 256 /* ^2 instance mod hash */
77 static disk_list_t
*disklist
[DISKLIST_MOD
];
81 extern kstat_ctl_t
*kc
;
84 static char *get_nfs_by_minor(uint_t
);
85 static char *cur_hostname(uint_t
, kstat_ctl_t
*);
86 static char *cur_special(char *, char *);
89 * Clear the snapshot so a cache miss in lookup_ks_name() will cause a fresh
90 * snapshot in drvinstpart2dev().
93 cleanup_iodevs_snapshot()
102 di_root
= DI_NODE_NIL
;
109 * Find information for (driver, instance) device: return zero on failure.
111 * NOTE: Failure of drvinstpart2dev works out OK for the caller if the kstat
112 * name is the same as public name: the caller will just use kstat name.
115 drvinstpart2dev(char *driver
, int instance
, char *part
,
116 char **devpathp
, char **adevpathp
, char **devidp
)
118 minor_match_t
*mm
, **mma
= mma_disk_tape_misc
;
124 /* setup "no result" return values */
125 if (devpathp
!= NULL
)
127 if (adevpathp
!= NULL
)
132 /* take <driver><instance><minor> snapshot if not established */
133 if (di_dim
== NULL
) {
134 di_dim
= di_dim_init();
140 devpath
= di_dim_path_dev(di_dim
, driver
, instance
, part
);
142 /* Try to find a minor_match that works */
143 for (mm
= *mma
++; mm
!= NULL
; mm
= *mma
++) {
144 if ((devpath
= di_dim_path_dev(di_dim
,
145 driver
, instance
, mm
->minor_name
)) != NULL
)
153 * At this point we have a devpath result. Return the information about
154 * the result that the caller is asking for.
156 if (devpathp
!= NULL
) /* devpath */
157 *devpathp
= safe_strdup(devpath
);
159 if (adevpathp
!= NULL
) { /* abbreviated devpath */
162 a
= strrchr(devpath
, '/');
168 if (part
== NULL
&& mm
->minor_isdisk
) {
170 * For disk kstats without a partition we return the
171 * last component with trailing "s#" or "p#" stripped
172 * off (i.e. partition/slice information is removed).
173 * For example for devpath of "/dev/dsk/c0t0d0s0" the
174 * abbreviated devpath would be "c0t0d0".
178 if ((s
= strrchr(a
, 's')) == NULL
&&
179 (s
= strrchr(a
, 'p')) == NULL
) {
183 /* don't include slice information in devpath */
186 *adevpathp
= safe_strdup(a
);
189 if (devidp
!= NULL
) { /* lookup the devid */
190 /* take snapshot if not established */
191 if (di_root
== DI_NODE_NIL
)
192 di_root
= di_init("/", DINFOCACHE
);
193 if (di_root
!= NULL
) {
194 /* get path to /devices devinfo node */
195 devicespath
= di_dim_path_devices(di_dim
,
196 driver
, instance
, NULL
);
198 /* find the node in the snapshot */
199 node
= di_lookup_node(di_root
, devicespath
);
202 /* and lookup devid property on the node */
203 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
204 DEVID_PROP_NAME
, &devid
) != -1)
211 return (1); /* success */
215 * Do <pid> to 'target-port' translation
218 drvpid2port(uint_t pid
, char **target_portp
)
221 char target_port
[MAXNAMELEN
];
223 /* setup "no result" return values */
224 *target_portp
= NULL
;
226 /* open scsi_vhci if not already done */
227 if (scsi_vhci_fd
== -1) {
228 scsi_vhci_fd
= open("/devices/scsi_vhci:devctl", O_RDONLY
);
229 if (scsi_vhci_fd
== -1)
230 return (0); /* failure */
234 * Perform ioctl for <pid> -> 'target-port' translation.
236 * NOTE: it is legimite for this ioctl to fail for transports
237 * that use mpxio, but don't set a 'target-port' pathinfo property.
238 * On failure we return the the "<pid>" as the target port string.
240 bzero(&ioc
, sizeof (sv_iocdata_t
));
242 ioc
.addr
= target_port
;
243 if (ioctl(scsi_vhci_fd
, SCSI_VHCI_GET_TARGET_LONGNAME
, &ioc
) < 0) {
244 (void) snprintf(target_port
, sizeof (target_port
), "%d", pid
);
247 *target_portp
= safe_strdup(target_port
);
248 return (1); /* success */
252 * Find/create a disk_list entry for the given kstat name.
253 * The basic format of a kstat name is
255 * "<driver><instance>[.<pid>.<phci-driver><instance>][,<partition>]".
257 * The <instance> is a decimal number. The ".<pid>.<phci-driver><instance>",
258 * which describes mpxio path stat information, and ",<partition>" parts are
259 * optional. The <pid> consists of the letter 't' followed by a decimal number.
260 * When available, we use the <pid> to find the 'target-port' via ioctls to
261 * the scsi_vhci driver.
264 lookup_ks_name(char *ks_name
, int want_devid
)
266 char *pidp
; /* ".<pid>... */
267 char *part
; /* ",partition... */
268 char *initiator
; /* ".<phci-driver>... */
271 char driver
[KSTAT_STRLEN
];
273 disk_list_t
**dlhp
; /* disklist head */
275 char *devpath
= NULL
;
276 char *adevpath
= NULL
;
279 char *target_port
= NULL
;
280 char portform
[MAXPATHLEN
];
282 /* Filter out illegal forms (like all digits) */
283 if (ks_name
== NULL
|| *ks_name
== '\0' ||
284 strspn(ks_name
, "0123456789") == strlen(ks_name
))
287 /* parse ks_name to create new entry */
288 pidp
= strchr(ks_name
, '.'); /* start of ".<pid>" */
289 initiator
= strrchr(ks_name
, '.'); /* start of ".<pHCI-driver>" */
290 if (pidp
!= NULL
&& pidp
== initiator
) /* can't have same start */
293 part
= strchr(ks_name
, ','); /* start of ",<partition>" */
294 p
= strchr(ks_name
, ':'); /* start of ":<partition>" */
295 if (part
!= NULL
&& p
!= NULL
)
296 goto fail
; /* can't have both */
299 if (part
!= NULL
&& pidp
!= NULL
)
300 goto fail
; /* <pid> and partition: bad */
302 p
= (part
!= NULL
) ? part
: pidp
;
304 p
= &ks_name
[strlen(ks_name
) - 1]; /* last char */
306 p
--; /* before ',' or '.' */
308 while ((p
>= ks_name
) && isdigit(*p
))
309 p
--; /* backwards over digits */
310 p
++; /* start of instance */
311 if ((*p
== '\0') || (*p
== ',') || (*p
== '.') || (*p
== ':'))
312 goto fail
; /* no <instance> */
314 (void) strncpy(driver
, ks_name
, len
);
318 part
++; /* skip ',' */
320 /* hash by instance and search for existing entry */
321 dlhp
= &disklist
[instance
& (DISKLIST_MOD
- 1)];
322 for (entry
= *dlhp
; entry
; entry
= entry
->next
) {
323 if (strcmp(entry
->ks_name
, ks_name
) == 0)
327 /* not found, translate kstat_name components and create new entry */
329 /* translate kstat_name dev information */
330 if (drvinstpart2dev(driver
, instance
, part
,
331 &devpath
, &adevpath
, want_devid
? &devid
: NULL
) == 0)
334 /* parse and translate path information */
336 /* parse path information: ".t#.<phci-driver><instance>" */
337 pidp
++; /* skip '.' */
338 initiator
++; /* skip '.' */
339 if (*pidp
!= 't' || !isdigit(pidp
[1]))
340 goto fail
; /* not ".t#" */
341 pid
= atoi(&pidp
[1]);
343 /* translate <pid> to 'target-port' */
344 if (drvpid2port(pid
, &target_port
) == 0)
347 /* Establish 'target-port' form. */
348 (void) snprintf(portform
, sizeof (portform
),
349 "%s.t%s.%s", adevpath
, target_port
, initiator
);
352 adevpath
= strdup(portform
);
355 /* make a new entry ... */
356 entry
= safe_alloc(sizeof (disk_list_t
));
357 entry
->ks_name
= safe_strdup(ks_name
);
358 entry
->dname
= devpath
;
359 entry
->dsk
= adevpath
;
360 entry
->devidstr
= devid
;
363 (void) printf("lookup_ks_name: new: %s %s\n",
364 ks_name
, entry
->dsk
? entry
->dsk
: "NULL");
367 /* add new entry to head of hashed list */
377 (void) printf("lookup_ks_name: failed: %s\n", ks_name
);
383 lookup_nfs_name(char *ks
, kstat_ctl_t
*kc
)
391 if (sscanf(ks
, "nfs%u", &minor
) == 1) {
393 cp
= get_nfs_by_minor(minor
);
395 if (strchr(cp
, ',') == NULL
) {
396 rstr
= safe_strdup(cp
);
399 host
= cur_hostname(minor
, kc
);
402 path
= cur_special(host
, cp
);
407 rstr
= safe_alloc(len
);
408 (void) snprintf(rstr
, len
,
409 "%s:%s", host
, path
);
411 rstr
= safe_strdup(cp
);
414 rstr
= safe_strdup(ks
);
418 rstr
= safe_strdup(cp
);
420 } else if (nfs_tried
== 0) {
430 get_nfs_by_minor(uint_t minor
)
436 if (localnfs
->minor
== minor
) {
437 return (localnfs
->device_name
);
439 localnfs
= localnfs
->next
;
445 * Read the cur_hostname from the mntinfo kstat
448 cur_hostname(uint_t minor
, kstat_ctl_t
*kc
)
451 static struct mntinfo_kstat mik
;
454 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
455 if (ksp
->ks_type
!= KSTAT_TYPE_RAW
)
457 if (ksp
->ks_instance
!= minor
)
459 if (strcmp(ksp
->ks_module
, "nfs"))
461 if (strcmp(ksp
->ks_name
, "mntinfo"))
463 if (ksp
->ks_flags
& KSTAT_FLAG_INVALID
)
465 if (kstat_read(kc
, ksp
, &mik
) == -1)
467 rstr
= safe_strdup(mik
.mik_curserver
);
474 * Given the hostname of the mounted server, extract the server
475 * mount point from the mnttab string.
478 * server1,server2,server3:/path
479 * server1:/path,server2:/path
480 * or a hybrid of the two
483 cur_special(char *hostname
, char *special
)
487 size_t hlen
= strlen(hostname
);
490 * find hostname in string
493 if ((cp
= strstr(special
, hostname
)) == NULL
)
497 * hostname must be followed by ',' or ':'
499 if (cp
[hlen
] != ',' && cp
[hlen
] != ':') {
505 * If hostname is followed by a ',' eat all characters until a ':'
516 path
= ++cp
; /* skip ':' */
519 * path is terminated by either 0, or space or ','
522 if (isspace(*cp
) || *cp
== ',') {