/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 * or http://www.opensolaris.org/os/licensing. * 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 */ /* * Copyright (c) 1998 by Sun Microsystems, Inc. * All rights reserved. */ /* * support functions for hba drivers to handle scsi reset notifications. */ #include #include /* * routine for reset notification setup. * The function is entered without adapter driver mutex being held. */ int scsi_hba_reset_notify_setup(struct scsi_address *ap, int flag, void (*callback)(caddr_t), caddr_t arg, kmutex_t *mutex, struct scsi_reset_notify_entry **listp) { struct scsi_reset_notify_entry *p, *beforep; int rval = DDI_FAILURE; mutex_enter(mutex); p = *listp; beforep = NULL; while (p) { if (p->ap == ap) break; /* An entry exist for this target */ beforep = p; p = p->next; } if ((flag & SCSI_RESET_CANCEL) && (p != NULL)) { if (beforep == NULL) { *listp = p->next; } else { beforep->next = p->next; } kmem_free(p, sizeof (struct scsi_reset_notify_entry)); rval = DDI_SUCCESS; } else if ((flag & SCSI_RESET_NOTIFY) && (p == NULL)) { p = kmem_zalloc(sizeof (struct scsi_reset_notify_entry), KM_SLEEP); p->ap = ap; p->callback = callback; p->arg = arg; p->next = *listp; *listp = p; rval = DDI_SUCCESS; } mutex_exit(mutex); return (rval); } /* * routine to deallocate the callback list */ void scsi_hba_reset_notify_tear_down(struct scsi_reset_notify_entry *listp) { struct scsi_reset_notify_entry *p, *next; p = listp; while (p) { next = p->next; kmem_free(p, sizeof (struct scsi_reset_notify_entry)); p = next; } } /* * routine to perform the notification callbacks after a reset. * The function is entered with adapter driver mutex being held. */ void scsi_hba_reset_notify_callback(kmutex_t *mutex, struct scsi_reset_notify_entry **listp) { int i, count; struct scsi_reset_notify_entry *p; struct notify_entry { void (*callback)(caddr_t); caddr_t arg; } *list; if ((p = *listp) == NULL) return; count = 0; while (p != NULL) { count++; p = p->next; } list = kmem_alloc(count * sizeof (struct notify_entry), KM_NOSLEEP); if (list == NULL) { cmn_err(CE_WARN, "scsi_reset_notify: kmem_alloc failed"); return; } for (i = 0, p = *listp; i < count; i++, p = p->next) { list[i].callback = p->callback; list[i].arg = p->arg; } mutex_exit(mutex); for (i = 0; i < count; i++) { if (list[i].callback != NULL) (void) (*list[i].callback)(list[i].arg); } kmem_free(list, count * sizeof (struct notify_entry)); mutex_enter(mutex); }