ceph: allocate and parse mount args before client instance

This simplifies much of the error handling during mount.  It also means
that we have the mount args before client creation, and we can initialize
based on those options.

Signed-off-by: Sage Weil <sage@newdream.net>
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index c7d673f..bf53581 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -600,8 +600,8 @@
 		pr_warning("writepage_start %p on forced umount\n", inode);
 		return -EIO; /* we're in a forced umount, don't write! */
 	}
-	if (client->mount_args.wsize && client->mount_args.wsize < wsize)
-		wsize = client->mount_args.wsize;
+	if (client->mount_args->wsize && client->mount_args->wsize < wsize)
+		wsize = client->mount_args->wsize;
 	if (wsize < PAGE_CACHE_SIZE)
 		wsize = PAGE_CACHE_SIZE;
 	max_pages_ever = wsize >> PAGE_CACHE_SHIFT;
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 7d16618..8b863db 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -270,7 +270,7 @@
 	 * lots of free/alloc churn.
 	 */
 	if (caps_avail_count >= caps_reserve_count +
-	    ceph_client(cap->ci->vfs_inode.i_sb)->mount_args.max_readdir) {
+	    ceph_client(cap->ci->vfs_inode.i_sb)->mount_args->max_readdir) {
 		caps_total_count--;
 		kmem_cache_free(ceph_cap_cachep, cap);
 	} else {
@@ -388,7 +388,7 @@
 static void __cap_set_timeouts(struct ceph_mds_client *mdsc,
 			       struct ceph_inode_info *ci)
 {
-	struct ceph_mount_args *ma = &mdsc->client->mount_args;
+	struct ceph_mount_args *ma = mdsc->client->mount_args;
 
 	ci->i_hold_caps_min = round_jiffies(jiffies +
 					    ma->caps_wanted_delay_min * HZ);
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 7bb8db5..4f74679 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -225,7 +225,7 @@
 	int err;
 	u32 ftype;
 	struct ceph_mds_reply_info_parsed *rinfo;
-	const int max_entries = client->mount_args.max_readdir;
+	const int max_entries = client->mount_args->max_readdir;
 
 	dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
 	if (fi->at_end)
@@ -479,7 +479,8 @@
 	/* .snap dir? */
 	if (err == -ENOENT &&
 	    ceph_vino(parent).ino != CEPH_INO_ROOT && /* no .snap in root dir */
-	    strcmp(dentry->d_name.name, client->mount_args.snapdir_name) == 0) {
+	    strcmp(dentry->d_name.name,
+		   client->mount_args->snapdir_name) == 0) {
 		struct inode *inode = ceph_get_snapdir(parent);
 		dout("ENOENT on snapdir %p '%.*s', linking to snapdir %p\n",
 		     dentry, dentry->d_name.len, dentry->d_name.name, inode);
@@ -550,7 +551,7 @@
 		spin_lock(&dir->i_lock);
 		dout(" dir %p flags are %d\n", dir, ci->i_ceph_flags);
 		if (strncmp(dentry->d_name.name,
-			    client->mount_args.snapdir_name,
+			    client->mount_args->snapdir_name,
 			    dentry->d_name.len) &&
 		    (ci->i_ceph_flags & CEPH_I_COMPLETE) &&
 		    (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 12d66c0..210cb66 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -943,7 +943,7 @@
 	int err = -ENOMEM;
 
 	if (extra < 0)
-		extra = mdsc->client->mount_args.cap_release_safety;
+		extra = mdsc->client->mount_args->cap_release_safety;
 
 	spin_lock(&session->s_cap_lock);
 
@@ -2601,7 +2601,7 @@
 		mutex_unlock(&mdsc->mutex);
 		dout("wait_requests waiting for requests\n");
 		wait_for_completion_timeout(&mdsc->safe_umount_waiters,
-				    client->mount_args.mount_timeout * HZ);
+				    client->mount_args->mount_timeout * HZ);
 		mutex_lock(&mdsc->mutex);
 
 		/* tear down remaining requests */
@@ -2693,7 +2693,7 @@
 	int i;
 	int n;
 	struct ceph_client *client = mdsc->client;
-	unsigned long started, timeout = client->mount_args.mount_timeout * HZ;
+	unsigned long started, timeout = client->mount_args->mount_timeout * HZ;
 
 	dout("close_sessions\n");
 
diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c
index e6e954c..61263c9 100644
--- a/fs/ceph/mon_client.c
+++ b/fs/ceph/mon_client.c
@@ -527,6 +527,40 @@
 	mutex_unlock(&monc->mutex);
 }
 
+/*
+ * On startup, we build a temporary monmap populated with the IPs
+ * provided by mount(2).
+ */
+static int build_initial_monmap(struct ceph_mon_client *monc)
+{
+	struct ceph_mount_args *args = monc->client->mount_args;
+	struct ceph_entity_addr *mon_addr = args->mon_addr;
+	int num_mon = args->num_mon;
+	int i;
+
+	/* build initial monmap */
+	monc->monmap = kzalloc(sizeof(*monc->monmap) +
+			       num_mon*sizeof(monc->monmap->mon_inst[0]),
+			       GFP_KERNEL);
+	if (!monc->monmap)
+		return -ENOMEM;
+	for (i = 0; i < num_mon; i++) {
+		monc->monmap->mon_inst[i].addr = mon_addr[i];
+		monc->monmap->mon_inst[i].addr.erank = 0;
+		monc->monmap->mon_inst[i].addr.nonce = 0;
+		monc->monmap->mon_inst[i].name.type =
+			CEPH_ENTITY_TYPE_MON;
+		monc->monmap->mon_inst[i].name.num = cpu_to_le64(i);
+	}
+	monc->monmap->num_mon = num_mon;
+
+	/* release addr memory */
+	kfree(args->mon_addr);
+	args->mon_addr = NULL;
+	args->num_mon = 0;
+	return 0;
+}
+
 int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl)
 {
 	int err = 0;
@@ -537,6 +571,10 @@
 	monc->monmap = NULL;
 	mutex_init(&monc->mutex);
 
+	err = build_initial_monmap(monc);
+	if (err)
+		goto out;
+
 	monc->con = NULL;
 
 	/* msg pools */
diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c
index 0a25405..7dc0f62 100644
--- a/fs/ceph/osd_client.c
+++ b/fs/ceph/osd_client.c
@@ -444,7 +444,7 @@
 	osdc->num_requests++;
 
 	req->r_timeout_stamp =
-		jiffies + osdc->client->mount_args.osd_timeout*HZ;
+		jiffies + osdc->client->mount_args->osd_timeout*HZ;
 
 	if (osdc->num_requests == 1) {
 		osdc->timeout_tid = req->r_tid;
@@ -609,7 +609,7 @@
 	reqhead->flags |= cpu_to_le32(req->r_flags);  /* e.g., RETRY */
 	reqhead->reassert_version = req->r_reassert_version;
 
-	req->r_timeout_stamp = jiffies+osdc->client->mount_args.osd_timeout*HZ;
+	req->r_timeout_stamp = jiffies+osdc->client->mount_args->osd_timeout*HZ;
 
 	ceph_msg_get(req->r_request); /* send consumes a ref */
 	ceph_con_send(&req->r_osd->o_con, req->r_request);
@@ -632,7 +632,7 @@
 		container_of(work, struct ceph_osd_client, timeout_work.work);
 	struct ceph_osd_request *req;
 	struct ceph_osd *osd;
-	unsigned long timeout = osdc->client->mount_args.osd_timeout * HZ;
+	unsigned long timeout = osdc->client->mount_args->osd_timeout * HZ;
 	unsigned long next_timeout = timeout + jiffies;
 	struct rb_node *p;
 
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index b094f50..9b7815d 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -110,7 +110,7 @@
 static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt)
 {
 	struct ceph_client *client = ceph_sb_to_client(mnt->mnt_sb);
-	struct ceph_mount_args *args = &client->mount_args;
+	struct ceph_mount_args *args = client->mount_args;
 
 	if (args->flags & CEPH_OPT_FSID)
 		seq_printf(m, ",fsidmajor=%llu,fsidminor%llu",
@@ -307,24 +307,24 @@
 };
 
 
-static int parse_mount_args(struct ceph_client *client,
-			    int flags, char *options, const char *dev_name,
-			    const char **path)
+static struct ceph_mount_args *parse_mount_args(int flags, char *options,
+						const char *dev_name,
+						const char **path)
 {
-	struct ceph_mount_args *args = &client->mount_args;
+	struct ceph_mount_args *args;
 	const char *c;
-	int err;
+	int err = -ENOMEM;
 	substring_t argstr[MAX_OPT_ARGS];
-	int num_mon;
-	struct ceph_entity_addr *mon_addr;
-	int i;
 
-	dout("parse_mount_args dev_name '%s'\n", dev_name);
-	memset(args, 0, sizeof(*args));
+	args = kzalloc(sizeof(*args), GFP_KERNEL);
+	if (!args)
+		return ERR_PTR(-ENOMEM);
+	args->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*args->mon_addr),
+				 GFP_KERNEL);
+	if (!args->mon_addr)
+		goto out;
 
-	mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*mon_addr), GFP_KERNEL);
-	if (!mon_addr)
-		return -ENOMEM;
+	dout("parse_mount_args %p, dev_name '%s'\n", args, dev_name);
 
 	/* start with defaults */
 	args->sb_flags = flags;
@@ -350,29 +350,11 @@
 	}
 
 	/* get mon ip(s) */
-	err = ceph_parse_ips(dev_name, *path, mon_addr,
-			     CEPH_MAX_MON, &num_mon);
+	err = ceph_parse_ips(dev_name, *path, args->mon_addr,
+			     CEPH_MAX_MON, &args->num_mon);
 	if (err < 0)
 		goto out;
 
-	/* build initial monmap */
-	err = -ENOMEM;
-	client->monc.monmap = kzalloc(sizeof(*client->monc.monmap) +
-			       num_mon*sizeof(client->monc.monmap->mon_inst[0]),
-			       GFP_KERNEL);
-	if (!client->monc.monmap)
-		goto out;
-	for (i = 0; i < num_mon; i++) {
-		client->monc.monmap->mon_inst[i].addr = mon_addr[i];
-		client->monc.monmap->mon_inst[i].addr.erank = 0;
-		client->monc.monmap->mon_inst[i].addr.nonce = 0;
-		client->monc.monmap->mon_inst[i].name.type =
-			CEPH_ENTITY_TYPE_MON;
-		client->monc.monmap->mon_inst[i].name.num = cpu_to_le64(i);
-	}
-	client->monc.monmap->num_mon = num_mon;
-	memset(&args->my_addr.in_addr, 0, sizeof(args->my_addr.in_addr));
-
 	/* path on server */
 	*path += 2;
 	dout("server path '%s'\n", *path);
@@ -415,7 +397,7 @@
 					     &args->my_addr,
 					     1, NULL);
 			if (err < 0)
-				return err;
+				goto out;
 			args->flags |= CEPH_OPT_MYIP;
 			break;
 
@@ -481,25 +463,28 @@
 			BUG_ON(token);
 		}
 	}
-	err = 0;
+	return args;
 
 out:
-	kfree(mon_addr);
-	return err;
+	kfree(args->mon_addr);
+	kfree(args);
+	return ERR_PTR(err);
 }
 
-static void release_mount_args(struct ceph_mount_args *args)
+static void destroy_mount_args(struct ceph_mount_args *args)
 {
+	dout("destroy_mount_args %p\n", args);
 	kfree(args->snapdir_name);
 	args->snapdir_name = NULL;
 	kfree(args->secret);
 	args->secret = NULL;
+	kfree(args);
 }
 
 /*
  * create a fresh client instance
  */
-static struct ceph_client *ceph_create_client(void)
+static struct ceph_client *ceph_create_client(struct ceph_mount_args *args)
 {
 	struct ceph_client *client;
 	int err = -ENOMEM;
@@ -515,6 +500,7 @@
 	client->sb = NULL;
 	client->mount_state = CEPH_MOUNT_MOUNTING;
 	client->whoami = -1;
+	client->mount_args = args;
 
 	client->msgr = NULL;
 
@@ -577,7 +563,7 @@
 	if (client->wb_pagevec_pool)
 		mempool_destroy(client->wb_pagevec_pool);
 
-	release_mount_args(&client->mount_args);
+	destroy_mount_args(client->mount_args);
 
 	kfree(client);
 	dout("destroy_client %p done\n", client);
@@ -613,7 +599,7 @@
 	req->r_ino1.ino = CEPH_INO_ROOT;
 	req->r_ino1.snap = CEPH_NOSNAP;
 	req->r_started = started;
-	req->r_timeout = client->mount_args.mount_timeout * HZ;
+	req->r_timeout = client->mount_args->mount_timeout * HZ;
 	req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
 	req->r_num_caps = 2;
 	err = ceph_mdsc_do_request(mdsc, NULL, req);
@@ -641,7 +627,7 @@
 {
 	struct ceph_entity_addr *myaddr = NULL;
 	int err;
-	unsigned long timeout = client->mount_args.mount_timeout * HZ;
+	unsigned long timeout = client->mount_args->mount_timeout * HZ;
 	unsigned long started = jiffies;  /* note the start time */
 	struct dentry *root;
 
@@ -651,7 +637,7 @@
 	/* initialize the messenger */
 	if (client->msgr == NULL) {
 		if (ceph_test_opt(client, MYIP))
-			myaddr = &client->mount_args.my_addr;
+			myaddr = &client->mount_args->my_addr;
 		client->msgr = ceph_messenger_create(myaddr);
 		if (IS_ERR(client->msgr)) {
 			err = PTR_ERR(client->msgr);
@@ -727,7 +713,7 @@
 
 	dout("set_super %p data %p\n", s, data);
 
-	s->s_flags = client->mount_args.sb_flags;
+	s->s_flags = client->mount_args->sb_flags;
 	s->s_maxbytes = 1ULL << 40;  /* temp value until we get mdsmap */
 
 	s->s_fs_info = client;
@@ -756,7 +742,7 @@
 static int ceph_compare_super(struct super_block *sb, void *data)
 {
 	struct ceph_client *new = data;
-	struct ceph_mount_args *args = &new->mount_args;
+	struct ceph_mount_args *args = new->mount_args;
 	struct ceph_client *other = ceph_sb_to_client(sb);
 	int i;
 
@@ -778,7 +764,7 @@
 		}
 		dout("mon ip matches existing sb %p\n", sb);
 	}
-	if (args->sb_flags != other->mount_args.sb_flags) {
+	if (args->sb_flags != other->mount_args->sb_flags) {
 		dout("flags differ\n");
 		return 0;
 	}
@@ -798,9 +784,9 @@
 	sb->s_bdi = &client->backing_dev_info;
 
 	/* set ra_pages based on rsize mount option? */
-	if (client->mount_args.rsize >= PAGE_CACHE_SIZE)
+	if (client->mount_args->rsize >= PAGE_CACHE_SIZE)
 		client->backing_dev_info.ra_pages =
-			(client->mount_args.rsize + PAGE_CACHE_SIZE - 1)
+			(client->mount_args->rsize + PAGE_CACHE_SIZE - 1)
 			>> PAGE_SHIFT;
 
 	err = bdi_register_dev(&client->backing_dev_info, sb->s_dev);
@@ -816,19 +802,23 @@
 	int err;
 	int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
 	const char *path = 0;
+	struct ceph_mount_args *args;
 
 	dout("ceph_get_sb\n");
+	args = parse_mount_args(flags, data, dev_name, &path);
+	if (IS_ERR(args)) {
+		err = PTR_ERR(args);
+		goto out_final;
+	}
 
 	/* create client (which we may/may not use) */
-	client = ceph_create_client();
-	if (IS_ERR(client))
-		return PTR_ERR(client);
+	client = ceph_create_client(args);
+	if (IS_ERR(client)) {
+		err = PTR_ERR(client);
+		goto out_final;
+	}
 
-	err = parse_mount_args(client, flags, data, dev_name, &path);
-	if (err < 0)
-		goto out;
-
-	if (client->mount_args.flags & CEPH_OPT_NOSHARE)
+	if (client->mount_args->flags & CEPH_OPT_NOSHARE)
 		compare_super = NULL;
 	sb = sget(fs_type, compare_super, ceph_set_super, client);
 	if (IS_ERR(sb)) {
@@ -846,7 +836,7 @@
 		/* set up mempools */
 		err = -ENOMEM;
 		client->wb_pagevec_pool = mempool_create_kmalloc_pool(10,
-			      client->mount_args.wsize >> PAGE_CACHE_SHIFT);
+			      client->mount_args->wsize >> PAGE_CACHE_SHIFT);
 		if (!client->wb_pagevec_pool)
 			goto out_splat;
 
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 3af42d9..a3d4943 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -42,13 +42,15 @@
 #define CEPH_OPT_DEFAULT   (CEPH_OPT_RBYTES)
 
 #define ceph_set_opt(client, opt) \
-	(client)->mount_args.flags |= CEPH_OPT_##opt;
+	(client)->mount_args->flags |= CEPH_OPT_##opt;
 #define ceph_test_opt(client, opt) \
-	(!!((client)->mount_args.flags & CEPH_OPT_##opt))
+	(!!((client)->mount_args->flags & CEPH_OPT_##opt))
 
 
 struct ceph_mount_args {
 	int sb_flags;
+	int num_mon;
+	struct ceph_entity_addr *mon_addr;
 	int flags;
 	int mount_timeout;
 	int caps_wanted_delay_min, caps_wanted_delay_max;
@@ -115,7 +117,7 @@
 	struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps;
 
 	struct mutex mount_mutex;       /* serialize mount attempts */
-	struct ceph_mount_args mount_args;
+	struct ceph_mount_args *mount_args;
 	struct ceph_fsid fsid;
 
 	struct super_block *sb;