1/* 2 * linux/fs/lockd/clntlock.c 3 * 4 * Lock handling for the client side NLM implementation 5 * 6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 7 */ 8 9#define __KERNEL_SYSCALLS__ 10 11#include <linux/module.h> 12#include <linux/types.h> 13#include <linux/sched.h> 14#include <linux/nfs_fs.h> 15#include <linux/unistd.h> 16#include <linux/sunrpc/clnt.h> 17#include <linux/sunrpc/svc.h> 18#include <linux/lockd/lockd.h> 19#include <linux/smp_lock.h> 20 21#define NLMDBG_FACILITY NLMDBG_CLIENT 22 23/* 24 * Local function prototypes 25 */ 26static int reclaimer(void *ptr); 27 28/* 29 * The following functions handle blocking and granting from the 30 * client perspective. 31 */ 32 33/* 34 * This is the representation of a blocked client lock. 35 */ 36struct nlm_wait { 37 struct nlm_wait * b_next; /* linked list */ 38 wait_queue_head_t b_wait; /* where to wait on */ 39 struct nlm_host * b_host; 40 struct file_lock * b_lock; /* local file lock */ 41 unsigned short b_reclaim; /* got to reclaim lock */ 42 u32 b_status; /* grant callback status */ 43}; 44 45static struct nlm_wait * nlm_blocked; 46 47/* 48 * Block on a lock 49 */ 50int 51nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp) 52{ 53 struct nlm_wait block, **head; 54 int err; 55 u32 pstate; 56 57 block.b_host = host; 58 block.b_lock = fl; 59 init_waitqueue_head(&block.b_wait); 60 block.b_status = NLM_LCK_BLOCKED; 61 block.b_next = nlm_blocked; 62 nlm_blocked = █ 63 64 /* Remember pseudo nsm state */ 65 pstate = host->h_state; 66 67 /* Go to sleep waiting for GRANT callback. Some servers seem 68 * to lose callbacks, however, so we're going to poll from 69 * time to time just to make sure. 70 * 71 * For now, the retry frequency is pretty high; normally 72 * a 1 minute timeout would do. See the comment before 73 * nlmclnt_lock for an explanation. 74 */ 75 sleep_on_timeout(&block.b_wait, 30*HZ); 76 77 for (head = &nlm_blocked; *head; head = &(*head)->b_next) { 78 if (*head == &block) { 79 *head = block.b_next; 80 break; 81 } 82 } 83 84 if (!signalled()) { 85 *statp = block.b_status; 86 return 0; 87 } 88 89 /* Okay, we were interrupted. Cancel the pending request 90 * unless the server has rebooted. 91 */ 92 if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0) 93 printk(KERN_NOTICE 94 "lockd: CANCEL call failed (errno %d)\n", -err); 95 96 return -ERESTARTSYS; 97} 98 99/* 100 * The server lockd has called us back to tell us the lock was granted 101 */ 102u32 103nlmclnt_grant(struct nlm_lock *lock) 104{ 105 struct nlm_wait *block; 106 107 /* 108 * Look up blocked request based on arguments. 109 * Warning: must not use cookie to match it! 110 */ 111 for (block = nlm_blocked; block; block = block->b_next) { 112 if (nlm_compare_locks(block->b_lock, &lock->fl)) 113 break; 114 } 115 116 /* Ooops, no blocked request found. */ 117 if (block == NULL) 118 return nlm_lck_denied; 119 120 /* Alright, we found the lock. Set the return status and 121 * wake up the caller. 122 */ 123 block->b_status = NLM_LCK_GRANTED; 124 wake_up(&block->b_wait); 125 126 return nlm_granted; 127} 128 129/* 130 * The following procedures deal with the recovery of locks after a 131 * server crash. 132 */ 133 134static 135void nlmclnt_mark_reclaim(struct nlm_host *host) 136{ 137 struct file_lock *fl; 138 struct inode *inode; 139 struct list_head *tmp; 140 141 list_for_each(tmp, &file_lock_list) { 142 fl = list_entry(tmp, struct file_lock, fl_link); 143 144 inode = fl->fl_file->f_dentry->d_inode; 145 if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) 146 continue; 147 if (fl->fl_u.nfs_fl.host != host) 148 continue; 149 if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) 150 continue; 151 fl->fl_u.nfs_fl.flags |= NFS_LCK_RECLAIM; 152 } 153} 154 155/* 156 * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, 157 * that we mark locks for reclaiming, and that we bump the pseudo NSM state. 158 */ 159static inline 160void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) 161{ 162 host->h_monitored = 0; 163 host->h_nsmstate = newstate; 164 host->h_state++; 165 host->h_nextrebind = 0; 166 nlm_rebind_host(host); 167 nlmclnt_mark_reclaim(host); 168 dprintk("NLM: reclaiming locks for host %s", host->h_name); 169} 170 171/* 172 * Reclaim all locks on server host. We do this by spawning a separate 173 * reclaimer thread. 174 */ 175void 176nlmclnt_recovery(struct nlm_host *host, u32 newstate) 177{ 178 if (host->h_reclaiming++) { 179 if (host->h_nsmstate == newstate) 180 return; 181 nlmclnt_prepare_reclaim(host, newstate); 182 } else { 183 nlmclnt_prepare_reclaim(host, newstate); 184 nlm_get_host(host); 185 MOD_INC_USE_COUNT; 186 kernel_thread(reclaimer, host, CLONE_SIGNAL); 187 } 188} 189 190static int 191reclaimer(void *ptr) 192{ 193 struct nlm_host *host = (struct nlm_host *) ptr; 194 struct nlm_wait *block; 195 struct list_head *tmp; 196 struct file_lock *fl; 197 struct inode *inode; 198 199 daemonize(); 200 reparent_to_init(); 201 snprintf(current->comm, sizeof(current->comm), 202 "%s-reclaim", 203 host->h_name); 204 205 /* This one ensures that our parent doesn't terminate while the 206 * reclaim is in progress */ 207 lock_kernel(); 208 lockd_up(); 209 210 /* First, reclaim all locks that have been marked. */ 211restart: 212 list_for_each(tmp, &file_lock_list) { 213 fl = list_entry(tmp, struct file_lock, fl_link); 214 215 inode = fl->fl_file->f_dentry->d_inode; 216 if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) 217 continue; 218 if (fl->fl_u.nfs_fl.host != host) 219 continue; 220 if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM)) 221 continue; 222 223 fl->fl_u.nfs_fl.flags &= ~NFS_LCK_RECLAIM; 224 nlmclnt_reclaim(host, fl); 225 if (signalled()) 226 break; 227 goto restart; 228 } 229 230 host->h_reclaiming = 0; 231 wake_up(&host->h_gracewait); 232 233 /* Now, wake up all processes that sleep on a blocked lock */ 234 for (block = nlm_blocked; block; block = block->b_next) { 235 if (block->b_host == host) { 236 block->b_status = NLM_LCK_DENIED_GRACE_PERIOD; 237 wake_up(&block->b_wait); 238 } 239 } 240 241 /* Release host handle after use */ 242 nlm_release_host(host); 243 lockd_down(); 244 unlock_kernel(); 245 MOD_DEC_USE_COUNT; 246 247 return 0; 248} 249