lockd: Add refcounting to struct nlm_block
[safe/jmp/linux-2.6] / fs / lockd / svclock.c
1 /*
2  * linux/fs/lockd/svclock.c
3  *
4  * Handling of server-side locks, mostly of the blocked variety.
5  * This is the ugliest part of lockd because we tread on very thin ice.
6  * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
7  * IMNSHO introducing the grant callback into the NLM protocol was one
8  * of the worst ideas Sun ever had. Except maybe for the idea of doing
9  * NFS file locking at all.
10  *
11  * I'm trying hard to avoid race conditions by protecting most accesses
12  * to a file's list of blocked locks through a semaphore. The global
13  * list of blocked locks is not protected in this fashion however.
14  * Therefore, some functions (such as the RPC callback for the async grant
15  * call) move blocked locks towards the head of the list *while some other
16  * process might be traversing it*. This should not be a problem in
17  * practice, because this will only cause functions traversing the list
18  * to visit some blocks twice.
19  *
20  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
21  */
22
23 #include <linux/config.h>
24 #include <linux/types.h>
25 #include <linux/errno.h>
26 #include <linux/kernel.h>
27 #include <linux/sched.h>
28 #include <linux/smp_lock.h>
29 #include <linux/sunrpc/clnt.h>
30 #include <linux/sunrpc/svc.h>
31 #include <linux/lockd/nlm.h>
32 #include <linux/lockd/lockd.h>
33
34 #define NLMDBG_FACILITY         NLMDBG_SVCLOCK
35
36 #ifdef CONFIG_LOCKD_V4
37 #define nlm_deadlock    nlm4_deadlock
38 #else
39 #define nlm_deadlock    nlm_lck_denied
40 #endif
41
42 static void nlmsvc_release_block(struct nlm_block *block);
43 static void     nlmsvc_insert_block(struct nlm_block *block, unsigned long);
44 static int      nlmsvc_remove_block(struct nlm_block *block);
45
46 static const struct rpc_call_ops nlmsvc_grant_ops;
47
48 /*
49  * The list of blocked locks to retry
50  */
51 static struct nlm_block *       nlm_blocked;
52
53 /*
54  * Insert a blocked lock into the global list
55  */
56 static void
57 nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
58 {
59         struct nlm_block **bp, *b;
60
61         dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
62         kref_get(&block->b_count);
63         if (block->b_queued)
64                 nlmsvc_remove_block(block);
65         bp = &nlm_blocked;
66         if (when != NLM_NEVER) {
67                 if ((when += jiffies) == NLM_NEVER)
68                         when ++;
69                 while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER)
70                         bp = &b->b_next;
71         } else
72                 while ((b = *bp) != 0)
73                         bp = &b->b_next;
74
75         block->b_queued = 1;
76         block->b_when = when;
77         block->b_next = b;
78         *bp = block;
79 }
80
81 /*
82  * Remove a block from the global list
83  */
84 static int
85 nlmsvc_remove_block(struct nlm_block *block)
86 {
87         struct nlm_block **bp, *b;
88
89         if (!block->b_queued)
90                 return 1;
91         for (bp = &nlm_blocked; (b = *bp) != 0; bp = &b->b_next) {
92                 if (b == block) {
93                         *bp = block->b_next;
94                         block->b_queued = 0;
95                         nlmsvc_release_block(block);
96                         return 1;
97                 }
98         }
99
100         return 0;
101 }
102
103 /*
104  * Find a block for a given lock and optionally remove it from
105  * the list.
106  */
107 static struct nlm_block *
108 nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove)
109 {
110         struct nlm_block        **head, *block;
111         struct file_lock        *fl;
112
113         dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n",
114                                 file, lock->fl.fl_pid,
115                                 (long long)lock->fl.fl_start,
116                                 (long long)lock->fl.fl_end, lock->fl.fl_type);
117         for (head = &nlm_blocked; (block = *head) != 0; head = &block->b_next) {
118                 fl = &block->b_call.a_args.lock.fl;
119                 dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
120                                 block->b_file, fl->fl_pid,
121                                 (long long)fl->fl_start,
122                                 (long long)fl->fl_end, fl->fl_type,
123                                 nlmdbg_cookie2a(&block->b_call.a_args.cookie));
124                 if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
125                         if (remove) {
126                                 *head = block->b_next;
127                                 block->b_queued = 0;
128                         }
129                         kref_get(&block->b_count);
130                         return block;
131                 }
132         }
133
134         return NULL;
135 }
136
137 static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
138 {
139         if(a->len != b->len)
140                 return 0;
141         if(memcmp(a->data,b->data,a->len))
142                 return 0;
143         return 1;
144 }
145
146 /*
147  * Find a block with a given NLM cookie.
148  */
149 static inline struct nlm_block *
150 nlmsvc_find_block(struct nlm_cookie *cookie,  struct sockaddr_in *sin)
151 {
152         struct nlm_block *block;
153
154         for (block = nlm_blocked; block; block = block->b_next) {
155                 dprintk("cookie: head of blocked queue %p, block %p\n", 
156                         nlm_blocked, block);
157                 if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)
158                                 && nlm_cmp_addr(sin, &block->b_host->h_addr))
159                         break;
160         }
161
162         if (block != NULL)
163                 kref_get(&block->b_count);
164         return block;
165 }
166
167 /*
168  * Create a block and initialize it.
169  *
170  * Note: we explicitly set the cookie of the grant reply to that of
171  * the blocked lock request. The spec explicitly mentions that the client
172  * should _not_ rely on the callback containing the same cookie as the
173  * request, but (as I found out later) that's because some implementations
174  * do just this. Never mind the standards comittees, they support our
175  * logging industries.
176  */
177 static inline struct nlm_block *
178 nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
179                                 struct nlm_lock *lock, struct nlm_cookie *cookie)
180 {
181         struct nlm_block        *block;
182         struct nlm_host         *host;
183         struct nlm_rqst         *call;
184
185         /* Create host handle for callback */
186         host = nlmclnt_lookup_host(&rqstp->rq_addr,
187                                 rqstp->rq_prot, rqstp->rq_vers);
188         if (host == NULL)
189                 return NULL;
190
191         /* Allocate memory for block, and initialize arguments */
192         if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
193                 goto failed;
194         memset(block, 0, sizeof(*block));
195         locks_init_lock(&block->b_call.a_args.lock.fl);
196         locks_init_lock(&block->b_call.a_res.lock.fl);
197         kref_init(&block->b_count);
198
199         if (!nlmclnt_setgrantargs(&block->b_call, lock))
200                 goto failed_free;
201
202         /* Set notifier function for VFS, and init args */
203         block->b_call.a_args.lock.fl.fl_flags |= FL_SLEEP;
204         block->b_call.a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
205         block->b_call.a_args.cookie = *cookie;  /* see above */
206
207         dprintk("lockd: created block %p...\n", block);
208
209         /* Create and initialize the block */
210         block->b_daemon = rqstp->rq_server;
211         block->b_host   = host;
212         block->b_file   = file;
213
214         /* Add to file's list of blocks */
215         block->b_fnext  = file->f_blocks;
216         file->f_blocks  = block;
217
218         /* Set up RPC arguments for callback */
219         call = &block->b_call;
220         call->a_host    = host;
221         call->a_flags   = RPC_TASK_ASYNC;
222
223         return block;
224
225 failed_free:
226         kfree(block);
227 failed:
228         nlm_release_host(host);
229         return NULL;
230 }
231
232 /*
233  * Delete a block. If the lock was cancelled or the grant callback
234  * failed, unlock is set to 1.
235  * It is the caller's responsibility to check whether the file
236  * can be closed hereafter.
237  */
238 static int nlmsvc_unlink_block(struct nlm_block *block)
239 {
240         int status;
241         dprintk("lockd: unlinking block %p...\n", block);
242
243         /* Remove block from list */
244         status = posix_unblock_lock(block->b_file->f_file, &block->b_call.a_args.lock.fl);
245         nlmsvc_remove_block(block);
246         return status;
247 }
248
249 static void nlmsvc_free_block(struct kref *kref)
250 {
251         struct nlm_block *block = container_of(kref, struct nlm_block, b_count);
252         struct nlm_file         *file = block->b_file;
253         struct nlm_block        **bp;
254
255         dprintk("lockd: freeing block %p...\n", block);
256
257         /* Remove block from file's list of blocks */
258         for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) {
259                 if (*bp == block) {
260                         *bp = block->b_fnext;
261                         break;
262                 }
263         }
264
265         if (block->b_host)
266                 nlm_release_host(block->b_host);
267         nlmclnt_freegrantargs(&block->b_call);
268         kfree(block);
269 }
270
271 static void nlmsvc_release_block(struct nlm_block *block)
272 {
273         if (block != NULL)
274                 kref_put(&block->b_count, nlmsvc_free_block);
275 }
276
277 /*
278  * Loop over all blocks and perform the action specified.
279  * (NLM_ACT_CHECK handled by nlmsvc_inspect_file).
280  */
281 int
282 nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action)
283 {
284         struct nlm_block        *block, *next;
285         /* XXX: Will everything get cleaned up if we don't unlock here? */
286
287         down(&file->f_sema);
288         for (block = file->f_blocks; block; block = next) {
289                 next = block->b_fnext;
290                 if (action == NLM_ACT_MARK)
291                         block->b_host->h_inuse = 1;
292                 else if (action == NLM_ACT_UNLOCK) {
293                         if (host == NULL || host == block->b_host)
294                                 nlmsvc_unlink_block(block);
295                 }
296         }
297         up(&file->f_sema);
298         return 0;
299 }
300
301 /*
302  * Attempt to establish a lock, and if it can't be granted, block it
303  * if required.
304  */
305 u32
306 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
307                         struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
308 {
309         struct nlm_block        *block, *newblock = NULL;
310         int                     error;
311         u32                     ret;
312
313         dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
314                                 file->f_file->f_dentry->d_inode->i_sb->s_id,
315                                 file->f_file->f_dentry->d_inode->i_ino,
316                                 lock->fl.fl_type, lock->fl.fl_pid,
317                                 (long long)lock->fl.fl_start,
318                                 (long long)lock->fl.fl_end,
319                                 wait);
320
321
322         lock->fl.fl_flags &= ~FL_SLEEP;
323 again:
324         /* Lock file against concurrent access */
325         down(&file->f_sema);
326         /* Get existing block (in case client is busy-waiting) */
327         block = nlmsvc_lookup_block(file, lock, 0);
328         if (block == NULL) {
329                 if (newblock != NULL)
330                         lock = &newblock->b_call.a_args.lock;
331         } else
332                 lock = &block->b_call.a_args.lock;
333
334         error = posix_lock_file(file->f_file, &lock->fl);
335         lock->fl.fl_flags &= ~FL_SLEEP;
336
337         dprintk("lockd: posix_lock_file returned %d\n", error);
338
339         switch(error) {
340                 case 0:
341                         ret = nlm_granted;
342                         goto out;
343                 case -EAGAIN:
344                         break;
345                 case -EDEADLK:
346                         ret = nlm_deadlock;
347                         goto out;
348                 default:                        /* includes ENOLCK */
349                         ret = nlm_lck_denied_nolocks;
350                         goto out;
351         }
352
353         ret = nlm_lck_denied;
354         if (!wait)
355                 goto out;
356
357         ret = nlm_lck_blocked;
358         if (block != NULL)
359                 goto out;
360
361         /* If we don't have a block, create and initialize it. Then
362          * retry because we may have slept in kmalloc. */
363         /* We have to release f_sema as nlmsvc_create_block may try to
364          * to claim it while doing host garbage collection */
365         if (newblock == NULL) {
366                 up(&file->f_sema);
367                 dprintk("lockd: blocking on this lock (allocating).\n");
368                 if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie)))
369                         return nlm_lck_denied_nolocks;
370                 goto again;
371         }
372
373         /* Append to list of blocked */
374         nlmsvc_insert_block(newblock, NLM_NEVER);
375 out:
376         up(&file->f_sema);
377         nlmsvc_release_block(newblock);
378         nlmsvc_release_block(block);
379         dprintk("lockd: nlmsvc_lock returned %u\n", ret);
380         return ret;
381 }
382
383 /*
384  * Test for presence of a conflicting lock.
385  */
386 u32
387 nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
388                                        struct nlm_lock *conflock)
389 {
390         dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
391                                 file->f_file->f_dentry->d_inode->i_sb->s_id,
392                                 file->f_file->f_dentry->d_inode->i_ino,
393                                 lock->fl.fl_type,
394                                 (long long)lock->fl.fl_start,
395                                 (long long)lock->fl.fl_end);
396
397         if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) {
398                 dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
399                                 conflock->fl.fl_type,
400                                 (long long)conflock->fl.fl_start,
401                                 (long long)conflock->fl.fl_end);
402                 conflock->caller = "somehost";  /* FIXME */
403                 conflock->oh.len = 0;           /* don't return OH info */
404                 conflock->svid = conflock->fl.fl_pid;
405                 return nlm_lck_denied;
406         }
407
408         return nlm_granted;
409 }
410
411 /*
412  * Remove a lock.
413  * This implies a CANCEL call: We send a GRANT_MSG, the client replies
414  * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
415  * afterwards. In this case the block will still be there, and hence
416  * must be removed.
417  */
418 u32
419 nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
420 {
421         int     error;
422
423         dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
424                                 file->f_file->f_dentry->d_inode->i_sb->s_id,
425                                 file->f_file->f_dentry->d_inode->i_ino,
426                                 lock->fl.fl_pid,
427                                 (long long)lock->fl.fl_start,
428                                 (long long)lock->fl.fl_end);
429
430         /* First, cancel any lock that might be there */
431         nlmsvc_cancel_blocked(file, lock);
432
433         lock->fl.fl_type = F_UNLCK;
434         error = posix_lock_file(file->f_file, &lock->fl);
435
436         return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
437 }
438
439 /*
440  * Cancel a previously blocked request.
441  *
442  * A cancel request always overrides any grant that may currently
443  * be in progress.
444  * The calling procedure must check whether the file can be closed.
445  */
446 u32
447 nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
448 {
449         struct nlm_block        *block;
450         int status = 0;
451
452         dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
453                                 file->f_file->f_dentry->d_inode->i_sb->s_id,
454                                 file->f_file->f_dentry->d_inode->i_ino,
455                                 lock->fl.fl_pid,
456                                 (long long)lock->fl.fl_start,
457                                 (long long)lock->fl.fl_end);
458
459         down(&file->f_sema);
460         if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL) {
461                 status = nlmsvc_unlink_block(block);
462                 nlmsvc_release_block(block);
463         }
464         up(&file->f_sema);
465         return status ? nlm_lck_denied : nlm_granted;
466 }
467
468 /*
469  * Unblock a blocked lock request. This is a callback invoked from the
470  * VFS layer when a lock on which we blocked is removed.
471  *
472  * This function doesn't grant the blocked lock instantly, but rather moves
473  * the block to the head of nlm_blocked where it can be picked up by lockd.
474  */
475 static void
476 nlmsvc_notify_blocked(struct file_lock *fl)
477 {
478         struct nlm_block        **bp, *block;
479
480         dprintk("lockd: VFS unblock notification for block %p\n", fl);
481         for (bp = &nlm_blocked; (block = *bp) != 0; bp = &block->b_next) {
482                 if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) {
483                         nlmsvc_insert_block(block, 0);
484                         svc_wake_up(block->b_daemon);
485                         return;
486                 }
487         }
488
489         printk(KERN_WARNING "lockd: notification for unknown block!\n");
490 }
491
492 static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
493 {
494         return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
495 }
496
497 struct lock_manager_operations nlmsvc_lock_operations = {
498         .fl_compare_owner = nlmsvc_same_owner,
499         .fl_notify = nlmsvc_notify_blocked,
500 };
501
502 /*
503  * Try to claim a lock that was previously blocked.
504  *
505  * Note that we use both the RPC_GRANTED_MSG call _and_ an async
506  * RPC thread when notifying the client. This seems like overkill...
507  * Here's why:
508  *  -   we don't want to use a synchronous RPC thread, otherwise
509  *      we might find ourselves hanging on a dead portmapper.
510  *  -   Some lockd implementations (e.g. HP) don't react to
511  *      RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
512  */
513 static void
514 nlmsvc_grant_blocked(struct nlm_block *block)
515 {
516         struct nlm_file         *file = block->b_file;
517         struct nlm_lock         *lock = &block->b_call.a_args.lock;
518         int                     error;
519
520         dprintk("lockd: grant blocked lock %p\n", block);
521
522         /* First thing is lock the file */
523         down(&file->f_sema);
524
525         /* Unlink block request from list */
526         nlmsvc_unlink_block(block);
527
528         /* If b_granted is true this means we've been here before.
529          * Just retry the grant callback, possibly refreshing the RPC
530          * binding */
531         if (block->b_granted) {
532                 nlm_rebind_host(block->b_host);
533                 goto callback;
534         }
535
536         /* Try the lock operation again */
537         lock->fl.fl_flags |= FL_SLEEP;
538         error = posix_lock_file(file->f_file, &lock->fl);
539         lock->fl.fl_flags &= ~FL_SLEEP;
540
541         switch (error) {
542         case 0:
543                 break;
544         case -EAGAIN:
545                 dprintk("lockd: lock still blocked\n");
546                 nlmsvc_insert_block(block, NLM_NEVER);
547                 goto out_unlock;
548         default:
549                 printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
550                                 -error, __FUNCTION__);
551                 nlmsvc_insert_block(block, 10 * HZ);
552                 goto out_unlock;
553         }
554
555 callback:
556         /* Lock was granted by VFS. */
557         dprintk("lockd: GRANTing blocked lock.\n");
558         block->b_granted = 1;
559
560         /* Schedule next grant callback in 30 seconds */
561         nlmsvc_insert_block(block, 30 * HZ);
562
563         /* Call the client */
564         kref_get(&block->b_count);
565         if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
566                                                 &nlmsvc_grant_ops) < 0)
567                 nlmsvc_release_block(block);
568 out_unlock:
569         up(&file->f_sema);
570 }
571
572 /*
573  * This is the callback from the RPC layer when the NLM_GRANTED_MSG
574  * RPC call has succeeded or timed out.
575  * Like all RPC callbacks, it is invoked by the rpciod process, so it
576  * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
577  * chain once more in order to have it removed by lockd itself (which can
578  * then sleep on the file semaphore without disrupting e.g. the nfs client).
579  */
580 static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
581 {
582         struct nlm_rqst         *call = data;
583         struct nlm_block        *block = container_of(call, struct nlm_block, b_call);
584         unsigned long           timeout;
585
586         dprintk("lockd: GRANT_MSG RPC callback\n");
587
588         /* Technically, we should down the file semaphore here. Since we
589          * move the block towards the head of the queue only, no harm
590          * can be done, though. */
591         if (task->tk_status < 0) {
592                 /* RPC error: Re-insert for retransmission */
593                 timeout = 10 * HZ;
594         } else if (block->b_done) {
595                 /* Block already removed, kill it for real */
596                 timeout = 0;
597         } else {
598                 /* Call was successful, now wait for client callback */
599                 timeout = 60 * HZ;
600         }
601         nlmsvc_insert_block(block, timeout);
602         svc_wake_up(block->b_daemon);
603         nlmsvc_release_block(block);
604 }
605
606 static const struct rpc_call_ops nlmsvc_grant_ops = {
607         .rpc_call_done = nlmsvc_grant_callback,
608 };
609
610 /*
611  * We received a GRANT_RES callback. Try to find the corresponding
612  * block.
613  */
614 void
615 nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status)
616 {
617         struct nlm_block        *block;
618         struct nlm_file         *file;
619
620         dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n", 
621                 *(unsigned int *)(cookie->data), 
622                 ntohl(rqstp->rq_addr.sin_addr.s_addr), status);
623         if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr)))
624                 return;
625         file = block->b_file;
626
627         file->f_count++;
628         down(&file->f_sema);
629         if (block) {
630                 if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
631                         /* Try again in a couple of seconds */
632                         nlmsvc_insert_block(block, 10 * HZ);
633                 } else {
634                         /* Lock is now held by client, or has been rejected.
635                          * In both cases, the block should be removed. */
636                         nlmsvc_unlink_block(block);
637                 }
638         }
639         up(&file->f_sema);
640         nlm_release_file(file);
641         nlmsvc_release_block(block);
642 }
643
644 /*
645  * Retry all blocked locks that have been notified. This is where lockd
646  * picks up locks that can be granted, or grant notifications that must
647  * be retransmitted.
648  */
649 unsigned long
650 nlmsvc_retry_blocked(void)
651 {
652         struct nlm_block        *block;
653
654         dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
655                         nlm_blocked,
656                         nlm_blocked? nlm_blocked->b_when : 0);
657         while ((block = nlm_blocked) != 0) {
658                 if (block->b_when == NLM_NEVER)
659                         break;
660                 if (time_after(block->b_when,jiffies))
661                         break;
662                 dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
663                         block, block->b_when, block->b_done);
664                 kref_get(&block->b_count);
665                 if (block->b_done)
666                         nlmsvc_unlink_block(block);
667                 else
668                         nlmsvc_grant_blocked(block);
669                 nlmsvc_release_block(block);
670         }
671
672         if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
673                 return (block->b_when - jiffies);
674
675         return MAX_SCHEDULE_TIMEOUT;
676 }