+ return error;
+}
+
+/*ARGSUSED*/
+int
+oslog_streamread(__unused dev_t dev, struct uio *uio, int flag)
+{
+ int error = 0;
+ int copy_size = 0;
+ static char logline[FIREHOSE_CHUNK_SIZE];
+
+ stream_lock();
+
+ if (!oslog_stream_open) {
+ stream_unlock();
+ return EBADF;
+ }
+
+ while (STAILQ_EMPTY(&oslog_stream_buf_head)) {
+ assert(oslog_stream_buf_bytesavail == oslog_stream_buf_size);
+
+ if (flag & IO_NDELAY || oslog_streamsoftc.sc_state & LOG_NBIO) {
+ stream_unlock();
+ return EWOULDBLOCK;
+ }
+
+ oslog_streamsoftc.sc_state |= LOG_RDWAIT;
+ wait_result_t wr = assert_wait((event_t)oslog_streambufp,
+ THREAD_INTERRUPTIBLE);
+ if (wr == THREAD_WAITING) {
+ stream_unlock();
+ wr = thread_block(THREAD_CONTINUE_NULL);
+ stream_lock();
+ }
+
+ switch (wr) {
+ case THREAD_AWAKENED:
+ case THREAD_TIMED_OUT:
+ break;
+ default:
+ stream_unlock();
+ return EINTR;
+ }
+ }
+
+ if (!oslog_stream_open) {
+ stream_unlock();
+ return EBADF;
+ }
+
+ int logpos = 0;
+ oslog_stream_buf_entry_t read_entry = NULL;
+ uint16_t rec_length;
+
+ read_entry = STAILQ_FIRST(&oslog_stream_buf_head);
+ assert(read_entry != NULL);
+ STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
+
+ // Copy the timestamp first
+ memcpy(logline + logpos, &read_entry->timestamp, sizeof(uint64_t));
+ logpos += sizeof(uint64_t);
+
+ switch (read_entry->type) {
+ /* Handle metadata messages */
+ case oslog_stream_link_type_metadata:
+ {
+ memcpy(logline + logpos,
+ (read_entry->metadata), read_entry->size);
+ logpos += read_entry->size;
+
+ stream_unlock();
+
+ // Free the list entry
+ kfree(read_entry, (sizeof(struct oslog_stream_buf_entry_s) + read_entry->size));
+ break;
+ }
+ /* Handle log messages */
+ case oslog_stream_link_type_log:
+ {
+ /* ensure that the correct read entry was dequeued */
+ assert(read_entry->offset == oslog_streambufp->msg_bufr);
+ rec_length = read_entry->size;
+
+ // If the next log line is contiguous in the buffer, copy it out.
+ if (read_entry->offset + rec_length <= oslog_streambufp->msg_size) {
+ memcpy(logline + logpos,
+ oslog_streambufp->msg_bufc + read_entry->offset, rec_length);
+
+ oslog_streambufp->msg_bufr += rec_length;
+ if (oslog_streambufp->msg_bufr == oslog_streambufp->msg_size) {
+ oslog_streambufp->msg_bufr = 0;
+ }
+ logpos += rec_length;
+ } else {
+ // Otherwise, copy until the end of the buffer, and
+ // copy the remaining bytes starting at index 0.
+ int bytes_left = oslog_streambufp->msg_size - read_entry->offset;
+ memcpy(logline + logpos,
+ oslog_streambufp->msg_bufc + read_entry->offset, bytes_left);
+ logpos += bytes_left;
+ rec_length -= bytes_left;
+
+ memcpy(logline + logpos, (const void *)oslog_streambufp->msg_bufc,
+ rec_length);
+ oslog_streambufp->msg_bufr = rec_length;
+ logpos += rec_length;
+ }
+
+ oslog_stream_buf_bytesavail += read_entry->size;
+ assert(oslog_stream_buf_bytesavail <= oslog_stream_buf_size);
+
+ assert(oslog_streambufp->msg_bufr < oslog_streambufp->msg_size);
+ STAILQ_INSERT_TAIL(&oslog_stream_free_head, read_entry, buf_entries);
+
+ stream_unlock();
+ break;
+ }
+ default:
+ {
+ panic("Got unexpected log entry type: %hhu\n", read_entry->type);
+ }
+ }
+
+ copy_size = min(logpos, uio_resid(uio));
+ if (copy_size != 0) {
+ error = uiomove((caddr_t)logline, copy_size, uio);
+ }
+ (void)hw_atomic_add(&oslog_s_streamed_msgcount, 1);
+
+ return error;