Commit 46628744 authored by Linus Torvalds's avatar Linus Torvalds

Merge http://nfsclient.bkbits.net/linux-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 6ffa96a3 c5230112
......@@ -8,6 +8,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \
proc.o read.o symlink.o unlink.o write.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
idmap.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-objs := $(nfs-y)
......@@ -35,6 +35,7 @@
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
static int nfs_opendir(struct inode *, struct file *);
static int nfs_readdir(struct file *, void *, filldir_t);
static struct dentry *nfs_lookup(struct inode *, struct dentry *);
static int nfs_cached_lookup(struct inode *, struct dentry *,
......@@ -52,7 +53,7 @@ static int nfs_rename(struct inode *, struct dentry *,
struct file_operations nfs_dir_operations = {
.read = generic_read_dir,
.readdir = nfs_readdir,
.open = nfs_open,
.open = nfs_opendir,
.release = nfs_release,
};
......@@ -71,6 +72,26 @@ struct inode_operations nfs_dir_inode_operations = {
.setattr = nfs_setattr,
};
/*
* Open file
*/
static int
nfs_opendir(struct inode *inode, struct file *filp)
{
struct nfs_server *server = NFS_SERVER(inode);
int res = 0;
lock_kernel();
/* Do cto revalidation */
if (server->flags & NFS_MOUNT_NOCTO)
res = __nfs_revalidate_inode(server, inode);
/* Call generic open code in order to cache credentials */
if (!res)
res = nfs_open(inode, filp);
unlock_kernel();
return res;
}
typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
typedef struct {
struct file *file;
......
......@@ -34,6 +34,7 @@
#define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct file *);
static int nfs_file_mmap(struct file *, struct vm_area_struct *);
static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t);
......@@ -48,7 +49,7 @@ struct file_operations nfs_file_operations = {
.aio_read = nfs_file_read,
.aio_write = nfs_file_write,
.mmap = nfs_file_mmap,
.open = nfs_open,
.open = nfs_file_open,
.flush = nfs_file_flush,
.release = nfs_release,
.fsync = nfs_fsync,
......@@ -67,6 +68,30 @@ struct inode_operations nfs_file_inode_operations = {
# define IS_SWAPFILE(inode) (0)
#endif
/*
* Open file
*/
static int
nfs_file_open(struct inode *inode, struct file *filp)
{
struct nfs_server *server = NFS_SERVER(inode);
int (*open)(struct inode *, struct file *);
int res = 0;
lock_kernel();
/* Do NFSv4 open() call */
if ((open = server->rpc_ops->file_open) != NULL)
res = open(inode, filp);
/* Do cto revalidation */
else if (server->flags & NFS_MOUNT_NOCTO)
res = __nfs_revalidate_inode(server, inode);
/* Call generic open code in order to cache credentials */
if (!res)
res = nfs_open(inode, filp);
unlock_kernel();
return res;
}
/*
* Flush all dirty pages, and check for write errors.
*
......
This diff is collapsed.
......@@ -33,6 +33,7 @@
#include <linux/smp_lock.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/nfs_idmap.h>
#include <linux/vfs.h>
#include <asm/system.h>
......@@ -140,6 +141,10 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
/* Clean up the V4 state */
nfs4_put_shareowner(inode, nfsi->wo_owner);
nfs4_put_shareowner(inode, nfsi->ro_owner);
nfs4_put_shareowner(inode, nfsi->rw_owner);
}
void
......@@ -148,6 +153,11 @@ nfs_put_super(struct super_block *sb)
struct nfs_server *server = NFS_SB(sb);
struct rpc_clnt *rpc;
#ifdef CONFIG_NFS_V4
if (server->idmap != NULL)
nfs_idmap_delete(server);
#endif /* CONFIG_NFS_V4 */
if ((rpc = server->client) != NULL)
rpc_shutdown_client(rpc);
......@@ -156,6 +166,7 @@ nfs_put_super(struct super_block *sb)
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
kfree(server->hostname);
}
......@@ -855,23 +866,13 @@ int nfs_open(struct inode *inode, struct file *filp)
{
struct rpc_auth *auth;
struct rpc_cred *cred;
int err = 0;
lock_kernel();
/* Ensure that we revalidate the data cache */
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO) {
err = __nfs_revalidate_inode(NFS_SERVER(inode),inode);
if (err)
goto out;
}
auth = NFS_CLIENT(inode)->cl_auth;
cred = rpcauth_lookupcred(auth, 0);
filp->private_data = cred;
if (filp->f_mode & FMODE_WRITE)
nfs_set_mmcred(inode, cred);
out:
unlock_kernel();
return err;
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
......@@ -1368,11 +1369,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
if (create_nfsv4_state(server, data))
goto out_shutdown;
if ((server->idmap = nfs_idmap_new(server)) == NULL)
printk(KERN_WARNING "NFS: couldn't start IDmap\n");
err = nfs_sb_init(sb);
if (err == 0)
return 0;
rpciod_down();
destroy_nfsv4_state(server);
if (server->idmap != NULL)
nfs_idmap_delete(server);
out_shutdown:
rpc_shutdown_client(server->client);
out_fail:
......@@ -1502,9 +1508,18 @@ static struct file_system_type nfs4_fs_type = {
.kill_sb = nfs_kill_super,
.fs_flags = FS_ODD_RENAME,
};
#define nfs4_zero_state(nfsi) \
do { \
(nfsi)->wo_owner = NULL; \
(nfsi)->ro_owner = NULL; \
(nfsi)->rw_owner = NULL; \
} while(0)
#define register_nfs4fs() register_filesystem(&nfs4_fs_type)
#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
#else
#define nfs4_zero_state(nfsi) \
do { } while (0)
#define register_nfs4fs() (0)
#define unregister_nfs4fs()
#endif
......@@ -1526,6 +1541,7 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
return NULL;
nfsi->flags = 0;
nfsi->mm_cred = NULL;
nfs4_zero_state(nfsi);
return &nfsi->vfs_inode;
}
......
This diff is collapsed.
......@@ -42,6 +42,16 @@
#include <linux/slab.h>
#include <linux/nfs_fs.h>
/* This protects most of the client-side state. */
static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED;
nfs4_stateid zero_stateid =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nfs4_stateid one_stateid =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/*
* nfs4_get_client(): returns an empty client structure
* nfs4_put_client(): drops reference to client structure
......@@ -54,11 +64,8 @@ nfs4_get_client(void)
{
struct nfs4_client *clp;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) {
atomic_set(&clp->cl_count, 1);
clp->cl_clientid = 0;
INIT_LIST_HEAD(&clp->cl_lockowners);
}
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
memset(clp, 0, sizeof(nfs4_verifier));
return clp;
}
......@@ -66,12 +73,155 @@ void
nfs4_put_client(struct nfs4_client *clp)
{
BUG_ON(!clp);
BUG_ON(!atomic_read(&clp->cl_count));
if (atomic_dec_and_test(&clp->cl_count)) {
BUG_ON(!list_empty(&clp->cl_lockowners));
kfree(clp);
}
static inline u32
nfs4_alloc_lockowner_id(struct nfs4_client *clp)
{
u32 res;
spin_lock(&state_spinlock);
res = clp->cl_lockowner_id ++;
spin_unlock(&state_spinlock);
return res;
}
/*
* nfs4_get_shareowner(): this is called on the OPEN or CREATE path to
* obtain a new shareowner.
*
* There are three shareowners (open_owner4 in rfc3010) per inode,
* one for each possible combination of share lock access. Since
* Linux does not support the deny access type, there are
* three (not 9) referenced by the nfs_inode:
*
* O_WRONLY: inode->wo_owner
* O_RDONLY: inode->ro_owner
* O_RDWR: inode->rw_owner
*
* We create a new shareowner the first time a file is OPENed with
* one of the above shares. All other OPENs with a similar
* share use the single stateid associated with the inode.
*
*/
struct nfs4_shareowner *
nfs4_get_shareowner(struct inode *dir)
{
struct nfs4_client *clp;
struct nfs4_shareowner *sp;
sp = kmalloc(sizeof(*sp),GFP_KERNEL);
if (!sp)
return NULL;
clp = (NFS_SB(dir->i_sb))->nfs4_state;
BUG_ON(!clp);
init_MUTEX(&sp->so_sema);
sp->so_seqid = 0; /* arbitrary */
memset(sp->so_stateid, 0, sizeof(nfs4_stateid));
sp->so_id = nfs4_alloc_lockowner_id(clp);
return sp;
}
/*
* Called for each non-null inode shareowner in nfs_clear_inode,
* or if nfs4_do_open fails.
*/
void
nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp)
{
if (!sp)
return;
if (sp->so_flags & O_ACCMODE)
nfs4_do_close(inode, sp);
kfree(sp);
}
/*
* Called with sp->so_sema held.
*
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
void
nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp)
{
if (status == NFS_OK || seqid_mutating_err(status))
sp->so_seqid++;
}
/*
* Called by nfs4_proc_open to set the appropriate stateid
*/
int
nfs4_set_inode_share(struct inode * inode, struct nfs4_shareowner *sp, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if (!nfsi->ro_owner) {
nfsi->ro_owner = sp;
return 0;
}
break;
case O_WRONLY:
if (!nfsi->wo_owner) {
nfsi->wo_owner = sp;
return 0;
}
break;
case O_RDWR:
if (!nfsi->rw_owner) {
nfsi->rw_owner = sp;
return 0;
}
}
return -EBUSY;
}
/*
* Boolean test to determine if an OPEN call goes on the wire.
*
* Called by nfs4_proc_open.
*/
int
nfs4_test_shareowner(struct inode *inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if(nfsi->ro_owner)
return 0;
break;
case O_WRONLY:
if(nfsi->wo_owner)
return 0;
break;
case O_RDWR:
if(nfsi->rw_owner)
return 0;
}
return 1;
}
struct nfs4_shareowner *
nfs4_get_inode_share(struct inode * inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
return nfsi->ro_owner;
case O_WRONLY:
return nfsi->wo_owner;
case O_RDWR:
return nfsi->rw_owner;
}
/* Duh gcc warning if we don't... */
return NULL;
}
/*
......
This diff is collapsed.
......@@ -206,6 +206,10 @@ enum {
NFSPROC4_CLNT_READ,
NFSPROC4_CLNT_WRITE,
NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM,
NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR,
};
#endif
......
......@@ -155,6 +155,13 @@ struct nfs_inode {
wait_queue_head_t nfs_i_wait;
#ifdef CONFIG_NFS_V4
/* NFSv4 state */
struct nfs4_shareowner *ro_owner;
struct nfs4_shareowner *wo_owner;
struct nfs4_shareowner *rw_owner;
#endif /* CONFIG_NFS_V4*/
struct inode vfs_inode;
};
......@@ -435,28 +442,75 @@ extern void * nfs_root_data(void);
#define NFS_JUKEBOX_RETRY_TIME (5 * HZ)
#ifdef CONFIG_NFS_V4
/*
* In a seqid-mutating op, this macro controls which error return
* values trigger incrementation of the seqid.
*
* from rfc 3010:
* The client MUST monotonically increment the sequence number for the
* CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE
* operations. This is true even in the event that the previous
* operation that used the sequence number received an error. The only
* exception to this rule is if the previous operation received one of
* the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID,
* NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR,
* NFSERR_RESOURCE, NFSERR_NOFILEHANDLE.
*
*/
#define seqid_mutating_err(err) \
(((err) != NFSERR_STALE_CLIENTID) && \
((err) != NFSERR_STALE_STATEID) && \
((err) != NFSERR_BAD_STATEID) && \
((err) != NFSERR_BAD_SEQID) && \
((err) != NFSERR_BAD_XDR) && \
((err) != NFSERR_RESOURCE) && \
((err) != NFSERR_NOFILEHANDLE))
struct nfs4_client {
atomic_t cl_count; /* refcount */
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm;
/*
* Starts a list of lockowners, linked through lo_list.
*/
struct list_head cl_lockowners; /* protected by state_spinlock */
u32 cl_lockowner_id;
};
/*
* The ->so_sema is held during all shareowner seqid-mutating operations:
* OPEN, OPEN_DOWNGRADE, and CLOSE.
* Its purpose is to properly serialize so_seqid, as mandated by
* the protocol.
*/
struct nfs4_shareowner {
u32 so_id; /* 32-bit identifier, unique */
struct semaphore so_sema;
u32 so_seqid; /* protected by so_sema */
nfs4_stateid so_stateid; /* protected by so_sema */
unsigned int so_flags; /* protected by so_sema */
};
/* nfs4proc.c */
extern int nfs4_proc_renew(struct nfs_server *server);
extern int nfs4_do_close(struct inode *inode, struct nfs4_shareowner *sp);
/* nfs4renewd.c */
extern int nfs4_init_renewd(struct nfs_server *server);
#endif /* CONFIG_NFS_V4 */
#ifdef CONFIG_NFS_V4
/* nfs4state.c */
extern struct nfs4_client *nfs4_get_client(void);
extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_shareowner * nfs4_get_shareowner(struct inode *inode);
void nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp);
extern int nfs4_set_inode_share(struct inode * inode,
struct nfs4_shareowner *sp, unsigned int flags);
extern void nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp);
extern int nfs4_test_shareowner(struct inode *inode, unsigned int open_flags);
struct nfs4_shareowner * nfs4_get_inode_share(struct inode * inode, unsigned int open_flags);
struct nfs4_mount_data;
static inline int
......@@ -481,6 +535,7 @@ destroy_nfsv4_state(struct nfs_server *server)
#else
#define create_nfsv4_state(server, data) 0
#define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_shareowner(inode, owner) do { } while (0)
#endif
#endif /* __KERNEL__ */
......
......@@ -36,6 +36,7 @@ struct nfs_server {
struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */
unsigned long lease_time; /* in jiffies */
unsigned long last_renewal; /* in jiffies */
void *idmap;
#endif
};
......
/*
* include/linux/nfs_idmap.h
*
* UID and GID to name mapping for clients.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NFS_IDMAP_H
#define NFS_IDMAP_H
/* XXX from bits/utmp.h */
#define IDMAP_NAMESZ 128
#define IDMAP_TYPE_USER 0
#define IDMAP_TYPE_GROUP 1
#define IDMAP_CONV_IDTONAME 0
#define IDMAP_CONV_NAMETOID 1
#define IDMAP_STATUS_INVALIDMSG 0x01
#define IDMAP_STATUS_AGAIN 0x02
#define IDMAP_STATUS_LOOKUPFAIL 0x04
#define IDMAP_STATUS_SUCCESS 0x08
struct idmap_msg {
u_int8_t im_type;
u_int8_t im_conv;
char im_name[IDMAP_NAMESZ];
u_int32_t im_id;
u_int8_t im_status;
};
#ifdef __KERNEL__
void *nfs_idmap_new(struct nfs_server *);
void nfs_idmap_delete(struct nfs_server *);
int nfs_idmap_id(struct nfs_server *, u_int8_t, char *, u_int, uid_t *);
int nfs_idmap_name(struct nfs_server *, u_int8_t, uid_t, char *, u_int *);
#endif /* __KERNEL__ */
#endif /* NFS_IDMAP_H */
......@@ -87,6 +87,67 @@ struct nfs_pathconf {
__u32 max_namelen; /* max name length */
};
/*
* Arguments to the open call.
*/
struct nfs_openargs {
struct nfs_fh * fh;
__u32 seqid;
__u32 share_access;
__u64 clientid;
__u32 id;
__u32 opentype;
__u32 createmode;
union {
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
} u;
struct qstr * name;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
struct nfs_server * server; /* Needed for ID mapping */
};
struct nfs_openres {
__u32 status;
nfs4_stateid stateid;
struct nfs_fh fh;
struct nfs4_change_info * cinfo;
__u32 rflags;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
struct nfs_server * server;
};
/*
* Arguments to the open_confirm call.
*/
struct nfs_open_confirmargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
struct nfs_open_confirmres {
__u32 status;
nfs4_stateid stateid;
};
/*
* Arguments to the close call.
*/
struct nfs_closeargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
struct nfs_closeres {
__u32 status;
nfs4_stateid stateid;
};
/*
* Arguments to the read call.
*/
......@@ -98,6 +159,7 @@ struct nfs_pathconf {
struct nfs_readargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u64 offset;
__u32 count;
unsigned int pgbase;
......@@ -120,6 +182,7 @@ struct nfs_readres {
struct nfs_writeargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u64 offset;
__u32 count;
enum nfs3_stable_how stable;
......@@ -182,6 +245,19 @@ struct nfs_renameargs {
unsigned int tolen;
};
struct nfs_setattrargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
struct iattr * iap;
struct nfs4_getattr * attr;
struct nfs_server * server; /* Needed for name mapping */
};
struct nfs_setattrres {
struct nfs4_getattr * attr;
struct nfs_server * server;
};
struct nfs_linkargs {
struct nfs_fh * fromfh;
struct nfs_fh * tofh;
......@@ -597,6 +673,7 @@ struct nfs_rpc_ops {
void (*read_setup) (struct nfs_read_data *, unsigned int count);
void (*write_setup) (struct nfs_write_data *, unsigned int count, int how);
void (*commit_setup) (struct nfs_write_data *, u64 start, u32 len, int how);
int (*file_open) (struct inode *, struct file *);
};
/*
......
......@@ -625,7 +625,8 @@ skb_read_bits(skb_reader_t *desc, void *to, size_t len)
{
if (len > desc->count)
len = desc->count;
skb_copy_bits(desc->skb, desc->offset, to, len);
if (skb_copy_bits(desc->skb, desc->offset, to, len))
return 0;
desc->count -= len;
desc->offset += len;
return len;
......@@ -669,11 +670,15 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0);
desc.csum = csum_block_add(desc.csum, csum2, desc.offset);
}
if (desc.count)
return -1;
if ((unsigned short)csum_fold(desc.csum))
return -1;
return 0;
no_checksum:
xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits);
if (desc.count)
return -1;
return 0;
}
......@@ -750,7 +755,8 @@ tcp_copy_data(skb_reader_t *desc, void *p, size_t len)
{
if (len > desc->count)
len = desc->count;
skb_copy_bits(desc->skb, desc->offset, p, len);
if (skb_copy_bits(desc->skb, desc->offset, p, len))
return 0;
desc->offset += len;
desc->count -= len;
return len;
......
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