Merge branch 'akpm' (patches from Andrew)
Merge updates from Andrew Morton: - various misc bits - most of MM (quite a lot of MM material is awaiting the merge of linux-next dependencies) - kasan - printk updates - procfs updates - MAINTAINERS - /lib updates - checkpatch updates * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (123 commits) init: reduce rootwait polling interval time to 5ms binfmt_elf: use vmalloc() for allocation of vma_filesz checkpatch: don't emit unified-diff error for rename-only patches checkpatch: don't check c99 types like uint8_t under tools checkpatch: avoid multiple line dereferences checkpatch: don't check .pl files, improve absolute path commit log test scripts/checkpatch.pl: fix spelling checkpatch: don't try to get maintained status when --no-tree is given lib/ida: document locking requirements a bit better lib/rbtree.c: fix typo in comment of ____rb_erase_color lib/Kconfig.debug: make CONFIG_STRICT_DEVMEM depend on CONFIG_DEVMEM MAINTAINERS: add drm and drm/i915 irc channels MAINTAINERS: add "C:" for URI for chat where developers hang out MAINTAINERS: add drm and drm/i915 bug filing info MAINTAINERS: add "B:" for URI where to file bugs get_maintainer: look for arbitrary letter prefixes in sections printk: add Kconfig option to set default console loglevel printk/sound: handle more message headers printk/btrfs: handle more message headers printk/kdb: handle more message headers ...
This commit is contained in:
@@ -2204,7 +2204,9 @@ static int elf_core_dump(struct coredump_params *cprm)
|
||||
|
||||
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
|
||||
|
||||
vma_filesz = kmalloc_array(segs - 1, sizeof(*vma_filesz), GFP_KERNEL);
|
||||
if (segs - 1 > ULONG_MAX / sizeof(*vma_filesz))
|
||||
goto end_coredump;
|
||||
vma_filesz = vmalloc((segs - 1) * sizeof(*vma_filesz));
|
||||
if (!vma_filesz)
|
||||
goto end_coredump;
|
||||
|
||||
@@ -2311,7 +2313,7 @@ end_coredump:
|
||||
cleanup:
|
||||
free_note_info(&info);
|
||||
kfree(shdr4extnum);
|
||||
kfree(vma_filesz);
|
||||
vfree(vma_filesz);
|
||||
kfree(phdr4note);
|
||||
kfree(elf);
|
||||
out:
|
||||
|
@@ -202,27 +202,31 @@ static struct ratelimit_state printk_limits[] = {
|
||||
void btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...)
|
||||
{
|
||||
struct super_block *sb = fs_info->sb;
|
||||
char lvl[4];
|
||||
char lvl[PRINTK_MAX_SINGLE_HEADER_LEN + 1];
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
const char *type = logtypes[4];
|
||||
const char *type = NULL;
|
||||
int kern_level;
|
||||
struct ratelimit_state *ratelimit;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
kern_level = printk_get_level(fmt);
|
||||
if (kern_level) {
|
||||
while ((kern_level = printk_get_level(fmt)) != 0) {
|
||||
size_t size = printk_skip_level(fmt) - fmt;
|
||||
memcpy(lvl, fmt, size);
|
||||
lvl[size] = '\0';
|
||||
|
||||
if (kern_level >= '0' && kern_level <= '7') {
|
||||
memcpy(lvl, fmt, size);
|
||||
lvl[size] = '\0';
|
||||
type = logtypes[kern_level - '0'];
|
||||
ratelimit = &printk_limits[kern_level - '0'];
|
||||
}
|
||||
fmt += size;
|
||||
type = logtypes[kern_level - '0'];
|
||||
ratelimit = &printk_limits[kern_level - '0'];
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
*lvl = '\0';
|
||||
/* Default to debug output */
|
||||
ratelimit = &printk_limits[7];
|
||||
type = logtypes[4];
|
||||
ratelimit = &printk_limits[4];
|
||||
}
|
||||
|
||||
vaf.fmt = fmt;
|
||||
|
10
fs/dax.c
10
fs/dax.c
@@ -342,7 +342,7 @@ static inline void *lock_slot(struct address_space *mapping, void **slot)
|
||||
radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
|
||||
|
||||
entry |= RADIX_DAX_ENTRY_LOCK;
|
||||
radix_tree_replace_slot(slot, (void *)entry);
|
||||
radix_tree_replace_slot(&mapping->page_tree, slot, (void *)entry);
|
||||
return (void *)entry;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ static inline void *unlock_slot(struct address_space *mapping, void **slot)
|
||||
radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
|
||||
|
||||
entry &= ~(unsigned long)RADIX_DAX_ENTRY_LOCK;
|
||||
radix_tree_replace_slot(slot, (void *)entry);
|
||||
radix_tree_replace_slot(&mapping->page_tree, slot, (void *)entry);
|
||||
return (void *)entry;
|
||||
}
|
||||
|
||||
@@ -643,12 +643,14 @@ static void *dax_insert_mapping_entry(struct address_space *mapping,
|
||||
}
|
||||
mapping->nrexceptional++;
|
||||
} else {
|
||||
struct radix_tree_node *node;
|
||||
void **slot;
|
||||
void *ret;
|
||||
|
||||
ret = __radix_tree_lookup(page_tree, index, NULL, &slot);
|
||||
ret = __radix_tree_lookup(page_tree, index, &node, &slot);
|
||||
WARN_ON_ONCE(ret != entry);
|
||||
radix_tree_replace_slot(slot, new_entry);
|
||||
__radix_tree_replace(page_tree, node, slot,
|
||||
new_entry, NULL, NULL);
|
||||
}
|
||||
if (vmf->flags & FAULT_FLAG_WRITE)
|
||||
radix_tree_tag_set(page_tree, index, PAGECACHE_TAG_DIRTY);
|
||||
|
@@ -1769,15 +1769,13 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||
* become available for writeback. Otherwise
|
||||
* we'll just busyloop.
|
||||
*/
|
||||
if (!list_empty(&wb->b_more_io)) {
|
||||
trace_writeback_wait(wb, work);
|
||||
inode = wb_inode(wb->b_more_io.prev);
|
||||
spin_lock(&inode->i_lock);
|
||||
spin_unlock(&wb->list_lock);
|
||||
/* This function drops i_lock... */
|
||||
inode_sleep_on_writeback(inode);
|
||||
spin_lock(&wb->list_lock);
|
||||
}
|
||||
trace_writeback_wait(wb, work);
|
||||
inode = wb_inode(wb->b_more_io.prev);
|
||||
spin_lock(&inode->i_lock);
|
||||
spin_unlock(&wb->list_lock);
|
||||
/* This function drops i_lock... */
|
||||
inode_sleep_on_writeback(inode);
|
||||
spin_lock(&wb->list_lock);
|
||||
}
|
||||
spin_unlock(&wb->list_lock);
|
||||
blk_finish_plug(&plug);
|
||||
|
@@ -1950,8 +1950,7 @@ static void ocfs2_write_end_inline(struct inode *inode, loff_t pos,
|
||||
}
|
||||
|
||||
int ocfs2_write_end_nolock(struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
loff_t pos, unsigned len, unsigned copied, void *fsdata)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned from, to, start = pos & (PAGE_SIZE - 1);
|
||||
@@ -2064,7 +2063,7 @@ static int ocfs2_write_end(struct file *file, struct address_space *mapping,
|
||||
int ret;
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
ret = ocfs2_write_end_nolock(mapping, pos, len, copied, page, fsdata);
|
||||
ret = ocfs2_write_end_nolock(mapping, pos, len, copied, fsdata);
|
||||
|
||||
up_write(&OCFS2_I(inode)->ip_alloc_sem);
|
||||
ocfs2_inode_unlock(inode, 1);
|
||||
@@ -2241,7 +2240,7 @@ static int ocfs2_dio_get_block(struct inode *inode, sector_t iblock,
|
||||
dwc->dw_zero_count++;
|
||||
}
|
||||
|
||||
ret = ocfs2_write_end_nolock(inode->i_mapping, pos, len, len, NULL, wc);
|
||||
ret = ocfs2_write_end_nolock(inode->i_mapping, pos, len, len, wc);
|
||||
BUG_ON(ret != len);
|
||||
ret = 0;
|
||||
unlock:
|
||||
|
@@ -44,8 +44,7 @@ int walk_page_buffers( handle_t *handle,
|
||||
struct buffer_head *bh));
|
||||
|
||||
int ocfs2_write_end_nolock(struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata);
|
||||
loff_t pos, unsigned len, unsigned copied, void *fsdata);
|
||||
|
||||
typedef enum {
|
||||
OCFS2_WRITE_BUFFER = 0,
|
||||
|
@@ -741,7 +741,7 @@ static inline void o2hb_prepare_block(struct o2hb_region *reg,
|
||||
hb_block = (struct o2hb_disk_heartbeat_block *)slot->ds_raw_block;
|
||||
memset(hb_block, 0, reg->hr_block_bytes);
|
||||
/* TODO: time stuff */
|
||||
cputime = CURRENT_TIME.tv_sec;
|
||||
cputime = ktime_get_real_seconds();
|
||||
if (!cputime)
|
||||
cputime = 1;
|
||||
|
||||
|
@@ -1609,8 +1609,6 @@ way_up_top:
|
||||
__dlm_insert_mle(dlm, mle);
|
||||
response = DLM_MASTER_RESP_NO;
|
||||
} else {
|
||||
// mlog(0, "mle was found\n");
|
||||
set_maybe = 1;
|
||||
spin_lock(&tmpmle->spinlock);
|
||||
if (tmpmle->master == dlm->node_num) {
|
||||
mlog(ML_ERROR, "no lockres, but an mle with this node as master!\n");
|
||||
@@ -1625,8 +1623,7 @@ way_up_top:
|
||||
response = DLM_MASTER_RESP_NO;
|
||||
} else
|
||||
response = DLM_MASTER_RESP_MAYBE;
|
||||
if (set_maybe)
|
||||
set_bit(request->node_idx, tmpmle->maybe_map);
|
||||
set_bit(request->node_idx, tmpmle->maybe_map);
|
||||
spin_unlock(&tmpmle->spinlock);
|
||||
}
|
||||
spin_unlock(&dlm->master_lock);
|
||||
@@ -1644,12 +1641,6 @@ send_response:
|
||||
* dlm_assert_master_worker() isn't called, we drop it here.
|
||||
*/
|
||||
if (dispatch_assert) {
|
||||
if (response != DLM_MASTER_RESP_YES)
|
||||
mlog(ML_ERROR, "invalid response %d\n", response);
|
||||
if (!res) {
|
||||
mlog(ML_ERROR, "bad lockres while trying to assert!\n");
|
||||
BUG();
|
||||
}
|
||||
mlog(0, "%u is the owner of %.*s, cleaning everyone else\n",
|
||||
dlm->node_num, res->lockname.len, res->lockname.name);
|
||||
spin_lock(&res->spinlock);
|
||||
|
@@ -2966,8 +2966,6 @@ int dlm_finalize_reco_handler(struct o2net_msg *msg, u32 len, void *data,
|
||||
spin_unlock(&dlm->spinlock);
|
||||
dlm_kick_recovery_thread(dlm);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
mlog(0, "%s: recovery done, reco master was %u, dead now %u, master now %u\n",
|
||||
|
@@ -703,7 +703,7 @@ static int ocfs2_remove_inode(struct inode *inode,
|
||||
goto bail_commit;
|
||||
}
|
||||
|
||||
di->i_dtime = cpu_to_le64(CURRENT_TIME.tv_sec);
|
||||
di->i_dtime = cpu_to_le64(ktime_get_real_seconds());
|
||||
di->i_flags &= cpu_to_le32(~(OCFS2_VALID_FL | OCFS2_ORPHANED_FL));
|
||||
ocfs2_journal_dirty(handle, di_bh);
|
||||
|
||||
|
@@ -1947,7 +1947,7 @@ static void ocfs2_queue_orphan_scan(struct ocfs2_super *osb)
|
||||
*/
|
||||
seqno++;
|
||||
os->os_count++;
|
||||
os->os_scantime = CURRENT_TIME;
|
||||
os->os_scantime = ktime_get_seconds();
|
||||
unlock:
|
||||
ocfs2_orphan_scan_unlock(osb, seqno);
|
||||
out:
|
||||
@@ -2004,7 +2004,7 @@ void ocfs2_orphan_scan_start(struct ocfs2_super *osb)
|
||||
struct ocfs2_orphan_scan *os;
|
||||
|
||||
os = &osb->osb_orphan_scan;
|
||||
os->os_scantime = CURRENT_TIME;
|
||||
os->os_scantime = ktime_get_seconds();
|
||||
if (ocfs2_is_hard_readonly(osb) || ocfs2_mount_local(osb))
|
||||
atomic_set(&os->os_state, ORPHAN_SCAN_INACTIVE);
|
||||
else {
|
||||
|
@@ -120,8 +120,7 @@ static int __ocfs2_page_mkwrite(struct file *file, struct buffer_head *di_bh,
|
||||
ret = VM_FAULT_NOPAGE;
|
||||
goto out;
|
||||
}
|
||||
ret = ocfs2_write_end_nolock(mapping, pos, len, len, locked_page,
|
||||
fsdata);
|
||||
ret = ocfs2_write_end_nolock(mapping, pos, len, len, fsdata);
|
||||
BUG_ON(ret != len);
|
||||
ret = VM_FAULT_LOCKED;
|
||||
out:
|
||||
|
@@ -516,6 +516,7 @@ static int __ocfs2_mknod_locked(struct inode *dir,
|
||||
struct ocfs2_extent_list *fel;
|
||||
u16 feat;
|
||||
struct ocfs2_inode_info *oi = OCFS2_I(inode);
|
||||
struct timespec64 ts;
|
||||
|
||||
*new_fe_bh = NULL;
|
||||
|
||||
@@ -564,10 +565,11 @@ static int __ocfs2_mknod_locked(struct inode *dir,
|
||||
fe->i_last_eb_blk = 0;
|
||||
strcpy(fe->i_signature, OCFS2_INODE_SIGNATURE);
|
||||
fe->i_flags |= cpu_to_le32(OCFS2_VALID_FL);
|
||||
ktime_get_real_ts64(&ts);
|
||||
fe->i_atime = fe->i_ctime = fe->i_mtime =
|
||||
cpu_to_le64(CURRENT_TIME.tv_sec);
|
||||
cpu_to_le64(ts.tv_sec);
|
||||
fe->i_mtime_nsec = fe->i_ctime_nsec = fe->i_atime_nsec =
|
||||
cpu_to_le32(CURRENT_TIME.tv_nsec);
|
||||
cpu_to_le32(ts.tv_nsec);
|
||||
fe->i_dtime = 0;
|
||||
|
||||
/*
|
||||
|
@@ -224,7 +224,7 @@ struct ocfs2_orphan_scan {
|
||||
struct ocfs2_super *os_osb;
|
||||
struct ocfs2_lock_res os_lockres; /* lock to synchronize scans */
|
||||
struct delayed_work os_orphan_scan_work;
|
||||
struct timespec os_scantime; /* time this node ran the scan */
|
||||
time64_t os_scantime; /* time this node ran the scan */
|
||||
u32 os_count; /* tracks node specific scans */
|
||||
u32 os_seqno; /* tracks cluster wide scans */
|
||||
atomic_t os_state; /* ACTIVE or INACTIVE */
|
||||
|
@@ -478,7 +478,6 @@ again:
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
ocfs2_unlock_refcount_tree(osb, tree, rw);
|
||||
ocfs2_refcount_tree_put(tree);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@@ -337,7 +337,7 @@ static int ocfs2_osb_dump(struct ocfs2_super *osb, char *buf, int len)
|
||||
out += snprintf(buf + out, len - out, "Disabled\n");
|
||||
else
|
||||
out += snprintf(buf + out, len - out, "%lu seconds ago\n",
|
||||
(get_seconds() - os->os_scantime.tv_sec));
|
||||
(unsigned long)(ktime_get_seconds() - os->os_scantime));
|
||||
|
||||
out += snprintf(buf + out, len - out, "%10s => %3s %10s\n",
|
||||
"Slots", "Num", "RecoGen");
|
||||
|
@@ -245,7 +245,7 @@ void render_sigset_t(struct seq_file *m, const char *header,
|
||||
if (sigismember(set, i+2)) x |= 2;
|
||||
if (sigismember(set, i+3)) x |= 4;
|
||||
if (sigismember(set, i+4)) x |= 8;
|
||||
seq_printf(m, "%x", x);
|
||||
seq_putc(m, hex_asc[x]);
|
||||
} while (i >= 4);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
@@ -342,10 +342,11 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p)
|
||||
|
||||
static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
|
||||
{
|
||||
seq_put_decimal_ull(m, "NoNewPrivs:\t", task_no_new_privs(p));
|
||||
#ifdef CONFIG_SECCOMP
|
||||
seq_put_decimal_ull(m, "Seccomp:\t", p->seccomp.mode);
|
||||
seq_putc(m, '\n');
|
||||
seq_put_decimal_ull(m, "\nSeccomp:\t", p->seccomp.mode);
|
||||
#endif
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
static inline void task_context_switch_counts(struct seq_file *m,
|
||||
|
@@ -104,9 +104,12 @@
|
||||
* in /proc for a task before it execs a suid executable.
|
||||
*/
|
||||
|
||||
static u8 nlink_tid;
|
||||
static u8 nlink_tgid;
|
||||
|
||||
struct pid_entry {
|
||||
const char *name;
|
||||
int len;
|
||||
unsigned int len;
|
||||
umode_t mode;
|
||||
const struct inode_operations *iop;
|
||||
const struct file_operations *fop;
|
||||
@@ -139,13 +142,13 @@ struct pid_entry {
|
||||
* Count the number of hardlinks for the pid_entry table, excluding the .
|
||||
* and .. links.
|
||||
*/
|
||||
static unsigned int pid_entry_count_dirs(const struct pid_entry *entries,
|
||||
static unsigned int __init pid_entry_nlink(const struct pid_entry *entries,
|
||||
unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int count;
|
||||
|
||||
count = 0;
|
||||
count = 2;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (S_ISDIR(entries[i].mode))
|
||||
++count;
|
||||
@@ -1967,7 +1970,7 @@ out:
|
||||
|
||||
struct map_files_info {
|
||||
fmode_t mode;
|
||||
unsigned long len;
|
||||
unsigned int len;
|
||||
unsigned char name[4*sizeof(long)+2]; /* max: %lx-%lx\0 */
|
||||
};
|
||||
|
||||
@@ -2412,14 +2415,14 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
|
||||
* Yes, it does not scale. And it should not. Don't add
|
||||
* new entries into /proc/<tgid>/ without very good reasons.
|
||||
*/
|
||||
last = &ents[nents - 1];
|
||||
for (p = ents; p <= last; p++) {
|
||||
last = &ents[nents];
|
||||
for (p = ents; p < last; p++) {
|
||||
if (p->len != dentry->d_name.len)
|
||||
continue;
|
||||
if (!memcmp(dentry->d_name.name, p->name, p->len))
|
||||
break;
|
||||
}
|
||||
if (p > last)
|
||||
if (p >= last)
|
||||
goto out;
|
||||
|
||||
error = proc_pident_instantiate(dir, dentry, task, p);
|
||||
@@ -2444,7 +2447,7 @@ static int proc_pident_readdir(struct file *file, struct dir_context *ctx,
|
||||
if (ctx->pos >= nents + 2)
|
||||
goto out;
|
||||
|
||||
for (p = ents + (ctx->pos - 2); p <= ents + nents - 1; p++) {
|
||||
for (p = ents + (ctx->pos - 2); p < ents + nents; p++) {
|
||||
if (!proc_fill_cache(file, ctx, p->name, p->len,
|
||||
proc_pident_instantiate, task, p))
|
||||
break;
|
||||
@@ -3068,8 +3071,7 @@ static int proc_pid_instantiate(struct inode *dir,
|
||||
inode->i_fop = &proc_tgid_base_operations;
|
||||
inode->i_flags|=S_IMMUTABLE;
|
||||
|
||||
set_nlink(inode, 2 + pid_entry_count_dirs(tgid_base_stuff,
|
||||
ARRAY_SIZE(tgid_base_stuff)));
|
||||
set_nlink(inode, nlink_tgid);
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
|
||||
@@ -3361,8 +3363,7 @@ static int proc_task_instantiate(struct inode *dir,
|
||||
inode->i_fop = &proc_tid_base_operations;
|
||||
inode->i_flags|=S_IMMUTABLE;
|
||||
|
||||
set_nlink(inode, 2 + pid_entry_count_dirs(tid_base_stuff,
|
||||
ARRAY_SIZE(tid_base_stuff)));
|
||||
set_nlink(inode, nlink_tid);
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
|
||||
@@ -3552,3 +3553,9 @@ static const struct file_operations proc_task_operations = {
|
||||
.iterate_shared = proc_task_readdir,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
void __init set_proc_pid_nlink(void)
|
||||
{
|
||||
nlink_tid = pid_entry_nlink(tid_base_stuff, ARRAY_SIZE(tid_base_stuff));
|
||||
nlink_tgid = pid_entry_nlink(tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff));
|
||||
}
|
||||
|
@@ -138,6 +138,16 @@ static void unuse_pde(struct proc_dir_entry *pde)
|
||||
/* pde is locked */
|
||||
static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
|
||||
{
|
||||
/*
|
||||
* close() (proc_reg_release()) can't delete an entry and proceed:
|
||||
* ->release hook needs to be available at the right moment.
|
||||
*
|
||||
* rmmod (remove_proc_entry() et al) can't delete an entry and proceed:
|
||||
* "struct file" needs to be available at the right moment.
|
||||
*
|
||||
* Therefore, first process to enter this function does ->release() and
|
||||
* signals its completion to the other process which does nothing.
|
||||
*/
|
||||
if (pdeo->closing) {
|
||||
/* somebody else is doing that, just wait */
|
||||
DECLARE_COMPLETION_ONSTACK(c);
|
||||
@@ -147,12 +157,13 @@ static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
} else {
|
||||
struct file *file;
|
||||
pdeo->closing = 1;
|
||||
pdeo->closing = true;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
file = pdeo->file;
|
||||
pde->proc_fops->release(file_inode(file), file);
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
list_del_init(&pdeo->lh);
|
||||
/* After ->release. */
|
||||
list_del(&pdeo->lh);
|
||||
if (pdeo->c)
|
||||
complete(pdeo->c);
|
||||
kfree(pdeo);
|
||||
@@ -167,6 +178,8 @@ void proc_entry_rundown(struct proc_dir_entry *de)
|
||||
if (atomic_add_return(BIAS, &de->in_use) != BIAS)
|
||||
wait_for_completion(&c);
|
||||
|
||||
/* ->pde_openers list can't grow from now on. */
|
||||
|
||||
spin_lock(&de->pde_unload_lock);
|
||||
while (!list_empty(&de->pde_openers)) {
|
||||
struct pde_opener *pdeo;
|
||||
@@ -312,16 +325,17 @@ static int proc_reg_open(struct inode *inode, struct file *file)
|
||||
struct pde_opener *pdeo;
|
||||
|
||||
/*
|
||||
* What for, you ask? Well, we can have open, rmmod, remove_proc_entry
|
||||
* sequence. ->release won't be called because ->proc_fops will be
|
||||
* cleared. Depending on complexity of ->release, consequences vary.
|
||||
* Ensure that
|
||||
* 1) PDE's ->release hook will be called no matter what
|
||||
* either normally by close()/->release, or forcefully by
|
||||
* rmmod/remove_proc_entry.
|
||||
*
|
||||
* We can't wait for mercy when close will be done for real, it's
|
||||
* deadlockable: rmmod foo </proc/foo . So, we're going to do ->release
|
||||
* by hand in remove_proc_entry(). For this, save opener's credentials
|
||||
* for later.
|
||||
* 2) rmmod isn't blocked by opening file in /proc and sitting on
|
||||
* the descriptor (including "rmmod foo </proc/foo" scenario).
|
||||
*
|
||||
* Save every "struct file" with custom ->release hook.
|
||||
*/
|
||||
pdeo = kzalloc(sizeof(struct pde_opener), GFP_KERNEL);
|
||||
pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL);
|
||||
if (!pdeo)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -338,7 +352,8 @@ static int proc_reg_open(struct inode *inode, struct file *file)
|
||||
if (rv == 0 && release) {
|
||||
/* To know what to release. */
|
||||
pdeo->file = file;
|
||||
/* Strictly for "too late" ->release in proc_reg_release(). */
|
||||
pdeo->closing = false;
|
||||
pdeo->c = NULL;
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
list_add(&pdeo->lh, &pde->pde_openers);
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
@@ -203,7 +203,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name);
|
||||
struct pde_opener {
|
||||
struct file *file;
|
||||
struct list_head lh;
|
||||
int closing;
|
||||
bool closing;
|
||||
struct completion *c;
|
||||
};
|
||||
extern const struct inode_operations proc_link_inode_operations;
|
||||
@@ -211,6 +211,7 @@ extern const struct inode_operations proc_link_inode_operations;
|
||||
extern const struct inode_operations proc_pid_link_inode_operations;
|
||||
|
||||
extern void proc_init_inodecache(void);
|
||||
void set_proc_pid_nlink(void);
|
||||
extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
|
||||
extern int proc_fill_super(struct super_block *, void *data, int flags);
|
||||
extern void proc_entry_rundown(struct proc_dir_entry *);
|
||||
|
@@ -122,6 +122,7 @@ void __init proc_root_init(void)
|
||||
int err;
|
||||
|
||||
proc_init_inodecache();
|
||||
set_proc_pid_nlink();
|
||||
err = register_filesystem(&proc_fs_type);
|
||||
if (err)
|
||||
return;
|
||||
|
@@ -1588,6 +1588,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr,
|
||||
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
pte_unmap_unlock(orig_pte, ptl);
|
||||
cond_resched();
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
|
Reference in New Issue
Block a user