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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1998 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* 28 * support functions for hba drivers to handle scsi reset notifications. 29 */ 30 31 #include <sys/scsi/scsi.h> 32 #include <sys/scsi/impl/scsi_reset_notify.h> 33 34 /* 35 * routine for reset notification setup. 36 * The function is entered without adapter driver mutex being held. 37 */ 38 39 int 40 scsi_hba_reset_notify_setup(struct scsi_address *ap, int flag, 41 void (*callback)(caddr_t), caddr_t arg, kmutex_t *mutex, 42 struct scsi_reset_notify_entry **listp) 43 { 44 struct scsi_reset_notify_entry *p, *beforep; 45 int rval = DDI_FAILURE; 46 47 mutex_enter(mutex); 48 p = *listp; 49 beforep = NULL; 50 while (p) { 51 if (p->ap == ap) 52 break; /* An entry exist for this target */ 53 beforep = p; 54 p = p->next; 55 } 56 57 if ((flag & SCSI_RESET_CANCEL) && (p != NULL)) { 58 if (beforep == NULL) { 59 *listp = p->next; 60 } else { 61 beforep->next = p->next; 62 } 63 kmem_free(p, sizeof (struct scsi_reset_notify_entry)); 64 rval = DDI_SUCCESS; 65 66 } else if ((flag & SCSI_RESET_NOTIFY) && (p == NULL)) { 67 p = kmem_zalloc(sizeof (struct scsi_reset_notify_entry), 68 KM_SLEEP); 69 p->ap = ap; 70 p->callback = callback; 71 p->arg = arg; 72 p->next = *listp; 73 *listp = p; 74 rval = DDI_SUCCESS; 75 } 76 mutex_exit(mutex); 77 return (rval); 78 } 79 80 /* 81 * routine to deallocate the callback list 82 */ 83 void 84 scsi_hba_reset_notify_tear_down(struct scsi_reset_notify_entry *listp) 85 { 86 struct scsi_reset_notify_entry *p, *next; 87 88 p = listp; 89 while (p) { 90 next = p->next; 91 kmem_free(p, sizeof (struct scsi_reset_notify_entry)); 92 p = next; 93 } 94 } 95 96 97 /* 98 * routine to perform the notification callbacks after a reset. 99 * The function is entered with adapter driver mutex being held. 100 */ 101 void 102 scsi_hba_reset_notify_callback(kmutex_t *mutex, 103 struct scsi_reset_notify_entry **listp) 104 { 105 int i, count; 106 struct scsi_reset_notify_entry *p; 107 struct notify_entry { 108 void (*callback)(caddr_t); 109 caddr_t arg; 110 } *list; 111 112 if ((p = *listp) == NULL) 113 return; 114 115 count = 0; 116 while (p != NULL) { 117 count++; 118 p = p->next; 119 } 120 121 list = kmem_alloc(count * sizeof (struct notify_entry), KM_NOSLEEP); 122 if (list == NULL) { 123 cmn_err(CE_WARN, "scsi_reset_notify: kmem_alloc failed"); 124 return; 125 } 126 127 for (i = 0, p = *listp; i < count; i++, p = p->next) { 128 list[i].callback = p->callback; 129 list[i].arg = p->arg; 130 } 131 132 mutex_exit(mutex); 133 for (i = 0; i < count; i++) { 134 if (list[i].callback != NULL) 135 (void) (*list[i].callback)(list[i].arg); 136 } 137 kmem_free(list, count * sizeof (struct notify_entry)); 138 mutex_enter(mutex); 139 } 140