kmsg_dump提供一种机制在系统挂掉的时候将log转存到其他介质如ramoos或者mtd等.我们以mtd中的使用为例来分析kmsg_dump机制.
使用kmsg_dump之前要先使用kmsg_dump_register来注册一个dumper,用于实际的log写函数.
我们先来看看kmsg_dump_register的定义,只定义了一个dump_list,将系统中所有注册的dump都挂在同一个list下,这就说明系统可以注册多个dumper
kernel/printk/printk.c
2819 int kmsg_dump_register(struct kmsg_dumper *dumper)
2820 {
2821 unsigned long flags;
2822 int err = -EBUSY;
2823
2824 /* The dump callback needs to be set */
2825 if (!dumper->dump)
2826 return -EINVAL;
2827
2828 spin_lock_irqsave(&dump_list_lock, flags);
2829 /* Don't allow registering multiple times */
2830 if (!dumper->registered) {
2831 dumper->registered = 1;
2832 list_add_tail_rcu(&dumper->list, &dump_list);
2833 err = 0;
2834 }
2835 spin_unlock_irqrestore(&dump_list_lock, flags);
2836
2837 return err;
2838 }
2839 EXPORT_SYMBOL_GPL(kmsg_dump_register);
下来我们看一个实际中定义的dumper
static void mtdoops_notify_add(struct mtd_info *mtd)
325 {
360 cxt->dump.max_reason = KMSG_DUMP_OOPS;
361 cxt->dump.dump = mtdoops_do_dump;
362 err = kmsg_dump_register(&cxt->dump);
}
361 很给dump.dump赋值。362行注册dump。我们看看mtdoops_do_dump的实现
303 static void mtdoops_do_dump(struct kmsg_dumper *dumper,
304 enum kmsg_dump_reason reason)
305 {
306 struct mtdoops_context *cxt = container_of(dumper,
307 struct mtdoops_context, dump);
308
309 /* Only dump oopses if dump_oops is set */
310 if (reason == KMSG_DUMP_OOPS && !dump_oops)
311 return;
312
313 kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
314 record_size - MTDOOPS_HEADER_SIZE, NULL);
315
316 /* Panics must be written immediately */
317 if (reason != KMSG_DUMP_OOPS)
318 mtdoops_write(cxt, 1);
319
320 /* For other cases, schedule work to write it "nicely" */
321 schedule_work(&cxt->work_write);
322 }
可见在dump中通过kmsg_dump_get_buffer来获取错误信息,再调用mtdoops_write来写到mtd 设备中。
我们重点看看kmsg_dump_get_buffer 是从哪里得到错误信息的呢?
3010 bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
3011 char *buf, size_t size, size_t *len)
3012 {
3013 unsigned long flags;
3014 u64 seq;
3015 u32 idx;
3016 u64 next_seq;
3017 u32 next_idx;
3018 enum log_flags prev;
3019 size_t l = 0;
3020 bool ret = false;
3021
3022 if (!dumper->active)
3023 goto out;
3024
3025 raw_spin_lock_irqsave(&logbuf_lock, flags);
3026 if (dumper->cur_seq < log_first_seq) {
3027 /* messages are gone, move to first available one */
3028 dumper->cur_seq = log_first_seq;
3029 dumper->cur_idx = log_first_idx;
3030 }
3031
3032 /* last entry */
3033 if (dumper->cur_seq >= dumper->next_seq) {
3034 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
3035 goto out;
3036 }
3037
3038 /* calculate length of entire buffer */
3039 seq = dumper->cur_seq;
3040 idx = dumper->cur_idx;
3041 prev = 0;
3042 while (seq < dumper->next_seq) {
3043 struct printk_log *msg = log_from_idx(idx);
3044
3045 l += msg_print_text(msg, prev, true, NULL, 0);
3046 idx = log_next(idx);
3047 seq++;
3048 prev = msg->flags;
3049 }
3050
3051 /* move first record forward until length fits into the buffer */
3052 seq = dumper->cur_seq;
3053 idx = dumper->cur_idx;
3054 prev = 0;
3055 while (l > size && seq < dumper->next_seq) {
3056 struct printk_log *msg = log_from_idx(idx);
3057
3058 l -= msg_print_text(msg, prev, true, NULL, 0);
3059 idx = log_next(idx);
3060 seq++;
3061 prev = msg->flags;
3062 }
3063
3064 /* last message in next interation */
3065 next_seq = seq;
3066 next_idx = idx;
3067
3068 l = 0;
3069 while (seq < dumper->next_seq) {
3070 struct printk_log *msg = log_from_idx(idx);
3071
3072 l += msg_print_text(msg, prev, syslog, buf + l, size - l);
3073 idx = log_next(idx);
3074 seq++;
3075 prev = msg->flags;
3076 }
3077
3078 dumper->next_seq = next_seq;
3079 dumper->next_idx = next_idx;
3080 ret = true;
3081 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
3082 out:
3083 if (len)
3084 *len = l;
3085 return ret;
3086 }
3087 EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
从这个函数中 log_from_idx可知,dump转存的错误信息是从prink的log_buf中获得的.
使用kmsg_dump之前要先使用kmsg_dump_register来注册一个dumper,用于实际的log写函数.
我们先来看看kmsg_dump_register的定义,只定义了一个dump_list,将系统中所有注册的dump都挂在同一个list下,这就说明系统可以注册多个dumper
kernel/printk/printk.c
2819 int kmsg_dump_register(struct kmsg_dumper *dumper)
2820 {
2821 unsigned long flags;
2822 int err = -EBUSY;
2823
2824 /* The dump callback needs to be set */
2825 if (!dumper->dump)
2826 return -EINVAL;
2827
2828 spin_lock_irqsave(&dump_list_lock, flags);
2829 /* Don't allow registering multiple times */
2830 if (!dumper->registered) {
2831 dumper->registered = 1;
2832 list_add_tail_rcu(&dumper->list, &dump_list);
2833 err = 0;
2834 }
2835 spin_unlock_irqrestore(&dump_list_lock, flags);
2836
2837 return err;
2838 }
2839 EXPORT_SYMBOL_GPL(kmsg_dump_register);
下来我们看一个实际中定义的dumper
static void mtdoops_notify_add(struct mtd_info *mtd)
325 {
360 cxt->dump.max_reason = KMSG_DUMP_OOPS;
361 cxt->dump.dump = mtdoops_do_dump;
362 err = kmsg_dump_register(&cxt->dump);
}
361 很给dump.dump赋值。362行注册dump。我们看看mtdoops_do_dump的实现
303 static void mtdoops_do_dump(struct kmsg_dumper *dumper,
304 enum kmsg_dump_reason reason)
305 {
306 struct mtdoops_context *cxt = container_of(dumper,
307 struct mtdoops_context, dump);
308
309 /* Only dump oopses if dump_oops is set */
310 if (reason == KMSG_DUMP_OOPS && !dump_oops)
311 return;
312
313 kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
314 record_size - MTDOOPS_HEADER_SIZE, NULL);
315
316 /* Panics must be written immediately */
317 if (reason != KMSG_DUMP_OOPS)
318 mtdoops_write(cxt, 1);
319
320 /* For other cases, schedule work to write it "nicely" */
321 schedule_work(&cxt->work_write);
322 }
可见在dump中通过kmsg_dump_get_buffer来获取错误信息,再调用mtdoops_write来写到mtd 设备中。
我们重点看看kmsg_dump_get_buffer 是从哪里得到错误信息的呢?
3010 bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
3011 char *buf, size_t size, size_t *len)
3012 {
3013 unsigned long flags;
3014 u64 seq;
3015 u32 idx;
3016 u64 next_seq;
3017 u32 next_idx;
3018 enum log_flags prev;
3019 size_t l = 0;
3020 bool ret = false;
3021
3022 if (!dumper->active)
3023 goto out;
3024
3025 raw_spin_lock_irqsave(&logbuf_lock, flags);
3026 if (dumper->cur_seq < log_first_seq) {
3027 /* messages are gone, move to first available one */
3028 dumper->cur_seq = log_first_seq;
3029 dumper->cur_idx = log_first_idx;
3030 }
3031
3032 /* last entry */
3033 if (dumper->cur_seq >= dumper->next_seq) {
3034 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
3035 goto out;
3036 }
3037
3038 /* calculate length of entire buffer */
3039 seq = dumper->cur_seq;
3040 idx = dumper->cur_idx;
3041 prev = 0;
3042 while (seq < dumper->next_seq) {
3043 struct printk_log *msg = log_from_idx(idx);
3044
3045 l += msg_print_text(msg, prev, true, NULL, 0);
3046 idx = log_next(idx);
3047 seq++;
3048 prev = msg->flags;
3049 }
3050
3051 /* move first record forward until length fits into the buffer */
3052 seq = dumper->cur_seq;
3053 idx = dumper->cur_idx;
3054 prev = 0;
3055 while (l > size && seq < dumper->next_seq) {
3056 struct printk_log *msg = log_from_idx(idx);
3057
3058 l -= msg_print_text(msg, prev, true, NULL, 0);
3059 idx = log_next(idx);
3060 seq++;
3061 prev = msg->flags;
3062 }
3063
3064 /* last message in next interation */
3065 next_seq = seq;
3066 next_idx = idx;
3067
3068 l = 0;
3069 while (seq < dumper->next_seq) {
3070 struct printk_log *msg = log_from_idx(idx);
3071
3072 l += msg_print_text(msg, prev, syslog, buf + l, size - l);
3073 idx = log_next(idx);
3074 seq++;
3075 prev = msg->flags;
3076 }
3077
3078 dumper->next_seq = next_seq;
3079 dumper->next_idx = next_idx;
3080 ret = true;
3081 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
3082 out:
3083 if (len)
3084 *len = l;
3085 return ret;
3086 }
3087 EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
从这个函数中 log_from_idx可知,dump转存的错误信息是从prink的log_buf中获得的.