Commit 53fc622b9e829c8e632e45ef8c14f054388759c1
1 parent
cf8e06f1a8
Exists in
master
and in
7 other branches
[PATCH 2/2] ocfs2: cluster aware flock()
Hook up ocfs2_flock(), using the new flock lock type in dlmglue.c. A new mount option, "localflocks" is added so that users can revert to old functionality as need be. Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Showing 7 changed files with 237 additions and 1 deletions Side-by-side Diff
Documentation/filesystems/ocfs2.txt
fs/ocfs2/Makefile
fs/ocfs2/file.c
... | ... | @@ -51,6 +51,7 @@ |
51 | 51 | #include "inode.h" |
52 | 52 | #include "ioctl.h" |
53 | 53 | #include "journal.h" |
54 | +#include "locks.h" | |
54 | 55 | #include "mmap.h" |
55 | 56 | #include "suballoc.h" |
56 | 57 | #include "super.h" |
... | ... | @@ -63,6 +64,35 @@ |
63 | 64 | return sync_mapping_buffers(inode->i_mapping); |
64 | 65 | } |
65 | 66 | |
67 | +static int ocfs2_init_file_private(struct inode *inode, struct file *file) | |
68 | +{ | |
69 | + struct ocfs2_file_private *fp; | |
70 | + | |
71 | + fp = kzalloc(sizeof(struct ocfs2_file_private), GFP_KERNEL); | |
72 | + if (!fp) | |
73 | + return -ENOMEM; | |
74 | + | |
75 | + fp->fp_file = file; | |
76 | + mutex_init(&fp->fp_mutex); | |
77 | + ocfs2_file_lock_res_init(&fp->fp_flock, fp); | |
78 | + file->private_data = fp; | |
79 | + | |
80 | + return 0; | |
81 | +} | |
82 | + | |
83 | +static void ocfs2_free_file_private(struct inode *inode, struct file *file) | |
84 | +{ | |
85 | + struct ocfs2_file_private *fp = file->private_data; | |
86 | + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | |
87 | + | |
88 | + if (fp) { | |
89 | + ocfs2_simple_drop_lockres(osb, &fp->fp_flock); | |
90 | + ocfs2_lock_res_free(&fp->fp_flock); | |
91 | + kfree(fp); | |
92 | + file->private_data = NULL; | |
93 | + } | |
94 | +} | |
95 | + | |
66 | 96 | static int ocfs2_file_open(struct inode *inode, struct file *file) |
67 | 97 | { |
68 | 98 | int status; |
... | ... | @@ -89,7 +119,18 @@ |
89 | 119 | |
90 | 120 | oi->ip_open_count++; |
91 | 121 | spin_unlock(&oi->ip_lock); |
92 | - status = 0; | |
122 | + | |
123 | + status = ocfs2_init_file_private(inode, file); | |
124 | + if (status) { | |
125 | + /* | |
126 | + * We want to set open count back if we're failing the | |
127 | + * open. | |
128 | + */ | |
129 | + spin_lock(&oi->ip_lock); | |
130 | + oi->ip_open_count--; | |
131 | + spin_unlock(&oi->ip_lock); | |
132 | + } | |
133 | + | |
93 | 134 | leave: |
94 | 135 | mlog_exit(status); |
95 | 136 | return status; |
96 | 137 | |
... | ... | @@ -108,11 +149,24 @@ |
108 | 149 | oi->ip_flags &= ~OCFS2_INODE_OPEN_DIRECT; |
109 | 150 | spin_unlock(&oi->ip_lock); |
110 | 151 | |
152 | + ocfs2_free_file_private(inode, file); | |
153 | + | |
111 | 154 | mlog_exit(0); |
112 | 155 | |
113 | 156 | return 0; |
114 | 157 | } |
115 | 158 | |
159 | +static int ocfs2_dir_open(struct inode *inode, struct file *file) | |
160 | +{ | |
161 | + return ocfs2_init_file_private(inode, file); | |
162 | +} | |
163 | + | |
164 | +static int ocfs2_dir_release(struct inode *inode, struct file *file) | |
165 | +{ | |
166 | + ocfs2_free_file_private(inode, file); | |
167 | + return 0; | |
168 | +} | |
169 | + | |
116 | 170 | static int ocfs2_sync_file(struct file *file, |
117 | 171 | struct dentry *dentry, |
118 | 172 | int datasync) |
... | ... | @@ -2191,6 +2245,7 @@ |
2191 | 2245 | #ifdef CONFIG_COMPAT |
2192 | 2246 | .compat_ioctl = ocfs2_compat_ioctl, |
2193 | 2247 | #endif |
2248 | + .flock = ocfs2_flock, | |
2194 | 2249 | .splice_read = ocfs2_file_splice_read, |
2195 | 2250 | .splice_write = ocfs2_file_splice_write, |
2196 | 2251 | }; |
2197 | 2252 | |
... | ... | @@ -2199,9 +2254,12 @@ |
2199 | 2254 | .read = generic_read_dir, |
2200 | 2255 | .readdir = ocfs2_readdir, |
2201 | 2256 | .fsync = ocfs2_sync_file, |
2257 | + .release = ocfs2_dir_release, | |
2258 | + .open = ocfs2_dir_open, | |
2202 | 2259 | .ioctl = ocfs2_ioctl, |
2203 | 2260 | #ifdef CONFIG_COMPAT |
2204 | 2261 | .compat_ioctl = ocfs2_compat_ioctl, |
2205 | 2262 | #endif |
2263 | + .flock = ocfs2_flock, | |
2206 | 2264 | }; |
fs/ocfs2/locks.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * locks.c | |
5 | + * | |
6 | + * Userspace file locking support | |
7 | + * | |
8 | + * Copyright (C) 2007 Oracle. All rights reserved. | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or | |
11 | + * modify it under the terms of the GNU General Public | |
12 | + * License as published by the Free Software Foundation; either | |
13 | + * version 2 of the License, or (at your option) any later version. | |
14 | + * | |
15 | + * This program is distributed in the hope that it will be useful, | |
16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | + * General Public License for more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public | |
21 | + * License along with this program; if not, write to the | |
22 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
23 | + * Boston, MA 021110-1307, USA. | |
24 | + */ | |
25 | + | |
26 | +#include <linux/fs.h> | |
27 | + | |
28 | +#define MLOG_MASK_PREFIX ML_INODE | |
29 | +#include <cluster/masklog.h> | |
30 | + | |
31 | +#include "ocfs2.h" | |
32 | + | |
33 | +#include "dlmglue.h" | |
34 | +#include "file.h" | |
35 | +#include "locks.h" | |
36 | + | |
37 | +static int ocfs2_do_flock(struct file *file, struct inode *inode, | |
38 | + int cmd, struct file_lock *fl) | |
39 | +{ | |
40 | + int ret = 0, level = 0, trylock = 0; | |
41 | + struct ocfs2_file_private *fp = file->private_data; | |
42 | + struct ocfs2_lock_res *lockres = &fp->fp_flock; | |
43 | + | |
44 | + if (fl->fl_type == F_WRLCK) | |
45 | + level = 1; | |
46 | + if (!IS_SETLKW(cmd)) | |
47 | + trylock = 1; | |
48 | + | |
49 | + mutex_lock(&fp->fp_mutex); | |
50 | + | |
51 | + if (lockres->l_flags & OCFS2_LOCK_ATTACHED && | |
52 | + lockres->l_level > LKM_NLMODE) { | |
53 | + int old_level = 0; | |
54 | + | |
55 | + if (lockres->l_level == LKM_EXMODE) | |
56 | + old_level = 1; | |
57 | + | |
58 | + if (level == old_level) | |
59 | + goto out; | |
60 | + | |
61 | + /* | |
62 | + * Converting an existing lock is not guaranteed to be | |
63 | + * atomic, so we can get away with simply unlocking | |
64 | + * here and allowing the lock code to try at the new | |
65 | + * level. | |
66 | + */ | |
67 | + | |
68 | + flock_lock_file_wait(file, | |
69 | + &(struct file_lock){.fl_type = F_UNLCK}); | |
70 | + | |
71 | + ocfs2_file_unlock(file); | |
72 | + } | |
73 | + | |
74 | + ret = ocfs2_file_lock(file, level, trylock); | |
75 | + if (ret) { | |
76 | + if (ret == -EAGAIN && trylock) | |
77 | + ret = -EWOULDBLOCK; | |
78 | + else | |
79 | + mlog_errno(ret); | |
80 | + goto out; | |
81 | + } | |
82 | + | |
83 | + ret = flock_lock_file_wait(file, fl); | |
84 | + | |
85 | +out: | |
86 | + mutex_unlock(&fp->fp_mutex); | |
87 | + | |
88 | + return ret; | |
89 | +} | |
90 | + | |
91 | +static int ocfs2_do_funlock(struct file *file, int cmd, struct file_lock *fl) | |
92 | +{ | |
93 | + int ret; | |
94 | + struct ocfs2_file_private *fp = file->private_data; | |
95 | + | |
96 | + mutex_lock(&fp->fp_mutex); | |
97 | + ocfs2_file_unlock(file); | |
98 | + ret = flock_lock_file_wait(file, fl); | |
99 | + mutex_unlock(&fp->fp_mutex); | |
100 | + | |
101 | + return ret; | |
102 | +} | |
103 | + | |
104 | +/* | |
105 | + * Overall flow of ocfs2_flock() was influenced by gfs2_flock(). | |
106 | + */ | |
107 | +int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl) | |
108 | +{ | |
109 | + struct inode *inode = file->f_mapping->host; | |
110 | + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | |
111 | + | |
112 | + if (!(fl->fl_flags & FL_FLOCK)) | |
113 | + return -ENOLCK; | |
114 | + if (__mandatory_lock(inode)) | |
115 | + return -ENOLCK; | |
116 | + | |
117 | + if ((osb->s_mount_opt & OCFS2_MOUNT_LOCALFLOCKS) || | |
118 | + ocfs2_mount_local(osb)) | |
119 | + return flock_lock_file_wait(file, fl); | |
120 | + | |
121 | + if (fl->fl_type == F_UNLCK) | |
122 | + return ocfs2_do_funlock(file, cmd, fl); | |
123 | + else | |
124 | + return ocfs2_do_flock(file, inode, cmd, fl); | |
125 | +} |
fs/ocfs2/locks.h
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * locks.h | |
5 | + * | |
6 | + * Function prototypes for Userspace file locking support | |
7 | + * | |
8 | + * Copyright (C) 2002, 2004 Oracle. All rights reserved. | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or | |
11 | + * modify it under the terms of the GNU General Public | |
12 | + * License as published by the Free Software Foundation; either | |
13 | + * version 2 of the License, or (at your option) any later version. | |
14 | + * | |
15 | + * This program is distributed in the hope that it will be useful, | |
16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | + * General Public License for more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public | |
21 | + * License along with this program; if not, write to the | |
22 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
23 | + * Boston, MA 021110-1307, USA. | |
24 | + */ | |
25 | + | |
26 | +#ifndef OCFS2_LOCKS_H | |
27 | +#define OCFS2_LOCKS_H | |
28 | + | |
29 | +int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl); | |
30 | + | |
31 | +#endif /* OCFS2_LOCKS_H */ |
fs/ocfs2/ocfs2.h
... | ... | @@ -171,6 +171,7 @@ |
171 | 171 | OCFS2_MOUNT_NOINTR = 1 << 2, /* Don't catch signals */ |
172 | 172 | OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */ |
173 | 173 | OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */ |
174 | + OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */ | |
174 | 175 | }; |
175 | 176 | |
176 | 177 | #define OCFS2_OSB_SOFT_RO 0x0001 |
fs/ocfs2/super.c
... | ... | @@ -153,6 +153,7 @@ |
153 | 153 | Opt_slot, |
154 | 154 | Opt_commit, |
155 | 155 | Opt_localalloc, |
156 | + Opt_localflocks, | |
156 | 157 | Opt_err, |
157 | 158 | }; |
158 | 159 | |
... | ... | @@ -170,6 +171,7 @@ |
170 | 171 | {Opt_slot, "preferred_slot=%u"}, |
171 | 172 | {Opt_commit, "commit=%u"}, |
172 | 173 | {Opt_localalloc, "localalloc=%d"}, |
174 | + {Opt_localflocks, "localflocks"}, | |
173 | 175 | {Opt_err, NULL} |
174 | 176 | }; |
175 | 177 | |
... | ... | @@ -848,6 +850,20 @@ |
848 | 850 | if (option >= 0 && (option <= ocfs2_local_alloc_size(sb) * 8)) |
849 | 851 | mopt->localalloc_opt = option; |
850 | 852 | break; |
853 | + case Opt_localflocks: | |
854 | + /* | |
855 | + * Changing this during remount could race | |
856 | + * flock() requests, or "unbalance" existing | |
857 | + * ones (e.g., a lock is taken in one mode but | |
858 | + * dropped in the other). If users care enough | |
859 | + * to flip locking modes during remount, we | |
860 | + * could add a "local" flag to individual | |
861 | + * flock structures for proper tracking of | |
862 | + * state. | |
863 | + */ | |
864 | + if (!is_remount) | |
865 | + mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS; | |
866 | + break; | |
851 | 867 | default: |
852 | 868 | mlog(ML_ERROR, |
853 | 869 | "Unrecognized mount option \"%s\" " |
... | ... | @@ -902,6 +918,9 @@ |
902 | 918 | |
903 | 919 | if (osb->local_alloc_size != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE) |
904 | 920 | seq_printf(s, ",localalloc=%d", osb->local_alloc_size); |
921 | + | |
922 | + if (opts & OCFS2_MOUNT_LOCALFLOCKS) | |
923 | + seq_printf(s, ",localflocks,"); | |
905 | 924 | |
906 | 925 | return 0; |
907 | 926 | } |