/* * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include "eventlib.h" #include "tracebuf.h" #include "eventlib_init.h" #include "eventlib_flt.h" /* This eventlib version supports only one version of filtering */ #define FLT_COMPAT_INFO 0 /***************************************************************************** * Filter Mask Exchange Protocol *****************************************************************************/ /* Get n-th slot in r2w memory */ static inline shmptr struct eventlib_flt_slot *flt_r2w_slot( struct eventlib_flt_ctx *flt, uint8_t n) { return (struct eventlib_flt_slot *) ((uintptr_t)(&flt->r2w->slots[0]) + n * flt->slot_size); } /* Reader: push local mask to slot */ static void flt_reader_push(struct eventlib_flt_ctx *flt) { uint8_t n = flt->r.slot_index; shmptr struct eventlib_flt_slot *slot = flt_r2w_slot(flt, n); uint32_t seqlock, ack; /* This is not first access to seqlock of this slot by this reader. * Nobody should have changed seqlock since previous access (unless * somebody violates access protocol... but in that caase, barrier * won't help anyway) */ seqlock = slot->seqlock; slot->seqlock = ++seqlock; write_barrier(); memcpy(slot->mask, flt->r.mask, MAX_MASK_SIZE); write_barrier(); slot->seqlock = ++seqlock; write_barrier(); read_barrier(); ack = flt->r2w->ack; if (ack & (1u << n)) sync_clear_bit(n, &flt->r2w->notify); else sync_set_bit(n, &flt->r2w->notify); } /* Writer: fetch any slot updates from readers */ static void flt_writer_refresh(struct eventlib_flt_ctx *flt) { uint8_t n; shmptr struct eventlib_flt_slot *slot; uint32_t notify, dirty, seqlock, remaining; bool fetched = false; unsigned int i; uint32_t *s, *d, v; /* quick check for updates */ read_barrier(); notify = flt->r2w->notify; dirty = notify ^ flt->w.ack; if (!dirty) return; /* continue only if update exists */ flt->r2w->ack = flt->w.ack = notify; write_barrier(); /* fetch any updated masks */ for (n = 0; dirty && n < NUM_SLOTS; n++) { if (!(dirty & (1u << n))) continue; dirty &= ~(1u << n); slot = flt_r2w_slot(flt, n); read_barrier(); seqlock = slot->seqlock; if ((seqlock & 1u) != 0) continue; read_barrier(); memcpy(flt->w.spare_mask, slot->mask, MAX_MASK_SIZE); read_barrier(); if (seqlock != slot->seqlock) continue; memcpy(&flt->w.mask_copy[n][0], flt->w.spare_mask, MAX_MASK_SIZE); flt->w.mask_copy_valid |= (1u << n); fetched = true; } /* if nothing fetched, return */ if (!fetched) return; /* re-calculate combined mask */ memset(flt->w.combined_mask, 0, MAX_MASK_SIZE); remaining = flt->w.mask_copy_valid; for (n = 0; remaining && n < NUM_SLOTS; n++) { if (!(remaining & (1u << n))) continue; remaining &= ~(1u << n); s = (uint32_t *)&flt->w.mask_copy[n][0]; d = (uint32_t *)flt->w.combined_mask; v = 0; for (i = 0; i < MAX_MASK_SIZE / 4; i++) { d[i] |= s[i]; v |= s[i]; } /* discover and discard full-zero slot copies */ if (!v) flt->w.mask_copy_valid &= ~(1u << n); } } /* Reader: allocate slot */ static int flt_reader_alloc_slot(struct eventlib_flt_ctx *flt) { shmptr struct eventlib_flt_slot *slot; uint32_t busy, all_slots_mask, seqlock; uint8_t n; all_slots_mask = (uint32_t)((1ull << NUM_SLOTS) - 1ull); while (1) { /* Barrier here ensures a new read of flt->r2w->busy from * memory. Still it could be updated by other actors * immediately after that read, but since it is used as a hint * to choose bits to test, it's not a problem */ read_barrier(); busy = flt->r2w->busy; if ((busy & all_slots_mask) == all_slots_mask) { /* There was no free slot at time of mask was * read from memory. Threat that as no free slot * error */ return -EBUSY; } /* Slot was available at time of mask read. Find it and * try to synchronously allocate it. This can race against * other reader doing the same and thus can fail - thus * entire operation is executed in loop */ for (n = 0; n < NUM_SLOTS; n++) { if (busy & (1u << n)) continue; if (sync_test_and_set_bit(n, &flt->r2w->busy) == 0) goto success; } } success: flt->r.slot_index = n; slot = flt_r2w_slot(flt, n); /* Just for case, ensure clean state at init */ /* This is the first access to seqlock of this slot by this reader. * Per generic rule, have a read barrier before accessing it, to force * read from memory. Although situation of no physical read here is * absolutely exotic */ read_barrier(); seqlock = slot->seqlock; if (seqlock & 1u) { slot->seqlock = ++seqlock; write_barrier(); } return 0; } /* Reader: release slot */ static void flt_reader_release_slot(struct eventlib_flt_ctx *flt) { /* push all-zero mask */ memset(flt->r.mask, 0, MAX_MASK_SIZE); flt_reader_push(flt); /* clear busy bit */ sync_clear_bit(flt->r.slot_index, &flt->r2w->busy); } static void flt_reader_set_all_bits(struct eventlib_flt_ctx *flt) { struct eventlib_flt_domain_geo *geo; uint8_t *p; int i, j; for (i = 0; i < EVENTLIB_FILTER_DOMAIN_MAX; i++) { geo = &flt->geo[i]; p = &flt->r.mask[geo->offset]; for (j = 0; j < geo->bits / 8; j++) *p++ = 0xff; if (geo->bits % 8) *p = (uint8_t)((1u << (geo->bits % 8)) - 1u); } } /***************************************************************************** * Filter Mask Control Initialization *****************************************************************************/ /* Init constants in eventlib_flt_ctx */ static int flt_init_consts(struct eventlib_flt_ctx *flt, uint16_t *num_bits) { struct eventlib_flt_domain_geo *geo; uint16_t bytes = 0; int i; for (i = 0; i < EVENTLIB_FILTER_DOMAIN_MAX; i++) { geo = &flt->geo[i]; geo->bits = num_bits[i]; geo->offset = bytes; bytes = (uint16_t)(bytes + EVENTLIB_FLT_MASK_SIZE(geo->bits)); } if (bytes > MAX_MASK_SIZE) return -ENOMEM; flt->slot_size = (uint32_t)sizeof(struct eventlib_flt_slot) + bytes; return 0; } /* Get r2w memory size (after flt_init_consts() completed) */ static inline uint32_t flt_r2w_size(struct eventlib_flt_ctx *flt) { return ((uint32_t)sizeof(struct eventlib_flt_r2w) + (NUM_SLOTS * flt->slot_size)); } static int flt_writer_init(struct eventlib_ctx *ctx) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint32_t size; int ret; memset(flt, 0, sizeof(*flt)); if (!(ctx->flags & EVENTLIB_FLAG_INIT_FILTERING)) return 0; if (ctx->flt_num_bits[EVENTLIB_FILTER_DOMAIN_EVENT_TYPE] < 1) return -EINVAL; ret = flt_init_consts(flt, ctx->flt_num_bits); if (ret != 0) return ret; /* initialize w2r shared memory area */ ret = subsys_w2r_carve(ctx, FILTERING, sizeof(struct eventlib_flt_w2r)); if (ret != 0) return ret; flt->w2r = subsys_w2r_area(ctx, FILTERING); size = subsys_w2r_size(ctx, FILTERING); if (size != sizeof(struct eventlib_flt_w2r)) return -ENOSPC; flt->w2r->compat = FLT_COMPAT_INFO; memcpy(flt->w2r->num_bits, ctx->flt_num_bits, sizeof(ctx->flt_num_bits)); /* initialize r2w shared memory area */ ret = subsys_r2w_carve(ctx, FILTERING, flt_r2w_size(flt)); if (ret != 0) return ret; flt->r2w = subsys_r2w_area(ctx, FILTERING); size = subsys_r2w_size(ctx, FILTERING); if (size != flt_r2w_size(flt)) return -ENOSPC; memset(flt->r2w, 0, size); flt->inited = true; return 0; } static int flt_reader_init(struct eventlib_ctx *ctx) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint32_t w2r_size = subsys_w2r_size(ctx, FILTERING); uint32_t r2w_size = subsys_r2w_size(ctx, FILTERING); int ret; memset(flt, 0, sizeof(*flt)); /* check if writer disabled filtering */ if ((w2r_size == 0) && (r2w_size == 0)) { memset(ctx->flt_num_bits, 0, sizeof(ctx->flt_num_bits)); return 0; } /* process w2r shared memory area */ if (w2r_size != sizeof(struct eventlib_flt_w2r)) return -EPROTONOSUPPORT; flt->w2r = subsys_w2r_area(ctx, FILTERING); if (flt->w2r->compat != FLT_COMPAT_INFO) return -EPROTONOSUPPORT; memcpy(ctx->flt_num_bits, flt->w2r->num_bits, sizeof(flt->w2r->num_bits)); if (ctx->flt_num_bits[EVENTLIB_FILTER_DOMAIN_EVENT_TYPE] < 1) return -EIO; ret = flt_init_consts(flt, ctx->flt_num_bits); if (ret != 0) return ret; /* process r2w shared memory area */ if (r2w_size != flt_r2w_size(flt)) return -EIO; flt->r2w = subsys_r2w_area(ctx, FILTERING); ret = flt_reader_alloc_slot(flt); if (ret != 0) return ret; if (!(ctx->flags & EVENTLIB_FLAG_INIT_FILTERING)) flt_reader_set_all_bits(flt); flt->inited = true; return 0; } int flt_init(struct eventlib_ctx *ctx) { int ret; switch (ctx->direction) { case EVENTLIB_DIRECTION_WRITER: ret = flt_writer_init(ctx); break; case EVENTLIB_DIRECTION_READER: ret = flt_reader_init(ctx); break; default: ret = -EINVAL; break; } return ret; } void flt_fini(struct eventlib_ctx *ctx) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; if (ctx->direction != EVENTLIB_DIRECTION_READER) return; if (flt->inited) flt_reader_release_slot(flt); } /***************************************************************************** * Filter Mask Access Implementation *****************************************************************************/ int eventlib_get_num_attached_readers(struct eventlib_ctx *ctx) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint32_t busy; int ret; if (!flt->inited) return -EPROTO; if (ctx->direction != EVENTLIB_DIRECTION_WRITER) return -EPROTO; read_barrier(); busy = flt->r2w->busy; ret = 0; while (busy) { if (busy & 1u) ret++; busy >>= 1; } return ret; } int eventlib_get_filter_mask(struct eventlib_ctx *ctx, enum eventlib_filter_domain domain, eventlib_bitmask_t mask) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; if (!flt->inited) return -EPROTO; if (domain >= EVENTLIB_FILTER_DOMAIN_MAX) return -EINVAL; if (ctx->direction == EVENTLIB_DIRECTION_WRITER) { flt_writer_refresh(flt); memcpy(mask, flt->w.combined_mask + flt->geo[domain].offset, EVENTLIB_FLT_MASK_SIZE(flt->geo[domain].bits)); } else memcpy(mask, flt->r.mask + flt->geo[domain].offset, EVENTLIB_FLT_MASK_SIZE(flt->geo[domain].bits)); return 0; } int eventlib_check_filter_bit(struct eventlib_ctx *ctx, enum eventlib_filter_domain domain, uint16_t bit) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint8_t *p, m; if (!flt->inited) return -EPROTO; if (domain >= EVENTLIB_FILTER_DOMAIN_MAX) return -EINVAL; if (bit >= flt->geo[domain].bits) return -EINVAL; if (ctx->direction == EVENTLIB_DIRECTION_WRITER) { flt_writer_refresh(flt); p = flt->w.combined_mask; } else p = flt->r.mask; p += flt->geo[domain].offset + (bit / 8); m = (uint8_t)(1u << (bit % 8)); return ((*p) & m) ? 1 : 0; } int eventlib_check_filter_mask(struct eventlib_ctx *ctx, enum eventlib_filter_domain domain, eventlib_bitmask_t mask) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint8_t *a, *b, m; uint32_t size; if (!flt->inited) return -EPROTO; if (domain >= EVENTLIB_FILTER_DOMAIN_MAX) return -EINVAL; if (ctx->direction == EVENTLIB_DIRECTION_WRITER) { flt_writer_refresh(flt); a = flt->w.combined_mask + flt->geo[domain].offset; } else a = flt->r.mask; size = flt->geo[domain].bits; b = mask; while (size >= 32) { if ((*((uint32_t *)a)) & (*((uint32_t *)b))) return 1; a += 4; b += 4; size -= 32; } while (size >= 8) { if ((*a) & (*b)) return 1; a++; b++; size -= 8; } if (size > 0) { m = (uint8_t)((1u << size) - 1u); if (((*a) & (*b) & m) != 0) return 1; } return 0; } int eventlib_set_filter_bit(struct eventlib_ctx *ctx, enum eventlib_filter_domain domain, uint16_t bit, int val) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; uint8_t *p, m; if (ctx->direction != EVENTLIB_DIRECTION_READER) return -EPROTO; if (!flt->inited) return -EPROTO; if (domain >= EVENTLIB_FILTER_DOMAIN_MAX) return -EINVAL; if (bit >= flt->geo[domain].bits) return -EINVAL; if (val != 0 && val != 1) return -EINVAL; p = flt->r.mask + flt->geo[domain].offset + (bit / 8); m = (uint8_t)(1u << (bit % 8)); if (val) *p |= m; else *p &= (uint8_t)(~m); flt_reader_push(flt); return 0; } int eventlib_set_filter_mask(struct eventlib_ctx *ctx, enum eventlib_filter_domain domain, eventlib_bitmask_t mask) { struct eventlib_flt_ctx *flt = &ctx->priv->flt; if (ctx->direction != EVENTLIB_DIRECTION_READER) return -EPROTO; if (!flt->inited) return -EPROTO; if (domain >= EVENTLIB_FILTER_DOMAIN_MAX) return -EINVAL; memcpy(flt->r.mask + flt->geo[domain].offset, mask, EVENTLIB_FLT_MASK_SIZE(flt->geo[domain].bits)); flt_reader_push(flt); return 0; }