![Tejun Heo](/assets/img/avatar_default.png)
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
454 lines
11 KiB
C
454 lines
11 KiB
C
/*
|
|
* file.c
|
|
*
|
|
* Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke
|
|
* Copyright (C) 1997 by Volker Lendecke
|
|
*
|
|
* Please add a note about your changes to smbfs in the ChangeLog file.
|
|
*/
|
|
|
|
#include <linux/time.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/net.h>
|
|
#include <linux/aio.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/system.h>
|
|
|
|
#include <linux/smbno.h>
|
|
#include <linux/smb_fs.h>
|
|
|
|
#include "smb_debug.h"
|
|
#include "proto.h"
|
|
|
|
static int
|
|
smb_fsync(struct file *file, struct dentry * dentry, int datasync)
|
|
{
|
|
struct smb_sb_info *server = server_from_dentry(dentry);
|
|
int result;
|
|
|
|
VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry));
|
|
|
|
/*
|
|
* The VFS will writepage() all dirty pages for us, but we
|
|
* should send a SMBflush to the server, letting it know that
|
|
* we want things synchronized with actual storage.
|
|
*
|
|
* Note: this function requires all pages to have been written already
|
|
* (should be ok with writepage_sync)
|
|
*/
|
|
result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Read a page synchronously.
|
|
*/
|
|
static int
|
|
smb_readpage_sync(struct dentry *dentry, struct page *page)
|
|
{
|
|
char *buffer = kmap(page);
|
|
loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
|
|
struct smb_sb_info *server = server_from_dentry(dentry);
|
|
unsigned int rsize = smb_get_rsize(server);
|
|
int count = PAGE_SIZE;
|
|
int result;
|
|
|
|
VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n",
|
|
DENTRY_PATH(dentry), count, offset, rsize);
|
|
|
|
result = smb_open(dentry, SMB_O_RDONLY);
|
|
if (result < 0)
|
|
goto io_error;
|
|
|
|
do {
|
|
if (count < rsize)
|
|
rsize = count;
|
|
|
|
result = server->ops->read(dentry->d_inode,offset,rsize,buffer);
|
|
if (result < 0)
|
|
goto io_error;
|
|
|
|
count -= result;
|
|
offset += result;
|
|
buffer += result;
|
|
dentry->d_inode->i_atime =
|
|
current_fs_time(dentry->d_inode->i_sb);
|
|
if (result < rsize)
|
|
break;
|
|
} while (count);
|
|
|
|
memset(buffer, 0, count);
|
|
flush_dcache_page(page);
|
|
SetPageUptodate(page);
|
|
result = 0;
|
|
|
|
io_error:
|
|
kunmap(page);
|
|
unlock_page(page);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* We are called with the page locked and we unlock it when done.
|
|
*/
|
|
static int
|
|
smb_readpage(struct file *file, struct page *page)
|
|
{
|
|
int error;
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
|
|
page_cache_get(page);
|
|
error = smb_readpage_sync(dentry, page);
|
|
page_cache_release(page);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Write a page synchronously.
|
|
* Offset is the data offset within the page.
|
|
*/
|
|
static int
|
|
smb_writepage_sync(struct inode *inode, struct page *page,
|
|
unsigned long pageoffset, unsigned int count)
|
|
{
|
|
loff_t offset;
|
|
char *buffer = kmap(page) + pageoffset;
|
|
struct smb_sb_info *server = server_from_inode(inode);
|
|
unsigned int wsize = smb_get_wsize(server);
|
|
int ret = 0;
|
|
|
|
offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset;
|
|
VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n",
|
|
inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize);
|
|
|
|
do {
|
|
int write_ret;
|
|
|
|
if (count < wsize)
|
|
wsize = count;
|
|
|
|
write_ret = server->ops->write(inode, offset, wsize, buffer);
|
|
if (write_ret < 0) {
|
|
PARANOIA("failed write, wsize=%d, write_ret=%d\n",
|
|
wsize, write_ret);
|
|
ret = write_ret;
|
|
break;
|
|
}
|
|
/* N.B. what if result < wsize?? */
|
|
#ifdef SMBFS_PARANOIA
|
|
if (write_ret < wsize)
|
|
PARANOIA("short write, wsize=%d, write_ret=%d\n",
|
|
wsize, write_ret);
|
|
#endif
|
|
buffer += wsize;
|
|
offset += wsize;
|
|
count -= wsize;
|
|
/*
|
|
* Update the inode now rather than waiting for a refresh.
|
|
*/
|
|
inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb);
|
|
SMB_I(inode)->flags |= SMB_F_LOCALWRITE;
|
|
if (offset > inode->i_size)
|
|
inode->i_size = offset;
|
|
} while (count);
|
|
|
|
kunmap(page);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Write a page to the server. This will be used for NFS swapping only
|
|
* (for now), and we currently do this synchronously only.
|
|
*
|
|
* We are called with the page locked and we unlock it when done.
|
|
*/
|
|
static int
|
|
smb_writepage(struct page *page, struct writeback_control *wbc)
|
|
{
|
|
struct address_space *mapping = page->mapping;
|
|
struct inode *inode;
|
|
unsigned long end_index;
|
|
unsigned offset = PAGE_CACHE_SIZE;
|
|
int err;
|
|
|
|
BUG_ON(!mapping);
|
|
inode = mapping->host;
|
|
BUG_ON(!inode);
|
|
|
|
end_index = inode->i_size >> PAGE_CACHE_SHIFT;
|
|
|
|
/* easy case */
|
|
if (page->index < end_index)
|
|
goto do_it;
|
|
/* things got complicated... */
|
|
offset = inode->i_size & (PAGE_CACHE_SIZE-1);
|
|
/* OK, are we completely out? */
|
|
if (page->index >= end_index+1 || !offset)
|
|
return 0; /* truncated - don't care */
|
|
do_it:
|
|
page_cache_get(page);
|
|
err = smb_writepage_sync(inode, page, 0, offset);
|
|
SetPageUptodate(page);
|
|
unlock_page(page);
|
|
page_cache_release(page);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
smb_updatepage(struct file *file, struct page *page, unsigned long offset,
|
|
unsigned int count)
|
|
{
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
|
|
DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count,
|
|
((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset);
|
|
|
|
return smb_writepage_sync(dentry->d_inode, page, offset, count);
|
|
}
|
|
|
|
static ssize_t
|
|
smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
|
unsigned long nr_segs, loff_t pos)
|
|
{
|
|
struct file * file = iocb->ki_filp;
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
ssize_t status;
|
|
|
|
VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry),
|
|
(unsigned long) iocb->ki_left, (unsigned long) pos);
|
|
|
|
status = smb_revalidate_inode(dentry);
|
|
if (status) {
|
|
PARANOIA("%s/%s validation failed, error=%Zd\n",
|
|
DENTRY_PATH(dentry), status);
|
|
goto out;
|
|
}
|
|
|
|
VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n",
|
|
(long)dentry->d_inode->i_size,
|
|
dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec);
|
|
|
|
status = generic_file_aio_read(iocb, iov, nr_segs, pos);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
smb_file_mmap(struct file * file, struct vm_area_struct * vma)
|
|
{
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
int status;
|
|
|
|
VERBOSE("file %s/%s, address %lu - %lu\n",
|
|
DENTRY_PATH(dentry), vma->vm_start, vma->vm_end);
|
|
|
|
status = smb_revalidate_inode(dentry);
|
|
if (status) {
|
|
PARANOIA("%s/%s validation failed, error=%d\n",
|
|
DENTRY_PATH(dentry), status);
|
|
goto out;
|
|
}
|
|
status = generic_file_mmap(file, vma);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static ssize_t
|
|
smb_file_splice_read(struct file *file, loff_t *ppos,
|
|
struct pipe_inode_info *pipe, size_t count,
|
|
unsigned int flags)
|
|
{
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
ssize_t status;
|
|
|
|
VERBOSE("file %s/%s, pos=%Ld, count=%lu\n",
|
|
DENTRY_PATH(dentry), *ppos, count);
|
|
|
|
status = smb_revalidate_inode(dentry);
|
|
if (status) {
|
|
PARANOIA("%s/%s validation failed, error=%Zd\n",
|
|
DENTRY_PATH(dentry), status);
|
|
goto out;
|
|
}
|
|
status = generic_file_splice_read(file, ppos, pipe, count, flags);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* This does the "real" work of the write. The generic routine has
|
|
* allocated the page, locked it, done all the page alignment stuff
|
|
* calculations etc. Now we should just copy the data from user
|
|
* space and write it back to the real medium..
|
|
*
|
|
* If the writer ends up delaying the write, the writer needs to
|
|
* increment the page use counts until he is done with the page.
|
|
*/
|
|
static int smb_write_begin(struct file *file, struct address_space *mapping,
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
struct page **pagep, void **fsdata)
|
|
{
|
|
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
*pagep = grab_cache_page_write_begin(mapping, index, flags);
|
|
if (!*pagep)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
static int smb_write_end(struct file *file, struct address_space *mapping,
|
|
loff_t pos, unsigned len, unsigned copied,
|
|
struct page *page, void *fsdata)
|
|
{
|
|
int status;
|
|
unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
|
|
|
|
lock_kernel();
|
|
status = smb_updatepage(file, page, offset, copied);
|
|
unlock_kernel();
|
|
|
|
if (!status) {
|
|
if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
|
|
SetPageUptodate(page);
|
|
status = copied;
|
|
}
|
|
|
|
unlock_page(page);
|
|
page_cache_release(page);
|
|
|
|
return status;
|
|
}
|
|
|
|
const struct address_space_operations smb_file_aops = {
|
|
.readpage = smb_readpage,
|
|
.writepage = smb_writepage,
|
|
.write_begin = smb_write_begin,
|
|
.write_end = smb_write_end,
|
|
};
|
|
|
|
/*
|
|
* Write to a file (through the page cache).
|
|
*/
|
|
static ssize_t
|
|
smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|
unsigned long nr_segs, loff_t pos)
|
|
{
|
|
struct file * file = iocb->ki_filp;
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
ssize_t result;
|
|
|
|
VERBOSE("file %s/%s, count=%lu@%lu\n",
|
|
DENTRY_PATH(dentry),
|
|
(unsigned long) iocb->ki_left, (unsigned long) pos);
|
|
|
|
result = smb_revalidate_inode(dentry);
|
|
if (result) {
|
|
PARANOIA("%s/%s validation failed, error=%Zd\n",
|
|
DENTRY_PATH(dentry), result);
|
|
goto out;
|
|
}
|
|
|
|
result = smb_open(dentry, SMB_O_WRONLY);
|
|
if (result)
|
|
goto out;
|
|
|
|
if (iocb->ki_left > 0) {
|
|
result = generic_file_aio_write(iocb, iov, nr_segs, pos);
|
|
VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
|
|
(long) file->f_pos, (long) dentry->d_inode->i_size,
|
|
dentry->d_inode->i_mtime.tv_sec,
|
|
dentry->d_inode->i_atime.tv_sec);
|
|
}
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
smb_file_open(struct inode *inode, struct file * file)
|
|
{
|
|
int result;
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
int smb_mode = (file->f_mode & O_ACCMODE) - 1;
|
|
|
|
lock_kernel();
|
|
result = smb_open(dentry, smb_mode);
|
|
if (result)
|
|
goto out;
|
|
SMB_I(inode)->openers++;
|
|
out:
|
|
unlock_kernel();
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
smb_file_release(struct inode *inode, struct file * file)
|
|
{
|
|
lock_kernel();
|
|
if (!--SMB_I(inode)->openers) {
|
|
/* We must flush any dirty pages now as we won't be able to
|
|
write anything after close. mmap can trigger this.
|
|
"openers" should perhaps include mmap'ers ... */
|
|
filemap_write_and_wait(inode->i_mapping);
|
|
smb_close(inode);
|
|
}
|
|
unlock_kernel();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check whether the required access is compatible with
|
|
* an inode's permission. SMB doesn't recognize superuser
|
|
* privileges, so we need our own check for this.
|
|
*/
|
|
static int
|
|
smb_file_permission(struct inode *inode, int mask)
|
|
{
|
|
int mode = inode->i_mode;
|
|
int error = 0;
|
|
|
|
VERBOSE("mode=%x, mask=%x\n", mode, mask);
|
|
|
|
/* Look at user permissions */
|
|
mode >>= 6;
|
|
if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC))
|
|
error = -EACCES;
|
|
return error;
|
|
}
|
|
|
|
static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin)
|
|
{
|
|
loff_t ret;
|
|
lock_kernel();
|
|
ret = generic_file_llseek_unlocked(file, offset, origin);
|
|
unlock_kernel();
|
|
return ret;
|
|
}
|
|
|
|
const struct file_operations smb_file_operations =
|
|
{
|
|
.llseek = smb_remote_llseek,
|
|
.read = do_sync_read,
|
|
.aio_read = smb_file_aio_read,
|
|
.write = do_sync_write,
|
|
.aio_write = smb_file_aio_write,
|
|
.ioctl = smb_ioctl,
|
|
.mmap = smb_file_mmap,
|
|
.open = smb_file_open,
|
|
.release = smb_file_release,
|
|
.fsync = smb_fsync,
|
|
.splice_read = smb_file_splice_read,
|
|
};
|
|
|
|
const struct inode_operations smb_file_inode_operations =
|
|
{
|
|
.permission = smb_file_permission,
|
|
.getattr = smb_getattr,
|
|
.setattr = smb_notify_change,
|
|
};
|