17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*8aec9182Sstephh * Common Development and Distribution License (the "License"). 6*8aec9182Sstephh * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21d9638e54Smws 227c478bd9Sstevel@tonic-gate /* 23*8aec9182Sstephh * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <fmd_alloc.h> 307c478bd9Sstevel@tonic-gate #include <fmd_eventq.h> 317c478bd9Sstevel@tonic-gate #include <fmd_module.h> 32d9638e54Smws #include <fmd_dispq.h> 33d9638e54Smws #include <fmd_subr.h> 34d9638e54Smws 35d9638e54Smws #include <fmd.h> 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate fmd_eventq_t * 38d9638e54Smws fmd_eventq_create(fmd_module_t *mp, fmd_eventqstat_t *stats, 39d9638e54Smws pthread_mutex_t *stats_lock, uint_t limit) 407c478bd9Sstevel@tonic-gate { 417c478bd9Sstevel@tonic-gate fmd_eventq_t *eq = fmd_zalloc(sizeof (fmd_eventq_t), FMD_SLEEP); 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&eq->eq_lock, NULL); 447c478bd9Sstevel@tonic-gate (void) pthread_cond_init(&eq->eq_cv, NULL); 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate eq->eq_mod = mp; 47d9638e54Smws eq->eq_stats = stats; 48d9638e54Smws eq->eq_stats_lock = stats_lock; 497c478bd9Sstevel@tonic-gate eq->eq_limit = limit; 50d9638e54Smws eq->eq_sgid = fmd_dispq_getgid(fmd.d_disp, eq); 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate return (eq); 537c478bd9Sstevel@tonic-gate } 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate void 567c478bd9Sstevel@tonic-gate fmd_eventq_destroy(fmd_eventq_t *eq) 577c478bd9Sstevel@tonic-gate { 587c478bd9Sstevel@tonic-gate fmd_eventqelem_t *eqe; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate while ((eqe = fmd_list_next(&eq->eq_list)) != NULL) { 617c478bd9Sstevel@tonic-gate fmd_list_delete(&eq->eq_list, eqe); 627c478bd9Sstevel@tonic-gate fmd_event_rele(eqe->eqe_event); 637c478bd9Sstevel@tonic-gate fmd_free(eqe, sizeof (fmd_eventqelem_t)); 647c478bd9Sstevel@tonic-gate } 657c478bd9Sstevel@tonic-gate 66d9638e54Smws fmd_dispq_delgid(fmd.d_disp, eq->eq_sgid); 677c478bd9Sstevel@tonic-gate fmd_free(eq, sizeof (fmd_eventq_t)); 687c478bd9Sstevel@tonic-gate } 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate static void 717c478bd9Sstevel@tonic-gate fmd_eventq_drop(fmd_eventq_t *eq, fmd_eventqelem_t *eqe) 727c478bd9Sstevel@tonic-gate { 73d9638e54Smws (void) pthread_mutex_lock(eq->eq_stats_lock); 74d9638e54Smws eq->eq_stats->eqs_dropped.fmds_value.ui64++; 75d9638e54Smws (void) pthread_mutex_unlock(eq->eq_stats_lock); 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate fmd_event_rele(eqe->eqe_event); 787c478bd9Sstevel@tonic-gate fmd_free(eqe, sizeof (fmd_eventqelem_t)); 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate 81d9638e54Smws /* 82d9638e54Smws * Update statistics when an event is dispatched and placed on a module's event 83d9638e54Smws * queue. This is essentially the same code as kstat_waitq_enter(9F). 84d9638e54Smws */ 85d9638e54Smws static void 86d9638e54Smws fmd_eventqstat_dispatch(fmd_eventq_t *eq) 87d9638e54Smws { 88d9638e54Smws fmd_eventqstat_t *eqs = eq->eq_stats; 89d9638e54Smws hrtime_t new, delta; 90d9638e54Smws uint32_t wcnt; 91d9638e54Smws 92d9638e54Smws (void) pthread_mutex_lock(eq->eq_stats_lock); 93d9638e54Smws 94d9638e54Smws new = gethrtime(); 95d9638e54Smws delta = new - eqs->eqs_wlastupdate.fmds_value.ui64; 96d9638e54Smws eqs->eqs_wlastupdate.fmds_value.ui64 = new; 97d9638e54Smws wcnt = eqs->eqs_wcnt.fmds_value.ui32++; 98d9638e54Smws 99d9638e54Smws if (wcnt != 0) { 100d9638e54Smws eqs->eqs_wlentime.fmds_value.ui64 += delta * wcnt; 101d9638e54Smws eqs->eqs_wtime.fmds_value.ui64 += delta; 102d9638e54Smws } 103d9638e54Smws 104d9638e54Smws eqs->eqs_dispatched.fmds_value.ui64++; 105d9638e54Smws (void) pthread_mutex_unlock(eq->eq_stats_lock); 106d9638e54Smws } 107d9638e54Smws 1087c478bd9Sstevel@tonic-gate void 1097c478bd9Sstevel@tonic-gate fmd_eventq_insert_at_head(fmd_eventq_t *eq, fmd_event_t *ep) 1107c478bd9Sstevel@tonic-gate { 111d9638e54Smws uint_t evt = FMD_EVENT_TYPE(ep); 112d9638e54Smws fmd_eventqelem_t *eqe; 1137c478bd9Sstevel@tonic-gate int ok; 1147c478bd9Sstevel@tonic-gate 115d9638e54Smws /* 116d9638e54Smws * If this event queue is acting as /dev/null, bounce the reference 117d9638e54Smws * count to free an unreferenced event and just return immediately. 118d9638e54Smws */ 119d9638e54Smws if (eq->eq_limit == 0) { 120d9638e54Smws fmd_event_hold(ep); 121d9638e54Smws fmd_event_rele(ep); 122d9638e54Smws return; 123d9638e54Smws } 124d9638e54Smws 125d9638e54Smws eqe = fmd_alloc(sizeof (fmd_eventqelem_t), FMD_SLEEP); 1267c478bd9Sstevel@tonic-gate fmd_event_hold(ep); 1277c478bd9Sstevel@tonic-gate eqe->eqe_event = ep; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&eq->eq_lock); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate if ((ok = eq->eq_size < eq->eq_limit || evt != FMD_EVT_PROTOCOL) != 0) { 1327c478bd9Sstevel@tonic-gate if (evt != FMD_EVT_CTL) 133d9638e54Smws fmd_eventqstat_dispatch(eq); 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate fmd_list_prepend(&eq->eq_list, eqe); 1367c478bd9Sstevel@tonic-gate eq->eq_size++; 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&eq->eq_cv); 140d9638e54Smws (void) pthread_mutex_unlock(&eq->eq_lock); 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate if (!ok) 1437c478bd9Sstevel@tonic-gate fmd_eventq_drop(eq, eqe); 1447c478bd9Sstevel@tonic-gate } 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate void 1477c478bd9Sstevel@tonic-gate fmd_eventq_insert_at_time(fmd_eventq_t *eq, fmd_event_t *ep) 1487c478bd9Sstevel@tonic-gate { 149d9638e54Smws uint_t evt = FMD_EVENT_TYPE(ep); 1507c478bd9Sstevel@tonic-gate hrtime_t hrt = fmd_event_hrtime(ep); 151d9638e54Smws fmd_eventqelem_t *eqe, *oqe; 1527c478bd9Sstevel@tonic-gate int ok; 1537c478bd9Sstevel@tonic-gate 154d9638e54Smws /* 155d9638e54Smws * If this event queue is acting as /dev/null, bounce the reference 156d9638e54Smws * count to free an unreferenced event and just return immediately. 157d9638e54Smws */ 158d9638e54Smws if (eq->eq_limit == 0) { 159d9638e54Smws fmd_event_hold(ep); 160d9638e54Smws fmd_event_rele(ep); 161d9638e54Smws return; 162d9638e54Smws } 163d9638e54Smws 164d9638e54Smws eqe = fmd_alloc(sizeof (fmd_eventqelem_t), FMD_SLEEP); 1657c478bd9Sstevel@tonic-gate fmd_event_hold(ep); 1667c478bd9Sstevel@tonic-gate eqe->eqe_event = ep; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&eq->eq_lock); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate /* 1717c478bd9Sstevel@tonic-gate * fmd makes no guarantees that events will be delivered in time order 1727c478bd9Sstevel@tonic-gate * because its transport can make no such guarantees. Instead we make 1737c478bd9Sstevel@tonic-gate * a looser guarantee that an enqueued event will be dequeued before 1747c478bd9Sstevel@tonic-gate * any newer *pending* events according to event time. This permits us 1757c478bd9Sstevel@tonic-gate * to state, for example, that a timer expiry event will be delivered 1767c478bd9Sstevel@tonic-gate * prior to any enqueued event whose time is after the timer expired. 1777c478bd9Sstevel@tonic-gate * We use a simple insertion sort for this task, as queue lengths are 1787c478bd9Sstevel@tonic-gate * typically short and events do *tend* to be received chronologically. 1797c478bd9Sstevel@tonic-gate */ 1807c478bd9Sstevel@tonic-gate for (oqe = fmd_list_prev(&eq->eq_list); oqe; oqe = fmd_list_prev(oqe)) { 1817c478bd9Sstevel@tonic-gate if (hrt >= fmd_event_hrtime(oqe->eqe_event)) 1827c478bd9Sstevel@tonic-gate break; /* 'ep' is newer than the event in 'oqe' */ 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate if ((ok = eq->eq_size < eq->eq_limit || evt != FMD_EVT_PROTOCOL) != 0) { 1867c478bd9Sstevel@tonic-gate if (evt != FMD_EVT_CTL) 187d9638e54Smws fmd_eventqstat_dispatch(eq); 1887c478bd9Sstevel@tonic-gate 189*8aec9182Sstephh if (oqe == NULL) 190*8aec9182Sstephh fmd_list_prepend(&eq->eq_list, eqe); 191*8aec9182Sstephh else 1927c478bd9Sstevel@tonic-gate fmd_list_insert_after(&eq->eq_list, oqe, eqe); 1937c478bd9Sstevel@tonic-gate eq->eq_size++; 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&eq->eq_cv); 197d9638e54Smws (void) pthread_mutex_unlock(&eq->eq_lock); 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate if (!ok) 2007c478bd9Sstevel@tonic-gate fmd_eventq_drop(eq, eqe); 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate fmd_event_t * 2047c478bd9Sstevel@tonic-gate fmd_eventq_delete(fmd_eventq_t *eq) 2057c478bd9Sstevel@tonic-gate { 206d9638e54Smws fmd_eventqstat_t *eqs = eq->eq_stats; 207d9638e54Smws hrtime_t new, delta; 208d9638e54Smws uint32_t wcnt; 209d9638e54Smws 2107c478bd9Sstevel@tonic-gate fmd_eventqelem_t *eqe; 2117c478bd9Sstevel@tonic-gate fmd_event_t *ep; 212d9638e54Smws top: 2137c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&eq->eq_lock); 2147c478bd9Sstevel@tonic-gate 215d9638e54Smws while (!(eq->eq_flags & FMD_EVENTQ_ABORT) && 216d9638e54Smws (eq->eq_size == 0 || (eq->eq_flags & FMD_EVENTQ_SUSPEND))) 2177c478bd9Sstevel@tonic-gate (void) pthread_cond_wait(&eq->eq_cv, &eq->eq_lock); 2187c478bd9Sstevel@tonic-gate 219d9638e54Smws if (eq->eq_flags & FMD_EVENTQ_ABORT) { 2207c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&eq->eq_lock); 2217c478bd9Sstevel@tonic-gate return (NULL); 2227c478bd9Sstevel@tonic-gate } 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate eqe = fmd_list_next(&eq->eq_list); 2257c478bd9Sstevel@tonic-gate fmd_list_delete(&eq->eq_list, eqe); 2267c478bd9Sstevel@tonic-gate eq->eq_size--; 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&eq->eq_lock); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate ep = eqe->eqe_event; 2317c478bd9Sstevel@tonic-gate fmd_free(eqe, sizeof (fmd_eventqelem_t)); 2327c478bd9Sstevel@tonic-gate 233d9638e54Smws /* 234d9638e54Smws * If we dequeued a control event, release it and go back to sleep. 235d9638e54Smws * fmd_event_rele() on the event will block as described in fmd_ctl.c. 236d9638e54Smws * This effectively renders control events invisible to our callers 237d9638e54Smws * as well as to statistics and observability tools (e.g. fmstat(1M)). 238d9638e54Smws */ 239d9638e54Smws if (FMD_EVENT_TYPE(ep) == FMD_EVT_CTL) { 240d9638e54Smws fmd_event_rele(ep); 241d9638e54Smws goto top; 242d9638e54Smws } 243d9638e54Smws 244d9638e54Smws /* 245d9638e54Smws * Before returning, update our statistics. This code is essentially 246d9638e54Smws * kstat_waitq_to_runq(9F), except simplified because our queues are 247d9638e54Smws * always consumed by a single thread (i.e. runq len == 1). 248d9638e54Smws */ 249d9638e54Smws (void) pthread_mutex_lock(eq->eq_stats_lock); 250d9638e54Smws 251d9638e54Smws new = gethrtime(); 252d9638e54Smws delta = new - eqs->eqs_wlastupdate.fmds_value.ui64; 253d9638e54Smws 254d9638e54Smws eqs->eqs_wlastupdate.fmds_value.ui64 = new; 255d9638e54Smws eqs->eqs_dlastupdate.fmds_value.ui64 = new; 256d9638e54Smws 257d9638e54Smws ASSERT(eqs->eqs_wcnt.fmds_value.ui32 != 0); 258d9638e54Smws wcnt = eqs->eqs_wcnt.fmds_value.ui32--; 259d9638e54Smws 260d9638e54Smws eqs->eqs_wlentime.fmds_value.ui64 += delta * wcnt; 261d9638e54Smws eqs->eqs_wtime.fmds_value.ui64 += delta; 262d9638e54Smws 263d9638e54Smws if (FMD_EVENT_TYPE(ep) == FMD_EVT_PROTOCOL) 264d9638e54Smws eqs->eqs_prdequeued.fmds_value.ui64++; 265d9638e54Smws 266d9638e54Smws eqs->eqs_dequeued.fmds_value.ui64++; 267d9638e54Smws (void) pthread_mutex_unlock(eq->eq_stats_lock); 268d9638e54Smws 2697c478bd9Sstevel@tonic-gate return (ep); 2707c478bd9Sstevel@tonic-gate } 2717c478bd9Sstevel@tonic-gate 272d9638e54Smws /* 273d9638e54Smws * Update statistics when an event is done being processed by the eventq's 274d9638e54Smws * consumer thread. This is essentially kstat_runq_exit(9F) simplified for 275d9638e54Smws * our principle that a single thread consumes the queue (i.e. runq len == 1). 276d9638e54Smws */ 277d9638e54Smws void 278d9638e54Smws fmd_eventq_done(fmd_eventq_t *eq) 279d9638e54Smws { 280d9638e54Smws fmd_eventqstat_t *eqs = eq->eq_stats; 281d9638e54Smws hrtime_t new, delta; 282d9638e54Smws 283d9638e54Smws (void) pthread_mutex_lock(eq->eq_stats_lock); 284d9638e54Smws 285d9638e54Smws new = gethrtime(); 286d9638e54Smws delta = new - eqs->eqs_dlastupdate.fmds_value.ui64; 287d9638e54Smws 288d9638e54Smws eqs->eqs_dlastupdate.fmds_value.ui64 = new; 289d9638e54Smws eqs->eqs_dtime.fmds_value.ui64 += delta; 290d9638e54Smws 291d9638e54Smws (void) pthread_mutex_unlock(eq->eq_stats_lock); 292d9638e54Smws } 293d9638e54Smws 2947c478bd9Sstevel@tonic-gate void 2957c478bd9Sstevel@tonic-gate fmd_eventq_cancel(fmd_eventq_t *eq, uint_t type, void *data) 2967c478bd9Sstevel@tonic-gate { 2977c478bd9Sstevel@tonic-gate fmd_eventqelem_t *eqe, *nqe; 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&eq->eq_lock); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate for (eqe = fmd_list_next(&eq->eq_list); eqe != NULL; eqe = nqe) { 3027c478bd9Sstevel@tonic-gate nqe = fmd_list_next(eqe); 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate if (fmd_event_match(eqe->eqe_event, type, data)) { 3057c478bd9Sstevel@tonic-gate fmd_list_delete(&eq->eq_list, eqe); 3067c478bd9Sstevel@tonic-gate eq->eq_size--; 3077c478bd9Sstevel@tonic-gate fmd_event_rele(eqe->eqe_event); 3087c478bd9Sstevel@tonic-gate fmd_free(eqe, sizeof (fmd_eventqelem_t)); 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&eq->eq_lock); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate void 316d9638e54Smws fmd_eventq_suspend(fmd_eventq_t *eq) 317d9638e54Smws { 318d9638e54Smws (void) pthread_mutex_lock(&eq->eq_lock); 319d9638e54Smws eq->eq_flags |= FMD_EVENTQ_SUSPEND; 320d9638e54Smws (void) pthread_mutex_unlock(&eq->eq_lock); 321d9638e54Smws } 322d9638e54Smws 323d9638e54Smws void 324d9638e54Smws fmd_eventq_resume(fmd_eventq_t *eq) 325d9638e54Smws { 326d9638e54Smws (void) pthread_mutex_lock(&eq->eq_lock); 327d9638e54Smws eq->eq_flags &= ~FMD_EVENTQ_SUSPEND; 328d9638e54Smws (void) pthread_cond_broadcast(&eq->eq_cv); 329d9638e54Smws (void) pthread_mutex_unlock(&eq->eq_lock); 330d9638e54Smws } 331d9638e54Smws 332d9638e54Smws void 3337c478bd9Sstevel@tonic-gate fmd_eventq_abort(fmd_eventq_t *eq) 3347c478bd9Sstevel@tonic-gate { 3357c478bd9Sstevel@tonic-gate fmd_eventqelem_t *eqe; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&eq->eq_lock); 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate while ((eqe = fmd_list_next(&eq->eq_list)) != NULL) { 3407c478bd9Sstevel@tonic-gate fmd_list_delete(&eq->eq_list, eqe); 3417c478bd9Sstevel@tonic-gate fmd_event_rele(eqe->eqe_event); 3427c478bd9Sstevel@tonic-gate fmd_free(eqe, sizeof (fmd_eventqelem_t)); 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 345d9638e54Smws eq->eq_flags |= FMD_EVENTQ_ABORT; 3467c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&eq->eq_cv); 347d9638e54Smws (void) pthread_mutex_unlock(&eq->eq_lock); 3487c478bd9Sstevel@tonic-gate } 349