blob: b11af21f60c1e447e47c3c9a48f65262ed8a61dc [file] [log] [blame]
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
* vim:expandtab:shiftwidth=8:tabstop=8:
*
* Copyright (C) 2001, 2002 Cluster File Systems, Inc. <braam@clusterfs.com>
* Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
*
* This file is part of InterMezzo, http://www.inter-mezzo.org.
*
* InterMezzo is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* InterMezzo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with InterMezzo; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Mostly platform independent upcall operations to a cache manager:
* -- upcalls
* -- upcall routines
*
*/
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/signal.h>
#include <linux/signal.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>
#include <asm/segment.h>
#include <linux/intermezzo_lib.h>
#include <linux/intermezzo_fs.h>
#include <linux/intermezzo_psdev.h>
#include <linux/intermezzo_idl.h>
/*
At present:
-- Asynchronous calls:
- kml: give a "more" kml indication to userland
- kml_truncate: initiate KML truncation
- release_permit: kernel is done with permit
-- Synchronous
- open: fetch file
- permit: get a permit
Errors returned by user level code are positive
*/
static struct izo_upcall_hdr *upc_pack(__u32 opcode, int pathlen, char *path,
char *fsetname, int reclen, char *rec,
int *size)
{
struct izo_upcall_hdr *hdr;
char *ptr;
ENTRY;
*size = sizeof(struct izo_upcall_hdr);
if ( fsetname ) {
*size += round_strlen(fsetname);
}
if ( path ) {
*size += round_strlen(path);
}
if ( rec ) {
*size += size_round(reclen);
}
PRESTO_ALLOC(hdr, *size);
if (!hdr) {
CERROR("intermezzo upcall: out of memory (opc %d)\n", opcode);
EXIT;
return NULL;
}
memset(hdr, 0, *size);
ptr = (char *)hdr + sizeof(*hdr);
/* XXX do we need fsuid ? */
hdr->u_len = *size;
hdr->u_version = IZO_UPC_VERSION;
hdr->u_opc = opcode;
hdr->u_pid = current->pid;
hdr->u_uid = current->fsuid;
if (path) {
/*XXX Robert: please review what len to pass in for
NUL terminated strings */
hdr->u_pathlen = strlen(path);
LOGL0(path, hdr->u_pathlen, ptr);
}
if (fsetname) {
hdr->u_fsetlen = strlen(fsetname);
LOGL0(fsetname, strlen(fsetname), ptr);
}
if (rec) {
hdr->u_reclen = reclen;
LOGL(rec, reclen, ptr);
}
EXIT;
return hdr;
}
/* the upcalls */
int izo_upc_kml(int minor, __u64 offset, __u32 first_recno, __u64 length, __u32 last_recno, char *fsetname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return 0;
}
hdr = upc_pack(IZO_UPC_KML, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
hdr->u_offset = offset;
hdr->u_first_recno = first_recno;
hdr->u_length = length;
hdr->u_last_recno = last_recno;
CDEBUG(D_UPCALL, "KML: fileset %s, offset %Lu, length %Lu, "
"first %u, last %d; minor %d\n",
fsetname, hdr->u_offset, hdr->u_length, hdr->u_first_recno,
hdr->u_last_recno, minor);
error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
EXIT;
return -error;
}
int izo_upc_kml_truncate(int minor, __u64 length, __u32 last_recno, char *fsetname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return 0;
}
hdr = upc_pack(IZO_UPC_KML_TRUNC, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
hdr->u_length = length;
hdr->u_last_recno = last_recno;
CDEBUG(D_UPCALL, "KML TRUNCATE: fileset %s, length %Lu, "
"last recno %d, minor %d\n",
fsetname, hdr->u_length, hdr->u_last_recno, minor);
error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
EXIT;
return error;
}
int izo_upc_open(int minor, __u32 pathlen, char *path, char *fsetname, struct lento_vfs_context *info)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_OPEN, pathlen, path, fsetname,
sizeof(*info), (char*)info, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
CDEBUG(D_UPCALL, "path %s\n", path);
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_get_fileid(int minor, __u32 reclen, char *rec,
__u32 pathlen, char *path, char *fsetname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_GET_FILEID, pathlen, path, fsetname, reclen, rec, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
CDEBUG(D_UPCALL, "path %s\n", path);
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_backfetch(int minor, char *path, char *fsetname, struct lento_vfs_context *info)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_BACKFETCH, strlen(path), path, fsetname,
sizeof(*info), (char *)info, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
/* This is currently synchronous, kml_reint_record blocks */
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_permit(int minor, struct dentry *dentry, __u32 pathlen, char *path,
char *fsetname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
hdr = upc_pack(IZO_UPC_PERMIT, pathlen, path, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
CDEBUG(D_UPCALL, "Permit minor %d path %s\n", minor, path);
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error == -EROFS) {
int err;
CERROR("InterMezzo: ERROR - requested permit for read-only "
"fileset.\n Setting \"%s\" read-only!\n", path);
err = izo_mark_cache(dentry, 0xFFFFFFFF, CACHE_CLIENT_RO, NULL);
if (err)
CERROR("InterMezzo ERROR: mark_cache %d\n", err);
} else if (error) {
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
}
EXIT;
return error;
}
/* This is a ping-pong upcall handled on the server when a client (uuid)
* requests the permit for itself. */
int izo_upc_revoke_permit(int minor, char *fsetname, __u8 uuid[16])
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
hdr = upc_pack(IZO_UPC_REVOKE_PERMIT, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_go_fetch_kml(int minor, char *fsetname, __u8 uuid[16],
__u64 kmlsize)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_GO_FETCH_KML, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
hdr->u_offset = kmlsize;
memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
if (error)
CERROR("%s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_connect(int minor, __u64 ip_address, __u64 port, __u8 uuid[16],
int client_flag)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_CONNECT, 0, NULL, NULL, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
hdr->u_offset = ip_address;
hdr->u_length = port;
memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
hdr->u_first_recno = client_flag;
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error) {
CERROR("%s: error %d\n", __FUNCTION__, error);
}
EXIT;
return -error;
}
int izo_upc_set_kmlsize(int minor, char *fsetname, __u8 uuid[16], __u64 kmlsize)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_SET_KMLSIZE, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
hdr->u_length = kmlsize;
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("%s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_repstatus(int minor, char * fsetname, struct izo_rcvd_rec *lr_server)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_REPSTATUS, 0, NULL, fsetname,
sizeof(*lr_server), (char*)lr_server,
&size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("%s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
#if 0
int izo_upc_client_make_branch(int minor, char *fsetname, char *tagname,
char *branchname)
{
int size, error;
struct izo_upcall_hdr *hdr;
int pathlen;
char *path;
ENTRY;
hdr = upc_pack(IZO_UPC_CLIENT_MAKE_BRANCH, strlen(tagname), tagname,
fsetname, strlen(branchname) + 1, branchname, &size);
if (!hdr || IS_ERR(hdr)) {
error = -PTR_ERR(hdr);
goto error;
}
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: error %d\n", error);
error:
PRESTO_FREE(path, pathlen);
EXIT;
return error;
}
#endif
int izo_upc_server_make_branch(int minor, char *fsetname)
{
int size, error;
struct izo_upcall_hdr *hdr;
ENTRY;
hdr = upc_pack(IZO_UPC_SERVER_MAKE_BRANCH, 0, NULL, fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
error = -PTR_ERR(hdr);
goto error;
}
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: error %d\n", error);
error:
EXIT;
return -error;
}
int izo_upc_branch_undo(int minor, char *fsetname, char *branchname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_BRANCH_UNDO, strlen(branchname), branchname,
fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}
int izo_upc_branch_redo(int minor, char *fsetname, char *branchname)
{
int size;
int error;
struct izo_upcall_hdr *hdr;
ENTRY;
if (!presto_lento_up(minor)) {
EXIT;
return -EIO;
}
hdr = upc_pack(IZO_UPC_BRANCH_REDO, strlen(branchname) + 1, branchname,
fsetname, 0, NULL, &size);
if (!hdr || IS_ERR(hdr)) {
EXIT;
return -PTR_ERR(hdr);
}
error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
if (error)
CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
EXIT;
return -error;
}