Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 5 additions & 81 deletions fs/nfs/pagelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,83 +167,6 @@ nfs_page_group_lock_head(struct nfs_page *req)
return head;
}

/*
* nfs_unroll_locks - unlock all newly locked reqs and wait on @req
* @head: head request of page group, must be holding head lock
* @req: request that couldn't lock and needs to wait on the req bit lock
*
* This is a helper function for nfs_lock_and_join_requests
* returns 0 on success, < 0 on error.
*/
static void
nfs_unroll_locks(struct nfs_page *head, struct nfs_page *req)
{
struct nfs_page *tmp;

/* relinquish all the locks successfully grabbed this run */
for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) {
if (!kref_read(&tmp->wb_kref))
continue;
nfs_unlock_and_release_request(tmp);
}
}

/*
* nfs_page_group_lock_subreq - try to lock a subrequest
* @head: head request of page group
* @subreq: request to lock
*
* This is a helper function for nfs_lock_and_join_requests which
* must be called with the head request and page group both locked.
* On error, it returns with the page group unlocked.
*/
static int
nfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq)
{
int ret;

if (!kref_get_unless_zero(&subreq->wb_kref))
return 0;
while (!nfs_lock_request(subreq)) {
nfs_page_group_unlock(head);
ret = nfs_wait_on_request(subreq);
if (!ret)
ret = nfs_page_group_lock(head);
if (ret < 0) {
nfs_unroll_locks(head, subreq);
nfs_release_request(subreq);
return ret;
}
}
return 0;
}

/*
* nfs_page_group_lock_subrequests - try to lock the subrequests
* @head: head request of page group
*
* This is a helper function for nfs_lock_and_join_requests which
* must be called with the head request locked.
*/
int nfs_page_group_lock_subrequests(struct nfs_page *head)
{
struct nfs_page *subreq;
int ret;

ret = nfs_page_group_lock(head);
if (ret < 0)
return ret;
/* lock each request in the page group */
for (subreq = head->wb_this_page; subreq != head;
subreq = subreq->wb_this_page) {
ret = nfs_page_group_lock_subreq(head, subreq);
if (ret < 0)
return ret;
}
nfs_page_group_unlock(head);
return 0;
}

/*
* nfs_page_set_headlock - set the request PG_HEADLOCK
* @req: request that is to be locked
Expand Down Expand Up @@ -310,13 +233,14 @@ nfs_page_group_unlock(struct nfs_page *req)
nfs_page_clear_headlock(req);
}

/*
* nfs_page_group_sync_on_bit_locked
/**
* nfs_page_group_sync_on_bit_locked - Test if all requests have @bit set
* @req: request in page group
* @bit: PG_* bit that is used to sync page group
*
* must be called with page group lock held
*/
static bool
nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
bool nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit)
{
struct nfs_page *head = req->wb_head;
struct nfs_page *tmp;
Expand Down
140 changes: 91 additions & 49 deletions fs/nfs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,10 @@ nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode)
}
}

static int
nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
static void nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
{
int ret;

if (!test_bit(PG_REMOVE, &req->wb_flags))
return 0;
ret = nfs_page_group_lock(req);
if (ret)
return ret;
if (test_and_clear_bit(PG_REMOVE, &req->wb_flags))
nfs_page_set_inode_ref(req, inode);
nfs_page_group_unlock(req);
return 0;
}

static struct nfs_page *
Expand Down Expand Up @@ -239,36 +229,6 @@ static struct nfs_page *nfs_page_find_head_request(struct page *page)
return req;
}

static struct nfs_page *nfs_find_and_lock_page_request(struct page *page)
{
struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *req, *head;
int ret;

for (;;) {
req = nfs_page_find_head_request(page);
if (!req)
return req;
head = nfs_page_group_lock_head(req);
if (head != req)
nfs_release_request(req);
if (IS_ERR(head))
return head;
ret = nfs_cancel_remove_inode(head, inode);
if (ret < 0) {
nfs_unlock_and_release_request(head);
return ERR_PTR(ret);
}
/* Ensure that nobody removed the request before we locked it */
if (head == nfs_page_private_request(page))
break;
if (PageSwapCache(page))
break;
nfs_unlock_and_release_request(head);
}
return head;
}

/* Adjust the file length if we're writing beyond the end */
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
{
Expand Down Expand Up @@ -547,6 +507,57 @@ nfs_join_page_group(struct nfs_page *head, struct inode *inode)
nfs_destroy_unlinked_subrequests(destroy_list, head, inode);
}

/*
* nfs_unroll_locks - unlock all newly locked reqs and wait on @req
* @head: head request of page group, must be holding head lock
* @req: request that couldn't lock and needs to wait on the req bit lock
*
* This is a helper function for nfs_lock_and_join_requests
* returns 0 on success, < 0 on error.
*/
static void
nfs_unroll_locks(struct nfs_page *head, struct nfs_page *req)
{
struct nfs_page *tmp;

/* relinquish all the locks successfully grabbed this run */
for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) {
if (!kref_read(&tmp->wb_kref))
continue;
nfs_unlock_and_release_request(tmp);
}
}

/*
* nfs_page_group_lock_subreq - try to lock a subrequest
* @head: head request of page group
* @subreq: request to lock
*
* This is a helper function for nfs_lock_and_join_requests which
* must be called with the head request and page group both locked.
* On error, it returns with the page group unlocked.
*/
static int
nfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq)
{
int ret;

if (!kref_get_unless_zero(&subreq->wb_kref))
return 0;
while (!nfs_lock_request(subreq)) {
nfs_page_group_unlock(head);
ret = nfs_wait_on_request(subreq);
if (!ret)
ret = nfs_page_group_lock(head);
if (ret < 0) {
nfs_unroll_locks(head, subreq);
nfs_release_request(subreq);
return ret;
}
}
return 0;
}

/*
* nfs_lock_and_join_requests - join all subreqs to the head req
* @page: the page used to lookup the "page group" of nfs_page structures
Expand All @@ -566,28 +577,57 @@ static struct nfs_page *
nfs_lock_and_join_requests(struct page *page)
{
struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *head;
struct nfs_page *head, *subreq;
int ret;

/*
* A reference is taken only on the head request which acts as a
* reference to the whole page group - the group will not be destroyed
* until the head reference is released.
*/
head = nfs_find_and_lock_page_request(page);
retry:
head = nfs_page_find_head_request(page);
if (IS_ERR_OR_NULL(head))
return head;

/* lock each request in the page group */
ret = nfs_page_group_lock_subrequests(head);
if (ret < 0) {
while (!nfs_lock_request(head)) {
ret = nfs_wait_on_request(head);
if (ret < 0) {
nfs_release_request(head);
return ERR_PTR(ret);
}
}

ret = nfs_page_group_lock(head);
if (ret < 0)
goto out_unlock;

/* Ensure that nobody removed the request before we locked it */
if (head != nfs_page_private_request(page) && !PageSwapCache(page)) {
nfs_page_group_unlock(head);
nfs_unlock_and_release_request(head);
return ERR_PTR(ret);
goto retry;
}

nfs_join_page_group(head, inode);
nfs_cancel_remove_inode(head, inode);

/* lock each request in the page group */
for (subreq = head->wb_this_page;
subreq != head;
subreq = subreq->wb_this_page) {
ret = nfs_page_group_lock_subreq(head, subreq);
if (ret < 0)
goto out_unlock;
}

nfs_page_group_unlock(head);

nfs_join_page_group(head, inode);
return head;

out_unlock:
nfs_unlock_and_release_request(head);
return ERR_PTR(ret);
}

static void nfs_write_error(struct nfs_page *req, int error)
Expand Down Expand Up @@ -788,7 +828,8 @@ static void nfs_inode_remove_request(struct nfs_page *req)
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *head;

if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
nfs_page_group_lock(req);
if (nfs_page_group_sync_on_bit_locked(req, PG_REMOVE)) {
head = req->wb_head;

spin_lock(&mapping->private_lock);
Expand All @@ -799,6 +840,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
}
spin_unlock(&mapping->private_lock);
}
nfs_page_group_unlock(req);

if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
nfs_release_request(req);
Expand Down
2 changes: 1 addition & 1 deletion include/linux/nfs_page.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req);
extern void nfs_unlock_and_release_request(struct nfs_page *);
extern struct nfs_page *nfs_page_group_lock_head(struct nfs_page *req);
extern int nfs_page_group_lock_subrequests(struct nfs_page *head);
extern void nfs_join_page_group(struct nfs_page *head, struct inode *inode);
extern int nfs_page_group_lock(struct nfs_page *);
extern void nfs_page_group_unlock(struct nfs_page *);
extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int);
extern bool nfs_page_group_sync_on_bit_locked(struct nfs_page *, unsigned int);
extern int nfs_page_set_headlock(struct nfs_page *req);
extern void nfs_page_clear_headlock(struct nfs_page *req);
extern bool nfs_async_iocounter_wait(struct rpc_task *, struct nfs_lock_context *);
Expand Down
2 changes: 2 additions & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/idr.h>
#include <linux/leds.h>
#include <linux/rculist.h>
#include <linux/srcu.h>

#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_sync.h>
Expand Down Expand Up @@ -345,6 +346,7 @@ struct amp_assoc {

struct hci_dev {
struct list_head list;
struct srcu_struct srcu;
struct mutex lock;

char name[8];
Expand Down
Loading
Loading