*             See: clock_gettime(CLOCK_BOOTTIME)
  *     Return
  *             Current *ktime*.
+ *
+ * int bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len)
+ *     Description
+ *             seq_printf uses seq_file seq_printf() to print out the format string.
+ *             The *m* represents the seq_file. The *fmt* and *fmt_size* are for
+ *             the format string itself. The *data* and *data_len* are format string
+ *             arguments. The *data* are a u64 array and corresponding format string
+ *             values are stored in the array. For strings and pointers where pointees
+ *             are accessed, only the pointer values are stored in the *data* array.
+ *             The *data_len* is the *data* size in term of bytes.
+ *
+ *             Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory.
+ *             Reading kernel memory may fail due to either invalid address or
+ *             valid address but requiring a major memory fault. If reading kernel memory
+ *             fails, the string for **%s** will be an empty string, and the ip
+ *             address for **%p{i,I}{4,6}** will be 0. Not returning error to
+ *             bpf program is consistent with what bpf_trace_printk() does for now.
+ *     Return
+ *             0 on success, or a negative errno in case of failure.
+ *
+ *             * **-EBUSY**            Percpu memory copy buffer is busy, can try again
+ *                                     by returning 1 from bpf program.
+ *             * **-EINVAL**           Invalid arguments, or invalid/unsupported formats.
+ *             * **-E2BIG**            Too many format specifiers.
+ *             * **-EOVERFLOW**        Overflow happens, the same object will be tried again.
+ *
+ * int bpf_seq_write(struct seq_file *m, const void *data, u32 len)
+ *     Description
+ *             seq_write uses seq_file seq_write() to write the data.
+ *             The *m* represents the seq_file. The *data* and *len* represent the
+ *             data to write in bytes.
+ *     Return
+ *             0 on success, or a negative errno in case of failure.
+ *
+ *             * **-EOVERFLOW**        Overflow happens, the same object will be tried again.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(get_netns_cookie),           \
        FN(get_current_ancestor_cgroup_id),     \
        FN(sk_assign),                  \
-       FN(ktime_get_boot_ns),
+       FN(ktime_get_boot_ns),          \
+       FN(seq_printf),                 \
+       FN(seq_write),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
 
        return &bpf_trace_printk_proto;
 }
 
+#define MAX_SEQ_PRINTF_VARARGS         12
+#define MAX_SEQ_PRINTF_MAX_MEMCPY      6
+#define MAX_SEQ_PRINTF_STR_LEN         128
+
+struct bpf_seq_printf_buf {
+       char buf[MAX_SEQ_PRINTF_MAX_MEMCPY][MAX_SEQ_PRINTF_STR_LEN];
+};
+static DEFINE_PER_CPU(struct bpf_seq_printf_buf, bpf_seq_printf_buf);
+static DEFINE_PER_CPU(int, bpf_seq_printf_buf_used);
+
+BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size,
+          const void *, data, u32, data_len)
+{
+       int err = -EINVAL, fmt_cnt = 0, memcpy_cnt = 0;
+       int i, buf_used, copy_size, num_args;
+       u64 params[MAX_SEQ_PRINTF_VARARGS];
+       struct bpf_seq_printf_buf *bufs;
+       const u64 *args = data;
+
+       buf_used = this_cpu_inc_return(bpf_seq_printf_buf_used);
+       if (WARN_ON_ONCE(buf_used > 1)) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       bufs = this_cpu_ptr(&bpf_seq_printf_buf);
+
+       /*
+        * bpf_check()->check_func_arg()->check_stack_boundary()
+        * guarantees that fmt points to bpf program stack,
+        * fmt_size bytes of it were initialized and fmt_size > 0
+        */
+       if (fmt[--fmt_size] != 0)
+               goto out;
+
+       if (data_len & 7)
+               goto out;
+
+       for (i = 0; i < fmt_size; i++) {
+               if (fmt[i] == '%') {
+                       if (fmt[i + 1] == '%')
+                               i++;
+                       else if (!data || !data_len)
+                               goto out;
+               }
+       }
+
+       num_args = data_len / 8;
+
+       /* check format string for allowed specifiers */
+       for (i = 0; i < fmt_size; i++) {
+               /* only printable ascii for now. */
+               if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               if (fmt[i] != '%')
+                       continue;
+
+               if (fmt[i + 1] == '%') {
+                       i++;
+                       continue;
+               }
+
+               if (fmt_cnt >= MAX_SEQ_PRINTF_VARARGS) {
+                       err = -E2BIG;
+                       goto out;
+               }
+
+               if (fmt_cnt >= num_args) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
+               i++;
+
+               /* skip optional "[0 +-][num]" width formating field */
+               while (fmt[i] == '0' || fmt[i] == '+'  || fmt[i] == '-' ||
+                      fmt[i] == ' ')
+                       i++;
+               if (fmt[i] >= '1' && fmt[i] <= '9') {
+                       i++;
+                       while (fmt[i] >= '0' && fmt[i] <= '9')
+                               i++;
+               }
+
+               if (fmt[i] == 's') {
+                       /* try our best to copy */
+                       if (memcpy_cnt >= MAX_SEQ_PRINTF_MAX_MEMCPY) {
+                               err = -E2BIG;
+                               goto out;
+                       }
+
+                       err = strncpy_from_unsafe(bufs->buf[memcpy_cnt],
+                                                 (void *) (long) args[fmt_cnt],
+                                                 MAX_SEQ_PRINTF_STR_LEN);
+                       if (err < 0)
+                               bufs->buf[memcpy_cnt][0] = '\0';
+                       params[fmt_cnt] = (u64)(long)bufs->buf[memcpy_cnt];
+
+                       fmt_cnt++;
+                       memcpy_cnt++;
+                       continue;
+               }
+
+               if (fmt[i] == 'p') {
+                       if (fmt[i + 1] == 0 ||
+                           fmt[i + 1] == 'K' ||
+                           fmt[i + 1] == 'x') {
+                               /* just kernel pointers */
+                               params[fmt_cnt] = args[fmt_cnt];
+                               fmt_cnt++;
+                               continue;
+                       }
+
+                       /* only support "%pI4", "%pi4", "%pI6" and "%pi6". */
+                       if (fmt[i + 1] != 'i' && fmt[i + 1] != 'I') {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       if (fmt[i + 2] != '4' && fmt[i + 2] != '6') {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       if (memcpy_cnt >= MAX_SEQ_PRINTF_MAX_MEMCPY) {
+                               err = -E2BIG;
+                               goto out;
+                       }
+
+
+                       copy_size = (fmt[i + 2] == '4') ? 4 : 16;
+
+                       err = probe_kernel_read(bufs->buf[memcpy_cnt],
+                                               (void *) (long) args[fmt_cnt],
+                                               copy_size);
+                       if (err < 0)
+                               memset(bufs->buf[memcpy_cnt], 0, copy_size);
+                       params[fmt_cnt] = (u64)(long)bufs->buf[memcpy_cnt];
+
+                       i += 2;
+                       fmt_cnt++;
+                       memcpy_cnt++;
+                       continue;
+               }
+
+               if (fmt[i] == 'l') {
+                       i++;
+                       if (fmt[i] == 'l')
+                               i++;
+               }
+
+               if (fmt[i] != 'i' && fmt[i] != 'd' &&
+                   fmt[i] != 'u' && fmt[i] != 'x') {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               params[fmt_cnt] = args[fmt_cnt];
+               fmt_cnt++;
+       }
+
+       /* Maximumly we can have MAX_SEQ_PRINTF_VARARGS parameter, just give
+        * all of them to seq_printf().
+        */
+       seq_printf(m, fmt, params[0], params[1], params[2], params[3],
+                  params[4], params[5], params[6], params[7], params[8],
+                  params[9], params[10], params[11]);
+
+       err = seq_has_overflowed(m) ? -EOVERFLOW : 0;
+out:
+       this_cpu_dec(bpf_seq_printf_buf_used);
+       return err;
+}
+
+static int bpf_seq_printf_btf_ids[5];
+static const struct bpf_func_proto bpf_seq_printf_proto = {
+       .func           = bpf_seq_printf,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_BTF_ID,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_PTR_TO_MEM_OR_NULL,
+       .arg5_type      = ARG_CONST_SIZE_OR_ZERO,
+       .btf_id         = bpf_seq_printf_btf_ids,
+};
+
+BPF_CALL_3(bpf_seq_write, struct seq_file *, m, const void *, data, u32, len)
+{
+       return seq_write(m, data, len) ? -EOVERFLOW : 0;
+}
+
+static int bpf_seq_write_btf_ids[5];
+static const struct bpf_func_proto bpf_seq_write_proto = {
+       .func           = bpf_seq_write,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_BTF_ID,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE_OR_ZERO,
+       .btf_id         = bpf_seq_write_btf_ids,
+};
+
 static __always_inline int
 get_map_perf_counter(struct bpf_map *map, u64 flags,
                     u64 *value, u64 *enabled, u64 *running)
        case BPF_FUNC_xdp_output:
                return &bpf_xdp_output_proto;
 #endif
+       case BPF_FUNC_seq_printf:
+               return prog->expected_attach_type == BPF_TRACE_ITER ?
+                      &bpf_seq_printf_proto :
+                      NULL;
+       case BPF_FUNC_seq_write:
+               return prog->expected_attach_type == BPF_TRACE_ITER ?
+                      &bpf_seq_write_proto :
+                      NULL;
        default:
                return raw_tp_prog_func_proto(func_id, prog);
        }
 
  *             See: clock_gettime(CLOCK_BOOTTIME)
  *     Return
  *             Current *ktime*.
+ *
+ * int bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len)
+ *     Description
+ *             seq_printf uses seq_file seq_printf() to print out the format string.
+ *             The *m* represents the seq_file. The *fmt* and *fmt_size* are for
+ *             the format string itself. The *data* and *data_len* are format string
+ *             arguments. The *data* are a u64 array and corresponding format string
+ *             values are stored in the array. For strings and pointers where pointees
+ *             are accessed, only the pointer values are stored in the *data* array.
+ *             The *data_len* is the *data* size in term of bytes.
+ *
+ *             Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory.
+ *             Reading kernel memory may fail due to either invalid address or
+ *             valid address but requiring a major memory fault. If reading kernel memory
+ *             fails, the string for **%s** will be an empty string, and the ip
+ *             address for **%p{i,I}{4,6}** will be 0. Not returning error to
+ *             bpf program is consistent with what bpf_trace_printk() does for now.
+ *     Return
+ *             0 on success, or a negative errno in case of failure.
+ *
+ *             * **-EBUSY**            Percpu memory copy buffer is busy, can try again
+ *                                     by returning 1 from bpf program.
+ *             * **-EINVAL**           Invalid arguments, or invalid/unsupported formats.
+ *             * **-E2BIG**            Too many format specifiers.
+ *             * **-EOVERFLOW**        Overflow happens, the same object will be tried again.
+ *
+ * int bpf_seq_write(struct seq_file *m, const void *data, u32 len)
+ *     Description
+ *             seq_write uses seq_file seq_write() to write the data.
+ *             The *m* represents the seq_file. The *data* and *len* represent the
+ *             data to write in bytes.
+ *     Return
+ *             0 on success, or a negative errno in case of failure.
+ *
+ *             * **-EOVERFLOW**        Overflow happens, the same object will be tried again.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(get_netns_cookie),           \
        FN(get_current_ancestor_cgroup_id),     \
        FN(sk_assign),                  \
-       FN(ktime_get_boot_ns),
+       FN(ktime_get_boot_ns),          \
+       FN(seq_printf),                 \
+       FN(seq_write),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call