162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/seq_file.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * helper functions for making synthetic files from sequences of records. 662306a36Sopenharmony_ci * initial implementation -- AV, Oct 2001. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/cache.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci#include <linux/vmalloc.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/cred.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/printk.h> 2062306a36Sopenharmony_ci#include <linux/string_helpers.h> 2162306a36Sopenharmony_ci#include <linux/uio.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <asm/page.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct kmem_cache *seq_file_cache __ro_after_init; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void seq_set_overflow(struct seq_file *m) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci m->count = m->size; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void *seq_buf_alloc(unsigned long size) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci if (unlikely(size > MAX_RW_COUNT)) 3662306a36Sopenharmony_ci return NULL; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return kvmalloc(size, GFP_KERNEL_ACCOUNT); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * seq_open - initialize sequential file 4362306a36Sopenharmony_ci * @file: file we initialize 4462306a36Sopenharmony_ci * @op: method table describing the sequence 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * seq_open() sets @file, associating it with a sequence described 4762306a36Sopenharmony_ci * by @op. @op->start() sets the iterator up and returns the first 4862306a36Sopenharmony_ci * element of sequence. @op->stop() shuts it down. @op->next() 4962306a36Sopenharmony_ci * returns the next element of sequence. @op->show() prints element 5062306a36Sopenharmony_ci * into the buffer. In case of error ->start() and ->next() return 5162306a36Sopenharmony_ci * ERR_PTR(error). In the end of sequence they return %NULL. ->show() 5262306a36Sopenharmony_ci * returns 0 in case of success and negative number in case of error. 5362306a36Sopenharmony_ci * Returning SEQ_SKIP means "discard this element and move on". 5462306a36Sopenharmony_ci * Note: seq_open() will allocate a struct seq_file and store its 5562306a36Sopenharmony_ci * pointer in @file->private_data. This pointer should not be modified. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint seq_open(struct file *file, const struct seq_operations *op) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct seq_file *p; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci WARN_ON(file->private_data); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL); 6462306a36Sopenharmony_ci if (!p) 6562306a36Sopenharmony_ci return -ENOMEM; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci file->private_data = p; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci mutex_init(&p->lock); 7062306a36Sopenharmony_ci p->op = op; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci // No refcounting: the lifetime of 'p' is constrained 7362306a36Sopenharmony_ci // to the lifetime of the file. 7462306a36Sopenharmony_ci p->file = file; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * seq_files support lseek() and pread(). They do not implement 7862306a36Sopenharmony_ci * write() at all, but we clear FMODE_PWRITE here for historical 7962306a36Sopenharmony_ci * reasons. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * If a client of seq_files a) implements file.write() and b) wishes to 8262306a36Sopenharmony_ci * support pwrite() then that client will need to implement its own 8362306a36Sopenharmony_ci * file.open() which calls seq_open() and then sets FMODE_PWRITE. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci file->f_mode &= ~FMODE_PWRITE; 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL(seq_open); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int traverse(struct seq_file *m, loff_t offset) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci loff_t pos = 0; 9362306a36Sopenharmony_ci int error = 0; 9462306a36Sopenharmony_ci void *p; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci m->index = 0; 9762306a36Sopenharmony_ci m->count = m->from = 0; 9862306a36Sopenharmony_ci if (!offset) 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!m->buf) { 10262306a36Sopenharmony_ci m->buf = seq_buf_alloc(m->size = PAGE_SIZE); 10362306a36Sopenharmony_ci if (!m->buf) 10462306a36Sopenharmony_ci return -ENOMEM; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci p = m->op->start(m, &m->index); 10762306a36Sopenharmony_ci while (p) { 10862306a36Sopenharmony_ci error = PTR_ERR(p); 10962306a36Sopenharmony_ci if (IS_ERR(p)) 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci error = m->op->show(m, p); 11262306a36Sopenharmony_ci if (error < 0) 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci if (unlikely(error)) { 11562306a36Sopenharmony_ci error = 0; 11662306a36Sopenharmony_ci m->count = 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci if (seq_has_overflowed(m)) 11962306a36Sopenharmony_ci goto Eoverflow; 12062306a36Sopenharmony_ci p = m->op->next(m, p, &m->index); 12162306a36Sopenharmony_ci if (pos + m->count > offset) { 12262306a36Sopenharmony_ci m->from = offset - pos; 12362306a36Sopenharmony_ci m->count -= m->from; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci pos += m->count; 12762306a36Sopenharmony_ci m->count = 0; 12862306a36Sopenharmony_ci if (pos == offset) 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci m->op->stop(m, p); 13262306a36Sopenharmony_ci return error; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciEoverflow: 13562306a36Sopenharmony_ci m->op->stop(m, p); 13662306a36Sopenharmony_ci kvfree(m->buf); 13762306a36Sopenharmony_ci m->count = 0; 13862306a36Sopenharmony_ci m->buf = seq_buf_alloc(m->size <<= 1); 13962306a36Sopenharmony_ci return !m->buf ? -ENOMEM : -EAGAIN; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/** 14362306a36Sopenharmony_ci * seq_read - ->read() method for sequential files. 14462306a36Sopenharmony_ci * @file: the file to read from 14562306a36Sopenharmony_ci * @buf: the buffer to read to 14662306a36Sopenharmony_ci * @size: the maximum number of bytes to read 14762306a36Sopenharmony_ci * @ppos: the current position in the file 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * Ready-made ->f_op->read() 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cissize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct iovec iov = { .iov_base = buf, .iov_len = size}; 15462306a36Sopenharmony_ci struct kiocb kiocb; 15562306a36Sopenharmony_ci struct iov_iter iter; 15662306a36Sopenharmony_ci ssize_t ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci init_sync_kiocb(&kiocb, file); 15962306a36Sopenharmony_ci iov_iter_init(&iter, ITER_DEST, &iov, 1, size); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci kiocb.ki_pos = *ppos; 16262306a36Sopenharmony_ci ret = seq_read_iter(&kiocb, &iter); 16362306a36Sopenharmony_ci *ppos = kiocb.ki_pos; 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ciEXPORT_SYMBOL(seq_read); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * Ready-made ->f_op->read_iter() 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cissize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct seq_file *m = iocb->ki_filp->private_data; 17462306a36Sopenharmony_ci size_t copied = 0; 17562306a36Sopenharmony_ci size_t n; 17662306a36Sopenharmony_ci void *p; 17762306a36Sopenharmony_ci int err = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!iov_iter_count(iter)) 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mutex_lock(&m->lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * if request is to read from zero offset, reset iterator to first 18662306a36Sopenharmony_ci * record as it might have been already advanced by previous requests 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci if (iocb->ki_pos == 0) { 18962306a36Sopenharmony_ci m->index = 0; 19062306a36Sopenharmony_ci m->count = 0; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Don't assume ki_pos is where we left it */ 19462306a36Sopenharmony_ci if (unlikely(iocb->ki_pos != m->read_pos)) { 19562306a36Sopenharmony_ci while ((err = traverse(m, iocb->ki_pos)) == -EAGAIN) 19662306a36Sopenharmony_ci ; 19762306a36Sopenharmony_ci if (err) { 19862306a36Sopenharmony_ci /* With prejudice... */ 19962306a36Sopenharmony_ci m->read_pos = 0; 20062306a36Sopenharmony_ci m->index = 0; 20162306a36Sopenharmony_ci m->count = 0; 20262306a36Sopenharmony_ci goto Done; 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci m->read_pos = iocb->ki_pos; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* grab buffer if we didn't have one */ 20962306a36Sopenharmony_ci if (!m->buf) { 21062306a36Sopenharmony_ci m->buf = seq_buf_alloc(m->size = PAGE_SIZE); 21162306a36Sopenharmony_ci if (!m->buf) 21262306a36Sopenharmony_ci goto Enomem; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci // something left in the buffer - copy it out first 21562306a36Sopenharmony_ci if (m->count) { 21662306a36Sopenharmony_ci n = copy_to_iter(m->buf + m->from, m->count, iter); 21762306a36Sopenharmony_ci m->count -= n; 21862306a36Sopenharmony_ci m->from += n; 21962306a36Sopenharmony_ci copied += n; 22062306a36Sopenharmony_ci if (m->count) // hadn't managed to copy everything 22162306a36Sopenharmony_ci goto Done; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci // get a non-empty record in the buffer 22462306a36Sopenharmony_ci m->from = 0; 22562306a36Sopenharmony_ci p = m->op->start(m, &m->index); 22662306a36Sopenharmony_ci while (1) { 22762306a36Sopenharmony_ci err = PTR_ERR(p); 22862306a36Sopenharmony_ci if (!p || IS_ERR(p)) // EOF or an error 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci err = m->op->show(m, p); 23162306a36Sopenharmony_ci if (err < 0) // hard error 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci if (unlikely(err)) // ->show() says "skip it" 23462306a36Sopenharmony_ci m->count = 0; 23562306a36Sopenharmony_ci if (unlikely(!m->count)) { // empty record 23662306a36Sopenharmony_ci p = m->op->next(m, p, &m->index); 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci if (!seq_has_overflowed(m)) // got it 24062306a36Sopenharmony_ci goto Fill; 24162306a36Sopenharmony_ci // need a bigger buffer 24262306a36Sopenharmony_ci m->op->stop(m, p); 24362306a36Sopenharmony_ci kvfree(m->buf); 24462306a36Sopenharmony_ci m->count = 0; 24562306a36Sopenharmony_ci m->buf = seq_buf_alloc(m->size <<= 1); 24662306a36Sopenharmony_ci if (!m->buf) 24762306a36Sopenharmony_ci goto Enomem; 24862306a36Sopenharmony_ci p = m->op->start(m, &m->index); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci // EOF or an error 25162306a36Sopenharmony_ci m->op->stop(m, p); 25262306a36Sopenharmony_ci m->count = 0; 25362306a36Sopenharmony_ci goto Done; 25462306a36Sopenharmony_ciFill: 25562306a36Sopenharmony_ci // one non-empty record is in the buffer; if they want more, 25662306a36Sopenharmony_ci // try to fit more in, but in any case we need to advance 25762306a36Sopenharmony_ci // the iterator once for every record shown. 25862306a36Sopenharmony_ci while (1) { 25962306a36Sopenharmony_ci size_t offs = m->count; 26062306a36Sopenharmony_ci loff_t pos = m->index; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci p = m->op->next(m, p, &m->index); 26362306a36Sopenharmony_ci if (pos == m->index) { 26462306a36Sopenharmony_ci pr_info_ratelimited("buggy .next function %ps did not update position index\n", 26562306a36Sopenharmony_ci m->op->next); 26662306a36Sopenharmony_ci m->index++; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci if (!p || IS_ERR(p)) // no next record for us 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci if (m->count >= iov_iter_count(iter)) 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci err = m->op->show(m, p); 27362306a36Sopenharmony_ci if (err > 0) { // ->show() says "skip it" 27462306a36Sopenharmony_ci m->count = offs; 27562306a36Sopenharmony_ci } else if (err || seq_has_overflowed(m)) { 27662306a36Sopenharmony_ci m->count = offs; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci m->op->stop(m, p); 28162306a36Sopenharmony_ci n = copy_to_iter(m->buf, m->count, iter); 28262306a36Sopenharmony_ci copied += n; 28362306a36Sopenharmony_ci m->count -= n; 28462306a36Sopenharmony_ci m->from = n; 28562306a36Sopenharmony_ciDone: 28662306a36Sopenharmony_ci if (unlikely(!copied)) { 28762306a36Sopenharmony_ci copied = m->count ? -EFAULT : err; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci iocb->ki_pos += copied; 29062306a36Sopenharmony_ci m->read_pos += copied; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci mutex_unlock(&m->lock); 29362306a36Sopenharmony_ci return copied; 29462306a36Sopenharmony_ciEnomem: 29562306a36Sopenharmony_ci err = -ENOMEM; 29662306a36Sopenharmony_ci goto Done; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL(seq_read_iter); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/** 30162306a36Sopenharmony_ci * seq_lseek - ->llseek() method for sequential files. 30262306a36Sopenharmony_ci * @file: the file in question 30362306a36Sopenharmony_ci * @offset: new position 30462306a36Sopenharmony_ci * @whence: 0 for absolute, 1 for relative position 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Ready-made ->f_op->llseek() 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ciloff_t seq_lseek(struct file *file, loff_t offset, int whence) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct seq_file *m = file->private_data; 31162306a36Sopenharmony_ci loff_t retval = -EINVAL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci mutex_lock(&m->lock); 31462306a36Sopenharmony_ci switch (whence) { 31562306a36Sopenharmony_ci case SEEK_CUR: 31662306a36Sopenharmony_ci offset += file->f_pos; 31762306a36Sopenharmony_ci fallthrough; 31862306a36Sopenharmony_ci case SEEK_SET: 31962306a36Sopenharmony_ci if (offset < 0) 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci retval = offset; 32262306a36Sopenharmony_ci if (offset != m->read_pos) { 32362306a36Sopenharmony_ci while ((retval = traverse(m, offset)) == -EAGAIN) 32462306a36Sopenharmony_ci ; 32562306a36Sopenharmony_ci if (retval) { 32662306a36Sopenharmony_ci /* with extreme prejudice... */ 32762306a36Sopenharmony_ci file->f_pos = 0; 32862306a36Sopenharmony_ci m->read_pos = 0; 32962306a36Sopenharmony_ci m->index = 0; 33062306a36Sopenharmony_ci m->count = 0; 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci m->read_pos = offset; 33362306a36Sopenharmony_ci retval = file->f_pos = offset; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci file->f_pos = offset; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci mutex_unlock(&m->lock); 34062306a36Sopenharmony_ci return retval; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ciEXPORT_SYMBOL(seq_lseek); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/** 34562306a36Sopenharmony_ci * seq_release - free the structures associated with sequential file. 34662306a36Sopenharmony_ci * @file: file in question 34762306a36Sopenharmony_ci * @inode: its inode 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * Frees the structures associated with sequential file; can be used 35062306a36Sopenharmony_ci * as ->f_op->release() if you don't have private data to destroy. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciint seq_release(struct inode *inode, struct file *file) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct seq_file *m = file->private_data; 35562306a36Sopenharmony_ci kvfree(m->buf); 35662306a36Sopenharmony_ci kmem_cache_free(seq_file_cache, m); 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ciEXPORT_SYMBOL(seq_release); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/** 36262306a36Sopenharmony_ci * seq_escape_mem - print data into buffer, escaping some characters 36362306a36Sopenharmony_ci * @m: target buffer 36462306a36Sopenharmony_ci * @src: source buffer 36562306a36Sopenharmony_ci * @len: size of source buffer 36662306a36Sopenharmony_ci * @flags: flags to pass to string_escape_mem() 36762306a36Sopenharmony_ci * @esc: set of characters that need escaping 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Puts data into buffer, replacing each occurrence of character from 37062306a36Sopenharmony_ci * given class (defined by @flags and @esc) with printable escaped sequence. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * Use seq_has_overflowed() to check for errors. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_civoid seq_escape_mem(struct seq_file *m, const char *src, size_t len, 37562306a36Sopenharmony_ci unsigned int flags, const char *esc) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci char *buf; 37862306a36Sopenharmony_ci size_t size = seq_get_buf(m, &buf); 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ret = string_escape_mem(src, len, buf, size, flags, esc); 38262306a36Sopenharmony_ci seq_commit(m, ret < size ? ret : -1); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ciEXPORT_SYMBOL(seq_escape_mem); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_civoid seq_vprintf(struct seq_file *m, const char *f, va_list args) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int len; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (m->count < m->size) { 39162306a36Sopenharmony_ci len = vsnprintf(m->buf + m->count, m->size - m->count, f, args); 39262306a36Sopenharmony_ci if (m->count + len < m->size) { 39362306a36Sopenharmony_ci m->count += len; 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci seq_set_overflow(m); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ciEXPORT_SYMBOL(seq_vprintf); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_civoid seq_printf(struct seq_file *m, const char *f, ...) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci va_list args; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci va_start(args, f); 40662306a36Sopenharmony_ci seq_vprintf(m, f, args); 40762306a36Sopenharmony_ci va_end(args); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ciEXPORT_SYMBOL(seq_printf); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci#ifdef CONFIG_BINARY_PRINTF 41262306a36Sopenharmony_civoid seq_bprintf(struct seq_file *m, const char *f, const u32 *binary) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int len; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (m->count < m->size) { 41762306a36Sopenharmony_ci len = bstr_printf(m->buf + m->count, m->size - m->count, f, 41862306a36Sopenharmony_ci binary); 41962306a36Sopenharmony_ci if (m->count + len < m->size) { 42062306a36Sopenharmony_ci m->count += len; 42162306a36Sopenharmony_ci return; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci seq_set_overflow(m); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ciEXPORT_SYMBOL(seq_bprintf); 42762306a36Sopenharmony_ci#endif /* CONFIG_BINARY_PRINTF */ 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * mangle_path - mangle and copy path to buffer beginning 43162306a36Sopenharmony_ci * @s: buffer start 43262306a36Sopenharmony_ci * @p: beginning of path in above buffer 43362306a36Sopenharmony_ci * @esc: set of characters that need escaping 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * Copy the path from @p to @s, replacing each occurrence of character from 43662306a36Sopenharmony_ci * @esc with usual octal escape. 43762306a36Sopenharmony_ci * Returns pointer past last written character in @s, or NULL in case of 43862306a36Sopenharmony_ci * failure. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cichar *mangle_path(char *s, const char *p, const char *esc) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci while (s <= p) { 44362306a36Sopenharmony_ci char c = *p++; 44462306a36Sopenharmony_ci if (!c) { 44562306a36Sopenharmony_ci return s; 44662306a36Sopenharmony_ci } else if (!strchr(esc, c)) { 44762306a36Sopenharmony_ci *s++ = c; 44862306a36Sopenharmony_ci } else if (s + 4 > p) { 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } else { 45162306a36Sopenharmony_ci *s++ = '\\'; 45262306a36Sopenharmony_ci *s++ = '0' + ((c & 0300) >> 6); 45362306a36Sopenharmony_ci *s++ = '0' + ((c & 070) >> 3); 45462306a36Sopenharmony_ci *s++ = '0' + (c & 07); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci return NULL; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ciEXPORT_SYMBOL(mangle_path); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/** 46262306a36Sopenharmony_ci * seq_path - seq_file interface to print a pathname 46362306a36Sopenharmony_ci * @m: the seq_file handle 46462306a36Sopenharmony_ci * @path: the struct path to print 46562306a36Sopenharmony_ci * @esc: set of characters to escape in the output 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * return the absolute path of 'path', as represented by the 46862306a36Sopenharmony_ci * dentry / mnt pair in the path parameter. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ciint seq_path(struct seq_file *m, const struct path *path, const char *esc) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci char *buf; 47362306a36Sopenharmony_ci size_t size = seq_get_buf(m, &buf); 47462306a36Sopenharmony_ci int res = -1; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (size) { 47762306a36Sopenharmony_ci char *p = d_path(path, buf, size); 47862306a36Sopenharmony_ci if (!IS_ERR(p)) { 47962306a36Sopenharmony_ci char *end = mangle_path(buf, p, esc); 48062306a36Sopenharmony_ci if (end) 48162306a36Sopenharmony_ci res = end - buf; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci seq_commit(m, res); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return res; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ciEXPORT_SYMBOL(seq_path); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/** 49162306a36Sopenharmony_ci * seq_file_path - seq_file interface to print a pathname of a file 49262306a36Sopenharmony_ci * @m: the seq_file handle 49362306a36Sopenharmony_ci * @file: the struct file to print 49462306a36Sopenharmony_ci * @esc: set of characters to escape in the output 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * return the absolute path to the file. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ciint seq_file_path(struct seq_file *m, struct file *file, const char *esc) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci return seq_path(m, &file->f_path, esc); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciEXPORT_SYMBOL(seq_file_path); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * Same as seq_path, but relative to supplied root. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ciint seq_path_root(struct seq_file *m, const struct path *path, 50862306a36Sopenharmony_ci const struct path *root, const char *esc) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci char *buf; 51162306a36Sopenharmony_ci size_t size = seq_get_buf(m, &buf); 51262306a36Sopenharmony_ci int res = -ENAMETOOLONG; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (size) { 51562306a36Sopenharmony_ci char *p; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci p = __d_path(path, root, buf, size); 51862306a36Sopenharmony_ci if (!p) 51962306a36Sopenharmony_ci return SEQ_SKIP; 52062306a36Sopenharmony_ci res = PTR_ERR(p); 52162306a36Sopenharmony_ci if (!IS_ERR(p)) { 52262306a36Sopenharmony_ci char *end = mangle_path(buf, p, esc); 52362306a36Sopenharmony_ci if (end) 52462306a36Sopenharmony_ci res = end - buf; 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci res = -ENAMETOOLONG; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci seq_commit(m, res); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return res < 0 && res != -ENAMETOOLONG ? res : 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* 53562306a36Sopenharmony_ci * returns the path of the 'dentry' from the root of its filesystem. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ciint seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci char *buf; 54062306a36Sopenharmony_ci size_t size = seq_get_buf(m, &buf); 54162306a36Sopenharmony_ci int res = -1; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (size) { 54462306a36Sopenharmony_ci char *p = dentry_path(dentry, buf, size); 54562306a36Sopenharmony_ci if (!IS_ERR(p)) { 54662306a36Sopenharmony_ci char *end = mangle_path(buf, p, esc); 54762306a36Sopenharmony_ci if (end) 54862306a36Sopenharmony_ci res = end - buf; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci seq_commit(m, res); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return res; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ciEXPORT_SYMBOL(seq_dentry); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_civoid *single_start(struct seq_file *p, loff_t *pos) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci return *pos ? NULL : SEQ_START_TOKEN; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void *single_next(struct seq_file *p, void *v, loff_t *pos) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci ++*pos; 56562306a36Sopenharmony_ci return NULL; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void single_stop(struct seq_file *p, void *v) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciint single_open(struct file *file, int (*show)(struct seq_file *, void *), 57362306a36Sopenharmony_ci void *data) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_ACCOUNT); 57662306a36Sopenharmony_ci int res = -ENOMEM; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (op) { 57962306a36Sopenharmony_ci op->start = single_start; 58062306a36Sopenharmony_ci op->next = single_next; 58162306a36Sopenharmony_ci op->stop = single_stop; 58262306a36Sopenharmony_ci op->show = show; 58362306a36Sopenharmony_ci res = seq_open(file, op); 58462306a36Sopenharmony_ci if (!res) 58562306a36Sopenharmony_ci ((struct seq_file *)file->private_data)->private = data; 58662306a36Sopenharmony_ci else 58762306a36Sopenharmony_ci kfree(op); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci return res; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ciEXPORT_SYMBOL(single_open); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciint single_open_size(struct file *file, int (*show)(struct seq_file *, void *), 59462306a36Sopenharmony_ci void *data, size_t size) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci char *buf = seq_buf_alloc(size); 59762306a36Sopenharmony_ci int ret; 59862306a36Sopenharmony_ci if (!buf) 59962306a36Sopenharmony_ci return -ENOMEM; 60062306a36Sopenharmony_ci ret = single_open(file, show, data); 60162306a36Sopenharmony_ci if (ret) { 60262306a36Sopenharmony_ci kvfree(buf); 60362306a36Sopenharmony_ci return ret; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci ((struct seq_file *)file->private_data)->buf = buf; 60662306a36Sopenharmony_ci ((struct seq_file *)file->private_data)->size = size; 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ciEXPORT_SYMBOL(single_open_size); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ciint single_release(struct inode *inode, struct file *file) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci const struct seq_operations *op = ((struct seq_file *)file->private_data)->op; 61462306a36Sopenharmony_ci int res = seq_release(inode, file); 61562306a36Sopenharmony_ci kfree(op); 61662306a36Sopenharmony_ci return res; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ciEXPORT_SYMBOL(single_release); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ciint seq_release_private(struct inode *inode, struct file *file) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct seq_file *seq = file->private_data; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci kfree(seq->private); 62562306a36Sopenharmony_ci seq->private = NULL; 62662306a36Sopenharmony_ci return seq_release(inode, file); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ciEXPORT_SYMBOL(seq_release_private); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_civoid *__seq_open_private(struct file *f, const struct seq_operations *ops, 63162306a36Sopenharmony_ci int psize) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci int rc; 63462306a36Sopenharmony_ci void *private; 63562306a36Sopenharmony_ci struct seq_file *seq; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci private = kzalloc(psize, GFP_KERNEL_ACCOUNT); 63862306a36Sopenharmony_ci if (private == NULL) 63962306a36Sopenharmony_ci goto out; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci rc = seq_open(f, ops); 64262306a36Sopenharmony_ci if (rc < 0) 64362306a36Sopenharmony_ci goto out_free; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci seq = f->private_data; 64662306a36Sopenharmony_ci seq->private = private; 64762306a36Sopenharmony_ci return private; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ciout_free: 65062306a36Sopenharmony_ci kfree(private); 65162306a36Sopenharmony_ciout: 65262306a36Sopenharmony_ci return NULL; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ciEXPORT_SYMBOL(__seq_open_private); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ciint seq_open_private(struct file *filp, const struct seq_operations *ops, 65762306a36Sopenharmony_ci int psize) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ciEXPORT_SYMBOL(seq_open_private); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_civoid seq_putc(struct seq_file *m, char c) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci if (m->count >= m->size) 66662306a36Sopenharmony_ci return; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci m->buf[m->count++] = c; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ciEXPORT_SYMBOL(seq_putc); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_civoid seq_puts(struct seq_file *m, const char *s) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci int len = strlen(s); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (m->count + len >= m->size) { 67762306a36Sopenharmony_ci seq_set_overflow(m); 67862306a36Sopenharmony_ci return; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci memcpy(m->buf + m->count, s, len); 68162306a36Sopenharmony_ci m->count += len; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ciEXPORT_SYMBOL(seq_puts); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/** 68662306a36Sopenharmony_ci * seq_put_decimal_ull_width - A helper routine for putting decimal numbers 68762306a36Sopenharmony_ci * without rich format of printf(). 68862306a36Sopenharmony_ci * only 'unsigned long long' is supported. 68962306a36Sopenharmony_ci * @m: seq_file identifying the buffer to which data should be written 69062306a36Sopenharmony_ci * @delimiter: a string which is printed before the number 69162306a36Sopenharmony_ci * @num: the number 69262306a36Sopenharmony_ci * @width: a minimum field width 69362306a36Sopenharmony_ci * 69462306a36Sopenharmony_ci * This routine will put strlen(delimiter) + number into seq_filed. 69562306a36Sopenharmony_ci * This routine is very quick when you show lots of numbers. 69662306a36Sopenharmony_ci * In usual cases, it will be better to use seq_printf(). It's easier to read. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_civoid seq_put_decimal_ull_width(struct seq_file *m, const char *delimiter, 69962306a36Sopenharmony_ci unsigned long long num, unsigned int width) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci int len; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */ 70462306a36Sopenharmony_ci goto overflow; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (delimiter && delimiter[0]) { 70762306a36Sopenharmony_ci if (delimiter[1] == 0) 70862306a36Sopenharmony_ci seq_putc(m, delimiter[0]); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci seq_puts(m, delimiter); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (!width) 71462306a36Sopenharmony_ci width = 1; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (m->count + width >= m->size) 71762306a36Sopenharmony_ci goto overflow; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci len = num_to_str(m->buf + m->count, m->size - m->count, num, width); 72062306a36Sopenharmony_ci if (!len) 72162306a36Sopenharmony_ci goto overflow; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci m->count += len; 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cioverflow: 72762306a36Sopenharmony_ci seq_set_overflow(m); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_civoid seq_put_decimal_ull(struct seq_file *m, const char *delimiter, 73162306a36Sopenharmony_ci unsigned long long num) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci return seq_put_decimal_ull_width(m, delimiter, num, 0); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ciEXPORT_SYMBOL(seq_put_decimal_ull); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/** 73862306a36Sopenharmony_ci * seq_put_hex_ll - put a number in hexadecimal notation 73962306a36Sopenharmony_ci * @m: seq_file identifying the buffer to which data should be written 74062306a36Sopenharmony_ci * @delimiter: a string which is printed before the number 74162306a36Sopenharmony_ci * @v: the number 74262306a36Sopenharmony_ci * @width: a minimum field width 74362306a36Sopenharmony_ci * 74462306a36Sopenharmony_ci * seq_put_hex_ll(m, "", v, 8) is equal to seq_printf(m, "%08llx", v) 74562306a36Sopenharmony_ci * 74662306a36Sopenharmony_ci * This routine is very quick when you show lots of numbers. 74762306a36Sopenharmony_ci * In usual cases, it will be better to use seq_printf(). It's easier to read. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_civoid seq_put_hex_ll(struct seq_file *m, const char *delimiter, 75062306a36Sopenharmony_ci unsigned long long v, unsigned int width) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci unsigned int len; 75362306a36Sopenharmony_ci int i; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (delimiter && delimiter[0]) { 75662306a36Sopenharmony_ci if (delimiter[1] == 0) 75762306a36Sopenharmony_ci seq_putc(m, delimiter[0]); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci seq_puts(m, delimiter); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* If x is 0, the result of __builtin_clzll is undefined */ 76362306a36Sopenharmony_ci if (v == 0) 76462306a36Sopenharmony_ci len = 1; 76562306a36Sopenharmony_ci else 76662306a36Sopenharmony_ci len = (sizeof(v) * 8 - __builtin_clzll(v) + 3) / 4; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (len < width) 76962306a36Sopenharmony_ci len = width; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (m->count + len > m->size) { 77262306a36Sopenharmony_ci seq_set_overflow(m); 77362306a36Sopenharmony_ci return; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci for (i = len - 1; i >= 0; i--) { 77762306a36Sopenharmony_ci m->buf[m->count + i] = hex_asc[0xf & v]; 77862306a36Sopenharmony_ci v = v >> 4; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci m->count += len; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci int len; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (m->count + 3 >= m->size) /* we'll write 2 bytes at least */ 78862306a36Sopenharmony_ci goto overflow; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (delimiter && delimiter[0]) { 79162306a36Sopenharmony_ci if (delimiter[1] == 0) 79262306a36Sopenharmony_ci seq_putc(m, delimiter[0]); 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci seq_puts(m, delimiter); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (m->count + 2 >= m->size) 79862306a36Sopenharmony_ci goto overflow; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (num < 0) { 80162306a36Sopenharmony_ci m->buf[m->count++] = '-'; 80262306a36Sopenharmony_ci num = -num; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (num < 10) { 80662306a36Sopenharmony_ci m->buf[m->count++] = num + '0'; 80762306a36Sopenharmony_ci return; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci len = num_to_str(m->buf + m->count, m->size - m->count, num, 0); 81162306a36Sopenharmony_ci if (!len) 81262306a36Sopenharmony_ci goto overflow; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci m->count += len; 81562306a36Sopenharmony_ci return; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cioverflow: 81862306a36Sopenharmony_ci seq_set_overflow(m); 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ciEXPORT_SYMBOL(seq_put_decimal_ll); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/** 82362306a36Sopenharmony_ci * seq_write - write arbitrary data to buffer 82462306a36Sopenharmony_ci * @seq: seq_file identifying the buffer to which data should be written 82562306a36Sopenharmony_ci * @data: data address 82662306a36Sopenharmony_ci * @len: number of bytes 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * Return 0 on success, non-zero otherwise. 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ciint seq_write(struct seq_file *seq, const void *data, size_t len) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci if (seq->count + len < seq->size) { 83362306a36Sopenharmony_ci memcpy(seq->buf + seq->count, data, len); 83462306a36Sopenharmony_ci seq->count += len; 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci seq_set_overflow(seq); 83862306a36Sopenharmony_ci return -1; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ciEXPORT_SYMBOL(seq_write); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/** 84362306a36Sopenharmony_ci * seq_pad - write padding spaces to buffer 84462306a36Sopenharmony_ci * @m: seq_file identifying the buffer to which data should be written 84562306a36Sopenharmony_ci * @c: the byte to append after padding if non-zero 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_civoid seq_pad(struct seq_file *m, char c) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci int size = m->pad_until - m->count; 85062306a36Sopenharmony_ci if (size > 0) { 85162306a36Sopenharmony_ci if (size + m->count > m->size) { 85262306a36Sopenharmony_ci seq_set_overflow(m); 85362306a36Sopenharmony_ci return; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci memset(m->buf + m->count, ' ', size); 85662306a36Sopenharmony_ci m->count += size; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci if (c) 85962306a36Sopenharmony_ci seq_putc(m, c); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ciEXPORT_SYMBOL(seq_pad); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci/* A complete analogue of print_hex_dump() */ 86462306a36Sopenharmony_civoid seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type, 86562306a36Sopenharmony_ci int rowsize, int groupsize, const void *buf, size_t len, 86662306a36Sopenharmony_ci bool ascii) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci const u8 *ptr = buf; 86962306a36Sopenharmony_ci int i, linelen, remaining = len; 87062306a36Sopenharmony_ci char *buffer; 87162306a36Sopenharmony_ci size_t size; 87262306a36Sopenharmony_ci int ret; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (rowsize != 16 && rowsize != 32) 87562306a36Sopenharmony_ci rowsize = 16; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci for (i = 0; i < len && !seq_has_overflowed(m); i += rowsize) { 87862306a36Sopenharmony_ci linelen = min(remaining, rowsize); 87962306a36Sopenharmony_ci remaining -= rowsize; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci switch (prefix_type) { 88262306a36Sopenharmony_ci case DUMP_PREFIX_ADDRESS: 88362306a36Sopenharmony_ci seq_printf(m, "%s%p: ", prefix_str, ptr + i); 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci case DUMP_PREFIX_OFFSET: 88662306a36Sopenharmony_ci seq_printf(m, "%s%.8x: ", prefix_str, i); 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci default: 88962306a36Sopenharmony_ci seq_printf(m, "%s", prefix_str); 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci size = seq_get_buf(m, &buffer); 89462306a36Sopenharmony_ci ret = hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, 89562306a36Sopenharmony_ci buffer, size, ascii); 89662306a36Sopenharmony_ci seq_commit(m, ret < size ? ret : -1); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci seq_putc(m, '\n'); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hex_dump); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistruct list_head *seq_list_start(struct list_head *head, loff_t pos) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct list_head *lh; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci list_for_each(lh, head) 90862306a36Sopenharmony_ci if (pos-- == 0) 90962306a36Sopenharmony_ci return lh; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return NULL; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_start); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistruct list_head *seq_list_start_head(struct list_head *head, loff_t pos) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci if (!pos) 91862306a36Sopenharmony_ci return head; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci return seq_list_start(head, pos - 1); 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_start_head); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistruct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct list_head *lh; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci lh = ((struct list_head *)v)->next; 92962306a36Sopenharmony_ci ++*ppos; 93062306a36Sopenharmony_ci return lh == head ? NULL : lh; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_next); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistruct list_head *seq_list_start_rcu(struct list_head *head, loff_t pos) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct list_head *lh; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci list_for_each_rcu(lh, head) 93962306a36Sopenharmony_ci if (pos-- == 0) 94062306a36Sopenharmony_ci return lh; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci return NULL; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_start_rcu); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistruct list_head *seq_list_start_head_rcu(struct list_head *head, loff_t pos) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci if (!pos) 94962306a36Sopenharmony_ci return head; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return seq_list_start_rcu(head, pos - 1); 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_start_head_rcu); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistruct list_head *seq_list_next_rcu(void *v, struct list_head *head, 95662306a36Sopenharmony_ci loff_t *ppos) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct list_head *lh; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci lh = list_next_rcu((struct list_head *)v); 96162306a36Sopenharmony_ci ++*ppos; 96262306a36Sopenharmony_ci return lh == head ? NULL : lh; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ciEXPORT_SYMBOL(seq_list_next_rcu); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/** 96762306a36Sopenharmony_ci * seq_hlist_start - start an iteration of a hlist 96862306a36Sopenharmony_ci * @head: the head of the hlist 96962306a36Sopenharmony_ci * @pos: the start position of the sequence 97062306a36Sopenharmony_ci * 97162306a36Sopenharmony_ci * Called at seq_file->op->start(). 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_cistruct hlist_node *seq_hlist_start(struct hlist_head *head, loff_t pos) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct hlist_node *node; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci hlist_for_each(node, head) 97862306a36Sopenharmony_ci if (pos-- == 0) 97962306a36Sopenharmony_ci return node; 98062306a36Sopenharmony_ci return NULL; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_start); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci/** 98562306a36Sopenharmony_ci * seq_hlist_start_head - start an iteration of a hlist 98662306a36Sopenharmony_ci * @head: the head of the hlist 98762306a36Sopenharmony_ci * @pos: the start position of the sequence 98862306a36Sopenharmony_ci * 98962306a36Sopenharmony_ci * Called at seq_file->op->start(). Call this function if you want to 99062306a36Sopenharmony_ci * print a header at the top of the output. 99162306a36Sopenharmony_ci */ 99262306a36Sopenharmony_cistruct hlist_node *seq_hlist_start_head(struct hlist_head *head, loff_t pos) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci if (!pos) 99562306a36Sopenharmony_ci return SEQ_START_TOKEN; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return seq_hlist_start(head, pos - 1); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_start_head); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/** 100262306a36Sopenharmony_ci * seq_hlist_next - move to the next position of the hlist 100362306a36Sopenharmony_ci * @v: the current iterator 100462306a36Sopenharmony_ci * @head: the head of the hlist 100562306a36Sopenharmony_ci * @ppos: the current position 100662306a36Sopenharmony_ci * 100762306a36Sopenharmony_ci * Called at seq_file->op->next(). 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_cistruct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, 101062306a36Sopenharmony_ci loff_t *ppos) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct hlist_node *node = v; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci ++*ppos; 101562306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 101662306a36Sopenharmony_ci return head->first; 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci return node->next; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_next); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/** 102362306a36Sopenharmony_ci * seq_hlist_start_rcu - start an iteration of a hlist protected by RCU 102462306a36Sopenharmony_ci * @head: the head of the hlist 102562306a36Sopenharmony_ci * @pos: the start position of the sequence 102662306a36Sopenharmony_ci * 102762306a36Sopenharmony_ci * Called at seq_file->op->start(). 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * This list-traversal primitive may safely run concurrently with 103062306a36Sopenharmony_ci * the _rcu list-mutation primitives such as hlist_add_head_rcu() 103162306a36Sopenharmony_ci * as long as the traversal is guarded by rcu_read_lock(). 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_cistruct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, 103462306a36Sopenharmony_ci loff_t pos) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct hlist_node *node; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci __hlist_for_each_rcu(node, head) 103962306a36Sopenharmony_ci if (pos-- == 0) 104062306a36Sopenharmony_ci return node; 104162306a36Sopenharmony_ci return NULL; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_start_rcu); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci/** 104662306a36Sopenharmony_ci * seq_hlist_start_head_rcu - start an iteration of a hlist protected by RCU 104762306a36Sopenharmony_ci * @head: the head of the hlist 104862306a36Sopenharmony_ci * @pos: the start position of the sequence 104962306a36Sopenharmony_ci * 105062306a36Sopenharmony_ci * Called at seq_file->op->start(). Call this function if you want to 105162306a36Sopenharmony_ci * print a header at the top of the output. 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * This list-traversal primitive may safely run concurrently with 105462306a36Sopenharmony_ci * the _rcu list-mutation primitives such as hlist_add_head_rcu() 105562306a36Sopenharmony_ci * as long as the traversal is guarded by rcu_read_lock(). 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_cistruct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, 105862306a36Sopenharmony_ci loff_t pos) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci if (!pos) 106162306a36Sopenharmony_ci return SEQ_START_TOKEN; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return seq_hlist_start_rcu(head, pos - 1); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_start_head_rcu); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/** 106862306a36Sopenharmony_ci * seq_hlist_next_rcu - move to the next position of the hlist protected by RCU 106962306a36Sopenharmony_ci * @v: the current iterator 107062306a36Sopenharmony_ci * @head: the head of the hlist 107162306a36Sopenharmony_ci * @ppos: the current position 107262306a36Sopenharmony_ci * 107362306a36Sopenharmony_ci * Called at seq_file->op->next(). 107462306a36Sopenharmony_ci * 107562306a36Sopenharmony_ci * This list-traversal primitive may safely run concurrently with 107662306a36Sopenharmony_ci * the _rcu list-mutation primitives such as hlist_add_head_rcu() 107762306a36Sopenharmony_ci * as long as the traversal is guarded by rcu_read_lock(). 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_cistruct hlist_node *seq_hlist_next_rcu(void *v, 108062306a36Sopenharmony_ci struct hlist_head *head, 108162306a36Sopenharmony_ci loff_t *ppos) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct hlist_node *node = v; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ++*ppos; 108662306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 108762306a36Sopenharmony_ci return rcu_dereference(head->first); 108862306a36Sopenharmony_ci else 108962306a36Sopenharmony_ci return rcu_dereference(node->next); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_next_rcu); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci/** 109462306a36Sopenharmony_ci * seq_hlist_start_percpu - start an iteration of a percpu hlist array 109562306a36Sopenharmony_ci * @head: pointer to percpu array of struct hlist_heads 109662306a36Sopenharmony_ci * @cpu: pointer to cpu "cursor" 109762306a36Sopenharmony_ci * @pos: start position of sequence 109862306a36Sopenharmony_ci * 109962306a36Sopenharmony_ci * Called at seq_file->op->start(). 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_cistruct hlist_node * 110262306a36Sopenharmony_ciseq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct hlist_node *node; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci for_each_possible_cpu(*cpu) { 110762306a36Sopenharmony_ci hlist_for_each(node, per_cpu_ptr(head, *cpu)) { 110862306a36Sopenharmony_ci if (pos-- == 0) 110962306a36Sopenharmony_ci return node; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci return NULL; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_start_percpu); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci/** 111762306a36Sopenharmony_ci * seq_hlist_next_percpu - move to the next position of the percpu hlist array 111862306a36Sopenharmony_ci * @v: pointer to current hlist_node 111962306a36Sopenharmony_ci * @head: pointer to percpu array of struct hlist_heads 112062306a36Sopenharmony_ci * @cpu: pointer to cpu "cursor" 112162306a36Sopenharmony_ci * @pos: start position of sequence 112262306a36Sopenharmony_ci * 112362306a36Sopenharmony_ci * Called at seq_file->op->next(). 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_cistruct hlist_node * 112662306a36Sopenharmony_ciseq_hlist_next_percpu(void *v, struct hlist_head __percpu *head, 112762306a36Sopenharmony_ci int *cpu, loff_t *pos) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct hlist_node *node = v; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci ++*pos; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (node->next) 113462306a36Sopenharmony_ci return node->next; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci for (*cpu = cpumask_next(*cpu, cpu_possible_mask); *cpu < nr_cpu_ids; 113762306a36Sopenharmony_ci *cpu = cpumask_next(*cpu, cpu_possible_mask)) { 113862306a36Sopenharmony_ci struct hlist_head *bucket = per_cpu_ptr(head, *cpu); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (!hlist_empty(bucket)) 114162306a36Sopenharmony_ci return bucket->first; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci return NULL; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ciEXPORT_SYMBOL(seq_hlist_next_percpu); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_civoid __init seq_file_init(void) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci seq_file_cache = KMEM_CACHE(seq_file, SLAB_ACCOUNT|SLAB_PANIC); 115062306a36Sopenharmony_ci} 1151