/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <fcntl.h>
#include <libdevinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <synch.h>
#include <libgen.h>
#include <syslog.h>
#include "libdiskmgt.h"
#include "disks_private.h"
#include "partition.h"
#define ALIASES 0
/*
* Set DM_LIBDISKMGT_DEBUG in the environment. Two levels of debugging:
* 1 - errors, warnings and minimal tracing information
* 2 - verbose information
* All output prints on stderr.
*/
int dm_debug = 0;
/* Lock protecting the cached data */
static int cache_loaded = 0;
static void clear_descriptors(void *gp);
static void del_drive_by_name(char *name);
static int initialize();
static int make_descriptors(int type);
static void rewalk_tree();
/*
* We only cache some of the data that we can obtain. For much of the data
* (e.g. slices & disks getting repartitioned) there are no events which would
* enable us to cache. As more events are added we can cache more information.
*
* Currently we cache the information we get from the dev tree walk. This is
* basically the information about the drives, aliases, devpaths, controllers
* and paths. We do not cache any information related to media, partitions
* or slices.
*
* A fundamental part of the API design is that the application can hold on
* to a set of descriptors for an indeterminate amount of time. Even if the
* application does not hold descriptors there is a window of time between the
* call that gets the descriptor and the use of the descriptor to get more
* information. Because of this, the cache design must work even if the object
* that the descriptor refers to no longer exists.
*
* Given this requirement, the code implements a two level cache. The
* descriptors that the application gets are really pointers into the first
* level of the cache. This first level contains the actual descriptors.
* These descriptors in turn refer to the objects we build from the dev tree
* walk which represent the drives and controllers. This is the second level
* in the cache.
*
* When we update the second level of the cache (the drives and controllers)
* we go through the first level (the descriptors) and update the pointers
* in those descriptors to refer to the new objects in the second level. If
* the object that the descriptor referred to is no longer in existence, we
* just null out the pointer in the descriptor. In this way the code that
* uses the descriptors knows that the object referred to by the descriptor
* no longer exists.
*
* We keep a reference count in the descriptors. This is incremented when
* we hand out a pointer to the descriptor and decremented when the application
* frees the descriptor it has. When the reference count goes to 0 we garbage
* collect the descriptors. In this way we only have to update active
* descriptors when we refresh the cache after an event.
*
* An example of the flow when we create descriptors:
* dm_get_descriptors libdiskmgt.c
* drive_get_descriptors drive.c
* cache_get_descriptors cache.c
* make_descriptors cache.c
* drive_make_descriptors drive.c
* cache_load_desc cache.c
* {update refcnts on descriptors & return them}
*
* The idea behind cache_get_descriptors and cache_load_desc is that we
* separate the act of making the descriptor within the cache (which requires
* us to call back out to one of the object functions - drive_make_descriptors)
* from the act of handing out the descriptor (which requires us to increment
* the refcnt). In this way we keep all of the refcnt handling centralized
* in one function instead of forcing each object to ensure it replicates
* the refcnt handling correctly.
*
* Descriptors use two different kinds of indrection to refer to their
* corresponding object. For objects we cache (controllers, paths & drives)
* the descriptor keeps a pointer to that object. For objects that we
* dynamically build, the descriptor uses a combination of a pointer to the
* base object (usually the drive) along with a name (e.g. the media name or
* the alias). For objects that are based on media (e.g. a slice) we actually
* have to maintain a pointer (to the disk) and two names (e.g. the slice name
* and the media name which is the secondary name).
*/
void
{
/* free devpaths */
}
/* free orig_paths */
}
}
void
{
}
void
{
int i;
/* free the path since it can't exist w/o the ctrlr */
}
}
}
void
{
int locked;
if (!cache_is_valid_desc(desc)) {
return;
}
desc_wlock(&locked);
/* this is the first descriptor, update head ptr */
} else {
}
}
}
}
void
{
int i;
for (i = 0; desc_list[i]; i++) {
}
}
void
{
}
/* the path objects are freed when we free the controller */
}
}
void
{
int i;
}
}
}
bus_t *
{
if (initialize() != 0) {
return (NULL);
}
return (bus_listp);
}
{
if (initialize() != 0) {
return (NULL);
}
return (controller_listp);
}
/*
* This routine will either get the existing descriptor from the descriptor
* cache or make make a new descriptor and put it in the descriptor cache and
* return a pointer to that descriptor. We increment the refcnt when we hand
* out the descriptor.
*/
{
*errp = 0;
/* make a new desc */
== NULL) {
}
}
}
return (dp);
}
descriptor_t **
{
int cnt = 0;
int pos;
int locked;
return (NULL);
}
desc_rlock(&locked);
/* count the number of active descriptors in the descriptor cache */
descp = desc_listp;
cnt++;
}
}
return (NULL);
}
pos = 0;
descp = desc_listp;
/* update refcnts before handing out the descriptors */
}
}
*errp = 0;
return (descs);
}
disk_t *
{
if (initialize() != 0) {
return (NULL);
}
return (disk_listp);
}
int
{
int valid = 0;
int locked;
desc_rlock(&locked);
break;
}
return (valid);
}
/*
* This function is called by the *_make_descriptors function
* (e.g. drive_make_descriptors) within each of the objects. This function
* makes sure that the descriptor is built in the descriptor cache but
* it does not hand out the descriptors, so the refcnt is never incremented.
*/
void
{
*errp = 0;
/* make a new desc */
}
}
}
void
{
*state = 0;
else if (rw_rdlock(&cache_lock) == 0)
*state = 1;
else
abort();
}
void
{
if (RW_WRITE_HELD(&cache_lock))
*state = 0;
else if (rw_wrlock(&cache_lock) == 0)
*state = 1;
else
abort();
}
void
{
if (*state == 1) {
(void) rw_unlock(&cache_lock);
*state = 0;
}
}
void
{
*state = 0;
*state = 1;
else
abort();
}
void
{
if (RW_WRITE_HELD(&desc_lock))
*state = 0;
*state = 1;
else
abort();
}
void
{
if (*state == 1) {
*state = 0;
}
}
/*
* This function is called when we get a devtree event. Type is either add
* or delete of a drive.
*
* For delete, we need to clean up the 2nd level structures and clean up
* the pointers between the them. We also clear the descriptor ptr.
*/
void
{
char *orig_name;
int clocked;
int dlocked;
/* update the cache */
switch (ev_type) {
case DM_EV_DISK_ADD:
rewalk_tree();
break;
case DM_EV_DISK_DELETE:
break;
}
}
/*
* Clear any descriptors that point at the specified cached object.
* We must go through the whole list since there can be multiple descriptors
* to the same drive object). The list is usually small (0 size) so this
* is not a big deal.
*/
static void
{
int locked;
desc_wlock(&locked);
/* clear descriptor */
}
}
}
/* remove the ptr from the controller to the specified disk */
static void
{
int i;
int j;
}
return;
}
}
}
/* remove the ptr from the path to the specified disk */
static void
{
int i;
int j;
}
return;
}
}
}
static void
{
int i;
/* clear any ptrs from controllers to this drive */
for (i = 0; dp->controllers[i]; i++) {
}
}
/* clear any ptrs from paths to this drive */
}
}
/* clear drive from disk list */
} else {
}
break;
}
prev = disk_listp;
} else {
}
}
}
/*
* Delete cached drive info when we get a devtree drive delete event.
*/
static void
{
return;
}
}
}
}
static descriptor_t *
{
int locked;
}
}
desc_rlock(&locked);
descp = desc_listp;
break;
else
break;
}
}
return (descp);
}
static int
{
if (cache_loaded) {
return (0);
}
if (args.dev_walk_status != 0) {
return (args.dev_walk_status);
}
cache_loaded = 1;
/*
* Only start the event thread if we are not doing an install
*/
if (events_start_event_watcher() != 0) {
/*
* Log a message about the failure to start
* sysevents and continue on.
*/
"libdiskmgt: sysevent thread for cache "
"events failed to start\n"));
}
}
return (0);
}
static int
{
int error;
if ((error = initialize()) != 0) {
return (error);
}
switch (type) {
case DM_DRIVE:
break;
case DM_BUS:
break;
case DM_CONTROLLER:
break;
case DM_PATH:
break;
case DM_ALIAS:
break;
case DM_MEDIA:
break;
case DM_PARTITION:
break;
case DM_SLICE:
break;
}
return (error);
}
static int
{
return (0);
}
return (1);
}
}
return (0);
}
static int
{
return (0);
}
return (1);
}
}
return (0);
}
static int
{
return (1);
}
} else {
/* oldp device id is null */
/* both disks have no device id, check aliases */
return (1);
}
}
}
return (0);
}
static descriptor_t *
{
descriptor_t *d;
int locked;
}
}
if (d == NULL) {
return (NULL);
}
switch (type) {
case DM_CONTROLLER:
d->p.controller = op;
break;
case DM_BUS:
break;
default:
break;
}
free(d);
return (NULL);
}
} else {
}
if (secondary_name != NULL) {
if (d->secondary_name == NULL) {
free(d);
return (NULL);
}
} else {
d->secondary_name = NULL;
}
} else {
d->secondary_name = NULL;
}
d->refcnt = 0;
desc_wlock(&locked);
/* add this descriptor to the head of the list */
if (desc_listp != NULL) {
desc_listp->prev = d;
}
d->next = desc_listp;
desc_listp = d;
return (d);
}
static void
{
if (args.dev_walk_status == 0) {
/* walk the existing descriptors and update the ptrs */
descp = desc_listp;
}
/* update the cached object ptrs */
} else {
}
/*
* Free the memory from either the old cached objects or the failed
* update objects.
*/
while (free_disklistp != NULL) {
}
while (free_controllerlistp != NULL) {
}
while (free_buslistp != NULL) {
}
}
/*
* Walk the new set of cached objects and update the descriptor ptr to point
* to the correct new object. If there is no object any more, set the desc
* ptr to null.
*/
static void
{
/* if the descriptor is already dead, we're done */
return;
}
/*
* All descriptors use a disk ptr except for controller descriptors
* and path descriptors.
*/
case DM_BUS:
break;
case DM_CONTROLLER:
break;
case DM_PATH:
break;
default:
break;
}
}
static void
{
/* walk the new objects and find the correct bus */
return;
}
}
/* we did not find the controller any more, clear the ptr in the desc */
}
static void
{
/* walk the new objects and find the correct controller */
return;
}
}
/* we did not find the controller any more, clear the ptr in the desc */
}
static void
{
/* walk the new objects and find the correct disk */
return;
}
}
/* we did not find the disk any more, clear the ptr in the descriptor */
}
static void
{
/* walk the new objects and find the correct path */
int i;
for (i = 0; pp[i]; i++) {
return;
}
}
}
}
/* we did not find the path any more, clear the ptr in the desc */
}