1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
5 * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
7 * This file is part of InterMezzo, http://www.inter-mezzo.org.
9 * InterMezzo is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
13 * InterMezzo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with InterMezzo; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Manage RCVD records for clients in the kernel
26 #define __NO_VERSION__
27 #include <linux/module.h>
29 #include <asm/uaccess.h>
31 #include <linux/errno.h>
33 #include <linux/intermezzo_fs.h>
36 * this file contains a hash table of replicators/clients for a
37 * fileset. It allows fast lookup and update of reintegration status
40 struct izo_offset_rec {
41 struct list_head or_list;
47 #define RCACHE_SIZE (1 << RCACHE_BITS)
48 #define RCACHE_MASK (RCACHE_SIZE - 1)
50 static struct list_head *
54 struct list_head *cache;
55 PRESTO_ALLOC(cache, sizeof(struct list_head) * RCACHE_SIZE);
57 CERROR("intermezzo-fatal: no memory for replicator cache\n");
60 memset(cache, 0, sizeof(struct list_head) * RCACHE_SIZE);
61 for (i = 0; i < RCACHE_SIZE; i++)
62 INIT_LIST_HEAD(&cache[i]);
67 static struct list_head *
68 izo_rep_hash(struct list_head *cache, char *uuid)
70 return &cache[(RCACHE_MASK & uuid[1])];
74 izo_rep_cache_clean(struct presto_file_set *fset)
77 struct list_head *bucket;
78 struct list_head *tmp;
80 if (fset->fset_clients == NULL)
82 for (i = 0; i < RCACHE_SIZE; i++) {
84 list_for_each_safe(tmp,bucket,&fset->fset_clients[i])
86 struct izo_offset_rec *offrec;
88 offrec = list_entry(tmp, struct izo_offset_rec,or_list);
89 PRESTO_FREE(offrec, sizeof(struct izo_offset_rec));
92 PRESTO_FREE(fset->fset_clients,sizeof(struct list_head) * RCACHE_SIZE);
95 struct izo_offset_rec *
96 izo_rep_cache_find(struct presto_file_set *fset, char *uuid)
98 struct list_head *tmp, *buck = izo_rep_hash(fset->fset_clients, uuid);
99 struct izo_offset_rec *rec = NULL;
101 list_for_each(tmp, buck) {
102 rec = list_entry(tmp, struct izo_offset_rec, or_list);
103 if ( memcmp(rec->or_uuid, uuid, sizeof(rec->or_uuid)) == 0 )
111 izo_rep_cache_add(struct presto_file_set *fset, struct izo_rcvd_rec *rec,
114 struct izo_offset_rec *offrec;
116 if (izo_rep_cache_find(fset, rec->lr_uuid)) {
117 CERROR("izo: duplicate client entry %s off %Ld\n",
118 fset->fset_name, offset);
122 PRESTO_ALLOC(offrec, sizeof(*offrec));
123 if (offrec == NULL) {
124 CERROR("izo: cannot allocate offrec\n");
128 memcpy(offrec->or_uuid, rec->lr_uuid, sizeof(rec->lr_uuid));
129 offrec->or_offset = offset;
131 list_add(&offrec->or_list,
132 izo_rep_hash(fset->fset_clients, rec->lr_uuid));
137 izo_rep_cache_init(struct presto_file_set *fset)
139 struct izo_rcvd_rec rec;
140 loff_t offset = 0, last_offset = 0;
142 fset->fset_clients = izo_rep_cache();
143 if (fset->fset_clients == NULL) {
144 CERROR("Error initializing client cache\n");
148 while ( presto_fread(fset->fset_rcvd.fd_file, (char *)&rec,
149 sizeof(rec), &offset) == sizeof(rec) ) {
152 if ((rc = izo_rep_cache_add(fset, &rec, last_offset)) < 0) {
153 izo_rep_cache_clean(fset);
157 last_offset = offset;
164 * Return local last_rcvd record for the client. Update or create
167 * XXX: After this call, any -EINVAL from izo_rcvd_get is a real error.
170 izo_repstatus(struct presto_file_set *fset, __u64 client_kmlsize,
171 struct izo_rcvd_rec *lr_client, struct izo_rcvd_rec *lr_server)
174 rc = izo_rcvd_get(lr_server, fset, lr_client->lr_uuid);
175 if (rc < 0 && rc != -EINVAL) {
179 /* client is new or has been reset. */
180 if (rc < 0 || (client_kmlsize == 0 && lr_client->lr_remote_offset == 0)) {
181 memset(lr_server, 0, sizeof(*lr_server));
182 memcpy(lr_server->lr_uuid, lr_client->lr_uuid, sizeof(lr_server->lr_uuid));
183 rc = izo_rcvd_write(fset, lr_server);
188 /* update intersync */
189 rc = izo_upc_repstatus(presto_f2m(fset), fset->fset_name, lr_server);
194 izo_rcvd_get(struct izo_rcvd_rec *rec, struct presto_file_set *fset, char *uuid)
196 struct izo_offset_rec *offrec;
197 struct izo_rcvd_rec tmprec;
200 offrec = izo_rep_cache_find(fset, uuid);
201 if (offrec == NULL) {
202 CDEBUG(D_SPECIAL, "izo_get_rcvd: uuid not in hash.\n");
205 offset = offrec->or_offset;
210 if (presto_fread(fset->fset_rcvd.fd_file, (char *)&tmprec,
211 sizeof(tmprec), &offset) != sizeof(tmprec)) {
212 CERROR("izo_get_rcvd: Unable to read from last_rcvd file offset "
217 memcpy(rec->lr_uuid, tmprec.lr_uuid, sizeof(tmprec.lr_uuid));
218 rec->lr_remote_recno = le64_to_cpu(tmprec.lr_remote_recno);
219 rec->lr_remote_offset = le64_to_cpu(tmprec.lr_remote_offset);
220 rec->lr_local_recno = le64_to_cpu(tmprec.lr_local_recno);
221 rec->lr_local_offset = le64_to_cpu(tmprec.lr_local_offset);
222 rec->lr_last_ctime = le64_to_cpu(tmprec.lr_last_ctime);
224 return offrec->or_offset;
227 /* Try to lookup the UUID in the hash. Insert it if it isn't found. Write the
230 * Returns the offset of the beginning of the record in the last_rcvd file. */
232 izo_rcvd_write(struct presto_file_set *fset, struct izo_rcvd_rec *rec)
234 struct izo_offset_rec *offrec;
239 offrec = izo_rep_cache_find(fset, rec->lr_uuid);
240 if (offrec == NULL) {
241 /* I don't think it should be possible for an entry to be not in
242 * the hash table without also having an invalid offset, but we
243 * handle it gracefully regardless. */
244 write_lock(&fset->fset_rcvd.fd_lock);
245 offset = fset->fset_rcvd.fd_offset;
246 fset->fset_rcvd.fd_offset += sizeof(*rec);
247 write_unlock(&fset->fset_rcvd.fd_lock);
249 rc = izo_rep_cache_add(fset, rec, offset);
255 offset = offrec->or_offset;
258 rc = presto_fwrite(fset->fset_rcvd.fd_file, (char *)rec, sizeof(*rec),
260 if (rc == sizeof(*rec))
261 /* presto_fwrite() advances 'offset' */
262 rc = offset - sizeof(*rec);
269 izo_rcvd_upd_remote(struct presto_file_set *fset, char * uuid, __u64 remote_recno,
272 struct izo_rcvd_rec rec;
277 rc = izo_rcvd_get(&rec, fset, uuid);
280 rec.lr_remote_recno = remote_recno;
281 rec.lr_remote_offset = remote_offset;
283 rc = izo_rcvd_write(fset, &rec);