int do_syslog(int type, char __user *buf, int len, bool from_file) { unsigned i, j, limit, count; int do_clear = 0; char c; int error; error = check_syslog_permissions(type, from_file); if (error) goto out; error = security_syslog(type); if (error) return error; switch (type) { case SYSLOG_ACTION_CLOSE: /* Close log */ break; case SYSLOG_ACTION_OPEN: /* Open log */ break; case SYSLOG_ACTION_READ: /* Read from log */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } error = wait_event_interruptible(log_wait, (log_start - log_end)); if (error) goto out; i = 0; raw_spin_lock_irq(&logbuf_lock); while (!error && (log_start != log_end) && i < len) { c = LOG_BUF(log_start); log_start++; raw_spin_unlock_irq(&logbuf_lock); error = __put_user(c,buf); buf++; i++; cond_resched(); raw_spin_lock_irq(&logbuf_lock); } raw_spin_unlock_irq(&logbuf_lock); if (!error) error = i; break; /* Read/clear last kernel messages */ case SYSLOG_ACTION_READ_CLEAR: do_clear = 1; /* FALL THRU */ /* Read last kernel messages */ case SYSLOG_ACTION_READ_ALL: error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } count = len; if (count > log_buf_len) count = log_buf_len; raw_spin_lock_irq(&logbuf_lock); if (count > logged_chars) count = logged_chars; if (do_clear) logged_chars = 0; limit = log_end; /* * __put_user() could sleep, and while we sleep * printk() could overwrite the messages * we try to copy to user space. Therefore * the messages are copied in reverse. <manfreds> */ for (i = 0; i < count && !error; i++) { j = limit-1-i; if (j + log_buf_len < log_end) break; c = LOG_BUF(j); raw_spin_unlock_irq(&logbuf_lock); error = __put_user(c,&buf[count-1-i]); cond_resched(); raw_spin_lock_irq(&logbuf_lock); } raw_spin_unlock_irq(&logbuf_lock); if (error) break; error = i; if (i != count) { int offset = count-error; /* buffer overflow during copy, correct user buffer. */ for (i = 0; i < error; i++) { if (__get_user(c,&buf[i+offset]) || __put_user(c,&buf[i])) { error = -EFAULT; break; } cond_resched(); } } break; /* Clear ring buffer */ case SYSLOG_ACTION_CLEAR: logged_chars = 0; break; /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_OFF: if (saved_console_loglevel == -1) saved_console_loglevel = console_loglevel; console_loglevel = minimum_console_loglevel; break; /* Enable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: if (saved_console_loglevel != -1) { console_loglevel = saved_console_loglevel; saved_console_loglevel = -1; } break; /* Set level of messages printed to console */ case SYSLOG_ACTION_CONSOLE_LEVEL: error = -EINVAL; if (len < 1 || len > 8) goto out; if (len < minimum_console_loglevel) len = minimum_console_loglevel; console_loglevel = len; /* Implicitly re-enable logging to console */ saved_console_loglevel = -1; error = 0; break; /* Number of chars in the log buffer */ case SYSLOG_ACTION_SIZE_UNREAD: error = log_end - log_start; break; /* Size of the log buffer */ case SYSLOG_ACTION_SIZE_BUFFER: error = log_buf_len; break; default: error = -EINVAL; break; } out: return error; }
int do_syslog(int type, char __user *buf, int len, bool from_file) { unsigned i, j, limit, count; int do_clear = 0; char c; int error; error = check_syslog_permissions(type, from_file); if (error) goto out; error = security_syslog(type); if (error) return error; switch (type) { case SYSLOG_ACTION_CLOSE: break; case SYSLOG_ACTION_OPEN: break; case SYSLOG_ACTION_READ: error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } error = wait_event_interruptible(log_wait, (log_start - log_end)); if (error) goto out; i = 0; raw_spin_lock_irq(&logbuf_lock); while (!error && (log_start != log_end) && i < len) { c = LOG_BUF(log_start); log_start++; raw_spin_unlock_irq(&logbuf_lock); error = __put_user(c,buf); buf++; i++; cond_resched(); raw_spin_lock_irq(&logbuf_lock); } raw_spin_unlock_irq(&logbuf_lock); if (!error) error = i; break; case SYSLOG_ACTION_READ_CLEAR: do_clear = 1; case SYSLOG_ACTION_READ_ALL: error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } count = len; if (count > log_buf_len) count = log_buf_len; raw_spin_lock_irq(&logbuf_lock); if (count > logged_chars) count = logged_chars; if (do_clear) logged_chars = 0; limit = log_end; for (i = 0; i < count && !error; i++) { j = limit-1-i; if (j + log_buf_len < log_end) break; c = LOG_BUF(j); raw_spin_unlock_irq(&logbuf_lock); error = __put_user(c,&buf[count-1-i]); cond_resched(); raw_spin_lock_irq(&logbuf_lock); } raw_spin_unlock_irq(&logbuf_lock); if (error) break; error = i; if (i != count) { int offset = count-error; for (i = 0; i < error; i++) { if (__get_user(c,&buf[i+offset]) || __put_user(c,&buf[i])) { error = -EFAULT; break; } cond_resched(); } } break; case SYSLOG_ACTION_CLEAR: logged_chars = 0; break; case SYSLOG_ACTION_CONSOLE_OFF: if (saved_console_loglevel == -1) saved_console_loglevel = console_loglevel; console_loglevel = minimum_console_loglevel; break; case SYSLOG_ACTION_CONSOLE_ON: if (saved_console_loglevel != -1) { console_loglevel = saved_console_loglevel; saved_console_loglevel = -1; } break; case SYSLOG_ACTION_CONSOLE_LEVEL: error = -EINVAL; if (len < 1 || len > 8) goto out; if (len < minimum_console_loglevel) len = minimum_console_loglevel; console_loglevel = len; saved_console_loglevel = -1; error = 0; break; case SYSLOG_ACTION_SIZE_UNREAD: error = log_end - log_start; break; case SYSLOG_ACTION_SIZE_BUFFER: error = log_buf_len; break; default: error = -EINVAL; break; } out: return error; }
static int stlog_print_all(char __user *buf, int size, bool clear) { char *text; int len = 0; text = kmalloc(RINGBUF_LINE_MAX + S_PREFIX_MAX, GFP_KERNEL); if (!text) return -ENOMEM; raw_spin_lock_irq(&ringbuf_lock); if (buf) { u64 next_seq; u64 seq; u32 idx; enum ringbuf_flags prev; if (stlog_clear_seq < ringbuf_first_seq) { /* messages are gone, move to first available one */ stlog_clear_seq = ringbuf_first_seq; stlog_clear_idx = ringbuf_first_idx; } seq = stlog_clear_seq; idx = stlog_clear_idx; prev = 0; while (seq < ringbuf_next_seq) { struct ringbuf *msg = ringbuf_from_idx(idx); len += stlog_print_text(msg, prev, false, NULL, 0); prev = msg->flags; idx = ringbuf_next(idx); seq++; } /* move first record forward until length fits into the buffer */ seq = stlog_clear_seq; idx = stlog_clear_idx; prev = 0; while (len > size && seq < ringbuf_next_seq) { struct ringbuf *msg = ringbuf_from_idx(idx); len -= stlog_print_text(msg, prev, false, NULL, 0); prev = msg->flags; idx = ringbuf_next(idx); seq++; } /* last message fitting into this dump */ next_seq = ringbuf_next_seq; len = 0; prev = 0; while (len >= 0 && seq < next_seq) { struct ringbuf *msg = ringbuf_from_idx(idx); int textlen; textlen = stlog_print_text(msg, prev, false, text, RINGBUF_LINE_MAX + S_PREFIX_MAX); if (textlen < 0) { len = textlen; break; } idx = ringbuf_next(idx); seq++; prev = msg->flags; raw_spin_unlock_irq(&ringbuf_lock); if (copy_to_user(buf + len, text, textlen)) len = -EFAULT; else{ #ifdef DEBUG_STLOG printk("[STLOG] %s seq %llu text %s \n",__func__,seq,text); #endif len += textlen; } raw_spin_lock_irq(&ringbuf_lock); if (seq < ringbuf_first_seq) { /* messages are gone, move to next one */ seq = ringbuf_first_seq; idx = ringbuf_first_idx; prev = 0; } } } if (clear) { stlog_clear_seq = ringbuf_next_seq; stlog_clear_idx = ringbuf_next_idx; } raw_spin_unlock_irq(&ringbuf_lock); kfree(text); return len; }
/* * schedule() is the main scheduler function. */ asmlinkage void __sched schedule(void) { struct task_struct *prev, *next; unsigned long *switch_count; struct rq *rq; int cpu; need_resched: preempt_disable(); cpu = smp_processor_id(); rq = cpu_rq(cpu); rcu_note_context_switch(cpu); prev = rq->curr; schedule_debug(prev); if (sched_feat(HRTICK)) hrtick_clear(rq); raw_spin_lock_irq(&rq->lock); switch_count = &prev->nivcsw; if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { if (unlikely(signal_pending_state(prev->state, prev))) { prev->state = TASK_RUNNING; } else { /* * If a worker is going to sleep, notify and * ask workqueue whether it wants to wake up a * task to maintain concurrency. If so, wake * up the task. */ if (prev->flags & PF_WQ_WORKER) { struct task_struct *to_wakeup; to_wakeup = wq_worker_sleeping(prev, cpu); if (to_wakeup) try_to_wake_up_local(to_wakeup); } deactivate_task(rq, prev, DEQUEUE_SLEEP); /* * If we are going to sleep and we have plugged IO queued, make * sure to submit it to avoid deadlocks. */ if (blk_needs_flush_plug(prev)) { raw_spin_unlock(&rq->lock); blk_schedule_flush_plug(prev); raw_spin_lock(&rq->lock); } } switch_count = &prev->nvcsw; } pre_schedule(rq, prev); if (unlikely(!rq->nr_running)) idle_balance(cpu, rq); put_prev_task(rq, prev); next = pick_next_task(rq); clear_tsk_need_resched(prev); rq->skip_clock_update = 0; if (likely(prev != next)) { rq->nr_switches++; rq->curr = next; ++*switch_count; context_switch(rq, prev, next); /* unlocks the rq */ /* * The context switch have flipped the stack from under us * and restored the local variables which were saved when * this task called schedule() in the past. prev == current * is still correct, but it can be moved to another cpu/rq. */ cpu = smp_processor_id(); rq = cpu_rq(cpu); } else raw_spin_unlock_irq(&rq->lock); post_schedule(rq); preempt_enable_no_resched(); if (need_resched()) goto need_resched; }