Date: Thu, 30 Jul 2015 19:52:44 +0000 (UTC) From: Mateusz Guzik <mjg@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r286094 - in head/sys: kern sys Message-ID: <201507301952.t6UJqiPK003032@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mjg Date: Thu Jul 30 19:52:43 2015 New Revision: 286094 URL: https://svnweb.freebsd.org/changeset/base/286094 Log: Revert r285125 until rmlocks get fixed. Right now there is a chance that sysctl unregister will cause reader to block on the sx lock associated with sysctl rmlock, in which case kernels with debug enabled will panic. Modified: head/sys/kern/kern_linker.c head/sys/kern/kern_sysctl.c head/sys/kern/vfs_init.c head/sys/sys/sysctl.h Modified: head/sys/kern/kern_linker.c ============================================================================== --- head/sys/kern/kern_linker.c Thu Jul 30 19:44:46 2015 (r286093) +++ head/sys/kern/kern_linker.c Thu Jul 30 19:52:43 2015 (r286094) @@ -292,10 +292,10 @@ linker_file_register_sysctls(linker_file return; sx_xunlock(&kld_sx); - sysctl_wlock(); + sysctl_xlock(); for (oidp = start; oidp < stop; oidp++) sysctl_register_oid(*oidp); - sysctl_wunlock(); + sysctl_xunlock(); sx_xlock(&kld_sx); } @@ -313,10 +313,10 @@ linker_file_unregister_sysctls(linker_fi return; sx_xunlock(&kld_sx); - sysctl_wlock(); + sysctl_xlock(); for (oidp = start; oidp < stop; oidp++) sysctl_unregister_oid(*oidp); - sysctl_wunlock(); + sysctl_xunlock(); sx_xlock(&kld_sx); } Modified: head/sys/kern/kern_sysctl.c ============================================================================== --- head/sys/kern/kern_sysctl.c Thu Jul 30 19:44:46 2015 (r286093) +++ head/sys/kern/kern_sysctl.c Thu Jul 30 19:52:43 2015 (r286094) @@ -54,7 +54,6 @@ __FBSDID("$FreeBSD$"); #include <sys/jail.h> #include <sys/lock.h> #include <sys/mutex.h> -#include <sys/rmlock.h> #include <sys/sbuf.h> #include <sys/sx.h> #include <sys/sysproto.h> @@ -78,7 +77,7 @@ static MALLOC_DEFINE(M_SYSCTLTMP, "sysct * The sysctllock protects the MIB tree. It also protects sysctl * contexts used with dynamic sysctls. The sysctl_register_oid() and * sysctl_unregister_oid() routines require the sysctllock to already - * be held, so the sysctl_wlock() and sysctl_wunlock() routines are + * be held, so the sysctl_xlock() and sysctl_xunlock() routines are * provided for the few places in the kernel which need to use that * API rather than using the dynamic API. Use of the dynamic API is * strongly encouraged for most code. @@ -87,21 +86,20 @@ static MALLOC_DEFINE(M_SYSCTLTMP, "sysct * sysctl requests. This is implemented by serializing any userland * sysctl requests larger than a single page via an exclusive lock. */ -static struct rmlock sysctllock; +static struct sx sysctllock; static struct sx sysctlmemlock; -#define SYSCTL_WLOCK() rm_wlock(&sysctllock) -#define SYSCTL_WUNLOCK() rm_wunlock(&sysctllock) -#define SYSCTL_RLOCK(tracker) rm_rlock(&sysctllock, (tracker)) -#define SYSCTL_RUNLOCK(tracker) rm_runlock(&sysctllock, (tracker)) -#define SYSCTL_WLOCKED() rm_wowned(&sysctllock) -#define SYSCTL_ASSERT_LOCKED() rm_assert(&sysctllock, RA_LOCKED) -#define SYSCTL_ASSERT_WLOCKED() rm_assert(&sysctllock, RA_WLOCKED) -#define SYSCTL_ASSERT_RLOCKED() rm_assert(&sysctllock, RA_RLOCKED) -#define SYSCTL_INIT() rm_init_flags(&sysctllock, "sysctl lock", \ - RM_SLEEPABLE) +#define SYSCTL_XLOCK() sx_xlock(&sysctllock) +#define SYSCTL_XUNLOCK() sx_xunlock(&sysctllock) +#define SYSCTL_SLOCK() sx_slock(&sysctllock) +#define SYSCTL_SUNLOCK() sx_sunlock(&sysctllock) +#define SYSCTL_XLOCKED() sx_xlocked(&sysctllock) +#define SYSCTL_ASSERT_LOCKED() sx_assert(&sysctllock, SA_LOCKED) +#define SYSCTL_ASSERT_XLOCKED() sx_assert(&sysctllock, SA_XLOCKED) +#define SYSCTL_ASSERT_SLOCKED() sx_assert(&sysctllock, SA_SLOCKED) +#define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock") #define SYSCTL_SLEEP(ch, wmesg, timo) \ - rm_sleep(ch, &sysctllock, 0, wmesg, timo) + sx_sleep(ch, &sysctllock, 0, wmesg, timo) static int sysctl_root(SYSCTL_HANDLER_ARGS); @@ -113,6 +111,29 @@ static int sysctl_remove_oid_locked(stru static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); +static void +sysctl_lock(bool xlock) +{ + + if (xlock) + SYSCTL_XLOCK(); + else + SYSCTL_SLOCK(); +} + +static bool +sysctl_unlock(void) +{ + bool xlocked; + + xlocked = SYSCTL_XLOCKED(); + if (xlocked) + SYSCTL_XUNLOCK(); + else + SYSCTL_SUNLOCK(); + return (xlocked); +} + static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) { @@ -133,32 +154,29 @@ sysctl_find_oidname(const char *name, st * Order by number in each list. */ void -sysctl_wlock(void) +sysctl_xlock(void) { - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); } void -sysctl_wunlock(void) +sysctl_xunlock(void) { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); } static int sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intptr_t arg2, - struct sysctl_req *req, struct rm_priotracker *tracker) + struct sysctl_req *req) { int error; + bool xlocked; if (oid->oid_kind & CTLFLAG_DYN) atomic_add_int(&oid->oid_running, 1); - - if (tracker != NULL) - SYSCTL_RUNLOCK(tracker); - else - SYSCTL_WUNLOCK(); + xlocked = sysctl_unlock(); if (!(oid->oid_kind & CTLFLAG_MPSAFE)) mtx_lock(&Giant); @@ -166,11 +184,7 @@ sysctl_root_handler_locked(struct sysctl if (!(oid->oid_kind & CTLFLAG_MPSAFE)) mtx_unlock(&Giant); - if (tracker != NULL) - SYSCTL_RLOCK(tracker); - else - SYSCTL_WLOCK(); - + sysctl_lock(xlocked); if (oid->oid_kind & CTLFLAG_DYN) { if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 && (oid->oid_kind & CTLFLAG_DYING) != 0) @@ -269,7 +283,7 @@ sysctl_load_tunable_by_oid_locked(struct return; } error = sysctl_root_handler_locked(oidp, oidp->oid_arg1, - oidp->oid_arg2, &req, NULL); + oidp->oid_arg2, &req); if (error != 0) printf("Setting sysctl %s failed: %d\n", path + rem, error); if (penv != NULL) @@ -289,7 +303,7 @@ sysctl_register_oid(struct sysctl_oid *o * First check if another oid with the same name already * exists in the parent's list. */ - SYSCTL_ASSERT_WLOCKED(); + SYSCTL_ASSERT_XLOCKED(); p = sysctl_find_oidname(oidp->oid_name, parent); if (p != NULL) { if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { @@ -383,7 +397,7 @@ sysctl_unregister_oid(struct sysctl_oid struct sysctl_oid *p; int error; - SYSCTL_ASSERT_WLOCKED(); + SYSCTL_ASSERT_XLOCKED(); error = ENOENT; if (oidp->oid_number == OID_AUTO) { error = EINVAL; @@ -439,7 +453,7 @@ sysctl_ctx_free(struct sysctl_ctx_list * * XXX This algorithm is a hack. But I don't know any * XXX better solution for now... */ - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); TAILQ_FOREACH(e, clist, link) { error = sysctl_remove_oid_locked(e->entry, 0, 0); if (error) @@ -459,7 +473,7 @@ sysctl_ctx_free(struct sysctl_ctx_list * e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); } if (error) { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return(EBUSY); } /* Now really delete the entries */ @@ -473,7 +487,7 @@ sysctl_ctx_free(struct sysctl_ctx_list * free(e, M_SYSCTLOID); e = e1; } - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (error); } @@ -483,7 +497,7 @@ sysctl_ctx_entry_add(struct sysctl_ctx_l { struct sysctl_ctx_entry *e; - SYSCTL_ASSERT_WLOCKED(); + SYSCTL_ASSERT_XLOCKED(); if (clist == NULL || oidp == NULL) return(NULL); e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); @@ -498,7 +512,7 @@ sysctl_ctx_entry_find(struct sysctl_ctx_ { struct sysctl_ctx_entry *e; - SYSCTL_ASSERT_WLOCKED(); + SYSCTL_ASSERT_XLOCKED(); if (clist == NULL || oidp == NULL) return(NULL); TAILQ_FOREACH(e, clist, link) { @@ -520,15 +534,15 @@ sysctl_ctx_entry_del(struct sysctl_ctx_l if (clist == NULL || oidp == NULL) return (EINVAL); - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); e = sysctl_ctx_entry_find(clist, oidp); if (e != NULL) { TAILQ_REMOVE(clist, e, link); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); free(e, M_SYSCTLOID); return (0); } else { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (ENOENT); } } @@ -544,9 +558,9 @@ sysctl_remove_oid(struct sysctl_oid *oid { int error; - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); error = sysctl_remove_oid_locked(oidp, del, recurse); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (error); } @@ -558,14 +572,14 @@ sysctl_remove_name(struct sysctl_oid *pa int error; error = ENOENT; - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); SLIST_FOREACH_SAFE(p, SYSCTL_CHILDREN(parent), oid_link, tmp) { if (strcmp(p->oid_name, name) == 0) { error = sysctl_remove_oid_locked(p, del, recurse); break; } } - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (error); } @@ -577,7 +591,7 @@ sysctl_remove_oid_locked(struct sysctl_o struct sysctl_oid *p, *tmp; int error; - SYSCTL_ASSERT_WLOCKED(); + SYSCTL_ASSERT_XLOCKED(); if (oidp == NULL) return(EINVAL); if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { @@ -652,7 +666,7 @@ sysctl_add_oid(struct sysctl_ctx_list *c if (parent == NULL) return(NULL); /* Check if the node already exists, otherwise create it */ - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); oidp = sysctl_find_oidname(name, parent); if (oidp != NULL) { if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { @@ -660,10 +674,10 @@ sysctl_add_oid(struct sysctl_ctx_list *c /* Update the context */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (oidp); } else { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); printf("can't re-use a leaf (%s)!\n", name); return (NULL); } @@ -686,7 +700,7 @@ sysctl_add_oid(struct sysctl_ctx_list *c sysctl_ctx_entry_add(clist, oidp); /* Register this oid */ sysctl_register_oid(oidp); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (oidp); } @@ -700,10 +714,10 @@ sysctl_rename_oid(struct sysctl_oid *oid char *oldname; newname = strdup(name, M_SYSCTLOID); - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); oldname = __DECONST(char *, oidp->oid_name); oidp->oid_name = newname; - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); free(oldname, M_SYSCTLOID); } @@ -715,21 +729,21 @@ sysctl_move_oid(struct sysctl_oid *oid, { struct sysctl_oid *oidp; - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); if (oid->oid_parent == parent) { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (0); } oidp = sysctl_find_oidname(oid->oid_name, parent); if (oidp != NULL) { - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (EEXIST); } sysctl_unregister_oid(oid); oid->oid_parent = parent; oid->oid_number = OID_AUTO; sysctl_register_oid(oid); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); return (0); } @@ -745,10 +759,10 @@ sysctl_register_all(void *arg) sx_init(&sysctlmemlock, "sysctl mem"); SYSCTL_INIT(); - SYSCTL_WLOCK(); + SYSCTL_XLOCK(); SET_FOREACH(oidp, sysctl_set) sysctl_register_oid(*oidp); - SYSCTL_WUNLOCK(); + SYSCTL_XUNLOCK(); } SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, 0); @@ -818,15 +832,14 @@ sysctl_sysctl_debug_dump_node(struct sys static int sysctl_sysctl_debug(SYSCTL_HANDLER_ARGS) { - struct rm_priotracker tracker; int error; error = priv_check(req->td, PRIV_SYSCTL_DEBUG); if (error) return (error); - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); sysctl_sysctl_debug_dump_node(&sysctl__children, 0); - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); return (ENOENT); } @@ -842,10 +855,9 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) int error = 0; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; - struct rm_priotracker tracker; char buf[10]; - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); while (namelen) { if (!lsp) { snprintf(buf,sizeof(buf),"%d",*name); @@ -888,7 +900,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) } error = SYSCTL_OUT(req, "", 1); out: - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); return (error); } @@ -967,12 +979,11 @@ sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) int i, j, error; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; - struct rm_priotracker tracker; int newoid[CTL_MAXNAME]; - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); if (i) return (ENOENT); error = SYSCTL_OUT(req, newoid, j * sizeof (int)); @@ -1031,7 +1042,6 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_AR char *p; int error, oid[CTL_MAXNAME], len = 0; struct sysctl_oid *op = 0; - struct rm_priotracker tracker; if (!req->newlen) return (ENOENT); @@ -1048,9 +1058,9 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_AR p [req->newlen] = '\0'; - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); error = name2oid(p, oid, &len, &op); - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); free(p, M_SYSCTL); @@ -1073,10 +1083,9 @@ static int sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; - struct rm_priotracker tracker; int error; - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) goto out; @@ -1090,7 +1099,7 @@ sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS goto out; error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); out: - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); return (error); } @@ -1102,10 +1111,9 @@ static int sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; - struct rm_priotracker tracker; int error; - SYSCTL_RLOCK(&tracker); + SYSCTL_SLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) goto out; @@ -1116,7 +1124,7 @@ sysctl_sysctl_oiddescr(SYSCTL_HANDLER_AR } error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1); out: - SYSCTL_RUNLOCK(&tracker); + SYSCTL_SUNLOCK(); return (error); } @@ -1421,7 +1429,9 @@ kernel_sysctl(struct thread *td, int *na req.newfunc = sysctl_new_kernel; req.lock = REQ_UNWIRED; + SYSCTL_SLOCK(); error = sysctl_root(0, name, namelen, &req); + SYSCTL_SUNLOCK(); if (req.lock == REQ_WIRED && req.validlen > 0) vsunlock(req.oldptr, req.validlen); @@ -1598,14 +1608,13 @@ static int sysctl_root(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; - struct rm_priotracker tracker; int error, indx, lvl; - SYSCTL_RLOCK(&tracker); + SYSCTL_ASSERT_SLOCKED(); error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); if (error) - goto out; + return (error); if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { /* @@ -1613,17 +1622,13 @@ sysctl_root(SYSCTL_HANDLER_ARGS) * no handler. Inform the user that it's a node. * The indx may or may not be the same as namelen. */ - if (oid->oid_handler == NULL) { - error = EISDIR; - goto out; - } + if (oid->oid_handler == NULL) + return (EISDIR); } /* Is this sysctl writable? */ - if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) { - error = EPERM; - goto out; - } + if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) + return (EPERM); KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL")); @@ -1633,11 +1638,10 @@ sysctl_root(SYSCTL_HANDLER_ARGS) * writing unless specifically granted for the node. */ if (IN_CAPABILITY_MODE(req->td)) { - if ((req->oldptr && !(oid->oid_kind & CTLFLAG_CAPRD)) || - (req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR))) { - error = EPERM; - goto out; - } + if (req->oldptr && !(oid->oid_kind & CTLFLAG_CAPRD)) + return (EPERM); + if (req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR)) + return (EPERM); } #endif @@ -1646,7 +1650,7 @@ sysctl_root(SYSCTL_HANDLER_ARGS) lvl = (oid->oid_kind & CTLMASK_SECURE) >> CTLSHIFT_SECURE; error = securelevel_gt(req->td->td_ucred, lvl); if (error) - goto out; + return (error); } /* Is this sysctl writable by only privileged users? */ @@ -1664,13 +1668,11 @@ sysctl_root(SYSCTL_HANDLER_ARGS) priv = PRIV_SYSCTL_WRITE; error = priv_check(req->td, priv); if (error) - goto out; + return (error); } - if (!oid->oid_handler) { - error = EINVAL; - goto out; - } + if (!oid->oid_handler) + return (EINVAL); if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { arg1 = (int *)arg1 + indx; @@ -1683,18 +1685,16 @@ sysctl_root(SYSCTL_HANDLER_ARGS) error = mac_system_check_sysctl(req->td->td_ucred, oid, arg1, arg2, req); if (error != 0) - goto out; + return (error); #endif #ifdef VIMAGE if ((oid->oid_kind & CTLFLAG_VNET) && arg1 != NULL) arg1 = (void *)(curvnet->vnet_data_base + (uintptr_t)arg1); #endif - error = sysctl_root_handler_locked(oid, arg1, arg2, req, &tracker); + error = sysctl_root_handler_locked(oid, arg1, arg2, req); KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error); -out: - SYSCTL_RUNLOCK(&tracker); return (error); } @@ -1794,7 +1794,9 @@ userland_sysctl(struct thread *td, int * for (;;) { req.oldidx = 0; req.newidx = 0; + SYSCTL_SLOCK(); error = sysctl_root(0, name, namelen, &req); + SYSCTL_SUNLOCK(); if (error != EAGAIN) break; kern_yield(PRI_USER); Modified: head/sys/kern/vfs_init.c ============================================================================== --- head/sys/kern/vfs_init.c Thu Jul 30 19:44:46 2015 (r286093) +++ head/sys/kern/vfs_init.c Thu Jul 30 19:52:43 2015 (r286094) @@ -291,7 +291,7 @@ vfs_register(struct vfsconf *vfc) * preserved by re-registering the oid after modifying its * number. */ - sysctl_wlock(); + sysctl_xlock(); SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link) { if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) { sysctl_unregister_oid(oidp); @@ -300,7 +300,7 @@ vfs_register(struct vfsconf *vfc) break; } } - sysctl_wunlock(); + sysctl_xunlock(); return (0); } Modified: head/sys/sys/sysctl.h ============================================================================== --- head/sys/sys/sysctl.h Thu Jul 30 19:44:46 2015 (r286093) +++ head/sys/sys/sysctl.h Thu Jul 30 19:52:43 2015 (r286094) @@ -807,8 +807,8 @@ int userland_sysctl(struct thread *td, i size_t *retval, int flags); int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, int *nindx, struct sysctl_req *req); -void sysctl_wlock(void); -void sysctl_wunlock(void); +void sysctl_xlock(void); +void sysctl_xunlock(void); int sysctl_wire_old_buffer(struct sysctl_req *req, size_t len); struct sbuf;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201507301952.t6UJqiPK003032>