5192 lines · 141.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/ceph/ceph_debug.h> #include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched/signal.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/wait.h>#include <linux/writeback.h>#include <linux/iversion.h>#include <linux/filelock.h>#include <linux/jiffies.h> #include "super.h"#include "mds_client.h"#include "cache.h"#include "crypto.h"#include <linux/ceph/decode.h>#include <linux/ceph/messenger.h>#include <trace/events/ceph.h> /* * Capability management * * The Ceph metadata servers control client access to inode metadata * and file data by issuing capabilities, granting clients permission * to read and/or write both inode field and file data to OSDs * (storage nodes).  Each capability consists of a set of bits * indicating which operations are allowed. * * If the client holds a *_SHARED cap, the client has a coherent value * that can be safely read from the cached inode. * * In the case of a *_EXCL (exclusive) or FILE_WR capabilities, the * client is allowed to change inode attributes (e.g., file size, * mtime), note its dirty state in the ceph_cap, and asynchronously * flush that metadata change to the MDS. * * In the event of a conflicting operation (perhaps by another * client), the MDS will revoke the conflicting client capabilities. * * In order for a client to cache an inode, it must hold a capability * with at least one MDS server.  When inodes are released, release * notifications are batched and periodically sent en masse to the MDS * cluster to release server state. */ static u64 __get_oldest_flush_tid(struct ceph_mds_client *mdsc);static void __kick_flushing_caps(struct ceph_mds_client *mdsc,				 struct ceph_mds_session *session,				 struct ceph_inode_info *ci,				 u64 oldest_flush_tid); /* * Generate readable cap strings for debugging output. */#define MAX_CAP_STR 20static char cap_str[MAX_CAP_STR][40];static DEFINE_SPINLOCK(cap_str_lock);static int last_cap_str; static char *gcap_string(char *s, int c){	if (c & CEPH_CAP_GSHARED)		*s++ = 's';	if (c & CEPH_CAP_GEXCL)		*s++ = 'x';	if (c & CEPH_CAP_GCACHE)		*s++ = 'c';	if (c & CEPH_CAP_GRD)		*s++ = 'r';	if (c & CEPH_CAP_GWR)		*s++ = 'w';	if (c & CEPH_CAP_GBUFFER)		*s++ = 'b';	if (c & CEPH_CAP_GWREXTEND)		*s++ = 'a';	if (c & CEPH_CAP_GLAZYIO)		*s++ = 'l';	return s;} const char *ceph_cap_string(int caps){	int i;	char *s;	int c; 	spin_lock(&cap_str_lock);	i = last_cap_str++;	if (last_cap_str == MAX_CAP_STR)		last_cap_str = 0;	spin_unlock(&cap_str_lock); 	s = cap_str[i]; 	if (caps & CEPH_CAP_PIN)		*s++ = 'p'; 	c = (caps >> CEPH_CAP_SAUTH) & 3;	if (c) {		*s++ = 'A';		s = gcap_string(s, c);	} 	c = (caps >> CEPH_CAP_SLINK) & 3;	if (c) {		*s++ = 'L';		s = gcap_string(s, c);	} 	c = (caps >> CEPH_CAP_SXATTR) & 3;	if (c) {		*s++ = 'X';		s = gcap_string(s, c);	} 	c = caps >> CEPH_CAP_SFILE;	if (c) {		*s++ = 'F';		s = gcap_string(s, c);	} 	if (s == cap_str[i])		*s++ = '-';	*s = 0;	return cap_str[i];} void ceph_caps_init(struct ceph_mds_client *mdsc){	INIT_LIST_HEAD(&mdsc->caps_list);	spin_lock_init(&mdsc->caps_list_lock);} void ceph_caps_finalize(struct ceph_mds_client *mdsc){	struct ceph_cap *cap; 	spin_lock(&mdsc->caps_list_lock);	while (!list_empty(&mdsc->caps_list)) {		cap = list_first_entry(&mdsc->caps_list,				       struct ceph_cap, caps_item);		list_del(&cap->caps_item);		kmem_cache_free(ceph_cap_cachep, cap);	}	mdsc->caps_total_count = 0;	mdsc->caps_avail_count = 0;	mdsc->caps_use_count = 0;	mdsc->caps_reserve_count = 0;	mdsc->caps_min_count = 0;	spin_unlock(&mdsc->caps_list_lock);} void ceph_adjust_caps_max_min(struct ceph_mds_client *mdsc,			      struct ceph_mount_options *fsopt){	spin_lock(&mdsc->caps_list_lock);	mdsc->caps_min_count = fsopt->max_readdir;	if (mdsc->caps_min_count < 1024)		mdsc->caps_min_count = 1024;	mdsc->caps_use_max = fsopt->caps_max;	if (mdsc->caps_use_max > 0 &&	    mdsc->caps_use_max < mdsc->caps_min_count)		mdsc->caps_use_max = mdsc->caps_min_count;	spin_unlock(&mdsc->caps_list_lock);} static void __ceph_unreserve_caps(struct ceph_mds_client *mdsc, int nr_caps){	struct ceph_cap *cap;	int i; 	if (nr_caps) {		BUG_ON(mdsc->caps_reserve_count < nr_caps);		mdsc->caps_reserve_count -= nr_caps;		if (mdsc->caps_avail_count >=		    mdsc->caps_reserve_count + mdsc->caps_min_count) {			mdsc->caps_total_count -= nr_caps;			for (i = 0; i < nr_caps; i++) {				cap = list_first_entry(&mdsc->caps_list,					struct ceph_cap, caps_item);				list_del(&cap->caps_item);				kmem_cache_free(ceph_cap_cachep, cap);			}		} else {			mdsc->caps_avail_count += nr_caps;		} 		doutc(mdsc->fsc->client,		      "caps %d = %d used + %d resv + %d avail\n",		      mdsc->caps_total_count, mdsc->caps_use_count,		      mdsc->caps_reserve_count, mdsc->caps_avail_count);		BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +						 mdsc->caps_reserve_count +						 mdsc->caps_avail_count);	}} /* * Called under mdsc->mutex. */int ceph_reserve_caps(struct ceph_mds_client *mdsc,		      struct ceph_cap_reservation *ctx, int need){	struct ceph_client *cl = mdsc->fsc->client;	int i, j;	struct ceph_cap *cap;	int have;	int alloc = 0;	int max_caps;	int err = 0;	bool trimmed = false;	struct ceph_mds_session *s;	LIST_HEAD(newcaps); 	doutc(cl, "ctx=%p need=%d\n", ctx, need); 	/* first reserve any caps that are already allocated */	spin_lock(&mdsc->caps_list_lock);	if (mdsc->caps_avail_count >= need)		have = need;	else		have = mdsc->caps_avail_count;	mdsc->caps_avail_count -= have;	mdsc->caps_reserve_count += have;	BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +					 mdsc->caps_reserve_count +					 mdsc->caps_avail_count);	spin_unlock(&mdsc->caps_list_lock); 	for (i = have; i < need; ) {		cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS);		if (cap) {			list_add(&cap->caps_item, &newcaps);			alloc++;			i++;			continue;		} 		if (!trimmed) {			for (j = 0; j < mdsc->max_sessions; j++) {				s = __ceph_lookup_mds_session(mdsc, j);				if (!s)					continue;				mutex_unlock(&mdsc->mutex); 				mutex_lock(&s->s_mutex);				max_caps = s->s_nr_caps - (need - i);				ceph_trim_caps(mdsc, s, max_caps);				mutex_unlock(&s->s_mutex); 				ceph_put_mds_session(s);				mutex_lock(&mdsc->mutex);			}			trimmed = true; 			spin_lock(&mdsc->caps_list_lock);			if (mdsc->caps_avail_count) {				int more_have;				if (mdsc->caps_avail_count >= need - i)					more_have = need - i;				else					more_have = mdsc->caps_avail_count; 				i += more_have;				have += more_have;				mdsc->caps_avail_count -= more_have;				mdsc->caps_reserve_count += more_have; 			}			spin_unlock(&mdsc->caps_list_lock); 			continue;		} 		pr_warn_client(cl, "ctx=%p ENOMEM need=%d got=%d\n", ctx, need,			       have + alloc);		err = -ENOMEM;		break;	} 	if (!err) {		BUG_ON(have + alloc != need);		ctx->count = need;		ctx->used = 0;	} 	spin_lock(&mdsc->caps_list_lock);	mdsc->caps_total_count += alloc;	mdsc->caps_reserve_count += alloc;	list_splice(&newcaps, &mdsc->caps_list); 	BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +					 mdsc->caps_reserve_count +					 mdsc->caps_avail_count); 	if (err)		__ceph_unreserve_caps(mdsc, have + alloc); 	spin_unlock(&mdsc->caps_list_lock); 	doutc(cl, "ctx=%p %d = %d used + %d resv + %d avail\n", ctx,	      mdsc->caps_total_count, mdsc->caps_use_count,	      mdsc->caps_reserve_count, mdsc->caps_avail_count);	return err;} void ceph_unreserve_caps(struct ceph_mds_client *mdsc,			 struct ceph_cap_reservation *ctx){	struct ceph_client *cl = mdsc->fsc->client;	bool reclaim = false;	if (!ctx->count)		return; 	doutc(cl, "ctx=%p count=%d\n", ctx, ctx->count);	spin_lock(&mdsc->caps_list_lock);	__ceph_unreserve_caps(mdsc, ctx->count);	ctx->count = 0; 	if (mdsc->caps_use_max > 0 &&	    mdsc->caps_use_count > mdsc->caps_use_max)		reclaim = true;	spin_unlock(&mdsc->caps_list_lock); 	if (reclaim)		ceph_reclaim_caps_nr(mdsc, ctx->used);} struct ceph_cap *ceph_get_cap(struct ceph_mds_client *mdsc,			      struct ceph_cap_reservation *ctx){	struct ceph_client *cl = mdsc->fsc->client;	struct ceph_cap *cap = NULL; 	/* temporary, until we do something about cap import/export */	if (!ctx) {		cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS);		if (cap) {			spin_lock(&mdsc->caps_list_lock);			mdsc->caps_use_count++;			mdsc->caps_total_count++;			spin_unlock(&mdsc->caps_list_lock);		} else {			spin_lock(&mdsc->caps_list_lock);			if (mdsc->caps_avail_count) {				BUG_ON(list_empty(&mdsc->caps_list)); 				mdsc->caps_avail_count--;				mdsc->caps_use_count++;				cap = list_first_entry(&mdsc->caps_list,						struct ceph_cap, caps_item);				list_del(&cap->caps_item); 				BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +				       mdsc->caps_reserve_count + mdsc->caps_avail_count);			}			spin_unlock(&mdsc->caps_list_lock);		} 		return cap;	} 	spin_lock(&mdsc->caps_list_lock);	doutc(cl, "ctx=%p (%d) %d = %d used + %d resv + %d avail\n", ctx,	      ctx->count, mdsc->caps_total_count, mdsc->caps_use_count,	      mdsc->caps_reserve_count, mdsc->caps_avail_count);	BUG_ON(!ctx->count);	BUG_ON(ctx->count > mdsc->caps_reserve_count);	BUG_ON(list_empty(&mdsc->caps_list)); 	ctx->count--;	ctx->used++;	mdsc->caps_reserve_count--;	mdsc->caps_use_count++; 	cap = list_first_entry(&mdsc->caps_list, struct ceph_cap, caps_item);	list_del(&cap->caps_item); 	BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +	       mdsc->caps_reserve_count + mdsc->caps_avail_count);	spin_unlock(&mdsc->caps_list_lock);	return cap;} void ceph_put_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap){	struct ceph_client *cl = mdsc->fsc->client; 	spin_lock(&mdsc->caps_list_lock);	doutc(cl, "%p %d = %d used + %d resv + %d avail\n", cap,	      mdsc->caps_total_count, mdsc->caps_use_count,	      mdsc->caps_reserve_count, mdsc->caps_avail_count);	mdsc->caps_use_count--;	/*	 * Keep some preallocated caps around (ceph_min_count), to	 * avoid lots of free/alloc churn.	 */	if (mdsc->caps_avail_count >= mdsc->caps_reserve_count +				      mdsc->caps_min_count) {		mdsc->caps_total_count--;		kmem_cache_free(ceph_cap_cachep, cap);	} else {		mdsc->caps_avail_count++;		list_add(&cap->caps_item, &mdsc->caps_list);	} 	BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +	       mdsc->caps_reserve_count + mdsc->caps_avail_count);	spin_unlock(&mdsc->caps_list_lock);} void ceph_reservation_status(struct ceph_fs_client *fsc,			     int *total, int *avail, int *used, int *reserved,			     int *min){	struct ceph_mds_client *mdsc = fsc->mdsc; 	spin_lock(&mdsc->caps_list_lock); 	if (total)		*total = mdsc->caps_total_count;	if (avail)		*avail = mdsc->caps_avail_count;	if (used)		*used = mdsc->caps_use_count;	if (reserved)		*reserved = mdsc->caps_reserve_count;	if (min)		*min = mdsc->caps_min_count; 	spin_unlock(&mdsc->caps_list_lock);} /* * Find ceph_cap for given mds, if any. * * Called with i_ceph_lock held. */struct ceph_cap *__get_cap_for_mds(struct ceph_inode_info *ci, int mds){	struct ceph_cap *cap;	struct rb_node *n = ci->i_caps.rb_node; 	while (n) {		cap = rb_entry(n, struct ceph_cap, ci_node);		if (mds < cap->mds)			n = n->rb_left;		else if (mds > cap->mds)			n = n->rb_right;		else			return cap;	}	return NULL;} struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, int mds){	struct ceph_cap *cap; 	spin_lock(&ci->i_ceph_lock);	cap = __get_cap_for_mds(ci, mds);	spin_unlock(&ci->i_ceph_lock);	return cap;} /* * Called under i_ceph_lock. */static void __insert_cap_node(struct ceph_inode_info *ci,			      struct ceph_cap *new){	struct rb_node **p = &ci->i_caps.rb_node;	struct rb_node *parent = NULL;	struct ceph_cap *cap = NULL; 	while (*p) {		parent = *p;		cap = rb_entry(parent, struct ceph_cap, ci_node);		if (new->mds < cap->mds)			p = &(*p)->rb_left;		else if (new->mds > cap->mds)			p = &(*p)->rb_right;		else			BUG();	} 	rb_link_node(&new->ci_node, parent, p);	rb_insert_color(&new->ci_node, &ci->i_caps);} /* * (re)set cap hold timeouts, which control the delayed release * of unused caps back to the MDS.  Should be called on cap use. */static void __cap_set_timeouts(struct ceph_mds_client *mdsc,			       struct ceph_inode_info *ci){

Lines 1–500 of 5192.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t