cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

plock.c (11345B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2005-2008 Red Hat, Inc.  All rights reserved.
      4 */
      5
      6#include <linux/fs.h>
      7#include <linux/miscdevice.h>
      8#include <linux/poll.h>
      9#include <linux/dlm.h>
     10#include <linux/dlm_plock.h>
     11#include <linux/slab.h>
     12
     13#include "dlm_internal.h"
     14#include "lockspace.h"
     15
     16static DEFINE_SPINLOCK(ops_lock);
     17static LIST_HEAD(send_list);
     18static LIST_HEAD(recv_list);
     19static DECLARE_WAIT_QUEUE_HEAD(send_wq);
     20static DECLARE_WAIT_QUEUE_HEAD(recv_wq);
     21
     22struct plock_async_data {
     23	void *fl;
     24	void *file;
     25	struct file_lock flc;
     26	int (*callback)(struct file_lock *fl, int result);
     27};
     28
     29struct plock_op {
     30	struct list_head list;
     31	int done;
     32	struct dlm_plock_info info;
     33	/* if set indicates async handling */
     34	struct plock_async_data *data;
     35};
     36
     37static inline void set_version(struct dlm_plock_info *info)
     38{
     39	info->version[0] = DLM_PLOCK_VERSION_MAJOR;
     40	info->version[1] = DLM_PLOCK_VERSION_MINOR;
     41	info->version[2] = DLM_PLOCK_VERSION_PATCH;
     42}
     43
     44static int check_version(struct dlm_plock_info *info)
     45{
     46	if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) ||
     47	    (DLM_PLOCK_VERSION_MINOR < info->version[1])) {
     48		log_print("plock device version mismatch: "
     49			  "kernel (%u.%u.%u), user (%u.%u.%u)",
     50			  DLM_PLOCK_VERSION_MAJOR,
     51			  DLM_PLOCK_VERSION_MINOR,
     52			  DLM_PLOCK_VERSION_PATCH,
     53			  info->version[0],
     54			  info->version[1],
     55			  info->version[2]);
     56		return -EINVAL;
     57	}
     58	return 0;
     59}
     60
     61static void dlm_release_plock_op(struct plock_op *op)
     62{
     63	kfree(op->data);
     64	kfree(op);
     65}
     66
     67static void send_op(struct plock_op *op)
     68{
     69	set_version(&op->info);
     70	spin_lock(&ops_lock);
     71	list_add_tail(&op->list, &send_list);
     72	spin_unlock(&ops_lock);
     73	wake_up(&send_wq);
     74}
     75
     76/* If a process was killed while waiting for the only plock on a file,
     77   locks_remove_posix will not see any lock on the file so it won't
     78   send an unlock-close to us to pass on to userspace to clean up the
     79   abandoned waiter.  So, we have to insert the unlock-close when the
     80   lock call is interrupted. */
     81
     82static void do_unlock_close(struct dlm_ls *ls, u64 number,
     83			    struct file *file, struct file_lock *fl)
     84{
     85	struct plock_op *op;
     86
     87	op = kzalloc(sizeof(*op), GFP_NOFS);
     88	if (!op)
     89		return;
     90
     91	op->info.optype		= DLM_PLOCK_OP_UNLOCK;
     92	op->info.pid		= fl->fl_pid;
     93	op->info.fsid		= ls->ls_global_id;
     94	op->info.number		= number;
     95	op->info.start		= 0;
     96	op->info.end		= OFFSET_MAX;
     97	if (fl->fl_lmops && fl->fl_lmops->lm_grant)
     98		op->info.owner	= (__u64) fl->fl_pid;
     99	else
    100		op->info.owner	= (__u64)(long) fl->fl_owner;
    101
    102	op->info.flags |= DLM_PLOCK_FL_CLOSE;
    103	send_op(op);
    104}
    105
    106int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
    107		   int cmd, struct file_lock *fl)
    108{
    109	struct plock_async_data *op_data;
    110	struct dlm_ls *ls;
    111	struct plock_op *op;
    112	int rv;
    113
    114	ls = dlm_find_lockspace_local(lockspace);
    115	if (!ls)
    116		return -EINVAL;
    117
    118	op = kzalloc(sizeof(*op), GFP_NOFS);
    119	if (!op) {
    120		rv = -ENOMEM;
    121		goto out;
    122	}
    123
    124	op->info.optype		= DLM_PLOCK_OP_LOCK;
    125	op->info.pid		= fl->fl_pid;
    126	op->info.ex		= (fl->fl_type == F_WRLCK);
    127	op->info.wait		= IS_SETLKW(cmd);
    128	op->info.fsid		= ls->ls_global_id;
    129	op->info.number		= number;
    130	op->info.start		= fl->fl_start;
    131	op->info.end		= fl->fl_end;
    132	/* async handling */
    133	if (fl->fl_lmops && fl->fl_lmops->lm_grant) {
    134		op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
    135		if (!op_data) {
    136			dlm_release_plock_op(op);
    137			rv = -ENOMEM;
    138			goto out;
    139		}
    140
    141		/* fl_owner is lockd which doesn't distinguish
    142		   processes on the nfs client */
    143		op->info.owner	= (__u64) fl->fl_pid;
    144		op_data->callback = fl->fl_lmops->lm_grant;
    145		locks_init_lock(&op_data->flc);
    146		locks_copy_lock(&op_data->flc, fl);
    147		op_data->fl		= fl;
    148		op_data->file	= file;
    149
    150		op->data = op_data;
    151
    152		send_op(op);
    153		rv = FILE_LOCK_DEFERRED;
    154		goto out;
    155	} else {
    156		op->info.owner	= (__u64)(long) fl->fl_owner;
    157	}
    158
    159	send_op(op);
    160
    161	rv = wait_event_interruptible(recv_wq, (op->done != 0));
    162	if (rv == -ERESTARTSYS) {
    163		spin_lock(&ops_lock);
    164		list_del(&op->list);
    165		spin_unlock(&ops_lock);
    166		log_print("%s: wait interrupted %x %llx, op removed",
    167			  __func__, ls->ls_global_id,
    168			  (unsigned long long)number);
    169		dlm_release_plock_op(op);
    170		do_unlock_close(ls, number, file, fl);
    171		goto out;
    172	}
    173
    174	WARN_ON(!list_empty(&op->list));
    175
    176	rv = op->info.rv;
    177
    178	if (!rv) {
    179		if (locks_lock_file_wait(file, fl) < 0)
    180			log_error(ls, "dlm_posix_lock: vfs lock error %llx",
    181				  (unsigned long long)number);
    182	}
    183
    184	dlm_release_plock_op(op);
    185out:
    186	dlm_put_lockspace(ls);
    187	return rv;
    188}
    189EXPORT_SYMBOL_GPL(dlm_posix_lock);
    190
    191/* Returns failure iff a successful lock operation should be canceled */
    192static int dlm_plock_callback(struct plock_op *op)
    193{
    194	struct plock_async_data *op_data = op->data;
    195	struct file *file;
    196	struct file_lock *fl;
    197	struct file_lock *flc;
    198	int (*notify)(struct file_lock *fl, int result) = NULL;
    199	int rv = 0;
    200
    201	WARN_ON(!list_empty(&op->list));
    202
    203	/* check if the following 2 are still valid or make a copy */
    204	file = op_data->file;
    205	flc = &op_data->flc;
    206	fl = op_data->fl;
    207	notify = op_data->callback;
    208
    209	if (op->info.rv) {
    210		notify(fl, op->info.rv);
    211		goto out;
    212	}
    213
    214	/* got fs lock; bookkeep locally as well: */
    215	flc->fl_flags &= ~FL_SLEEP;
    216	if (posix_lock_file(file, flc, NULL)) {
    217		/*
    218		 * This can only happen in the case of kmalloc() failure.
    219		 * The filesystem's own lock is the authoritative lock,
    220		 * so a failure to get the lock locally is not a disaster.
    221		 * As long as the fs cannot reliably cancel locks (especially
    222		 * in a low-memory situation), we're better off ignoring
    223		 * this failure than trying to recover.
    224		 */
    225		log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p",
    226			  (unsigned long long)op->info.number, file, fl);
    227	}
    228
    229	rv = notify(fl, 0);
    230	if (rv) {
    231		/* XXX: We need to cancel the fs lock here: */
    232		log_print("dlm_plock_callback: lock granted after lock request "
    233			  "failed; dangling lock!\n");
    234		goto out;
    235	}
    236
    237out:
    238	dlm_release_plock_op(op);
    239	return rv;
    240}
    241
    242int dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
    243		     struct file_lock *fl)
    244{
    245	struct dlm_ls *ls;
    246	struct plock_op *op;
    247	int rv;
    248	unsigned char fl_flags = fl->fl_flags;
    249
    250	ls = dlm_find_lockspace_local(lockspace);
    251	if (!ls)
    252		return -EINVAL;
    253
    254	op = kzalloc(sizeof(*op), GFP_NOFS);
    255	if (!op) {
    256		rv = -ENOMEM;
    257		goto out;
    258	}
    259
    260	/* cause the vfs unlock to return ENOENT if lock is not found */
    261	fl->fl_flags |= FL_EXISTS;
    262
    263	rv = locks_lock_file_wait(file, fl);
    264	if (rv == -ENOENT) {
    265		rv = 0;
    266		goto out_free;
    267	}
    268	if (rv < 0) {
    269		log_error(ls, "dlm_posix_unlock: vfs unlock error %d %llx",
    270			  rv, (unsigned long long)number);
    271	}
    272
    273	op->info.optype		= DLM_PLOCK_OP_UNLOCK;
    274	op->info.pid		= fl->fl_pid;
    275	op->info.fsid		= ls->ls_global_id;
    276	op->info.number		= number;
    277	op->info.start		= fl->fl_start;
    278	op->info.end		= fl->fl_end;
    279	if (fl->fl_lmops && fl->fl_lmops->lm_grant)
    280		op->info.owner	= (__u64) fl->fl_pid;
    281	else
    282		op->info.owner	= (__u64)(long) fl->fl_owner;
    283
    284	if (fl->fl_flags & FL_CLOSE) {
    285		op->info.flags |= DLM_PLOCK_FL_CLOSE;
    286		send_op(op);
    287		rv = 0;
    288		goto out;
    289	}
    290
    291	send_op(op);
    292	wait_event(recv_wq, (op->done != 0));
    293
    294	WARN_ON(!list_empty(&op->list));
    295
    296	rv = op->info.rv;
    297
    298	if (rv == -ENOENT)
    299		rv = 0;
    300
    301out_free:
    302	dlm_release_plock_op(op);
    303out:
    304	dlm_put_lockspace(ls);
    305	fl->fl_flags = fl_flags;
    306	return rv;
    307}
    308EXPORT_SYMBOL_GPL(dlm_posix_unlock);
    309
    310int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file,
    311		  struct file_lock *fl)
    312{
    313	struct dlm_ls *ls;
    314	struct plock_op *op;
    315	int rv;
    316
    317	ls = dlm_find_lockspace_local(lockspace);
    318	if (!ls)
    319		return -EINVAL;
    320
    321	op = kzalloc(sizeof(*op), GFP_NOFS);
    322	if (!op) {
    323		rv = -ENOMEM;
    324		goto out;
    325	}
    326
    327	op->info.optype		= DLM_PLOCK_OP_GET;
    328	op->info.pid		= fl->fl_pid;
    329	op->info.ex		= (fl->fl_type == F_WRLCK);
    330	op->info.fsid		= ls->ls_global_id;
    331	op->info.number		= number;
    332	op->info.start		= fl->fl_start;
    333	op->info.end		= fl->fl_end;
    334	if (fl->fl_lmops && fl->fl_lmops->lm_grant)
    335		op->info.owner	= (__u64) fl->fl_pid;
    336	else
    337		op->info.owner	= (__u64)(long) fl->fl_owner;
    338
    339	send_op(op);
    340	wait_event(recv_wq, (op->done != 0));
    341
    342	WARN_ON(!list_empty(&op->list));
    343
    344	/* info.rv from userspace is 1 for conflict, 0 for no-conflict,
    345	   -ENOENT if there are no locks on the file */
    346
    347	rv = op->info.rv;
    348
    349	fl->fl_type = F_UNLCK;
    350	if (rv == -ENOENT)
    351		rv = 0;
    352	else if (rv > 0) {
    353		locks_init_lock(fl);
    354		fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
    355		fl->fl_flags = FL_POSIX;
    356		fl->fl_pid = -op->info.pid;
    357		fl->fl_start = op->info.start;
    358		fl->fl_end = op->info.end;
    359		rv = 0;
    360	}
    361
    362	dlm_release_plock_op(op);
    363out:
    364	dlm_put_lockspace(ls);
    365	return rv;
    366}
    367EXPORT_SYMBOL_GPL(dlm_posix_get);
    368
    369/* a read copies out one plock request from the send list */
    370static ssize_t dev_read(struct file *file, char __user *u, size_t count,
    371			loff_t *ppos)
    372{
    373	struct dlm_plock_info info;
    374	struct plock_op *op = NULL;
    375
    376	if (count < sizeof(info))
    377		return -EINVAL;
    378
    379	spin_lock(&ops_lock);
    380	if (!list_empty(&send_list)) {
    381		op = list_entry(send_list.next, struct plock_op, list);
    382		if (op->info.flags & DLM_PLOCK_FL_CLOSE)
    383			list_del(&op->list);
    384		else
    385			list_move(&op->list, &recv_list);
    386		memcpy(&info, &op->info, sizeof(info));
    387	}
    388	spin_unlock(&ops_lock);
    389
    390	if (!op)
    391		return -EAGAIN;
    392
    393	/* there is no need to get a reply from userspace for unlocks
    394	   that were generated by the vfs cleaning up for a close
    395	   (the process did not make an unlock call). */
    396
    397	if (op->info.flags & DLM_PLOCK_FL_CLOSE)
    398		dlm_release_plock_op(op);
    399
    400	if (copy_to_user(u, &info, sizeof(info)))
    401		return -EFAULT;
    402	return sizeof(info);
    403}
    404
    405/* a write copies in one plock result that should match a plock_op
    406   on the recv list */
    407static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
    408			 loff_t *ppos)
    409{
    410	struct plock_op *op = NULL, *iter;
    411	struct dlm_plock_info info;
    412	int do_callback = 0;
    413
    414	if (count != sizeof(info))
    415		return -EINVAL;
    416
    417	if (copy_from_user(&info, u, sizeof(info)))
    418		return -EFAULT;
    419
    420	if (check_version(&info))
    421		return -EINVAL;
    422
    423	spin_lock(&ops_lock);
    424	list_for_each_entry(iter, &recv_list, list) {
    425		if (iter->info.fsid == info.fsid &&
    426		    iter->info.number == info.number &&
    427		    iter->info.owner == info.owner) {
    428			list_del_init(&iter->list);
    429			memcpy(&iter->info, &info, sizeof(info));
    430			if (iter->data)
    431				do_callback = 1;
    432			else
    433				iter->done = 1;
    434			op = iter;
    435			break;
    436		}
    437	}
    438	spin_unlock(&ops_lock);
    439
    440	if (op) {
    441		if (do_callback)
    442			dlm_plock_callback(op);
    443		else
    444			wake_up(&recv_wq);
    445	} else
    446		log_print("%s: no op %x %llx - may got interrupted?", __func__,
    447			  info.fsid, (unsigned long long)info.number);
    448	return count;
    449}
    450
    451static __poll_t dev_poll(struct file *file, poll_table *wait)
    452{
    453	__poll_t mask = 0;
    454
    455	poll_wait(file, &send_wq, wait);
    456
    457	spin_lock(&ops_lock);
    458	if (!list_empty(&send_list))
    459		mask = EPOLLIN | EPOLLRDNORM;
    460	spin_unlock(&ops_lock);
    461
    462	return mask;
    463}
    464
    465static const struct file_operations dev_fops = {
    466	.read    = dev_read,
    467	.write   = dev_write,
    468	.poll    = dev_poll,
    469	.owner   = THIS_MODULE,
    470	.llseek  = noop_llseek,
    471};
    472
    473static struct miscdevice plock_dev_misc = {
    474	.minor = MISC_DYNAMIC_MINOR,
    475	.name = DLM_PLOCK_MISC_NAME,
    476	.fops = &dev_fops
    477};
    478
    479int dlm_plock_init(void)
    480{
    481	int rv;
    482
    483	rv = misc_register(&plock_dev_misc);
    484	if (rv)
    485		log_print("dlm_plock_init: misc_register failed %d", rv);
    486	return rv;
    487}
    488
    489void dlm_plock_exit(void)
    490{
    491	misc_deregister(&plock_dev_misc);
    492}
    493