Commit 37c55498 authored by Urban Widmark's avatar Urban Widmark Committed by Linus Torvalds

[PATCH] smbfs - smbiod

This patch for 2.5.25 is a rewrite of how smbfs builds requests. It allows
for more parallellism, better error handling and supporting oplocks with
further patches.
parent 145ecbd6
......@@ -4,7 +4,8 @@
obj-$(CONFIG_SMB_FS) += smbfs.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \
smbiod.o request.o
# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
# SMBFS_PARANOIA should normally be enabled.
......@@ -23,7 +24,7 @@ include $(TOPDIR)/Rules.make
#
# getopt.c not included. It is intentionally separate
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c
proto:
-rm -f proto.h
......@@ -31,5 +32,7 @@ proto:
@echo >> proto2.h " * Autogenerated with cproto on: " `date`
@echo >> proto2.h " */"
@echo >> proto2.h ""
@echo >> proto2.h "struct smb_request;"
@echo >> proto2.h ""
cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO -D__KERNEL__ $(SRC) >> proto2.h
mv proto2.h proto.h
......@@ -42,9 +42,7 @@ smb_fsync(struct file *file, struct dentry * dentry, int datasync)
* Note: this function requires all pages to have been written already
* (should be ok with writepage_sync)
*/
smb_lock_server(server);
result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid);
smb_unlock_server(server);
return result;
}
......@@ -129,7 +127,7 @@ smb_writepage_sync(struct inode *inode, struct page *page,
offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset;
VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n",
inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize);
inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize);
do {
if (count < wsize)
......
......@@ -434,17 +434,17 @@ smb_put_super(struct super_block *sb)
{
struct smb_sb_info *server = SMB_SB(sb);
if (server->sock_file) {
smb_dont_catch_keepalive(server);
fput(server->sock_file);
}
smbiod_unregister_server(server);
smb_lock_server(server);
server->state = CONN_INVALID;
smb_close_socket(server);
if (server->conn_pid)
kill_proc(server->conn_pid, SIGTERM, 1);
kill_proc(server->conn_pid, SIGTERM, 1);
smb_kfree(server->ops);
if (server->packet)
smb_vfree(server->packet);
if (server->remote_nls) {
unload_nls(server->remote_nls);
......@@ -455,6 +455,7 @@ smb_put_super(struct super_block *sb)
server->local_nls = NULL;
}
sb->u.generic_sbp = NULL;
smb_unlock_server(server);
smb_kfree(server);
}
......@@ -491,32 +492,25 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
server->mnt = NULL;
server->sock_file = NULL;
init_MUTEX(&server->sem);
init_waitqueue_head(&server->wait);
INIT_LIST_HEAD(&server->entry);
INIT_LIST_HEAD(&server->xmitq);
INIT_LIST_HEAD(&server->recvq);
server->conn_error = 0;
server->conn_pid = 0;
server->state = CONN_INVALID; /* no connection yet */
server->generation = 0;
server->packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
server->packet = smb_vmalloc(server->packet_size);
if (!server->packet)
goto out_no_mem;
/* Allocate the global temp buffer and some superblock helper structs */
/* FIXME: move these to the smb_sb_info struct */
VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
2*SMB_MAXPATHLEN + 20);
sizeof(struct smb_mount_data_kernel));
mem = smb_kmalloc(sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
2*SMB_MAXPATHLEN + 20, GFP_KERNEL);
sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
if (!mem)
goto out_no_temp;
goto out_no_mem;
server->ops = mem;
server->mnt = mem + sizeof(struct smb_ops);
server->name_buf = mem + sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel);
server->temp_buf = mem + sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
SMB_MAXPATHLEN + 1;
/* Setup NLS stuff */
server->remote_nls = NULL;
......@@ -573,14 +567,14 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
goto out_no_root;
smb_new_dentry(sb->s_root);
smbiod_register_server(server);
return 0;
out_no_root:
iput(root_inode);
out_bad_option:
smb_kfree(mem);
out_no_temp:
smb_vfree(server->packet);
out_no_mem:
if (!server->mnt)
printk(KERN_ERR "smb_fill_super: allocation failure\n");
......@@ -764,14 +758,19 @@ static int __init init_smb_fs(void)
err = init_inodecache();
if (err)
goto out1;
goto out_inode;
err = smb_init_request_cache();
if (err)
goto out_request;
err = register_filesystem(&smb_fs_type);
if (err)
goto out;
return 0;
out:
smb_destroy_request_cache();
out_request:
destroy_inodecache();
out1:
out_inode:
return err;
}
......@@ -779,6 +778,7 @@ static void __exit exit_smb_fs(void)
{
DEBUG1("unregistering ...\n");
unregister_filesystem(&smb_fs_type);
smb_destroy_request_cache();
destroy_inodecache();
#ifdef DEBUG_SMB_MALLOC
printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
......
......@@ -41,7 +41,13 @@ smb_ioctl(struct inode *inode, struct file *filp,
case SMB_IOC_NEWCONN:
/* arg is smb_conn_opt, or NULL if no connection was made */
if (!arg) {
result = smb_wakeup(server);
result = 0;
smb_lock_server(server);
server->state = CONN_RETRIED;
printk(KERN_ERR "Connection attempt failed! [%d]\n",
server->conn_error);
smbiod_flush(server);
smb_unlock_server(server);
break;
}
......
This diff is collapsed.
/*
* Autogenerated with cproto on: Thu Nov 22 21:18:04 CET 2001
* Autogenerated with cproto on: Fri Jul 12 22:15:26 CEST 2002
*/
struct smb_request;
/* proc.c */
extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
extern __u32 smb_len(__u8 *p);
extern int smb_get_rsize(struct smb_sb_info *server);
extern int smb_get_wsize(struct smb_sb_info *server);
extern int smb_errno(struct smb_sb_info *server);
extern int smb_errno(struct smb_request *req);
extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
extern int smb_wakeup(struct smb_sb_info *server);
extern __u8 *smb_setup_header(struct smb_sb_info *server, __u8 command, __u16 wct, __u16 bcc);
extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc);
extern int smb_open(struct dentry *dentry, int wish);
extern int smb_close(struct inode *ino);
extern int smb_close_fileid(struct dentry *dentry, __u16 fileid);
......@@ -36,19 +37,21 @@ extern void smb_invalidate_dircache_entries(struct dentry *parent);
extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos);
extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry);
/* sock.c */
extern void smb_data_ready(struct sock *sk, int len);
extern int smb_valid_socket(struct inode *inode);
extern int smb_catch_keepalive(struct smb_sb_info *server);
extern int smb_dont_catch_keepalive(struct smb_sb_info *server);
extern void smb_close_socket(struct smb_sb_info *server);
extern int smb_round_length(int len);
extern int smb_request(struct smb_sb_info *server);
extern int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param, int *lrdata, unsigned char **rdata, int *lrparam, unsigned char **rparam);
extern int smb_recv_available(struct smb_sb_info *server);
extern int smb_receive_header(struct smb_sb_info *server);
extern int smb_receive_drop(struct smb_sb_info *server);
extern int smb_receive(struct smb_sb_info *server, struct smb_request *req);
extern int smb_send_request(struct smb_request *req);
/* inode.c */
extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr);
extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr);
extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr);
extern void smb_invalidate_inodes(struct smb_sb_info *server);
extern int smb_revalidate_inode(struct dentry *dentry);
extern int smb_fill_super(struct super_block *sb, void *raw_data, int silent);
extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
extern int smb_notify_change(struct dentry *dentry, struct iattr *attr);
/* file.c */
......@@ -57,3 +60,19 @@ extern struct file_operations smb_file_operations;
extern struct inode_operations smb_file_inode_operations;
/* ioctl.c */
extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
/* smbiod.c */
extern void smbiod_wake_up(void);
extern void smbiod_register_server(struct smb_sb_info *server);
extern void smbiod_unregister_server(struct smb_sb_info *server);
extern void smbiod_flush(struct smb_sb_info *server);
extern int smbiod_retry(struct smb_sb_info *server);
/* request.c */
extern int smb_init_request_cache(void);
extern void smb_destroy_request_cache(void);
extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize);
extern void smb_rget(struct smb_request *req);
extern void smb_rput(struct smb_request *req);
extern int smb_add_request(struct smb_request *req);
extern int smb_request_send_req(struct smb_request *req);
extern int smb_request_send_server(struct smb_sb_info *server);
extern int smb_request_recv(struct smb_sb_info *server);
This diff is collapsed.
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/list.h>
struct smb_request {
struct list_head rq_queue; /* recvq or xmitq for the server */
atomic_t rq_count;
wait_queue_head_t rq_wait;
int rq_flags;
int rq_mid; /* multiplex ID, set by request.c */
struct smb_sb_info *rq_server;
/* header + word count + parameter words + byte count */
unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2];
int rq_bufsize;
unsigned char *rq_buffer;
/* FIXME: this is not good enough for merging IO requests. */
unsigned char *rq_page;
int rq_rsize;
int rq_resp_wct;
int rq_resp_bcc;
int rq_rlen;
int rq_bytes_recvd;
int rq_slen;
int rq_bytes_sent;
int rq_iovlen;
struct iovec rq_iov[4];
int (*rq_setup_read) (struct smb_request *);
void (*rq_callback) (struct smb_request *);
/* ------ trans2 stuff ------ */
u16 rq_trans2_command; /* 0 if not a trans2 request */
unsigned int rq_ldata;
unsigned char *rq_data;
unsigned int rq_lparm;
unsigned char *rq_parm;
int rq_fragment;
u32 rq_total_data;
u32 rq_total_parm;
int rq_trans2bufsize;
unsigned char *rq_trans2buffer;
/* ------ response ------ */
unsigned short rq_rcls;
unsigned short rq_err;
int rq_errno;
};
#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */
#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */
#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */
#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */
#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */
#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */
/*
* smbiod.c
*
* Copyright (C) 2000, Charles Loep / Corel Corp.
* Copyright (C) 2001, Urban Widmark
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/dcache.h>
#include <linux/smp_lock.h>
#include <net/ip.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smb_mount.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "smb_debug.h"
#include "request.h"
#include "proto.h"
static int smbiod_pid = -1;
static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait);
static LIST_HEAD(smb_servers);
static spinlock_t servers_lock = SPIN_LOCK_UNLOCKED;
#define SMBIOD_DATA_READY (1<<0)
static long smbiod_flags;
static int smbiod(void *);
static void smbiod_start(void);
static void smbiod_stop(void);
/*
* called when there's work for us to do
*/
void smbiod_wake_up()
{
if (smbiod_pid == -1)
return;
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
wake_up_interruptible(&smbiod_wait);
}
/*
* start smbiod if none is running
*/
static void smbiod_start()
{
if (smbiod_pid != -1)
return;
smbiod_pid = kernel_thread(smbiod, NULL, 0);
}
/*
* stop smbiod if there are no open connections
*/
static void smbiod_stop()
{
if (smbiod_pid != -1 && list_empty(&smb_servers))
kill_proc(smbiod_pid, SIGKILL, 1);
}
/*
* register a server & start smbiod if necessary
*/
void smbiod_register_server(struct smb_sb_info *server)
{
spin_lock(&servers_lock);
list_add(&server->entry, &smb_servers);
VERBOSE("%p\n", server);
smbiod_start();
spin_unlock(&servers_lock);
}
/*
* unregister a server & stop smbiod if necessary
*/
void smbiod_unregister_server(struct smb_sb_info *server)
{
spin_lock(&servers_lock);
list_del_init(&server->entry);
VERBOSE("%p\n", server);
smbiod_stop();
spin_unlock(&servers_lock);
smb_lock_server(server);
smbiod_flush(server);
smb_unlock_server(server);
}
void smbiod_flush(struct smb_sb_info *server)
{
struct list_head *tmp, *n;
struct smb_request *req;
list_for_each_safe(tmp, n, &server->xmitq) {
req = list_entry(tmp, struct smb_request, rq_queue);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
list_for_each_safe(tmp, n, &server->recvq) {
req = list_entry(tmp, struct smb_request, rq_queue);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
}
/*
* Wake up smbmount and make it reconnect to the server.
* This must be called with the server locked.
*
* FIXME: add smbconnect version to this
*/
int smbiod_retry(struct smb_sb_info *server)
{
struct list_head *head;
struct smb_request *req;
pid_t pid = server->conn_pid;
int result = 0;
VERBOSE("state: %d\n", server->state);
if (server->state == CONN_VALID || server->state == CONN_RETRYING)
goto out;
smb_invalidate_inodes(server);
/*
* Some requests are meaningless after a retry, so we abort them.
* One example are all requests using 'fileid' since the files are
* closed on retry.
*/
head = server->xmitq.next;
while (head != &server->xmitq) {
req = list_entry(head, struct smb_request, rq_queue);
head = head->next;
if (req->rq_flags & SMB_REQ_NORETRY) {
VERBOSE("aborting request %p on xmitq\n", req);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
}
/*
* FIXME: test the code for retrying request we already sent
*/
head = server->recvq.next;
while (head != &server->recvq) {
req = list_entry(head, struct smb_request, rq_queue);
head = head->next;
#if 0
if (req->rq_flags & SMB_REQ_RETRY) {
/* must move the request to the xmitq */
VERBOSE("retrying request %p on recvq\n", req);
list_del(&req->rq_queue);
list_add(&req->rq_queue, &server->xmitq);
continue;
}
#endif
VERBOSE("aborting request %p on recvq\n", req);
/* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
smb_close_socket(server);
if (pid == 0) {
/* FIXME: this is fatal, umount? */
printk(KERN_ERR "smb_retry: no connection process\n");
server->state = CONN_RETRIED;
goto out;
}
/*
* Change state so that only one retry per server will be started.
*/
server->state = CONN_RETRYING;
/*
* Note: use the "priv" flag, as a user process may need to reconnect.
*/
result = kill_proc(pid, SIGUSR1, 1);
if (result) {
/* FIXME: this is most likely fatal, umount? */
printk(KERN_ERR "smb_retry: signal failed [%d]\n", result);
goto out;
}
VERBOSE("signalled pid %d\n", pid);
/* FIXME: The retried requests should perhaps get a "time boost". */
out:
return result;
}
/*
* Currently handles lockingX packets.
*/
static void smbiod_handle_request(struct smb_sb_info *server)
{
PARANOIA("smbiod got a request ... and we don't implement oplocks!\n");
server->rstate = SMB_RECV_DROP;
}
/*
* Do some IO for one server.
*/
static void smbiod_doio(struct smb_sb_info *server)
{
int result;
int maxwork = 7;
if (server->state != CONN_VALID)
goto out;
do {
result = smb_request_recv(server);
if (result < 0) {
server->state = CONN_INVALID;
smbiod_retry(server);
goto out; /* reconnecting is slow */
} else if (server->rstate == SMB_RECV_REQUEST)
smbiod_handle_request(server);
} while (result > 0 && maxwork-- > 0);
/*
* If there is more to read then we want to be sure to wake up again.
*/
if (server->state != CONN_VALID)
goto out;
if (smb_recv_available(server) > 0)
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
do {
result = smb_request_send_server(server);
if (result < 0) {
server->state = CONN_INVALID;
smbiod_retry(server);
goto out; /* reconnecting is slow */
}
} while (result > 0);
/*
* If the last request was not sent out we want to wake up again.
*/
if (!list_empty(&server->xmitq))
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
out:
}
/*
* smbiod kernel thread
*/
static int smbiod(void *unused)
{
daemonize();
spin_lock_irq(&current->sigmask_lock);
siginitsetinv(&current->blocked, sigmask(SIGKILL));
recalc_sigpending();
spin_unlock_irq(&current->sigmask_lock);
strcpy(current->comm, "smbiod");
VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid);
for (;;) {
struct smb_sb_info *server;
struct list_head *pos, *n;
/* FIXME: Use poll? */
wait_event_interruptible(smbiod_wait,
test_bit(SMBIOD_DATA_READY, &smbiod_flags));
if (signal_pending(current))
break;
clear_bit(SMBIOD_DATA_READY, &smbiod_flags);
/*
* We must hold the servers_lock while looking for servers
* to check or else we have a race with put_super.
*/
spin_lock(&servers_lock);
list_for_each_safe(pos, n, &smb_servers) {
server = list_entry(pos, struct smb_sb_info, entry);
VERBOSE("checking server %p\n", server);
smb_lock_server(server);
spin_unlock(&servers_lock);
smbiod_doio(server);
smb_unlock_server(server);
spin_lock(&servers_lock);
}
spin_unlock(&servers_lock);
}
VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid);
smbiod_pid = -1;
return 0;
}
This diff is collapsed.
......@@ -14,17 +14,42 @@
#include <linux/types.h>
#include <linux/smb.h>
/*
* Upper limit on the total number of active smb_request structs.
*/
#define MAX_REQUEST_HARD 256
enum smb_receive_state {
SMB_RECV_START, /* No data read, looking for length + sig */
SMB_RECV_HEADER, /* Reading the header data */
SMB_RECV_HCOMPLETE, /* Done with the header */
SMB_RECV_PARAM, /* Reading parameter words */
SMB_RECV_DATA, /* Reading data bytes */
SMB_RECV_END, /* End of request */
SMB_RECV_DROP, /* Dropping this SMB */
SMB_RECV_REQUEST, /* Received a request and not a reply */
};
/* structure access macros */
#define server_from_inode(inode) SMB_SB((inode)->i_sb)
#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb)
#define SB_of(server) ((server)->super_block)
struct smb_sb_info {
/* List of all smbfs superblocks */
struct list_head entry;
enum smb_conn_state state;
struct file * sock_file;
int conn_error;
enum smb_receive_state rstate;
atomic_t nr_requests;
struct list_head xmitq;
struct list_head recvq;
u16 mid;
struct smb_mount_data_kernel *mnt;
unsigned char *temp_buf;
/* Connections are counted. Each time a new socket arrives,
* generation is incremented.
......@@ -34,13 +59,15 @@ struct smb_sb_info {
struct smb_conn_opt opt;
struct semaphore sem;
wait_queue_head_t wait;
__u32 packet_size;
unsigned char * packet;
unsigned short rcls; /* The error codes we received */
unsigned short err;
unsigned char header[SMB_HEADER_LEN + 20*2 + 2];
u32 header_len;
u32 smb_len;
u32 smb_read;
/* We use our own data_ready callback, but need the original one */
void *data_ready;
......@@ -48,15 +75,16 @@ struct smb_sb_info {
struct nls_table *remote_nls;
struct nls_table *local_nls;
/* utf8 can make strings longer so we can't do in-place conversion.
This is a buffer for temporary stuff. We only need one so no need
to put it on the stack. This points to temp_buf space. */
char *name_buf;
struct smb_ops *ops;
struct super_block *super_block;
};
static inline int
smb_lock_server_interruptible(struct smb_sb_info *server)
{
return down_interruptible(&(server->sem));
}
static inline void
smb_lock_server(struct smb_sb_info *server)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment