7fe7f8487a
When processes are allowed to attempt to lock a non-contiguous range of nfs write requests, it is possible for generic_writepages to 'wrap round' the address space, and call writepage() on a request that is already locked by the same process. We avoid the deadlock by checking if the page index is contiguous with the list of nfs write requests that is already held in our nfs_pageio_descriptor prior to attempting to lock a new request. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
150 lines
3.8 KiB
C
150 lines
3.8 KiB
C
/*
|
|
* linux/include/linux/nfs_page.h
|
|
*
|
|
* Copyright (C) 2000 Trond Myklebust
|
|
*
|
|
* NFS page cache wrapper.
|
|
*/
|
|
|
|
#ifndef _LINUX_NFS_PAGE_H
|
|
#define _LINUX_NFS_PAGE_H
|
|
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/sunrpc/auth.h>
|
|
#include <linux/nfs_xdr.h>
|
|
|
|
#include <asm/atomic.h>
|
|
|
|
/*
|
|
* Valid flags for the radix tree
|
|
*/
|
|
#define NFS_PAGE_TAG_WRITEBACK 0
|
|
|
|
/*
|
|
* Valid flags for a dirty buffer
|
|
*/
|
|
#define PG_BUSY 0
|
|
#define PG_NEED_COMMIT 1
|
|
#define PG_NEED_RESCHED 2
|
|
#define PG_NEED_FLUSH 3
|
|
|
|
struct nfs_inode;
|
|
struct nfs_page {
|
|
struct list_head wb_list, /* Defines state of page: */
|
|
*wb_list_head; /* read/write/commit */
|
|
struct page *wb_page; /* page to read in/write out */
|
|
struct nfs_open_context *wb_context; /* File state context info */
|
|
atomic_t wb_complete; /* i/os we're waiting for */
|
|
pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */
|
|
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
|
|
wb_pgbase, /* Start of page data */
|
|
wb_bytes; /* Length of request */
|
|
atomic_t wb_count; /* reference count */
|
|
unsigned long wb_flags;
|
|
struct nfs_writeverf wb_verf; /* Commit cookie */
|
|
};
|
|
|
|
struct nfs_pageio_descriptor {
|
|
struct list_head pg_list;
|
|
unsigned long pg_bytes_written;
|
|
size_t pg_count;
|
|
size_t pg_bsize;
|
|
unsigned int pg_base;
|
|
|
|
struct inode *pg_inode;
|
|
int (*pg_doio)(struct inode *, struct list_head *, unsigned int, size_t, int);
|
|
int pg_ioflags;
|
|
int pg_error;
|
|
};
|
|
|
|
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
|
|
|
|
extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx,
|
|
struct inode *inode,
|
|
struct page *page,
|
|
unsigned int offset,
|
|
unsigned int count);
|
|
extern void nfs_clear_request(struct nfs_page *req);
|
|
extern void nfs_release_request(struct nfs_page *req);
|
|
|
|
|
|
extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst,
|
|
pgoff_t idx_start, unsigned int npages);
|
|
extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
|
|
struct inode *inode,
|
|
int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
|
|
size_t bsize,
|
|
int how);
|
|
extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *,
|
|
struct nfs_page *);
|
|
extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc);
|
|
extern void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *, pgoff_t);
|
|
extern int nfs_wait_on_request(struct nfs_page *);
|
|
extern void nfs_unlock_request(struct nfs_page *req);
|
|
extern int nfs_set_page_writeback_locked(struct nfs_page *req);
|
|
extern void nfs_clear_page_writeback(struct nfs_page *req);
|
|
|
|
|
|
/*
|
|
* Lock the page of an asynchronous request without incrementing the wb_count
|
|
*/
|
|
static inline int
|
|
nfs_lock_request_dontget(struct nfs_page *req)
|
|
{
|
|
return !test_and_set_bit(PG_BUSY, &req->wb_flags);
|
|
}
|
|
|
|
/*
|
|
* Lock the page of an asynchronous request
|
|
*/
|
|
static inline int
|
|
nfs_lock_request(struct nfs_page *req)
|
|
{
|
|
if (test_and_set_bit(PG_BUSY, &req->wb_flags))
|
|
return 0;
|
|
atomic_inc(&req->wb_count);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* nfs_list_add_request - Insert a request into a list
|
|
* @req: request
|
|
* @head: head of list into which to insert the request.
|
|
*/
|
|
static inline void
|
|
nfs_list_add_request(struct nfs_page *req, struct list_head *head)
|
|
{
|
|
list_add_tail(&req->wb_list, head);
|
|
req->wb_list_head = head;
|
|
}
|
|
|
|
|
|
/**
|
|
* nfs_list_remove_request - Remove a request from its wb_list
|
|
* @req: request
|
|
*/
|
|
static inline void
|
|
nfs_list_remove_request(struct nfs_page *req)
|
|
{
|
|
if (list_empty(&req->wb_list))
|
|
return;
|
|
list_del_init(&req->wb_list);
|
|
req->wb_list_head = NULL;
|
|
}
|
|
|
|
static inline struct nfs_page *
|
|
nfs_list_entry(struct list_head *head)
|
|
{
|
|
return list_entry(head, struct nfs_page, wb_list);
|
|
}
|
|
|
|
static inline
|
|
loff_t req_offset(struct nfs_page *req)
|
|
{
|
|
return (((loff_t)req->wb_index) << PAGE_CACHE_SHIFT) + req->wb_offset;
|
|
}
|
|
|
|
#endif /* _LINUX_NFS_PAGE_H */
|